delete the claude-done hook and its unauthenticated endpoint
The chain was: a Stop hook in Claude's settings curls POST /api/hooks/claude-done, the platform POSTs /_officer/panel-refresh to the pty sidecar, the sidecar sends a `panel-refresh` frame to every attached terminal, and the Claude Code panel bumps `preview:refresh` and `files:refresh-signal`. It has never fired. generateClaudeSettings writes settings.json into the MANAGED home under DATA_PATH, but HOME_DIR points terminals at the owner's real login home — which is where Claude reads its settings from. Verified on this machine: no claude-done hook exists in ~/.claude/settings.json, and DATA_PATH/*/home/.claude does not exist at all. Deleting rather than repairing it, because the Chat panel already does exactly this job from onTurnComplete — in-process, conditioned on the turn having made tool calls, with no hook, no HTTP round trip, and no endpoint. The chat UI is where agent work happens; the terminal TUI is not the destination. Also removes /api/hooks/claude-done, which was mounted above protectedRouter and so was the one unauthenticated write-ish endpoint on the API surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,13 +11,15 @@ type CommandTerminalWrapperProps = {
|
||||
panelId: string;
|
||||
command: string;
|
||||
statePrefix: string;
|
||||
onPanelRefresh?: () => void;
|
||||
};
|
||||
|
||||
export const CommandTerminalWrapper = ({ panelId, command, statePrefix, onPanelRefresh }: CommandTerminalWrapperProps) => {
|
||||
export const CommandTerminalWrapper = ({ panelId, command, statePrefix }: CommandTerminalWrapperProps) => {
|
||||
const { dashboardId, cwd } = useWorkspace();
|
||||
const stateKey = dashboardId ? `ws-${statePrefix}-${dashboardId}` : `ws-${statePrefix}-default`;
|
||||
const { value: terminals, setValue: setTerminals } = useDashboardState<Record<string, string>>(stateKey, EMPTY_TERMINALS);
|
||||
const { value: terminals, setValue: setTerminals } = useDashboardState<Record<string, string>>(
|
||||
stateKey,
|
||||
EMPTY_TERMINALS,
|
||||
);
|
||||
const sessionId = terminals[panelId];
|
||||
|
||||
useEffect(() => {
|
||||
@@ -43,7 +45,6 @@ export const CommandTerminalWrapper = ({ panelId, command, statePrefix, onPanelR
|
||||
sessionId={sessionId}
|
||||
cwd={cwd}
|
||||
initialInput={fullCommand}
|
||||
onPanelRefresh={onPanelRefresh}
|
||||
onConnectionChange={onConnectionChange}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,6 @@ export type TerminalViewProps = {
|
||||
onExit?: () => void;
|
||||
onCommandDone?: (exitCode: number, output: string) => void;
|
||||
onDisconnect?: () => void;
|
||||
onPanelRefresh?: () => void;
|
||||
onConnectionChange?: (state: TerminalConnectionState) => void;
|
||||
/** The shell's own title (OSC 0/2) — what's actually running, for the panel header. */
|
||||
onTitleChange?: (title: string) => void;
|
||||
@@ -89,7 +88,6 @@ export const TerminalView = ({
|
||||
onExit,
|
||||
onCommandDone,
|
||||
onDisconnect,
|
||||
onPanelRefresh,
|
||||
onConnectionChange,
|
||||
onTitleChange,
|
||||
onBell,
|
||||
@@ -105,7 +103,6 @@ export const TerminalView = ({
|
||||
const onExitRef = useRef<TerminalViewProps['onExit']>(onExit);
|
||||
const onCommandDoneRef = useRef<TerminalViewProps['onCommandDone']>(onCommandDone);
|
||||
const onDisconnectRef = useRef<TerminalViewProps['onDisconnect']>(onDisconnect);
|
||||
const onPanelRefreshRef = useRef<TerminalViewProps['onPanelRefresh']>(onPanelRefresh);
|
||||
const onConnectionChangeRef = useRef<TerminalViewProps['onConnectionChange']>(onConnectionChange);
|
||||
const commandRef = useRef(command);
|
||||
const initialInputRef = useRef(initialInput);
|
||||
@@ -114,7 +111,6 @@ export const TerminalView = ({
|
||||
onExitRef.current = onExit;
|
||||
onCommandDoneRef.current = onCommandDone;
|
||||
onDisconnectRef.current = onDisconnect;
|
||||
onPanelRefreshRef.current = onPanelRefresh;
|
||||
onConnectionChangeRef.current = onConnectionChange;
|
||||
commandRef.current = command;
|
||||
initialInputRef.current = initialInput;
|
||||
@@ -295,8 +291,6 @@ export const TerminalView = ({
|
||||
onExitRef.current?.();
|
||||
} else if (msg.type === 'detached') {
|
||||
term.write('\r\n[Session taken over]\r\n');
|
||||
} else if (msg.type === 'panel-refresh') {
|
||||
onPanelRefreshRef.current?.();
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { AppRegistryMeta } from '../../AppRegistry';
|
||||
import { TerminalSquare, Monitor, Columns2, PenLine, Sparkles, ListTree } from 'lucide-react';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { TerminalWrapper } from './TerminalWrapper';
|
||||
import { HostTerminalWrapper } from './HostTerminalWrapper';
|
||||
import { CommandTerminalWrapper } from './CommandTerminalWrapper';
|
||||
@@ -29,24 +27,12 @@ const NvimWrapper = ({ panelId }: { panelId: string }) => (
|
||||
<CommandTerminalWrapper panelId={panelId} command="nvim" statePrefix="nvim" />
|
||||
);
|
||||
|
||||
const ClaudeCodeWrapper = ({ panelId }: { panelId: string }) => {
|
||||
const [, setPreviewRefresh] = usePanelChannel<number>('preview:refresh', 0);
|
||||
const [, setFilesRefresh] = usePanelChannel<number>('files:refresh-signal', 0);
|
||||
|
||||
const onPanelRefresh = useCallback(() => {
|
||||
setPreviewRefresh(Date.now());
|
||||
setFilesRefresh(Date.now());
|
||||
}, [setPreviewRefresh, setFilesRefresh]);
|
||||
|
||||
return (
|
||||
<CommandTerminalWrapper
|
||||
panelId={panelId}
|
||||
command="claude --dangerously-skip-permissions"
|
||||
statePrefix="claude-code"
|
||||
onPanelRefresh={onPanelRefresh}
|
||||
/>
|
||||
);
|
||||
};
|
||||
// No turn-complete refresh here. Claude's Stop hook was supposed to drive one, but it was written into the
|
||||
// managed home under DATA_PATH while HOME_DIR points terminals at the owner's real login home — so it was
|
||||
// never installed and never fired. The Chat panel does the same job from onTurnComplete, in-process.
|
||||
const ClaudeCodeWrapper = ({ panelId }: { panelId: string }) => (
|
||||
<CommandTerminalWrapper panelId={panelId} command="claude --dangerously-skip-permissions" statePrefix="claude-code" />
|
||||
);
|
||||
|
||||
export const appRegistryMetas: AppRegistryMeta[] = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user