claude-code as channel model, container trust/permissions fixes, terminal cwd fix
- add claude-code as virtual model in channel messaging (telegram/discord/whatsapp) - new send-claude-code.ts: docker exec claude -p with session resumption - route claude-code model in sendAndAwait before Pi pipeline - append claude-code to listPiModels output - fix container .claude mount (rw for sub-mounts), hooks format (matcher-based) - pre-seed hasTrustDialogAccepted and bypassPermissions in container settings - git init in entrypoint to skip workspace trust prompt - fix ~/~ double-tilde in CommandTerminalWrapper cwd resolution - remove --continue from claude-code panel command Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,9 +9,10 @@ type CommandTerminalWrapperProps = {
|
||||
panelId: string;
|
||||
command: string;
|
||||
statePrefix: string;
|
||||
onPanelRefresh?: () => void;
|
||||
};
|
||||
|
||||
export const CommandTerminalWrapper = ({ panelId, command, statePrefix }: CommandTerminalWrapperProps) => {
|
||||
export const CommandTerminalWrapper = ({ panelId, command, statePrefix, onPanelRefresh }: 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);
|
||||
@@ -37,8 +38,8 @@ export const CommandTerminalWrapper = ({ panelId, command, statePrefix }: Comman
|
||||
|
||||
if (!sessionId) return null;
|
||||
|
||||
const cwdPath = cwd && cwd !== '~' ? `~/${cwd.replace(/^\//, '')}` : null;
|
||||
const cwdPath = cwd && cwd !== '~' ? (cwd.startsWith('~') ? cwd : `~/${cwd.replace(/^\//, '')}`) : null;
|
||||
const fullCommand = cwdPath ? `cd ${cwdPath} && ${command}` : command;
|
||||
|
||||
return <TerminalView className="h-full w-full p-2" sessionId={sessionId} cwd={cwd} initialInput={fullCommand} />;
|
||||
return <TerminalView className="h-full w-full p-2" sessionId={sessionId} cwd={cwd} initialInput={fullCommand} onPanelRefresh={onPanelRefresh} />;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TerminalSquare, Monitor, Columns2, PenLine } from 'lucide-react';
|
||||
import { TerminalSquare, Monitor, Columns2, PenLine, Sparkles } from 'lucide-react';
|
||||
import { useWorkspace } from '../../components/Workspace';
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
import { useTerminalMode } from './useTerminalMode';
|
||||
@@ -61,3 +61,14 @@ export const NvimHeader = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const ClaudeCodeHeader = () => {
|
||||
const { cwd } = useWorkspace();
|
||||
return (
|
||||
<>
|
||||
<Sparkles className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="text-xs font-medium shrink-0">Claude Code</span>
|
||||
<span className="text-[10px] font-mono truncate opacity-60">{cwd}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ export type TerminalViewProps = {
|
||||
onExit?: () => void;
|
||||
onCommandDone?: (exitCode: number, output: string) => void;
|
||||
onDisconnect?: () => void;
|
||||
onPanelRefresh?: () => void;
|
||||
};
|
||||
|
||||
const DEFAULT_THEME: Required<TerminalTheme> = {
|
||||
@@ -69,6 +70,7 @@ export const TerminalView = ({
|
||||
onExit,
|
||||
onCommandDone,
|
||||
onDisconnect,
|
||||
onPanelRefresh,
|
||||
}: TerminalViewProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const termRef = useRef<XTerm | null>(null);
|
||||
@@ -78,6 +80,7 @@ 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 commandRef = useRef(command);
|
||||
const initialInputRef = useRef(initialInput);
|
||||
|
||||
@@ -85,6 +88,7 @@ export const TerminalView = ({
|
||||
onExitRef.current = onExit;
|
||||
onCommandDoneRef.current = onCommandDone;
|
||||
onDisconnectRef.current = onDisconnect;
|
||||
onPanelRefreshRef.current = onPanelRefresh;
|
||||
commandRef.current = command;
|
||||
initialInputRef.current = initialInput;
|
||||
|
||||
@@ -188,6 +192,8 @@ 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,9 +1,11 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { AppRegistryMeta } from '../../AppRegistry';
|
||||
import { TerminalSquare, Monitor, Columns2, PenLine } from 'lucide-react';
|
||||
import { TerminalSquare, Monitor, Columns2, PenLine, Sparkles } from 'lucide-react';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { TerminalWrapper } from './TerminalWrapper';
|
||||
import { HostTerminalWrapper } from './HostTerminalWrapper';
|
||||
import { CommandTerminalWrapper } from './CommandTerminalWrapper';
|
||||
import { TerminalHeader, HostTerminalHeader, TmuxHeader, NvimHeader } from './Headers';
|
||||
import { TerminalHeader, HostTerminalHeader, TmuxHeader, NvimHeader, ClaudeCodeHeader } from './Headers';
|
||||
|
||||
export { TerminalView, type TerminalViewProps } from './Terminal';
|
||||
|
||||
@@ -15,6 +17,25 @@ 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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const appRegistryMetas: AppRegistryMeta[] = [
|
||||
{
|
||||
key: 'officerdev/terminal',
|
||||
@@ -45,4 +66,11 @@ export const appRegistryMetas: AppRegistryMeta[] = [
|
||||
component: NvimWrapper,
|
||||
header: NvimHeader,
|
||||
},
|
||||
{
|
||||
key: 'officerdev/claude-code',
|
||||
name: 'Claude Code',
|
||||
icon: Sparkles,
|
||||
component: ClaudeCodeWrapper,
|
||||
header: ClaudeCodeHeader,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user