From d7b907ed9fb16668694e09cc43ed8eada67a4a2a Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Wed, 1 Apr 2026 20:28:50 -0400 Subject: [PATCH 01/48] fix(cursor): make bare cursor command useful and harden tool-result translation --- README.md | 2 +- docs/cursor-integration.md | 7 + src/commands/cursor-command-display.ts | 28 +- src/commands/cursor-command.ts | 1 + src/cursor/cursor-translator.ts | 401 +++++++++++++++++----- tests/unit/cursor/cursor-daemon.test.ts | 132 +++++++ tests/unit/cursor/cursor-protobuf.test.ts | 305 +++++++++++++++- 7 files changed, 781 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index c10672d9..fa1346e7 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ The dashboard provides visual management for all account types: ccs # Default Claude session ccs gemini # Gemini (OAuth) ccs codex # OpenAI Codex (OAuth) -ccs cursor # Cursor IDE integration (token import + local daemon) +ccs cursor # Cursor status + local daemon connection details ccs kiro # Kiro/AWS CodeWhisperer (OAuth) ccs ghcp # GitHub Copilot (OAuth device flow) ccs agy # Antigravity (OAuth) diff --git a/docs/cursor-integration.md b/docs/cursor-integration.md index 8278544e..5e190e8c 100644 --- a/docs/cursor-integration.md +++ b/docs/cursor-integration.md @@ -49,6 +49,13 @@ ccs cursor start ccs cursor status ``` +Or use the bare command to see the same status view plus the local endpoint details +needed for OpenAI-compatible and Anthropic-compatible clients: + +```bash +ccs cursor +``` + ### 5) Stop daemon ```bash diff --git a/src/commands/cursor-command-display.ts b/src/commands/cursor-command-display.ts index 34260d88..5e1daf57 100644 --- a/src/commands/cursor-command-display.ts +++ b/src/commands/cursor-command-display.ts @@ -12,7 +12,7 @@ export function renderCursorHelp(): number { printLines([ 'Cursor IDE Integration', '', - 'Usage: ccs cursor [options]', + 'Usage: ccs cursor [subcommand] [options]', '', 'Subcommands:', ' auth Import Cursor IDE authentication token', @@ -32,6 +32,7 @@ export function renderCursorHelp(): number { ' 1. ccs cursor enable # Enable integration', ' 2. ccs cursor auth # Import Cursor IDE token', ' 3. ccs cursor start # Start daemon', + ' 4. ccs cursor # Show status and runtime connection details', '', 'Or use the web UI: ccs config -> Cursor page', '', @@ -45,6 +46,10 @@ export function renderCursorStatus( authStatus: CursorAuthStatus, daemonStatus: CursorDaemonStatus ): void { + const localBaseUrl = `http://127.0.0.1:${cursorConfig.port}`; + const isReady = + cursorConfig.enabled && authStatus.authenticated && !authStatus.expired && daemonStatus.running; + console.log('Cursor IDE Status'); console.log('─────────────────'); console.log(''); @@ -77,15 +82,23 @@ export function renderCursorStatus( console.log(` Ghost mode: ${cursorConfig.ghost_mode ? 'On' : 'Off'}`); console.log(''); - if ( - cursorConfig.enabled && - authStatus.authenticated && - !authStatus.expired && - daemonStatus.running - ) { + console.log('Runtime:'); + console.log(` OpenAI base: ${localBaseUrl}/v1`); + console.log(` Anthropic base: ${localBaseUrl}`); + console.log(` Chat route: ${localBaseUrl}/v1/chat/completions`); + console.log(` Messages route: ${localBaseUrl}/v1/messages`); + console.log(` Models route: ${localBaseUrl}/v1/models`); + console.log(''); + console.log('Client setup:'); + console.log(' Raw settings: ~/.ccs/cursor.settings.json'); + console.log(' Subcommands: ccs cursor help'); + + if (isReady) { return; } + console.log(''); + console.log('Next steps:'); if (!cursorConfig.enabled) { console.log(' - Enable: ccs cursor enable'); @@ -96,6 +109,7 @@ export function renderCursorStatus( if (!daemonStatus.running) { console.log(' - Start: ccs cursor start'); } + console.log(' - Help: ccs cursor help'); } export function renderCursorModels(models: CursorModel[], defaultModel: string): void { diff --git a/src/commands/cursor-command.ts b/src/commands/cursor-command.ts index ae8d5363..05a9245b 100644 --- a/src/commands/cursor-command.ts +++ b/src/commands/cursor-command.ts @@ -56,6 +56,7 @@ export async function handleCursorCommand(args: string[]): Promise { case 'disable': return handleDisable(); case undefined: + return handleStatus(); case 'help': case '--help': case '-h': diff --git a/src/cursor/cursor-translator.ts b/src/cursor/cursor-translator.ts index f709ab43..fa6617c7 100644 --- a/src/cursor/cursor-translator.ts +++ b/src/cursor/cursor-translator.ts @@ -1,14 +1,42 @@ /** * OpenAI to Cursor Request Translator - * Converts OpenAI messages to Cursor format + * Converts OpenAI messages to Cursor format. */ -import type { CursorMessage, CursorToolResult, CursorTool } from './cursor-protobuf-schema.js'; +import type { CursorMessage, CursorTool } from './cursor-protobuf-schema.js'; + +interface OpenAITextPart { + type: 'text'; + text?: string; +} + +interface OpenAIToolUsePart { + type: 'tool_use'; + id?: string; + name?: string; + input?: Record; +} + +interface OpenAIToolResultPart { + type: 'tool_result'; + tool_use_id?: string; + content?: unknown; +} + +interface OpenAIUnknownPart { + type: string; + [key: string]: unknown; +} + +type OpenAIContentPart = + | OpenAITextPart + | OpenAIToolUsePart + | OpenAIToolResultPart + | OpenAIUnknownPart; -/** OpenAI message format */ interface OpenAIMessage { role: string; - content: string | Array<{ type: string; text?: string }>; + content: string | OpenAIContentPart[]; name?: string; tool_call_id?: string; tool_calls?: Array<{ @@ -18,113 +46,326 @@ interface OpenAIMessage { }>; } -/** OpenAI request body */ interface OpenAIRequestBody { messages: OpenAIMessage[]; tools?: CursorTool[]; reasoning_effort?: string; } +const MAX_TOOL_RESULT_CHARS = 12_000; +const TOOL_RESULT_SERIALIZATION_FALLBACK = '[unserializable content]'; +const TOOL_USE_ARGUMENTS_FALLBACK = '{}'; + +function isTextPart(part: OpenAIContentPart): part is OpenAITextPart { + return part.type === 'text'; +} + +function isToolUsePart(part: OpenAIContentPart): part is OpenAIToolUsePart { + return part.type === 'tool_use'; +} + +function isToolResultPart(part: OpenAIContentPart): part is OpenAIToolResultPart { + return part.type === 'tool_result'; +} + +function extractTextContent(content: OpenAIMessage['content']): string { + if (typeof content === 'string') { + return content; + } + + let text = ''; + for (const part of content) { + if (isTextPart(part) && part.text) { + text += part.text; + } + } + + return text; +} + +function stringifyUnknown(value: unknown, fallback = ''): string { + try { + const serialized = JSON.stringify(value); + return typeof serialized === 'string' ? serialized : fallback; + } catch { + return fallback; + } +} + +function sanitizeToolResultText(text: string): string { + return text.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, ''); +} + +function truncateToolResultText(text: string): string { + if (text.length <= MAX_TOOL_RESULT_CHARS) { + return text; + } + + let suffix = '\n[truncated]'; + let keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); + let omittedChars = text.length - keepLength; + + suffix = `\n[truncated ${omittedChars} chars]`; + keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); + omittedChars = text.length - keepLength; + suffix = `\n[truncated ${omittedChars} chars]`; + + return `${text.slice(0, keepLength)}${suffix}`; +} + +function escapeXml(text: string): string { + return text.replace(/&/g, '&').replace(//g, '>'); +} + +function buildToolResultBlock(toolName: string, toolCallId: string, resultText: string): string { + const cleanResult = truncateToolResultText(escapeXml(sanitizeToolResultText(resultText))); + + return [ + '', + `${escapeXml(toolName || 'tool')}`, + `${escapeXml(toolCallId)}`, + `${cleanResult}`, + '', + ].join('\n'); +} + +function normalizeToolCallId(toolCallId: string | undefined): string { + return typeof toolCallId === 'string' ? toolCallId.split('\n')[0] : ''; +} + +function extractToolResultText(content: unknown): string { + if (content === undefined) { + return ''; + } + + if (typeof content === 'string') { + return content; + } + + if (Array.isArray(content)) { + return content + .filter(isTextPart) + .map((part) => part.text || '') + .join(''); + } + + return stringifyUnknown(content, TOOL_RESULT_SERIALIZATION_FALLBACK); +} + +function createFallbackToolUseId(messageIndex: number, partIndex: number): string { + return `toolu_cursor_fallback_${messageIndex}_${partIndex}`; +} + +function resolveToolUseId( + part: OpenAIToolUsePart, + messageIndex: number, + partIndex: number +): string { + return typeof part.id === 'string' && part.id.length > 0 + ? part.id + : createFallbackToolUseId(messageIndex, partIndex); +} + +function rememberToolCallMeta( + toolCallMetaMap: Map, + toolCalls: NonNullable +): void { + for (const toolCall of toolCalls) { + const toolCallId = toolCall.id || ''; + const toolName = toolCall.function?.name || 'tool'; + if (!toolCallId) { + continue; + } + + toolCallMetaMap.set(toolCallId, { name: toolName }); + + const normalizedId = normalizeToolCallId(toolCallId); + if (normalizedId && normalizedId !== toolCallId) { + toolCallMetaMap.set(normalizedId, { name: toolName }); + } + } +} + +function rememberToolUseParts( + toolCallMetaMap: Map, + content: OpenAIMessage['content'], + messageIndex: number +): void { + if (!Array.isArray(content)) { + return; + } + + for (let partIndex = 0; partIndex < content.length; partIndex++) { + const part = content[partIndex]; + if (!isToolUsePart(part)) { + continue; + } + + const toolCallId = resolveToolUseId(part, messageIndex, partIndex); + const toolName = part.name || 'tool'; + toolCallMetaMap.set(toolCallId, { name: toolName }); + + const normalizedId = normalizeToolCallId(toolCallId); + if (normalizedId && normalizedId !== toolCallId) { + toolCallMetaMap.set(normalizedId, { name: toolName }); + } + } +} + +function extractToolCallsFromContent( + content: OpenAIMessage['content'], + messageIndex: number +): NonNullable { + if (!Array.isArray(content)) { + return []; + } + + return content.flatMap((part, partIndex) => { + if (!isToolUsePart(part)) { + return []; + } + + return [ + { + id: resolveToolUseId(part, messageIndex, partIndex), + type: 'function', + function: { + name: part.name || 'tool', + arguments: stringifyUnknown(part.input ?? {}, TOOL_USE_ARGUMENTS_FALLBACK), + }, + }, + ]; + }); +} + +function mergeAssistantToolCalls( + primaryToolCalls: NonNullable, + secondaryToolCalls: NonNullable +): NonNullable { + const merged: NonNullable = []; + const seenIds = new Set(); + + for (const toolCall of [...primaryToolCalls, ...secondaryToolCalls]) { + if (seenIds.has(toolCall.id)) { + continue; + } + + seenIds.add(toolCall.id); + merged.push(toolCall); + } + + return merged; +} + +function renderUserContent( + content: OpenAIMessage['content'], + toolCallMetaMap: Map +): string { + if (typeof content === 'string') { + return content; + } + + const parts: string[] = []; + let textBuffer = ''; + for (const part of content) { + if (isTextPart(part) && part.text) { + textBuffer += part.text; + continue; + } + + if (!isToolResultPart(part)) { + continue; + } + + if (textBuffer) { + parts.push(textBuffer); + textBuffer = ''; + } + + const toolCallId = part.tool_use_id || ''; + const normalizedId = normalizeToolCallId(toolCallId); + const toolName = + toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedId)?.name || 'tool'; + parts.push(buildToolResultBlock(toolName, toolCallId, extractToolResultText(part.content))); + } + + if (textBuffer) { + parts.push(textBuffer); + } + + return parts.join('\n'); +} + /** - * Convert OpenAI messages to Cursor format with native tool_results support + * Convert OpenAI messages to Cursor format with a safer tool-result strategy. * - system → user with [System Instructions] prefix - * - tool → accumulate into tool_results array for next user/assistant message - * - assistant with tool_calls → keep tool_calls structure (Cursor supports it natively) + * - tool → flatten into a structured user text block for Cursor compatibility + * - assistant with tool_calls → keep tool_calls in the translated shape for metadata recovery */ function convertMessages(messages: OpenAIMessage[]): CursorMessage[] { const result: CursorMessage[] = []; - let pendingToolResults: CursorToolResult[] = []; - - for (let i = 0; i < messages.length; i++) { - const msg = messages[i]; + const toolCallMetaMap = new Map(); + for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) { + const msg = messages[messageIndex]; if (msg.role === 'system') { - let content = ''; - if (typeof msg.content === 'string') { - content = msg.content; - } else if (Array.isArray(msg.content)) { - for (const part of msg.content) { - if (part.type === 'text' && part.text) content += part.text; - } - } result.push({ role: 'user', - content: `[System Instructions]\n${content}`, + content: `[System Instructions]\n${extractTextContent(msg.content)}`, }); continue; } if (msg.role === 'tool') { - let toolContent = ''; - if (typeof msg.content === 'string') { - toolContent = msg.content; - } else if (Array.isArray(msg.content)) { - for (const part of msg.content) { - if (part.type === 'text' && part.text) { - toolContent += part.text; - } - } - } - - const toolName = msg.name || 'tool'; const toolCallId = msg.tool_call_id || ''; + const normalizedToolCallId = normalizeToolCallId(toolCallId); + const rememberedToolName = + toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedToolCallId)?.name; + const toolName = msg.name || rememberedToolName || 'tool'; - // Accumulate tool result - pendingToolResults.push({ - tool_call_id: toolCallId, - name: toolName, - index: pendingToolResults.length, - raw_args: toolContent, + result.push({ + role: 'user', + content: buildToolResultBlock(toolName, toolCallId, extractTextContent(msg.content)), }); continue; } if (msg.role === 'user' || msg.role === 'assistant') { - let content = ''; - - if (typeof msg.content === 'string') { - content = msg.content; - } else if (Array.isArray(msg.content)) { - for (const part of msg.content) { - if (part.type === 'text' && part.text) { - content += part.text; - } + if (msg.role === 'assistant') { + const assistantToolCalls = mergeAssistantToolCalls( + msg.tool_calls || [], + extractToolCallsFromContent(msg.content, messageIndex) + ); + if (msg.tool_calls?.length) { + rememberToolCallMeta(toolCallMetaMap, msg.tool_calls); } - } + rememberToolUseParts(toolCallMetaMap, msg.content, messageIndex); - // Keep tool_calls structure for assistant messages - if (msg.role === 'assistant' && msg.tool_calls && msg.tool_calls.length > 0) { - const assistantMsg: CursorMessage = { role: 'assistant', content: '' }; + const content = extractTextContent(msg.content); + if (assistantToolCalls.length > 0) { + result.push({ + role: 'assistant', + content, + tool_calls: assistantToolCalls, + }); + } else if (content) { + result.push({ + role: 'assistant', + content, + }); + } + } else { + const content = renderUserContent(msg.content, toolCallMetaMap); if (content) { - assistantMsg.content = content; + result.push({ + role: 'user', + content, + }); } - assistantMsg.tool_calls = msg.tool_calls; - - // Attach pending tool results to assistant message with tool_calls - if (pendingToolResults.length > 0) { - assistantMsg.tool_results = pendingToolResults; - pendingToolResults = []; - } - - result.push(assistantMsg); - } else if (content || pendingToolResults.length > 0) { - const msgObj: CursorMessage = { - role: msg.role, - content: content || '', - }; - - // Attach pending tool results to this message - if (pendingToolResults.length > 0) { - msgObj.tool_results = pendingToolResults; - pendingToolResults = []; - } - - result.push(msgObj); } continue; } - // Unknown role - skip with debug warning if (process.env.CCS_DEBUG) { console.error(`[cursor] Unknown message role: ${msg.role}, skipping`); } @@ -133,10 +374,6 @@ function convertMessages(messages: OpenAIMessage[]): CursorMessage[] { return result; } -/** - * Transform OpenAI request to Cursor format - * Returns modified body with converted messages - */ export function buildCursorRequest( _model: string, body: OpenAIRequestBody, @@ -146,10 +383,8 @@ export function buildCursorRequest( messages: CursorMessage[]; tools?: CursorTool[]; } { - const messages = convertMessages(body.messages || []); - return { ...body, - messages, + messages: convertMessages(body.messages || []), }; } diff --git a/tests/unit/cursor/cursor-daemon.test.ts b/tests/unit/cursor/cursor-daemon.test.ts index 5a2f15bb..8e17e2d0 100644 --- a/tests/unit/cursor/cursor-daemon.test.ts +++ b/tests/unit/cursor/cursor-daemon.test.ts @@ -19,7 +19,12 @@ import { } from '../../../src/cursor/cursor-daemon'; import { getCcsDir } from '../../../src/utils/config-manager'; import { handleCursorCommand } from '../../../src/commands/cursor-command'; +import { + renderCursorHelp, + renderCursorStatus, +} from '../../../src/commands/cursor-command-display'; import { loadCredentials } from '../../../src/cursor/cursor-auth'; +import { DEFAULT_CURSOR_CONFIG } from '../../../src/config/unified-config-types'; // Test isolation let originalCcsHome: string | undefined; @@ -271,6 +276,34 @@ describe('stopDaemon', () => { }); describe('handleCursorCommand', () => { + it('treats bare ccs cursor as a status entrypoint instead of help', async () => { + const originalLog = console.log; + const originalError = console.error; + const logs: string[] = []; + const errors: string[] = []; + + console.log = (...args: unknown[]) => { + logs.push(args.map((arg) => String(arg)).join(' ')); + }; + console.error = (...args: unknown[]) => { + errors.push(args.map((arg) => String(arg)).join(' ')); + }; + + try { + const exitCode = await handleCursorCommand([]); + + expect(exitCode).toBe(0); + expect(errors).toHaveLength(0); + expect(logs.some((line) => line.includes('Cursor IDE Status'))).toBe(true); + expect(logs.some((line) => line.includes('Usage: ccs cursor [options]'))).toBe( + false + ); + } finally { + console.log = originalLog; + console.error = originalError; + } + }); + it('returns exit code 1 for unknown subcommand', async () => { const exitCode = await handleCursorCommand(['nonexistent']); expect(exitCode).toBe(1); @@ -297,3 +330,102 @@ describe('handleCursorCommand', () => { expect(credentials?.machineId).toBe(machineId); }); }); + +describe('renderCursorStatus', () => { + it('shows runtime endpoint guidance when Cursor is ready', () => { + const originalLog = console.log; + const logs: string[] = []; + + console.log = (...args: unknown[]) => { + logs.push(args.map((arg) => String(arg)).join(' ')); + }; + + try { + renderCursorStatus( + { ...DEFAULT_CURSOR_CONFIG, enabled: true, port: 20129 }, + { + authenticated: true, + expired: false, + tokenAge: 0, + credentials: { + accessToken: 'a'.repeat(60), + machineId: '1234567890abcdef1234567890abcdef', + authMethod: 'manual', + importedAt: new Date().toISOString(), + }, + }, + { running: true, port: 20129, pid: 1234 } + ); + + expect(logs.some((line) => line.includes('OpenAI base: http://127.0.0.1:20129/v1'))).toBe( + true + ); + expect( + logs.some((line) => line.includes('Chat route: http://127.0.0.1:20129/v1/chat/completions')) + ).toBe(true); + expect( + logs.some((line) => line.includes('Anthropic base: http://127.0.0.1:20129')) + ).toBe(true); + expect(logs.some((line) => line.includes('Raw settings: ~/.ccs/cursor.settings.json'))).toBe( + true + ); + } finally { + console.log = originalLog; + } + }); + + it('shows runtime guidance even when setup is incomplete', () => { + const originalLog = console.log; + const logs: string[] = []; + + console.log = (...args: unknown[]) => { + logs.push(args.map((arg) => String(arg)).join(' ')); + }; + + try { + renderCursorStatus( + { ...DEFAULT_CURSOR_CONFIG, enabled: false, port: 20129 }, + { + authenticated: false, + expired: false, + tokenAge: undefined, + credentials: undefined, + }, + { running: false, port: 20129, pid: undefined } + ); + + expect(logs.some((line) => line.includes('OpenAI base: http://127.0.0.1:20129/v1'))).toBe( + true + ); + expect(logs.some((line) => line.includes('Next steps:'))).toBe(true); + expect(logs.some((line) => line.includes(' - Help: ccs cursor help'))).toBe(true); + } finally { + console.log = originalLog; + } + }); +}); + +describe('renderCursorHelp', () => { + it('shows bare ccs cursor as an optional entrypoint', () => { + const originalLog = console.log; + const logs: string[] = []; + + console.log = (...args: unknown[]) => { + logs.push(args.map((arg) => String(arg)).join(' ')); + }; + + try { + const exitCode = renderCursorHelp(); + + expect(exitCode).toBe(0); + expect(logs.some((line) => line.includes('Usage: ccs cursor [subcommand] [options]'))).toBe( + true + ); + expect( + logs.some((line) => line.includes('4. ccs cursor # Show status and runtime connection details')) + ).toBe(true); + } finally { + console.log = originalLog; + } + }); +}); diff --git a/tests/unit/cursor/cursor-protobuf.test.ts b/tests/unit/cursor/cursor-protobuf.test.ts index 70d9e973..e2af6027 100644 --- a/tests/unit/cursor/cursor-protobuf.test.ts +++ b/tests/unit/cursor/cursor-protobuf.test.ts @@ -13,6 +13,7 @@ import { import { decodeVarint, decodeField, + decodeMessage, parseConnectRPCFrame, } from '../../../src/cursor/cursor-protobuf-decoder'; import { buildCursorRequest } from '../../../src/cursor/cursor-translator'; @@ -251,11 +252,256 @@ describe('Message Translation', () => { {} ); + expect(result.messages).toHaveLength(3); + expect(result.messages[1].role).toBe('user'); + expect(result.messages[1].content).toContain(''); + expect(result.messages[1].content).toContain('get_weather'); + expect(result.messages[1].content).toContain('call_123'); + expect(result.messages[2]).toEqual({ role: 'user', content: 'What is the weather?' }); + }); + + it('should recover tool names for tool results without a name field', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_456', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + ], + }, + { + role: 'tool', + content: 'done', + tool_call_id: 'call_456', + }, + ], + }, + false, + {} + ); + expect(result.messages).toHaveLength(2); - // Tool result should be attached to next message - expect(result.messages[1].tool_results).toBeDefined(); - expect(result.messages[1].tool_results).toHaveLength(1); - expect(result.messages[1].tool_results![0].tool_call_id).toBe('call_123'); + expect(result.messages[1].content).toContain('search_docs'); + expect(result.messages[1].tool_results).toBeUndefined(); + }); + + it('should flatten user content arrays that include tool_result blocks', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_789', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + ], + }, + { + role: 'user', + content: [ + { type: 'text', text: 'Tool finished.' }, + { + type: 'tool_result', + tool_use_id: 'call_789', + content: { answer: 'Cursor integration ready' }, + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(2); + expect(result.messages[1].role).toBe('user'); + expect(result.messages[1].content).toContain('Tool finished.'); + expect(result.messages[1].content).toContain('search_docs'); + expect(result.messages[1].content).toContain('{"answer":"Cursor integration ready"}'); + }); + + it('should convert assistant tool_use content blocks into tool_calls', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: [ + { type: 'text', text: 'Inspecting the workspace.' }, + { + type: 'tool_use', + id: 'call_999', + name: 'list_files', + input: { path: '/tmp' }, + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(1); + expect(result.messages[0].role).toBe('assistant'); + expect(result.messages[0].content).toBe('Inspecting the workspace.'); + expect(result.messages[0].tool_calls).toEqual([ + { + id: 'call_999', + type: 'function', + function: { name: 'list_files', arguments: '{"path":"/tmp"}' }, + }, + ]); + }); + + it('should synthesize fallback ids for tool_use blocks that omit them', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: [ + { + type: 'tool_use', + name: 'search_docs', + input: { q: 'cursor' }, + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(1); + expect(result.messages[0].tool_calls).toHaveLength(1); + expect(result.messages[0].tool_calls![0].id).toBe('toolu_cursor_fallback_0_0'); + }); + + it('should dedupe tool calls when assistant messages contain both tool_calls and tool_use blocks', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: [ + { + type: 'tool_use', + id: 'call_dupe', + name: 'search_docs', + input: { q: 'cursor' }, + }, + ], + tool_calls: [ + { + id: 'call_dupe', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(1); + expect(result.messages[0].tool_calls).toEqual([ + { + id: 'call_dupe', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + ]); + }); + + it('should truncate oversized tool result payloads', () => { + const oversizedResult = '&'.repeat(12_050); + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_big', + type: 'function', + function: { name: 'read_file', arguments: '{"path":"big.txt"}' }, + }, + ], + }, + { + role: 'tool', + content: oversizedResult, + tool_call_id: 'call_big', + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(2); + expect(result.messages[1].content).toContain('[truncated '); + expect(result.messages[1].content.length).toBeLessThan(12_250); + }); + + it('should mark unserializable structured tool results explicitly', () => { + const circular: Record = {}; + circular.self = circular; + + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_bad', + type: 'function', + function: { name: 'read_json', arguments: '{"path":"bad.json"}' }, + }, + ], + }, + { + role: 'user', + content: [ + { + type: 'tool_result', + tool_use_id: 'call_bad', + content: circular, + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(2); + expect(result.messages[1].content).toContain('[unserializable content]'); }); it('should handle array content format', () => { @@ -344,6 +590,57 @@ describe('Request Encoding', () => { expect(result).toBeInstanceOf(Uint8Array); expect(result.length).toBeGreaterThan(0); }); + + it('should preserve flattened tool_result blocks through protobuf encoding', () => { + const translated = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_wire', + type: 'function', + function: { name: 'read_file', arguments: '{"path":"notes.txt"}' }, + }, + ], + }, + { + role: 'tool', + content: 'workspace snapshot', + tool_call_id: 'call_wire', + }, + ], + }, + false, + {} + ); + + const body = generateCursorBody(translated.messages, 'gpt-4', [], null); + const frame = parseConnectRPCFrame(Buffer.from(body)); + expect(frame).not.toBeNull(); + + const topLevel = decodeMessage(frame!.payload); + const requestPayload = topLevel.get(FIELD.Request.REQUEST)?.[0]?.value as Uint8Array; + const chatRequest = decodeMessage(requestPayload); + const encodedMessages = (chatRequest.get(FIELD.Chat.MESSAGES) || []).map((entry) => + decodeMessage(entry.value as Uint8Array) + ); + const decoder = new TextDecoder(); + const contents = encodedMessages.map((message) => + decoder.decode(message.get(FIELD.Message.CONTENT)?.[0]?.value as Uint8Array) + ); + + expect(contents.some((content) => content.includes(''))).toBe(true); + expect(contents.some((content) => content.includes('read_file'))).toBe( + true + ); + expect( + contents.some((content) => content.includes('call_wire')) + ).toBe(true); + }); }); describe('Edge cases', () => { From 27f6a675be6f97d6ae41e2729f2146c9b5c8ad9f Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Wed, 1 Apr 2026 20:57:19 -0400 Subject: [PATCH 02/48] fix(cursor): address review feedback and translator edge cases --- src/commands/cursor-command-display.ts | 4 +- src/commands/cursor-command.ts | 2 +- src/commands/help-command.ts | 7 +- src/cursor/cursor-translator.ts | 102 ++++++++--- src/utils/config-manager.ts | 9 + .../unit/commands/help-command-parity.test.ts | 9 + tests/unit/cursor/cursor-daemon.test.ts | 75 +++++++- tests/unit/cursor/cursor-protobuf.test.ts | 168 +++++++++++++++++- 8 files changed, 338 insertions(+), 38 deletions(-) diff --git a/src/commands/cursor-command-display.ts b/src/commands/cursor-command-display.ts index 5e1daf57..5bc1f4d8 100644 --- a/src/commands/cursor-command-display.ts +++ b/src/commands/cursor-command-display.ts @@ -1,5 +1,6 @@ import type { CursorAuthStatus, CursorDaemonStatus, CursorModel } from '../cursor/types'; import type { CursorConfig } from '../config/unified-config-types'; +import { getCcsDirDisplay } from '../utils/config-manager'; import { color } from '../utils/ui'; function printLines(lines: string[]): void { @@ -47,6 +48,7 @@ export function renderCursorStatus( daemonStatus: CursorDaemonStatus ): void { const localBaseUrl = `http://127.0.0.1:${cursorConfig.port}`; + const dirDisplay = getCcsDirDisplay(); const isReady = cursorConfig.enabled && authStatus.authenticated && !authStatus.expired && daemonStatus.running; @@ -90,7 +92,7 @@ export function renderCursorStatus( console.log(` Models route: ${localBaseUrl}/v1/models`); console.log(''); console.log('Client setup:'); - console.log(' Raw settings: ~/.ccs/cursor.settings.json'); + console.log(` Raw settings: ${dirDisplay}/cursor.settings.json`); console.log(' Subcommands: ccs cursor help'); if (isReady) { diff --git a/src/commands/cursor-command.ts b/src/commands/cursor-command.ts index 05a9245b..b103b735 100644 --- a/src/commands/cursor-command.ts +++ b/src/commands/cursor-command.ts @@ -1,7 +1,7 @@ /** * Cursor CLI Command * - * Handles `ccs cursor ` commands. + * Handles `ccs cursor [subcommand]` commands. */ import { diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index a30e6829..494f6779 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { initUI, box, color, dim, sectionHeader, subheader } from '../utils/ui'; import { isUnifiedMode } from '../config/unified-config-loader'; -import { getCcsDir, getCcsDirSource } from '../utils/config-manager'; +import { getCcsDirDisplay } from '../utils/config-manager'; import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { getOfficialChannelsSupportMessage } from '../channels/official-channels-runtime'; @@ -129,8 +129,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); writeLine(''); // Resolve display path for dynamic sections - const [dirSource] = getCcsDirSource(); - const dirDisplay = dirSource === 'default' ? '~/.ccs' : getCcsDir(); + const dirDisplay = getCcsDirDisplay(); // Usage section writeLine(subheader('Usage:')); @@ -288,7 +287,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); 'Auto-detects token from Cursor installation', ], [ - ['ccs cursor ', 'Use Cursor IDE integration'], + ['ccs cursor', 'Cursor status + local daemon connection details'], ['ccs cursor auth', 'Import Cursor token'], ['ccs cursor auth --manual --token --machine-id ', 'Manual token import'], ['ccs cursor status', 'Show connection status'], diff --git a/src/cursor/cursor-translator.ts b/src/cursor/cursor-translator.ts index fa6617c7..72112568 100644 --- a/src/cursor/cursor-translator.ts +++ b/src/cursor/cursor-translator.ts @@ -55,6 +55,7 @@ interface OpenAIRequestBody { const MAX_TOOL_RESULT_CHARS = 12_000; const TOOL_RESULT_SERIALIZATION_FALLBACK = '[unserializable content]'; const TOOL_USE_ARGUMENTS_FALLBACK = '{}'; +const TOOL_CALL_ID_PATTERN = /^[a-zA-Z0-9_-]+$/; function isTextPart(part: OpenAIContentPart): part is OpenAITextPart { return part.type === 'text'; @@ -68,19 +69,19 @@ function isToolResultPart(part: OpenAIContentPart): part is OpenAIToolResultPart return part.type === 'tool_result'; } -function extractTextContent(content: OpenAIMessage['content']): string { +function extractTextContent(content: OpenAIMessage['content'], separator = ''): string { if (typeof content === 'string') { return content; } - let text = ''; + const parts: string[] = []; for (const part of content) { if (isTextPart(part) && part.text) { - text += part.text; + parts.push(part.text); } } - return text; + return parts.join(separator); } function stringifyUnknown(value: unknown, fallback = ''): string { @@ -101,16 +102,16 @@ function truncateToolResultText(text: string): string { return text; } - let suffix = '\n[truncated]'; - let keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); - let omittedChars = text.length - keepLength; - - suffix = `\n[truncated ${omittedChars} chars]`; - keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); - omittedChars = text.length - keepLength; - suffix = `\n[truncated ${omittedChars} chars]`; - - return `${text.slice(0, keepLength)}${suffix}`; + let omittedChars = text.length - MAX_TOOL_RESULT_CHARS; + while (true) { + const suffix = `\n[truncated ${omittedChars} chars]`; + const keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); + const nextOmittedChars = text.length - keepLength; + if (nextOmittedChars === omittedChars) { + return `${text.slice(0, keepLength)}${suffix}`; + } + omittedChars = nextOmittedChars; + } } function escapeXml(text: string): string { @@ -133,6 +134,19 @@ function normalizeToolCallId(toolCallId: string | undefined): string { return typeof toolCallId === 'string' ? toolCallId.split('\n')[0] : ''; } +function sanitizeToolCallId(toolCallId: string | undefined): string { + const normalizedId = normalizeToolCallId(toolCallId).trim(); + if (!normalizedId) { + return ''; + } + + if (TOOL_CALL_ID_PATTERN.test(normalizedId)) { + return normalizedId; + } + + return normalizedId.replace(/[^a-zA-Z0-9_-]/g, ''); +} + function extractToolResultText(content: unknown): string { if (content === undefined) { return ''; @@ -146,7 +160,7 @@ function extractToolResultText(content: unknown): string { return content .filter(isTextPart) .map((part) => part.text || '') - .join(''); + .join('\n'); } return stringifyUnknown(content, TOOL_RESULT_SERIALIZATION_FALLBACK); @@ -161,9 +175,34 @@ function resolveToolUseId( messageIndex: number, partIndex: number ): string { - return typeof part.id === 'string' && part.id.length > 0 - ? part.id - : createFallbackToolUseId(messageIndex, partIndex); + const sanitizedId = sanitizeToolCallId(part.id); + return sanitizedId || createFallbackToolUseId(messageIndex, partIndex); +} + +function requireToolResultId(toolCallId: string | undefined, location: string): string { + const sanitizedId = sanitizeToolCallId(toolCallId); + if (sanitizedId) { + return sanitizedId; + } + + throw new Error(`${location} must include a valid tool result id`); +} + +function normalizeAssistantToolCalls( + toolCalls: NonNullable, + messageIndex: number +): NonNullable { + return toolCalls.map((toolCall, toolCallIndex) => ({ + ...toolCall, + id: sanitizeToolCallId(toolCall.id) || createFallbackToolUseId(messageIndex, toolCallIndex), + function: { + name: toolCall.function?.name || 'tool', + arguments: + typeof toolCall.function?.arguments === 'string' + ? toolCall.function.arguments + : stringifyUnknown(toolCall.function?.arguments ?? {}, TOOL_USE_ARGUMENTS_FALLBACK), + }, + })); } function rememberToolCallMeta( @@ -259,7 +298,8 @@ function mergeAssistantToolCalls( function renderUserContent( content: OpenAIMessage['content'], - toolCallMetaMap: Map + toolCallMetaMap: Map, + messageIndex: number ): string { if (typeof content === 'string') { return content; @@ -267,7 +307,8 @@ function renderUserContent( const parts: string[] = []; let textBuffer = ''; - for (const part of content) { + for (let partIndex = 0; partIndex < content.length; partIndex++) { + const part = content[partIndex]; if (isTextPart(part) && part.text) { textBuffer += part.text; continue; @@ -282,7 +323,10 @@ function renderUserContent( textBuffer = ''; } - const toolCallId = part.tool_use_id || ''; + const toolCallId = requireToolResultId( + part.tool_use_id, + `messages[${messageIndex}].content[${partIndex}]` + ); const normalizedId = normalizeToolCallId(toolCallId); const toolName = toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedId)?.name || 'tool'; @@ -317,7 +361,10 @@ function convertMessages(messages: OpenAIMessage[]): CursorMessage[] { } if (msg.role === 'tool') { - const toolCallId = msg.tool_call_id || ''; + const toolCallId = requireToolResultId( + msg.tool_call_id, + `messages[${messageIndex}].tool_call_id` + ); const normalizedToolCallId = normalizeToolCallId(toolCallId); const rememberedToolName = toolCallMetaMap.get(toolCallId)?.name || toolCallMetaMap.get(normalizedToolCallId)?.name; @@ -325,19 +372,20 @@ function convertMessages(messages: OpenAIMessage[]): CursorMessage[] { result.push({ role: 'user', - content: buildToolResultBlock(toolName, toolCallId, extractTextContent(msg.content)), + content: buildToolResultBlock(toolName, toolCallId, extractTextContent(msg.content, '\n')), }); continue; } if (msg.role === 'user' || msg.role === 'assistant') { if (msg.role === 'assistant') { + const normalizedToolCalls = normalizeAssistantToolCalls(msg.tool_calls || [], messageIndex); const assistantToolCalls = mergeAssistantToolCalls( - msg.tool_calls || [], + normalizedToolCalls, extractToolCallsFromContent(msg.content, messageIndex) ); - if (msg.tool_calls?.length) { - rememberToolCallMeta(toolCallMetaMap, msg.tool_calls); + if (normalizedToolCalls.length > 0) { + rememberToolCallMeta(toolCallMetaMap, normalizedToolCalls); } rememberToolUseParts(toolCallMetaMap, msg.content, messageIndex); @@ -355,7 +403,7 @@ function convertMessages(messages: OpenAIMessage[]): CursorMessage[] { }); } } else { - const content = renderUserContent(msg.content, toolCallMetaMap); + const content = renderUserContent(msg.content, toolCallMetaMap, messageIndex); if (content) { result.push({ role: 'user', diff --git a/src/utils/config-manager.ts b/src/utils/config-manager.ts index e20e3660..f32b83a5 100644 --- a/src/utils/config-manager.ts +++ b/src/utils/config-manager.ts @@ -102,6 +102,15 @@ export function getCcsDirSource(): [string, string] { return [r.source, r.dir]; } +/** + * Get the CCS directory as a user-facing display path. + * Keeps the default path concise while preserving explicit overrides. + */ +export function getCcsDirDisplay(): string { + const [source, dir] = getCcsDirSource(); + return source === 'default' ? '~/.ccs' : dir; +} + /** * Cloud sync folder patterns for security warning. */ diff --git a/tests/unit/commands/help-command-parity.test.ts b/tests/unit/commands/help-command-parity.test.ts index 08397323..913fe14f 100644 --- a/tests/unit/commands/help-command-parity.test.ts +++ b/tests/unit/commands/help-command-parity.test.ts @@ -88,6 +88,15 @@ describe('help command parity', () => { ).toBe(true); }); + test('root help documents bare cursor as the status entrypoint', async () => { + const lines: string[] = []; + await handleHelpCommand((line) => lines.push(line)); + + const rendered = stripAnsi(lines.join('\n')); + expect(rendered.includes('ccs cursor ')).toBe(false); + expect(rendered.includes('Cursor status + local daemon connection details')).toBe(true); + }); + test('root help explains Claude [1m] as an explicit CCS suffix with upstream limits', async () => { const lines: string[] = []; await handleHelpCommand((line) => lines.push(line)); diff --git a/tests/unit/cursor/cursor-daemon.test.ts b/tests/unit/cursor/cursor-daemon.test.ts index 8e17e2d0..1cf151ed 100644 --- a/tests/unit/cursor/cursor-daemon.test.ts +++ b/tests/unit/cursor/cursor-daemon.test.ts @@ -2,7 +2,7 @@ * Unit tests for Cursor daemon module */ -import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; +import { describe, it, expect, beforeEach, afterEach, spyOn } from 'bun:test'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; @@ -273,6 +273,33 @@ describe('stopDaemon', () => { } } }); + + it('refuses to stop when daemon ownership cannot be verified', async () => { + const killSpy = spyOn(process, 'kill').mockImplementation( + ((pid: number, signal?: NodeJS.Signals | number) => { + if (pid === process.pid && signal === 0) { + const err = new Error('EPERM') as NodeJS.ErrnoException; + err.code = 'EPERM'; + throw err; + } + + return true; + }) as typeof process.kill + ); + + writePidToFile(process.pid); + + try { + const result = await stopDaemon(); + + expect(result.success).toBe(false); + expect(result.error).toContain('unable to verify daemon ownership'); + expect(fs.existsSync(path.join(getTestCursorDir(), 'daemon.pid'))).toBe(true); + } finally { + killSpy.mockRestore(); + removePidFile(); + } + }); }); describe('handleCursorCommand', () => { @@ -366,9 +393,9 @@ describe('renderCursorStatus', () => { expect( logs.some((line) => line.includes('Anthropic base: http://127.0.0.1:20129')) ).toBe(true); - expect(logs.some((line) => line.includes('Raw settings: ~/.ccs/cursor.settings.json'))).toBe( - true - ); + expect( + logs.some((line) => line.includes(`Raw settings: ${getCcsDir()}/cursor.settings.json`)) + ).toBe(true); } finally { console.log = originalLog; } @@ -403,6 +430,46 @@ describe('renderCursorStatus', () => { console.log = originalLog; } }); + + it('falls back to ~/.ccs in status output when no CCS override is active', () => { + const originalLog = console.log; + const originalCcsHomeValue = process.env.CCS_HOME; + const logs: string[] = []; + + delete process.env.CCS_HOME; + console.log = (...args: unknown[]) => { + logs.push(args.map((arg) => String(arg)).join(' ')); + }; + + try { + renderCursorStatus( + { ...DEFAULT_CURSOR_CONFIG, enabled: true, port: 20129 }, + { + authenticated: true, + expired: false, + tokenAge: 0, + credentials: { + accessToken: 'a'.repeat(60), + machineId: '1234567890abcdef1234567890abcdef', + authMethod: 'manual', + importedAt: new Date().toISOString(), + }, + }, + { running: true, port: 20129, pid: 1234 } + ); + + expect(logs.some((line) => line.includes('Raw settings: ~/.ccs/cursor.settings.json'))).toBe( + true + ); + } finally { + if (originalCcsHomeValue !== undefined) { + process.env.CCS_HOME = originalCcsHomeValue; + } else { + delete process.env.CCS_HOME; + } + console.log = originalLog; + } + }); }); describe('renderCursorHelp', () => { diff --git a/tests/unit/cursor/cursor-protobuf.test.ts b/tests/unit/cursor/cursor-protobuf.test.ts index e2af6027..d3cdf709 100644 --- a/tests/unit/cursor/cursor-protobuf.test.ts +++ b/tests/unit/cursor/cursor-protobuf.test.ts @@ -223,7 +223,7 @@ describe('Message Translation', () => { expect(result.messages[0].tool_calls![0].function.name).toBe('get_weather'); }); - it('should accumulate tool results', () => { + it('should flatten tool results into user tool_result blocks', () => { const result = buildCursorRequest( 'gpt-4', { @@ -260,6 +260,56 @@ describe('Message Translation', () => { expect(result.messages[2]).toEqual({ role: 'user', content: 'What is the weather?' }); }); + it('should preserve consecutive tool messages as separate user turns', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_one', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + { + id: 'call_two', + type: 'function', + function: { name: 'read_file', arguments: '{"path":"README.md"}' }, + }, + ], + }, + { + role: 'tool', + content: 'first result', + tool_call_id: 'call_one', + }, + { + role: 'tool', + content: 'second result', + tool_call_id: 'call_two', + }, + { role: 'user', content: 'Summarize both results.' }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(4); + expect(result.messages[1]).toEqual({ + role: 'user', + content: expect.stringContaining('call_one'), + }); + expect(result.messages[2]).toEqual({ + role: 'user', + content: expect.stringContaining('call_two'), + }); + expect(result.messages[3]).toEqual({ role: 'user', content: 'Summarize both results.' }); + }); + it('should recover tool names for tool results without a name field', () => { const result = buildCursorRequest( 'gpt-4', @@ -332,6 +382,45 @@ describe('Message Translation', () => { expect(result.messages[1].content).toContain('{"answer":"Cursor integration ready"}'); }); + it('should preserve line breaks for multipart tool_result text content', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_lines', + type: 'function', + function: { name: 'read_file', arguments: '{"path":"notes.txt"}' }, + }, + ], + }, + { + role: 'user', + content: [ + { + type: 'tool_result', + tool_use_id: 'call_lines', + content: [ + { type: 'text', text: 'first line' }, + { type: 'text', text: 'second line' }, + ], + }, + ], + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(2); + expect(result.messages[1].content).toContain('first line\nsecond line'); + }); + it('should convert assistant tool_use content blocks into tool_calls', () => { const result = buildCursorRequest( 'gpt-4', @@ -393,6 +482,38 @@ describe('Message Translation', () => { expect(result.messages[0].tool_calls![0].id).toBe('toolu_cursor_fallback_0_0'); }); + it('should normalize invalid assistant tool call ids before emitting tool results', () => { + const result = buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'assistant', + content: '', + tool_calls: [ + { + id: 'call_bad\nline two', + type: 'function', + function: { name: 'search_docs', arguments: '{"q":"cursor"}' }, + }, + ], + }, + { + role: 'tool', + content: 'done', + tool_call_id: 'call_bad\nline two', + }, + ], + }, + false, + {} + ); + + expect(result.messages).toHaveLength(2); + expect(result.messages[0].tool_calls![0].id).toBe('call_bad'); + expect(result.messages[1].content).toContain('call_bad'); + }); + it('should dedupe tool calls when assistant messages contain both tool_calls and tool_use blocks', () => { const result = buildCursorRequest( 'gpt-4', @@ -463,6 +584,10 @@ describe('Message Translation', () => { expect(result.messages).toHaveLength(2); expect(result.messages[1].content).toContain('[truncated '); expect(result.messages[1].content.length).toBeLessThan(12_250); + + const resultMatch = result.messages[1].content.match(/([\s\S]*)<\/result>/); + expect(resultMatch).not.toBeNull(); + expect(resultMatch?.[1].length).toBeLessThanOrEqual(12_000); }); it('should mark unserializable structured tool results explicitly', () => { @@ -504,6 +629,47 @@ describe('Message Translation', () => { expect(result.messages[1].content).toContain('[unserializable content]'); }); + it('should reject tool_result blocks without a valid tool_use_id', () => { + expect(() => + buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'user', + content: [ + { + type: 'tool_result', + content: 'done', + }, + ], + }, + ], + }, + false, + {} + ) + ).toThrow('messages[0].content[0] must include a valid tool result id'); + }); + + it('should reject tool role messages without a valid tool_call_id', () => { + expect(() => + buildCursorRequest( + 'gpt-4', + { + messages: [ + { + role: 'tool', + content: 'done', + }, + ], + }, + false, + {} + ) + ).toThrow('messages[0].tool_call_id must include a valid tool result id'); + }); + it('should handle array content format', () => { const result = buildCursorRequest( 'gpt-4', From 2d67f40175705eafba2476256f171413696a89a2 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Wed, 1 Apr 2026 21:39:08 -0400 Subject: [PATCH 03/48] fix(cursor): route bare cursor through runtime profile --- README.md | 5 +- docs/cursor-integration.md | 16 +- src/auth/profile-detector.ts | 38 ++++ src/ccs.ts | 48 +++++ src/commands/cursor-command-display.ts | 12 +- src/commands/cursor-command.ts | 8 +- src/commands/help-command.ts | 2 +- src/commands/root-command-router.ts | 7 - src/cursor/cursor-profile-executor.ts | 190 ++++++++++++++++++ src/cursor/index.ts | 1 + src/shared/claude-extension-setup.ts | 72 ++++--- src/targets/target-runtime-compatibility.ts | 7 + src/types/profile.ts | 2 +- tests/unit/auth/profile-detector.test.ts | 53 +++++ .../unit/commands/help-command-parity.test.ts | 4 +- .../unit/commands/root-command-router.test.ts | 9 + tests/unit/cursor/cursor-daemon.test.ts | 16 +- .../cursor/cursor-profile-executor.test.ts | 89 ++++++++ .../target-runtime-compatibility.test.ts | 29 ++- 19 files changed, 549 insertions(+), 59 deletions(-) create mode 100644 src/cursor/cursor-profile-executor.ts create mode 100644 tests/unit/cursor/cursor-profile-executor.test.ts diff --git a/README.md b/README.md index fa1346e7..8ad79ecf 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ The dashboard provides visual management for all account types: | **Gemini** | OAuth | `ccs gemini` | Zero-config, fast iteration | | **Codex** | OAuth | `ccs codex` | Code generation | | **Copilot** | OAuth | `ccs copilot` or `ccs ghcp` | GitHub Copilot models | -| **Cursor IDE** | Local Token | `ccs cursor` | Cursor subscription models via local daemon | +| **Cursor IDE** | Local Token | `ccs cursor` | Run Claude through Cursor models via local daemon | | **Kiro** | OAuth (AWS default) | `ccs kiro` | AWS CodeWhisperer (Claude-powered) | | **Antigravity** | OAuth | `ccs agy` | Alternative routing | | **OpenRouter** | API Key | `ccs openrouter` | 300+ models, unified API | @@ -184,7 +184,7 @@ The dashboard provides visual management for all account types: ccs # Default Claude session ccs gemini # Gemini (OAuth) ccs codex # OpenAI Codex (OAuth) -ccs cursor # Cursor status + local daemon connection details +ccs cursor # Run Claude through Cursor local proxy ccs kiro # Kiro/AWS CodeWhisperer (OAuth) ccs ghcp # GitHub Copilot (OAuth device flow) ccs agy # Antigravity (OAuth) @@ -379,6 +379,7 @@ Dashboard parity: `ccs config` -> Accounts -> Add Kiro account -> choose `Auth M ccs cursor enable ccs cursor auth ccs cursor start +ccs cursor "explain this repo" ccs cursor status ``` diff --git a/docs/cursor-integration.md b/docs/cursor-integration.md index 5e190e8c..79caee5a 100644 --- a/docs/cursor-integration.md +++ b/docs/cursor-integration.md @@ -43,20 +43,26 @@ ccs cursor auth --manual --token --machine-id ccs cursor start ``` -### 4) Verify status +### 4) Run Cursor-backed Claude + +```bash +ccs cursor "explain this repo" +``` + +### 5) Verify status ```bash ccs cursor status ``` -Or use the bare command to see the same status view plus the local endpoint details -needed for OpenAI-compatible and Anthropic-compatible clients: +Use `ccs cursor` with bare or normal Claude args to run through the local Cursor proxy. +The admin namespace remains available for setup and inspection: ```bash -ccs cursor +ccs cursor help ``` -### 5) Stop daemon +### 6) Stop daemon ```bash ccs cursor stop diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index 04b132e6..d21e187c 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -16,6 +16,7 @@ import { Config, Settings, ProfileMetadata } from '../types'; import { UnifiedConfig, CopilotConfig, + CursorConfig, CLIProxyVariantConfig, CompositeVariantConfig, CompositeTierConfig, @@ -49,6 +50,8 @@ export interface ProfileDetectionResult { env?: Record; /** For copilot profile: the copilot config */ copilotConfig?: CopilotConfig; + /** For cursor profile: the cursor config */ + cursorConfig?: CursorConfig; /** For composite variants: true when variant mixes providers per tier */ isComposite?: boolean; /** For composite variants: which tier is the default */ @@ -252,6 +255,7 @@ class ProfileDetector { * Priority order: * 0. Hardcoded CLIProxy profiles (gemini, codex, agy, qwen) * 0.5. Copilot profile (if enabled in config) + * 0.75. Cursor profile (if enabled in config) * 1. Unified config profiles (if config.yaml exists or CCS_UNIFIED_CONFIG=1) * 2. User-defined CLIProxy variants (config.cliproxy section) [legacy] * 3. Settings-based profiles (config.profiles section) [legacy] @@ -302,6 +306,35 @@ class ProfileDetector { }; } + // Priority 0.75: Check Cursor profile - local Cursor daemon runtime + if (profileName === 'cursor') { + const unifiedConfig = this.readUnifiedConfig(); + const cursorConfig = unifiedConfig?.cursor; + + if (!cursorConfig?.enabled) { + const error = new Error( + 'Cursor profile is not enabled.\n\n' + + 'To enable Cursor integration:\n' + + ' 1. Run: ccs cursor enable\n' + + ' 2. Import auth: ccs cursor auth\n' + + ' 3. Start daemon: ccs cursor start\n\n' + + 'Or manually edit ~/.ccs/config.yaml:\n' + + ' cursor:\n' + + ' enabled: true' + ) as ProfileNotFoundError; + error.profileName = profileName; + error.suggestions = []; + error.availableProfiles = this.listAvailableProfiles(); + throw error; + } + + return { + type: 'cursor', + name: 'cursor', + cursorConfig, + }; + } + // Priority 1: Try unified config if available const unifiedConfig = this.readUnifiedConfig(); if (unifiedConfig) { @@ -452,6 +485,11 @@ class ProfileDetector { lines.push(` - copilot (model: ${currentCopilotModel})`); } + if (unifiedConfig.cursor?.enabled) { + lines.push('Cursor local proxy:'); + lines.push(` - cursor (model: ${unifiedConfig.cursor.model})`); + } + // CLIProxy variants from unified config const variants = Object.keys(unifiedConfig.cliproxy?.variants || {}); if (variants.length > 0) { diff --git a/src/ccs.ts b/src/ccs.ts index aafaee74..c3298493 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -450,6 +450,20 @@ async function main(): Promise { } } + // Special case: cursor command (Cursor local proxy integration) + // Route known admin subcommands to the command handler, keep all other args as profile passthrough. + if (firstArg === 'cursor' && args.length > 1) { + const { isCursorSubcommandToken, handleCursorCommand } = await import( + './commands/cursor-command' + ); + const cursorToken = args[1]; + + if (isCursorSubcommandToken(cursorToken)) { + const exitCode = await handleCursorCommand(args.slice(1)); + process.exit(exitCode); + } + } + // First-time install: offer setup wizard for interactive users // Check independently of recovery status (user may have empty config.yaml) // Skip if headless, CI, or non-TTY environment @@ -880,6 +894,40 @@ async function main(): Promise { claudeCli ); process.exit(exitCode); + } else if (profileInfo.type === 'cursor') { + // CURSOR FLOW: local Cursor daemon profile + ensureWebSearchMcpOrThrow(); + installImageAnalyzerHook(); + ensureImageAnalyzerHooks({ + profileName: profileInfo.name, + profileType: profileInfo.type, + }); + + const { executeCursorProfile } = await import('./cursor'); + const cursorConfig = profileInfo.cursorConfig; + if (!cursorConfig) { + console.error(fail('Cursor configuration not found')); + process.exit(1); + } + const continuityInheritance = await resolveProfileContinuityInheritance({ + profileName: profileInfo.name, + profileType: profileInfo.type, + target: resolvedTarget, + }); + if (continuityInheritance.sourceAccount && process.env.CCS_DEBUG) { + console.error( + info( + `Continuity inheritance active: profile "${profileInfo.name}" -> account "${continuityInheritance.sourceAccount}"` + ) + ); + } + const exitCode = await executeCursorProfile( + cursorConfig, + remainingArgs, + continuityInheritance.claudeConfigDir, + claudeCli + ); + process.exit(exitCode); } else if (profileInfo.type === 'settings') { // Settings-based profiles (glm, glmt) are third-party providers if (resolvedTarget === 'claude') { diff --git a/src/commands/cursor-command-display.ts b/src/commands/cursor-command-display.ts index 5bc1f4d8..e63c109a 100644 --- a/src/commands/cursor-command-display.ts +++ b/src/commands/cursor-command-display.ts @@ -13,7 +13,7 @@ export function renderCursorHelp(): number { printLines([ 'Cursor IDE Integration', '', - 'Usage: ccs cursor [subcommand] [options]', + 'Usage: ccs cursor ', '', 'Subcommands:', ' auth Import Cursor IDE authentication token', @@ -25,6 +25,9 @@ export function renderCursorHelp(): number { ' disable Disable cursor integration in unified config', ' help Show this help message', '', + 'Runtime entry:', + ' ccs cursor [claude args] # Run Claude via the local Cursor proxy', + '', 'Auth options:', ' ccs cursor auth # Auto-detect from Cursor SQLite', ' ccs cursor auth --manual --token --machine-id ', @@ -33,7 +36,8 @@ export function renderCursorHelp(): number { ' 1. ccs cursor enable # Enable integration', ' 2. ccs cursor auth # Import Cursor IDE token', ' 3. ccs cursor start # Start daemon', - ' 4. ccs cursor # Show status and runtime connection details', + ' 4. ccs cursor "task" # Run Claude through Cursor', + ' 5. ccs cursor status # Inspect auth/daemon wiring', '', 'Or use the web UI: ccs config -> Cursor page', '', @@ -93,7 +97,9 @@ export function renderCursorStatus( console.log(''); console.log('Client setup:'); console.log(` Raw settings: ${dirDisplay}/cursor.settings.json`); - console.log(' Subcommands: ccs cursor help'); + console.log(' Runtime entry: ccs cursor [claude args]'); + console.log(' Status command: ccs cursor status'); + console.log(' Help command: ccs cursor help'); if (isReady) { return; diff --git a/src/commands/cursor-command.ts b/src/commands/cursor-command.ts index b103b735..4c918f65 100644 --- a/src/commands/cursor-command.ts +++ b/src/commands/cursor-command.ts @@ -34,6 +34,12 @@ export const CURSOR_SUBCOMMANDS = [ '-h', ] as const; +export function isCursorSubcommandToken(token?: string): boolean { + return ( + Boolean(token) && CURSOR_SUBCOMMANDS.includes(token as (typeof CURSOR_SUBCOMMANDS)[number]) + ); +} + /** * Handle cursor subcommand. */ @@ -56,7 +62,7 @@ export async function handleCursorCommand(args: string[]): Promise { case 'disable': return handleDisable(); case undefined: - return handleStatus(); + return handleHelp(); case 'help': case '--help': case '-h': diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index 494f6779..023b149d 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -287,7 +287,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); 'Auto-detects token from Cursor installation', ], [ - ['ccs cursor', 'Cursor status + local daemon connection details'], + ['ccs cursor', 'Run Claude via Cursor local proxy'], ['ccs cursor auth', 'Import Cursor token'], ['ccs cursor auth --manual --token --machine-id ', 'Manual token import'], ['ccs cursor status', 'Show connection status'], diff --git a/src/commands/root-command-router.ts b/src/commands/root-command-router.ts index 98f4a178..450dec5b 100644 --- a/src/commands/root-command-router.ts +++ b/src/commands/root-command-router.ts @@ -172,13 +172,6 @@ const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ await handleSetupCommand(args); }, }, - { - name: 'cursor', - handle: async (args) => { - const { handleCursorCommand } = await import('./cursor-command'); - process.exit(await handleCursorCommand(args)); - }, - }, ]; export async function tryHandleRootCommand(args: string[]): Promise { diff --git a/src/cursor/cursor-profile-executor.ts b/src/cursor/cursor-profile-executor.ts new file mode 100644 index 00000000..74900445 --- /dev/null +++ b/src/cursor/cursor-profile-executor.ts @@ -0,0 +1,190 @@ +import { spawn } from 'child_process'; + +import type { CursorConfig } from '../config/unified-config-types'; +import { getGlobalEnvConfig } from '../config/unified-config-loader'; +import { ensureCliproxyService } from '../cliproxy'; +import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; +import { fail, info, ok } from '../utils/ui'; +import { + appendThirdPartyWebSearchToolArgs, + createWebSearchTraceContext, + getWebSearchHookEnv, + syncWebSearchMcpToConfigDir, +} from '../utils/websearch-manager'; +import { getImageAnalysisHookEnv, resolveImageAnalysisRuntimeStatus } from '../utils/hooks'; +import { stripClaudeCodeEnv } from '../utils/shell-executor'; +import { checkAuthStatus } from './cursor-auth'; +import { isDaemonRunning, startDaemon } from './cursor-daemon'; + +interface CursorImageAnalysisResolution { + env: Record; + warning: string | null; +} + +export function generateCursorEnv( + config: CursorConfig, + claudeConfigDir?: string +): Record { + const opusModel = config.opus_model || config.model; + const sonnetModel = config.sonnet_model || config.model; + const haikuModel = config.haiku_model || config.model; + + return { + ANTHROPIC_BASE_URL: `http://127.0.0.1:${config.port}`, + ANTHROPIC_AUTH_TOKEN: 'cursor-managed', + ANTHROPIC_MODEL: config.model, + ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel, + ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel, + ANTHROPIC_SMALL_FAST_MODEL: haikuModel, + ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel, + DISABLE_NON_ESSENTIAL_MODEL_CALLS: '1', + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1', + ...(claudeConfigDir ? { CLAUDE_CONFIG_DIR: claudeConfigDir } : {}), + }; +} + +export async function resolveCursorImageAnalysisEnv( + verbose = false +): Promise { + const env = getImageAnalysisHookEnv({ + profileName: 'cursor', + profileType: 'cursor', + }); + const provider = env['CCS_CURRENT_PROVIDER']; + if (env['CCS_IMAGE_ANALYSIS_SKIP'] === '1' || !provider) { + return { env, warning: null }; + } + + const status = await resolveImageAnalysisRuntimeStatus({ + profileName: 'cursor', + profileType: 'cursor', + }); + + if (status.effectiveRuntimeMode === 'native-read') { + return { + env: { + ...env, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }, + warning: `${status.effectiveRuntimeReason || `Image analysis via ${provider} is unavailable.`} This session will use native Read.`, + }; + } + + if (status.proxyReadiness === 'stopped') { + const ensureServiceResult = await ensureCliproxyService(CLIPROXY_DEFAULT_PORT, verbose); + if (!ensureServiceResult.started) { + return { + env: { + ...env, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }, + warning: `Image analysis via ${provider} is unavailable because CCS could not start the local CLIProxy service. This session will use native Read.`, + }; + } + } + + return { env, warning: null }; +} + +export async function executeCursorProfile( + config: CursorConfig, + claudeArgs: string[], + claudeConfigDir?: string, + claudeCliPath = 'claude' +): Promise { + if (!config.enabled) { + console.error(fail('Cursor integration is not enabled.')); + console.error(''); + console.error('Enable it first: ccs cursor enable'); + return 1; + } + + const authStatus = checkAuthStatus(); + if (!authStatus.authenticated) { + console.error(fail('Cursor credentials not found.')); + console.error(''); + console.error('Authenticate first: ccs cursor auth'); + return 1; + } + if (authStatus.expired) { + console.error(fail('Cursor credentials have expired.')); + console.error(''); + console.error('Refresh them with: ccs cursor auth'); + return 1; + } + + let daemonRunning = await isDaemonRunning(config.port); + if (!daemonRunning) { + if (config.auto_start) { + console.log(info('Starting cursor daemon...')); + const result = await startDaemon({ + port: config.port, + ghost_mode: config.ghost_mode, + }); + if (!result.success) { + console.error(fail(`Failed to start cursor daemon: ${result.error}`)); + return 1; + } + console.log(ok(`Daemon started on port ${config.port}`)); + daemonRunning = true; + } else { + console.error(fail('Cursor daemon is not running.')); + console.error(''); + console.error('Start the daemon:'); + console.error(' ccs cursor start'); + console.error('Or enable auto_start in the Cursor config section.'); + return 1; + } + } + + const cursorEnv = generateCursorEnv(config, claudeConfigDir); + const globalEnvConfig = getGlobalEnvConfig(); + const globalEnv = globalEnvConfig.enabled ? globalEnvConfig.env : {}; + const webSearchEnv = getWebSearchHookEnv(); + const { env: imageAnalysisEnv, warning: imageAnalysisWarning } = + await resolveCursorImageAnalysisEnv(); + const env = stripClaudeCodeEnv({ + ...process.env, + ...globalEnv, + ...cursorEnv, + ...webSearchEnv, + ...imageAnalysisEnv, + CCS_PROFILE_TYPE: 'cursor', + }); + + console.log(info(`Using Cursor proxy (model: ${config.model})`)); + if (imageAnalysisWarning) { + console.log(info(imageAnalysisWarning)); + } + console.log(''); + + syncWebSearchMcpToConfigDir(claudeConfigDir); + + return new Promise((resolve) => { + const launchArgs = appendThirdPartyWebSearchToolArgs(claudeArgs); + const traceEnv = createWebSearchTraceContext({ + launcher: 'cursor.executor', + args: launchArgs, + profile: 'cursor', + profileType: 'cursor', + claudeConfigDir, + }); + + const proc = spawn(claudeCliPath, launchArgs, { + stdio: 'inherit', + env: { ...env, ...traceEnv }, + shell: process.platform === 'win32', + }); + + proc.on('close', (code) => { + resolve(code ?? 0); + }); + + proc.on('error', (err) => { + console.error(fail(`Failed to start Claude: ${err.message}`)); + resolve(1); + }); + }); +} diff --git a/src/cursor/index.ts b/src/cursor/index.ts index 0322083c..42e42860 100644 --- a/src/cursor/index.ts +++ b/src/cursor/index.ts @@ -45,3 +45,4 @@ export { // Executor export { CursorExecutor } from './cursor-executor'; +export { executeCursorProfile, generateCursorEnv } from './cursor-profile-executor'; diff --git a/src/shared/claude-extension-setup.ts b/src/shared/claude-extension-setup.ts index 9f6a9385..cf04b0fc 100644 --- a/src/shared/claude-extension-setup.ts +++ b/src/shared/claude-extension-setup.ts @@ -11,6 +11,7 @@ import { import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { getProxyTarget } from '../cliproxy/proxy-target-resolver'; import { generateCopilotEnv } from '../copilot/copilot-executor'; +import { generateCursorEnv } from '../cursor'; import InstanceManager from '../management/instance-manager'; import SharedManager from '../management/shared-manager'; import { expandPath } from '../utils/helpers'; @@ -97,6 +98,7 @@ function describeProfile(profileName: string, result: ProfileDetectionResult): s if (result.type === 'account') return 'Claude account instance isolated through CLAUDE_CONFIG_DIR.'; if (result.type === 'copilot') return 'GitHub Copilot profile routed through copilot-api.'; + if (result.type === 'cursor') return 'Cursor profile routed through the local cursor daemon.'; return 'Native Claude profile resolution.'; } @@ -129,6 +131,12 @@ export function listClaudeExtensionProfiles(): ClaudeExtensionProfileOption[] { } catch { // Copilot disabled; skip from setup UI. } + try { + detector.detectProfileType('cursor'); + deduped.push('cursor'); + } catch { + // Cursor disabled; skip from setup UI. + } return deduped .map((profileName) => createProfileOption(profileName, detector.detectProfileType(profileName))) @@ -201,40 +209,47 @@ async function resolveExtensionEnv( } return generateCopilotEnv(result.copilotConfig, continuity.claudeConfigDir); })() - : (() => { - if (!result.provider) { - throw new Error( - `Profile "${requestedProfile}" is missing CLIProxy provider metadata.` - ); - } - const proxyTarget = getProxyTarget(); - const port = result.port || CLIPROXY_DEFAULT_PORT; - if (proxyTarget.isRemote) { + : result.type === 'cursor' + ? (() => { + if (!result.cursorConfig) { + throw new Error(`Profile "${requestedProfile}" is missing cursor configuration.`); + } + return generateCursorEnv(result.cursorConfig, continuity.claudeConfigDir); + })() + : (() => { + if (!result.provider) { + throw new Error( + `Profile "${requestedProfile}" is missing CLIProxy provider metadata.` + ); + } + const proxyTarget = getProxyTarget(); + const port = result.port || CLIPROXY_DEFAULT_PORT; + if (proxyTarget.isRemote) { + warnings.push( + `CLIProxy is configured for remote routing via ${proxyTarget.protocol}://${proxyTarget.host}:${proxyTarget.port}.` + ); + return result.isComposite && result.compositeTiers && result.compositeDefaultTier + ? getCompositeEnvVars( + result.compositeTiers, + result.compositeDefaultTier, + port, + result.settingsPath, + proxyTarget + ) + : getRemoteEnvVars(result.provider, proxyTarget, result.settingsPath); + } warnings.push( - `CLIProxy is configured for remote routing via ${proxyTarget.protocol}://${proxyTarget.host}:${proxyTarget.port}.` + 'CLIProxy-backed profiles require the local or remote proxy endpoint to be reachable.' ); return result.isComposite && result.compositeTiers && result.compositeDefaultTier ? getCompositeEnvVars( result.compositeTiers, result.compositeDefaultTier, port, - result.settingsPath, - proxyTarget + result.settingsPath ) - : getRemoteEnvVars(result.provider, proxyTarget, result.settingsPath); - } - warnings.push( - 'CLIProxy-backed profiles require the local or remote proxy endpoint to be reachable.' - ); - return result.isComposite && result.compositeTiers && result.compositeDefaultTier - ? getCompositeEnvVars( - result.compositeTiers, - result.compositeDefaultTier, - port, - result.settingsPath - ) - : getEffectiveEnvVars(result.provider, port, result.settingsPath); - })(); + : getEffectiveEnvVars(result.provider, port, result.settingsPath); + })(); if (result.type === 'settings' && isDeprecatedGlmtProfileName(requestedProfile)) { const normalized = normalizeDeprecatedGlmtEnv(sortEnvRecord(env)); @@ -257,6 +272,11 @@ async function resolveExtensionEnv( 'copilot-api must stay reachable for this profile to work inside the IDE extension.' ); } + if (result.type === 'cursor') { + warnings.push( + 'The local Cursor daemon must stay reachable for this profile to work inside the IDE extension.' + ); + } if (Object.keys(env).length === 0) { throw new Error(`Profile "${requestedProfile}" has no extension environment to export.`); } diff --git a/src/targets/target-runtime-compatibility.ts b/src/targets/target-runtime-compatibility.ts index 9c9a83ca..627479cf 100644 --- a/src/targets/target-runtime-compatibility.ts +++ b/src/targets/target-runtime-compatibility.ts @@ -37,6 +37,9 @@ export function evaluateTargetRuntimeCompatibility( if (input.profileType === 'copilot') { return unsupported('Factory Droid does not support Copilot profiles.'); } + if (input.profileType === 'cursor') { + return unsupported('Factory Droid does not support Cursor local-proxy profiles.'); + } return { supported: true }; } @@ -51,6 +54,10 @@ export function evaluateTargetRuntimeCompatibility( return unsupported('Codex CLI does not support Copilot profiles.'); } + if (input.profileType === 'cursor') { + return unsupported('Codex CLI does not support Cursor local-proxy profiles.'); + } + if (input.profileType === 'default') { return { supported: true }; } diff --git a/src/types/profile.ts b/src/types/profile.ts index 75e3ff44..9d2472d8 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -1,4 +1,4 @@ /** * Profile mode types used across routing and target adapters. */ -export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'copilot' | 'default'; +export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'copilot' | 'cursor' | 'default'; diff --git a/tests/unit/auth/profile-detector.test.ts b/tests/unit/auth/profile-detector.test.ts index c8a2f324..f46f2319 100644 --- a/tests/unit/auth/profile-detector.test.ts +++ b/tests/unit/auth/profile-detector.test.ts @@ -195,5 +195,58 @@ describe('ProfileDetector', () => { existsSyncSpy.mockRestore(); } }); + + it('should detect cursor as a first-class runtime profile when enabled', () => { + const mockUnifiedConfig = { + version: 2, + cursor: { + enabled: true, + port: 20129, + auto_start: true, + ghost_mode: true, + model: 'gpt-5.3-codex', + }, + }; + + const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true); + const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue( + mockUnifiedConfig as any + ); + + try { + const result = detector.detectProfileType('cursor'); + expect(result.type).toBe('cursor'); + expect(result.name).toBe('cursor'); + expect(result.cursorConfig?.auto_start).toBe(true); + } finally { + isUnifiedModeSpy.mockRestore(); + loadUnifiedConfigSpy.mockRestore(); + } + }); + + it('should throw a helpful error when cursor profile is disabled', () => { + const mockUnifiedConfig = { + version: 2, + cursor: { + enabled: false, + port: 20129, + auto_start: false, + ghost_mode: true, + model: 'gpt-5.3-codex', + }, + }; + + const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true); + const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue( + mockUnifiedConfig as any + ); + + try { + expect(() => detector.detectProfileType('cursor')).toThrow(/Cursor profile is not enabled/); + } finally { + isUnifiedModeSpy.mockRestore(); + loadUnifiedConfigSpy.mockRestore(); + } + }); }); }); diff --git a/tests/unit/commands/help-command-parity.test.ts b/tests/unit/commands/help-command-parity.test.ts index 913fe14f..7ad072ae 100644 --- a/tests/unit/commands/help-command-parity.test.ts +++ b/tests/unit/commands/help-command-parity.test.ts @@ -88,13 +88,13 @@ describe('help command parity', () => { ).toBe(true); }); - test('root help documents bare cursor as the status entrypoint', async () => { + test('root help documents bare cursor as the runtime entrypoint', async () => { const lines: string[] = []; await handleHelpCommand((line) => lines.push(line)); const rendered = stripAnsi(lines.join('\n')); expect(rendered.includes('ccs cursor ')).toBe(false); - expect(rendered.includes('Cursor status + local daemon connection details')).toBe(true); + expect(rendered.includes('Run Claude via Cursor local proxy')).toBe(true); }); test('root help explains Claude [1m] as an explicit CCS suffix with upstream limits', async () => { diff --git a/tests/unit/commands/root-command-router.test.ts b/tests/unit/commands/root-command-router.test.ts index 50314f95..7e32630e 100644 --- a/tests/unit/commands/root-command-router.test.ts +++ b/tests/unit/commands/root-command-router.test.ts @@ -74,6 +74,15 @@ describe('root-command-router', () => { expect(calls).toEqual([]); }); + it('does not capture cursor so bare cursor can fall through to profile routing', async () => { + const tryHandleRootCommand = await loadTryHandleRootCommand(); + + await expect(tryHandleRootCommand(['cursor'])).resolves.toBe(false); + await expect(tryHandleRootCommand(['cursor', 'status'])).resolves.toBe(false); + + expect(calls).toEqual([]); + }); + it('prints update help without invoking the updater', async () => { const tryHandleRootCommand = await loadTryHandleRootCommand(); diff --git a/tests/unit/cursor/cursor-daemon.test.ts b/tests/unit/cursor/cursor-daemon.test.ts index 1cf151ed..bcfc301a 100644 --- a/tests/unit/cursor/cursor-daemon.test.ts +++ b/tests/unit/cursor/cursor-daemon.test.ts @@ -303,7 +303,7 @@ describe('stopDaemon', () => { }); describe('handleCursorCommand', () => { - it('treats bare ccs cursor as a status entrypoint instead of help', async () => { + it('shows help when invoked without an admin subcommand', async () => { const originalLog = console.log; const originalError = console.error; const logs: string[] = []; @@ -321,10 +321,8 @@ describe('handleCursorCommand', () => { expect(exitCode).toBe(0); expect(errors).toHaveLength(0); - expect(logs.some((line) => line.includes('Cursor IDE Status'))).toBe(true); - expect(logs.some((line) => line.includes('Usage: ccs cursor [options]'))).toBe( - false - ); + expect(logs.some((line) => line.includes('Cursor IDE Integration'))).toBe(true); + expect(logs.some((line) => line.includes('Usage: ccs cursor '))).toBe(true); } finally { console.log = originalLog; console.error = originalError; @@ -473,7 +471,7 @@ describe('renderCursorStatus', () => { }); describe('renderCursorHelp', () => { - it('shows bare ccs cursor as an optional entrypoint', () => { + it('shows bare ccs cursor as the runtime entrypoint', () => { const originalLog = console.log; const logs: string[] = []; @@ -485,11 +483,9 @@ describe('renderCursorHelp', () => { const exitCode = renderCursorHelp(); expect(exitCode).toBe(0); - expect(logs.some((line) => line.includes('Usage: ccs cursor [subcommand] [options]'))).toBe( - true - ); + expect(logs.some((line) => line.includes('Usage: ccs cursor '))).toBe(true); expect( - logs.some((line) => line.includes('4. ccs cursor # Show status and runtime connection details')) + logs.some((line) => line.includes('ccs cursor [claude args]')) ).toBe(true); } finally { console.log = originalLog; diff --git a/tests/unit/cursor/cursor-profile-executor.test.ts b/tests/unit/cursor/cursor-profile-executor.test.ts new file mode 100644 index 00000000..e655c882 --- /dev/null +++ b/tests/unit/cursor/cursor-profile-executor.test.ts @@ -0,0 +1,89 @@ +import { afterEach, beforeEach, describe, expect, it } from 'bun:test'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +import { + executeCursorProfile, + generateCursorEnv, +} from '../../../src/cursor/cursor-profile-executor'; +import { saveCredentials } from '../../../src/cursor/cursor-auth'; +import type { CursorConfig } from '../../../src/config/unified-config-types'; + +const BASE_CONFIG: CursorConfig = { + enabled: true, + port: 20129, + auto_start: false, + ghost_mode: true, + model: 'gpt-5.3-codex', +}; + +describe('cursor-profile-executor', () => { + let originalCcsHome: string | undefined; + let tempDir: string; + + beforeEach(() => { + originalCcsHome = process.env.CCS_HOME; + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-cursor-profile-executor-')); + process.env.CCS_HOME = tempDir; + }); + + afterEach(() => { + if (originalCcsHome !== undefined) { + process.env.CCS_HOME = originalCcsHome; + } else { + delete process.env.CCS_HOME; + } + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + it('builds Cursor env for Claude runtime', () => { + const env = generateCursorEnv( + { + ...BASE_CONFIG, + opus_model: 'cursor-opus', + sonnet_model: 'cursor-sonnet', + haiku_model: 'cursor-haiku', + }, + '/tmp/claude-config' + ); + + expect(env.ANTHROPIC_BASE_URL).toBe('http://127.0.0.1:20129'); + expect(env.ANTHROPIC_AUTH_TOKEN).toBe('cursor-managed'); + expect(env.ANTHROPIC_MODEL).toBe('gpt-5.3-codex'); + expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('cursor-opus'); + expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('cursor-sonnet'); + expect(env.ANTHROPIC_DEFAULT_HAIKU_MODEL).toBe('cursor-haiku'); + expect(env.CLAUDE_CONFIG_DIR).toBe('/tmp/claude-config'); + }); + + it('fails fast when Cursor integration is disabled', async () => { + const exitCode = await executeCursorProfile({ ...BASE_CONFIG, enabled: false }, []); + expect(exitCode).toBe(1); + }); + + it('fails when credentials are missing', async () => { + const exitCode = await executeCursorProfile(BASE_CONFIG, []); + expect(exitCode).toBe(1); + }); + + it('fails with actionable guidance when daemon is down and auto_start is false', async () => { + saveCredentials({ + accessToken: 'a'.repeat(60), + machineId: '1234567890abcdef1234567890abcdef', + authMethod: 'manual', + importedAt: new Date().toISOString(), + }); + + const exitCode = await executeCursorProfile( + { + ...BASE_CONFIG, + port: 29991, + auto_start: false, + }, + [] + ); + + expect(exitCode).toBe(1); + }); +}); diff --git a/tests/unit/targets/target-runtime-compatibility.test.ts b/tests/unit/targets/target-runtime-compatibility.test.ts index e75a0600..60a2d1fa 100644 --- a/tests/unit/targets/target-runtime-compatibility.test.ts +++ b/tests/unit/targets/target-runtime-compatibility.test.ts @@ -3,6 +3,27 @@ import { describe, expect, test } from 'bun:test'; import { evaluateTargetRuntimeCompatibility } from '../../../src/targets/target-runtime-compatibility'; describe('evaluateTargetRuntimeCompatibility', () => { + test('rejects account, copilot, and cursor profiles on Droid target', () => { + expect( + evaluateTargetRuntimeCompatibility({ + target: 'droid', + profileType: 'account', + }).supported + ).toBe(false); + expect( + evaluateTargetRuntimeCompatibility({ + target: 'droid', + profileType: 'copilot', + }).supported + ).toBe(false); + expect( + evaluateTargetRuntimeCompatibility({ + target: 'droid', + profileType: 'cursor', + }).supported + ).toBe(false); + }); + test('supports native Codex default sessions', () => { expect( evaluateTargetRuntimeCompatibility({ @@ -69,7 +90,7 @@ describe('evaluateTargetRuntimeCompatibility', () => { expect(genericSettingsCompatibility.reason).toMatch(/currently supports native default sessions/); }); - test('rejects account and copilot profiles on Codex target', () => { + test('rejects account, copilot, and cursor profiles on Codex target', () => { expect( evaluateTargetRuntimeCompatibility({ target: 'codex', @@ -82,5 +103,11 @@ describe('evaluateTargetRuntimeCompatibility', () => { profileType: 'copilot', }).supported ).toBe(false); + expect( + evaluateTargetRuntimeCompatibility({ + target: 'codex', + profileType: 'cursor', + }).supported + ).toBe(false); }); }); From f7ddad6c19353ae3b32e82541526a2149160ee36 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 03:21:38 -0400 Subject: [PATCH 04/48] fix(cursor): harden runtime follow-up regressions --- src/auth/profile-detector.ts | 5 +- src/cursor/cursor-translator.ts | 3 +- .../hooks/image-analysis-backend-resolver.ts | 10 +++ tests/unit/auth/profile-detector.test.ts | 68 ++++++++++++------- .../cursor/cursor-profile-executor.test.ts | 9 +++ tests/unit/cursor/cursor-protobuf.test.ts | 25 ++++++- .../image-analysis-backend-resolver.test.ts | 38 +++++++++++ 7 files changed, 126 insertions(+), 32 deletions(-) diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index d21e187c..a67f8d98 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -21,7 +21,7 @@ import { CompositeVariantConfig, CompositeTierConfig, } from '../config/unified-config-types'; -import { loadUnifiedConfig, isUnifiedMode } from '../config/unified-config-loader'; +import { loadUnifiedConfig, isUnifiedMode, getCursorConfig } from '../config/unified-config-loader'; import { getCcsDir } from '../utils/config-manager'; import { getProfileLookupCandidates, isLegacyProfileAlias } from '../utils/profile-compat'; import type { CLIProxyProvider } from '../cliproxy/types'; @@ -308,8 +308,7 @@ class ProfileDetector { // Priority 0.75: Check Cursor profile - local Cursor daemon runtime if (profileName === 'cursor') { - const unifiedConfig = this.readUnifiedConfig(); - const cursorConfig = unifiedConfig?.cursor; + const cursorConfig = getCursorConfig(); if (!cursorConfig?.enabled) { const error = new Error( diff --git a/src/cursor/cursor-translator.ts b/src/cursor/cursor-translator.ts index 72112568..c3c078da 100644 --- a/src/cursor/cursor-translator.ts +++ b/src/cursor/cursor-translator.ts @@ -119,7 +119,8 @@ function escapeXml(text: string): string { } function buildToolResultBlock(toolName: string, toolCallId: string, resultText: string): string { - const cleanResult = truncateToolResultText(escapeXml(sanitizeToolResultText(resultText))); + // Truncate raw tool output before XML escaping so the cap reflects original content. + const cleanResult = escapeXml(truncateToolResultText(sanitizeToolResultText(resultText))); return [ '', diff --git a/src/utils/hooks/image-analysis-backend-resolver.ts b/src/utils/hooks/image-analysis-backend-resolver.ts index ec7921e4..2acae204 100644 --- a/src/utils/hooks/image-analysis-backend-resolver.ts +++ b/src/utils/hooks/image-analysis-backend-resolver.ts @@ -396,6 +396,16 @@ function resolveBackend( }; } + if (profileType === 'cursor' || profileName === 'cursor') { + return { + backendId: null, + backendDisplayName: null, + resolutionSource: 'unresolved', + reason: + 'Cursor image analysis does not inherit the global fallback backend. Set image_analysis.profile_backends.cursor to an explicit provider to enable transformer-backed image analysis.', + }; + } + if (profileType === 'copilot' || profileName === 'copilot') { const backendId = normalizeImageAnalysisBackendId('ghcp', Object.keys(config.provider_models)); return { diff --git a/tests/unit/auth/profile-detector.test.ts b/tests/unit/auth/profile-detector.test.ts index f46f2319..84d57415 100644 --- a/tests/unit/auth/profile-detector.test.ts +++ b/tests/unit/auth/profile-detector.test.ts @@ -197,21 +197,14 @@ describe('ProfileDetector', () => { }); it('should detect cursor as a first-class runtime profile when enabled', () => { - const mockUnifiedConfig = { - version: 2, - cursor: { - enabled: true, - port: 20129, - auto_start: true, - ghost_mode: true, - model: 'gpt-5.3-codex', - }, - }; - const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true); - const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue( - mockUnifiedConfig as any - ); + const getCursorConfigSpy = spyOn(unifiedConfigLoader, 'getCursorConfig').mockReturnValue({ + enabled: true, + port: 20129, + auto_start: true, + ghost_mode: true, + model: 'gpt-5.3-codex', + }); try { const result = detector.detectProfileType('cursor'); @@ -220,32 +213,55 @@ describe('ProfileDetector', () => { expect(result.cursorConfig?.auto_start).toBe(true); } finally { isUnifiedModeSpy.mockRestore(); - loadUnifiedConfigSpy.mockRestore(); + getCursorConfigSpy.mockRestore(); } }); - it('should throw a helpful error when cursor profile is disabled', () => { - const mockUnifiedConfig = { - version: 2, - cursor: { - enabled: false, + it('should merge default cursor fields when enabled via partial unified config', () => { + const originalCcsHome = process.env.CCS_HOME; + process.env.CCS_HOME = tempDir; + const ccsDir = path.join(tempDir, '.ccs'); + fs.mkdirSync(ccsDir, { recursive: true }); + fs.writeFileSync( + path.join(ccsDir, 'config.yaml'), + ['version: 12', 'cursor:', ' enabled: true'].join('\n') + ); + + try { + const localDetector = new ProfileDetector(); + const result = localDetector.detectProfileType('cursor'); + expect(result.type).toBe('cursor'); + expect(result.cursorConfig).toEqual({ + enabled: true, port: 20129, auto_start: false, ghost_mode: true, model: 'gpt-5.3-codex', - }, - }; + }); + } finally { + if (originalCcsHome !== undefined) { + process.env.CCS_HOME = originalCcsHome; + } else { + delete process.env.CCS_HOME; + } + } + }); + it('should throw a helpful error when cursor profile is disabled', () => { const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true); - const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue( - mockUnifiedConfig as any - ); + const getCursorConfigSpy = spyOn(unifiedConfigLoader, 'getCursorConfig').mockReturnValue({ + enabled: false, + port: 20129, + auto_start: false, + ghost_mode: true, + model: 'gpt-5.3-codex', + }); try { expect(() => detector.detectProfileType('cursor')).toThrow(/Cursor profile is not enabled/); } finally { isUnifiedModeSpy.mockRestore(); - loadUnifiedConfigSpy.mockRestore(); + getCursorConfigSpy.mockRestore(); } }); }); diff --git a/tests/unit/cursor/cursor-profile-executor.test.ts b/tests/unit/cursor/cursor-profile-executor.test.ts index e655c882..c0c2a1fc 100644 --- a/tests/unit/cursor/cursor-profile-executor.test.ts +++ b/tests/unit/cursor/cursor-profile-executor.test.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import { executeCursorProfile, generateCursorEnv, + resolveCursorImageAnalysisEnv, } from '../../../src/cursor/cursor-profile-executor'; import { saveCredentials } from '../../../src/cursor/cursor-auth'; import type { CursorConfig } from '../../../src/config/unified-config-types'; @@ -57,6 +58,14 @@ describe('cursor-profile-executor', () => { expect(env.CLAUDE_CONFIG_DIR).toBe('/tmp/claude-config'); }); + it('skips image-analysis provider routing for cursor unless explicitly mapped', async () => { + const { env, warning } = await resolveCursorImageAnalysisEnv(); + + expect(env.CCS_CURRENT_PROVIDER).toBe(''); + expect(env.CCS_IMAGE_ANALYSIS_SKIP).toBe('1'); + expect(warning).toBeNull(); + }); + it('fails fast when Cursor integration is disabled', async () => { const exitCode = await executeCursorProfile({ ...BASE_CONFIG, enabled: false }, []); expect(exitCode).toBe(1); diff --git a/tests/unit/cursor/cursor-protobuf.test.ts b/tests/unit/cursor/cursor-protobuf.test.ts index d3cdf709..c2bdbd3b 100644 --- a/tests/unit/cursor/cursor-protobuf.test.ts +++ b/tests/unit/cursor/cursor-protobuf.test.ts @@ -22,6 +22,25 @@ import { CursorExecutor } from '../../../src/cursor/cursor-executor'; import { WIRE_TYPE, FIELD } from '../../../src/cursor/cursor-protobuf-schema'; import { StreamingFrameParser, decompressPayload } from '../../../src/cursor/cursor-stream-parser'; +const MAX_TOOL_RESULT_CHARS = 12_000; + +function computeExpectedToolResultOmittedChars(textLength: number): number { + if (textLength <= MAX_TOOL_RESULT_CHARS) { + return 0; + } + + let omittedChars = textLength - MAX_TOOL_RESULT_CHARS; + while (true) { + const suffix = `\n[truncated ${omittedChars} chars]`; + const keepLength = Math.max(MAX_TOOL_RESULT_CHARS - suffix.length, 0); + const nextOmittedChars = textLength - keepLength; + if (nextOmittedChars === omittedChars) { + return omittedChars; + } + omittedChars = nextOmittedChars; + } +} + describe('Protobuf Encoding/Decoding', () => { describe('encodeVarint / decodeVarint round-trip', () => { it('should encode and decode 0', () => { @@ -555,6 +574,8 @@ describe('Message Translation', () => { it('should truncate oversized tool result payloads', () => { const oversizedResult = '&'.repeat(12_050); + const omittedChars = computeExpectedToolResultOmittedChars(oversizedResult.length); + const preservedChars = oversizedResult.length - omittedChars; const result = buildCursorRequest( 'gpt-4', { @@ -583,11 +604,11 @@ describe('Message Translation', () => { expect(result.messages).toHaveLength(2); expect(result.messages[1].content).toContain('[truncated '); - expect(result.messages[1].content.length).toBeLessThan(12_250); + expect(result.messages[1].content).toContain(`[truncated ${omittedChars} chars]`); const resultMatch = result.messages[1].content.match(/([\s\S]*)<\/result>/); expect(resultMatch).not.toBeNull(); - expect(resultMatch?.[1].length).toBeLessThanOrEqual(12_000); + expect((resultMatch?.[1].match(/&/g) ?? []).length).toBe(preservedChars); }); it('should mark unserializable structured tool results explicitly', () => { diff --git a/tests/unit/utils/hooks/image-analysis-backend-resolver.test.ts b/tests/unit/utils/hooks/image-analysis-backend-resolver.test.ts index 84d1ffdb..7525e7d3 100644 --- a/tests/unit/utils/hooks/image-analysis-backend-resolver.test.ts +++ b/tests/unit/utils/hooks/image-analysis-backend-resolver.test.ts @@ -44,6 +44,44 @@ describe('image-analysis-backend-resolver', () => { expect(status.resolutionSource).toBe('copilot-alias'); }); + it('does not route cursor image analysis through the fallback backend by default', () => { + const status = resolveImageAnalysisStatus( + { + profileName: 'cursor', + profileType: 'cursor', + }, + DEFAULT_IMAGE_ANALYSIS_CONFIG + ); + + expect(status.supported).toBe(false); + expect(status.backendId).toBeNull(); + expect(status.status).toBe('skipped'); + expect(status.resolutionSource).toBe('unresolved'); + expect(status.reason).toContain('profile_backends.cursor'); + }); + + it('allows cursor image analysis only when explicitly mapped to a backend', () => { + const config: ImageAnalysisConfig = { + ...DEFAULT_IMAGE_ANALYSIS_CONFIG, + profile_backends: { + cursor: 'ghcp', + }, + }; + + const status = resolveImageAnalysisStatus( + { + profileName: 'cursor', + profileType: 'cursor', + }, + config + ); + + expect(status.supported).toBe(true); + expect(status.backendId).toBe('ghcp'); + expect(status.status).toBe('mapped'); + expect(status.resolutionSource).toBe('profile-backend'); + }); + it('uses the fallback backend for an unmapped third-party settings profile', () => { const status = resolveImageAnalysisStatus( { From f61cc87aa2c3e9b54e07c32a04f503cdfd336ee4 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 12:52:17 -0400 Subject: [PATCH 05/48] feat(dashboard): show all provider presets by default in Create API Profile modal Remove collapsible "More Presets" toggle. All alternative providers are now always visible in a labeled section below Featured Providers, improving discoverability for users like Minimax, GLM, DeepSeek. Closes #892 --- .../profiles/profile-create-dialog.tsx | 66 +++++-------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/ui/src/components/profiles/profile-create-dialog.tsx b/ui/src/components/profiles/profile-create-dialog.tsx index cdf8b449..8a67a7cf 100644 --- a/ui/src/components/profiles/profile-create-dialog.tsx +++ b/ui/src/components/profiles/profile-create-dialog.tsx @@ -31,17 +31,7 @@ import { Badge } from '@/components/ui/badge'; import { ProviderLogo } from '@/components/cliproxy/provider-logo'; import { useCreateProfile } from '@/hooks/use-profiles'; import { useOpenRouterCatalog } from '@/hooks/use-openrouter-models'; -import { - Loader2, - Plus, - AlertTriangle, - Info, - Eye, - EyeOff, - Settings2, - Sparkles, - ChevronDown, -} from 'lucide-react'; +import { Loader2, Plus, AlertTriangle, Info, Eye, EyeOff, Settings2, Sparkles } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; @@ -113,9 +103,6 @@ const RECOMMENDED_PRESETS = getPresetsByCategory('recommended'); const QUICK_TEMPLATE_PRESETS = PROVIDER_PRESETS.filter( (preset) => preset.category !== 'recommended' ); -const QUICK_TEMPLATE_PRESET_IDS = new Set( - QUICK_TEMPLATE_PRESETS.map((preset) => preset.id) -); export function ProfileCreateDialog({ open, @@ -129,7 +116,6 @@ export function ProfileCreateDialog({ const [urlWarning, setUrlWarning] = useState(null); const [showApiKey, setShowApiKey] = useState(false); const [selectedPreset, setSelectedPreset] = useState(DEFAULT_PRESET_ID); - const [showMorePresets, setShowMorePresets] = useState(false); const [modelSearch, setModelSearch] = useState(''); // OpenRouter models for model picker @@ -192,7 +178,6 @@ export function ProfileCreateDialog({ setActiveTab('basic'); setUrlWarning(null); setShowApiKey(false); - setShowMorePresets(false); setModelSearch(''); // Set initial preset based on initialMode @@ -221,13 +206,11 @@ export function ProfileCreateDialog({ if (preset) { setSelectedPreset(preset.id); - setShowMorePresets(QUICK_TEMPLATE_PRESET_IDS.has(preset.id)); applyPresetToForm(preset); return; } setSelectedPreset(CUSTOM_PRESET_ID); - setShowMorePresets(false); applyPresetToForm(null); }; @@ -290,10 +273,7 @@ export function ProfileCreateDialog({ !!errors.model || !!errors.opusModel || !!errors.sonnetModel || !!errors.haikuModel; const isCreating = createMutation.isPending; - const isQuickTemplateSelected = - selectedPreset !== CUSTOM_PRESET_ID && QUICK_TEMPLATE_PRESET_IDS.has(selectedPreset); const isOpenRouter = currentPreset?.id === DEFAULT_PRESET_ID; - const showQuickTemplates = showMorePresets || isQuickTemplateSelected; return ( @@ -353,38 +333,22 @@ export function ProfileCreateDialog({
- - - {showQuickTemplates && ( -
-
- {QUICK_TEMPLATE_PRESETS.map((preset) => ( - handlePresetSelect(preset.id)} - density="compact" - /> - ))} -
+ +
+
+ {QUICK_TEMPLATE_PRESETS.map((preset) => ( + handlePresetSelect(preset.id)} + density="compact" + /> + ))}
- )} +
From a2d1ab85e8efcfa0042d88e14c48a2ac64d565c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 2 Apr 2026 18:13:50 +0000 Subject: [PATCH 06/48] chore(release): 7.65.2-dev.1 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 690ae37d..fa5b1dfd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.65.2", + "version": "7.65.2-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", From 813b2dd4d054a5c4cb783cfeb3c7d2d65abc4a0c Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 15:42:48 -0400 Subject: [PATCH 07/48] feat(image-analysis): add provider-backed runtime --- lib/hooks/image-analysis-runtime.cjs | 458 ++++++++++++++++++ lib/hooks/image-analyzer-transformer.cjs | 445 ++--------------- lib/mcp/ccs-image-analysis-server.cjs | 337 +++++++++++++ src/api/services/profile-lifecycle-service.ts | 4 + src/api/services/profile-writer.ts | 3 + src/ccs.ts | 42 +- src/cliproxy/executor/env-resolver.ts | 29 +- src/cliproxy/executor/index.ts | 18 +- src/cliproxy/services/variant-settings.ts | 11 + src/commands/install-command.ts | 19 +- src/copilot/copilot-executor.ts | 31 +- src/delegation/headless-executor.ts | 99 +++- src/management/checks/image-analysis-check.ts | 12 +- src/management/instance-manager.ts | 2 +- .../hooks/get-image-analysis-hook-env.ts | 64 +++ .../hooks/image-analysis-backend-resolver.ts | 10 +- .../hooks/image-analysis-runtime-status.ts | 10 +- .../hooks/image-analyzer-hook-installer.ts | 101 ++-- .../image-analyzer-profile-hook-injector.ts | 4 + src/utils/hooks/index.ts | 15 +- src/utils/image-analysis/claude-tool-args.ts | 74 +++ src/utils/image-analysis/index.ts | 20 + src/utils/image-analysis/mcp-installer.ts | 393 +++++++++++++++ .../routes/image-analysis-routes.ts | 1 - 24 files changed, 1715 insertions(+), 487 deletions(-) create mode 100644 lib/hooks/image-analysis-runtime.cjs create mode 100644 lib/mcp/ccs-image-analysis-server.cjs create mode 100644 src/utils/image-analysis/claude-tool-args.ts create mode 100644 src/utils/image-analysis/mcp-installer.ts diff --git a/lib/hooks/image-analysis-runtime.cjs b/lib/hooks/image-analysis-runtime.cjs new file mode 100644 index 00000000..91188ce5 --- /dev/null +++ b/lib/hooks/image-analysis-runtime.cjs @@ -0,0 +1,458 @@ +const fs = require('fs'); +const http = require('http'); +const https = require('https'); +const path = require('path'); + +const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.heic', '.bmp', '.tiff']; +const PDF_EXTENSIONS = ['.pdf']; +const DEFAULT_MODEL = 'gemini-2.5-flash'; +const DEFAULT_TIMEOUT_SEC = 60; +const MAX_FILE_SIZE_MB = 10; +const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024; +const SCREENSHOT_NAME_REGEX = + /(screen[-_ ]?shot|screen[-_ ]?capture|screencap|snapshot|snip|clip|capture)/i; +const TEMPLATE_FILE_NAMES = { + default: 'default.txt', + screenshot: 'screenshot.txt', + document: 'document.txt', +}; +const FALLBACK_PROMPTS = { + default: `Analyze this image/document thoroughly and provide a detailed description. + +Include: +1. Overall content and purpose +2. Text content (if any) - transcribe important text verbatim +3. Visual elements (diagrams, charts, UI components, icons) +4. Layout and structure (sections, hierarchy, flow) +5. Colors, styling, notable design elements +6. Any actionable information (buttons, links, code snippets) + +Be comprehensive - this description replaces direct visual access. +The AI assistant reading this cannot see the original image.`, + screenshot: `Analyze this screenshot in detail for a developer who cannot see it. + +Focus on: +1. Application/website type and state +2. UI elements visible (buttons, inputs, menus, modals) +3. All text content - transcribe exactly +4. Error messages or notifications (quote exactly) +5. Layout and component hierarchy +6. Interactive elements and their states +7. Console output or logs if visible +8. Any code snippets shown + +Be precise - this enables the assistant to help debug or understand the UI.`, + document: `Analyze this document/PDF thoroughly for a developer. + +Extract and provide: +1. Document title, type, and structure +2. All text content - transcribe in reading order +3. Tables - format as markdown tables +4. Lists and bullet points - preserve structure +5. Code blocks or technical content +6. Diagrams or flowcharts - describe in detail +7. Headers and section organization +8. Any important metadata visible + +Accuracy in text extraction is critical.`, +}; + +function debugLog(message, data = {}) { + if (!process.env.CCS_DEBUG) return; + + const lines = [`[CCS Hook] ${message}`]; + for (const [key, value] of Object.entries(data)) { + if (value !== undefined && value !== null && value !== '') { + lines.push(` ${key}: ${value}`); + } + } + console.error(lines.join('\n')); +} + +function parseProviderModels(envValue) { + if (!envValue) return {}; + return envValue.split(',').reduce((acc, pair) => { + const [provider, ...modelParts] = pair.split(':'); + const model = modelParts.join(':').trim(); + if (provider && model) { + acc[provider.trim()] = model; + } + return acc; + }, {}); +} + +function normalizeTemplateName(value) { + if (typeof value !== 'string') return null; + const normalized = value.trim().toLowerCase(); + return Object.prototype.hasOwnProperty.call(TEMPLATE_FILE_NAMES, normalized) ? normalized : null; +} + +function selectPromptTemplate(filePath, requestedTemplate) { + const explicitTemplate = normalizeTemplateName(requestedTemplate); + if (explicitTemplate) { + return explicitTemplate; + } + + const extension = path.extname(filePath).toLowerCase(); + if (PDF_EXTENSIONS.includes(extension)) { + return 'document'; + } + + return SCREENSHOT_NAME_REGEX.test(path.basename(filePath)) ? 'screenshot' : 'default'; +} + +function readPromptFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8').trim(); + return content.length > 0 ? content : null; + } catch { + return null; + } +} + +function loadPromptTemplate(filePath, requestedTemplate, focus) { + const template = selectPromptTemplate(filePath, requestedTemplate); + const promptsDir = process.env.CCS_IMAGE_ANALYSIS_PROMPTS_DIR || ''; + const promptPath = promptsDir + ? path.join(promptsDir, TEMPLATE_FILE_NAMES[template]) + : null; + const promptText = (promptPath && readPromptFile(promptPath)) || FALLBACK_PROMPTS[template]; + + if (!focus || !focus.trim()) { + return { + template, + promptSource: promptPath ? 'installed-or-fallback' : 'bundled-fallback', + prompt: promptText, + }; + } + + return { + template, + promptSource: promptPath ? 'installed-or-fallback' : 'bundled-fallback', + prompt: `${promptText}\n\nSpecific focus:\n${focus.trim()}`, + }; +} + +function getCurrentProvider() { + return process.env.CCS_CURRENT_PROVIDER || ''; +} + +function getConfiguredModel() { + const explicitModel = process.env.CCS_IMAGE_ANALYSIS_MODEL; + if (explicitModel && explicitModel.trim()) { + return explicitModel.trim(); + } + + const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); + return providerModels[getCurrentProvider()] || DEFAULT_MODEL; +} + +function getModelsToTry() { + const models = []; + const seen = new Set(); + + const explicitModel = process.env.CCS_IMAGE_ANALYSIS_MODEL; + if (explicitModel && explicitModel.trim()) { + models.push(explicitModel.trim()); + seen.add(explicitModel.trim()); + } + + const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); + const providerModel = providerModels[getCurrentProvider()]; + if (providerModel && !seen.has(providerModel)) { + models.push(providerModel); + seen.add(providerModel); + } + + if (models.length === 0) { + models.push(DEFAULT_MODEL); + } + + return models; +} + +function getRuntimeBaseUrl() { + const runtimePath = (process.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH || '') + .trim() + .replace(/\/+$/, ''); + const explicitBaseUrl = process.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL; + if (explicitBaseUrl && explicitBaseUrl.trim()) { + const normalizedBaseUrl = explicitBaseUrl.trim().replace(/\/+$/, ''); + if (!runtimePath) { + return normalizedBaseUrl; + } + + try { + const parsed = new URL(normalizedBaseUrl); + const normalizedPath = parsed.pathname.replace(/\/+$/, ''); + if (normalizedPath === runtimePath) { + return normalizedBaseUrl; + } + + if (normalizedPath.length > 0 && normalizedPath !== '/') { + return normalizedBaseUrl; + } + + parsed.pathname = runtimePath; + return parsed.toString().replace(/\/+$/, ''); + } catch { + return `${normalizedBaseUrl}${runtimePath}`; + } + } + + const port = Number.parseInt(process.env.CCS_CLIPROXY_PORT || '8317', 10); + return `http://127.0.0.1:${port}${runtimePath}`; +} + +function getRuntimeEndpoint() { + return `${getRuntimeBaseUrl()}/v1/messages`; +} + +function getApiKey() { + if (Object.prototype.hasOwnProperty.call(process.env, 'CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY')) { + const explicitApiKey = (process.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY || '').trim(); + return explicitApiKey || 'ccs-internal-managed'; + } + + return process.env.CCS_CLIPROXY_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || 'ccs-internal-managed'; +} + +function getTimeoutMs(timeoutMs) { + if (typeof timeoutMs === 'number' && timeoutMs > 0) { + return timeoutMs; + } + + const timeoutSec = Number.parseInt( + process.env.CCS_IMAGE_ANALYSIS_TIMEOUT || `${DEFAULT_TIMEOUT_SEC}`, + 10 + ); + return Math.max(1, Math.min(600, timeoutSec)) * 1000; +} + +function isAnalyzableFile(filePath) { + const ext = path.extname(filePath).toLowerCase(); + return IMAGE_EXTENSIONS.includes(ext) || PDF_EXTENSIONS.includes(ext); +} + +function getMediaType(filePath) { + const ext = path.extname(filePath).toLowerCase(); + return ( + { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.heic': 'image/heic', + '.bmp': 'image/bmp', + '.tiff': 'image/tiff', + '.pdf': 'application/pdf', + }[ext] || 'application/octet-stream' + ); +} + +function encodeFileToBase64(filePath) { + return fs.readFileSync(filePath).toString('base64'); +} + +function buildContentBlock(base64Data, mediaType) { + const source = { + type: 'base64', + media_type: mediaType, + data: base64Data, + }; + + if (mediaType === 'application/pdf') { + return { + type: 'document', + source, + }; + } + + return { + type: 'image', + source, + }; +} + +function extractTextContent(response) { + if (!response || !Array.isArray(response.content)) { + return null; + } + + const textBlocks = response.content + .filter((block) => block && block.type === 'text' && typeof block.text === 'string') + .map((block) => block.text) + .filter((text) => text.trim()); + + return textBlocks.length > 0 ? textBlocks.join('\n\n') : null; +} + +function parseCliProxyResponse(data) { + const response = JSON.parse(data); + const text = extractTextContent(response); + if (!text) { + throw new Error('No text content in response'); + } + return text; +} + +function analyzeViaCliProxy(base64Data, mediaType, model, prompt, timeoutMs) { + return new Promise((resolve, reject) => { + const endpoint = new URL(getRuntimeEndpoint()); + const transport = endpoint.protocol === 'https:' ? https : http; + const requestBody = JSON.stringify({ + model, + max_tokens: 4096, + messages: [ + { + role: 'user', + content: [ + { type: 'text', text: prompt }, + buildContentBlock(base64Data, mediaType), + ], + }, + ], + }); + + const req = transport.request( + { + protocol: endpoint.protocol, + hostname: endpoint.hostname, + port: endpoint.port, + path: `${endpoint.pathname}${endpoint.search}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(requestBody), + 'x-api-key': getApiKey(), + }, + timeout: timeoutMs, + }, + (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode === 401 || res.statusCode === 403) { + reject(new Error(`AUTH_ERROR:${res.statusCode}`)); + return; + } + + if (res.statusCode === 429) { + reject(new Error(`RATE_LIMIT:${res.headers['retry-after'] || ''}`)); + return; + } + + if (res.statusCode !== 200) { + reject(new Error(`API_ERROR:${res.statusCode}:${data}`)); + return; + } + + try { + resolve(parseCliProxyResponse(data)); + } catch (error) { + reject(error); + } + }); + } + ); + + req.on('error', (error) => reject(error)); + req.on('timeout', () => { + req.destroy(); + reject(new Error('TIMEOUT')); + }); + req.write(requestBody); + req.end(); + }); +} + +async function analyzeWithRetry(base64Data, mediaType, prompt, timeoutMs) { + const models = getModelsToTry(); + let lastError = null; + + for (const [index, model] of models.entries()) { + try { + debugLog(`Trying model ${index + 1}/${models.length}`, { model }); + const description = await analyzeViaCliProxy(base64Data, mediaType, model, prompt, timeoutMs); + return { description, model }; + } catch (error) { + lastError = error; + const message = error.message || ''; + if ( + index === models.length - 1 || + ['AUTH_ERROR', 'RATE_LIMIT', 'TIMEOUT', 'EACCES', 'EPERM', 'ECONNREFUSED'].some((token) => + message.includes(token) + ) + ) { + throw error; + } + } + } + + throw lastError || new Error('No models configured for image analysis'); +} + +async function analyzeFile(filePath, options = {}) { + const stats = fs.statSync(filePath); + if (stats.size >= MAX_FILE_SIZE_BYTES) { + throw new Error(`FILE_TOO_LARGE:${stats.size}`); + } + + const timeoutMs = getTimeoutMs(options.timeoutMs); + const { template, prompt, promptSource } = loadPromptTemplate( + filePath, + options.template, + options.focus + ); + const model = getConfiguredModel(); + + debugLog('Starting image analysis', { + file: path.basename(filePath), + size: `${(stats.size / 1024).toFixed(1)} KB`, + provider: getCurrentProvider() || 'unknown', + model, + modelsToTry: getModelsToTry().join(' -> '), + timeout: `${timeoutMs / 1000}s`, + endpoint: getRuntimeEndpoint(), + template, + promptSource, + }); + + const base64Data = encodeFileToBase64(filePath); + const mediaType = getMediaType(filePath); + debugLog('File encoded', { + mediaType, + base64Length: `${(base64Data.length / 1024).toFixed(1)}KB`, + }); + + const result = await analyzeWithRetry(base64Data, mediaType, prompt, timeoutMs); + debugLog('Analysis complete', { + responseLength: `${result.description.length} chars`, + model: result.model, + template, + }); + + return { + description: result.description, + model: result.model, + fileSize: stats.size, + mediaType, + template, + }; +} + +module.exports = { + DEFAULT_MODEL, + DEFAULT_TIMEOUT_SEC, + MAX_FILE_SIZE_BYTES, + analyzeFile, + getRuntimeEndpoint, + isAnalyzableFile, + parseProviderModels, + selectPromptTemplate, +}; diff --git a/lib/hooks/image-analyzer-transformer.cjs b/lib/hooks/image-analyzer-transformer.cjs index ba86f4d3..77f1ee11 100755 --- a/lib/hooks/image-analyzer-transformer.cjs +++ b/lib/hooks/image-analyzer-transformer.cjs @@ -24,7 +24,7 @@ const fs = require('fs'); const path = require('path'); -const http = require('http'); +const { analyzeFile, isAnalyzableFile, parseProviderModels } = require('./image-analysis-runtime.cjs'); // ============================================================================ // PLATFORM DETECTION @@ -36,19 +36,8 @@ const isWindows = process.platform === 'win32'; // CONFIGURATION // ============================================================================ -const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.heic', '.bmp', '.tiff']; -const PDF_EXTENSIONS = ['.pdf']; - const DEFAULT_MODEL = 'gemini-2.5-flash'; const DEFAULT_TIMEOUT_SEC = 60; -const MAX_FILE_SIZE_MB = 10; -const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024; - -const CLIPROXY_HOST = '127.0.0.1'; -const CLIPROXY_PORT = parseInt(process.env.CCS_CLIPROXY_PORT || '8317', 10); -const CLIPROXY_PATH = '/v1/messages'; -// API key passed via env from cliproxy-executor, defaults to CCS internal key -const CLIPROXY_API_KEY = process.env.CCS_CLIPROXY_API_KEY || 'ccs-internal-managed'; // ============================================================================ // ERROR CODES (for categorization) @@ -65,23 +54,6 @@ const ERROR_CODES = { UNKNOWN: 'UNKNOWN', }; -// Default analysis prompt -const DEFAULT_PROMPT = `Analyze this image/document thoroughly and provide a detailed description. - -Include: -1. Overall content and purpose -2. Text content (if any) - transcribe important text -3. Visual elements (diagrams, charts, UI components) -4. Layout and structure -5. Colors, styling, notable design elements -6. Any actionable information (buttons, links, code) - -Be comprehensive - this description replaces direct visual access.`; - -// ============================================================================ -// HELPER FUNCTIONS -// ============================================================================ - /** * Output debug information to stderr * Only outputs when CCS_DEBUG=1 @@ -105,19 +77,19 @@ function debugLog(message, data = {}) { */ function getDebugContext(filePath, stats) { const currentProvider = process.env.CCS_CURRENT_PROVIDER || 'unknown'; - const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); - const model = providerModels[currentProvider] || DEFAULT_MODEL; + const model = + process.env.CCS_IMAGE_ANALYSIS_MODEL || + parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS)[currentProvider] || + DEFAULT_MODEL; const timeout = parseInt(process.env.CCS_IMAGE_ANALYSIS_TIMEOUT || DEFAULT_TIMEOUT_SEC, 10); - const modelsToTry = getModelsToTry(); return { file: path.basename(filePath), size: stats ? `${(stats.size / 1024).toFixed(1)} KB` : 'unknown', provider: currentProvider, model: model, - modelsToTry: modelsToTry.length > 1 ? modelsToTry.join(' -> ') : model, timeout: `${timeout}s`, - endpoint: `http://${CLIPROXY_HOST}:${CLIPROXY_PORT}${CLIPROXY_PATH}`, + endpoint: process.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL || '(runtime fallback)', }; } @@ -126,319 +98,12 @@ function getDebugContext(filePath, stats) { */ function getProviderContext() { const provider = process.env.CCS_CURRENT_PROVIDER || 'unknown'; - const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); - const model = providerModels[provider] || DEFAULT_MODEL; + const model = + process.env.CCS_IMAGE_ANALYSIS_MODEL || + parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS)[provider] || + DEFAULT_MODEL; return { provider, model }; } - -/** - * Parse provider_models env var to object - * Format: provider:model,provider:model - */ -function parseProviderModels(envValue) { - if (!envValue) return {}; - const result = {}; - envValue.split(',').forEach((pair) => { - const [provider, model] = pair.split(':'); - if (provider && model && model.trim()) { - result[provider.trim()] = model.trim(); - } - }); - return result; -} - -/** - * Extract concatenated text content from CLIProxy response blocks. - * Skips thinking and other non-text blocks. - */ -function extractTextContent(response) { - if (!response || !Array.isArray(response.content)) { - return null; - } - - const textBlocks = response.content - .filter((block) => block && block.type === 'text' && typeof block.text === 'string') - .map((block) => block.text) - .filter((text) => text.trim()); - - if (textBlocks.length === 0) { - return null; - } - - return textBlocks.join('\n\n'); -} - -/** - * Parse raw CLIProxy response body and extract text content. - */ -function parseCliProxyResponse(data) { - let response; - try { - response = JSON.parse(data); - } catch (err) { - throw new Error(`Failed to parse response: ${err.message}`); - } - - const text = extractTextContent(response); - if (!text) { - throw new Error('No text content in response'); - } - - return text; -} - -/** - * Get model for current provider from provider_models mapping - * Returns primary model only (for display/logging) - */ -function getModelForProvider() { - const currentProvider = process.env.CCS_CURRENT_PROVIDER || ''; - const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); - return providerModels[currentProvider] || DEFAULT_MODEL; -} - -/** - * Get list of models to try in order: - * 1. provider_models[current_provider] (if exists) - * 2. DEFAULT_MODEL when no provider-specific vision model is configured - * - * ANTHROPIC_MODEL is intentionally ignored here because the chat model may - * not be vision-capable on the current provider route. - */ -function getModelsToTry() { - const currentProvider = process.env.CCS_CURRENT_PROVIDER || ''; - const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); - - const models = []; - const seen = new Set(); - - // 1. Provider-specific model - if (providerModels[currentProvider]) { - models.push(providerModels[currentProvider]); - seen.add(providerModels[currentProvider]); - } - - // 2. Default model — only when no provider-specific model is configured, - // since DEFAULT_MODEL (gemini-2.5-flash) may not be routable on the - // current provider's CLIProxy endpoint (e.g. codex, claude) - if (models.length === 0 && !seen.has(DEFAULT_MODEL)) { - models.push(DEFAULT_MODEL); - seen.add(DEFAULT_MODEL); - } - - return models; -} - -/** - * Analyze with retry logic - tries models in order until one succeeds - */ -async function analyzeWithRetry(base64Data, mediaType, timeoutMs) { - const models = getModelsToTry(); - // Defensive check - should never happen but provides clear error - if (models.length === 0) { - throw new Error('No models configured for image analysis'); - } - let lastError = null; - - for (let i = 0; i < models.length; i++) { - const model = models[i]; - try { - debugLog(`Trying model ${i + 1}/${models.length}`, { model }); - const result = await analyzeViaCliProxy(base64Data, mediaType, model, timeoutMs); - if (i > 0) { - debugLog('Retry succeeded', { model, attempt: i + 1 }); - } - return { description: result, model }; - } catch (err) { - lastError = err; - const isLastModel = i === models.length - 1; - - // Don't retry on certain errors (auth, rate limit, timeout, file access, network) - const errMsg = err.message || ''; - const noRetryPatterns = [ - 'AUTH_ERROR', 'RATE_LIMIT', 'TIMEOUT', - 'EACCES', 'EPERM', 'ECONNREFUSED', - 'ENOTFOUND', 'ENETUNREACH', 'EAI_AGAIN' // Network errors - no point retrying - ]; - const shouldNotRetry = noRetryPatterns.some(p => errMsg.includes(p)); - - if (shouldNotRetry || isLastModel) { - debugLog('Analysis failed, no more retries', { - model, - error: errMsg, - reason: shouldNotRetry ? 'non-retryable error' : 'last model' - }); - throw err; - } - - debugLog('Model failed, trying next', { - model, - error: errMsg.substring(0, 100), - nextModel: models[i + 1] - }); - } - } - - throw lastError || new Error('No models available'); -} - -/** - * Check if file is an analyzable image or PDF - */ -function isAnalyzableFile(filePath) { - const ext = path.extname(filePath).toLowerCase(); - return IMAGE_EXTENSIONS.includes(ext) || PDF_EXTENSIONS.includes(ext); -} - -/** - * Get MIME type from file extension - */ -function getMediaType(filePath) { - const ext = path.extname(filePath).toLowerCase(); - const mimeTypes = { - '.jpg': 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png': 'image/png', - '.gif': 'image/gif', - '.webp': 'image/webp', - '.heic': 'image/heic', - '.bmp': 'image/bmp', - '.tiff': 'image/tiff', - '.pdf': 'application/pdf', - }; - return mimeTypes[ext] || 'application/octet-stream'; -} - -/** - * Encode file to base64 - */ -function encodeFileToBase64(filePath) { - const content = fs.readFileSync(filePath); - return content.toString('base64'); -} - -/** - * Check if CLIProxy is available - */ -function isCliProxyAvailable() { - return new Promise((resolve) => { - const req = http.request( - { - hostname: CLIPROXY_HOST, - port: CLIPROXY_PORT, - path: '/', - method: 'GET', - timeout: 2000, - }, - (res) => { - resolve(res.statusCode >= 200 && res.statusCode < 500); - } - ); - - req.on('error', () => resolve(false)); - req.on('timeout', () => { - req.destroy(); - resolve(false); - }); - - req.end(); - }); -} - -/** - * Analyze file via CLIProxy vision API - */ -function analyzeViaCliProxy(base64Data, mediaType, model, timeoutMs) { - return new Promise((resolve, reject) => { - const requestBody = JSON.stringify({ - model: model, - max_tokens: 4096, - messages: [ - { - role: 'user', - content: [ - { type: 'text', text: DEFAULT_PROMPT }, - { - type: 'image', - source: { - type: 'base64', - media_type: mediaType, - data: base64Data, - }, - }, - ], - }, - ], - }); - - const req = http.request( - { - hostname: CLIPROXY_HOST, - port: CLIPROXY_PORT, - path: CLIPROXY_PATH, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(requestBody), - 'x-api-key': CLIPROXY_API_KEY, - }, - timeout: timeoutMs, - }, - (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('error', (err) => { - reject(err); - }); - - res.on('end', () => { - // Categorize by status code - if (res.statusCode === 401 || res.statusCode === 403) { - reject(new Error(`AUTH_ERROR:${res.statusCode}`)); - return; - } - - if (res.statusCode === 429) { - const retryAfter = res.headers['retry-after']; - reject(new Error(`RATE_LIMIT:${retryAfter || ''}`)); - return; - } - - if (res.statusCode !== 200) { - reject(new Error(`API_ERROR:${res.statusCode}:${data}`)); - return; - } - - if (!data || !data.trim()) { - reject(new Error('Empty response from CLIProxy')); - return; - } - - try { - const text = parseCliProxyResponse(data); - resolve(text); - } catch (err) { - reject(err); - } - }); - } - ); - - req.on('error', (err) => reject(err)); - req.on('timeout', () => { - req.destroy(); - reject(new Error('TIMEOUT')); - }); - - req.write(requestBody); - req.end(); - }); -} - /** * Format analysis description for Claude (matches websearch format) */ @@ -520,29 +185,6 @@ function outputFileTooLargeError(filePath, actualSizeMB, maxSizeMB) { process.exit(2); } -/** - * CLIProxy unavailable error - */ -function outputCliProxyUnavailableError(filePath, endpoint) { - const output = formatErrorOutput( - filePath, - ERROR_CODES.CLIPROXY_UNAVAILABLE, - `CLIProxy not available at ${endpoint}`, - [ - 'CLIProxy service may not be running', - 'Start with: ccs config (opens dashboard, starts CLIProxy)', - 'Or manually: ccs cliproxy start', - `Verify: curl ${endpoint}`, - 'Check status: ccs doctor', - ] - ); - console.log(JSON.stringify(output)); - process.exit(2); -} - -/** - * Authentication error - */ function outputAuthError(filePath, statusCode) { const { provider } = getProviderContext(); const output = formatErrorOutput( @@ -758,10 +400,11 @@ function shouldSkipHook() { } // Check if current provider has a vision model configured + const explicitModel = process.env.CCS_IMAGE_ANALYSIS_MODEL; const currentProvider = process.env.CCS_CURRENT_PROVIDER || ''; const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS); - if (!providerModels[currentProvider]) { + if (!explicitModel?.trim() && !providerModels[currentProvider]) { debugLog(`Skipping: provider "${currentProvider}" not in provider_models`, { configured_providers: Object.keys(providerModels).join(', ') || 'none', }); @@ -832,57 +475,15 @@ async function processHook() { process.exit(0); } - // Check if file exists if (!fs.existsSync(filePath)) { - // Let native Read handle the error process.exit(0); } - // Check file size - const stats = fs.statSync(filePath); - if (stats.size >= MAX_FILE_SIZE_BYTES) { - outputFileTooLargeError(filePath, stats.size / 1024 / 1024, MAX_FILE_SIZE_MB); - return; - } + const debugContext = getDebugContext(filePath, null); + debugLog('Image analysis runtime prepared', debugContext); - // Check CLIProxy availability - const cliProxyAvailable = await isCliProxyAvailable(); - if (!cliProxyAvailable) { - debugLog('Blocking: CLIProxy not available', { - endpoint: `http://${CLIPROXY_HOST}:${CLIPROXY_PORT}`, - action: 'blocking to prevent context overflow', - }); - outputCliProxyUnavailableFallback(filePath); - return; - } - - const model = getModelForProvider(); - const timeout = parseInt(process.env.CCS_IMAGE_ANALYSIS_TIMEOUT || DEFAULT_TIMEOUT_SEC, 10); - const timeoutMs = Math.max(1, Math.min(600, timeout)) * 1000; - - // Get debug context before analysis - const debugContext = getDebugContext(filePath, stats); - debugLog('Starting image analysis', debugContext); - - // Encode file to base64 - const base64Data = encodeFileToBase64(filePath); - const mediaType = getMediaType(filePath); - - debugLog('File encoded', { - mediaType: mediaType, - base64Length: `${(base64Data.length / 1024).toFixed(1)}KB`, - }); - - // Analyze via CLIProxy with retry logic - const { description, model: usedModel } = await analyzeWithRetry(base64Data, mediaType, timeoutMs); - - debugLog('Analysis complete', { - responseLength: `${description.length} chars`, - model: usedModel, - }); - - // Output success - outputSuccess(filePath, description, usedModel, stats.size); + const result = await analyzeFile(filePath); + outputSuccess(filePath, result.description, result.model, result.fileSize); } catch (err) { if (process.env.CCS_DEBUG) { console.error('[CCS Hook] Error:', err.message); @@ -893,7 +494,10 @@ async function processHook() { // Categorize error by message pattern const errMsg = err.message || ''; - if (errMsg.startsWith('AUTH_ERROR:')) { + if (errMsg.startsWith('FILE_TOO_LARGE:')) { + const fileSizeMb = Number.parseInt(errMsg.split(':')[1], 10) / 1024 / 1024; + outputFileTooLargeError(filePath, fileSizeMb, 10); + } else if (errMsg.startsWith('AUTH_ERROR:')) { const statusCode = parseInt(errMsg.split(':')[1], 10); outputAuthError(filePath, statusCode); } else if (errMsg.startsWith('RATE_LIMIT:')) { @@ -907,8 +511,13 @@ async function processHook() { } else if (errMsg === 'TIMEOUT' || errMsg.includes('timed out') || errMsg.includes('timeout')) { const timeout = parseInt(process.env.CCS_IMAGE_ANALYSIS_TIMEOUT || DEFAULT_TIMEOUT_SEC, 10); outputTimeoutError(filePath, timeout); - } else if (errMsg.includes('ECONNREFUSED') || errMsg.includes('ENOTFOUND')) { - outputCliProxyUnavailableError(filePath, `http://${CLIPROXY_HOST}:${CLIPROXY_PORT}`); + } else if ( + errMsg.includes('ECONNREFUSED') || + errMsg.includes('ENOTFOUND') || + errMsg.includes('ENETUNREACH') || + errMsg.includes('EAI_AGAIN') + ) { + outputCliProxyUnavailableFallback(filePath); } else if (errMsg.includes('EACCES') || errMsg.includes('EPERM')) { outputFileAccessError(filePath, errMsg); } else { diff --git a/lib/mcp/ccs-image-analysis-server.cjs b/lib/mcp/ccs-image-analysis-server.cjs new file mode 100644 index 00000000..3616d540 --- /dev/null +++ b/lib/mcp/ccs-image-analysis-server.cjs @@ -0,0 +1,337 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +function loadRuntimeModule() { + const candidates = [ + path.join(__dirname, 'image-analysis-runtime.cjs'), + path.join(__dirname, '../hooks/image-analysis-runtime.cjs'), + ]; + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + return require(candidate); + } + } + + throw new Error( + `ccs-image-analysis runtime not found. Checked: ${candidates.map((candidate) => path.basename(candidate)).join(', ')}` + ); +} + +const { analyzeFile, isAnalyzableFile } = loadRuntimeModule(); + +const PROTOCOL_VERSION = '2024-11-05'; +const SERVER_NAME = 'ccs-image-analysis'; +const SERVER_VERSION = '1.0.0'; +const TOOL_NAME = 'ImageAnalysis'; +const TOOL_ALIASES = ['AnalyzeImage', 'ReadImage']; +const TEMPLATE_NAMES = ['default', 'screenshot', 'document']; +const TOOL_DESCRIPTION = + 'Analyze a local image or PDF file with CCS provider-backed vision. Prefer this tool over Read for image and PDF paths. Use Read for text, code, and other plain files.'; + +function isSupportedToolName(name) { + return name === TOOL_NAME || TOOL_ALIASES.includes(name); +} + +function shouldExposeTools() { + return ( + process.env.CCS_IMAGE_ANALYSIS_ENABLED === '1' && + process.env.CCS_IMAGE_ANALYSIS_SKIP !== '1' && + Boolean(process.env.CCS_CURRENT_PROVIDER || process.env.CCS_IMAGE_ANALYSIS_MODEL) + ); +} + +function getTools() { + if (!shouldExposeTools()) { + return []; + } + + return [ + { + name: TOOL_NAME, + description: TOOL_DESCRIPTION, + inputSchema: { + type: 'object', + properties: { + filePath: { + type: 'string', + description: + 'Absolute or workspace-relative path to a local image or PDF file to analyze.', + }, + focus: { + type: 'string', + description: + 'Optional question or area of focus, for example "explain the error dialog" or "transcribe the visible text".', + }, + template: { + type: 'string', + enum: TEMPLATE_NAMES, + description: + 'Optional prompt template override. Use screenshot for UI captures, document for PDFs/docs, or default for general images.', + }, + }, + required: ['filePath'], + additionalProperties: false, + }, + }, + ]; +} + +function writeMessage(message) { + process.stdout.write(`${JSON.stringify(message)}\n`); +} + +function writeResponse(id, result) { + writeMessage({ + jsonrpc: '2.0', + id, + result, + }); +} + +function writeError(id, code, message) { + writeMessage({ + jsonrpc: '2.0', + id, + error: { + code, + message, + }, + }); +} + +function formatResult(filePath, result, focus) { + const lines = [ + '[Image Analysis via CCS]', + '', + `File: ${path.basename(filePath)} (${(result.fileSize / 1024).toFixed(1)} KB)`, + `Model: ${result.model}`, + `Template: ${result.template}`, + ]; + + if (focus && focus.trim()) { + lines.push(`Focus: ${focus.trim()}`); + } + + lines.push('', '---', '', result.description); + return lines.join('\n'); +} + +function normalizeTemplate(value) { + if (typeof value !== 'string') { + return undefined; + } + + const normalized = value.trim().toLowerCase(); + return TEMPLATE_NAMES.includes(normalized) ? normalized : undefined; +} + +function resolveFilePath(toolArgs) { + if (!toolArgs || typeof toolArgs !== 'object') { + return ''; + } + + const candidates = [toolArgs.filePath, toolArgs.file_path, toolArgs.path]; + for (const candidate of candidates) { + if (typeof candidate === 'string' && candidate.trim().length > 0) { + return candidate.trim(); + } + } + + return ''; +} + +function resolveFocus(toolArgs) { + return typeof toolArgs.focus === 'string' && toolArgs.focus.trim().length > 0 + ? toolArgs.focus.trim() + : undefined; +} + +function formatErrorDetail(filePath, error) { + const message = error instanceof Error ? error.message : String(error); + + if (message.startsWith('FILE_TOO_LARGE:')) { + const fileSizeBytes = Number.parseInt(message.split(':')[1], 10); + const sizeMb = Number.isFinite(fileSizeBytes) ? (fileSizeBytes / 1024 / 1024).toFixed(1) : '?'; + return `ImageAnalysis cannot process ${path.basename(filePath)} because it is too large (${sizeMb} MB). The limit is 10 MB.`; + } + + if (message.startsWith('AUTH_ERROR:')) { + return `ImageAnalysis failed because CCS vision auth for this provider is unavailable (${message.split(':')[1]}).`; + } + + if (message.startsWith('RATE_LIMIT:')) { + return `ImageAnalysis hit a provider rate limit while analyzing ${path.basename(filePath)}.`; + } + + if (message.startsWith('API_ERROR:')) { + return `ImageAnalysis failed at the CCS provider route while analyzing ${path.basename(filePath)}.`; + } + + if ( + message === 'TIMEOUT' || + message.includes('timed out') || + message.includes('timeout') || + message.includes('ECONNREFUSED') || + message.includes('ENOTFOUND') || + message.includes('ENETUNREACH') || + message.includes('EAI_AGAIN') + ) { + return `ImageAnalysis could not reach the configured CCS provider route for ${path.basename(filePath)}.`; + } + + if (message.includes('EACCES') || message.includes('EPERM')) { + return `ImageAnalysis could not read ${filePath} because access was denied.`; + } + + return `ImageAnalysis failed for ${path.basename(filePath)}: ${message}`; +} + +async function handleToolCall(message) { + const id = message.id; + const params = message.params || {}; + const toolArgs = params.arguments || {}; + const toolName = params.name || ''; + + if (!isSupportedToolName(toolName)) { + writeError(id, -32602, `Unknown tool: ${toolName}`); + return; + } + + if (!shouldExposeTools()) { + writeResponse(id, { + content: [ + { + type: 'text', + text: 'CCS ImageAnalysis is unavailable for this profile or no provider-backed vision route is ready.', + }, + ], + isError: true, + }); + return; + } + + const filePath = resolveFilePath(toolArgs); + if (!filePath) { + writeError(id, -32602, `Tool "${TOOL_NAME}" requires a non-empty filePath.`); + return; + } + + if (!fs.existsSync(filePath)) { + writeResponse(id, { + content: [{ type: 'text', text: `ImageAnalysis could not find file: ${filePath}` }], + isError: true, + }); + return; + } + + if (!isAnalyzableFile(filePath)) { + writeResponse(id, { + content: [ + { + type: 'text', + text: `ImageAnalysis only supports image and PDF files. Use Read for ${path.basename(filePath)} instead.`, + }, + ], + isError: true, + }); + return; + } + + const focus = resolveFocus(toolArgs); + const template = normalizeTemplate(toolArgs.template); + + try { + const result = await analyzeFile(filePath, { focus, template }); + writeResponse(id, { + content: [{ type: 'text', text: formatResult(filePath, result, focus) }], + }); + } catch (error) { + writeResponse(id, { + content: [{ type: 'text', text: formatErrorDetail(filePath, error) }], + isError: true, + }); + } +} + +async function handleMessage(message) { + if (!message || message.jsonrpc !== '2.0' || typeof message.method !== 'string') { + return; + } + + switch (message.method) { + case 'initialize': + writeResponse(message.id, { + protocolVersion: PROTOCOL_VERSION, + capabilities: { + tools: {}, + }, + serverInfo: { + name: SERVER_NAME, + version: SERVER_VERSION, + }, + }); + return; + case 'notifications/initialized': + return; + case 'ping': + writeResponse(message.id, {}); + return; + case 'tools/list': + writeResponse(message.id, { tools: getTools() }); + return; + case 'tools/call': + await handleToolCall(message); + return; + default: + if (message.id !== undefined) { + writeError(message.id, -32601, `Method not found: ${message.method}`); + } + } +} + +let inputBuffer = Buffer.alloc(0); + +function processIncomingBuffer() { + while (true) { + const newlineIndex = inputBuffer.indexOf(0x0a); + if (newlineIndex === -1) { + return; + } + + const lineBuffer = inputBuffer.subarray(0, newlineIndex); + inputBuffer = inputBuffer.subarray(newlineIndex + 1); + const line = lineBuffer.toString('utf8').replace(/\r$/, '').trim(); + if (!line) { + continue; + } + + try { + const message = JSON.parse(line); + Promise.resolve(handleMessage(message)).catch((error) => { + if (process.env.CCS_DEBUG) { + console.error(`[ccs-image-analysis] ${error instanceof Error ? error.stack : error}`); + } + }); + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + `[ccs-image-analysis] Failed to parse JSON-RPC message: ${ + error instanceof Error ? error.message : error + }` + ); + } + } + } +} + +process.stdin.on('data', (chunk) => { + inputBuffer = Buffer.concat([inputBuffer, chunk]); + processIncomingBuffer(); +}); + +process.stdin.on('end', () => { + process.exit(0); +}); diff --git a/src/api/services/profile-lifecycle-service.ts b/src/api/services/profile-lifecycle-service.ts index 50f3b86b..28d43d11 100644 --- a/src/api/services/profile-lifecycle-service.ts +++ b/src/api/services/profile-lifecycle-service.ts @@ -11,6 +11,7 @@ import type { TargetType } from '../../targets/target-adapter'; import { getPersistedTargetChoices, isPersistedTargetType } from '../../targets/target-metadata'; import { getCcsDir, getConfigPath, loadConfigSafe } from '../../utils/config-manager'; import { ensureWebSearchMcpOrThrow } from '../../utils/websearch-manager'; +import { ensureImageAnalysisMcpOrThrow } from '../../utils/image-analysis'; import { isSensitiveKey } from '../../utils/sensitive-keys'; import { isReservedName } from '../../config/reserved-names'; import { isUnifiedMode, mutateUnifiedConfig } from '../../config/unified-config-loader'; @@ -219,6 +220,7 @@ export function registerApiProfileOrphans(options?: { try { if (orphan.validation.valid) { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } registerApiProfileInConfig(orphan.name, options?.target || 'claude', options?.force || false); result.registered.push(orphan.name); @@ -269,6 +271,7 @@ export function copyApiProfile( writeJsonObjectAtomically(destinationSettingsPath, sourceSettings); try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (hookError) { rollbackSettingsFile(destinationSettingsPath, previousDestinationContent, destinationExisted); throw hookError; @@ -394,6 +397,7 @@ export function importApiProfileBundle( writeJsonObjectAtomically(settingsPath, settings); try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (hookError) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw hookError; diff --git a/src/api/services/profile-writer.ts b/src/api/services/profile-writer.ts index efb70c32..a39fdce9 100644 --- a/src/api/services/profile-writer.ts +++ b/src/api/services/profile-writer.ts @@ -9,6 +9,7 @@ import { expandPath } from '../../utils/helpers'; import { validateApiName } from './validation-service'; import { mutateUnifiedConfig, isUnifiedMode } from '../../config/unified-config-loader'; import { ensureWebSearchMcpOrThrow } from '../../utils/websearch-manager'; +import { ensureImageAnalysisMcpOrThrow } from '../../utils/image-analysis'; import type { TargetType } from '../../targets/target-adapter'; import { resolveDroidProvider } from '../../targets/droid-provider'; import { isReservedName } from '../../config/reserved-names'; @@ -127,6 +128,7 @@ function createSettingsFile( try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (error) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw error; @@ -216,6 +218,7 @@ function createApiProfileUnified( try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (error) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw error; diff --git a/src/ccs.ts b/src/ccs.ts index aafaee74..acc261b5 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -31,11 +31,17 @@ import { appendThirdPartyWebSearchToolArgs, createWebSearchTraceContext, } from './utils/websearch-manager'; +import { + ensureImageAnalysisMcpOrThrow, + syncImageAnalysisMcpToConfigDir, + appendThirdPartyImageAnalysisToolArgs, +} from './utils/image-analysis'; import { getGlobalEnvConfig, getOfficialChannelsConfig } from './config/unified-config-loader'; import { ensureProfileHooks as ensureImageAnalyzerHooks } from './utils/hooks/image-analyzer-profile-hook-injector'; import { + applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, - installImageAnalyzerHook, + prepareImageAnalysisFallbackHook, resolveImageAnalysisRuntimeStatus, } from './utils/hooks'; import { fail, info, warn } from './utils/ui'; @@ -74,7 +80,11 @@ import { resolveDroidReasoningRuntime, } from './targets/droid-reasoning-runtime'; import { DroidCommandRouterError, routeDroidCommandArgs } from './targets/droid-command-router'; -import { resolveCliproxyBridgeMetadata } from './api/services/cliproxy-profile-bridge'; +import { + resolveCliproxyBridgeMetadata, + resolveCliproxyBridgeProfile, +} from './api/services/cliproxy-profile-bridge'; +import { getEffectiveApiKey } from './cliproxy/auth-token-manager'; // Version and Update check utilities import { getVersion } from './utils/version'; @@ -685,7 +695,10 @@ async function main(): Promise { // CLIPROXY FLOW: OAuth-based profiles (gemini, codex, agy, qwen) or user-defined variants if (resolvedTarget === 'claude') { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } + const imageAnalysisFallbackHookReady = + resolvedTarget === 'claude' ? prepareImageAnalysisFallbackHook() : false; const provider = profileInfo.provider || (profileInfo.name as CLIProxyProvider); // Inject Image Analyzer hook into profile settings before launch ensureImageAnalyzerHooks({ @@ -694,6 +707,7 @@ async function main(): Promise { cliproxyProvider: provider, isComposite: profileInfo.isComposite, settingsPath: profileInfo.settingsPath ? expandPath(profileInfo.settingsPath) : undefined, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); const customSettingsPath = profileInfo.settingsPath; // undefined for hardcoded profiles const variantPort = profileInfo.port; // variant-specific port for isolation @@ -848,11 +862,14 @@ async function main(): Promise { } else if (profileInfo.type === 'copilot') { // COPILOT FLOW: GitHub Copilot subscription via copilot-api proxy ensureWebSearchMcpOrThrow(); - installImageAnalyzerHook(); + ensureImageAnalysisMcpOrThrow(); + const imageAnalysisFallbackHookReady = + resolvedTarget === 'claude' ? prepareImageAnalysisFallbackHook() : false; // Inject Image Analyzer hook into profile settings before launch ensureImageAnalyzerHooks({ profileName: profileInfo.name, profileType: profileInfo.type, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); const { executeCopilotProfile } = await import('./copilot'); @@ -884,7 +901,7 @@ async function main(): Promise { // Settings-based profiles (glm, glmt) are third-party providers if (resolvedTarget === 'claude') { ensureWebSearchMcpOrThrow(); - installImageAnalyzerHook(); + ensureImageAnalysisMcpOrThrow(); } // Display WebSearch status (single line, equilibrium UX) @@ -907,6 +924,9 @@ async function main(): Promise { } const inheritedClaudeConfigDir = continuityInheritance.claudeConfigDir; syncWebSearchMcpToConfigDir(inheritedClaudeConfigDir); + syncImageAnalysisMcpToConfigDir(inheritedClaudeConfigDir); + const imageAnalysisFallbackHookReady = + resolvedTarget === 'claude' ? prepareImageAnalysisFallbackHook() : false; const expandedSettingsPath = resolvedSettingsPath ?? (profileInfo.settingsPath @@ -920,6 +940,7 @@ async function main(): Promise { settingsPath: expandedSettingsPath, settings, cliproxyBridge, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); if (resolvedTarget !== 'claude') { const compatibility = evaluateTargetRuntimeCompatibility({ @@ -1022,13 +1043,24 @@ async function main(): Promise { profileType: profileInfo.type, settings, cliproxyBridge, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); + const currentBridgeAuthToken = cliproxyBridge + ? resolveCliproxyBridgeProfile(cliproxyBridge.provider).apiKey + : getEffectiveApiKey(); let imageAnalysisEnv = getImageAnalysisHookEnv({ profileName: profileInfo.name, profileType: profileInfo.type, settings, cliproxyBridge, }); + imageAnalysisEnv = applyImageAnalysisRuntimeOverrides(imageAnalysisEnv, { + backendId: imageAnalysisStatus.backendId, + model: imageAnalysisStatus.model, + runtimePath: imageAnalysisStatus.runtimePath, + baseUrl: cliproxyBridge?.currentBaseUrl || '', + apiKey: currentBridgeAuthToken, + }); const imageAnalysisProvider = imageAnalysisEnv['CCS_CURRENT_PROVIDER']; if ( @@ -1130,7 +1162,7 @@ async function main(): Promise { const launchArgs = [ '--settings', expandedSettingsPath, - ...appendThirdPartyWebSearchToolArgs(remainingArgs), + ...appendThirdPartyWebSearchToolArgs(appendThirdPartyImageAnalysisToolArgs(remainingArgs)), ]; const traceEnv = createWebSearchTraceContext({ launcher: 'ccs.settings-profile', diff --git a/src/cliproxy/executor/env-resolver.ts b/src/cliproxy/executor/env-resolver.ts index 83a0294b..688bfd05 100644 --- a/src/cliproxy/executor/env-resolver.ts +++ b/src/cliproxy/executor/env-resolver.ts @@ -20,7 +20,10 @@ import { applyExtendedContextConfig } from '../config/extended-context-config'; import { CLIProxyProvider } from '../types'; import { CompositeTierConfig } from '../../config/unified-config-types'; import { getWebSearchHookEnv } from '../../utils/websearch-manager'; -import { getImageAnalysisHookEnv } from '../../utils/hooks/get-image-analysis-hook-env'; +import { + applyImageAnalysisRuntimeOverrides, + getImageAnalysisHookEnv, +} from '../../utils/hooks/get-image-analysis-hook-env'; import { resolveImageAnalysisRuntimeStatus } from '../../utils/hooks/image-analysis-runtime-status'; import { hasImageAnalysisProfileHook } from '../../utils/hooks/image-analyzer-profile-hook-injector'; import { hasImageAnalyzerHook } from '../../utils/hooks/image-analyzer-hook-installer'; @@ -30,6 +33,7 @@ import { ToolSanitizationProxy } from '../tool-sanitization-proxy'; import { HttpsTunnelProxy } from '../https-tunnel-proxy'; import { MODEL_ENV_VAR_KEYS, normalizeModelIdForProvider } from '../model-id-normalizer'; import type { ProxyTarget } from '../proxy-target-resolver'; +import { getEffectiveApiKey } from '../auth-token-manager'; import { isSettings, type Settings } from '../../types/config'; export interface RemoteProxyConfig { @@ -76,6 +80,7 @@ interface CliproxyImageAnalysisDeps { hasImageAnalysisProfileHook: typeof hasImageAnalysisProfileHook; hasImageAnalyzerHook: typeof hasImageAnalyzerHook; resolveImageAnalysisRuntimeStatus: typeof resolveImageAnalysisRuntimeStatus; + getLocalRuntimeApiKey: typeof getEffectiveApiKey; } interface ResolveCliproxyImageAnalysisEnvOptions { @@ -97,6 +102,7 @@ const defaultCliproxyImageAnalysisDeps: CliproxyImageAnalysisDeps = { hasImageAnalysisProfileHook, hasImageAnalyzerHook, resolveImageAnalysisRuntimeStatus, + getLocalRuntimeApiKey: getEffectiveApiKey, }; const CODEX_EFFORT_SUFFIX_REGEX = /^(.*)-(xhigh|high|medium)$/i; @@ -148,6 +154,14 @@ function loadImageAnalysisSettings(settingsPath?: string): Settings | undefined } } +function buildDirectProxyBaseUrl(target: ProxyTarget): string { + const isDefaultPort = + (target.protocol === 'https' && target.port === 443) || + (target.protocol === 'http' && target.port === 80); + const portSuffix = isDefaultPort ? '' : `:${target.port}`; + return `${target.protocol}://${target.host}${portSuffix}`; +} + export async function resolveCliproxyImageAnalysisEnv( options: ResolveCliproxyImageAnalysisEnvOptions, deps: Partial = {} @@ -190,7 +204,18 @@ export async function resolveCliproxyImageAnalysisEnv( }; } - return { env, warning: null }; + return { + env: applyImageAnalysisRuntimeOverrides(env, { + backendId: status.backendId, + model: status.model, + runtimePath: status.runtimePath, + baseUrl: buildDirectProxyBaseUrl(options.proxyTarget), + apiKey: options.proxyTarget.isRemote + ? options.proxyTarget.authToken || '' + : resolvedDeps.getLocalRuntimeApiKey(), + }), + warning: null, + }; } /** diff --git a/src/cliproxy/executor/index.ts b/src/cliproxy/executor/index.ts index b7b6cdeb..16ee372b 100644 --- a/src/cliproxy/executor/index.ts +++ b/src/cliproxy/executor/index.ts @@ -58,8 +58,12 @@ import { appendThirdPartyWebSearchToolArgs, createWebSearchTraceContext, } from '../../utils/websearch-manager'; +import { + ensureImageAnalysisMcpOrThrow, + syncImageAnalysisMcpToConfigDir, + appendThirdPartyImageAnalysisToolArgs, +} from '../../utils/image-analysis'; import { loadOrCreateUnifiedConfig, getThinkingConfig } from '../../config/unified-config-loader'; -import { installImageAnalyzerHook } from '../../utils/hooks'; import { HttpsTunnelProxy } from '../https-tunnel-proxy'; import { isKiroAuthMethod, KiroAuthMethod, normalizeKiroAuthMethod } from '../auth/auth-types'; import { resolveProfileContinuityInheritance } from '../../auth/profile-continuity-inheritance'; @@ -205,11 +209,9 @@ export async function execClaudeWithCLIProxy( // Setup first-class CCS WebSearch runtime ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); displayWebSearchStatus(); - // Sync image analyzer hook from npm package to ~/.ccs/hooks/ - installImageAnalyzerHook(); - const providerConfig = getProviderConfig(provider); log(`Provider: ${providerConfig.displayName}`); warnOAuthBanRisk(provider); @@ -870,6 +872,8 @@ export async function execClaudeWithCLIProxy( } } + syncImageAnalysisMcpToConfigDir(inheritedClaudeConfigDir); + // Build initial env vars to get ANTHROPIC_BASE_URL const initialEnvVars = buildClaudeEnvironment({ provider, @@ -1072,7 +1076,11 @@ export async function execClaudeWithCLIProxy( : getProviderSettingsPath(provider); let claude: ChildProcess; - const launchArgs = ['--settings', settingsPath, ...appendThirdPartyWebSearchToolArgs(claudeArgs)]; + const launchArgs = [ + '--settings', + settingsPath, + ...appendThirdPartyWebSearchToolArgs(appendThirdPartyImageAnalysisToolArgs(claudeArgs)), + ]; const traceEnv = createWebSearchTraceContext({ launcher: 'cliproxy.executor', args: launchArgs, diff --git a/src/cliproxy/services/variant-settings.ts b/src/cliproxy/services/variant-settings.ts index 924aa95d..2e3b89ec 100644 --- a/src/cliproxy/services/variant-settings.ts +++ b/src/cliproxy/services/variant-settings.ts @@ -15,7 +15,9 @@ import { getClaudeEnvVars, CLIPROXY_DEFAULT_PORT } from '../config-generator'; import { CLIProxyProvider } from '../types'; import { CompositeTierConfig } from '../../config/unified-config-types'; import { ensureWebSearchMcpOrThrow } from '../../utils/websearch-manager'; +import { ensureImageAnalysisMcpOrThrow } from '../../utils/image-analysis'; import { ensureProfileHooks as ensureImageAnalyzerHooks } from '../../utils/hooks/image-analyzer-profile-hook-injector'; +import { prepareImageAnalysisFallbackHook } from '../../utils/hooks'; import { getEffectiveApiKey } from '../auth-token-manager'; import { warn } from '../../utils/ui'; import { normalizeModelIdForProvider } from '../model-id-normalizer'; @@ -155,10 +157,12 @@ export function createSettingsFile( try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (error) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw error; } + const imageAnalysisFallbackHookReady = prepareImageAnalysisFallbackHook(); // Inject Image Analyzer hooks into variant settings ensureImageAnalyzerHooks({ @@ -166,6 +170,7 @@ export function createSettingsFile( profileType: 'cliproxy', cliproxyProvider: provider, settingsPath, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); return settingsPath; @@ -194,10 +199,12 @@ export function createSettingsFileUnified( try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (error) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw error; } + const imageAnalysisFallbackHookReady = prepareImageAnalysisFallbackHook(); // Inject Image Analyzer hooks into variant settings ensureImageAnalyzerHooks({ @@ -205,6 +212,7 @@ export function createSettingsFileUnified( profileType: 'cliproxy', cliproxyProvider: provider, settingsPath, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); return settingsPath; @@ -297,16 +305,19 @@ export function createCompositeSettingsFile( if (path.resolve(settingsPath) === path.resolve(defaultSettingsPath)) { try { ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); } catch (error) { rollbackSettingsFile(settingsPath, previousSettingsContent, settingsExisted); throw error; } + const imageAnalysisFallbackHookReady = prepareImageAnalysisFallbackHook(); ensureImageAnalyzerHooks({ profileName: `composite-${name}`, profileType: 'cliproxy', cliproxyProvider: tiers[defaultTier].provider, isComposite: true, settingsPath, + sharedHookInstalled: imageAnalysisFallbackHookReady, }); } diff --git a/src/commands/install-command.ts b/src/commands/install-command.ts index fb45f770..a829f00a 100644 --- a/src/commands/install-command.ts +++ b/src/commands/install-command.ts @@ -6,6 +6,8 @@ import { info, ok, color, box, initUI } from '../utils/ui'; import { uninstallWebSearchHook, uninstallWebSearchMcp } from '../utils/websearch'; +import { uninstallImageAnalysisMcp } from '../utils/image-analysis'; +import { uninstallImageAnalyzerHook } from '../utils/hooks'; import { ClaudeSymlinkManager } from '../utils/claude-symlink-manager'; /** @@ -49,12 +51,25 @@ export async function handleUninstallCommand(): Promise { removed += 1; } - // 3. Remove symlinks from ~/.claude/ + // 3. Remove Image Analysis hook fallback + managed MCP runtime + const imageHookRemoved = uninstallImageAnalyzerHook(); + if (imageHookRemoved) { + console.log(ok('Removed Image Analysis hook fallback')); + removed += 1; + } + + const imageMcpRemoved = uninstallImageAnalysisMcp(); + if (imageMcpRemoved) { + console.log(ok('Removed Image Analysis MCP runtime')); + removed += 1; + } + + // 4. Remove symlinks from ~/.claude/ const symlinkManager = new ClaudeSymlinkManager(); const symlinksRemoved = symlinkManager.uninstall(); removed += symlinksRemoved; // Add actual count of symlinks removed - // 4. Summary + // 5. Summary console.log(''); if (removed > 0) { console.log(ok('Uninstall complete!')); diff --git a/src/copilot/copilot-executor.ts b/src/copilot/copilot-executor.ts index e727fbb4..c78763d6 100644 --- a/src/copilot/copilot-executor.ts +++ b/src/copilot/copilot-executor.ts @@ -9,6 +9,7 @@ import { spawn } from 'child_process'; import { CopilotConfig } from '../config/unified-config-types'; import { getGlobalEnvConfig } from '../config/unified-config-loader'; import { ensureCliproxyService } from '../cliproxy'; +import { getEffectiveApiKey } from '../cliproxy/auth-token-manager'; import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { checkAuthStatus, isCopilotApiInstalled } from './copilot-auth'; import { isDaemonRunning, startDaemon } from './copilot-daemon'; @@ -22,13 +23,23 @@ import { createWebSearchTraceContext, syncWebSearchMcpToConfigDir, } from '../utils/websearch-manager'; -import { getImageAnalysisHookEnv, resolveImageAnalysisRuntimeStatus } from '../utils/hooks'; +import { + appendThirdPartyImageAnalysisToolArgs, + ensureImageAnalysisMcpOrThrow, + syncImageAnalysisMcpToConfigDir, +} from '../utils/image-analysis'; +import { + applyImageAnalysisRuntimeOverrides, + getImageAnalysisHookEnv, + resolveImageAnalysisRuntimeStatus, +} from '../utils/hooks'; import { stripClaudeCodeEnv } from '../utils/shell-executor'; interface CopilotImageAnalysisDeps { ensureCliproxyService: typeof ensureCliproxyService; getImageAnalysisHookEnv: typeof getImageAnalysisHookEnv; resolveImageAnalysisRuntimeStatus: typeof resolveImageAnalysisRuntimeStatus; + getLocalRuntimeApiKey: typeof getEffectiveApiKey; } interface CopilotImageAnalysisResolution { @@ -96,6 +107,7 @@ export async function resolveCopilotImageAnalysisEnv( ensureCliproxyService, getImageAnalysisHookEnv, resolveImageAnalysisRuntimeStatus, + getLocalRuntimeApiKey: getEffectiveApiKey, ...deps, }; @@ -141,7 +153,16 @@ export async function resolveCopilotImageAnalysisEnv( } } - return { env, warning: null }; + return { + env: applyImageAnalysisRuntimeOverrides(env, { + backendId: status.backendId, + model: status.model, + runtimePath: status.runtimePath, + baseUrl: `http://127.0.0.1:${CLIPROXY_DEFAULT_PORT}`, + apiKey: resolvedDeps.getLocalRuntimeApiKey(), + }), + warning: null, + }; } /** @@ -251,11 +272,15 @@ export async function executeCopilotProfile( } console.log(''); + ensureImageAnalysisMcpOrThrow(); syncWebSearchMcpToConfigDir(claudeConfigDir); + syncImageAnalysisMcpToConfigDir(claudeConfigDir); // Spawn Claude CLI return new Promise((resolve) => { - const launchArgs = appendThirdPartyWebSearchToolArgs(claudeArgs); + const launchArgs = appendThirdPartyWebSearchToolArgs( + appendThirdPartyImageAnalysisToolArgs(claudeArgs) + ); const traceEnv = createWebSearchTraceContext({ launcher: 'copilot.executor', args: launchArgs, diff --git a/src/delegation/headless-executor.ts b/src/delegation/headless-executor.ts index 074016b9..903d5a6e 100644 --- a/src/delegation/headless-executor.ts +++ b/src/delegation/headless-executor.ts @@ -15,10 +15,26 @@ import { ui, warn, info } from '../utils/ui'; import { type ExecutionOptions, type ExecutionResult, type StreamMessage } from './executor/types'; import { StreamBuffer, formatToolVerbose } from './executor/stream-parser'; import { buildExecutionResult } from './executor/result-aggregator'; -import { getCcsDir, getModelDisplayName } from '../utils/config-manager'; +import { getCcsDir, getModelDisplayName, loadSettings } from '../utils/config-manager'; import { getProfileLookupCandidates } from '../utils/profile-compat'; import { getClaudeLaunchEnvOverrides, stripClaudeCodeEnv } from '../utils/shell-executor'; import { resolveProfileContinuityInheritance } from '../auth/profile-continuity-inheritance'; +import { + appendThirdPartyImageAnalysisToolArgs, + ensureImageAnalysisMcpOrThrow, + syncImageAnalysisMcpToConfigDir, +} from '../utils/image-analysis'; +import { + applyImageAnalysisRuntimeOverrides, + getImageAnalysisHookEnv, + prepareImageAnalysisFallbackHook, + resolveImageAnalysisRuntimeStatus, +} from '../utils/hooks'; +import { ensureProfileHooks as ensureImageAnalyzerHooks } from '../utils/hooks/image-analyzer-profile-hook-injector'; +import { resolveCliproxyBridgeMetadata, resolveCliproxyBridgeProfile } from '../api/services'; +import { ensureCliproxyService } from '../cliproxy'; +import { getEffectiveApiKey } from '../cliproxy/auth-token-manager'; +import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { appendThirdPartyWebSearchToolArgs, appendWebSearchTrace, @@ -105,7 +121,80 @@ export class HeadlessExecutor { } ensureWebSearchMcpOrThrow(); + ensureImageAnalysisMcpOrThrow(); syncWebSearchMcpToConfigDir(inheritedClaudeConfigDir); + syncImageAnalysisMcpToConfigDir(inheritedClaudeConfigDir); + + const settings = loadSettings(settingsPath); + const cliproxyBridge = resolveCliproxyBridgeMetadata(settings); + const imageAnalysisFallbackHookReady = prepareImageAnalysisFallbackHook(); + ensureImageAnalyzerHooks({ + profileName: profile, + profileType: 'settings', + settingsPath, + settings, + cliproxyBridge, + sharedHookInstalled: imageAnalysisFallbackHookReady, + }); + const imageAnalysisStatus = await resolveImageAnalysisRuntimeStatus({ + profileName: profile, + profileType: 'settings', + settings, + cliproxyBridge, + sharedHookInstalled: imageAnalysisFallbackHookReady, + }); + const currentBridgeAuthToken = cliproxyBridge + ? resolveCliproxyBridgeProfile(cliproxyBridge.provider).apiKey + : getEffectiveApiKey(); + let imageAnalysisEnv = getImageAnalysisHookEnv({ + profileName: profile, + profileType: 'settings', + settings, + cliproxyBridge, + }); + imageAnalysisEnv = applyImageAnalysisRuntimeOverrides(imageAnalysisEnv, { + backendId: imageAnalysisStatus.backendId, + model: imageAnalysisStatus.model, + runtimePath: imageAnalysisStatus.runtimePath, + baseUrl: cliproxyBridge?.currentBaseUrl || '', + apiKey: currentBridgeAuthToken, + }); + + const imageAnalysisProvider = imageAnalysisEnv['CCS_CURRENT_PROVIDER']; + if ( + imageAnalysisEnv['CCS_IMAGE_ANALYSIS_SKIP'] !== '1' && + imageAnalysisProvider && + imageAnalysisStatus.effectiveRuntimeMode === 'native-read' + ) { + console.error( + info( + `${imageAnalysisStatus.effectiveRuntimeReason || `Image analysis via ${imageAnalysisProvider} is unavailable.`} This delegation will use native Read.` + ) + ); + imageAnalysisEnv = { + ...imageAnalysisEnv, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }; + } else if ( + imageAnalysisEnv['CCS_IMAGE_ANALYSIS_SKIP'] !== '1' && + imageAnalysisProvider && + imageAnalysisStatus.proxyReadiness === 'stopped' + ) { + const ensureServiceResult = await ensureCliproxyService(CLIPROXY_DEFAULT_PORT, false); + if (!ensureServiceResult.started) { + console.error( + warn( + `Image analysis via ${imageAnalysisProvider} is unavailable because CCS could not start the local CLIProxy service. This delegation will use native Read.` + ) + ); + imageAnalysisEnv = { + ...imageAnalysisEnv, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }; + } + } // Smart slash command detection and preservation const processedPrompt = this._processSlashCommand(enhancedPrompt); @@ -190,7 +279,9 @@ export class HeadlessExecutor { } } - const launchArgs = appendThirdPartyWebSearchToolArgs(args); + const launchArgs = appendThirdPartyWebSearchToolArgs( + appendThirdPartyImageAnalysisToolArgs(args) + ); const traceEnv = createWebSearchTraceContext({ launcher: 'delegation.headless-executor', args: launchArgs, @@ -217,6 +308,7 @@ export class HeadlessExecutor { sessionId, sessionMgr, claudeConfigDir: inheritedClaudeConfigDir, + imageAnalysisEnv, traceEnv, }); } @@ -235,6 +327,7 @@ export class HeadlessExecutor { sessionId: string | null; sessionMgr: SessionManager; claudeConfigDir?: string; + imageAnalysisEnv?: Record; traceEnv?: Record; } ): Promise { @@ -246,6 +339,7 @@ export class HeadlessExecutor { sessionId, sessionMgr, claudeConfigDir, + imageAnalysisEnv = {}, traceEnv = {}, } = ctx; @@ -265,6 +359,7 @@ export class HeadlessExecutor { ...process.env, ...getClaudeLaunchEnvOverrides(), ...getWebSearchHookEnv(), + ...imageAnalysisEnv, ...traceEnv, ...(claudeConfigDir ? { CLAUDE_CONFIG_DIR: claudeConfigDir } : {}), CCS_PROFILE_TYPE: 'settings', diff --git a/src/management/checks/image-analysis-check.ts b/src/management/checks/image-analysis-check.ts index 34d35394..7ef0645c 100644 --- a/src/management/checks/image-analysis-check.ts +++ b/src/management/checks/image-analysis-check.ts @@ -70,15 +70,17 @@ export async function runImageAnalysisCheck(results: HealthCheck): Promise if (!cliproxyAvailable) { results.details['Image Analysis'] = { status: 'WARN', - info: `Enabled but CLIProxy not running`, + info: 'Enabled; local CLIProxy will start on launch if needed', }; results.warnings.push({ name: 'Image Analysis', - message: 'CLIProxy not running - image analysis will fail', - fix: 'ccs config (starts CLIProxy)', + message: + 'CLIProxy not running yet - CCS will start it automatically when ImageAnalysis is used', + fix: 'Optional warm-up: ccs config', }); - console.log(` ${warn('CLIProxy:')} Not running at http://127.0.0.1:${CLIPROXY_DEFAULT_PORT}`); - console.log(` ${dim('Note:')} Start with: ccs config`); + console.log( + ` ${warn('CLIProxy:')} Idle at http://127.0.0.1:${CLIPROXY_DEFAULT_PORT} (auto-start on launch)` + ); return; } console.log(` ${ok('CLIProxy:')} Available at http://127.0.0.1:${CLIPROXY_DEFAULT_PORT}`); diff --git a/src/management/instance-manager.ts b/src/management/instance-manager.ts index 776ebf98..b9729459 100644 --- a/src/management/instance-manager.ts +++ b/src/management/instance-manager.ts @@ -14,7 +14,7 @@ import { DEFAULT_ACCOUNT_CONTEXT_MODE } from '../auth/account-context'; import type { AccountContextPolicy } from '../auth/account-context'; import { getCcsDir, getCcsHome } from '../utils/config-manager'; -const MANAGED_MCP_SERVER_NAMES = new Set(['ccs-websearch']); +const MANAGED_MCP_SERVER_NAMES = new Set(['ccs-websearch', 'ccs-image-analysis']); /** Options for instance creation */ export interface InstanceOptions { diff --git a/src/utils/hooks/get-image-analysis-hook-env.ts b/src/utils/hooks/get-image-analysis-hook-env.ts index 40835023..b68546eb 100644 --- a/src/utils/hooks/get-image-analysis-hook-env.ts +++ b/src/utils/hooks/get-image-analysis-hook-env.ts @@ -8,7 +8,9 @@ */ import { getImageAnalysisConfig } from '../../config/unified-config-loader'; +import { resolveCliproxyBridgeProfile } from '../../api/services/cliproxy-profile-bridge'; import { mapExternalProviderName } from '../../cliproxy/provider-capabilities'; +import { getPromptsDir } from '../image-analysis/hook-installer'; import { resolveImageAnalysisStatus, type ImageAnalysisResolutionContext, @@ -23,6 +25,14 @@ function serializeProviderModels(providerModels: Record): string .join(','); } +export interface ImageAnalysisRuntimeOverrides { + backendId?: string | null; + model?: string | null; + runtimePath?: string | null; + baseUrl?: string | null; + apiKey?: string | null; +} + /** * Get image analysis hook environment variables. * These env vars control the hook's behavior via Claude Code hook system. @@ -45,12 +55,66 @@ export function getImageAnalysisHookEnv( ? resolveImageAnalysisStatus(context, config) : resolveImageAnalysisStatus({ profileName: '' }, config); const skipImageAnalysis = !status.supported; + const runtimeApiKey = + typeof context === 'object' && context.cliproxyBridge + ? resolveCliproxyBridgeProfile(context.cliproxyBridge.provider).apiKey + : ''; return { CCS_IMAGE_ANALYSIS_ENABLED: config.enabled ? '1' : '0', CCS_IMAGE_ANALYSIS_TIMEOUT: String(Number(config.timeout) || 60), CCS_IMAGE_ANALYSIS_PROVIDER_MODELS: serializeProviderModels(config.provider_models), CCS_CURRENT_PROVIDER: status.backendId || '', + CCS_IMAGE_ANALYSIS_BACKEND_ID: status.backendId || '', + CCS_IMAGE_ANALYSIS_MODEL: status.model || '', + CCS_IMAGE_ANALYSIS_RUNTIME_PATH: status.runtimePath || '', + CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL: + typeof context === 'object' ? context.cliproxyBridge?.currentBaseUrl || '' : '', + ...(runtimeApiKey ? { CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY: runtimeApiKey } : {}), + CCS_IMAGE_ANALYSIS_PROMPTS_DIR: getPromptsDir(), CCS_IMAGE_ANALYSIS_SKIP: skipImageAnalysis ? '1' : '0', }; } + +/** + * Overlay execution-specific runtime values onto the baseline image-analysis env. + * Launch paths use this to pin analysis to the exact provider route and auth + * token selected for the current session rather than any stale saved values. + */ +export function applyImageAnalysisRuntimeOverrides( + env: Record, + overrides: ImageAnalysisRuntimeOverrides +): Record { + const nextEnv = { ...env }; + + const backendId = overrides.backendId?.trim(); + if (backendId) { + nextEnv.CCS_CURRENT_PROVIDER = backendId; + nextEnv.CCS_IMAGE_ANALYSIS_BACKEND_ID = backendId; + } + + const model = overrides.model?.trim(); + if (model) { + nextEnv.CCS_IMAGE_ANALYSIS_MODEL = model; + } + + const runtimePath = overrides.runtimePath?.trim(); + if (runtimePath) { + nextEnv.CCS_IMAGE_ANALYSIS_RUNTIME_PATH = runtimePath; + } + + if (overrides.baseUrl !== undefined) { + nextEnv.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL = overrides.baseUrl?.trim() || ''; + } + + if (overrides.apiKey !== undefined) { + const apiKey = overrides.apiKey?.trim(); + if (apiKey) { + nextEnv.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY = apiKey; + } else { + delete nextEnv.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY; + } + } + + return nextEnv; +} diff --git a/src/utils/hooks/image-analysis-backend-resolver.ts b/src/utils/hooks/image-analysis-backend-resolver.ts index ec7921e4..2138893a 100644 --- a/src/utils/hooks/image-analysis-backend-resolver.ts +++ b/src/utils/hooks/image-analysis-backend-resolver.ts @@ -566,13 +566,13 @@ export function resolveImageAnalysisStatus( ? 'CLIProxy runtime readiness has not been verified yet.' : null, effectiveRuntimeMode: - config.enabled && resolution.backendId && model && status !== 'hook-missing' - ? 'cliproxy-image-analysis' - : 'native-read', + config.enabled && resolution.backendId && model ? 'cliproxy-image-analysis' : 'native-read', effectiveRuntimeReason: - status === 'hook-missing' || !config.enabled || !resolution.backendId || !model + !config.enabled || !resolution.backendId || !model ? reason - : null, + : status === 'attention' || status === 'hook-missing' + ? reason + : null, profileModel: nativeSupport.profileModel, nativeReadPreference: nativeSupport.nativeReadPreference, nativeImageCapable: nativeSupport.nativeImageCapable, diff --git a/src/utils/hooks/image-analysis-runtime-status.ts b/src/utils/hooks/image-analysis-runtime-status.ts index 8fd910d2..7897b8c5 100644 --- a/src/utils/hooks/image-analysis-runtime-status.ts +++ b/src/utils/hooks/image-analysis-runtime-status.ts @@ -119,13 +119,6 @@ function resolveEffectiveRuntime( }; } - if (status.status === 'hook-missing') { - return { - effectiveRuntimeMode: 'native-read', - effectiveRuntimeReason: status.reason, - }; - } - if (status.authReadiness === 'missing' || status.authReadiness === 'unknown') { return { effectiveRuntimeMode: 'native-read', @@ -142,7 +135,8 @@ function resolveEffectiveRuntime( return { effectiveRuntimeMode: 'cliproxy-image-analysis', - effectiveRuntimeReason: status.status === 'attention' ? status.reason : null, + effectiveRuntimeReason: + status.status === 'attention' || status.status === 'hook-missing' ? status.reason : null, }; } diff --git a/src/utils/hooks/image-analyzer-hook-installer.ts b/src/utils/hooks/image-analyzer-hook-installer.ts index 26f32224..e124c74a 100644 --- a/src/utils/hooks/image-analyzer-hook-installer.ts +++ b/src/utils/hooks/image-analyzer-hook-installer.ts @@ -14,6 +14,7 @@ import { getImageAnalyzerHookPath } from './image-analyzer-hook-configuration'; import { getCcsHooksDir } from '../config-manager'; import { getImageAnalysisConfig } from '../../config/unified-config-loader'; import { removeMigrationMarker } from './image-analyzer-profile-hook-injector'; +import { installImageAnalysisPrompts } from '../image-analysis/hook-installer'; // Re-export from hook-configuration for backward compatibility export { @@ -23,12 +24,65 @@ export { // Hook file name const IMAGE_ANALYZER_HOOK = 'image-analyzer-transformer.cjs'; +const IMAGE_ANALYSIS_RUNTIME = 'image-analysis-runtime.cjs'; + +function getImageAnalysisRuntimeHookPath(): string { + return path.join(getCcsHooksDir(), IMAGE_ANALYSIS_RUNTIME); +} + +function getHookArtifacts(): Array<{ fileName: string; destinationPath: string }> { + return [ + { fileName: IMAGE_ANALYZER_HOOK, destinationPath: getImageAnalyzerHookPath() }, + { + fileName: IMAGE_ANALYSIS_RUNTIME, + destinationPath: getImageAnalysisRuntimeHookPath(), + }, + ]; +} + +function resolveHookSourceBasePath( + artifacts: Array<{ fileName: string; destinationPath: string }> +): string | null { + const possibleBasePaths = [ + path.join(__dirname, '..', '..', '..', 'lib', 'hooks'), + path.join(__dirname, '..', '..', 'lib', 'hooks'), + path.join(__dirname, '..', 'lib', 'hooks'), + ]; + + for (const basePath of possibleBasePaths) { + if (artifacts.every(({ fileName }) => fs.existsSync(path.join(basePath, fileName)))) { + return basePath; + } + } + + return null; +} + +function artifactsMatch(sourcePath: string, destinationPath: string): boolean { + try { + return fs.readFileSync(sourcePath).equals(fs.readFileSync(destinationPath)); + } catch { + return false; + } +} /** * Check if image analyzer hook is installed */ export function hasImageAnalyzerHook(): boolean { - return fs.existsSync(getImageAnalyzerHookPath()); + const artifacts = getHookArtifacts(); + if (!artifacts.every(({ destinationPath }) => fs.existsSync(destinationPath))) { + return false; + } + + const sourceBasePath = resolveHookSourceBasePath(artifacts); + if (!sourceBasePath) { + return true; + } + + return artifacts.every(({ fileName, destinationPath }) => + artifactsMatch(path.join(sourceBasePath, fileName), destinationPath) + ); } /** @@ -56,38 +110,25 @@ export function installImageAnalyzerHook(): boolean { fs.mkdirSync(hooksDir, { recursive: true, mode: 0o700 }); } - const hookPath = getImageAnalyzerHookPath(); + const artifacts = getHookArtifacts(); + const sourceBasePath = resolveHookSourceBasePath(artifacts); - // Find the bundled hook script - // In npm package: node_modules/ccs/lib/hooks/ - // In development: lib/hooks/ - const possiblePaths = [ - path.join(__dirname, '..', '..', '..', 'lib', 'hooks', IMAGE_ANALYZER_HOOK), - path.join(__dirname, '..', '..', 'lib', 'hooks', IMAGE_ANALYZER_HOOK), - path.join(__dirname, '..', 'lib', 'hooks', IMAGE_ANALYZER_HOOK), - ]; - - let sourcePath: string | null = null; - for (const p of possiblePaths) { - if (fs.existsSync(p)) { - sourcePath = p; - break; - } - } - - if (!sourcePath) { + if (!sourceBasePath) { if (process.env.CCS_DEBUG) { console.error(warn(`Image analyzer hook source not found: ${IMAGE_ANALYZER_HOOK}`)); } return false; } - // Copy hook to ~/.ccs/hooks/ - fs.copyFileSync(sourcePath, hookPath); - fs.chmodSync(hookPath, 0o755); + for (const { fileName, destinationPath } of artifacts) { + fs.copyFileSync(path.join(sourceBasePath, fileName), destinationPath); + fs.chmodSync(destinationPath, 0o755); + } + + installImageAnalysisPrompts(); if (process.env.CCS_DEBUG) { - console.error(info(`Installed image analyzer hook: ${hookPath}`)); + console.error(info(`Installed image analyzer hook runtime: ${hooksDir}`)); } // Note: Hook registration is handled by ensureProfileHooks() in image-analyzer-profile-injector.ts @@ -113,12 +154,14 @@ export function installImageAnalyzerHook(): boolean { */ export function uninstallImageAnalyzerHook(): boolean { try { - const hookPath = getImageAnalyzerHookPath(); + const artifactPaths = [getImageAnalyzerHookPath(), getImageAnalysisRuntimeHookPath()]; - if (fs.existsSync(hookPath)) { - fs.unlinkSync(hookPath); - if (process.env.CCS_DEBUG) { - console.error(info(`Uninstalled image analyzer hook: ${hookPath}`)); + for (const artifactPath of artifactPaths) { + if (fs.existsSync(artifactPath)) { + fs.unlinkSync(artifactPath); + if (process.env.CCS_DEBUG) { + console.error(info(`Uninstalled image analyzer artifact: ${artifactPath}`)); + } } } diff --git a/src/utils/hooks/image-analyzer-profile-hook-injector.ts b/src/utils/hooks/image-analyzer-profile-hook-injector.ts index a2b28978..7517dcf9 100644 --- a/src/utils/hooks/image-analyzer-profile-hook-injector.ts +++ b/src/utils/hooks/image-analyzer-profile-hook-injector.ts @@ -138,6 +138,10 @@ export function ensureProfileHooks(input: string | ImageAnalysisResolutionContex return false; } + if (context.sharedHookInstalled === false) { + return false; + } + // One-time migration marker migrateGlobalHook(); diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 68973870..913ff285 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -6,7 +6,16 @@ * @module utils/hooks */ -export { getImageAnalysisHookEnv } from './get-image-analysis-hook-env'; +import { + hasImageAnalyzerHook as hasInstalledImageAnalyzerHook, + installImageAnalyzerHook as installSharedImageAnalyzerHook, +} from './image-analyzer-hook-installer'; + +export { + getImageAnalysisHookEnv, + applyImageAnalysisRuntimeOverrides, + type ImageAnalysisRuntimeOverrides, +} from './get-image-analysis-hook-env'; export { canonicalizeImageAnalysisConfig, resolveImageAnalysisStatus, @@ -26,3 +35,7 @@ export { uninstallImageAnalyzerHook, } from './image-analyzer-hook-installer'; export { ensureProfileHooks as ensureImageAnalyzerProfileHooks } from './image-analyzer-profile-hook-injector'; + +export function prepareImageAnalysisFallbackHook(): boolean { + return hasInstalledImageAnalyzerHook() || installSharedImageAnalyzerHook(); +} diff --git a/src/utils/image-analysis/claude-tool-args.ts b/src/utils/image-analysis/claude-tool-args.ts new file mode 100644 index 00000000..f1d0cf0b --- /dev/null +++ b/src/utils/image-analysis/claude-tool-args.ts @@ -0,0 +1,74 @@ +/** + * Claude launch argument helpers for first-class Image Analysis. + */ + +const APPEND_SYSTEM_PROMPT_FLAG = '--append-system-prompt'; +const IMAGE_ANALYSIS_STEERING_PROMPT = + 'For local image or PDF files, prefer the CCS MCP tool ImageAnalysis instead of Read. Use Read for text, code, and other plain files. If the user asks a specific question about the visual, pass that question as the focus field when useful. If ImageAnalysis is unavailable or fails, you may fall back to Read.'; + +function splitArgsAtTerminator(args: string[]): { optionArgs: string[]; trailingArgs: string[] } { + const terminatorIndex = args.indexOf('--'); + if (terminatorIndex === -1) { + return { optionArgs: args, trailingArgs: [] }; + } + + return { + optionArgs: args.slice(0, terminatorIndex), + trailingArgs: args.slice(terminatorIndex), + }; +} + +function getImmediateFlagValue(args: string[], index: number): string | null { + const value = args[index + 1]; + if (value === undefined || value === '--' || value.startsWith('--')) { + return null; + } + return value; +} + +function hasExactFlagValue(args: string[], flag: string, expectedValue: string): boolean { + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + + if (arg === flag) { + const value = getImmediateFlagValue(args, index); + if (value === expectedValue) { + return true; + } + continue; + } + + if (arg === `${flag}=${expectedValue}`) { + return true; + } + + if (arg.startsWith(`${flag}=`) && arg.slice(flag.length + 1) === expectedValue) { + return true; + } + } + + return false; +} + +function ensureImageAnalysisSteeringPrompt(args: string[]): string[] { + const { optionArgs, trailingArgs } = splitArgsAtTerminator(args); + + if (hasExactFlagValue(optionArgs, APPEND_SYSTEM_PROMPT_FLAG, IMAGE_ANALYSIS_STEERING_PROMPT)) { + return args; + } + + return [ + ...optionArgs, + APPEND_SYSTEM_PROMPT_FLAG, + IMAGE_ANALYSIS_STEERING_PROMPT, + ...trailingArgs, + ]; +} + +export function appendThirdPartyImageAnalysisToolArgs(args: string[]): string[] { + return ensureImageAnalysisSteeringPrompt(args); +} + +export function getImageAnalysisSteeringPrompt(): string { + return IMAGE_ANALYSIS_STEERING_PROMPT; +} diff --git a/src/utils/image-analysis/index.ts b/src/utils/image-analysis/index.ts index e239ae6d..89d5968d 100644 --- a/src/utils/image-analysis/index.ts +++ b/src/utils/image-analysis/index.ts @@ -5,3 +5,23 @@ */ export { getPromptsDir, installImageAnalysisPrompts } from './hook-installer'; +export { + getImageAnalysisMcpServerName, + getImageAnalysisMcpServerPath, + getImageAnalysisMcpRuntimePath, + installImageAnalysisMcpServer, + ensureImageAnalysisMcpConfig, + ensureImageAnalysisMcp, + uninstallImageAnalysisMcpServer, + removeImageAnalysisMcpConfig, + uninstallImageAnalysisMcp, + syncImageAnalysisMcpToConfigDir, + ensureImageAnalysisMcpOrThrow, +} from './mcp-installer'; +export { + appendThirdPartyImageAnalysisToolArgs, + getImageAnalysisSteeringPrompt, +} from './claude-tool-args'; + +export const IMAGE_ANALYSIS_PROMPT_TEMPLATES = ['default', 'screenshot', 'document'] as const; +export type ImageAnalysisPromptTemplate = (typeof IMAGE_ANALYSIS_PROMPT_TEMPLATES)[number]; diff --git a/src/utils/image-analysis/mcp-installer.ts b/src/utils/image-analysis/mcp-installer.ts new file mode 100644 index 00000000..11d53607 --- /dev/null +++ b/src/utils/image-analysis/mcp-installer.ts @@ -0,0 +1,393 @@ +/** + * Image Analysis MCP installer and ~/.claude.json provisioning. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import { getImageAnalysisConfig } from '../../config/unified-config-loader'; +import { getCcsDir } from '../config-manager'; +import { getClaudeUserConfigPath } from '../claude-config-path'; +import { info, warn } from '../ui'; +import { InstanceManager } from '../../management/instance-manager'; +import { installImageAnalysisPrompts } from './hook-installer'; + +const IMAGE_ANALYSIS_MCP_SERVER = 'ccs-image-analysis-server.cjs'; +const IMAGE_ANALYSIS_MCP_RUNTIME = 'image-analysis-runtime.cjs'; +const IMAGE_ANALYSIS_MCP_SERVER_NAME = 'ccs-image-analysis'; + +interface ClaudeUserConfig { + mcpServers?: Record; + [key: string]: unknown; +} + +interface ManagedImageAnalysisMcpConfig { + type: 'stdio'; + command: 'node'; + args: [string]; + env: Record; +} + +function getCcsMcpDir(): string { + return path.join(getCcsDir(), 'mcp'); +} + +export function getImageAnalysisMcpServerName(): string { + return IMAGE_ANALYSIS_MCP_SERVER_NAME; +} + +export function getImageAnalysisMcpServerPath(): string { + return path.join(getCcsMcpDir(), IMAGE_ANALYSIS_MCP_SERVER); +} + +export function getImageAnalysisMcpRuntimePath(): string { + return path.join(getCcsMcpDir(), IMAGE_ANALYSIS_MCP_RUNTIME); +} + +function hasMatchingContents(sourcePath: string, destinationPath: string): boolean { + if (!fs.existsSync(destinationPath)) { + return false; + } + + const source = fs.readFileSync(sourcePath); + try { + const destination = fs.readFileSync(destinationPath); + return source.equals(destination); + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn(`Existing Image Analysis MCP server is unreadable: ${(error as Error).message}`) + ); + } + return false; + } +} + +function getTempPath(targetPath: string): string { + const suffix = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`; + return `${targetPath}.${suffix}.tmp`; +} + +function resolveBundledArtifactSourcePath(fileName: string): string | null { + const possiblePaths = [ + path.join(__dirname, '..', '..', '..', 'lib', 'mcp', fileName), + path.join(__dirname, '..', '..', 'lib', 'mcp', fileName), + path.join(__dirname, '..', 'lib', 'mcp', fileName), + path.join(__dirname, '..', '..', '..', 'lib', 'hooks', fileName), + path.join(__dirname, '..', '..', 'lib', 'hooks', fileName), + path.join(__dirname, '..', 'lib', 'hooks', fileName), + ]; + + for (const candidate of possiblePaths) { + if (fs.existsSync(candidate)) { + return candidate; + } + } + + return null; +} + +function readClaudeUserConfig(configPath: string): ClaudeUserConfig | null { + if (!fs.existsSync(configPath)) { + return {}; + } + + try { + const raw = fs.readFileSync(configPath, 'utf8'); + const parsed = JSON.parse(raw); + if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { + return null; + } + return parsed as ClaudeUserConfig; + } catch { + return null; + } +} + +function writeClaudeUserConfig(configPath: string, config: ClaudeUserConfig): boolean { + const tempPath = getTempPath(configPath); + const fileMode = fs.existsSync(configPath) ? fs.statSync(configPath).mode & 0o777 : 0o600; + + try { + fs.writeFileSync(tempPath, JSON.stringify(config, null, 2) + '\n', 'utf8'); + fs.chmodSync(tempPath, fileMode); + fs.renameSync(tempPath, configPath); + return true; + } finally { + if (fs.existsSync(tempPath)) { + fs.unlinkSync(tempPath); + } + } +} + +function removeManagedServerConfig(configPath: string): boolean { + if (!fs.existsSync(configPath)) { + return false; + } + + const config = readClaudeUserConfig(configPath); + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Malformed Claude config prevents MCP cleanup: ${configPath}`)); + } + return false; + } + + const existingServers = + config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers) + ? { ...(config.mcpServers as Record) } + : {}; + + if (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) { + return false; + } + + delete existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + + const nextConfig: ClaudeUserConfig = { ...config }; + if (Object.keys(existingServers).length === 0) { + delete nextConfig.mcpServers; + } else { + nextConfig.mcpServers = existingServers; + } + + try { + writeClaudeUserConfig(configPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Removed Image Analysis MCP config from ${configPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn( + `Failed to remove Image Analysis MCP config from ${configPath}: ${(error as Error).message}` + ) + ); + } + return false; + } +} + +export function installImageAnalysisMcpServer(): boolean { + const config = getImageAnalysisConfig(); + if (!config.enabled) { + return false; + } + + const artifacts = [ + { + fileName: IMAGE_ANALYSIS_MCP_SERVER, + sourcePath: resolveBundledArtifactSourcePath(IMAGE_ANALYSIS_MCP_SERVER), + destinationPath: getImageAnalysisMcpServerPath(), + }, + { + fileName: IMAGE_ANALYSIS_MCP_RUNTIME, + sourcePath: resolveBundledArtifactSourcePath(IMAGE_ANALYSIS_MCP_RUNTIME), + destinationPath: getImageAnalysisMcpRuntimePath(), + }, + ]; + + const missingArtifact = artifacts.find((artifact) => !artifact.sourcePath); + if (missingArtifact) { + if (process.env.CCS_DEBUG) { + console.error( + warn(`Image Analysis MCP runtime source not found: ${missingArtifact.fileName}`) + ); + } + return false; + } + + const mcpDir = getCcsMcpDir(); + if (!fs.existsSync(mcpDir)) { + fs.mkdirSync(mcpDir, { recursive: true, mode: 0o700 }); + } + + try { + for (const artifact of artifacts) { + const sourcePath = artifact.sourcePath; + if (!sourcePath) { + continue; + } + + if (hasMatchingContents(sourcePath, artifact.destinationPath)) { + continue; + } + + const tempPath = getTempPath(artifact.destinationPath); + + try { + fs.copyFileSync(sourcePath, tempPath); + fs.chmodSync(tempPath, 0o755); + try { + fs.renameSync(tempPath, artifact.destinationPath); + } catch (renameError) { + const errorCode = (renameError as NodeJS.ErrnoException).code; + if (errorCode !== 'EEXIST' && errorCode !== 'EPERM') { + throw renameError; + } + + if (!hasMatchingContents(sourcePath, artifact.destinationPath)) { + fs.copyFileSync(tempPath, artifact.destinationPath); + fs.chmodSync(artifact.destinationPath, 0o755); + } + } + } finally { + if (fs.existsSync(tempPath)) { + fs.unlinkSync(tempPath); + } + } + } + + installImageAnalysisPrompts(); + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn(`Failed to install Image Analysis MCP server: ${(error as Error).message}`) + ); + } + return false; + } +} + +export function ensureImageAnalysisMcpConfig(): boolean { + const imageConfig = getImageAnalysisConfig(); + if (!imageConfig.enabled) { + return false; + } + + const claudeUserConfigPath = getClaudeUserConfigPath(); + const claudeUserConfigDir = path.dirname(claudeUserConfigPath); + const config = readClaudeUserConfig(claudeUserConfigPath); + + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn('Malformed ~/.claude.json prevents Image Analysis MCP provisioning')); + } + return false; + } + + if (!fs.existsSync(claudeUserConfigDir)) { + fs.mkdirSync(claudeUserConfigDir, { recursive: true, mode: 0o700 }); + } + + const existingServers = + config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers) + ? (config.mcpServers as Record) + : {}; + const desiredServerConfig: ManagedImageAnalysisMcpConfig = { + type: 'stdio', + command: 'node', + args: [getImageAnalysisMcpServerPath()], + env: {}, + }; + + const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + if ( + typeof currentConfig === 'object' && + currentConfig !== null && + JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig) + ) { + return true; + } + + const nextConfig: ClaudeUserConfig = { + ...config, + mcpServers: { + ...existingServers, + [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig, + }, + }; + + try { + writeClaudeUserConfig(claudeUserConfigPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Failed to update ~/.claude.json: ${(error as Error).message}`)); + } + return false; + } +} + +export function ensureImageAnalysisMcp(): boolean { + const imageConfig = getImageAnalysisConfig(); + if (!imageConfig.enabled) { + return false; + } + + const installed = installImageAnalysisMcpServer(); + const configured = installed && ensureImageAnalysisMcpConfig(); + return installed && configured; +} + +export function syncImageAnalysisMcpToConfigDir(claudeConfigDir: string | undefined): boolean { + if (!claudeConfigDir) { + return false; + } + + return new InstanceManager().syncMcpServers(claudeConfigDir); +} + +export function uninstallImageAnalysisMcpServer(): boolean { + const artifactPaths = [getImageAnalysisMcpServerPath(), getImageAnalysisMcpRuntimePath()]; + if (!artifactPaths.some((artifactPath) => fs.existsSync(artifactPath))) { + return false; + } + + try { + let removed = false; + for (const artifactPath of artifactPaths) { + if (!fs.existsSync(artifactPath)) { + continue; + } + fs.unlinkSync(artifactPath); + removed = true; + } + return removed; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn(`Failed to remove Image Analysis MCP server: ${(error as Error).message}`) + ); + } + return false; + } +} + +export function removeImageAnalysisMcpConfig(): boolean { + let removed = removeManagedServerConfig(getClaudeUserConfigPath()); + + const instanceManager = new InstanceManager(); + for (const instanceName of instanceManager.listInstances()) { + const instancePath = instanceManager.getInstancePath(instanceName); + const instanceClaudeConfigPath = path.join(instancePath, '.claude.json'); + removed = removeManagedServerConfig(instanceClaudeConfigPath) || removed; + } + + return removed; +} + +export function uninstallImageAnalysisMcp(): boolean { + const removedConfig = removeImageAnalysisMcpConfig(); + const removedServer = uninstallImageAnalysisMcpServer(); + return removedConfig || removedServer; +} + +export function ensureImageAnalysisMcpOrThrow(): void { + const imageConfig = getImageAnalysisConfig(); + if (!imageConfig.enabled) { + return; + } + + if (!ensureImageAnalysisMcp()) { + console.error( + warn( + 'Image Analysis is enabled, but CCS could not prepare the local ImageAnalysis tool. This session will fall back to native Read.' + ) + ); + } +} diff --git a/src/web-server/routes/image-analysis-routes.ts b/src/web-server/routes/image-analysis-routes.ts index 3b98337e..06148c82 100644 --- a/src/web-server/routes/image-analysis-routes.ts +++ b/src/web-server/routes/image-analysis-routes.ts @@ -84,7 +84,6 @@ function resolveCurrentTargetMode( if (target !== 'claude') return 'bypassed'; if (status.nativeReadPreference) return 'native'; if (!status.backendId) return 'unresolved'; - if (status.status === 'hook-missing') return 'setup'; if (status.effectiveRuntimeMode === 'native-read') return 'fallback'; return 'active'; } From f379a4001f768cb7885fbffb322e63d678244aec Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 15:43:09 -0400 Subject: [PATCH 08/48] test(image-analysis): cover runtime auth and fallback --- tests/e2e/image-analyzer-hook.e2e.test.ts | 124 ++++-- .../api/profile-lifecycle-service.test.ts | 13 +- .../unit/api/profile-writer-anthropic.test.ts | 15 +- .../env-resolver-codex-fallback.test.ts | 38 ++ .../unit/copilot/copilot-executor-env.test.ts | 4 + .../ccs-image-analysis-mcp-server.test.ts | 380 ++++++++++++++++++ ...ings-profile-image-analysis-launch.test.ts | 198 +++++++++ .../utils/claudecode-env-stripping.test.ts | 35 ++ .../image-analysis-runtime-status.test.ts | 4 +- .../image-analyzer-hook-installer.test.ts | 64 +++ ...age-analyzer-profile-hook-injector.test.ts | 19 + .../image-analysis/claude-tool-args.test.ts | 39 ++ .../image-analysis/mcp-installer.test.ts | 180 +++++++++ .../web-server/image-analysis-routes.test.ts | 2 +- ...tings-routes-image-analysis-status.test.ts | 14 +- 15 files changed, 1077 insertions(+), 52 deletions(-) create mode 100644 tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts create mode 100644 tests/unit/targets/settings-profile-image-analysis-launch.test.ts create mode 100644 tests/unit/utils/hooks/image-analyzer-hook-installer.test.ts create mode 100644 tests/unit/utils/image-analysis/claude-tool-args.test.ts create mode 100644 tests/unit/utils/image-analysis/mcp-installer.test.ts diff --git a/tests/e2e/image-analyzer-hook.e2e.test.ts b/tests/e2e/image-analyzer-hook.e2e.test.ts index d9a3015b..332f95da 100644 --- a/tests/e2e/image-analyzer-hook.e2e.test.ts +++ b/tests/e2e/image-analyzer-hook.e2e.test.ts @@ -17,7 +17,7 @@ */ import { describe, it, expect, beforeAll, afterAll } from 'bun:test'; -import { spawnSync } from 'child_process'; +import { spawn, spawnSync } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as http from 'http'; @@ -163,6 +163,56 @@ function invokeHook( }; } +function invokeHookAsync( + input: object, + env: Record = {} +): Promise<{ code: number; stdout: string; stderr: string }> { + return new Promise((resolve, reject) => { + const child = spawn('node', [HOOK_PATH], { + env: { + ...process.env, + CCS_CLIPROXY_API_KEY: CLIPROXY_API_KEY, + CCS_CLIPROXY_PORT: String(MOCK_PORT), + CCS_IMAGE_ANALYSIS_PROVIDER_MODELS: DEFAULT_PROVIDER_MODELS, + CCS_CURRENT_PROVIDER: DEFAULT_PROVIDER, + ...env, + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + let stdout = ''; + let stderr = ''; + const timer = setTimeout(() => { + child.kill('SIGKILL'); + reject(new Error('Hook timed out')); + }, 10000); + + child.stdout.on('data', (chunk) => { + stdout += chunk.toString(); + }); + + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + }); + + child.on('error', (error) => { + clearTimeout(timer); + reject(error); + }); + + child.on('close', (code) => { + clearTimeout(timer); + resolve({ + code: code ?? -1, + stdout, + stderr, + }); + }); + + child.stdin.end(JSON.stringify(input)); + }); +} + /** * Create a minimal valid PNG file (1x1 red pixel) */ @@ -272,15 +322,8 @@ function createTestTextFile(filepath: string, content: string): void { * Create a large file exceeding 10MB */ function createLargeFile(filepath: string, sizeMB: number): void { - const bufferSize = 1024 * 1024; // 1MB - const totalBuffers = sizeMB; - const buffer = Buffer.alloc(bufferSize, 'A'); - const stream = fs.createWriteStream(filepath); - - for (let i = 0; i < totalBuffers; i++) { - stream.write(buffer); - } - stream.end(); + const totalBytes = sizeMB * 1024 * 1024; + fs.writeFileSync(filepath, Buffer.alloc(totalBytes, 'A')); } // ============================================================================ @@ -498,12 +541,12 @@ describe('Image Analyzer Hook', () => { // ========================================================================== describe('File Size Limits', () => { - it('should reject files larger than 10MB', () => { + it('should reject files larger than 10MB', async () => { // Create 11MB file const largePath = path.join(TEST_DIR, 'large-test.png'); createLargeFile(largePath, 11); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: largePath }, @@ -533,7 +576,7 @@ describe('Image Analyzer Hook', () => { it('should block when CLIProxy is unavailable to prevent context overflow', async () => { // Force hook to use a port that's definitely not running - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -553,7 +596,7 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toContain('CLIProxy unavailable'); }); - it('should analyze PNG via mock CLIProxy and return analysis', () => { + it('should analyze PNG via mock CLIProxy and return analysis', async () => { resetMockState(); mockResponse = { content: @@ -561,7 +604,7 @@ describe('Image Analyzer Hook', () => { statusCode: 200, }; - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -577,14 +620,14 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toContain('red square'); }); - it('should analyze JPEG via mock CLIProxy', () => { + it('should analyze JPEG via mock CLIProxy', async () => { resetMockState(); mockResponse = { content: 'A minimalist white image, possibly a blank canvas or placeholder.', statusCode: 200, }; - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testJpegPath }, @@ -598,10 +641,10 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toContain('white image'); }); - it('should include API key in request header', () => { + it('should include API key in request header', async () => { resetMockState(); - invokeHook( + await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -614,10 +657,10 @@ describe('Image Analyzer Hook', () => { expect(lastRequest?.headers['x-api-key']).toBe(CLIPROXY_API_KEY); }); - it('should send correct request format to CLIProxy', () => { + it('should send correct request format to CLIProxy', async () => { resetMockState(); - invokeHook( + await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -627,13 +670,14 @@ describe('Image Analyzer Hook', () => { CCS_PROFILE_TYPE: 'cliproxy', CCS_CURRENT_PROVIDER: 'agy', CCS_IMAGE_ANALYSIS_PROVIDER_MODELS: 'agy:gemini-3-1-flash-preview', + CCS_IMAGE_ANALYSIS_RUNTIME_PATH: '/api/provider/agy', } ); // Verify request format expect(lastRequest).not.toBeNull(); expect(lastRequest?.method).toBe('POST'); - expect(lastRequest?.path).toBe('/v1/messages'); + expect(lastRequest?.path).toBe('/api/provider/agy/v1/messages'); const body = lastRequest?.body as { model: string; @@ -664,10 +708,10 @@ describe('Image Analyzer Hook', () => { expect(imageContent?.source?.data).toBeDefined(); }); - it('should use correct media type for JPEG', () => { + it('should use correct media type for JPEG', async () => { resetMockState(); - invokeHook( + await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testJpegPath }, @@ -687,10 +731,10 @@ describe('Image Analyzer Hook', () => { expect(imageContent?.source?.media_type).toBe('image/jpeg'); }); - it('should respect debug mode and output debug messages', () => { + it('should respect debug mode and output debug messages', async () => { resetMockState(); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -703,14 +747,14 @@ describe('Image Analyzer Hook', () => { expect(result.stderr).toContain('Starting image analysis'); }); - it('should handle API error response gracefully (pass through)', () => { + it('should handle API error response gracefully (pass through)', async () => { resetMockState(); mockResponse = { content: '', statusCode: 500, }; - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -726,10 +770,10 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toContain('Error'); }); - it('should use model from provider_models mapping', () => { + it('should use model from provider_models mapping', async () => { resetMockState(); - invokeHook( + await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -747,8 +791,8 @@ describe('Image Analyzer Hook', () => { expect(body.model).toBe('gpt-5.1-codex-mini'); // Model from provider_models }); - it('should skip when provider is not in provider_models', () => { - const result = invokeHook( + it('should skip when provider is not in provider_models', async () => { + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -770,10 +814,10 @@ describe('Image Analyzer Hook', () => { // ========================================================================== describe('Output Format Validation', () => { - it('should output valid JSON structure on success', () => { + it('should output valid JSON structure on success', async () => { resetMockState(); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -794,10 +838,10 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toBeDefined(); }); - it('should include filename in output', () => { + it('should include filename in output', async () => { resetMockState(); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -809,10 +853,10 @@ describe('Image Analyzer Hook', () => { expect(output.hookSpecificOutput.permissionDecisionReason).toContain('test-image.png'); }); - it('should include model name in output', () => { + it('should include model name in output', async () => { resetMockState(); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: testPngPath }, @@ -826,7 +870,7 @@ describe('Image Analyzer Hook', () => { ); }); - it('should output valid JSON structure on file read error', () => { + it('should output valid JSON structure on file read error', async () => { // Create and immediately delete file to trigger error const errorPath = path.join(TEST_DIR, 'error-test.png'); createTestPng(errorPath); @@ -834,7 +878,7 @@ describe('Image Analyzer Hook', () => { // Make file unreadable (simulate permission error) fs.chmodSync(errorPath, 0o000); - const result = invokeHook( + const result = await invokeHookAsync( { tool_name: 'Read', tool_input: { file_path: errorPath }, diff --git a/tests/unit/api/profile-lifecycle-service.test.ts b/tests/unit/api/profile-lifecycle-service.test.ts index bd832b47..310d9af5 100644 --- a/tests/unit/api/profile-lifecycle-service.test.ts +++ b/tests/unit/api/profile-lifecycle-service.test.ts @@ -171,15 +171,22 @@ describe('profile lifecycle service', () => { fs.writeFileSync(path.join(ccsDir, 'config.json'), JSON.stringify({ profiles: {} }, null, 2) + '\n'); fs.writeFileSync(path.join(ccsDir, 'config.yaml'), 'version: 12\nwebsearch:\n enabled: false\n', 'utf8'); - const copyFileSpy = spyOn(fs, 'copyFileSync').mockImplementation(() => { - throw new Error('copy should not run when WebSearch is disabled'); + const originalCopyFileSync = fs.copyFileSync.bind(fs); + const copyFileSpy = spyOn(fs, 'copyFileSync').mockImplementation((source, destination) => { + const sourcePath = String(source); + const destinationPath = String(destination); + if (sourcePath.includes('websearch') || destinationPath.includes('websearch')) { + throw new Error('websearch copy should not run when WebSearch is disabled'); + } + return originalCopyFileSync(source, destination); }); const result = await runInScopedCcsDir(() => registerApiProfileOrphans({ names: ['extra'] })); - expect(copyFileSpy).not.toHaveBeenCalled(); + expect(copyFileSpy).toHaveBeenCalled(); expect(result.registered).toEqual(['extra']); expect(result.skipped).toEqual([]); + expect(fs.existsSync(path.join(ccsDir, 'hooks', 'websearch-transformer.cjs'))).toBe(false); }); it('registers malformed orphan settings when force bypasses validation', async () => { diff --git a/tests/unit/api/profile-writer-anthropic.test.ts b/tests/unit/api/profile-writer-anthropic.test.ts index d3c52fa7..6eab981b 100644 --- a/tests/unit/api/profile-writer-anthropic.test.ts +++ b/tests/unit/api/profile-writer-anthropic.test.ts @@ -130,8 +130,14 @@ describe('profile-writer Anthropic direct', () => { 'utf8' ); - const copyFileSpy = spyOn(fs, 'copyFileSync').mockImplementation(() => { - throw new Error('copy should not run when WebSearch is disabled'); + const originalCopyFileSync = fs.copyFileSync.bind(fs); + const copyFileSpy = spyOn(fs, 'copyFileSync').mockImplementation((source, destination) => { + const sourcePath = String(source); + const destinationPath = String(destination); + if (sourcePath.includes('websearch') || destinationPath.includes('websearch')) { + throw new Error('websearch copy should not run when WebSearch is disabled'); + } + return originalCopyFileSync(source, destination); }); const result = createApiProfile( @@ -142,9 +148,12 @@ describe('profile-writer Anthropic direct', () => { ); expect(result.success).toBe(true); - expect(copyFileSpy).not.toHaveBeenCalled(); + expect(copyFileSpy).toHaveBeenCalled(); expect(fs.existsSync(path.join(tempHome, '.ccs', 'disabled-websearch.settings.json'))).toBe( true ); + expect(fs.existsSync(path.join(tempHome, '.ccs', 'hooks', 'websearch-transformer.cjs'))).toBe( + false + ); }); }); diff --git a/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts b/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts index 0978ede3..24cb3d18 100644 --- a/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts +++ b/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts @@ -258,6 +258,44 @@ describe('resolveCliproxyImageAnalysisEnv', () => { expect(result.env.CCS_CURRENT_PROVIDER).toBe('agy'); expect(result.env.CCS_IMAGE_ANALYSIS_SKIP).toBe('0'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('https://remote.example.com:9443'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH).toBe('/api/provider/agy'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('remote-token'); + expect(result.warning).toBeNull(); + }); + + it('pins local cliproxy image analysis to the resolved local API key', async () => { + const result = await resolveCliproxyImageAnalysisEnv( + { + profileName: 'orq', + provider: 'agy', + profileSettingsPath: '/tmp/orq.settings.json', + proxyTarget: { + host: '127.0.0.1', + port: 8317, + protocol: 'http', + isRemote: false, + }, + proxyReachable: true, + }, + { + getImageAnalysisHookEnv: () => ({ + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_TIMEOUT: '60', + CCS_IMAGE_ANALYSIS_PROVIDER_MODELS: 'agy:gemini-2.5-pro', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_SKIP: '0', + }), + hasImageAnalysisProfileHook: () => true, + hasImageAnalyzerHook: () => true, + resolveImageAnalysisRuntimeStatus: async () => createImageAnalysisStatus(), + getLocalRuntimeApiKey: () => 'local-runtime-token', + } + ); + + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('http://127.0.0.1:8317'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH).toBe('/api/provider/agy'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('local-runtime-token'); expect(result.warning).toBeNull(); }); }); diff --git a/tests/unit/copilot/copilot-executor-env.test.ts b/tests/unit/copilot/copilot-executor-env.test.ts index 609be2b5..982287eb 100644 --- a/tests/unit/copilot/copilot-executor-env.test.ts +++ b/tests/unit/copilot/copilot-executor-env.test.ts @@ -136,11 +136,15 @@ describe('generateCopilotEnv', () => { port: 8317, }; }, + getLocalRuntimeApiKey: () => 'local-runtime-token', }); expect(ensureCalls).toBe(1); expect(result.env.CCS_CURRENT_PROVIDER).toBe('ghcp'); expect(result.env.CCS_IMAGE_ANALYSIS_SKIP).toBe('0'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('http://127.0.0.1:8317'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH).toBe('/api/provider/ghcp'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('local-runtime-token'); expect(result.warning).toBeNull(); }); }); diff --git a/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts b/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts new file mode 100644 index 00000000..6e77eb3b --- /dev/null +++ b/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts @@ -0,0 +1,380 @@ +import { afterEach, describe, expect, it } from 'bun:test'; +import { spawn } from 'child_process'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import * as http from 'node:http'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +const serverPath = join(process.cwd(), 'lib', 'mcp', 'ccs-image-analysis-server.cjs'); + +function encodeMessage(message: unknown): string { + return `${JSON.stringify(message)}\n`; +} + +function collectResponses( + child: ReturnType, + expectedCount: number +): Promise>> { + return new Promise((resolve, reject) => { + let buffer = Buffer.alloc(0); + const responses: Array> = []; + const timer = setTimeout(() => reject(new Error('Timed out waiting for MCP responses')), 5000); + + function tryParse(): void { + while (true) { + const newlineIndex = buffer.indexOf('\n'); + if (newlineIndex === -1) { + return; + } + + const body = buffer.slice(0, newlineIndex).toString('utf8').replace(/\r$/, '').trim(); + buffer = buffer.slice(newlineIndex + 1); + if (!body) { + continue; + } + + responses.push(JSON.parse(body) as Record); + if (responses.length >= expectedCount) { + clearTimeout(timer); + resolve(responses); + return; + } + } + } + + child.stdout.on('data', (chunk: Buffer) => { + buffer = Buffer.concat([buffer, chunk]); + try { + tryParse(); + } catch (error) { + clearTimeout(timer); + reject(error); + } + }); + + child.on('error', (error) => { + clearTimeout(timer); + reject(error); + }); + }); +} + +function createTestPng(filePath: string): void { + const png = Buffer.from([ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, + 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, + 0xcf, 0xc0, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x18, 0xdd, 0x8d, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + ]); + writeFileSync(filePath, png); +} + +function createTestPdf(filePath: string): void { + writeFileSync(filePath, '%PDF-1.4\n1 0 obj\n<<>>\nendobj\ntrailer\n<<>>\n%%EOF\n', 'utf8'); +} + +describe('ccs-image-analysis MCP server', () => { + let tempDir = ''; + let imagePath = ''; + let mockServer: http.Server | null = null; + + afterEach(async () => { + await new Promise((resolve) => { + mockServer?.close(() => resolve()); + if (!mockServer) resolve(); + }); + mockServer = null; + + if (tempDir) { + rmSync(tempDir, { recursive: true, force: true }); + tempDir = ''; + imagePath = ''; + } + }); + + it('lists the ImageAnalysis tool and posts directly to the provider-scoped CLIProxy route', async () => { + tempDir = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-mcp-server-')); + imagePath = join(tempDir, 'screen.png'); + createTestPng(imagePath); + + let receivedRequest: { path?: string; apiKey?: string; body?: unknown } | null = null; + const mockPort = await new Promise((resolve, reject) => { + mockServer = http.createServer((req, res) => { + let body = ''; + req.on('data', (chunk) => { + body += chunk.toString(); + }); + req.on('end', () => { + receivedRequest = { + path: req.url || '/', + apiKey: req.headers['x-api-key'] as string, + body: body ? JSON.parse(body) : null, + }; + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + content: [{ type: 'text', text: 'The screenshot shows a red pixel debug fixture.' }], + }) + ); + }); + }); + + mockServer.once('error', reject); + mockServer.listen(0, '127.0.0.1', () => { + const address = mockServer?.address(); + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve mock server port')); + return; + } + resolve(address.port); + }); + }); + + const child = spawn('node', [serverPath], { + env: { + ...process.env, + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_SKIP: '0', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_MODEL: 'gemini-3-1-flash-preview', + CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL: `http://127.0.0.1:${mockPort}`, + CCS_IMAGE_ANALYSIS_RUNTIME_PATH: '/api/provider/agy', + CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY: 'test-api-key', + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + try { + const responsesPromise = collectResponses(child, 3); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'bun-test', version: '1.0.0' }, + }, + }) + ); + child.stdin.write(encodeMessage({ jsonrpc: '2.0', id: 2, method: 'tools/list' })); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 3, + method: 'tools/call', + params: { + name: 'ImageAnalysis', + arguments: { filePath: imagePath, focus: 'Describe the visible issue' }, + }, + }) + ); + + const responses = await responsesPromise; + const toolsList = responses.find((message) => message.id === 2); + const toolCall = responses.find((message) => message.id === 3); + + expect(toolsList?.result).toEqual({ + tools: [ + { + name: 'ImageAnalysis', + description: + 'Analyze a local image or PDF file with CCS provider-backed vision. Prefer this tool over Read for image and PDF paths. Use Read for text, code, and other plain files.', + inputSchema: { + type: 'object', + properties: { + filePath: { + type: 'string', + description: + 'Absolute or workspace-relative path to a local image or PDF file to analyze.', + }, + focus: { + type: 'string', + description: + 'Optional question or area of focus, for example "explain the error dialog" or "transcribe the visible text".', + }, + template: { + type: 'string', + enum: ['default', 'screenshot', 'document'], + description: + 'Optional prompt template override. Use screenshot for UI captures, document for PDFs/docs, or default for general images.', + }, + }, + required: ['filePath'], + additionalProperties: false, + }, + }, + ], + }); + expect(receivedRequest?.path).toBe('/api/provider/agy/v1/messages'); + expect(receivedRequest?.apiKey).toBe('test-api-key'); + expect( + (((receivedRequest?.body as { messages: Array<{ content: Array<{ text?: string }> }> }) + ?.messages[0]?.content[0] || {}) as { text?: string }).text + ).toContain('Specific focus'); + expect(toolCall?.result).toBeDefined(); + expect( + ((toolCall?.result as { content: Array<{ text: string }> }).content[0] || {}).text + ).toContain('red pixel debug fixture'); + expect( + ((toolCall?.result as { content: Array<{ text: string }> }).content[0] || {}).text + ).toContain('Model: gemini-3-1-flash-preview'); + } finally { + child.kill(); + } + }); + + it('returns a structured tool error when the file does not exist', async () => { + const child = spawn('node', [serverPath], { + env: { + ...process.env, + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_SKIP: '0', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_MODEL: 'gemini-3-1-flash-preview', + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + try { + const responsesPromise = collectResponses(child, 2); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'bun-test', version: '1.0.0' }, + }, + }) + ); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 2, + method: 'tools/call', + params: { + name: 'ImageAnalysis', + arguments: { filePath: join(tmpdir(), 'missing-image.png') }, + }, + }) + ); + + const responses = await responsesPromise; + const toolCall = responses.find((message) => message.id === 2); + + expect(toolCall?.result).toEqual({ + content: [ + { + type: 'text', + text: `ImageAnalysis could not find file: ${join(tmpdir(), 'missing-image.png')}`, + }, + ], + isError: true, + }); + } finally { + child.kill(); + } + }); + + it('sends PDF files as document blocks to the provider-scoped route', async () => { + tempDir = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-mcp-server-pdf-')); + const pdfPath = join(tempDir, 'manual.pdf'); + createTestPdf(pdfPath); + + let receivedRequest: { path?: string; apiKey?: string; body?: unknown } | null = null; + const mockPort = await new Promise((resolve, reject) => { + mockServer = http.createServer((req, res) => { + let body = ''; + req.on('data', (chunk) => { + body += chunk.toString(); + }); + req.on('end', () => { + receivedRequest = { + path: req.url || '/', + apiKey: req.headers['x-api-key'] as string, + body: body ? JSON.parse(body) : null, + }; + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + content: [{ type: 'text', text: 'The PDF contains a one-page fixture.' }], + }) + ); + }); + }); + + mockServer.once('error', reject); + mockServer.listen(0, '127.0.0.1', () => { + const address = mockServer?.address(); + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve mock server port')); + return; + } + resolve(address.port); + }); + }); + + const child = spawn('node', [serverPath], { + env: { + ...process.env, + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_SKIP: '0', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_MODEL: 'gemini-3-1-flash-preview', + CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL: `http://127.0.0.1:${mockPort}`, + CCS_IMAGE_ANALYSIS_RUNTIME_PATH: '/api/provider/agy', + CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY: 'test-api-key', + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + try { + const responsesPromise = collectResponses(child, 2); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'bun-test', version: '1.0.0' }, + }, + }) + ); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 2, + method: 'tools/call', + params: { + name: 'ImageAnalysis', + arguments: { filePath: pdfPath }, + }, + }) + ); + + const responses = await responsesPromise; + const requestBody = receivedRequest?.body as { + messages: Array<{ content: Array<{ type: string; source?: { media_type?: string } }> }>; + }; + + expect(receivedRequest?.path).toBe('/api/provider/agy/v1/messages'); + expect(receivedRequest?.apiKey).toBe('test-api-key'); + expect(requestBody.messages[0]?.content[1]?.type).toBe('document'); + expect(requestBody.messages[0]?.content[1]?.source?.media_type).toBe('application/pdf'); + expect(((responses[1]?.result as { content: Array<{ text: string }> }).content[0] || {}).text).toContain( + 'one-page fixture' + ); + } finally { + child.kill(); + } + }); +}); diff --git a/tests/unit/targets/settings-profile-image-analysis-launch.test.ts b/tests/unit/targets/settings-profile-image-analysis-launch.test.ts new file mode 100644 index 00000000..5baed5f7 --- /dev/null +++ b/tests/unit/targets/settings-profile-image-analysis-launch.test.ts @@ -0,0 +1,198 @@ +import { afterEach, beforeEach, describe, expect, it } from 'bun:test'; +import { spawnSync } from 'child_process'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +const STEERING_PROMPT_SNIPPET = 'prefer the CCS MCP tool ImageAnalysis instead of Read'; + +interface RunResult { + status: number | null; + stdout: string; + stderr: string; +} + +function runCcs(args: string[], env: NodeJS.ProcessEnv): RunResult { + const ccsEntry = path.join(process.cwd(), 'src', 'ccs.ts'); + const result = spawnSync(process.execPath, [ccsEntry, ...args], { + encoding: 'utf8', + env, + timeout: 20000, + }); + + return { + status: result.status, + stdout: result.stdout || '', + stderr: result.stderr || '', + }; +} + +describe('settings profile ImageAnalysis launch', () => { + let tmpHome = ''; + let ccsDir = ''; + let settingsPath = ''; + let fakeClaudePath = ''; + let claudeArgsLogPath = ''; + let claudeEnvLogPath = ''; + let baseEnv: NodeJS.ProcessEnv; + + beforeEach(() => { + if (process.platform === 'win32') { + return; + } + + tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-image-analysis-launch-')); + ccsDir = path.join(tmpHome, '.ccs'); + settingsPath = path.join(ccsDir, 'glm.settings.json'); + fakeClaudePath = path.join(tmpHome, 'fake-claude.sh'); + claudeArgsLogPath = path.join(tmpHome, 'claude-args.txt'); + claudeEnvLogPath = path.join(tmpHome, 'claude-env.txt'); + + fs.mkdirSync(ccsDir, { recursive: true }); + fs.writeFileSync( + path.join(ccsDir, 'config.json'), + JSON.stringify({ profiles: { glm: settingsPath } }, null, 2) + '\n' + ); + fs.writeFileSync( + path.join(ccsDir, 'config.yaml'), + [ + 'version: 12', + 'websearch:', + ' enabled: false', + 'image_analysis:', + ' enabled: true', + ' timeout: 60', + ' fallback_backend: agy', + ' provider_models:', + ' agy: gemini-3-1-flash-preview', + 'cliproxy:', + ' auth:', + ' api_key: current-token', + '', + ].join('\n'), + 'utf8' + ); + fs.writeFileSync( + settingsPath, + JSON.stringify( + { + env: { + ANTHROPIC_BASE_URL: 'https://api.z.ai/api/provider/agy', + ANTHROPIC_AUTH_TOKEN: 'stale-token', + ANTHROPIC_MODEL: 'glm-5', + }, + }, + null, + 2 + ) + '\n' + ); + + fs.writeFileSync( + fakeClaudePath, + `#!/bin/sh +printf "%s\n" "$@" > "${claudeArgsLogPath}" +{ + printf "runtimeApiKey=%s\n" "$CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY" + printf "runtimeBaseUrl=%s\n" "$CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL" + printf "runtimePath=%s\n" "$CCS_IMAGE_ANALYSIS_RUNTIME_PATH" +} > "${claudeEnvLogPath}" +exit 0 +`, + { encoding: 'utf8', mode: 0o755 } + ); + fs.chmodSync(fakeClaudePath, 0o755); + + baseEnv = { + ...process.env, + CI: '1', + NO_COLOR: '1', + CCS_HOME: tmpHome, + CCS_CLAUDE_PATH: fakeClaudePath, + CCS_DEBUG: '1', + }; + }); + + afterEach(() => { + if (process.platform === 'win32') { + return; + } + + fs.rmSync(tmpHome, { recursive: true, force: true }); + }); + + it('keeps launch non-fatal when the shared Read-hook fallback cannot be prepared', () => { + if (process.platform === 'win32') return; + + fs.writeFileSync(path.join(ccsDir, 'hooks'), 'not-a-directory', 'utf8'); + + const result = runCcs(['glm', 'smoke'], baseEnv); + + expect(result.status).toBe(0); + expect(result.stderr).not.toContain('could not prepare the local ImageAnalysis tool'); + expect(fs.existsSync(claudeArgsLogPath)).toBe(true); + const launchedArgs = fs.readFileSync(claudeArgsLogPath, 'utf8'); + expect(launchedArgs).toContain('--append-system-prompt'); + expect(launchedArgs).toContain(STEERING_PROMPT_SNIPPET); + }); + + it('keeps launch non-fatal when Image Analysis is disabled', () => { + if (process.platform === 'win32') return; + + fs.writeFileSync( + path.join(ccsDir, 'config.yaml'), + 'version: 12\nwebsearch:\n enabled: false\nimage_analysis:\n enabled: false\n', + 'utf8' + ); + fs.writeFileSync(path.join(ccsDir, 'hooks'), 'not-a-directory', 'utf8'); + + const result = runCcs(['glm', 'smoke'], baseEnv); + + expect(result.status).toBe(0); + expect(result.stderr).not.toContain('could not prepare the local ImageAnalysis tool'); + expect(fs.existsSync(claudeArgsLogPath)).toBe(true); + const launchedArgs = fs.readFileSync(claudeArgsLogPath, 'utf8'); + expect(launchedArgs).toContain('--append-system-prompt'); + expect(launchedArgs).toContain(STEERING_PROMPT_SNIPPET); + }); + + it('pins bridge-backed image analysis to the current CLIProxy auth token', () => { + if (process.platform === 'win32') return; + + const result = runCcs(['glm', 'smoke'], baseEnv); + + expect(result.status).toBe(0); + expect(fs.existsSync(claudeEnvLogPath)).toBe(true); + const launchedEnv = fs.readFileSync(claudeEnvLogPath, 'utf8'); + expect(launchedEnv).toContain('runtimeApiKey=current-token'); + expect(launchedEnv).not.toContain('stale-token'); + expect(launchedEnv).toContain('runtimePath=/api/provider/agy'); + }); + + it('pins direct settings image analysis to the current local CLIProxy auth token', () => { + if (process.platform === 'win32') return; + + fs.writeFileSync( + settingsPath, + JSON.stringify( + { + env: { + ANTHROPIC_BASE_URL: 'https://api.z.ai/v1', + ANTHROPIC_AUTH_TOKEN: 'stale-token', + ANTHROPIC_MODEL: 'glm-5', + }, + }, + null, + 2 + ) + '\n' + ); + + const result = runCcs(['glm', 'smoke'], baseEnv); + + expect(result.status).toBe(0); + expect(fs.existsSync(claudeEnvLogPath)).toBe(true); + const launchedEnv = fs.readFileSync(claudeEnvLogPath, 'utf8'); + expect(launchedEnv).toContain('runtimeApiKey=current-token'); + expect(launchedEnv).not.toContain('stale-token'); + expect(launchedEnv).toContain('runtimePath=/api/provider/'); + }); +}); diff --git a/tests/unit/utils/claudecode-env-stripping.test.ts b/tests/unit/utils/claudecode-env-stripping.test.ts index acd83eb7..aa13eefd 100644 --- a/tests/unit/utils/claudecode-env-stripping.test.ts +++ b/tests/unit/utils/claudecode-env-stripping.test.ts @@ -384,6 +384,41 @@ describe('CLAUDECODE environment stripping', () => { }); }); + it('headless executor prepares image-analysis MCP and compatibility hook fallback', async () => { + writeConfigWithAutoUpdatePreference(false); + const ccsDir = path.join(process.env.CCS_HOME as string, '.ccs'); + const settingsPath = path.join(ccsDir, 'glm.settings.json'); + fs.writeFileSync(settingsPath, '{}\n', 'utf8'); + process.env.CCS_CLAUDE_PATH = 'claude'; + + const result = await HeadlessExecutor.execute('glm', 'describe screenshot', { + permissionMode: 'default', + timeout: 1000, + }); + + expect(result.success).toBe(true); + expect(spawnCalls.length).toBeGreaterThan(0); + + const persistedSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as { + hooks?: { PreToolUse?: Array<{ matcher?: string }> }; + }; + expect(persistedSettings.hooks?.PreToolUse?.some((hook) => hook.matcher === 'Read')).toBe(true); + + const claudeUserConfig = JSON.parse( + fs.readFileSync(path.join(process.env.CCS_HOME as string, '.claude.json'), 'utf8') + ) as { + mcpServers?: Record; + }; + expect(claudeUserConfig.mcpServers?.['ccs-image-analysis']).toEqual({ + type: 'stdio', + command: 'node', + args: [path.join(ccsDir, 'mcp', 'ccs-image-analysis-server.cjs')], + env: {}, + }); + expect(fs.existsSync(path.join(ccsDir, 'hooks', 'image-analyzer-transformer.cjs'))).toBe(true); + expect(fs.existsSync(path.join(ccsDir, 'hooks', 'image-analysis-runtime.cjs'))).toBe(true); + }); + it('headless executor propagates a WebSearch trace launch id when tracing is enabled', async () => { writeConfigWithAutoUpdatePreference(false); const ccsDir = path.join(process.env.CCS_HOME as string, '.ccs'); diff --git a/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts b/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts index dd099371..40ae163d 100644 --- a/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts +++ b/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts @@ -116,7 +116,7 @@ describe('image-analysis-runtime-status', () => { expect(status.effectiveRuntimeReason).toContain('remote.example:443'); }); - it('keeps hook-missing on native read even when auth and proxy are ready', async () => { + it('keeps hook-missing as a degraded fallback state while primary runtime stays active', async () => { const status = await hydrateImageAnalysisRuntimeStatus( createStatus({ status: 'hook-missing', @@ -144,7 +144,7 @@ describe('image-analysis-runtime-status', () => { expect(status.authReadiness).toBe('ready'); expect(status.proxyReadiness).toBe('ready'); - expect(status.effectiveRuntimeMode).toBe('native-read'); + expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis'); expect(status.effectiveRuntimeReason).toContain('Profile hook is missing'); }); }); diff --git a/tests/unit/utils/hooks/image-analyzer-hook-installer.test.ts b/tests/unit/utils/hooks/image-analyzer-hook-installer.test.ts new file mode 100644 index 00000000..ca8671e2 --- /dev/null +++ b/tests/unit/utils/hooks/image-analyzer-hook-installer.test.ts @@ -0,0 +1,64 @@ +import { afterEach, beforeEach, describe, expect, it } from 'bun:test'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { + hasImageAnalyzerHook, + installImageAnalyzerHook, +} from '../../../../src/utils/hooks/image-analyzer-hook-installer'; +import { prepareImageAnalysisFallbackHook } from '../../../../src/utils/hooks'; + +describe('image-analyzer-hook-installer', () => { + let tempHome = ''; + let originalCcsHome: string | undefined; + let runtimePath = ''; + let bundledRuntimePath = ''; + + beforeEach(() => { + tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-image-analyzer-hook-')); + originalCcsHome = process.env.CCS_HOME; + process.env.CCS_HOME = tempHome; + + const ccsDir = path.join(tempHome, '.ccs'); + fs.mkdirSync(ccsDir, { recursive: true }); + fs.writeFileSync( + path.join(ccsDir, 'config.yaml'), + 'version: 12\nimage_analysis:\n enabled: true\n', + 'utf8' + ); + + runtimePath = path.join(ccsDir, 'hooks', 'image-analysis-runtime.cjs'); + bundledRuntimePath = path.join(process.cwd(), 'lib', 'hooks', 'image-analysis-runtime.cjs'); + }); + + afterEach(() => { + if (originalCcsHome === undefined) { + delete process.env.CCS_HOME; + } else { + process.env.CCS_HOME = originalCcsHome; + } + + if (tempHome) { + fs.rmSync(tempHome, { recursive: true, force: true }); + } + }); + + it('treats a missing runtime artifact as not ready and repairs it on prepare', () => { + expect(installImageAnalyzerHook()).toBe(true); + fs.unlinkSync(runtimePath); + + expect(hasImageAnalyzerHook()).toBe(false); + expect(prepareImageAnalysisFallbackHook()).toBe(true); + expect(fs.existsSync(runtimePath)).toBe(true); + expect(hasImageAnalyzerHook()).toBe(true); + }); + + it('refreshes stale runtime content during fallback hook preparation', () => { + expect(installImageAnalyzerHook()).toBe(true); + fs.writeFileSync(runtimePath, 'stale runtime\n', 'utf8'); + + expect(hasImageAnalyzerHook()).toBe(false); + expect(prepareImageAnalysisFallbackHook()).toBe(true); + expect(fs.readFileSync(runtimePath, 'utf8')).toBe(fs.readFileSync(bundledRuntimePath, 'utf8')); + }); +}); diff --git a/tests/unit/utils/hooks/image-analyzer-profile-hook-injector.test.ts b/tests/unit/utils/hooks/image-analyzer-profile-hook-injector.test.ts index 96c07f32..f0e47f19 100644 --- a/tests/unit/utils/hooks/image-analyzer-profile-hook-injector.test.ts +++ b/tests/unit/utils/hooks/image-analyzer-profile-hook-injector.test.ts @@ -67,4 +67,23 @@ describe('image-analyzer-profile-hook-injector', () => { expect(fs.existsSync(defaultSettingsPath)).toBe(false); expect(persisted.hooks?.PreToolUse?.some((hook) => hook.matcher === 'Read')).toBe(true); }); + + it('skips copilot hook persistence when the shared fallback hook is unavailable', () => { + const settingsPath = path.join(tempHome, '.ccs', 'copilot.settings.json'); + writeJson(settingsPath, {}); + + const ensured = ensureProfileHooks({ + profileName: 'copilot', + profileType: 'copilot', + settingsPath, + sharedHookInstalled: false, + }); + + const persisted = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as { + hooks?: { PreToolUse?: Array<{ matcher?: string }> }; + }; + + expect(ensured).toBe(false); + expect(persisted.hooks).toBeUndefined(); + }); }); diff --git a/tests/unit/utils/image-analysis/claude-tool-args.test.ts b/tests/unit/utils/image-analysis/claude-tool-args.test.ts new file mode 100644 index 00000000..8f62322b --- /dev/null +++ b/tests/unit/utils/image-analysis/claude-tool-args.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'bun:test'; +import { + appendThirdPartyImageAnalysisToolArgs, + getImageAnalysisSteeringPrompt, +} from '../../../../src/utils/image-analysis'; + +describe('appendThirdPartyImageAnalysisToolArgs', () => { + it('appends the steering prompt for image analysis', () => { + const args = appendThirdPartyImageAnalysisToolArgs(['-p', 'describe the screenshot']); + + expect(args).toEqual(['-p', 'describe the screenshot', '--append-system-prompt', getImageAnalysisSteeringPrompt()]); + }); + + it('does not duplicate the steering prompt when already present', () => { + const steeringPrompt = getImageAnalysisSteeringPrompt(); + const args = appendThirdPartyImageAnalysisToolArgs([ + '-p', + 'describe the screenshot', + '--append-system-prompt', + steeringPrompt, + ]); + + expect(args.filter((arg) => arg === steeringPrompt)).toHaveLength(1); + expect(args.filter((arg) => arg === '--append-system-prompt')).toHaveLength(1); + }); + + it('preserves trailing arguments after --', () => { + const args = appendThirdPartyImageAnalysisToolArgs(['-p', 'describe', '--', 'extra']); + + expect(args).toEqual([ + '-p', + 'describe', + '--append-system-prompt', + getImageAnalysisSteeringPrompt(), + '--', + 'extra', + ]); + }); +}); diff --git a/tests/unit/utils/image-analysis/mcp-installer.test.ts b/tests/unit/utils/image-analysis/mcp-installer.test.ts new file mode 100644 index 00000000..b958bb5a --- /dev/null +++ b/tests/unit/utils/image-analysis/mcp-installer.test.ts @@ -0,0 +1,180 @@ +import { afterEach, describe, expect, it, mock } from 'bun:test'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { + ensureImageAnalysisMcp, + getImageAnalysisMcpRuntimePath, + getImageAnalysisMcpServerName, + getImageAnalysisMcpServerPath, + uninstallImageAnalysisMcp, +} from '../../../../src/utils/image-analysis'; + +describe('ensureImageAnalysisMcp', () => { + let tempHome: string | undefined; + let originalCcsHome: string | undefined; + + function setupTempHome(): string { + tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-image-analysis-mcp-')); + originalCcsHome = process.env.CCS_HOME; + process.env.CCS_HOME = tempHome; + return tempHome; + } + + function getCcsDir(): string { + if (!tempHome) { + throw new Error('tempHome not initialized'); + } + return path.join(tempHome, '.ccs'); + } + + function writeEnabledConfig(): void { + const ccsDir = getCcsDir(); + fs.mkdirSync(ccsDir, { recursive: true }); + fs.writeFileSync( + path.join(ccsDir, 'config.yaml'), + [ + 'version: 12', + 'image_analysis:', + ' enabled: true', + ' timeout: 60', + ' fallback_backend: agy', + ' provider_models:', + ' agy: gemini-3-1-flash-preview', + '', + ].join('\n'), + 'utf8' + ); + } + + function getManagedConfig() { + return { + type: 'stdio', + command: 'node', + args: [getImageAnalysisMcpServerPath()], + env: {}, + }; + } + + afterEach(() => { + mock.restore(); + + if (originalCcsHome !== undefined) { + process.env.CCS_HOME = originalCcsHome; + } else { + delete process.env.CCS_HOME; + } + + if (tempHome && fs.existsSync(tempHome)) { + fs.rmSync(tempHome, { recursive: true, force: true }); + } + + tempHome = undefined; + originalCcsHome = undefined; + }); + + it('installs the MCP server and preserves existing user mcpServers entries', () => { + setupTempHome(); + writeEnabledConfig(); + + const claudeUserConfigPath = path.join(tempHome as string, '.claude.json'); + fs.writeFileSync( + claudeUserConfigPath, + JSON.stringify( + { + mcpServers: { + existing: { command: 'uvx', args: ['some-server'] }, + }, + }, + null, + 2 + ) + '\n', + 'utf8' + ); + + expect(ensureImageAnalysisMcp()).toBe(true); + expect(fs.existsSync(getImageAnalysisMcpServerPath())).toBe(true); + expect(fs.existsSync(getImageAnalysisMcpRuntimePath())).toBe(true); + + const config = JSON.parse(fs.readFileSync(claudeUserConfigPath, 'utf8')) as { + mcpServers: Record; + }; + + expect(config.mcpServers.existing).toEqual({ command: 'uvx', args: ['some-server'] }); + expect(config.mcpServers[getImageAnalysisMcpServerName()]).toEqual(getManagedConfig()); + }); + + it('removes the managed MCP runtime while preserving unrelated server entries', () => { + setupTempHome(); + writeEnabledConfig(); + + const claudeUserConfigPath = path.join(tempHome as string, '.claude.json'); + fs.writeFileSync( + claudeUserConfigPath, + JSON.stringify( + { + mcpServers: { + existing: { command: 'uvx', args: ['some-server'] }, + }, + }, + null, + 2 + ) + '\n', + 'utf8' + ); + + expect(ensureImageAnalysisMcp()).toBe(true); + + const instancePath = path.join(tempHome as string, '.ccs', 'instances', 'work'); + fs.mkdirSync(instancePath, { recursive: true }); + fs.writeFileSync( + path.join(instancePath, '.claude.json'), + JSON.stringify( + { + mcpServers: { + existing: { command: 'uvx', args: ['instance-server'] }, + [getImageAnalysisMcpServerName()]: { command: 'node', args: ['/tmp/override.cjs'] }, + }, + otherKey: 'keep-me', + }, + null, + 2 + ) + '\n', + 'utf8' + ); + + expect(uninstallImageAnalysisMcp()).toBe(true); + expect(fs.existsSync(getImageAnalysisMcpServerPath())).toBe(false); + expect(fs.existsSync(getImageAnalysisMcpRuntimePath())).toBe(false); + + const globalConfig = JSON.parse(fs.readFileSync(claudeUserConfigPath, 'utf8')) as { + mcpServers: Record; + }; + expect(globalConfig.mcpServers).toEqual({ + existing: { command: 'uvx', args: ['some-server'] }, + }); + + const instanceConfig = JSON.parse( + fs.readFileSync(path.join(instancePath, '.claude.json'), 'utf8') + ) as { + otherKey: string; + mcpServers: Record; + }; + expect(instanceConfig.otherKey).toBe('keep-me'); + expect(instanceConfig.mcpServers).toEqual({ + existing: { command: 'uvx', args: ['instance-server'] }, + }); + }); + + it('installs the first-class MCP runtime even when the legacy hooks path is unusable', () => { + setupTempHome(); + writeEnabledConfig(); + + const hooksPath = path.join(getCcsDir(), 'hooks'); + fs.writeFileSync(hooksPath, 'not-a-directory', 'utf8'); + + expect(ensureImageAnalysisMcp()).toBe(true); + expect(fs.existsSync(getImageAnalysisMcpServerPath())).toBe(true); + expect(fs.existsSync(getImageAnalysisMcpRuntimePath())).toBe(true); + }); +}); diff --git a/tests/unit/web-server/image-analysis-routes.test.ts b/tests/unit/web-server/image-analysis-routes.test.ts index 557a9916..fb34f566 100644 --- a/tests/unit/web-server/image-analysis-routes.test.ts +++ b/tests/unit/web-server/image-analysis-routes.test.ts @@ -171,7 +171,7 @@ describe('image-analysis routes', () => { expect.objectContaining({ name: 'glm', target: 'claude', - currentTargetMode: 'setup', + currentTargetMode: 'fallback', }), expect.objectContaining({ name: 'codexProfile', diff --git a/tests/unit/web-server/settings-routes-image-analysis-status.test.ts b/tests/unit/web-server/settings-routes-image-analysis-status.test.ts index bb375874..2352830c 100644 --- a/tests/unit/web-server/settings-routes-image-analysis-status.test.ts +++ b/tests/unit/web-server/settings-routes-image-analysis-status.test.ts @@ -12,9 +12,17 @@ function writeJson(filePath: string, value: Record): void { } function installSharedHook(tempHome: string): string { - const hookPath = path.join(tempHome, '.ccs', 'hooks', 'image-analyzer-transformer.cjs'); - fs.mkdirSync(path.dirname(hookPath), { recursive: true }); - fs.writeFileSync(hookPath, '#!/usr/bin/env node\n', 'utf8'); + const hooksDir = path.join(tempHome, '.ccs', 'hooks'); + fs.mkdirSync(hooksDir, { recursive: true }); + + for (const fileName of ['image-analyzer-transformer.cjs', 'image-analysis-runtime.cjs']) { + fs.copyFileSync( + path.join(process.cwd(), 'lib', 'hooks', fileName), + path.join(hooksDir, fileName) + ); + } + + const hookPath = path.join(hooksDir, 'image-analyzer-transformer.cjs'); return hookPath; } From 6d1c8957212cd0a7412ca0e6a888ce70c9adbdfb Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 15:43:30 -0400 Subject: [PATCH 09/48] docs(image-analysis): document managed runtime flow --- README.md | 33 +++++++ docs/codebase-summary.md | 13 ++- docs/image-analysis.md | 108 +++++++++++++++++++++ docs/project-overview-pdr.md | 25 +++-- docs/project-roadmap.md | 3 +- docs/system-architecture/provider-flows.md | 32 +++--- src/commands/help-command.ts | 8 +- 7 files changed, 188 insertions(+), 34 deletions(-) create mode 100644 docs/image-analysis.md diff --git a/README.md b/README.md index c10672d9..2f210a9c 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ The dashboard provides visual management for all account types: > Setting `websearch.enabled: false` disables the managed local runtime, but CCS still suppresses Anthropic's native `WebSearch` on third-party backends because those providers cannot execute it correctly. > **Image backend visibility:** `ccs config image-analysis --set-fallback ` defines the backend CCS should use when a profile alias cannot be inferred directly. Use `--set-profile-backend ` and `--clear-profile-backend ` for explicit per-profile mappings. In the dashboard, the global `Settings -> Image` section now shows the shared backend routing state, while each profile editor keeps a compact `Image` status card that links back to those global controls. +> Third-party launches now expose a first-class local `ImageAnalysis` MCP tool when the runtime is ready, route requests directly to the resolved CCS provider path, and fall back to native `Read` when the managed runtime is unavailable. > **Copilot config behavior:** Opening the dashboard or other read-only Copilot endpoints does not rewrite `~/.ccs/copilot.settings.json`. If CCS detects deprecated Copilot model IDs such as `raptor-mini`, it shows warnings immediately and only persists replacements when you explicitly save the Copilot configuration. @@ -687,6 +688,38 @@ See [docs/websearch.md](./docs/websearch.md) for detailed configuration and trou
+## Image Analysis + +Third-party profiles (Gemini, Codex, GLM bridge profiles, Copilot, and similar routes) now use a first-class local `ImageAnalysis` MCP tool instead of relying on a denied `Read` hook as the normal experience. + +### How It Works + +| Profile Type | Image Method | +|--------------|--------------| +| Claude (native) | Native Claude vision / native `Read` | +| Third-party profiles | CCS local MCP `ImageAnalysis` tool when available | +| Third-party when runtime unavailable | Native `Read` fallback | + +### Direct Provider Routing + +When the managed tool is used, CCS resolves the backend before launch and posts image-analysis requests directly to the provider-scoped CCS route: + +```text +/api/provider//v1/messages +``` + +That path goes from Claude -> `ccs-image-analysis.ImageAnalysis` -> CCS/CLIProxy provider routing. It does not bounce through Claude Code, a helper CLI, or a second model wrapper. + +### Prompting and Fallback + +CCS appends a short steering hint telling Claude to prefer `ImageAnalysis` over `Read` for local image and PDF files. The tool uses editable prompt templates from `~/.ccs/prompts/image-analysis/` and automatically picks `default`, `screenshot`, or `document`. + +If the local runtime, auth, or proxy path is unavailable, CCS keeps the launch non-fatal and falls back to native `Read`. The legacy `Read` hook remains only as a compatibility fallback when CCS can install it safely. + +See [docs/image-analysis.md](./docs/image-analysis.md) for configuration, routing details, and troubleshooting. + +
+ ## Remote CLIProxy CCS v7.x supports connecting to remote CLIProxyAPI instances, enabling: diff --git a/docs/codebase-summary.md b/docs/codebase-summary.md index 3e3e1607..dbe3f73b 100644 --- a/docs/codebase-summary.md +++ b/docs/codebase-summary.md @@ -54,7 +54,7 @@ src/ │ │ └── [subcommand files...] │ ├── cliproxy-command.ts # CLIProxy subcommand handling │ ├── config-command.ts # Config management commands -│ ├── config-image-analysis-command.ts # Image analysis hook config (NEW v7.34) +│ ├── config-image-analysis-command.ts # First-class ImageAnalysis config (NEW v7.34) │ ├── named-command-router.ts # Reusable named-command dispatcher │ ├── doctor-command.ts # Health diagnostics │ ├── env-command.ts # Export shell env vars for third-party tools (v7.39) @@ -149,7 +149,7 @@ src/ │ ├── index.ts # Barrel export │ ├── checks/ # Diagnostic checks │ │ ├── index.ts -│ │ └── image-analysis-check.ts # Image hook validation (NEW v7.34) +│ │ └── image-analysis-check.ts # ImageAnalysis runtime validation (NEW v7.34) │ └── repair/ # Auto-repair logic │ └── index.ts │ @@ -169,15 +169,17 @@ src/ │ │ └── spinners.ts # Progress spinners │ ├── websearch/ # Search tool integrations │ │ └── index.ts -│ ├── hooks/ # Claude Code hooks (NEW v7.34) +│ ├── hooks/ # Claude Code compatibility hooks (NEW v7.34) │ │ ├── index.ts │ │ ├── image-analyzer-hook-installer.ts │ │ ├── image-analyzer-hook-configuration.ts │ │ ├── image-analyzer-profile-hook-injector.ts │ │ └── get-image-analysis-hook-env.ts -│ ├── image-analysis/ # Image analysis hook utilities (NEW v7.34) +│ ├── image-analysis/ # ImageAnalysis MCP/runtime utilities (NEW v7.34) │ │ ├── index.ts -│ │ └── hook-installer.ts +│ │ ├── hook-installer.ts +│ │ ├── mcp-installer.ts +│ │ └── claude-tool-args.ts │ └── [utility files...] │ └── web-server/ # Express web server (heavily modularized) @@ -636,4 +638,5 @@ tests/ - [System Architecture](./system-architecture/index.md) - High-level architecture diagrams - [Project Roadmap](./project-roadmap.md) - Modularization phases and future work - [WebSearch](./websearch.md) - WebSearch feature documentation +- [Image Analysis](./image-analysis.md) - First-class ImageAnalysis runtime documentation - [CLAUDE.md](../CLAUDE.md) - AI-facing development guidance diff --git a/docs/image-analysis.md b/docs/image-analysis.md new file mode 100644 index 00000000..27217400 --- /dev/null +++ b/docs/image-analysis.md @@ -0,0 +1,108 @@ +# Image Analysis Configuration Guide + +CCS provides first-class image and PDF analysis for third-party Claude launches that do not have reliable native vision support. + +## How Image Analysis Works + +Native Claude accounts keep Anthropic's own vision flow. + +Third-party profiles now use a CCS-managed local MCP tool named `ImageAnalysis` when the runtime is available. CCS also appends a short steering hint so Claude prefers that tool over `Read` for local image and PDF files. + +If the managed runtime, auth, or proxy path is unavailable, CCS falls back to native `Read` instead of failing the whole launch. The old `Read` hook remains only as a compatibility fallback when it can be installed safely. + +## Routing Model + +ImageAnalysis requests go straight to the CCS-managed provider route: + +```text +Claude -> ccs-image-analysis MCP -> CCS provider route -> /api/provider//v1/messages +``` + +Important: +- CCS does not relay image analysis through Claude Code, another CLI, or a second model wrapper. +- For bridge-backed settings profiles, CCS resolves the backend and provider path before launch. +- CCS avoids leaking a profile's ordinary third-party `ANTHROPIC_BASE_URL` or token into image analysis unless that profile is explicitly using a CLIProxy bridge. + +## Profile Behavior + +| Profile Type | Image Method | +|--------------|--------------| +| Claude `default` / `account` | Native Claude vision / native `Read` | +| Third-party settings / CLIProxy / Copilot | CCS local `ImageAnalysis` MCP tool when ready | +| Third-party when runtime unavailable | Native `Read` fallback | + +## Configuration + +Configure via dashboard (`Settings -> Image`) or `~/.ccs/config.yaml`: + +```yaml +image_analysis: + enabled: true + timeout: 60 + fallback_backend: agy + provider_models: + agy: gemini-3-1-flash-preview + codex: gpt-5.1-codex-mini + ghcp: claude-haiku-4.5 +``` + +Useful commands: + +```bash +ccs config image-analysis +ccs config image-analysis --enable +ccs config image-analysis --disable +ccs config image-analysis --set-fallback agy +ccs config image-analysis --set-profile-backend glm agy +ccs config image-analysis --clear-profile-backend glm +``` + +## Prompt Templates + +CCS installs editable prompt templates at: + +```text +~/.ccs/prompts/image-analysis/ +``` + +Templates: +- `default.txt` +- `screenshot.txt` +- `document.txt` + +CCS automatically selects `screenshot` for screenshot-like filenames, `document` for PDFs, and `default` otherwise. + +## Runtime Environment + +Key runtime env vars: + +| Variable | Purpose | +|----------|---------| +| `CCS_IMAGE_ANALYSIS_SKIP` | Disable image analysis for the current launch | +| `CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL` | Explicit CCS runtime base URL | +| `CCS_IMAGE_ANALYSIS_RUNTIME_PATH` | Provider route such as `/api/provider/agy` | +| `CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY` | Explicit CCS runtime auth key | +| `CCS_IMAGE_ANALYSIS_MODEL` | Force a single image-analysis model | +| `CCS_DEBUG` | Verbose runtime logging | + +## Troubleshooting + +### Claude still uses `Read` + +- Confirm `ccs config image-analysis` shows `enabled: true` +- Check the active profile resolves to a configured backend +- Run with `CCS_DEBUG=1` to see runtime preparation details + +### ImageAnalysis is not exposed + +- Verify CLIProxy auth for the resolved backend +- Verify the local or remote CLIProxy target is reachable +- Check `~/.claude.json` and inherited account configs for `ccs-image-analysis` + +### I need to prove requests are going directly to the provider route + +Run with `CCS_DEBUG=1` and inspect the resolved runtime path. The request target should be provider-scoped, for example: + +```text +/api/provider/agy/v1/messages +``` diff --git a/docs/project-overview-pdr.md b/docs/project-overview-pdr.md index 9df5d38a..a1ea1475 100644 --- a/docs/project-overview-pdr.md +++ b/docs/project-overview-pdr.md @@ -1,6 +1,6 @@ # CCS Product Development Requirements (PDR) -Last Updated: 2026-03-24 +Last Updated: 2026-04-02 ## Product Overview @@ -10,7 +10,7 @@ Last Updated: 2026-03-24 **Description**: CLI wrapper enabling seamless switching between multiple Claude accounts and alternative AI providers (GLM, Gemini, Codex, OpenRouter, Qwen, Kimi, DeepSeek) with a React-based dashboard for configuration management. Supports both local and remote CLIProxyAPI instances, hybrid quota management, and official Claude channel runtime setup for Telegram, Discord, and iMessage. -**Current Version**: v7.34.x (Image Analysis Hook + Performance Improvements) +**Current Version**: v7.34.x+ (First-class ImageAnalysis MCP tooling, WebSearch MCP, performance improvements) --- @@ -36,8 +36,9 @@ CCS provides: 4. **API Profiles**: GLM, Kimi, OpenRouter, any Anthropic-compatible API 5. **Visual Dashboard**: React SPA for configuration management 6. **Automatic WebSearch**: First-class local WebSearch tool with deterministic provider chain for third-party providers -7. **Usage Analytics**: Token tracking, cost analysis, model breakdown -8. **Official Claude Channels**: Runtime auto-enable plus dashboard token/config flow for Telegram, Discord, and macOS-only iMessage +7. **Automatic Image Analysis**: First-class local ImageAnalysis tool with direct provider routing for third-party profiles +8. **Usage Analytics**: Token tracking, cost analysis, model breakdown +9. **Official Claude Channels**: Runtime auto-enable plus dashboard token/config flow for Telegram, Discord, and macOS-only iMessage --- @@ -99,6 +100,13 @@ CCS provides: - Keep Gemini CLI, OpenCode, and Grok as optional legacy fallback - Graceful fallback chain +### FR-007A: First-Class Image Analysis +- Expose a CCS-managed local `ImageAnalysis` MCP tool for third-party profiles that need provider-backed vision +- Resolve the provider route before launch and send requests directly to `/api/provider//v1/messages` +- Use editable prompt templates for `default`, `screenshot`, and `document` analysis modes +- Keep the old `Read` hook as compatibility fallback only, not the primary user experience +- Fall back to native `Read` without failing the whole launch when the managed runtime is unavailable + ### FR-008: Remote CLIProxy Support - Connect to remote CLIProxyAPI instances - CLI flags for proxy configuration (--proxy-host, --proxy-port, etc.) @@ -277,10 +285,11 @@ CCS provides: - [x] Entrypoint with privilege dropping ### v7.34 Release (Complete) -- [x] Image Analysis Hook for vision model proxying -- [x] Auto-injection for agy, gemini, codex, cliproxy profiles -- [x] Skip hook for Claude Sub accounts (native vision) -- [x] CLIProxy fallback with deprecated block-image-read +- [x] First-class `ImageAnalysis` MCP tool for third-party launches +- [x] Direct provider-scoped routing for image analysis requests +- [x] Prompt template selection for default / screenshot / document flows +- [x] Hook fallback retained only for compatibility +- [x] Non-fatal native `Read` fallback when managed runtime is unavailable - [x] `ccs config image-analysis` CLI command - [x] Doctor integration for hook validation - [x] 791-line E2E test suite for image analysis diff --git a/docs/project-roadmap.md b/docs/project-roadmap.md index 182449dc..f8d5ca5c 100644 --- a/docs/project-roadmap.md +++ b/docs/project-roadmap.md @@ -1,6 +1,6 @@ # CCS Project Roadmap -Last Updated: 2026-04-01 +Last Updated: 2026-04-02 Forward-looking roadmap documenting current priorities, GitHub issues, and future feature plans. @@ -41,6 +41,7 @@ All major modularization work is complete. The codebase evolved from monolithic ### Recent Fixes +- **2026-04-02**: Third-party image and PDF analysis now follows the same first-class local-tool model as WebSearch. CCS provisions `ccs-image-analysis` as a managed MCP tool, routes requests directly to provider-scoped CCS endpoints such as `/api/provider/agy/v1/messages`, keeps editable prompt templates under `~/.ccs/prompts/image-analysis/`, and demotes the old `Read` hook to a best-effort compatibility fallback. Launches now stay non-fatal and fall back to native `Read` when the managed runtime cannot be prepared. - **2026-04-01**: The `Compatible -> Codex CLI` dashboard now exposes manual long-context controls for `model_context_window` and `model_auto_compact_token_limit`. CCS reads and patches those upstream Codex config keys directly, adds official guidance that GPT-5.4 long context is experimental and opt-in, and keeps the behavior manual-only so the dashboard never auto-fills or auto-saves long-context values for the user. - **2026-03-30**: **#862** Third-party WebSearch now uses a first-class CCS-managed MCP tool path instead of relying on a denied native Anthropic `WebSearch` call as the normal UX. CCS provisions `ccs-websearch` into `~/.claude.json`, syncs it into isolated account configs when needed, suppresses native `WebSearch` on third-party launches, preserves the provider order `Exa -> Tavily -> Brave -> DuckDuckGo -> legacy CLI fallback`, and keeps the old hook runtime only as shared provider plumbing plus compatibility fallback. Uninstall cleanup now also removes the managed WebSearch MCP runtime. - **2026-03-28**: **#773** CCS now ships a dedicated `Compatible -> Codex CLI` dashboard route with a real split-view control center. The page detects the local Codex binary, keeps overview/docs guidance, and adds guided editors for the user-owned `~/.codex/config.toml` layer: top-level runtime defaults, project trust, profiles, model providers, MCP servers, and supported feature flags. Structured saves intentionally normalize TOML formatting and drop comments, so the raw editor remains the fidelity escape hatch. Follow-up fixes added immediate raw snapshot refresh, refresh/discard recovery for stale raw drafts, dirty raw-editor guarding for structured controls, project-trust path validation, read-only handling for unreadable config files, preservation of unsupported upstream values such as granular `approval_policy`, and feature reset-to-default support. CCS still warns that transient runtime overrides such as `codex -c key=value` and `CCS_CODEX_API_KEY` may change effective behavior without persisting into the file. diff --git a/docs/system-architecture/provider-flows.md b/docs/system-architecture/provider-flows.md index a9e4c8a1..768c8e0f 100644 --- a/docs/system-architecture/provider-flows.md +++ b/docs/system-architecture/provider-flows.md @@ -537,29 +537,29 @@ Image Analysis Hook enables vision model proxying through CLIProxy with automati Claude CLI processes image request | v - Hook intercepts image request + Claude prefers ImageAnalysis MCP tool | v - Vision Model Proxying (via CLIProxyAPI) + CCS provider-backed image analysis | - +---> Gemini, Codex, AGY support vision + +---> Provider route resolved before launch | - +---> Kiro (Claude native vision) + +---> Direct request to /api/provider//v1/messages | - +---> Skip for Claude Sub accounts (native vision) + +---> Native Read fallback if runtime/auth/proxy is unavailable | v - Vision response returned to Claude CLI + Text description returned to Claude CLI ``` -### Hook Environment +### Runtime Environment ```typescript // getImageAnalysisHookEnv() { - ANTHROPIC_IMAGE_HOOK_URL: 'http://localhost:8317/api/image-analysis', - // or for remote proxy: - ANTHROPIC_IMAGE_HOOK_URL: 'https://proxy.example.com:8317/api/image-analysis', + CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL: 'http://127.0.0.1:8317', + CCS_IMAGE_ANALYSIS_RUNTIME_PATH: '/api/provider/agy', + CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY: 'ccs-internal-managed', } ``` @@ -567,12 +567,12 @@ Image Analysis Hook enables vision model proxying through CLIProxy with automati | Provider | Vision Support | Notes | |----------|---|---| -| Gemini | ✓ | Via CLIProxy image analysis | -| Codex | ✓ | Via CLIProxy image analysis | -| Antigravity | ✓ | Via CLIProxy image analysis | -| Kiro | ✓ | Native Claude vision (no proxy needed) | -| Copilot | ✗ | Not supported | -| GLM/Kimi | ✗ | Requires direct API implementation | +| Gemini | ✓ | Via CCS ImageAnalysis provider route | +| Codex | ✓ | Via CCS ImageAnalysis provider route | +| Antigravity | ✓ | Via CCS ImageAnalysis provider route | +| Kiro | ✓ | Via mapped CCS provider route when configured | +| Copilot | ✓ | Via mapped ghcp provider route | +| GLM/Kimi | ✓ | Via explicit or fallback backend mapping | --- diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index a30e6829..07ee1766 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -578,7 +578,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); // Image Analysis printSubSection( - 'Image Analysis (CLIProxy vision)', + 'Image Analysis (first-class local tool)', [ ['ccs config image-analysis', 'Show current settings'], ['ccs config image-analysis --enable', 'Enable for CLIProxy providers'], @@ -586,9 +586,9 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); ['ccs config image-analysis --timeout 120', 'Set analysis timeout'], ['ccs config image-analysis --set-model

', 'Set provider model'], ['', ''], - ['Note:', 'When enabled, images/PDFs are analyzed via vision models'], - ['', 'instead of passing raw data to Claude. Works with CLIProxy'], - ['', 'providers (agy, gemini, codex, kiro, ghcp).'], + ['Note:', 'When ready, third-party launches expose the local ImageAnalysis MCP tool'], + ['', 'and route requests directly to the resolved CCS provider path.'], + ['', 'If runtime/auth/proxy is unavailable, CCS falls back to native Read.'], ], writeLine ); From 77b488f8b185e2427a3777f12bc3ed2d8f8cc522 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 18:36:19 -0400 Subject: [PATCH 10/48] fix(image-analysis): harden runtime and provisioning --- lib/hooks/image-analysis-runtime.cjs | 21 +- lib/mcp/ccs-image-analysis-server.cjs | 139 +++++++++-- src/ccs.ts | 32 ++- src/cliproxy/executor/env-resolver.ts | 20 +- src/cliproxy/executor/index.ts | 37 ++- src/commands/install-command.ts | 2 +- src/copilot/copilot-executor.ts | 38 +++- src/delegation/headless-executor.ts | 30 ++- .../hooks/get-image-analysis-hook-env.ts | 57 +++++ .../hooks/image-analysis-runtime-status.ts | 24 +- src/utils/hooks/index.ts | 3 + src/utils/image-analysis/index.ts | 3 + src/utils/image-analysis/mcp-installer.ts | 215 ++++++++++++------ .../routes/image-analysis-routes.ts | 46 +++- .../env-resolver-codex-fallback.test.ts | 40 ++++ .../ccs-image-analysis-mcp-server.test.ts | 109 ++++++++- ...ings-profile-image-analysis-launch.test.ts | 16 +- .../hooks/get-image-analysis-hook-env.test.ts | 61 +++++ .../image-analysis-runtime-status.test.ts | 42 +++- .../image-analysis/mcp-installer.test.ts | 15 +- .../web-server/image-analysis-routes.test.ts | 30 ++- 21 files changed, 811 insertions(+), 169 deletions(-) create mode 100644 tests/unit/utils/hooks/get-image-analysis-hook-env.test.ts diff --git a/lib/hooks/image-analysis-runtime.cjs b/lib/hooks/image-analysis-runtime.cjs index 91188ce5..13f1e66b 100644 --- a/lib/hooks/image-analysis-runtime.cjs +++ b/lib/hooks/image-analysis-runtime.cjs @@ -9,6 +9,7 @@ const DEFAULT_MODEL = 'gemini-2.5-flash'; const DEFAULT_TIMEOUT_SEC = 60; const MAX_FILE_SIZE_MB = 10; const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024; +const MAX_PROMPT_TEMPLATE_BYTES = 32 * 1024; const SCREENSHOT_NAME_REGEX = /(screen[-_ ]?shot|screen[-_ ]?capture|screencap|snapshot|snip|clip|capture)/i; const TEMPLATE_FILE_NAMES = { @@ -103,6 +104,10 @@ function selectPromptTemplate(filePath, requestedTemplate) { function readPromptFile(filePath) { try { + const stats = fs.statSync(filePath); + if (stats.size > MAX_PROMPT_TEMPLATE_BYTES) { + return null; + } const content = fs.readFileSync(filePath, 'utf8').trim(); return content.length > 0 ? content : null; } catch { @@ -189,10 +194,6 @@ function getRuntimeBaseUrl() { return normalizedBaseUrl; } - if (normalizedPath.length > 0 && normalizedPath !== '/') { - return normalizedBaseUrl; - } - parsed.pathname = runtimePath; return parsed.toString().replace(/\/+$/, ''); } catch { @@ -217,6 +218,11 @@ function getApiKey() { return process.env.CCS_CLIPROXY_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN || 'ccs-internal-managed'; } +function shouldAllowSelfSigned() { + const value = `${process.env.CCS_IMAGE_ANALYSIS_RUNTIME_ALLOW_SELF_SIGNED || ''}`.trim().toLowerCase(); + return value === '1' || value === 'true' || value === 'yes'; +} + function getTimeoutMs(timeoutMs) { if (typeof timeoutMs === 'number' && timeoutMs > 0) { return timeoutMs; @@ -301,6 +307,7 @@ function analyzeViaCliProxy(base64Data, mediaType, model, prompt, timeoutMs) { return new Promise((resolve, reject) => { const endpoint = new URL(getRuntimeEndpoint()); const transport = endpoint.protocol === 'https:' ? https : http; + const apiKey = getApiKey(); const requestBody = JSON.stringify({ model, max_tokens: 4096, @@ -325,9 +332,13 @@ function analyzeViaCliProxy(base64Data, mediaType, model, prompt, timeoutMs) { headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(requestBody), - 'x-api-key': getApiKey(), + 'x-api-key': apiKey, + Authorization: `Bearer ${apiKey}`, }, timeout: timeoutMs, + ...(endpoint.protocol === 'https:' && shouldAllowSelfSigned() + ? { rejectUnauthorized: false } + : {}), }, (res) => { let data = ''; diff --git a/lib/mcp/ccs-image-analysis-server.cjs b/lib/mcp/ccs-image-analysis-server.cjs index 3616d540..a5d2a057 100644 --- a/lib/mcp/ccs-image-analysis-server.cjs +++ b/lib/mcp/ccs-image-analysis-server.cjs @@ -55,11 +55,11 @@ function getTools() { inputSchema: { type: 'object', properties: { - filePath: { - type: 'string', - description: - 'Absolute or workspace-relative path to a local image or PDF file to analyze.', - }, + filePath: { + type: 'string', + description: + 'Workspace-relative path, or an absolute path inside the current workspace, to a local image or PDF file to analyze.', + }, focus: { type: 'string', description: @@ -128,6 +128,20 @@ function normalizeTemplate(value) { return TEMPLATE_NAMES.includes(normalized) ? normalized : undefined; } +function normalizePathForComparison(value) { + return process.platform === 'win32' ? value.toLowerCase() : value; +} + +function isPathWithinWorkspace(workspaceRoot, candidatePath) { + const relativePath = path.relative(workspaceRoot, candidatePath); + return ( + relativePath === '' || + (!relativePath.startsWith('..') && + !relativePath.startsWith(`..${path.sep}`) && + !path.isAbsolute(relativePath)) + ); +} + function resolveFilePath(toolArgs) { if (!toolArgs || typeof toolArgs !== 'object') { return ''; @@ -143,6 +157,60 @@ function resolveFilePath(toolArgs) { return ''; } +function resolveWorkspaceFilePath(toolArgs) { + const requestedPath = resolveFilePath(toolArgs); + if (!requestedPath) { + return { filePath: '', error: null }; + } + + const workspaceRoot = (() => { + try { + return fs.realpathSync(process.cwd()); + } catch { + return path.resolve(process.cwd()); + } + })(); + const absolutePath = path.resolve(process.cwd(), requestedPath); + const comparisonPath = (() => { + if (fs.existsSync(absolutePath)) { + return fs.realpathSync(absolutePath); + } + + const suffixSegments = []; + let currentPath = absolutePath; + while (!fs.existsSync(currentPath)) { + const parentPath = path.dirname(currentPath); + if (parentPath === currentPath) { + break; + } + suffixSegments.unshift(path.basename(currentPath)); + currentPath = parentPath; + } + + const resolvedExistingPath = fs.existsSync(currentPath) + ? fs.realpathSync(currentPath) + : path.resolve(currentPath); + return path.join(resolvedExistingPath, ...suffixSegments); + })(); + + if ( + !isPathWithinWorkspace( + normalizePathForComparison(workspaceRoot), + normalizePathForComparison(comparisonPath) + ) + ) { + return { + filePath: '', + error: 'ImageAnalysis only allows files inside the current workspace.', + }; + } + + return { + filePath: absolutePath, + error: null, + }; +} + function resolveFocus(toolArgs) { return typeof toolArgs.focus === 'string' && toolArgs.focus.trim().length > 0 ? toolArgs.focus.trim() @@ -213,9 +281,13 @@ async function handleToolCall(message) { return; } - const filePath = resolveFilePath(toolArgs); + const { filePath, error: filePathError } = resolveWorkspaceFilePath(toolArgs); if (!filePath) { - writeError(id, -32602, `Tool "${TOOL_NAME}" requires a non-empty filePath.`); + writeError( + id, + -32602, + filePathError || `Tool "${TOOL_NAME}" requires a non-empty filePath.` + ); return; } @@ -296,22 +368,53 @@ let inputBuffer = Buffer.alloc(0); function processIncomingBuffer() { while (true) { - const newlineIndex = inputBuffer.indexOf(0x0a); - if (newlineIndex === -1) { - return; - } + let body; + const startsWithLegacyHeaders = inputBuffer + .slice(0, Math.min(inputBuffer.length, 32)) + .toString('utf8') + .toLowerCase() + .startsWith('content-length:'); - const lineBuffer = inputBuffer.subarray(0, newlineIndex); - inputBuffer = inputBuffer.subarray(newlineIndex + 1); - const line = lineBuffer.toString('utf8').replace(/\r$/, '').trim(); - if (!line) { - continue; + if (startsWithLegacyHeaders) { + const headerEnd = inputBuffer.indexOf('\r\n\r\n'); + if (headerEnd === -1) { + return; + } + + const headerText = inputBuffer.slice(0, headerEnd).toString('utf8'); + const contentLengthMatch = headerText.match(/content-length:\s*(\d+)/i); + if (!contentLengthMatch) { + inputBuffer = Buffer.alloc(0); + return; + } + + const contentLength = Number.parseInt(contentLengthMatch[1], 10); + const messageEnd = headerEnd + 4 + contentLength; + if (inputBuffer.length < messageEnd) { + return; + } + + body = inputBuffer.slice(headerEnd + 4, messageEnd).toString('utf8'); + inputBuffer = inputBuffer.slice(messageEnd); + } else { + const newlineIndex = inputBuffer.indexOf('\n'); + if (newlineIndex === -1) { + return; + } + + body = inputBuffer.slice(0, newlineIndex).toString('utf8').replace(/\r$/, '').trim(); + inputBuffer = inputBuffer.slice(newlineIndex + 1); + if (!body) { + continue; + } } try { - const message = JSON.parse(line); + const message = JSON.parse(body); Promise.resolve(handleMessage(message)).catch((error) => { - if (process.env.CCS_DEBUG) { + if (message && message.id !== undefined) { + writeError(message.id, -32603, (error && error.message) || 'Internal error'); + } else if (process.env.CCS_DEBUG) { console.error(`[ccs-image-analysis] ${error instanceof Error ? error.stack : error}`); } }); diff --git a/src/ccs.ts b/src/ccs.ts index acc261b5..f45172c3 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -42,6 +42,7 @@ import { applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, prepareImageAnalysisFallbackHook, + resolveImageAnalysisRuntimeConnection, resolveImageAnalysisRuntimeStatus, } from './utils/hooks'; import { fail, info, warn } from './utils/ui'; @@ -80,11 +81,7 @@ import { resolveDroidReasoningRuntime, } from './targets/droid-reasoning-runtime'; import { DroidCommandRouterError, routeDroidCommandArgs } from './targets/droid-command-router'; -import { - resolveCliproxyBridgeMetadata, - resolveCliproxyBridgeProfile, -} from './api/services/cliproxy-profile-bridge'; -import { getEffectiveApiKey } from './cliproxy/auth-token-manager'; +import { resolveCliproxyBridgeMetadata } from './api/services/cliproxy-profile-bridge'; // Version and Update check utilities import { getVersion } from './utils/version'; @@ -899,9 +896,10 @@ async function main(): Promise { process.exit(exitCode); } else if (profileInfo.type === 'settings') { // Settings-based profiles (glm, glmt) are third-party providers + const imageAnalysisMcpReady = + resolvedTarget === 'claude' ? ensureImageAnalysisMcpOrThrow() : true; if (resolvedTarget === 'claude') { ensureWebSearchMcpOrThrow(); - ensureImageAnalysisMcpOrThrow(); } // Display WebSearch status (single line, equilibrium UX) @@ -1045,9 +1043,7 @@ async function main(): Promise { cliproxyBridge, sharedHookInstalled: imageAnalysisFallbackHookReady, }); - const currentBridgeAuthToken = cliproxyBridge - ? resolveCliproxyBridgeProfile(cliproxyBridge.provider).apiKey - : getEffectiveApiKey(); + const runtimeConnection = resolveImageAnalysisRuntimeConnection(); let imageAnalysisEnv = getImageAnalysisHookEnv({ profileName: profileInfo.name, profileType: profileInfo.type, @@ -1058,10 +1054,19 @@ async function main(): Promise { backendId: imageAnalysisStatus.backendId, model: imageAnalysisStatus.model, runtimePath: imageAnalysisStatus.runtimePath, - baseUrl: cliproxyBridge?.currentBaseUrl || '', - apiKey: currentBridgeAuthToken, + baseUrl: runtimeConnection.baseUrl, + apiKey: runtimeConnection.apiKey, + allowSelfSigned: runtimeConnection.allowSelfSigned, }); + if (!imageAnalysisMcpReady) { + imageAnalysisEnv = { + ...imageAnalysisEnv, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }; + } + const imageAnalysisProvider = imageAnalysisEnv['CCS_CURRENT_PROVIDER']; if ( resolvedTarget === 'claude' && @@ -1159,10 +1164,13 @@ async function main(): Promise { return; } + const imageAnalysisArgs = imageAnalysisMcpReady + ? appendThirdPartyImageAnalysisToolArgs(remainingArgs) + : remainingArgs; const launchArgs = [ '--settings', expandedSettingsPath, - ...appendThirdPartyWebSearchToolArgs(appendThirdPartyImageAnalysisToolArgs(remainingArgs)), + ...appendThirdPartyWebSearchToolArgs(imageAnalysisArgs), ]; const traceEnv = createWebSearchTraceContext({ launcher: 'ccs.settings-profile', diff --git a/src/cliproxy/executor/env-resolver.ts b/src/cliproxy/executor/env-resolver.ts index 688bfd05..a6233879 100644 --- a/src/cliproxy/executor/env-resolver.ts +++ b/src/cliproxy/executor/env-resolver.ts @@ -23,6 +23,7 @@ import { getWebSearchHookEnv } from '../../utils/websearch-manager'; import { applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, + resolveImageAnalysisRuntimeConnection, } from '../../utils/hooks/get-image-analysis-hook-env'; import { resolveImageAnalysisRuntimeStatus } from '../../utils/hooks/image-analysis-runtime-status'; import { hasImageAnalysisProfileHook } from '../../utils/hooks/image-analyzer-profile-hook-injector'; @@ -89,6 +90,7 @@ interface ResolveCliproxyImageAnalysisEnvOptions { profileSettingsPath?: string; isComposite?: boolean; proxyTarget: ProxyTarget; + tunnelPort?: number | null; proxyReachable: boolean; } @@ -154,14 +156,6 @@ function loadImageAnalysisSettings(settingsPath?: string): Settings | undefined } } -function buildDirectProxyBaseUrl(target: ProxyTarget): string { - const isDefaultPort = - (target.protocol === 'https' && target.port === 443) || - (target.protocol === 'http' && target.port === 80); - const portSuffix = isDefaultPort ? '' : `:${target.port}`; - return `${target.protocol}://${target.host}${portSuffix}`; -} - export async function resolveCliproxyImageAnalysisEnv( options: ResolveCliproxyImageAnalysisEnvOptions, deps: Partial = {} @@ -204,15 +198,21 @@ export async function resolveCliproxyImageAnalysisEnv( }; } + const runtimeConnection = resolveImageAnalysisRuntimeConnection({ + proxyTarget: options.proxyTarget, + tunnelPort: options.tunnelPort, + }); + return { env: applyImageAnalysisRuntimeOverrides(env, { backendId: status.backendId, model: status.model, runtimePath: status.runtimePath, - baseUrl: buildDirectProxyBaseUrl(options.proxyTarget), + baseUrl: runtimeConnection.baseUrl, apiKey: options.proxyTarget.isRemote - ? options.proxyTarget.authToken || '' + ? runtimeConnection.apiKey : resolvedDeps.getLocalRuntimeApiKey(), + allowSelfSigned: runtimeConnection.allowSelfSigned, }), warning: null, }; diff --git a/src/cliproxy/executor/index.ts b/src/cliproxy/executor/index.ts index 16ee372b..b7a38327 100644 --- a/src/cliproxy/executor/index.ts +++ b/src/cliproxy/executor/index.ts @@ -209,7 +209,7 @@ export async function execClaudeWithCLIProxy( // Setup first-class CCS WebSearch runtime ensureWebSearchMcpOrThrow(); - ensureImageAnalysisMcpOrThrow(); + const imageAnalysisMcpReady = ensureImageAnalysisMcpOrThrow(); displayWebSearchStatus(); const providerConfig = getProviderConfig(provider); @@ -843,15 +843,27 @@ export async function execClaudeWithCLIProxy( protocol: 'http' as const, isRemote: false as const, }; - const { env: imageAnalysisEnv, warning: imageAnalysisWarning } = - await resolveCliproxyImageAnalysisEnv({ - profileName: cfg.profileName || provider, - provider, - profileSettingsPath: cfg.customSettingsPath, - isComposite: cfg.isComposite, - proxyTarget: imageAnalysisProxyTarget, - proxyReachable: true, - }); + const imageAnalysisResolution = await resolveCliproxyImageAnalysisEnv({ + profileName: cfg.profileName || provider, + provider, + profileSettingsPath: cfg.customSettingsPath, + isComposite: cfg.isComposite, + proxyTarget: imageAnalysisProxyTarget, + tunnelPort, + proxyReachable: true, + }); + const imageAnalysisProvisioningFailed = + !imageAnalysisMcpReady && imageAnalysisResolution.env.CCS_IMAGE_ANALYSIS_ENABLED === '1'; + const imageAnalysisEnv = imageAnalysisProvisioningFailed + ? { + ...imageAnalysisResolution.env, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + } + : imageAnalysisResolution.env; + const imageAnalysisWarning = imageAnalysisProvisioningFailed + ? 'ImageAnalysis MCP provisioning failed. This session will use native Read.' + : imageAnalysisResolution.warning; // 9. Setup tool sanitization proxy let toolSanitizationProxy: ToolSanitizationProxy | null = null; @@ -1076,10 +1088,13 @@ export async function execClaudeWithCLIProxy( : getProviderSettingsPath(provider); let claude: ChildProcess; + const imageAnalysisArgs = imageAnalysisMcpReady + ? appendThirdPartyImageAnalysisToolArgs(claudeArgs) + : claudeArgs; const launchArgs = [ '--settings', settingsPath, - ...appendThirdPartyWebSearchToolArgs(appendThirdPartyImageAnalysisToolArgs(claudeArgs)), + ...appendThirdPartyWebSearchToolArgs(imageAnalysisArgs), ]; const traceEnv = createWebSearchTraceContext({ launcher: 'cliproxy.executor', diff --git a/src/commands/install-command.ts b/src/commands/install-command.ts index a829f00a..4353c670 100644 --- a/src/commands/install-command.ts +++ b/src/commands/install-command.ts @@ -75,7 +75,7 @@ export async function handleUninstallCommand(): Promise { console.log(ok('Uninstall complete!')); console.log(''); console.log(info('~/.ccs/ directory preserved')); - console.log(info('To reinstall: ccs --install')); + console.log(info('To reinstall: npm install -g @kaitranntt/ccs --force')); } else { console.log(info('Nothing to uninstall')); } diff --git a/src/copilot/copilot-executor.ts b/src/copilot/copilot-executor.ts index c78763d6..18b84154 100644 --- a/src/copilot/copilot-executor.ts +++ b/src/copilot/copilot-executor.ts @@ -31,6 +31,7 @@ import { import { applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, + resolveImageAnalysisRuntimeConnection, resolveImageAnalysisRuntimeStatus, } from '../utils/hooks'; import { stripClaudeCodeEnv } from '../utils/shell-executor'; @@ -153,13 +154,17 @@ export async function resolveCopilotImageAnalysisEnv( } } + const runtimeConnection = resolveImageAnalysisRuntimeConnection(); return { env: applyImageAnalysisRuntimeOverrides(env, { backendId: status.backendId, model: status.model, runtimePath: status.runtimePath, - baseUrl: `http://127.0.0.1:${CLIPROXY_DEFAULT_PORT}`, - apiKey: resolvedDeps.getLocalRuntimeApiKey(), + baseUrl: runtimeConnection.baseUrl, + apiKey: runtimeConnection.proxyTarget.isRemote + ? runtimeConnection.apiKey + : resolvedDeps.getLocalRuntimeApiKey(), + allowSelfSigned: runtimeConnection.allowSelfSigned, }), warning: null, }; @@ -252,11 +257,25 @@ export async function executeCopilotProfile( // Get global env vars (DISABLE_TELEMETRY, etc.) for third-party profiles const globalEnvConfig = getGlobalEnvConfig(); const globalEnv = globalEnvConfig.enabled ? globalEnvConfig.env : {}; + const imageAnalysisMcpReady = ensureImageAnalysisMcpOrThrow(); + syncWebSearchMcpToConfigDir(claudeConfigDir); + syncImageAnalysisMcpToConfigDir(claudeConfigDir); // Merge with current environment (global env first, copilot overrides, then hook env vars) const webSearchEnv = getWebSearchHookEnv(); - const { env: imageAnalysisEnv, warning: imageAnalysisWarning } = - await resolveCopilotImageAnalysisEnv(); + const imageAnalysisResolution = await resolveCopilotImageAnalysisEnv(); + const imageAnalysisProvisioningFailed = + !imageAnalysisMcpReady && imageAnalysisResolution.env.CCS_IMAGE_ANALYSIS_ENABLED === '1'; + const imageAnalysisWarning = imageAnalysisProvisioningFailed + ? 'ImageAnalysis MCP provisioning failed. This session will use native Read.' + : imageAnalysisResolution.warning; + const imageAnalysisEnv = imageAnalysisProvisioningFailed + ? { + ...imageAnalysisResolution.env, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + } + : imageAnalysisResolution.env; const env = stripClaudeCodeEnv({ ...process.env, ...globalEnv, @@ -272,15 +291,12 @@ export async function executeCopilotProfile( } console.log(''); - ensureImageAnalysisMcpOrThrow(); - syncWebSearchMcpToConfigDir(claudeConfigDir); - syncImageAnalysisMcpToConfigDir(claudeConfigDir); - // Spawn Claude CLI return new Promise((resolve) => { - const launchArgs = appendThirdPartyWebSearchToolArgs( - appendThirdPartyImageAnalysisToolArgs(claudeArgs) - ); + const imageAnalysisArgs = imageAnalysisMcpReady + ? appendThirdPartyImageAnalysisToolArgs(claudeArgs) + : claudeArgs; + const launchArgs = appendThirdPartyWebSearchToolArgs(imageAnalysisArgs); const traceEnv = createWebSearchTraceContext({ launcher: 'copilot.executor', args: launchArgs, diff --git a/src/delegation/headless-executor.ts b/src/delegation/headless-executor.ts index 903d5a6e..a9d05247 100644 --- a/src/delegation/headless-executor.ts +++ b/src/delegation/headless-executor.ts @@ -28,12 +28,12 @@ import { applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, prepareImageAnalysisFallbackHook, + resolveImageAnalysisRuntimeConnection, resolveImageAnalysisRuntimeStatus, } from '../utils/hooks'; import { ensureProfileHooks as ensureImageAnalyzerHooks } from '../utils/hooks/image-analyzer-profile-hook-injector'; -import { resolveCliproxyBridgeMetadata, resolveCliproxyBridgeProfile } from '../api/services'; +import { resolveCliproxyBridgeMetadata } from '../api/services'; import { ensureCliproxyService } from '../cliproxy'; -import { getEffectiveApiKey } from '../cliproxy/auth-token-manager'; import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; import { appendThirdPartyWebSearchToolArgs, @@ -121,7 +121,7 @@ export class HeadlessExecutor { } ensureWebSearchMcpOrThrow(); - ensureImageAnalysisMcpOrThrow(); + const imageAnalysisMcpReady = ensureImageAnalysisMcpOrThrow(); syncWebSearchMcpToConfigDir(inheritedClaudeConfigDir); syncImageAnalysisMcpToConfigDir(inheritedClaudeConfigDir); @@ -143,9 +143,7 @@ export class HeadlessExecutor { cliproxyBridge, sharedHookInstalled: imageAnalysisFallbackHookReady, }); - const currentBridgeAuthToken = cliproxyBridge - ? resolveCliproxyBridgeProfile(cliproxyBridge.provider).apiKey - : getEffectiveApiKey(); + const runtimeConnection = resolveImageAnalysisRuntimeConnection(); let imageAnalysisEnv = getImageAnalysisHookEnv({ profileName: profile, profileType: 'settings', @@ -156,10 +154,19 @@ export class HeadlessExecutor { backendId: imageAnalysisStatus.backendId, model: imageAnalysisStatus.model, runtimePath: imageAnalysisStatus.runtimePath, - baseUrl: cliproxyBridge?.currentBaseUrl || '', - apiKey: currentBridgeAuthToken, + baseUrl: runtimeConnection.baseUrl, + apiKey: runtimeConnection.apiKey, + allowSelfSigned: runtimeConnection.allowSelfSigned, }); + if (!imageAnalysisMcpReady) { + imageAnalysisEnv = { + ...imageAnalysisEnv, + CCS_CURRENT_PROVIDER: '', + CCS_IMAGE_ANALYSIS_SKIP: '1', + }; + } + const imageAnalysisProvider = imageAnalysisEnv['CCS_CURRENT_PROVIDER']; if ( imageAnalysisEnv['CCS_IMAGE_ANALYSIS_SKIP'] !== '1' && @@ -279,9 +286,10 @@ export class HeadlessExecutor { } } - const launchArgs = appendThirdPartyWebSearchToolArgs( - appendThirdPartyImageAnalysisToolArgs(args) - ); + const imageAnalysisArgs = imageAnalysisMcpReady + ? appendThirdPartyImageAnalysisToolArgs(args) + : args; + const launchArgs = appendThirdPartyWebSearchToolArgs(imageAnalysisArgs); const traceEnv = createWebSearchTraceContext({ launcher: 'delegation.headless-executor', args: launchArgs, diff --git a/src/utils/hooks/get-image-analysis-hook-env.ts b/src/utils/hooks/get-image-analysis-hook-env.ts index b68546eb..1bae3cf8 100644 --- a/src/utils/hooks/get-image-analysis-hook-env.ts +++ b/src/utils/hooks/get-image-analysis-hook-env.ts @@ -9,7 +9,13 @@ import { getImageAnalysisConfig } from '../../config/unified-config-loader'; import { resolveCliproxyBridgeProfile } from '../../api/services/cliproxy-profile-bridge'; +import { getEffectiveApiKey } from '../../cliproxy/auth-token-manager'; import { mapExternalProviderName } from '../../cliproxy/provider-capabilities'; +import { + buildProxyUrl, + getProxyTarget, + type ProxyTarget, +} from '../../cliproxy/proxy-target-resolver'; import { getPromptsDir } from '../image-analysis/hook-installer'; import { resolveImageAnalysisStatus, @@ -31,6 +37,53 @@ export interface ImageAnalysisRuntimeOverrides { runtimePath?: string | null; baseUrl?: string | null; apiKey?: string | null; + allowSelfSigned?: boolean | null; +} + +export interface ImageAnalysisRuntimeConnection { + baseUrl: string; + apiKey: string; + allowSelfSigned: boolean; + proxyTarget: ProxyTarget; +} + +export interface ResolveImageAnalysisRuntimeConnectionOptions { + proxyTarget?: ProxyTarget; + tunnelPort?: number | null; +} + +function stripTrailingSlash(value: string): string { + return value.replace(/\/+$/, ''); +} + +export function resolveImageAnalysisRuntimeConnection( + options: ResolveImageAnalysisRuntimeConnectionOptions = {} +): ImageAnalysisRuntimeConnection { + const proxyTarget = options.proxyTarget ?? getProxyTarget(); + const apiKey = proxyTarget.authToken?.trim() || getEffectiveApiKey(); + + if (proxyTarget.isRemote && options.tunnelPort && options.tunnelPort > 0) { + return { + baseUrl: `http://127.0.0.1:${options.tunnelPort}`, + apiKey, + allowSelfSigned: false, + proxyTarget: { + host: '127.0.0.1', + port: options.tunnelPort, + protocol: 'http', + isRemote: false, + }, + }; + } + + return { + baseUrl: stripTrailingSlash(buildProxyUrl(proxyTarget, '')), + apiKey, + allowSelfSigned: Boolean( + proxyTarget.isRemote && proxyTarget.protocol === 'https' && proxyTarget.allowSelfSigned + ), + proxyTarget, + }; } /** @@ -116,5 +169,9 @@ export function applyImageAnalysisRuntimeOverrides( } } + if (overrides.allowSelfSigned !== undefined) { + nextEnv.CCS_IMAGE_ANALYSIS_RUNTIME_ALLOW_SELF_SIGNED = overrides.allowSelfSigned ? '1' : '0'; + } + return nextEnv; } diff --git a/src/utils/hooks/image-analysis-runtime-status.ts b/src/utils/hooks/image-analysis-runtime-status.ts index 7897b8c5..9ec8a388 100644 --- a/src/utils/hooks/image-analysis-runtime-status.ts +++ b/src/utils/hooks/image-analysis-runtime-status.ts @@ -1,4 +1,9 @@ import { getAuthStatus, initializeAccounts, type AuthStatus } from '../../cliproxy/auth-handler'; +import { + checkRemoteProxy, + type RemoteProxyClientConfig, + type RemoteProxyStatus, +} from '../../cliproxy/remote-proxy-client'; import { fetchRemoteAuthStatus, type RemoteAuthStatus } from '../../cliproxy/remote-auth-fetcher'; import { getProxyTarget, type ProxyTarget } from '../../cliproxy/proxy-target-resolver'; import { getProviderDisplayName, isCLIProxyProvider } from '../../cliproxy/provider-capabilities'; @@ -15,6 +20,7 @@ import { } from './image-analysis-backend-resolver'; interface ImageAnalysisRuntimeStatusDeps { + checkRemoteProxy: (config: RemoteProxyClientConfig) => Promise; fetchRemoteAuthStatus: (target: ProxyTarget) => Promise; getAuthStatus: (provider: CLIProxyProvider) => AuthStatus; getProxyTarget: () => ProxyTarget; @@ -23,6 +29,7 @@ interface ImageAnalysisRuntimeStatusDeps { } const defaultDeps: ImageAnalysisRuntimeStatusDeps = { + checkRemoteProxy, fetchRemoteAuthStatus, getAuthStatus, getProxyTarget, @@ -91,16 +98,25 @@ async function resolveProxyReadiness( } const target = deps.getProxyTarget(); - const reachable = await deps.isCliproxyRunning(); if (target.isRemote) { + const remoteStatus = await deps.checkRemoteProxy({ + host: target.host, + port: target.port, + protocol: target.protocol, + authToken: target.authToken, + allowSelfSigned: target.allowSelfSigned, + }); + return { - proxyReadiness: reachable ? 'remote' : 'unavailable', - proxyReason: reachable + proxyReadiness: remoteStatus.reachable ? 'remote' : 'unavailable', + proxyReason: remoteStatus.reachable ? `Remote CLIProxy target ${target.host}:${target.port} is reachable.` - : `Remote CLIProxy target ${target.host}:${target.port} is unreachable.`, + : remoteStatus.error || + `Remote CLIProxy target ${target.host}:${target.port} is unreachable.`, }; } + const reachable = await deps.isCliproxyRunning(); return { proxyReadiness: reachable ? 'ready' : 'stopped', proxyReason: reachable diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 913ff285..6f61058e 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -14,7 +14,10 @@ import { export { getImageAnalysisHookEnv, applyImageAnalysisRuntimeOverrides, + resolveImageAnalysisRuntimeConnection, type ImageAnalysisRuntimeOverrides, + type ImageAnalysisRuntimeConnection, + type ResolveImageAnalysisRuntimeConnectionOptions, } from './get-image-analysis-hook-env'; export { canonicalizeImageAnalysisConfig, diff --git a/src/utils/image-analysis/index.ts b/src/utils/image-analysis/index.ts index 89d5968d..852a612a 100644 --- a/src/utils/image-analysis/index.ts +++ b/src/utils/image-analysis/index.ts @@ -9,6 +9,9 @@ export { getImageAnalysisMcpServerName, getImageAnalysisMcpServerPath, getImageAnalysisMcpRuntimePath, + hasImageAnalysisMcpServerInstalled, + hasImageAnalysisMcpConfig, + hasImageAnalysisMcpReady, installImageAnalysisMcpServer, ensureImageAnalysisMcpConfig, ensureImageAnalysisMcp, diff --git a/src/utils/image-analysis/mcp-installer.ts b/src/utils/image-analysis/mcp-installer.ts index 11d53607..c8b3e571 100644 --- a/src/utils/image-analysis/mcp-installer.ts +++ b/src/utils/image-analysis/mcp-installer.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as lockfile from 'proper-lockfile'; import { getImageAnalysisConfig } from '../../config/unified-config-loader'; import { getCcsDir } from '../config-manager'; import { getClaudeUserConfigPath } from '../claude-config-path'; @@ -119,53 +120,115 @@ function writeClaudeUserConfig(configPath: string, config: ClaudeUserConfig): bo } } -function removeManagedServerConfig(configPath: string): boolean { - if (!fs.existsSync(configPath)) { - return false; +function withClaudeUserConfigLock(configPath: string, callback: () => T): T { + const lockTarget = fs.existsSync(configPath) ? configPath : path.dirname(configPath); + let release: (() => void) | undefined; + + if (!fs.existsSync(path.dirname(configPath))) { + fs.mkdirSync(path.dirname(configPath), { recursive: true, mode: 0o700 }); } + try { + release = lockfile.lockSync(lockTarget, { stale: 10000 }) as () => void; + return callback(); + } finally { + if (release) { + try { + release(); + } catch { + // Best-effort release. + } + } + } +} + +export function hasImageAnalysisMcpServerInstalled(): boolean { + return ( + fs.existsSync(getImageAnalysisMcpServerPath()) && + fs.existsSync(getImageAnalysisMcpRuntimePath()) + ); +} + +export function hasImageAnalysisMcpConfig(configPath = getClaudeUserConfigPath()): boolean { const config = readClaudeUserConfig(configPath); if (config === null) { - if (process.env.CCS_DEBUG) { - console.error(warn(`Malformed Claude config prevents MCP cleanup: ${configPath}`)); - } return false; } const existingServers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers) - ? { ...(config.mcpServers as Record) } + ? (config.mcpServers as Record) : {}; + const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; - if (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) { + return ( + typeof currentConfig === 'object' && + currentConfig !== null && + JSON.stringify(currentConfig) === + JSON.stringify({ + type: 'stdio', + command: 'node', + args: [getImageAnalysisMcpServerPath()], + env: {}, + }) + ); +} + +export function hasImageAnalysisMcpReady(configPath = getClaudeUserConfigPath()): boolean { + return hasImageAnalysisMcpServerInstalled() && hasImageAnalysisMcpConfig(configPath); +} + +function removeManagedServerConfig(configPath: string): boolean { + if (!fs.existsSync(configPath)) { return false; } - delete existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; - - const nextConfig: ClaudeUserConfig = { ...config }; - if (Object.keys(existingServers).length === 0) { - delete nextConfig.mcpServers; - } else { - nextConfig.mcpServers = existingServers; - } - - try { - writeClaudeUserConfig(configPath, nextConfig); - if (process.env.CCS_DEBUG) { - console.error(info(`Removed Image Analysis MCP config from ${configPath}`)); + return withClaudeUserConfigLock(configPath, () => { + const config = readClaudeUserConfig(configPath); + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Malformed Claude config prevents MCP cleanup: ${configPath}`)); + } + return false; } - return true; - } catch (error) { - if (process.env.CCS_DEBUG) { - console.error( - warn( - `Failed to remove Image Analysis MCP config from ${configPath}: ${(error as Error).message}` - ) - ); + + const existingServers = + config.mcpServers && + typeof config.mcpServers === 'object' && + !Array.isArray(config.mcpServers) + ? { ...(config.mcpServers as Record) } + : {}; + + if (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) { + return false; } - return false; - } + + delete existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + + const nextConfig: ClaudeUserConfig = { ...config }; + if (Object.keys(existingServers).length === 0) { + delete nextConfig.mcpServers; + } else { + nextConfig.mcpServers = existingServers; + } + + try { + writeClaudeUserConfig(configPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Removed Image Analysis MCP config from ${configPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn( + `Failed to remove Image Analysis MCP config from ${configPath}: ${(error as Error).message}` + ) + ); + } + return false; + } + }); } export function installImageAnalysisMcpServer(): boolean { @@ -258,23 +321,9 @@ export function ensureImageAnalysisMcpConfig(): boolean { const claudeUserConfigPath = getClaudeUserConfigPath(); const claudeUserConfigDir = path.dirname(claudeUserConfigPath); - const config = readClaudeUserConfig(claudeUserConfigPath); - - if (config === null) { - if (process.env.CCS_DEBUG) { - console.error(warn('Malformed ~/.claude.json prevents Image Analysis MCP provisioning')); - } - return false; - } - if (!fs.existsSync(claudeUserConfigDir)) { fs.mkdirSync(claudeUserConfigDir, { recursive: true, mode: 0o700 }); } - - const existingServers = - config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers) - ? (config.mcpServers as Record) - : {}; const desiredServerConfig: ManagedImageAnalysisMcpConfig = { type: 'stdio', command: 'node', @@ -282,35 +331,52 @@ export function ensureImageAnalysisMcpConfig(): boolean { env: {}, }; - const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; - if ( - typeof currentConfig === 'object' && - currentConfig !== null && - JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig) - ) { - return true; - } + return withClaudeUserConfigLock(claudeUserConfigPath, () => { + const config = readClaudeUserConfig(claudeUserConfigPath); - const nextConfig: ClaudeUserConfig = { - ...config, - mcpServers: { - ...existingServers, - [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig, - }, - }; + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn('Malformed ~/.claude.json prevents Image Analysis MCP provisioning')); + } + return false; + } - try { - writeClaudeUserConfig(claudeUserConfigPath, nextConfig); - if (process.env.CCS_DEBUG) { - console.error(info(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`)); + const existingServers = + config.mcpServers && + typeof config.mcpServers === 'object' && + !Array.isArray(config.mcpServers) + ? (config.mcpServers as Record) + : {}; + const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + if ( + typeof currentConfig === 'object' && + currentConfig !== null && + JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig) + ) { + return true; } - return true; - } catch (error) { - if (process.env.CCS_DEBUG) { - console.error(warn(`Failed to update ~/.claude.json: ${(error as Error).message}`)); + + const nextConfig: ClaudeUserConfig = { + ...config, + mcpServers: { + ...existingServers, + [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig, + }, + }; + + try { + writeClaudeUserConfig(claudeUserConfigPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Failed to update ~/.claude.json: ${(error as Error).message}`)); + } + return false; } - return false; - } + }); } export function ensureImageAnalysisMcp(): boolean { @@ -377,17 +443,20 @@ export function uninstallImageAnalysisMcp(): boolean { return removedConfig || removedServer; } -export function ensureImageAnalysisMcpOrThrow(): void { +export function ensureImageAnalysisMcpOrThrow(): boolean { const imageConfig = getImageAnalysisConfig(); if (!imageConfig.enabled) { - return; + return false; } - if (!ensureImageAnalysisMcp()) { + const ready = ensureImageAnalysisMcp(); + if (!ready) { console.error( warn( 'Image Analysis is enabled, but CCS could not prepare the local ImageAnalysis tool. This session will fall back to native Read.' ) ); } + + return ready; } diff --git a/src/web-server/routes/image-analysis-routes.ts b/src/web-server/routes/image-analysis-routes.ts index 06148c82..970d0e02 100644 --- a/src/web-server/routes/image-analysis-routes.ts +++ b/src/web-server/routes/image-analysis-routes.ts @@ -16,9 +16,15 @@ import { extractProviderFromPathname } from '../../cliproxy/model-id-normalizer' import { normalizeImageAnalysisBackendId, resolveImageAnalysisRuntimeStatus, + prepareImageAnalysisFallbackHook, } from '../../utils/hooks'; import { hasImageAnalyzerHook } from '../../utils/hooks/image-analyzer-hook-installer'; import { hasImageAnalysisProfileHook } from '../../utils/hooks/image-analyzer-profile-hook-injector'; +import { InstanceManager } from '../../management/instance-manager'; +import { + ensureImageAnalysisMcpOrThrow, + hasImageAnalysisMcpReady, +} from '../../utils/image-analysis'; const router = Router(); const IMAGE_ANALYSIS_LOCAL_ACCESS_ERROR = @@ -78,16 +84,25 @@ function resolveTarget(target: unknown): DashboardTarget { function resolveCurrentTargetMode( target: DashboardTarget, - status: Awaited> + status: Awaited>, + managedToolReady: boolean ): CurrentTargetMode { if (!status.enabled) return 'disabled'; if (target !== 'claude') return 'bypassed'; if (status.nativeReadPreference) return 'native'; + if (!managedToolReady) return 'setup'; if (!status.backendId) return 'unresolved'; if (status.effectiveRuntimeMode === 'native-read') return 'fallback'; return 'active'; } +function syncManagedImageAnalysisToInstances(): void { + const instanceManager = new InstanceManager(); + for (const instanceName of instanceManager.listInstances()) { + instanceManager.syncMcpServers(instanceManager.getInstancePath(instanceName)); + } +} + function resolveBackendState( status: Awaited> ): BackendState { @@ -109,6 +124,7 @@ async function buildDashboardPayload() { const config = getImageAnalysisConfig(); const { profiles, variants } = listApiProfiles(); const sharedHookInstalled = hasImageAnalyzerHook(); + const managedToolReady = hasImageAnalysisMcpReady(); const profileRows = await Promise.all( profiles.map(async (profile) => { @@ -146,7 +162,11 @@ async function buildDashboardPayload() { status: status.status, effectiveRuntimeMode: status.effectiveRuntimeMode, effectiveRuntimeReason: status.effectiveRuntimeReason, - currentTargetMode: resolveCurrentTargetMode(resolveTarget(profile.target), status), + currentTargetMode: resolveCurrentTargetMode( + resolveTarget(profile.target), + status, + managedToolReady + ), profileModel: status.profileModel, nativeReadPreference: status.nativeReadPreference, nativeImageCapable: status.nativeImageCapable, @@ -190,7 +210,11 @@ async function buildDashboardPayload() { status: status.status, effectiveRuntimeMode: status.effectiveRuntimeMode, effectiveRuntimeReason: status.effectiveRuntimeReason, - currentTargetMode: resolveCurrentTargetMode(resolveTarget(variant.target), status), + currentTargetMode: resolveCurrentTargetMode( + resolveTarget(variant.target), + status, + managedToolReady + ), profileModel: status.profileModel, nativeReadPreference: status.nativeReadPreference, nativeImageCapable: status.nativeImageCapable, @@ -260,6 +284,11 @@ async function buildDashboardPayload() { summaryState = 'disabled'; title = 'Disabled'; detail = 'Image is turned off globally. Images and PDFs fall back to native file access.'; + } else if (!managedToolReady) { + summaryState = 'needs_setup'; + title = 'Needs local runtime'; + detail = + 'CCS could not provision the local ImageAnalysis MCP runtime yet. Profiles will fall back to native Read until provisioning succeeds.'; } else if (backendRows.length === 0) { summaryState = 'needs_setup'; title = 'Needs provider models'; @@ -288,6 +317,10 @@ async function buildDashboardPayload() { bypassedProfileCount, nativeProfileCount, }, + runtime: { + managedToolReady, + sharedHookInstalled, + }, backends: backendRows, profiles: allProfileRows, catalog: { @@ -434,6 +467,13 @@ router.put('/', async (req: Request, res: Response): Promise => { }; }); + const nextEnabled = body.enabled ?? currentConfig.enabled; + if (nextEnabled) { + ensureImageAnalysisMcpOrThrow(); + prepareImageAnalysisFallbackHook(); + syncManagedImageAnalysisToInstances(); + } + res.json(await buildDashboardPayload()); } catch (error) { res.status(500).json({ error: (error as Error).message }); diff --git a/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts b/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts index 24cb3d18..36036cfd 100644 --- a/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts +++ b/tests/unit/cliproxy/env-resolver-codex-fallback.test.ts @@ -261,6 +261,7 @@ describe('resolveCliproxyImageAnalysisEnv', () => { expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('https://remote.example.com:9443'); expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH).toBe('/api/provider/agy'); expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('remote-token'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_ALLOW_SELF_SIGNED).toBe('1'); expect(result.warning).toBeNull(); }); @@ -296,6 +297,45 @@ describe('resolveCliproxyImageAnalysisEnv', () => { expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('http://127.0.0.1:8317'); expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_PATH).toBe('/api/provider/agy'); expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('local-runtime-token'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_ALLOW_SELF_SIGNED).toBe('0'); + expect(result.warning).toBeNull(); + }); + + it('routes remote HTTPS image analysis through the local tunnel when available', async () => { + const result = await resolveCliproxyImageAnalysisEnv( + { + profileName: 'orq', + provider: 'agy', + profileSettingsPath: '/tmp/orq.settings.json', + proxyTarget: { + host: 'remote.example.com', + port: 9443, + protocol: 'https', + authToken: 'remote-token', + managementKey: 'remote-management-key', + allowSelfSigned: true, + isRemote: true, + }, + tunnelPort: 9911, + proxyReachable: true, + }, + { + getImageAnalysisHookEnv: () => ({ + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_TIMEOUT: '60', + CCS_IMAGE_ANALYSIS_PROVIDER_MODELS: 'agy:gemini-2.5-pro', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_SKIP: '0', + }), + hasImageAnalysisProfileHook: () => true, + hasImageAnalyzerHook: () => true, + resolveImageAnalysisRuntimeStatus: async () => createImageAnalysisStatus(), + } + ); + + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_BASE_URL).toBe('http://127.0.0.1:9911'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_API_KEY).toBe('remote-token'); + expect(result.env.CCS_IMAGE_ANALYSIS_RUNTIME_ALLOW_SELF_SIGNED).toBe('0'); expect(result.warning).toBeNull(); }); }); diff --git a/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts b/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts index 6e77eb3b..b4919ede 100644 --- a/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts +++ b/tests/unit/hooks/ccs-image-analysis-mcp-server.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'bun:test'; import { spawn } from 'child_process'; -import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import * as http from 'node:http'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; @@ -11,6 +11,11 @@ function encodeMessage(message: unknown): string { return `${JSON.stringify(message)}\n`; } +function encodeLegacyMessage(message: unknown): string { + const body = JSON.stringify(message); + return `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`; +} + function collectResponses( child: ReturnType, expectedCount: number @@ -133,6 +138,7 @@ describe('ccs-image-analysis MCP server', () => { }); const child = spawn('node', [serverPath], { + cwd: tempDir, env: { ...process.env, CCS_IMAGE_ANALYSIS_ENABLED: '1', @@ -189,7 +195,7 @@ describe('ccs-image-analysis MCP server', () => { filePath: { type: 'string', description: - 'Absolute or workspace-relative path to a local image or PDF file to analyze.', + 'Workspace-relative path, or an absolute path inside the current workspace, to a local image or PDF file to analyze.', }, focus: { type: 'string', @@ -228,7 +234,10 @@ describe('ccs-image-analysis MCP server', () => { }); it('returns a structured tool error when the file does not exist', async () => { + tempDir = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-mcp-server-missing-')); + const missingPath = join(tempDir, 'missing-image.png'); const child = spawn('node', [serverPath], { + cwd: tempDir, env: { ...process.env, CCS_IMAGE_ANALYSIS_ENABLED: '1', @@ -260,7 +269,7 @@ describe('ccs-image-analysis MCP server', () => { method: 'tools/call', params: { name: 'ImageAnalysis', - arguments: { filePath: join(tmpdir(), 'missing-image.png') }, + arguments: { filePath: missingPath }, }, }) ); @@ -272,7 +281,7 @@ describe('ccs-image-analysis MCP server', () => { content: [ { type: 'text', - text: `ImageAnalysis could not find file: ${join(tmpdir(), 'missing-image.png')}`, + text: `ImageAnalysis could not find file: ${missingPath}`, }, ], isError: true, @@ -282,6 +291,62 @@ describe('ccs-image-analysis MCP server', () => { } }); + it('rejects paths outside the current workspace', async () => { + tempDir = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-mcp-server-scope-')); + const workspaceDir = join(tempDir, 'workspace'); + mkdirSync(workspaceDir, { recursive: true }); + const outsidePath = join(tempDir, 'outside.png'); + createTestPng(outsidePath); + + const child = spawn('node', [serverPath], { + cwd: workspaceDir, + env: { + ...process.env, + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_SKIP: '0', + CCS_CURRENT_PROVIDER: 'agy', + CCS_IMAGE_ANALYSIS_MODEL: 'gemini-3-1-flash-preview', + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + try { + const responsesPromise = collectResponses(child, 2); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'bun-test', version: '1.0.0' }, + }, + }) + ); + child.stdin.write( + encodeMessage({ + jsonrpc: '2.0', + id: 2, + method: 'tools/call', + params: { + name: 'ImageAnalysis', + arguments: { filePath: '../outside.png' }, + }, + }) + ); + + const responses = await responsesPromise; + const toolCall = responses.find((message) => message.id === 2); + expect(toolCall?.error).toEqual({ + code: -32602, + message: 'ImageAnalysis only allows files inside the current workspace.', + }); + } finally { + child.kill(); + } + }); + it('sends PDF files as document blocks to the provider-scoped route', async () => { tempDir = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-mcp-server-pdf-')); const pdfPath = join(tempDir, 'manual.pdf'); @@ -322,6 +387,7 @@ describe('ccs-image-analysis MCP server', () => { }); const child = spawn('node', [serverPath], { + cwd: tempDir, env: { ...process.env, CCS_IMAGE_ANALYSIS_ENABLED: '1', @@ -377,4 +443,39 @@ describe('ccs-image-analysis MCP server', () => { child.kill(); } }); + + it('accepts legacy Content-Length framed requests for compatibility', async () => { + const child = spawn('node', [serverPath], { + env: { + ...process.env, + CCS_IMAGE_ANALYSIS_ENABLED: '1', + CCS_IMAGE_ANALYSIS_SKIP: '1', + CCS_CURRENT_PROVIDER: 'agy', + }, + stdio: ['pipe', 'pipe', 'pipe'], + }); + + try { + const responsesPromise = collectResponses(child, 2); + child.stdin.write( + encodeLegacyMessage({ + jsonrpc: '2.0', + id: 1, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: {}, + clientInfo: { name: 'bun-test', version: '1.0.0' }, + }, + }) + ); + child.stdin.write(encodeLegacyMessage({ jsonrpc: '2.0', id: 2, method: 'tools/list' })); + + const responses = await responsesPromise; + const toolsList = responses.find((message) => message.id === 2); + expect(toolsList?.result).toEqual({ tools: [] }); + } finally { + child.kill(); + } + }); }); diff --git a/tests/unit/targets/settings-profile-image-analysis-launch.test.ts b/tests/unit/targets/settings-profile-image-analysis-launch.test.ts index 5baed5f7..5154512e 100644 --- a/tests/unit/targets/settings-profile-image-analysis-launch.test.ts +++ b/tests/unit/targets/settings-profile-image-analysis-launch.test.ts @@ -151,8 +151,20 @@ exit 0 expect(result.stderr).not.toContain('could not prepare the local ImageAnalysis tool'); expect(fs.existsSync(claudeArgsLogPath)).toBe(true); const launchedArgs = fs.readFileSync(claudeArgsLogPath, 'utf8'); - expect(launchedArgs).toContain('--append-system-prompt'); - expect(launchedArgs).toContain(STEERING_PROMPT_SNIPPET); + expect(launchedArgs).not.toContain(STEERING_PROMPT_SNIPPET); + }); + + it('falls back to native Read when the ImageAnalysis MCP runtime cannot be provisioned', () => { + if (process.platform === 'win32') return; + + fs.writeFileSync(path.join(tmpHome, '.claude.json'), '{not-json', 'utf8'); + + const result = runCcs(['glm', 'smoke'], baseEnv); + + expect(result.status).toBe(0); + expect(result.stderr).toContain('could not prepare the local ImageAnalysis tool'); + const launchedArgs = fs.readFileSync(claudeArgsLogPath, 'utf8'); + expect(launchedArgs).not.toContain(STEERING_PROMPT_SNIPPET); }); it('pins bridge-backed image analysis to the current CLIProxy auth token', () => { diff --git a/tests/unit/utils/hooks/get-image-analysis-hook-env.test.ts b/tests/unit/utils/hooks/get-image-analysis-hook-env.test.ts new file mode 100644 index 00000000..4dbf93bb --- /dev/null +++ b/tests/unit/utils/hooks/get-image-analysis-hook-env.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'bun:test'; +import { resolveImageAnalysisRuntimeConnection } from '../../../../src/utils/hooks'; + +describe('resolveImageAnalysisRuntimeConnection', () => { + it('returns a direct local runtime connection for local CLIProxy targets', () => { + const connection = resolveImageAnalysisRuntimeConnection({ + proxyTarget: { + host: '127.0.0.1', + port: 8317, + protocol: 'http', + isRemote: false, + }, + }); + + expect(connection.baseUrl).toBe('http://127.0.0.1:8317'); + expect(connection.allowSelfSigned).toBe(false); + expect(connection.proxyTarget.isRemote).toBe(false); + }); + + it('returns the remote runtime base URL and TLS flag for self-signed targets', () => { + const connection = resolveImageAnalysisRuntimeConnection({ + proxyTarget: { + host: 'remote.example.com', + port: 9443, + protocol: 'https', + authToken: 'remote-token', + allowSelfSigned: true, + isRemote: true, + }, + }); + + expect(connection.baseUrl).toBe('https://remote.example.com:9443'); + expect(connection.apiKey).toBe('remote-token'); + expect(connection.allowSelfSigned).toBe(true); + expect(connection.proxyTarget.isRemote).toBe(true); + }); + + it('prefers the local tunnel when one is active for a remote target', () => { + const connection = resolveImageAnalysisRuntimeConnection({ + proxyTarget: { + host: 'remote.example.com', + port: 9443, + protocol: 'https', + authToken: 'remote-token', + allowSelfSigned: true, + isRemote: true, + }, + tunnelPort: 9911, + }); + + expect(connection.baseUrl).toBe('http://127.0.0.1:9911'); + expect(connection.apiKey).toBe('remote-token'); + expect(connection.allowSelfSigned).toBe(false); + expect(connection.proxyTarget).toMatchObject({ + host: '127.0.0.1', + port: 9911, + protocol: 'http', + isRemote: false, + }); + }); +}); diff --git a/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts b/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts index 40ae163d..ccb02d26 100644 --- a/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts +++ b/tests/unit/utils/hooks/image-analysis-runtime-status.test.ts @@ -38,6 +38,7 @@ function createStatus(overrides: Partial = {}): ImageAnalys describe('image-analysis-runtime-status', () => { it('falls back to native read when provider auth is missing', async () => { const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), { + checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }), getProxyTarget: () => ({ host: '127.0.0.1', port: 8317, @@ -63,6 +64,7 @@ describe('image-analysis-runtime-status', () => { it('marks an idle local proxy as launchable when auth is ready', async () => { const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), { + checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }), getProxyTarget: () => ({ host: '127.0.0.1', port: 8317, @@ -107,7 +109,10 @@ describe('image-analysis-runtime-status', () => { source: 'remote', }, ], - isCliproxyRunning: async () => false, + checkRemoteProxy: async () => ({ + reachable: false, + error: 'Remote CLIProxy target remote.example:443 is unreachable.', + }), }); expect(status.authReadiness).toBe('ready'); @@ -123,6 +128,7 @@ describe('image-analysis-runtime-status', () => { reason: 'Profile hook is missing from the persisted settings file.', }), { + checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }), getProxyTarget: () => ({ host: '127.0.0.1', port: 8317, @@ -147,4 +153,38 @@ describe('image-analysis-runtime-status', () => { expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis'); expect(status.effectiveRuntimeReason).toContain('Profile hook is missing'); }); + + it('marks a reachable remote proxy as remote instead of inspecting local 8317', async () => { + const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), { + getProxyTarget: () => ({ + host: 'remote.example', + port: 443, + protocol: 'https', + authToken: 'token', + managementKey: 'secret', + allowSelfSigned: true, + isRemote: true, + }), + fetchRemoteAuthStatus: async () => [ + { + provider: 'ghcp', + displayName: 'GitHub Copilot (OAuth)', + authenticated: true, + tokenFiles: 1, + accounts: [], + defaultAccount: null, + source: 'remote', + }, + ], + checkRemoteProxy: async () => ({ + reachable: true, + latencyMs: 42, + }), + isCliproxyRunning: async () => false, + }); + + expect(status.proxyReadiness).toBe('remote'); + expect(status.proxyReason).toContain('remote.example:443'); + expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis'); + }); }); diff --git a/tests/unit/utils/image-analysis/mcp-installer.test.ts b/tests/unit/utils/image-analysis/mcp-installer.test.ts index b958bb5a..cae4076a 100644 --- a/tests/unit/utils/image-analysis/mcp-installer.test.ts +++ b/tests/unit/utils/image-analysis/mcp-installer.test.ts @@ -1,12 +1,14 @@ -import { afterEach, describe, expect, it, mock } from 'bun:test'; +import { afterEach, describe, expect, it, mock, spyOn } from 'bun:test'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; +import * as lockfile from 'proper-lockfile'; import { ensureImageAnalysisMcp, getImageAnalysisMcpRuntimePath, getImageAnalysisMcpServerName, getImageAnalysisMcpServerPath, + hasImageAnalysisMcpReady, uninstallImageAnalysisMcp, } from '../../../../src/utils/image-analysis'; @@ -102,6 +104,7 @@ describe('ensureImageAnalysisMcp', () => { expect(config.mcpServers.existing).toEqual({ command: 'uvx', args: ['some-server'] }); expect(config.mcpServers[getImageAnalysisMcpServerName()]).toEqual(getManagedConfig()); + expect(hasImageAnalysisMcpReady(claudeUserConfigPath)).toBe(true); }); it('removes the managed MCP runtime while preserving unrelated server entries', () => { @@ -177,4 +180,14 @@ describe('ensureImageAnalysisMcp', () => { expect(fs.existsSync(getImageAnalysisMcpServerPath())).toBe(true); expect(fs.existsSync(getImageAnalysisMcpRuntimePath())).toBe(true); }); + + it('serializes ~/.claude.json updates with a file lock', () => { + setupTempHome(); + writeEnabledConfig(); + + const lockSpy = spyOn(lockfile, 'lockSync'); + + expect(ensureImageAnalysisMcp()).toBe(true); + expect(lockSpy).toHaveBeenCalled(); + }); }); diff --git a/tests/unit/web-server/image-analysis-routes.test.ts b/tests/unit/web-server/image-analysis-routes.test.ts index fb34f566..2b329b15 100644 --- a/tests/unit/web-server/image-analysis-routes.test.ts +++ b/tests/unit/web-server/image-analysis-routes.test.ts @@ -171,7 +171,7 @@ describe('image-analysis routes', () => { expect.objectContaining({ name: 'glm', target: 'claude', - currentTargetMode: 'fallback', + currentTargetMode: 'setup', }), expect.objectContaining({ name: 'codexProfile', @@ -185,9 +185,13 @@ describe('image-analysis routes', () => { backendCount: 2, bypassedProfileCount: 1, }); + expect(payload.runtime).toMatchObject({ + managedToolReady: false, + sharedHookInstalled: false, + }); }); - it('updates the saved config through the dashboard route', async () => { + it('updates the saved config through the dashboard route and provisions the local runtime', async () => { const response = await fetch(`${baseUrl}/api/image-analysis`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, @@ -219,6 +223,28 @@ describe('image-analysis routes', () => { expect(payload.config.providerModels).toMatchObject({ gemini: 'gemini-2.5-pro', }); + expect(payload.runtime).toMatchObject({ + managedToolReady: true, + sharedHookInstalled: true, + }); + + const ccsDir = path.join(tempHome, '.ccs'); + expect(fs.existsSync(path.join(ccsDir, 'mcp', 'ccs-image-analysis-server.cjs'))).toBe(true); + expect(fs.existsSync(path.join(ccsDir, 'mcp', 'image-analysis-runtime.cjs'))).toBe(true); + expect(fs.existsSync(path.join(ccsDir, 'hooks', 'image-analyzer-transformer.cjs'))).toBe(true); + expect(fs.existsSync(path.join(ccsDir, 'hooks', 'image-analysis-runtime.cjs'))).toBe(true); + + const claudeConfig = JSON.parse( + fs.readFileSync(path.join(tempHome, '.claude.json'), 'utf8') + ) as { + mcpServers?: Record; + }; + expect(claudeConfig.mcpServers?.['ccs-image-analysis']).toEqual({ + type: 'stdio', + command: 'node', + args: [path.join(ccsDir, 'mcp', 'ccs-image-analysis-server.cjs')], + env: {}, + }); }); it('rejects profile mappings that point to a missing backend with a client error', async () => { From afc8b0bb6ea51dd6432bd0f248a4043ab86e2311 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 18:53:11 -0400 Subject: [PATCH 11/48] fix(image-analysis): handle installer lock contention --- src/utils/image-analysis/mcp-installer.ts | 185 +++++++++++------- .../image-analysis/mcp-installer.test.ts | 23 +++ 2 files changed, 132 insertions(+), 76 deletions(-) diff --git a/src/utils/image-analysis/mcp-installer.ts b/src/utils/image-analysis/mcp-installer.ts index c8b3e571..5dc5ed5e 100644 --- a/src/utils/image-analysis/mcp-installer.ts +++ b/src/utils/image-analysis/mcp-installer.ts @@ -121,7 +121,7 @@ function writeClaudeUserConfig(configPath: string, config: ClaudeUserConfig): bo } function withClaudeUserConfigLock(configPath: string, callback: () => T): T { - const lockTarget = fs.existsSync(configPath) ? configPath : path.dirname(configPath); + const lockTarget = path.dirname(configPath); let release: (() => void) | undefined; if (!fs.existsSync(path.dirname(configPath))) { @@ -142,6 +142,11 @@ function withClaudeUserConfigLock(configPath: string, callback: () => T): T { } } +function isLockUnavailableError(error: unknown): boolean { + const code = (error as NodeJS.ErrnoException | undefined)?.code; + return code === 'ELOCKED' || code === 'ENOTACQUIRED'; +} + export function hasImageAnalysisMcpServerInstalled(): boolean { return ( fs.existsSync(getImageAnalysisMcpServerPath()) && @@ -183,52 +188,66 @@ function removeManagedServerConfig(configPath: string): boolean { return false; } - return withClaudeUserConfigLock(configPath, () => { - const config = readClaudeUserConfig(configPath); - if (config === null) { - if (process.env.CCS_DEBUG) { - console.error(warn(`Malformed Claude config prevents MCP cleanup: ${configPath}`)); + try { + return withClaudeUserConfigLock(configPath, () => { + const config = readClaudeUserConfig(configPath); + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Malformed Claude config prevents MCP cleanup: ${configPath}`)); + } + return false; } - return false; - } - const existingServers = - config.mcpServers && - typeof config.mcpServers === 'object' && - !Array.isArray(config.mcpServers) - ? { ...(config.mcpServers as Record) } - : {}; + const existingServers = + config.mcpServers && + typeof config.mcpServers === 'object' && + !Array.isArray(config.mcpServers) + ? { ...(config.mcpServers as Record) } + : {}; - if (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) { - return false; - } - - delete existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; - - const nextConfig: ClaudeUserConfig = { ...config }; - if (Object.keys(existingServers).length === 0) { - delete nextConfig.mcpServers; - } else { - nextConfig.mcpServers = existingServers; - } - - try { - writeClaudeUserConfig(configPath, nextConfig); - if (process.env.CCS_DEBUG) { - console.error(info(`Removed Image Analysis MCP config from ${configPath}`)); + if (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) { + return false; } - return true; - } catch (error) { + + delete existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + + const nextConfig: ClaudeUserConfig = { ...config }; + if (Object.keys(existingServers).length === 0) { + delete nextConfig.mcpServers; + } else { + nextConfig.mcpServers = existingServers; + } + + try { + writeClaudeUserConfig(configPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Removed Image Analysis MCP config from ${configPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error( + warn( + `Failed to remove Image Analysis MCP config from ${configPath}: ${(error as Error).message}` + ) + ); + } + return false; + } + }); + } catch (error) { + if (isLockUnavailableError(error)) { if (process.env.CCS_DEBUG) { console.error( warn( - `Failed to remove Image Analysis MCP config from ${configPath}: ${(error as Error).message}` + `Image Analysis MCP cleanup skipped because ${configPath} is locked by another process` ) ); } return false; } - }); + throw error; + } } export function installImageAnalysisMcpServer(): boolean { @@ -331,52 +350,66 @@ export function ensureImageAnalysisMcpConfig(): boolean { env: {}, }; - return withClaudeUserConfigLock(claudeUserConfigPath, () => { - const config = readClaudeUserConfig(claudeUserConfigPath); + try { + return withClaudeUserConfigLock(claudeUserConfigPath, () => { + const config = readClaudeUserConfig(claudeUserConfigPath); - if (config === null) { + if (config === null) { + if (process.env.CCS_DEBUG) { + console.error(warn('Malformed ~/.claude.json prevents Image Analysis MCP provisioning')); + } + return false; + } + + const existingServers = + config.mcpServers && + typeof config.mcpServers === 'object' && + !Array.isArray(config.mcpServers) + ? (config.mcpServers as Record) + : {}; + const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; + if ( + typeof currentConfig === 'object' && + currentConfig !== null && + JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig) + ) { + return true; + } + + const nextConfig: ClaudeUserConfig = { + ...config, + mcpServers: { + ...existingServers, + [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig, + }, + }; + + try { + writeClaudeUserConfig(claudeUserConfigPath, nextConfig); + if (process.env.CCS_DEBUG) { + console.error(info(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`)); + } + return true; + } catch (error) { + if (process.env.CCS_DEBUG) { + console.error(warn(`Failed to update ~/.claude.json: ${(error as Error).message}`)); + } + return false; + } + }); + } catch (error) { + if (isLockUnavailableError(error)) { if (process.env.CCS_DEBUG) { - console.error(warn('Malformed ~/.claude.json prevents Image Analysis MCP provisioning')); + console.error( + warn( + `Image Analysis MCP provisioning skipped because ${claudeUserConfigPath} is locked by another process` + ) + ); } return false; } - - const existingServers = - config.mcpServers && - typeof config.mcpServers === 'object' && - !Array.isArray(config.mcpServers) - ? (config.mcpServers as Record) - : {}; - const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME]; - if ( - typeof currentConfig === 'object' && - currentConfig !== null && - JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig) - ) { - return true; - } - - const nextConfig: ClaudeUserConfig = { - ...config, - mcpServers: { - ...existingServers, - [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig, - }, - }; - - try { - writeClaudeUserConfig(claudeUserConfigPath, nextConfig); - if (process.env.CCS_DEBUG) { - console.error(info(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`)); - } - return true; - } catch (error) { - if (process.env.CCS_DEBUG) { - console.error(warn(`Failed to update ~/.claude.json: ${(error as Error).message}`)); - } - return false; - } - }); + throw error; + } } export function ensureImageAnalysisMcp(): boolean { diff --git a/tests/unit/utils/image-analysis/mcp-installer.test.ts b/tests/unit/utils/image-analysis/mcp-installer.test.ts index cae4076a..cc82e884 100644 --- a/tests/unit/utils/image-analysis/mcp-installer.test.ts +++ b/tests/unit/utils/image-analysis/mcp-installer.test.ts @@ -184,10 +184,33 @@ describe('ensureImageAnalysisMcp', () => { it('serializes ~/.claude.json updates with a file lock', () => { setupTempHome(); writeEnabledConfig(); + const claudeUserConfigPath = path.join(tempHome as string, '.claude.json'); + fs.writeFileSync(claudeUserConfigPath, '{}\n', 'utf8'); const lockSpy = spyOn(lockfile, 'lockSync'); expect(ensureImageAnalysisMcp()).toBe(true); expect(lockSpy).toHaveBeenCalled(); + expect(lockSpy.mock.calls[0]?.[0]).toBe(tempHome as string); + }); + + it('returns false instead of throwing when ~/.claude.json is already locked', () => { + setupTempHome(); + writeEnabledConfig(); + + const claudeUserConfigPath = path.join(tempHome as string, '.claude.json'); + fs.writeFileSync(claudeUserConfigPath, '{}\n', 'utf8'); + + const release = lockfile.lockSync(tempHome as string, { stale: 10000 }) as () => void; + + try { + let result: boolean | undefined; + expect(() => { + result = ensureImageAnalysisMcp(); + }).not.toThrow(); + expect(result).toBe(false); + } finally { + release(); + } }); }); From 0347f5a973577c0888d9013babbc1fa98304c459 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 2 Apr 2026 18:56:13 -0400 Subject: [PATCH 12/48] fix(image-analysis): stabilize mcp config locking --- src/utils/image-analysis/mcp-installer.ts | 11 ++++++++--- tests/unit/utils/image-analysis/mcp-installer.test.ts | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/utils/image-analysis/mcp-installer.ts b/src/utils/image-analysis/mcp-installer.ts index 5dc5ed5e..76b8c3d2 100644 --- a/src/utils/image-analysis/mcp-installer.ts +++ b/src/utils/image-analysis/mcp-installer.ts @@ -121,11 +121,16 @@ function writeClaudeUserConfig(configPath: string, config: ClaudeUserConfig): bo } function withClaudeUserConfigLock(configPath: string, callback: () => T): T { - const lockTarget = path.dirname(configPath); + const configDir = path.dirname(configPath); + const lockTarget = path.join(configDir, `${path.basename(configPath)}.ccs-lock`); let release: (() => void) | undefined; - if (!fs.existsSync(path.dirname(configPath))) { - fs.mkdirSync(path.dirname(configPath), { recursive: true, mode: 0o700 }); + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true, mode: 0o700 }); + } + + if (!fs.existsSync(lockTarget)) { + fs.writeFileSync(lockTarget, '', { encoding: 'utf8', mode: 0o600 }); } try { diff --git a/tests/unit/utils/image-analysis/mcp-installer.test.ts b/tests/unit/utils/image-analysis/mcp-installer.test.ts index cc82e884..40b617f5 100644 --- a/tests/unit/utils/image-analysis/mcp-installer.test.ts +++ b/tests/unit/utils/image-analysis/mcp-installer.test.ts @@ -191,7 +191,7 @@ describe('ensureImageAnalysisMcp', () => { expect(ensureImageAnalysisMcp()).toBe(true); expect(lockSpy).toHaveBeenCalled(); - expect(lockSpy.mock.calls[0]?.[0]).toBe(tempHome as string); + expect(lockSpy.mock.calls[0]?.[0]).toBe(path.join(tempHome as string, '.claude.json.ccs-lock')); }); it('returns false instead of throwing when ~/.claude.json is already locked', () => { @@ -200,8 +200,11 @@ describe('ensureImageAnalysisMcp', () => { const claudeUserConfigPath = path.join(tempHome as string, '.claude.json'); fs.writeFileSync(claudeUserConfigPath, '{}\n', 'utf8'); + fs.writeFileSync(path.join(tempHome as string, '.claude.json.ccs-lock'), '', 'utf8'); - const release = lockfile.lockSync(tempHome as string, { stale: 10000 }) as () => void; + const release = lockfile.lockSync(path.join(tempHome as string, '.claude.json.ccs-lock'), { + stale: 10000, + }) as () => void; try { let result: boolean | undefined; From c46726fd49d73a30701329b29e32ed37f51e5be4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 2 Apr 2026 23:09:14 +0000 Subject: [PATCH 13/48] chore(release): 7.65.3-dev.1 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8383c3cf..4ff2a3f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.65.3", + "version": "7.65.3-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", From 748fde659982450b86b2fbd7b1eff998830234a2 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 00:40:43 -0400 Subject: [PATCH 14/48] fix(ci): restore cursor image analyzer hook import --- src/ccs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ccs.ts b/src/ccs.ts index 017b051d..790f652e 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -41,6 +41,7 @@ import { ensureProfileHooks as ensureImageAnalyzerHooks } from './utils/hooks/im import { applyImageAnalysisRuntimeOverrides, getImageAnalysisHookEnv, + installImageAnalyzerHook, prepareImageAnalysisFallbackHook, resolveImageAnalysisRuntimeConnection, resolveImageAnalysisRuntimeStatus, From 3eb55d0e33fb3f2a3b293ab532a7d6892b1b57ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 04:47:13 +0000 Subject: [PATCH 15/48] chore(release): 7.65.3-dev.2 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ff2a3f3..39fe8b2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.65.3-dev.1", + "version": "7.65.3-dev.2", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", From 24ba2abe10a59e907978dfb7335554c58a5b12e4 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 00:35:27 -0400 Subject: [PATCH 16/48] feat(cli): refresh help and shell completion UX - add a shared command catalog for root help and completion routing - replace shell-local completion logic with a hidden __complete backend - add topic help and parity tests for router, help, and completion coverage --- scripts/completion/README.md | 188 ++-- scripts/completion/ccs.bash | 224 +---- scripts/completion/ccs.fish | 271 +----- scripts/completion/ccs.ps1 | 474 +--------- scripts/completion/ccs.zsh | 330 +------ src/ccs.ts | 14 +- src/commands/command-catalog.ts | 330 +++++++ src/commands/completion-backend.ts | 260 ++++++ src/commands/help-command.ts | 858 ++++-------------- src/commands/root-command-router.ts | 15 +- src/commands/shell-completion-command.ts | 37 +- tests/unit/commands/command-catalog.test.ts | 27 + .../unit/commands/completion-backend.test.ts | 132 +++ .../unit/commands/help-command-parity.test.ts | 169 +--- .../unit/commands/root-command-router.test.ts | 16 +- 15 files changed, 1227 insertions(+), 2118 deletions(-) create mode 100644 src/commands/command-catalog.ts create mode 100644 src/commands/completion-backend.ts create mode 100644 tests/unit/commands/command-catalog.test.ts create mode 100644 tests/unit/commands/completion-backend.test.ts diff --git a/scripts/completion/README.md b/scripts/completion/README.md index d70103e0..037ebe83 100644 --- a/scripts/completion/README.md +++ b/scripts/completion/README.md @@ -2,14 +2,16 @@ Tab completion for CCS commands, subcommands, profiles, and flags. +The completion scripts are thin adapters over the hidden `ccs __complete` backend so +all supported shells stay aligned with the same command graph. + **Supported Shells:** Bash, Zsh, Fish, PowerShell ## Features - Complete profile names (both settings-based and account-based) -- Complete `ccs auth` subcommands (create, list, show, remove, default) -- Complete flags (`--help`, `--version`, `--json`, `--verbose`, `--yes`) -- Complete profile names for auth subcommands +- Complete root commands, help topics, provider shortcuts, and command flags +- Complete `ccs auth` and `ccs api` lifecycle subcommands - Context-aware: suggests relevant options based on current command ## Quick Install (Recommended) @@ -32,6 +34,12 @@ ccs --shell-completion --fish # Force fish ccs --shell-completion --powershell # Force PowerShell ``` +**Help and verification:** +```bash +ccs help completion +ccs --shell-completion --force +``` + ## Manual Installation Completion files are installed to `~/.ccs/completions/` during `npm install`. @@ -116,188 +124,104 @@ sudo cp scripts/completion/ccs.fish /usr/share/fish/vendor_completions.d/ ```bash $ ccs -auth doctor glm kimi work personal --help --version +auth api cliproxy config doctor docker help -$ ccs auth -create list show remove default --help +$ ccs help +profiles providers completion targets ``` -### Profile Completion +### Context Completion ```bash $ ccs auth show work personal team --json -$ ccs auth remove -work personal team --yes -y +$ ccs api +create discover copy export import remove ``` -### Flag Completion +### Backend Contract ```bash -$ ccs auth list ---verbose --json - -$ ccs auth show work ---json +$ ccs __complete --shell bash --current do +doctor +docker ``` -## Completion Behavior - -### Top-level (after `ccs`) -- Built-in commands: `auth`, `doctor` -- Flags: `--help`, `--version`, `-h`, `-v` -- Settings-based profiles: from `~/.ccs/config.json` -- Account-based profiles: from `~/.ccs/profiles.json` - -### After `ccs auth` -- Subcommands: `create`, `list`, `show`, `remove`, `default` -- Flags: `--help`, `-h` - -### After `ccs auth ` -- **create**: No completion (user enters new profile name) - - Flags: `--force` -- **list**: No profile completion - - Flags: `--verbose`, `--json` -- **show**: Account profiles only - - Flags: `--json` -- **remove**: Account profiles only - - Flags: `--yes`, `-y` -- **default**: Account profiles only - -### After `ccs ` -- No completion (Claude CLI arguments are free-form) +Shell adapters now call the shared CCS completion backend instead of maintaining their own +copy of the command graph. That means: +- top-level commands, help topics, and provider shortcuts come from CCS itself +- dynamic profiles and CLIProxy variants resolve through the real config loaders +- bash, zsh, fish, and PowerShell stay aligned with the same completion logic ## Troubleshooting -### Bash: Completion not working +### Bash -1. Check if bash-completion is installed: - ```bash - # macOS - brew install bash-completion - - # Ubuntu/Debian - sudo apt install bash-completion - ``` - -2. Verify jq is installed (required for profile completion): - ```bash - command -v jq - ``` - -3. Check if completion is loaded: +1. Check if completion is loaded: ```bash complete -p ccs ``` - - Should output: - ``` - complete -F _ccs_completion ccs +2. Verify the backend directly: + ```bash + ccs __complete --shell bash --current "" -- help ``` -### Zsh: Completion not working +### Zsh -1. Verify completion system is enabled in `~/.zshrc`: +1. Verify completion system is enabled: ```zsh autoload -Uz compinit && compinit ``` - -2. Check if completion is loaded: - ```zsh - which _ccs - ``` - -3. Rebuild completion cache: +2. Rebuild the cache if needed: ```zsh rm ~/.zcompdump && compinit ``` - -### PowerShell: Completion not working - -1. Check PowerShell version (5.1+ required): - ```powershell - $PSVersionTable.PSVersion +3. Verify the backend directly: + ```zsh + ccs __complete --shell zsh --current "" -- help ``` -2. Verify profile is loaded: +### PowerShell + +1. Check that the profile exists: ```powershell Test-Path $PROFILE ``` - -3. Check if completion is registered: +2. Verify the backend directly: ```powershell - (Get-ArgumentCompleter).CommandName | Select-String ccs + ccs __complete --shell powershell --current "" -- help ``` -### Fish: Completion not working +### Fish -1. Check Fish version (3.0+ required): - ```fish - fish --version - ``` - -2. Verify completion file is in the right location: +1. Verify completion file location: ```fish ls ~/.config/fish/completions/ccs.fish ``` - -3. Verify jq is installed (required for profile completion): - ```fish - which jq - ``` - -4. Test completion manually: +2. Test completion manually: ```fish complete -C'ccs ' ``` - -5. If needed, rebuild completions: +3. Verify the backend directly: ```fish - fish_update_completions + ccs __complete --shell fish --current "" -- help ``` ## Technical Details -### Bash Implementation -- Uses `complete -F` for programmable completion -- Compatible with bash 3.2+ (macOS default) -- Reads profiles dynamically using `jq` -- Context-aware based on `COMP_CWORD` and `COMP_WORDS` - -### Zsh Implementation -- Uses `_arguments` and `_describe` for rich completion -- Compatible with zsh 5.0+ -- Supports completion descriptions -- Context-aware using `$state` and `$words` - -### PowerShell Implementation -- Uses `Register-ArgumentCompleter` -- Compatible with PowerShell 5.1+ -- Reads profiles dynamically using `ConvertFrom-Json` -- Provides `CompletionResult` objects - -### Fish Implementation -- Uses declarative `complete` command -- Compatible with Fish 3.0+ -- Automatic loading from `~/.config/fish/completions/` -- Helper functions for dynamic profile loading -- Context-aware using `__fish_seen_subcommand_from` -- No manual sourcing required - -## Dependencies - -- **jq**: Required for reading profiles from JSON files - - Install: `brew install jq` (macOS) or `apt install jq` (Ubuntu) - - Already required by CCS core functionality +- Bash uses `complete -F` +- Zsh uses a custom `_ccs` completion function +- Fish uses `complete -a` with backend command substitution +- PowerShell uses `Register-ArgumentCompleter` +- All four shells now delegate suggestion logic to `ccs __complete` ## Contributing -When adding new commands or flags: -1. Update all four completion scripts (bash, zsh, fish, PowerShell) -2. Test on each shell -3. Update this README with new completion examples -4. Maintain cross-shell parity +When adding or changing command surfaces: +1. Update the shared TypeScript command/completion catalog +2. Run `bun run validate` +3. Smoke-check at least one installed shell adapter plus the backend directly ## See Also diff --git a/scripts/completion/ccs.bash b/scripts/completion/ccs.bash index 985e8806..f7d19e10 100644 --- a/scripts/completion/ccs.bash +++ b/scripts/completion/ccs.bash @@ -1,210 +1,42 @@ # Bash completion for CCS (Claude Code Switch) # Compatible with bash 3.2+ -# -# Installation: -# Add to ~/.bashrc or ~/.bash_profile: -# source /path/to/ccs/scripts/completion/ccs.bash -# -# Or install system-wide (requires sudo): -# sudo cp scripts/completion/ccs.bash /etc/bash_completion.d/ccs _ccs_completion() { - local cur prev words cword + local cur COMPREPLY=() - - # Get current word and previous word cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - # Top-level completion (first argument) - if [[ ${COMP_CWORD} -eq 1 ]]; then - local commands="auth api cliproxy config doctor env persist sync update" - local flags="--help --version --shell-completion -h -v -sc" - local cliproxy_profiles="gemini codex agy qwen" - local profiles="" - - # Add profiles from config.json (settings-based profiles) - if [[ -f ~/.ccs/config.json ]]; then - profiles="$profiles $(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)" - fi - - # Add profiles from profiles.json (account-based profiles) - if [[ -f ~/.ccs/profiles.json ]]; then - profiles="$profiles $(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true)" - fi - - # Add cliproxy variants from config.json - if [[ -f ~/.ccs/config.json ]]; then - profiles="$profiles $(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null || true)" - fi - - # Combine all options - local opts="$commands $flags $cliproxy_profiles $profiles" - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 + local tokens_before_current=() + if [[ ${COMP_CWORD} -gt 1 ]]; then + tokens_before_current=("${COMP_WORDS[@]:1:COMP_CWORD-1}") fi - # CLIProxy provider flags (gemini, codex, agy, qwen) - if [[ ${COMP_WORDS[1]} =~ ^(gemini|codex|agy|qwen)$ ]]; then - local provider_flags="--auth --config --logout --headless --help -h" - COMPREPLY=( $(compgen -W "${provider_flags}" -- ${cur}) ) - return 0 - fi - - # auth subcommands - if [[ ${prev} == "auth" ]]; then - local auth_commands="create list show remove default --help -h" - COMPREPLY=( $(compgen -W "${auth_commands}" -- ${cur}) ) - return 0 - fi - - # api subcommands - if [[ ${prev} == "api" ]]; then - local api_commands="create list remove --help -h" - COMPREPLY=( $(compgen -W "${api_commands}" -- ${cur}) ) - return 0 - fi - - # cliproxy subcommands - if [[ ${prev} == "cliproxy" ]]; then - local cliproxy_commands="create list remove --install --latest --help -h" - COMPREPLY=( $(compgen -W "${cliproxy_commands}" -- ${cur}) ) - return 0 - fi - - # Completion for cliproxy subcommands - if [[ ${COMP_WORDS[1]} == "cliproxy" ]]; then - case "${prev}" in - remove|delete|rm) - # Complete with cliproxy variant names - if [[ -f ~/.ccs/config.json ]]; then - local variants=$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null || true) - COMPREPLY=( $(compgen -W "${variants}" -- ${cur}) ) - fi - return 0 - ;; - create) - # Complete with create flags - COMPREPLY=( $(compgen -W "--provider --model --force --yes -y" -- ${cur}) ) - return 0 - ;; - --provider) - # Complete with provider names - COMPREPLY=( $(compgen -W "gemini codex agy qwen" -- ${cur}) ) - return 0 - ;; - list|ls) - # No flags for list - return 0 - ;; - --install) - # User enters version number - return 0 - ;; - esac - fi - - # Completion for api subcommands - if [[ ${COMP_WORDS[1]} == "api" ]]; then - case "${prev}" in - remove|delete|rm) - # Complete with settings profile names - if [[ -f ~/.ccs/config.json ]]; then - local profiles=$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true) - COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) ) - fi - return 0 - ;; - create) - # Complete with create flags - COMPREPLY=( $(compgen -W "--base-url --api-key --model --force --yes -y" -- ${cur}) ) - return 0 - ;; - list) - # No flags for list - return 0 - ;; - esac - fi - - # Completion for auth subcommands that need profile names - if [[ ${COMP_WORDS[1]} == "auth" ]]; then - case "${prev}" in - show|remove|default) - # Complete with account profile names only - if [[ -f ~/.ccs/profiles.json ]]; then - local profiles=$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true) - COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) ) - fi - return 0 - ;; - create) - # Complete with create flags - COMPREPLY=( $(compgen -W "--force" -- ${cur}) ) - return 0 - ;; - list) - # Complete with list flags - COMPREPLY=( $(compgen -W "--verbose --json" -- ${cur}) ) - return 0 - ;; - esac - fi - - # env subcommands - if [[ ${COMP_WORDS[1]} == "env" ]]; then - case "${prev}" in - env) - # Complete with profile names and flags (inline profiles since $cliproxy_profiles is out of scope) - local env_opts="--format --shell --ide --help -h gemini codex agy qwen iflow kiro ghcp claude default" - if [[ -f ~/.ccs/config.json ]]; then - env_opts="$env_opts $(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null || true)" - fi - if [[ -f ~/.ccs/profiles.json ]]; then - env_opts="$env_opts $(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null || true)" - fi - COMPREPLY=( $(compgen -W "${env_opts}" -- ${cur}) ) - return 0 - ;; - --format) - COMPREPLY=( $(compgen -W "openai anthropic raw claude-extension" -- ${cur}) ) - return 0 - ;; - --shell) - COMPREPLY=( $(compgen -W "auto bash zsh fish powershell" -- ${cur}) ) - return 0 - ;; - --ide) - COMPREPLY=( $(compgen -W "vscode cursor windsurf" -- ${cur}) ) - return 0 - ;; - *) - COMPREPLY=( $(compgen -W "--format --shell --ide --help -h" -- ${cur}) ) - return 0 - ;; - esac - fi - - # Flags for doctor command - if [[ ${COMP_WORDS[1]} == "doctor" ]]; then - COMPREPLY=( $(compgen -W "--help -h" -- ${cur}) ) - return 0 - fi - - # Flags for update command - if [[ ${COMP_WORDS[1]} == "update" ]]; then - COMPREPLY=( $(compgen -W "--force --beta --dev --help -h" -- ${cur}) ) - return 0 - fi - - # Flags for shell-completion command - if [[ ${prev} == "--shell-completion" || ${prev} == "-sc" ]]; then - COMPREPLY=( $(compgen -W "--bash --zsh --fish --powershell" -- ${cur}) ) - return 0 - fi + while IFS= read -r line; do + [[ -n "${line}" ]] && COMPREPLY+=("${line}") + done < <(__ccs_completion_run "${cur}" "${tokens_before_current[@]}") return 0 } -# Register completion function +__ccs_completion_run() { + local current="$1" + shift || true + + local script_dir repo_root repo_cli + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + repo_root="$(cd "${script_dir}/../.." && pwd)" + repo_cli="${repo_root}/dist/ccs.js" + if [[ ! -f "${repo_cli}" ]]; then + repo_cli="${repo_root}/bin/ccs.js" + fi + if [[ -f "${repo_cli}" ]]; then + node "${repo_cli}" __complete --shell bash --current "${current}" -- "$@" 2>/dev/null + return 0 + fi + + if command -v ccs >/dev/null 2>&1; then + ccs __complete --shell bash --current "${current}" -- "$@" 2>/dev/null + fi +} + complete -F _ccs_completion ccs diff --git a/scripts/completion/ccs.fish b/scripts/completion/ccs.fish index 107607b9..e033b08a 100644 --- a/scripts/completion/ccs.fish +++ b/scripts/completion/ccs.fish @@ -1,256 +1,27 @@ # Fish completion for CCS (Claude Code Switch) -# Compatible with fish 3.0+ -# -# Features: -# - Categorized completions with [cmd], [proxy], [model], and [account] prefixes -# - Dynamic profile loading from config.json and profiles.json -# - Context-aware subcommand completion -# -# Installation: -# Copy to ~/.config/fish/completions/: -# mkdir -p ~/.config/fish/completions -# cp scripts/completion/ccs.fish ~/.config/fish/completions/ -# -# Fish will automatically load completions from this directory. -# No need to source or reload - completions are loaded on demand. -# Helper function to get settings profiles -function __fish_ccs_get_settings_profiles - set -l config_path ~/.ccs/config.json +function __fish_ccs_complete + set -l tokens_before_current (commandline -opc) + if test (count $tokens_before_current) -gt 0 + set -e tokens_before_current[1] + end - if test -f $config_path - jq -r '.profiles | keys[]' $config_path 2>/dev/null + set -l current (commandline -ct) + + set -l script_file (status filename) + set -l repo_root (realpath (dirname $script_file)/../.. 2>/dev/null) + set -l repo_cli "$repo_root/dist/ccs.js" + if not test -f "$repo_cli" + set repo_cli "$repo_root/bin/ccs.js" + end + if test -f "$repo_cli" + node "$repo_cli" __complete --shell fish --current "$current" -- $tokens_before_current 2>/dev/null + return + end + + if command -sq ccs + ccs __complete --shell fish --current "$current" -- $tokens_before_current 2>/dev/null end end -# Helper function to get custom/unknown settings profiles -function __fish_ccs_get_custom_settings_profiles - set -l config_path ~/.ccs/config.json - set -l known_profiles default glm kimi - - if test -f $config_path - set -l all_profiles (jq -r '.profiles | keys[]' $config_path 2>/dev/null) - - for profile in $all_profiles - if not contains $profile $known_profiles - echo $profile - end - end - end -end - -# Helper function to get cliproxy variants -function __fish_ccs_get_cliproxy_variants - set -l config_path ~/.ccs/config.json - - if test -f $config_path - jq -r '.cliproxy | keys[]' $config_path 2>/dev/null - end -end - -# Helper function to get profiles with all types -function __fish_ccs_get_profiles - __fish_ccs_get_settings_profiles - __fish_ccs_get_account_profiles -end - -# Helper function to get account profiles only -function __fish_ccs_get_account_profiles - set -l profiles_path ~/.ccs/profiles.json - - if test -f $profiles_path - jq -r '.profiles | keys[]' $profiles_path 2>/dev/null - end -end - -# Helper function to check if we're in auth context -function __fish_ccs_using_auth - __fish_seen_subcommand_from auth -end - -# Helper function to check specific auth subcommand -function __fish_ccs_using_auth_subcommand - set -l subcommand $argv[1] - __fish_ccs_using_auth; and __fish_seen_subcommand_from $subcommand -end - -# Helper function to check if we're in api context -function __fish_ccs_using_api - __fish_seen_subcommand_from api -end - -# Helper function to check specific api subcommand -function __fish_ccs_using_api_subcommand - set -l subcommand $argv[1] - __fish_ccs_using_api; and __fish_seen_subcommand_from $subcommand -end - -# Helper function to check if we're in cliproxy context -function __fish_ccs_using_cliproxy - __fish_seen_subcommand_from cliproxy -end - -# Helper function to check specific cliproxy subcommand -function __fish_ccs_using_cliproxy_subcommand - set -l subcommand $argv[1] - __fish_ccs_using_cliproxy; and __fish_seen_subcommand_from $subcommand -end - -# Helper function to check if we're in profile context -function __fish_ccs_using_profile - __fish_seen_subcommand_from profile -end - -# Helper function to check specific profile subcommand -function __fish_ccs_using_profile_subcommand - set -l subcommand $argv[1] - __fish_ccs_using_profile; and __fish_seen_subcommand_from $subcommand -end - -# Helper function to check if we're using a CLIProxy provider -function __fish_ccs_using_provider - __fish_seen_subcommand_from gemini codex agy qwen -end - -# Disable file completion for ccs -complete -c ccs -f - -# Top-level flags -complete -c ccs -s h -l help -d 'Show help message' -complete -c ccs -s v -l version -d 'Show version information' -complete -c ccs -s sc -l shell-completion -d 'Install shell completion' - -# Commands - grouped with [cmd] prefix for visual distinction -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'auth' -d '[cmd] Manage multiple Claude accounts' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'api' -d '[cmd] Manage API profiles (create/remove)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'cliproxy' -d '[cmd] Manage CLIProxy variants and binary' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'doctor' -d '[cmd] Run health check and diagnostics' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'env' -d '[cmd] Export env vars for third-party tools' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'sync' -d '[cmd] Sync delegation commands and skills' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'update' -d '[cmd] Update CCS to latest version' - -# CLIProxy profiles - grouped with [proxy] prefix for OAuth providers -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'gemini' -d '[proxy] Google Gemini (OAuth)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'codex' -d '[proxy] OpenAI Codex (OAuth)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'agy' -d '[proxy] Antigravity (OAuth)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'qwen' -d '[proxy] Qwen Code (OAuth)' - -# Model profiles - grouped with [model] prefix for visual distinction -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'default' -d '[model] Default Claude Sonnet 4.5' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'glm' -d '[model] GLM-4.6 (cost-optimized)' -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a 'kimi' -d '[model] Kimi for Coding (long-context)' - -# Custom model profiles - dynamic with [model] prefix -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_custom_settings_profiles)' -d '[model] Settings-based profile' - -# CLIProxy variants - dynamic with [variant] prefix -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_cliproxy_variants)' -d '[variant] CLIProxy variant' - -# Account profiles - dynamic with [account] prefix -complete -c ccs -n 'not __fish_seen_subcommand_from auth api cliproxy doctor env sync update gemini codex agy qwen' -a '(__fish_ccs_get_account_profiles)' -d '[account] Account-based profile' - -# shell-completion subflags -complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l bash -d 'Install for bash' -complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l zsh -d 'Install for zsh' -complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l fish -d 'Install for fish' -complete -c ccs -n '__fish_seen_argument -l shell-completion; or __fish_seen_argument -s sc' -l powershell -d 'Install for PowerShell' - -# CLIProxy provider flags (gemini, codex, agy, qwen) -complete -c ccs -n '__fish_ccs_using_provider' -l auth -d 'Authenticate only' -complete -c ccs -n '__fish_ccs_using_provider' -l config -d 'Change model configuration' -complete -c ccs -n '__fish_ccs_using_provider' -l logout -d 'Clear authentication' -complete -c ccs -n '__fish_ccs_using_provider' -l headless -d 'Headless auth (for SSH)' -complete -c ccs -n '__fish_ccs_using_provider' -s h -l help -d 'Show help' - -# update command flags -complete -c ccs -n '__fish_seen_subcommand_from update' -l force -d 'Force reinstall current version' -complete -c ccs -n '__fish_seen_subcommand_from update' -l beta -d 'Install from dev channel' -complete -c ccs -n '__fish_seen_subcommand_from update' -l dev -d 'Install from dev channel' -complete -c ccs -n '__fish_seen_subcommand_from update' -s h -l help -d 'Show help' - -# doctor command flags -complete -c ccs -n '__fish_seen_subcommand_from doctor' -s h -l help -d 'Show help for doctor command' - -# env command completions -complete -c ccs -n '__fish_seen_subcommand_from env; and not __fish_seen_argument -l format -l shell' -a 'gemini codex agy qwen iflow kiro ghcp claude' -d '[proxy] CLIProxy profile' -complete -c ccs -n '__fish_seen_subcommand_from env' -l format -d 'Output format' -complete -c ccs -n '__fish_seen_subcommand_from env; and __fish_seen_argument -l format' -a 'openai anthropic raw' -d 'Format' -complete -c ccs -n '__fish_seen_subcommand_from env' -l shell -d 'Shell syntax' -complete -c ccs -n '__fish_seen_subcommand_from env; and __fish_seen_argument -l shell' -a 'auto bash zsh fish powershell' -d 'Shell' -complete -c ccs -n '__fish_seen_subcommand_from env' -s h -l help -d 'Show help for env command' - -# ============================================================================ -# auth subcommands -# ============================================================================ -complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'create' -d 'Create new profile and login' -complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'list' -d 'List all saved profiles' -complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'show' -d 'Show profile details' -complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'remove' -d 'Remove saved profile' -complete -c ccs -n '__fish_ccs_using_auth; and not __fish_seen_subcommand_from create list show remove default' -a 'default' -d 'Set default profile' - -complete -c ccs -n '__fish_ccs_using_auth' -s h -l help -d 'Show help for auth commands' -complete -c ccs -n '__fish_ccs_using_auth_subcommand create' -l force -d 'Allow overwriting existing profile' -complete -c ccs -n '__fish_ccs_using_auth_subcommand list' -l verbose -d 'Show additional details' -complete -c ccs -n '__fish_ccs_using_auth_subcommand list' -l json -d 'Output in JSON format' -complete -c ccs -n '__fish_ccs_using_auth_subcommand show' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile' -complete -c ccs -n '__fish_ccs_using_auth_subcommand show' -l json -d 'Output in JSON format' -complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile' -complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -l yes -d 'Skip confirmation prompts' -complete -c ccs -n '__fish_ccs_using_auth_subcommand remove' -s y -d 'Skip confirmation prompts' -complete -c ccs -n '__fish_ccs_using_auth_subcommand default' -a '(__fish_ccs_get_account_profiles)' -d 'Account profile' - -# ============================================================================ -# api subcommands -# ============================================================================ -complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new API profile' -complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all API profiles' -complete -c ccs -n '__fish_ccs_using_api; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove an API profile' - -complete -c ccs -n '__fish_ccs_using_api' -s h -l help -d 'Show help for api commands' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l base-url -d 'API base URL' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l api-key -d 'API key' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l model -d 'Default model' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l force -d 'Overwrite existing profile' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -l yes -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_api_subcommand create' -s y -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -a '(__fish_ccs_get_settings_profiles)' -d 'Settings profile' -complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -l yes -d 'Skip confirmation' -complete -c ccs -n '__fish_ccs_using_api_subcommand remove' -s y -d 'Skip confirmation' - -# ============================================================================ -# cliproxy subcommands -# ============================================================================ -complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new CLIProxy variant' -complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all CLIProxy variants' -complete -c ccs -n '__fish_ccs_using_cliproxy; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove a CLIProxy variant' - -complete -c ccs -n '__fish_ccs_using_cliproxy' -s h -l help -d 'Show help for cliproxy commands' -complete -c ccs -n '__fish_ccs_using_cliproxy' -l install -d 'Install specific version' -complete -c ccs -n '__fish_ccs_using_cliproxy' -l latest -d 'Install latest version' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l provider -d 'Provider name' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create; and __fish_seen_argument -l provider' -a 'gemini codex agy qwen' -d 'Provider' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l model -d 'Model name' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l force -d 'Overwrite existing variant' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -l yes -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand create' -s y -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -a '(__fish_ccs_get_cliproxy_variants)' -d 'CLIProxy variant' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -l yes -d 'Skip confirmation' -complete -c ccs -n '__fish_ccs_using_cliproxy_subcommand remove' -s y -d 'Skip confirmation' - -# ============================================================================ -# profile subcommands (legacy - redirects to api) -# ============================================================================ -complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'create' -d 'Create new API profile' -complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'list' -d 'List all profiles' -complete -c ccs -n '__fish_ccs_using_profile; and not __fish_seen_subcommand_from create list remove' -a 'remove' -d 'Remove a profile' - -complete -c ccs -n '__fish_ccs_using_profile' -s h -l help -d 'Show help for profile commands' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l base-url -d 'API base URL' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l api-key -d 'API key' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l model -d 'Default model' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l force -d 'Overwrite existing profile' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -l yes -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_profile_subcommand create' -s y -d 'Skip prompts' -complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -a '(__fish_ccs_get_settings_profiles)' -d 'Settings profile' -complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -l yes -d 'Skip confirmation' -complete -c ccs -n '__fish_ccs_using_profile_subcommand remove' -s y -d 'Skip confirmation' +complete -c ccs -f -a "(__fish_ccs_complete)" diff --git a/scripts/completion/ccs.ps1 b/scripts/completion/ccs.ps1 index 500cf988..0ec00e94 100644 --- a/scripts/completion/ccs.ps1 +++ b/scripts/completion/ccs.ps1 @@ -1,446 +1,56 @@ # PowerShell completion for CCS (Claude Code Switch) -# Compatible with PowerShell 5.1+ -# -# Installation: -# Add to your PowerShell profile ($PROFILE): -# . /path/to/ccs/scripts/completion/ccs.ps1 -# -# Or install for current user: -# Copy-Item scripts/completion/ccs.ps1 ~\Documents\PowerShell\Scripts\ -# Add to profile: . ~\Documents\PowerShell\Scripts\ccs.ps1 + +function Invoke-CcsCompletionBackend { + param( + [string]$CurrentWord, + [string[]]$TokensBeforeCurrent + ) + + $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path + $repoCli = Join-Path $repoRoot 'dist\ccs.js' + if (-not (Test-Path $repoCli)) { + $repoCli = Join-Path $repoRoot 'bin\ccs.js' + } + if (Test-Path $repoCli) { + & node $repoCli __complete --shell powershell --current $CurrentWord -- @TokensBeforeCurrent 2>$null + return + } + + if (Get-Command ccs -ErrorAction SilentlyContinue) { + & ccs __complete --shell powershell --current $CurrentWord -- @TokensBeforeCurrent 2>$null + } +} Register-ArgumentCompleter -CommandName ccs -ScriptBlock { param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters) - $commands = @('auth', 'api', 'cliproxy', 'doctor', 'env', 'sync', 'update', '--help', '--version', '--shell-completion', '-h', '-v', '-sc') - $cliproxyProfiles = @('gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp', 'claude') - $authCommands = @('create', 'list', 'show', 'remove', 'default', '--help', '-h') - $apiCommands = @('create', 'list', 'remove', '--help', '-h') - $cliproxyCommands = @('create', 'list', 'remove', '--install', '--latest', '--help', '-h') - $apiCreateFlags = @('--base-url', '--api-key', '--model', '--force', '--yes', '-y') - $cliproxyCreateFlags = @('--provider', '--model', '--force', '--yes', '-y') - $providerFlags = @('--auth', '--config', '--logout', '--headless', '--help', '-h') - $updateFlags = @('--force', '--beta', '--dev', '--help', '-h') - $envFlags = @('--format', '--shell', '--help', '-h') - $envFormats = @('openai', 'anthropic', 'raw') - $envShells = @('auto', 'bash', 'zsh', 'fish', 'powershell') - $shellCompletionFlags = @('--bash', '--zsh', '--fish', '--powershell') - $listFlags = @('--verbose', '--json') - $removeFlags = @('--yes', '-y') - $showFlags = @('--json') - $providers = @('gemini', 'codex', 'agy', 'qwen') - - # Get current position in command - $words = $commandAst.ToString() -split '\s+' | Where-Object { $_ -ne '' } - $position = $words.Count - - # Helper function to get profiles - function Get-CcsProfiles { - param([string]$Type = 'all') - - $profiles = @() - - # Settings-based profiles - if ($Type -in @('all', 'settings')) { - $configPath = "$env:USERPROFILE\.ccs\config.json" - if (Test-Path $configPath) { - try { - $config = Get-Content $configPath -Raw | ConvertFrom-Json - $profiles += $config.profiles.PSObject.Properties.Name - } catch {} + $commandElements = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text }) + $tokensBeforeCurrent = @() + if ($commandElements.Count -gt 1) { + $tokensBeforeCurrent = $commandElements[1..($commandElements.Count - 1)] + if ($tokensBeforeCurrent.Count -gt 0 -and $tokensBeforeCurrent[-1] -eq $wordToComplete) { + $tokensBeforeCurrent = if ($tokensBeforeCurrent.Count -gt 1) { + $tokensBeforeCurrent[0..($tokensBeforeCurrent.Count - 2)] + } else { + @() } } - - # Account-based profiles - if ($Type -in @('all', 'account')) { - $profilesPath = "$env:USERPROFILE\.ccs\profiles.json" - if (Test-Path $profilesPath) { - try { - $data = Get-Content $profilesPath -Raw | ConvertFrom-Json - $profiles += $data.profiles.PSObject.Properties.Name - } catch {} - } - } - - # CLIProxy variants - if ($Type -in @('all', 'cliproxy')) { - $configPath = "$env:USERPROFILE\.ccs\config.json" - if (Test-Path $configPath) { - try { - $config = Get-Content $configPath -Raw | ConvertFrom-Json - if ($config.cliproxy) { - $profiles += $config.cliproxy.PSObject.Properties.Name - } - } catch {} - } - } - - return $profiles | Sort-Object -Unique } - # Top-level completion - if ($position -eq 2) { - $allOptions = $commands + $cliproxyProfiles + (Get-CcsProfiles) - $allOptions | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) + foreach ($line in Invoke-CcsCompletionBackend -CurrentWord $wordToComplete -TokensBeforeCurrent $tokensBeforeCurrent) { + if ([string]::IsNullOrWhiteSpace($line)) { + continue } - return - } - # shell-completion flag completion - if ($words[1] -eq '--shell-completion' -or $words[1] -eq '-sc') { - if ($position -eq 3) { - $shellCompletionFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - return - } + $parts = $line -split "`t", 2 + $value = $parts[0] + $description = if ($parts.Count -gt 1) { $parts[1] } else { $parts[0] } - # CLIProxy provider flags (gemini, codex, agy, qwen) - if ($words[1] -in $cliproxyProfiles) { - $providerFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - return - } - - # update command completion - if ($words[1] -eq 'update') { - $updateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - return - } - - # env command completion - if ($words[1] -eq 'env') { - if ($position -eq 3) { - $options = $cliproxyProfiles + (Get-CcsProfiles -Type settings) + $envFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } elseif ($position -ge 4) { - switch ($words[$position - 2]) { - '--format' { - $envFormats | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - '--shell' { - $envShells | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - default { - $envFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } - return - } - - # auth subcommand completion - if ($words[1] -eq 'auth') { - if ($position -eq 3) { - $authCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } elseif ($position -eq 4) { - switch ($words[2]) { - 'show' { - $options = (Get-CcsProfiles -Type account) + $showFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'remove' { - $options = (Get-CcsProfiles -Type account) + $removeFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'default' { - Get-CcsProfiles -Type account | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'list' { - $listFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'create' { - @('--force') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } elseif ($position -eq 5) { - switch ($words[2]) { - 'show' { - $showFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'remove' { - $removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } - return - } - - # api subcommand completion - if ($words[1] -eq 'api') { - if ($position -eq 3) { - $apiCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } elseif ($position -eq 4) { - switch ($words[2]) { - 'remove' { - $options = (Get-CcsProfiles -Type settings) + $removeFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'create' { - $apiCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } elseif ($position -eq 5) { - switch ($words[2]) { - 'remove' { - $removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } - return - } - - # cliproxy subcommand completion - if ($words[1] -eq 'cliproxy') { - if ($position -eq 3) { - $cliproxyCommands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } elseif ($position -eq 4) { - switch ($words[2]) { - 'remove' { - $options = (Get-CcsProfiles -Type cliproxy) + $removeFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'create' { - $cliproxyCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } elseif ($position -eq 5) { - switch ($words[2]) { - 'remove' { - $removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'create' { - # After --provider, complete with provider names - if ($words[3] -eq '--provider') { - $providers | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } - } - return - } - - # profile subcommand completion (legacy) - if ($words[1] -eq 'profile') { - if ($position -eq 3) { - @('create', 'list', 'remove', '--help', '-h') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } elseif ($position -eq 4) { - switch ($words[2]) { - 'remove' { - $options = (Get-CcsProfiles -Type settings) + $removeFlags - $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - 'create' { - $apiCreateFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } elseif ($position -eq 5) { - switch ($words[2]) { - 'remove' { - $removeFlags | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new( - $_, - $_, - 'ParameterValue', - $_ - ) - } - } - } - } - return + [System.Management.Automation.CompletionResult]::new( + $value, + $value, + 'ParameterValue', + $description + ) } } diff --git a/scripts/completion/ccs.zsh b/scripts/completion/ccs.zsh index 9c948b6a..97e59220 100644 --- a/scripts/completion/ccs.zsh +++ b/scripts/completion/ccs.zsh @@ -1,319 +1,41 @@ #compdef ccs # Zsh completion for CCS (Claude Code Switch) -# Compatible with zsh 5.0+ -# -# Installation: -# Add to ~/.zshrc: -# fpath=(~/.zsh/completion $fpath) -# autoload -Uz compinit && compinit -# source /path/to/ccs/scripts/completion/ccs.zsh -# -# Or install system-wide: -# sudo cp scripts/completion/ccs.zsh /usr/local/share/zsh/site-functions/_ccs - -# Set up completion styles for better formatting and colors -zstyle ':completion:*:*:ccs:*:commands' list-colors '=(#b)(auth|api|cliproxy|doctor|env|sync|update)([[:space:]]#--[[:space:]]#*)==0\;34=2\;37' -zstyle ':completion:*:*:ccs:*:proxy-profiles' list-colors '=(#b)(gemini|codex|agy|qwen)([[:space:]]#--[[:space:]]#*)==0\;35=2\;37' -zstyle ':completion:*:*:ccs:*:model-profiles' list-colors '=(#b)(default|glm|kimi|[^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;32=2\;37' -zstyle ':completion:*:*:ccs:*:account-profiles' list-colors '=(#b)([^[:space:]]##)([[:space:]]#--[[:space:]]#*)==0\;33=2\;37' -zstyle ':completion:*:*:ccs:*' group-name '' -zstyle ':completion:*:*:ccs:*:descriptions' format $'\n%B%F{yellow}── %d ──%f%b' -zstyle ':completion:*:*:ccs:*' list-separator ' -- ' -zstyle ':completion:*:*:ccs:*' list-rows-first true -zstyle ':completion:*:*:ccs:*' menu select _ccs() { - local -a commands proxy_profiles settings_profiles_described account_profiles_described cliproxy_variants_described - local curcontext="$curcontext" state line - typeset -A opt_args + local current + current="${words[CURRENT]}" - # Define top-level commands - commands=( - 'auth:Manage multiple Claude accounts' - 'api:Manage API profiles (create/remove)' - 'cliproxy:Manage CLIProxy variants and binary' - 'doctor:Run health check and diagnostics' - 'env:Export env vars for third-party tools' - 'sync:Sync delegation commands and skills' - 'update:Update CCS to latest version' - ) - - # Define CLIProxy hardcoded profiles (OAuth providers) - proxy_profiles=( - 'gemini:Google Gemini (OAuth)' - 'codex:OpenAI Codex (OAuth)' - 'agy:Antigravity (OAuth)' - 'qwen:Qwen Code (OAuth)' - 'iflow:iFlow (OAuth)' - 'kiro:Kiro (OAuth)' - 'ghcp:GitHub Copilot (OAuth)' - 'claude:Claude Direct (OAuth)' - ) - - # Define known settings profiles with descriptions - local -A profile_descriptions - profile_descriptions=( - 'default' 'Default Claude Sonnet 4.5' - 'glm' 'GLM-4.6 (cost-optimized)' - 'kimi' 'Kimi for Coding (long-context)' - ) - - # Load settings-based profiles from config.json - if [[ -f ~/.ccs/config.json ]]; then - local -a raw_settings_profiles - raw_settings_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null)"}) - - for profile in $raw_settings_profiles; do - local desc="${profile_descriptions[$profile]:-Settings-based profile}" - settings_profiles_described+=("${profile}:${desc}") - done + local -a tokens_before_current + if (( CURRENT > 2 )); then + tokens_before_current=("${words[@]:2:$((CURRENT-2))}") + else + tokens_before_current=() fi - # Load account-based profiles from profiles.json - if [[ -f ~/.ccs/profiles.json ]]; then - local -a raw_account_profiles - raw_account_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null)"}) - - for profile in $raw_account_profiles; do - account_profiles_described+=("${profile}:Account-based profile") - done - fi - - # Load cliproxy variants from config.json - if [[ -f ~/.ccs/config.json ]]; then - local -a raw_cliproxy_variants - raw_cliproxy_variants=(${(f)"$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null)"}) - - for variant in $raw_cliproxy_variants; do - cliproxy_variants_described+=("${variant}:CLIProxy variant") - done - fi - - _arguments -C \ - '(- *)'{-h,--help}'[Show help message]' \ - '(- *)'{-v,--version}'[Show version information]' \ - '(- *)'{-sc,--shell-completion}'[Install shell completion]' \ - '1: :->command' \ - '*:: :->args' - - case $state in - command) - _describe -t commands 'commands' commands - _describe -t proxy-profiles 'CLIProxy profiles' proxy_profiles - _describe -t model-profiles 'model profiles' settings_profiles_described - _describe -t account-profiles 'account profiles' account_profiles_described - _describe -t cliproxy-variants 'CLIProxy variants' cliproxy_variants_described - ;; - - args) - case $words[1] in - auth) - _ccs_auth - ;; - api) - _ccs_api - ;; - cliproxy) - _ccs_cliproxy - ;; - update) - _arguments \ - '--force[Force reinstall current version]' \ - '--beta[Install from dev channel]' \ - '--dev[Install from dev channel]' \ - '(- *)'{-h,--help}'[Show help]' - ;; - doctor) - _arguments \ - '(- *)'{-h,--help}'[Show help for doctor command]' - ;; - env) - _arguments \ - '--format[Output format]:format:(openai anthropic raw)' \ - '--shell[Shell syntax]:shell:(auto bash zsh fish powershell)' \ - '(- *)'{-h,--help}'[Show help]' \ - '1:profile:($proxy_profiles ${(k)settings_profiles_described})' - ;; - gemini|codex|agy|qwen) - _arguments \ - '--auth[Authenticate only]' \ - '--config[Change model configuration]' \ - '--logout[Clear authentication]' \ - '--headless[Headless auth (for SSH)]' \ - '(- *)'{-h,--help}'[Show help]' - ;; - --shell-completion|-sc) - _arguments \ - '--bash[Install for bash]' \ - '--zsh[Install for zsh]' \ - '--fish[Install for fish]' \ - '--powershell[Install for PowerShell]' - ;; - *) - _message 'Claude CLI arguments' - ;; - esac - ;; - esac + local -a suggestions + suggestions=("${(@f)$(__ccs_completion_run "${current}" "${tokens_before_current[@]}")}") + compadd -- "${suggestions[@]}" } -_ccs_api() { - local curcontext="$curcontext" state line - typeset -A opt_args +__ccs_completion_run() { + local current="$1" + shift || true - local -a api_commands settings_profiles - - api_commands=( - 'create:Create new API profile (interactive)' - 'list:List all API profiles' - 'remove:Remove an API profile' - ) - - if [[ -f ~/.ccs/config.json ]]; then - settings_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/config.json 2>/dev/null)"}) + local script_path script_dir repo_root repo_cli + script_path="${(%):-%N}" + script_dir="${script_path:A:h}" + repo_root="${script_dir:h:h}" + repo_cli="${repo_root}/dist/ccs.js" + if [[ ! -f "${repo_cli}" ]]; then + repo_cli="${repo_root}/bin/ccs.js" + fi + if [[ -f "${repo_cli}" ]]; then + node "${repo_cli}" __complete --shell zsh --current "${current}" -- "$@" 2>/dev/null + return 0 fi - _arguments -C \ - '(- *)'{-h,--help}'[Show help for api commands]' \ - '1: :->subcommand' \ - '*:: :->subargs' - - case $state in - subcommand) - _describe -t api-commands 'api commands' api_commands - ;; - - subargs) - case $words[1] in - create) - _arguments \ - '1:profile name:' \ - '--base-url[API base URL]:url:' \ - '--api-key[API key]:key:' \ - '--model[Default model]:model:' \ - '--force[Overwrite existing profile]' \ - {--yes,-y}'[Skip prompts]' - ;; - list) - ;; - remove|delete|rm) - _arguments \ - '1:profile:($settings_profiles)' \ - {--yes,-y}'[Skip confirmation]' - ;; - esac - ;; - esac -} - -_ccs_cliproxy() { - local curcontext="$curcontext" state line - typeset -A opt_args - - local -a cliproxy_commands cliproxy_variants providers - - cliproxy_commands=( - 'create:Create new CLIProxy variant profile' - 'list:List all CLIProxy variant profiles' - 'remove:Remove a CLIProxy variant profile' - ) - - providers=(gemini codex agy qwen) - - if [[ -f ~/.ccs/config.json ]]; then - cliproxy_variants=(${(f)"$(jq -r '.cliproxy | keys[]' ~/.ccs/config.json 2>/dev/null)"}) + if (( $+commands[ccs] )); then + ccs __complete --shell zsh --current "${current}" -- "$@" 2>/dev/null fi - - _arguments -C \ - '(- *)'{-h,--help}'[Show help for cliproxy commands]' \ - '--install[Install specific version]:version:' \ - '--latest[Install latest version]' \ - '1: :->subcommand' \ - '*:: :->subargs' - - case $state in - subcommand) - _describe -t cliproxy-commands 'cliproxy commands' cliproxy_commands - ;; - - subargs) - case $words[1] in - create) - _arguments \ - '1:variant name:' \ - '--provider[Provider name]:provider:($providers)' \ - '--model[Model name]:model:' \ - '--force[Overwrite existing variant]' \ - {--yes,-y}'[Skip prompts]' - ;; - list|ls) - ;; - remove|delete|rm) - _arguments \ - '1:variant:($cliproxy_variants)' \ - {--yes,-y}'[Skip confirmation]' - ;; - esac - ;; - esac } - -_ccs_auth() { - local curcontext="$curcontext" state line - typeset -A opt_args - - local -a auth_commands account_profiles - - auth_commands=( - 'create:Create new profile and login' - 'list:List all saved profiles' - 'show:Show profile details' - 'remove:Remove saved profile' - 'default:Set default profile' - ) - - if [[ -f ~/.ccs/profiles.json ]]; then - account_profiles=(${(f)"$(jq -r '.profiles | keys[]' ~/.ccs/profiles.json 2>/dev/null)"}) - fi - - _arguments -C \ - '(- *)'{-h,--help}'[Show help for auth commands]' \ - '1: :->subcommand' \ - '*:: :->subargs' - - case $state in - subcommand) - _describe -t auth-commands 'auth commands' auth_commands - ;; - - subargs) - case $words[1] in - create) - _message 'new profile name' - _arguments '--force[Allow overwriting existing profile]' - ;; - list) - _arguments \ - '--verbose[Show additional details]' \ - '--json[Output in JSON format]' - ;; - show) - _arguments \ - '1:profile:($account_profiles)' \ - '--json[Output in JSON format]' - ;; - remove) - _arguments \ - '1:profile:($account_profiles)' \ - {--yes,-y}'[Skip confirmation prompts]' - ;; - default) - _arguments '1:profile:($account_profiles)' - ;; - esac - ;; - esac -} - -_ccs "$@" diff --git a/src/ccs.ts b/src/ccs.ts index 790f652e..64bfddd6 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -318,10 +318,11 @@ async function main(): Promise { registerTarget(new CodexAdapter()); const args = process.argv.slice(2); + const isCompletionCommand = args[0] === '__complete'; // Initialize UI colors early to ensure consistent colored output // Must happen before any status messages (ok, info, fail, etc.) - if (process.stdout.isTTY && !process.env['CI']) { + if (!isCompletionCommand && process.stdout.isTTY && !process.env['CI']) { const { initUI } = await import('./utils/ui'); await initUI(); } @@ -361,7 +362,7 @@ async function main(): Promise { // Security warning: cloud sync paths expose OAuth tokens const cloudService = detectCloudSyncPath(configDirValue); - if (cloudService) { + if (!isCompletionCommand && cloudService) { console.error(warn(`CCS directory is under ${cloudService}.`)); console.error(' OAuth tokens in cliproxy/auth/ will be synced to cloud.'); console.error(' Consider: CCS_DIR=/path/outside/cloud ccs ...'); @@ -372,7 +373,7 @@ async function main(): Promise { } else if (process.env.CCS_DIR) { // Also warn for CCS_DIR env var pointing to cloud sync const cloudService = detectCloudSyncPath(process.env.CCS_DIR); - if (cloudService) { + if (!isCompletionCommand && cloudService) { console.error(warn(`CCS directory is under ${cloudService}.`)); console.error(' OAuth tokens in cliproxy/auth/ will be synced to cloud.'); console.error(' Consider: CCS_DIR=/path/outside/cloud ccs ...'); @@ -380,13 +381,18 @@ async function main(): Promise { } else if (process.env.CCS_HOME) { // Also warn for CCS_HOME env var pointing to cloud sync const cloudService = detectCloudSyncPath(process.env.CCS_HOME); - if (cloudService) { + if (!isCompletionCommand && cloudService) { console.error(warn(`CCS directory is under ${cloudService}.`)); console.error(' OAuth tokens in cliproxy/auth/ will be synced to cloud.'); console.error(' Consider: CCS_DIR=/path/outside/cloud ccs ...'); } } + if (isCompletionCommand) { + await tryHandleRootCommand(args); + return; + } + if (shouldPassthroughNativeCodexFlagCommand(args)) { execNativeCodexFlagCommand(args); return; diff --git a/src/commands/command-catalog.ts b/src/commands/command-catalog.ts new file mode 100644 index 00000000..a04589e7 --- /dev/null +++ b/src/commands/command-catalog.ts @@ -0,0 +1,330 @@ +import { COPILOT_SUBCOMMANDS } from '../copilot/constants'; +import { CLIPROXY_PROVIDER_IDS } from '../cliproxy/provider-capabilities'; +import { CURSOR_SUBCOMMANDS } from './cursor-command'; + +export type HelpTopicName = 'profiles' | 'providers' | 'completion' | 'targets'; + +export interface HelpTopicEntry { + name: HelpTopicName; + summary: string; +} + +export interface RootCommandEntry { + name: string; + summary: string; + group: 'start' | 'manage' | 'runtime' | 'operations'; + visibility: 'public' | 'hidden'; + aliases?: readonly string[]; +} + +export interface ShortcutEntry { + name: string; + summary: string; +} + +export const ROOT_HELP_TOPICS: readonly HelpTopicEntry[] = [ + { name: 'profiles', summary: 'Account profiles, API profiles, and CLIProxy variants' }, + { name: 'providers', summary: 'Built-in OAuth providers and runtime shortcuts' }, + { name: 'completion', summary: 'Shell completion install, refresh, and testing' }, + { name: 'targets', summary: 'Claude, Droid, and Codex target routing' }, +] as const; + +export const ROOT_COMMAND_CATALOG: readonly RootCommandEntry[] = [ + { + name: 'help', + summary: 'Show root help or help for a topic/command', + group: 'start', + aliases: ['--help', '-h'], + visibility: 'public', + }, + { + name: 'version', + summary: 'Show version and install details', + group: 'start', + aliases: ['--version', '-v'], + visibility: 'public', + }, + { + name: 'setup', + summary: 'Run the first-time setup wizard', + group: 'start', + aliases: ['--setup'], + visibility: 'public', + }, + { + name: 'config', + summary: 'Open the dashboard and config subcommands', + group: 'start', + visibility: 'public', + }, + { + name: 'doctor', + summary: 'Run health checks and diagnostics', + group: 'start', + aliases: ['--doctor'], + visibility: 'public', + }, + { + name: 'auth', + summary: 'Manage concurrent Claude accounts', + group: 'manage', + visibility: 'public', + }, + { + name: 'api', + summary: 'Create, discover, copy, export, and import API profiles', + group: 'manage', + visibility: 'public', + }, + { + name: 'cliproxy', + summary: 'Manage CLIProxy variants, quota, and local lifecycle', + group: 'manage', + visibility: 'public', + }, + { + name: 'env', + summary: 'Export profile env for third-party tools', + group: 'manage', + visibility: 'public', + }, + { + name: 'persist', + summary: 'Write profile setup to ~/.claude/settings.json', + group: 'manage', + visibility: 'public', + }, + { name: 'tokens', summary: 'Manage CLIProxy auth tokens', group: 'manage', visibility: 'public' }, + { + name: 'migrate', + summary: 'Move legacy JSON config to unified YAML config', + group: 'manage', + aliases: ['--migrate'], + visibility: 'public', + }, + { + name: 'cursor', + summary: 'Run or manage the Cursor bridge', + group: 'runtime', + visibility: 'public', + }, + { + name: 'copilot', + summary: 'Run or manage the GitHub Copilot bridge', + group: 'runtime', + visibility: 'public', + }, + { + name: 'docker', + summary: 'Deploy or operate the bundled Docker stack', + group: 'operations', + visibility: 'public', + }, + { + name: 'sync', + summary: 'Sync delegation commands and skills', + group: 'operations', + aliases: ['--sync'], + visibility: 'public', + }, + { + name: 'update', + summary: 'Update CCS to the latest version', + group: 'operations', + aliases: ['--update'], + visibility: 'public', + }, + { + name: 'cleanup', + summary: 'Remove old CLIProxy logs', + group: 'operations', + aliases: ['--cleanup'], + visibility: 'public', + }, + { + name: '--shell-completion', + summary: 'Install shell completion', + group: 'operations', + aliases: ['-sc'], + visibility: 'hidden', + }, + { + name: '--install', + summary: 'Post-install bootstrap hook', + group: 'operations', + visibility: 'hidden', + }, + { + name: '--uninstall', + summary: 'Post-uninstall cleanup hook', + group: 'operations', + visibility: 'hidden', + }, + { + name: '__complete', + summary: 'Hidden shell completion backend', + group: 'operations', + visibility: 'hidden', + }, +] as const; + +export const BUILTIN_PROVIDER_SHORTCUTS: readonly ShortcutEntry[] = CLIPROXY_PROVIDER_IDS.map( + (name) => ({ + name, + summary: + { + gemini: 'Google Gemini via CLIProxy OAuth', + codex: 'OpenAI Codex via CLIProxy OAuth', + agy: 'Antigravity via CLIProxy OAuth', + qwen: 'Qwen Code via CLIProxy OAuth', + iflow: 'iFlow via CLIProxy OAuth', + kiro: 'Kiro via CLIProxy OAuth', + ghcp: 'GitHub Copilot via CLIProxy OAuth', + claude: 'Claude via CLIProxy OAuth', + kimi: 'Kimi via CLIProxy OAuth', + }[name] || 'CLIProxy OAuth provider', + }) +); + +export const ROOT_PROFILE_EXAMPLES: readonly ShortcutEntry[] = [ + { name: 'ccs auth create work', summary: 'Create a concurrent Claude account profile' }, + { name: 'ccs api create --preset glm', summary: 'Create a GLM-backed API profile' }, + { + name: 'ccs api create --preset anthropic --1m', + summary: 'Create a Claude API profile with explicit [1m]', + }, + { + name: 'ccs env --format claude-extension --ide vscode', + summary: 'Export IDE extension settings', + }, +] as const; + +export const ROOT_COMPATIBLE_ALIAS_EXAMPLES: readonly ShortcutEntry[] = [ + { name: '--target claude|droid|codex', summary: 'Route a profile to the target runtime' }, + { name: 'ccs-droid / ccsd', summary: 'Explicit Droid runtime aliases' }, + { name: 'ccs-codex / ccsx / ccsxp', summary: 'Explicit Codex runtime aliases' }, +] as const; + +export const ROOT_COMMAND_FLAGS = [ + '--help', + '-h', + '--version', + '-v', + '--shell-completion', + '-sc', +] as const; +export const AUTH_SUBCOMMANDS = [ + 'create', + 'list', + 'show', + 'remove', + 'default', + 'reset-default', +] as const; +export const API_SUBCOMMANDS = [ + 'create', + 'list', + 'discover', + 'copy', + 'export', + 'import', + 'remove', +] as const; +export const CLIPROXY_SUBCOMMANDS = [ + 'create', + 'edit', + 'list', + 'remove', + 'catalog', + 'sync', + 'quota', + 'start', + 'restart', + 'status', + 'stop', + 'doctor', + 'default', + 'pause', + 'resume', +] as const; +export const CONFIG_SUBCOMMANDS = ['auth', 'channels', 'image-analysis', 'thinking'] as const; +export const DOCKER_SUBCOMMANDS = ['up', 'down', 'status', 'update', 'logs', 'config'] as const; +export const TOKENS_FLAGS = [ + '--show', + '--api-key', + '--secret', + '--regenerate-secret', + '--variant', + '--reset', + '--help', + '-h', +] as const; +export const MIGRATE_FLAGS = ['--dry-run', '--rollback', '--list-backups', '--help'] as const; +export const CLEANUP_FLAGS = [ + '--errors', + '--days=', + '--dry-run', + '--force', + '--help', + '-h', +] as const; +export const PROVIDER_FLAGS = [ + '--auth', + '--add', + '--paste-callback', + '--accounts', + '--use', + '--config', + '--thinking', + '--effort', + '--1m', + '--no-1m', + '--logout', + '--headless', + '--port-forward', + '--help', + '-h', +] as const; + +export const COMMAND_FLAG_SUGGESTIONS: Readonly> = { + '--shell-completion': ['--bash', '--zsh', '--fish', '--powershell', '--force', '-f'], + auth: ['--help', '-h'], + api: ['--help', '-h'], + cleanup: CLEANUP_FLAGS, + config: ['--help', '-h', '--port', '-p', '--host', '-H', '--dev'], + cursor: ['--help', '-h'], + doctor: ['--fix', '-f', '--help', '-h'], + docker: ['--help', '-h', '--host'], + env: ['--format', '--shell', '--ide', '--help', '-h'], + migrate: MIGRATE_FLAGS, + tokens: TOKENS_FLAGS, + update: ['--force', '--beta', '--dev', '--help', '-h'], +}; + +export const CURSOR_COMPLETION_SUBCOMMANDS = [...CURSOR_SUBCOMMANDS] as const; +export const COPILOT_COMPLETION_SUBCOMMANDS = [...COPILOT_SUBCOMMANDS, 'help'] as const; + +export function getPublicRootCommands(): readonly RootCommandEntry[] { + return ROOT_COMMAND_CATALOG.filter((entry) => entry.visibility === 'public'); +} + +export function getAllRootCommandTokens(): string[] { + return uniqueStrings( + ROOT_COMMAND_CATALOG.flatMap((entry) => [entry.name, ...(entry.aliases || [])]).concat( + 'copilot' + ) + ); +} + +export function getPublicRootCommandTokens(): string[] { + return uniqueStrings( + ROOT_COMMAND_CATALOG.filter((entry) => entry.visibility === 'public').flatMap((entry) => [ + entry.name, + ...(entry.aliases || []), + ]) + ); +} + +export function uniqueStrings(values: readonly string[]): string[] { + return [...new Set(values.filter(Boolean))].sort((left, right) => left.localeCompare(right)); +} diff --git a/src/commands/completion-backend.ts b/src/commands/completion-backend.ts new file mode 100644 index 00000000..2537d4c5 --- /dev/null +++ b/src/commands/completion-backend.ts @@ -0,0 +1,260 @@ +import ProfileDetector from '../auth/profile-detector'; +import { + API_SUBCOMMANDS, + AUTH_SUBCOMMANDS, + BUILTIN_PROVIDER_SHORTCUTS, + CLEANUP_FLAGS, + COMMAND_FLAG_SUGGESTIONS, + CONFIG_SUBCOMMANDS, + COPILOT_COMPLETION_SUBCOMMANDS, + DOCKER_SUBCOMMANDS, + ROOT_COMMAND_FLAGS, + ROOT_HELP_TOPICS, + TOKENS_FLAGS, + PROVIDER_FLAGS, + uniqueStrings, + getPublicRootCommandTokens, + CLIPROXY_SUBCOMMANDS, + MIGRATE_FLAGS, + CURSOR_COMPLETION_SUBCOMMANDS, +} from './command-catalog'; + +export interface CompletionSuggestion { + value: string; + description?: string; +} + +type CompletionShell = 'bash' | 'zsh' | 'fish' | 'powershell'; + +interface CompletionRequest { + current: string; + shell: CompletionShell; + tokensBeforeCurrent: string[]; +} + +function suggestion(value: string, description?: string): CompletionSuggestion { + return { value, description }; +} + +function filterSuggestions( + suggestions: readonly CompletionSuggestion[], + current: string +): CompletionSuggestion[] { + const needle = current.trim().toLowerCase(); + const deduped = [...new Map(suggestions.map((entry) => [entry.value, entry])).values()]; + if (!needle) return deduped.sort((left, right) => left.value.localeCompare(right.value)); + return deduped + .filter((entry) => entry.value.toLowerCase().startsWith(needle)) + .sort((left, right) => left.value.localeCompare(right.value)); +} + +function getDynamicProfileSuggestions(): CompletionSuggestion[] { + const detector = new ProfileDetector(); + const profiles = detector.getAllProfiles(); + + return uniqueStrings([ + ...profiles.settings, + ...profiles.accounts, + ...profiles.cliproxyVariants, + ]).map((value) => suggestion(value)); +} + +function getTopLevelSuggestions(): CompletionSuggestion[] { + return [ + ...getPublicRootCommandTokens().map((value) => suggestion(value)), + ...ROOT_COMMAND_FLAGS.map((value) => suggestion(value)), + ...BUILTIN_PROVIDER_SHORTCUTS.map((entry) => suggestion(entry.name, entry.summary)), + ...getDynamicProfileSuggestions(), + ]; +} + +function getProfileDetector(): ProfileDetector { + return new ProfileDetector(); +} + +function getProfileNames(type: 'settings' | 'accounts' | 'cliproxyVariants'): string[] { + return getProfileDetector().getAllProfiles()[type]; +} + +function completeSubcommands( + values: readonly string[], + flags: readonly string[] = ['--help', '-h'] +): CompletionSuggestion[] { + return uniqueStrings([...values, ...flags]).map((value) => suggestion(value)); +} + +function getSuggestionsForCommand(tokensBeforeCurrent: string[]): CompletionSuggestion[] { + const [command, subcommand] = tokensBeforeCurrent; + const lastToken = tokensBeforeCurrent[tokensBeforeCurrent.length - 1]; + + switch (command) { + case undefined: + return getTopLevelSuggestions(); + case 'help': + return completeSubcommands(ROOT_HELP_TOPICS.map((topic) => topic.name)); + case 'auth': + if (!subcommand) return completeSubcommands(AUTH_SUBCOMMANDS); + if (subcommand === 'show') + return completeSubcommands(getProfileNames('accounts'), ['--json']); + if (subcommand === 'remove') + return completeSubcommands(getProfileNames('accounts'), ['--yes', '-y']); + if (subcommand === 'default') return completeSubcommands(getProfileNames('accounts')); + if (subcommand === 'create') { + return completeSubcommands( + [], + ['--force', '--share-context', '--context-group', '--deeper-continuity', '--bare'] + ); + } + if (subcommand === 'list') return completeSubcommands([], ['--verbose', '--json']); + return completeSubcommands([], COMMAND_FLAG_SUGGESTIONS.auth); + case 'api': + if (!subcommand) return completeSubcommands(API_SUBCOMMANDS); + if (subcommand === 'create') { + return completeSubcommands( + [], + [ + '--preset', + '--cliproxy-provider', + '--base-url', + '--api-key', + '--model', + '--1m', + '--no-1m', + '--target', + '--force', + '--yes', + '-y', + ] + ); + } + if (subcommand === 'discover') + return completeSubcommands([], ['--register', '--json', '--force']); + if (subcommand === 'copy') + return completeSubcommands(getProfileNames('settings'), [ + '--target', + '--force', + '--yes', + '-y', + ]); + if (subcommand === 'export') + return completeSubcommands(getProfileNames('settings'), ['--out', '--include-secrets']); + if (subcommand === 'import') + return completeSubcommands([], ['--name', '--yes', '-y', '--force']); + if (subcommand === 'remove') + return completeSubcommands(getProfileNames('settings'), ['--yes', '-y']); + return completeSubcommands([], COMMAND_FLAG_SUGGESTIONS.api); + case 'cliproxy': + if (!subcommand) + return completeSubcommands(CLIPROXY_SUBCOMMANDS, [ + '--install', + '--latest', + '--update', + '--backend', + '--verbose', + '-v', + '--help', + '-h', + ]); + if (['remove', 'edit'].includes(subcommand)) { + return completeSubcommands(getProfileNames('cliproxyVariants'), ['--yes', '-y']); + } + if (subcommand === 'create' && lastToken === '--provider') { + return BUILTIN_PROVIDER_SHORTCUTS.map((entry) => suggestion(entry.name, entry.summary)); + } + return completeSubcommands( + [], + ['--provider', '--model', '--target', '--backend', '--force', '--yes', '-y', '--help', '-h'] + ); + case 'config': + if (!subcommand || subcommand.startsWith('-')) { + return completeSubcommands(CONFIG_SUBCOMMANDS, COMMAND_FLAG_SUGGESTIONS.config); + } + return []; + case 'docker': + if (!subcommand || subcommand.startsWith('-')) { + return completeSubcommands(DOCKER_SUBCOMMANDS, COMMAND_FLAG_SUGGESTIONS.docker); + } + if (subcommand === 'up') + return completeSubcommands([], ['--port', '--proxy-port', '--host', '--help', '-h']); + if (subcommand === 'logs') + return completeSubcommands([], ['--follow', '--service', '--host', '--help', '-h']); + return completeSubcommands([], COMMAND_FLAG_SUGGESTIONS.docker); + case 'cursor': + return completeSubcommands(CURSOR_COMPLETION_SUBCOMMANDS); + case 'copilot': + return completeSubcommands(COPILOT_COMPLETION_SUBCOMMANDS); + case 'env': + if (lastToken === '--format') + return completeSubcommands(['openai', 'anthropic', 'raw', 'claude-extension']); + if (lastToken === '--shell') + return completeSubcommands(['auto', 'bash', 'zsh', 'fish', 'powershell']); + if (lastToken === '--ide') return completeSubcommands(['vscode', 'cursor', 'windsurf']); + return completeSubcommands( + uniqueStrings([ + ...getProfileNames('settings'), + ...getProfileNames('accounts'), + ...BUILTIN_PROVIDER_SHORTCUTS.map((entry) => entry.name), + ]), + COMMAND_FLAG_SUGGESTIONS.env + ); + case 'tokens': + if (lastToken === '--variant') + return completeSubcommands(getProfileNames('cliproxyVariants')); + return completeSubcommands([], TOKENS_FLAGS); + case 'migrate': + return completeSubcommands([], MIGRATE_FLAGS); + case 'cleanup': + return completeSubcommands([], CLEANUP_FLAGS); + case '--shell-completion': + case '-sc': + return completeSubcommands( + [], + ['--bash', '--zsh', '--fish', '--powershell', '--force', '-f'] + ); + default: + if (BUILTIN_PROVIDER_SHORTCUTS.some((entry) => entry.name === command)) { + if (command === 'agy') + return completeSubcommands([], [...PROVIDER_FLAGS, '--accept-agr-risk']); + if (command === 'kiro') { + return completeSubcommands( + [], + [...PROVIDER_FLAGS, '--kiro-auth-method', '--import', '--incognito'] + ); + } + return completeSubcommands([], PROVIDER_FLAGS); + } + return completeSubcommands([], COMMAND_FLAG_SUGGESTIONS[command] || []); + } +} + +export function getCompletionSuggestions(request: CompletionRequest): CompletionSuggestion[] { + return filterSuggestions(getSuggestionsForCommand(request.tokensBeforeCurrent), request.current); +} + +function parseCompletionArgs(args: string[]): CompletionRequest { + const shellIndex = args.indexOf('--shell'); + const currentIndex = args.indexOf('--current'); + const separatorIndex = args.indexOf('--'); + const shell = (shellIndex !== -1 ? args[shellIndex + 1] : 'bash') as CompletionShell; + const current = currentIndex !== -1 ? args[currentIndex + 1] || '' : ''; + const tokensBeforeCurrent = separatorIndex === -1 ? [] : args.slice(separatorIndex + 1); + return { shell, current, tokensBeforeCurrent }; +} + +function formatForShell( + shell: CompletionShell, + suggestions: readonly CompletionSuggestion[] +): string[] { + if (shell === 'fish' || shell === 'powershell') { + return suggestions.map((entry) => + entry.description ? `${entry.value}\t${entry.description}` : entry.value + ); + } + return suggestions.map((entry) => entry.value); +} + +export async function handleCompletionCommand(args: string[]): Promise { + const request = parseCompletionArgs(args); + const suggestions = getCompletionSuggestions(request); + process.stdout.write(formatForShell(request.shell, suggestions).join('\n')); +} diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index a457168a..88efc512 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -1,695 +1,229 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { initUI, box, color, dim, sectionHeader, subheader } from '../utils/ui'; -import { isUnifiedMode } from '../config/unified-config-loader'; -import { getCcsDirDisplay } from '../utils/config-manager'; -import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager'; -import { getOfficialChannelsSupportMessage } from '../channels/official-channels-runtime'; +import packageJson from '../../package.json'; +import { color, dim, header, initUI, subheader } from '../utils/ui'; +import { + BUILTIN_PROVIDER_SHORTCUTS, + ROOT_COMMAND_CATALOG, + ROOT_COMPATIBLE_ALIAS_EXAMPLES, + ROOT_HELP_TOPICS, + ROOT_PROFILE_EXAMPLES, + getPublicRootCommands, + type HelpTopicName, + type RootCommandEntry, +} from './command-catalog'; type HelpWriter = (line: string) => void; -// Get version from package.json (same as version-command.ts) -const VERSION = JSON.parse( - fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8') -).version; +function getTopicSummary(name: HelpTopicName): string { + return ROOT_HELP_TOPICS.find((topic) => topic.name === name)?.summary || ''; +} -/** - * Print a major section with ═══ borders (only for 3 main sections) - * Format: - * ═══ TITLE ═══ - * Subtitle line 1 - * Subtitle line 2 - * - * command Description - */ -function printMajorSection( +function writeCommandTable( title: string, - subtitles: string[], - items: [string, string][], - writeLine: HelpWriter = console.log + entries: readonly { name: string; summary: string }[], + writeLine: HelpWriter ): void { - // Section header with ═══ borders - writeLine(sectionHeader(title)); - - // Subtitles on separate lines (dim) - for (const subtitle of subtitles) { - writeLine(` ${dim(subtitle)}`); + writeLine(subheader(title)); + const maxWidth = Math.max(...entries.map((entry) => entry.name.length)); + for (const entry of entries) { + writeLine(` ${color(entry.name.padEnd(maxWidth + 2), 'command')} ${entry.summary}`); } - - // Empty line before items - writeLine(''); - - // Calculate max command length for alignment - const maxCmdLen = Math.max(...items.map(([cmd]) => cmd.length)); - - for (const [cmd, desc] of items) { - const paddedCmd = cmd.padEnd(maxCmdLen + 2); - writeLine(` ${color(paddedCmd, 'command')} ${desc}`); - } - - // Extra spacing after section writeLine(''); } -/** - * Print a sub-section with colored title - * Format: - * Title (context): - * command Description - */ -function printSubSection( - title: string, - items: [string, string][], - writeLine: HelpWriter = console.log -): void { - // Sub-section header (colored, no borders) - writeLine(subheader(`${title}:`)); +function writeGroupedCommands(group: RootCommandEntry['group'], writeLine: HelpWriter): void { + const entries = getPublicRootCommands() + .filter((entry) => entry.group === group) + .map((entry) => ({ name: entry.name, summary: entry.summary })); - // Calculate max command length for alignment - const maxCmdLen = Math.max(...items.map(([cmd]) => cmd.length)); + const titles: Record = { + start: 'Start Here', + manage: 'Profile Management', + operations: 'Operations', + runtime: 'Compatible Runtimes', + }; - for (const [cmd, desc] of items) { - const paddedCmd = cmd.padEnd(maxCmdLen + 2); - writeLine(` ${color(paddedCmd, 'command')} ${desc}`); - } + writeCommandTable(titles[group], entries, writeLine); +} - // Spacing after section +async function showProfilesHelp(writeLine: HelpWriter): Promise { + await initUI(); + writeLine(header('CCS Profiles Help')); + writeLine(''); + writeCommandTable( + 'Profile Types', + [ + { name: 'ccs auth create ', summary: 'Concurrent Claude account profile' }, + { name: 'ccs api create', summary: 'API-backed settings profile' }, + { name: 'ccs cliproxy create ', summary: 'Named CLIProxy variant profile' }, + { name: 'ccs env ', summary: 'Export an existing profile for other tools' }, + ], + writeLine + ); + writeCommandTable('Examples', ROOT_PROFILE_EXAMPLES, writeLine); + writeLine(` ${dim('Deep help: ccs auth --help | ccs api --help | ccs cliproxy --help')}`); writeLine(''); } -/** - * Print a config/paths section - * Format: - * Title: - * Label: path - */ -function printConfigSection( - title: string, - items: [string, string][], - writeLine: HelpWriter = console.log -): void { - writeLine(subheader(`${title}:`)); - - // Calculate max label length for alignment - const maxLabelLen = Math.max(...items.map(([label]) => label.length)); - - for (const [label, path] of items) { - const paddedLabel = label.padEnd(maxLabelLen); - writeLine(` ${paddedLabel} ${color(path, 'path')}`); - } - +async function showProvidersHelp(writeLine: HelpWriter): Promise { + await initUI(); + writeLine(header('CCS Providers Help')); + writeLine(''); + writeCommandTable('Built-in OAuth Providers', BUILTIN_PROVIDER_SHORTCUTS, writeLine); + writeCommandTable( + 'Common Setup Paths', + [ + { + name: 'ccs --auth', + summary: 'Authenticate a provider account without launching', + }, + { name: 'ccs api create --preset ', summary: 'Create an API-backed provider profile' }, + { name: 'ccs config', summary: 'Use the dashboard for provider and model setup' }, + ], + writeLine + ); + writeLine(` ${dim('Deep help: ccs cliproxy --help | ccs api --help')}`); writeLine(''); } -/** - * Display comprehensive help information for CCS (Claude Code Switch) - */ +async function showTargetsHelp(writeLine: HelpWriter): Promise { + await initUI(); + writeLine(header('CCS Targets Help')); + writeLine(''); + writeCommandTable('Target Routing', ROOT_COMPATIBLE_ALIAS_EXAMPLES, writeLine); + writeCommandTable( + 'Examples', + [ + { name: 'ccs glm --target droid', summary: 'Run a profile on Droid instead of Claude' }, + { + name: 'ccs --target codex', + summary: 'Open a native Codex session with your current setup', + }, + { name: 'ccs codex-api --target codex', summary: 'Run a routed API bridge on native Codex' }, + ], + writeLine + ); +} + export async function handleHelpCommand(writeLine: HelpWriter = console.log): Promise { - // Initialize UI (if not already) await initUI(); - // Hero box with ASCII art logo and config hint - // Each letter: C=╔═╗/║ /╚═╝, C=╔═╗/║ /╚═╝, S=╔═╗/╚═╗/╚═╝ - const logo = ` -╔═╗ ╔═╗ ╔═╗ -║ ║ ╚═╗ v${VERSION} -╚═╝ ╚═╝ ╚═╝ - -Claude Code Profile & Model Switcher - -Run ${color('ccs config', 'command')} for web dashboard`.trim(); - - writeLine( - box(logo, { - padding: 1, - borderStyle: 'round', - titleAlignment: 'center', - }) - ); + writeLine(header(`CCS CLI v${packageJson.version}`)); + writeLine(''); + writeLine(' Claude profile switching, provider routing, and compatible runtime bridges.'); writeLine(''); - // Resolve display path for dynamic sections - const dirDisplay = getCcsDirDisplay(); - - // Usage section - writeLine(subheader('Usage:')); - writeLine(` ${color('ccs', 'command')} [profile] [claude-args...]`); - writeLine(` ${color('ccs', 'command')} [flags]`); + writeLine(subheader('Usage')); + writeLine(` ${color('ccs [claude-args...]', 'command')}`); + writeLine(` ${color('ccs [options]', 'command')}`); + writeLine(` ${color('ccs help ', 'command')}`); writeLine(''); - // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 1: API Key Profiles - // ═══════════════════════════════════════════════════════════════════════════ - printMajorSection( - 'API Key Profiles', - [`Configure in ${dirDisplay}/*.settings.json`], + writeGroupedCommands('start', writeLine); + writeGroupedCommands('manage', writeLine); + writeGroupedCommands('runtime', writeLine); + writeGroupedCommands('operations', writeLine); + + writeCommandTable( + 'OAuth Provider Shortcuts', + BUILTIN_PROVIDER_SHORTCUTS.map((entry) => ({ + name: `ccs ${entry.name}`, + summary: entry.summary, + })), + writeLine + ); + + writeCommandTable('Examples', ROOT_PROFILE_EXAMPLES, writeLine); + writeCommandTable('Targets and Aliases', ROOT_COMPATIBLE_ALIAS_EXAMPLES, writeLine); + writeCommandTable( + 'More Help', [ - ['ccs', 'Use default Claude account'], - ['ccs glm', 'GLM 5 (API key required)'], - ['ccs km', 'Kimi for Coding (API key)'], - [ - 'ccs api create --preset alibaba-coding-plan', - 'Alibaba Coding Plan (Anthropic-compatible API key)', - ], - ['ccs ollama', 'Local Ollama (http://localhost:11434)'], - ['ccs llamacpp', 'Local llama.cpp (http://127.0.0.1:8080)'], - ['ccs ollama-cloud', 'Ollama Cloud (API key required)'], - ['', ''], // Spacer - ['ccs api create --preset anthropic', 'Anthropic direct API key (sk-ant-...)'], - [ - 'ccs api create --cliproxy-provider gemini', - 'Create routed API profile from CLIProxy Gemini', - ], - ['ccs api create', 'Create custom API profile'], - ['ccs api discover --register', 'Discover/register orphan settings files'], - ['ccs api copy ', 'Duplicate API profile'], - ['ccs api export ', 'Export profile bundle'], - ['ccs api import ', 'Import profile bundle'], - ['ccs api remove', 'Remove an API profile'], - ['ccs api list', 'List all API profiles'], + { name: 'ccs help profiles', summary: getTopicSummary('profiles') }, + { name: 'ccs help providers', summary: getTopicSummary('providers') }, + { name: 'ccs help completion', summary: getTopicSummary('completion') }, + { name: 'ccs help targets', summary: getTopicSummary('targets') }, + { name: 'ccs api --help', summary: 'Deep help for API profile lifecycle commands' }, + { name: 'ccs cliproxy --help', summary: 'Deep help for variants, quota, and lifecycle' }, + { name: 'ccs docker --help', summary: 'Deep help for Docker deployment commands' }, + { name: 'ccs cursor --help', summary: 'Deep help for Cursor runtime/admin commands' }, + { name: 'ccs copilot --help', summary: 'Deep help for GitHub Copilot commands' }, ], writeLine ); - // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 2: Account Management - // ═══════════════════════════════════════════════════════════════════════════ - printMajorSection( - 'Account Management', - ['Run multiple Claude accounts concurrently'], - [ - ['ccs auth --help', 'Show account management commands'], - [ - 'ccs auth create ', - 'Create account profile (supports --bare, shared groups, --deeper-continuity)', - ], - ['ccs config', 'Dashboard: Accounts table can edit context mode/group/continuity depth'], - [ - '~/.ccs/config.yaml', - 'Optional: continuity.inherit_from_account maps API/CLIProxy/copilot/default profiles to an account context', - ], - ['ccs auth list', 'List all account profiles'], - ['ccs auth default ', 'Set default profile'], - ['ccs auth reset-default', 'Restore original CCS default'], - ['ccs cliproxy auth claude', 'Alternative: authenticate Claude account pool via CLIProxy'], - ], - writeLine - ); - - // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 3: CLI Proxy (OAuth Providers) - // ═══════════════════════════════════════════════════════════════════════════ - printMajorSection( - 'CLI Proxy Plus (OAuth Providers)', - [ - 'Zero-config OAuth authentication via CLIProxy Plus', - 'First run: Browser opens for authentication, then model selection', - 'Settings: ~/.ccs/{provider}.settings.json (created after auth)', - 'Safety: do not reuse one Google account across "ccs gemini" and "ccs agy" (issue #509)', - 'Antigravity requires multi-step responsibility confirmation (issue #509)', - 'If you want to keep Google AI access, do not continue this shared-account setup', - 'CCS is as-is and does not take responsibility for account bans/access loss', - ], - [ - ['ccs gemini', 'Google Gemini (gemini-2.5-pro or 3-pro)'], - ['ccs codex', 'OpenAI Codex (supports -medium/-high/-xhigh model suffixes)'], - ['ccs agy', 'Antigravity (Claude/Gemini models)'], - ['ccs qwen', 'Qwen Code OAuth (CLIProxy)'], - ['ccs kimi', 'Kimi (Moonshot AI K2/K2.5 models)'], - ['ccs kiro', 'Kiro (AWS CodeWhisperer Claude models)'], - ['ccs ghcp', 'GitHub Copilot (OAuth via CLIProxy Plus)'], - ['', ''], // Spacer - ['ccs --auth', 'Authenticate only'], - ['ccs --auth --add', 'Add another account'], - [ - 'ccs --paste-callback', - 'Show auth URL and prompt for callback paste (cross-browser)', - ], - ['ccs --accounts', 'List all accounts'], - ['ccs --use ', 'Switch to account'], - ['ccs --config', 'Change model (agy, gemini)'], - [ - 'ccs agy --accept-agr-risk', - 'Bypass interactive Antigravity confirmation (you accept full responsibility)', - ], - [ - 'ccs --thinking ', - 'Set thinking budget (low/medium/high/xhigh/auto/off or number)', - ], - ['ccs codex --effort ', 'Set codex reasoning effort (minimal/low/medium/high/xhigh)'], - ['ccs --1m', 'Request explicit 1M context when the selected model supports [1m]'], - ['ccs --no-1m', 'Force standard context / clear [1m]'], - ['ccs --logout', 'Clear authentication'], - ['ccs --headless', 'Headless auth (for SSH)'], - ['ccs --port-forward', 'Force port-forwarding mode (skip prompt)'], - ['ccs kiro --auth --kiro-auth-method aws', 'Kiro via AWS Builder ID (device code)'], - ['ccs kiro --auth --kiro-auth-method aws-authcode', 'Kiro via AWS auth code flow'], - ['ccs kiro --auth --kiro-auth-method google', 'Kiro via Google OAuth'], - ['ccs kiro --auth --kiro-auth-method github', 'Kiro via GitHub OAuth (Dashboard flow)'], - ['ccs kiro --import', 'Import token from Kiro IDE'], - ['ccs kiro --incognito', 'Use incognito browser (default: normal)'], - ['ccs codex "explain code"', 'Use with prompt'], - ], - writeLine - ); - - // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 4: GitHub Copilot Integration (copilot-api) - // ═══════════════════════════════════════════════════════════════════════════ - printMajorSection( - 'GitHub Copilot Integration (copilot-api)', - [ - 'Use your GitHub Copilot subscription with Claude Code via copilot-api', - 'Requires: npm install -g copilot-api', - 'Note: For OAuth-based access, use ccs ghcp instead', - ], - [ - ['ccs copilot', 'Use Copilot via copilot-api daemon'], - ['ccs copilot auth', 'Authenticate with GitHub'], - ['ccs copilot status', 'Show integration status'], - ['ccs copilot models', 'List available models'], - ['ccs copilot usage', 'Show Copilot quota usage'], - ['ccs copilot start', 'Start copilot-api daemon'], - ['ccs copilot stop', 'Stop copilot-api daemon'], - ['ccs copilot enable', 'Enable integration'], - ['ccs copilot disable', 'Disable integration'], - ], - writeLine - ); - - // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 5: Cursor IDE Integration - // ═══════════════════════════════════════════════════════════════════════════ - printMajorSection( - 'Cursor IDE Integration', - [ - 'Use Cursor IDE with Claude Code via cursor proxy daemon', - 'Auto-detects token from Cursor installation', - ], - [ - ['ccs cursor', 'Run Claude via Cursor local proxy'], - ['ccs cursor auth', 'Import Cursor token'], - ['ccs cursor auth --manual --token --machine-id ', 'Manual token import'], - ['ccs cursor status', 'Show connection status'], - ['ccs cursor models', 'List available models'], - ['ccs cursor start', 'Start proxy daemon'], - ['ccs cursor stop', 'Stop proxy daemon'], - ['ccs cursor enable', 'Enable cursor integration'], - ['ccs cursor disable', 'Disable cursor integration'], - ], - writeLine - ); - - // ═══════════════════════════════════════════════════════════════════════════ - // SUB-SECTIONS (simpler styling) - // ═══════════════════════════════════════════════════════════════════════════ - - // Delegation - printSubSection( - 'Delegation (inside Claude Code CLI)', - [ - ['/ccs "task"', 'Delegate task (auto-selects profile)'], - ['/ccs --glm "task"', 'Force GLM-5 for simple tasks'], - ['/ccs --kimi "task"', 'Force Kimi OAuth for long context'], - ['/ccs --km "task"', 'Force Kimi API key for long context'], - ['/ccs:continue "follow-up"', 'Continue last delegation session'], - ], - writeLine - ); - - // Delegation CLI Flags (Claude Code passthrough) - printSubSection( - 'Delegation Flags (Claude Code passthrough)', - [ - ['--max-turns ', 'Limit agentic turns (prevents loops)'], - ['--fallback-model ', 'Auto-fallback on overload (sonnet)'], - ['--agents ', 'Inject dynamic subagents'], - ['--betas ', 'Enable experimental features'], - ['--allowedTools ', 'Restrict available tools'], - ['--disallowedTools ', 'Block specific tools'], - ], - writeLine - ); - - // Diagnostics - printSubSection( - 'Diagnostics', - [ - ['ccs setup', 'First-time setup wizard'], - ['ccs doctor', 'Run health check and diagnostics'], - ['ccs cleanup', 'Remove old CLIProxy logs'], - ['ccs config', 'Open web dashboard (includes Claude IDE Extension setup page)'], - ['ccs config auth setup', 'Configure dashboard login'], - ['ccs config auth show', 'Show dashboard auth status'], - ['ccs config channels', 'Show Official Channels status'], - [ - 'ccs config channels --set telegram,discord', - 'Auto-add Telegram + Discord on supported native Claude runs', - ], - ['ccs config channels --set-token telegram=', 'Save TELEGRAM_BOT_TOKEN'], - ['ccs config image-analysis', 'Show image analysis settings'], - ['ccs config image-analysis --enable', 'Enable image analysis'], - ['ccs config thinking', 'Show thinking/reasoning settings'], - ['ccs config thinking --mode auto', 'Set thinking mode'], - ['ccs config thinking --clear-provider-override codex', 'Clear provider overrides'], - ['ccs config --port 3000', 'Use specific port'], - ['ccs config --host 0.0.0.0', 'Force all-interface binding for remote devices'], - ['ccs persist ', 'Write profile setup to ~/.claude/settings.json'], - ['ccs persist --list-backups', 'List available settings.json backups'], - ['ccs persist --restore', 'Restore settings.json from latest backup'], - ['ccs sync', 'Sync delegation commands and skills'], - ['ccs update', 'Update CCS to latest version'], - ['ccs update --force', 'Force reinstall current version'], - ['ccs update --beta', 'Install from dev channel (unstable)'], - ], - writeLine - ); - - // Environment export - printSubSection( - 'Environment Export', - [ - ['ccs env ', 'Export env vars for third-party tools'], - ['ccs env --format openai', 'OpenAI-compatible vars (OpenCode/Cursor)'], - ['ccs env --format anthropic', 'Anthropic vars (default)'], - ['ccs env --format raw', 'All effective env vars'], - [ - 'ccs env --format claude-extension --ide vscode', - 'VS Code/Cursor Claude extension settings JSON', - ], - [ - 'ccs env --format claude-extension --ide windsurf', - 'Windsurf Claude extension settings JSON', - ], - ['ccs env --shell fish', 'Fish shell syntax'], - ], - writeLine - ); - - // Flags - printSubSection( - 'Flags', - [ - ['--config-dir ', 'Use custom CCS config directory'], - ['--target ', 'Target CLI: claude (default), droid, codex (runtime-only)'], - ['-h, --help', 'Show this help message'], - ['-v, --version', 'Show version and installation info'], - ['-sc, --shell-completion', 'Install shell auto-completion'], - ], - writeLine - ); - - // Aliases - printSubSection( - 'Aliases', - [ - ['ccs-droid [args]', 'Explicit Droid runtime alias'], - ['ccsd [args]', 'Legacy shortcut for: ccs-droid [args]'], - ['ccs-codex [args]', 'Explicit Codex runtime alias'], - ['ccsx [args]', 'Short alias for: ccs-codex [args]'], - ['ccsxp [args]', 'Shortcut for: ccs codex --target codex [args]'], - ], - writeLine - ); - - // Multi-target examples - printSubSection( - 'Multi-Target', - [ - ['ccs glm --target droid', 'Run GLM profile on Droid CLI'], - ['ccs-droid glm', 'Same as above (explicit alias)'], - ['ccsd glm', 'Legacy shortcut for ccs-droid'], - ['ccs --target codex', 'Open a native Codex session with your existing ~/.codex setup'], - ['ccs-codex', 'Same as above (explicit Codex alias)'], - ['ccsx', 'Short alias for ccs-codex'], - ['ccsxp "your prompt"', 'Run built-in CLIProxy Codex on native Codex CLI'], - ['ccs codex --target codex', 'Explicit form of ccsxp'], - [ - 'ccs api create codex-api --cliproxy-provider codex', - 'Create a routed API bridge that can also run on Codex', - ], - ['ccs codex-api --target codex', 'Run a Codex bridge profile on native Codex CLI'], - ['ccs-droid codex', 'Run built-in CLIProxy Codex profile on Droid'], - ['ccs-droid agy', 'Run built-in CLIProxy Antigravity profile on Droid'], - [ - 'ccs-droid codex exec --skip-permissions-unsafe "fix failing tests"', - 'Pass through Droid exec mode', - ], - [ - 'ccs-droid codex -m custom:gpt-5.3-codex "fix failing tests"', - 'Auto-routes short exec flags', - ], - [ - 'ccs-droid codex --skip-permissions-unsafe "fix failing tests"', - 'Auto-routes to Droid exec when exec-only flags are detected', - ], - [ - 'ccs cliproxy create my-codex --provider codex --target droid', - 'Create CLIProxy variant with Droid as default target', - ], - ['ccs glm', 'Run GLM profile on Claude Code (default)'], - ], - writeLine - ); - - printSubSection( - 'Codex + CLIProxy', - [ - ['ccsxp "your prompt"', 'Use the built-in CCS Codex provider shortcut on native Codex'], - ['ccs config', 'Open Compatible -> Codex CLI for native Codex setup and diagnostics'], - [ - 'Default provider', - 'Set to cliproxy if plain codex or a personal cxp alias should use CLIProxy', - ], - ['Model provider auth', 'Save cliproxy with env_key = "CLIPROXY_API_KEY"'], - ], - writeLine - ); - - // Configuration - printConfigSection( - 'Configuration', - [ - ['Config File:', isUnifiedMode() ? `${dirDisplay}/config.yaml` : `${dirDisplay}/config.json`], - ['Profiles:', `${dirDisplay}/profiles.json`], - ['Instances:', `${dirDisplay}/instances/`], - ['Settings:', `${dirDisplay}/*.settings.json`], - ], - writeLine - ); - - // CLI Proxy management - printSubSection( - 'CLI Proxy Plus Management', - [ - ['ccs cliproxy', 'Show CLIProxy Plus status and version'], - ['ccs cliproxy --help', 'Full CLIProxy Plus management help'], - ['ccs cliproxy doctor', 'Quota diagnostics (Antigravity)'], - ['ccs cliproxy --install ', 'Install specific version (e.g., 6.6.6)'], - ['ccs cliproxy --latest', 'Update to latest version'], - ['', ''], // Spacer - ['ccs cliproxy pause

', 'Pause account from rotation'], - ['ccs cliproxy resume

', 'Resume paused account'], - ['ccs cliproxy status', 'Show CLIProxy process status'], - ['ccs cliproxy quota', 'Show quota/tier/pause status for all providers'], - ['ccs cliproxy quota --provider ', 'Show quota/tier/pause status for one provider'], - ], - writeLine - ); - - printSubSection( - 'Docker Deployment', - [ - ['ccs docker --help', 'Manage the integrated CCS + CLIProxy Docker stack'], - ['ccs docker up', 'Build and start the stack locally'], - ['ccs docker up --host ', 'Stage assets to ~/.ccs/docker and deploy remotely'], - ['ccs docker status', 'Show docker compose and supervisor status'], - ['ccs docker logs --follow', 'Tail combined CCS + CLIProxy logs'], - ['ccs docker update', 'Update CCS and CLIProxy inside the running container'], - ['ccs docker config', 'Show bundled asset paths and deployment defaults'], - ], - writeLine - ); - - // CLI Proxy configuration flags (new) - printSubSection( - 'CLI Proxy Configuration', - [ - ['--proxy-host ', 'Remote proxy hostname/IP'], - ['--proxy-port ', `Proxy port (default: ${CLIPROXY_DEFAULT_PORT})`], - ['--proxy-protocol ', 'Protocol: http or https (default: http)'], - ['--proxy-auth-token ', 'Auth token for remote proxy'], - ['--proxy-timeout ', 'Connection timeout in ms (default: 2000)'], - ['--local-proxy', 'Force local mode, ignore remote config'], - ['--remote-only', 'Fail if remote unreachable (no fallback)'], - ['--allow-self-signed', 'Allow self-signed certs (for dev proxies)'], - ], - writeLine - ); - - // W3: Thinking Budget explanation - printSubSection( - 'Extended Thinking / Reasoning', - [ - ['--thinking off', 'Disable extended thinking'], - ['--thinking auto', 'Let model decide dynamically'], - ['--thinking low', '1K tokens - Quick responses'], - ['--thinking medium', '8K tokens - Standard analysis'], - ['--thinking high', '24K tokens - Deep reasoning'], - ['--thinking xhigh', '32K tokens - Maximum depth'], - ['--thinking ', 'Custom token budget (512-100000)'], - ['', ''], - ['--effort ', 'Codex alias for reasoning effort (minimal/low/medium/high/xhigh)'], - ['--effort xhigh', 'Pin Codex effort to xhigh for this run'], - ['', ''], - ['Droid exec:', 'Use native Droid flag: --reasoning-effort '], - ['', 'CCS auto-maps --thinking/--effort to --reasoning-effort in droid exec mode.'], - ['', 'For interactive droid sessions, CCS applies reasoning via Droid BYOK model config.'], - ['', 'When multiple reasoning flags are provided, the first flag wins.'], - ['', ''], - ['Note:', 'Extended thinking allocates compute for step-by-step reasoning'], - ['', 'before responding.'], - ['', 'Providers: agy/gemini use --thinking, codex uses --effort (or --thinking alias).'], - ['', 'Codex model suffixes also pin effort: -medium / -high / -xhigh.'], - ], - writeLine - ); - - // Extended Context (1M) - printSubSection( - 'Extended Context (--1m)', - [ - ['--1m', 'Request 1M token context when the selected model supports [1m]'], - ['--no-1m', 'Force standard context (Claude default stays plain)'], - ['', ''], - ['Auto behavior:', 'Gemini models: CCS auto-adds [1m] when supported'], - ['', 'Claude models: plain by default, opt-in with --1m or saved [1m]'], - ['', ''], - ['Note:', 'CCS only controls the saved [1m] suffix.'], - ['', 'Provider pricing and entitlement stay upstream.'], - [ - '', - 'Some accounts/providers can still return 429 extra-usage errors for long-context requests.', - ], - ], - writeLine - ); - - // Image Analysis - printSubSection( - 'Image Analysis (first-class local tool)', - [ - ['ccs config image-analysis', 'Show current settings'], - ['ccs config image-analysis --enable', 'Enable for CLIProxy providers'], - ['ccs config image-analysis --disable', 'Disable (use native Read)'], - ['ccs config image-analysis --timeout 120', 'Set analysis timeout'], - ['ccs config image-analysis --set-model

', 'Set provider model'], - ['', ''], - ['Note:', 'When ready, third-party launches expose the local ImageAnalysis MCP tool'], - ['', 'and route requests directly to the resolved CCS provider path.'], - ['', 'If runtime/auth/proxy is unavailable, CCS falls back to native Read.'], - ], - writeLine - ); - - printSubSection( - 'Official Channels (official Claude plugins)', - [ - ['ccs config', 'Dashboard -> Settings -> Channels (fastest path)'], - ['ccs config channels', 'Show current status'], - [ - 'ccs config channels --set telegram,discord', - 'Auto-add selected channels on native Claude default/account sessions', - ], - ['ccs config channels --set all', 'Enable Telegram, Discord, and iMessage'], - ['ccs config channels --unattended', 'Also add --dangerously-skip-permissions'], - ['ccs config channels --set-token telegram=', 'Save TELEGRAM_BOT_TOKEN'], - ['ccs config channels --set-token discord=', 'Save DISCORD_BOT_TOKEN'], - ['ccs config channels --clear-token [channel]', 'Remove one or all saved channel tokens'], - ['', ''], - ['', 'Fastest path: turn on the channel, save the token if needed, then run ccs.'], - ['Note:', getOfficialChannelsSupportMessage()], - ['', 'Telegram/Discord tokens live in ~/.claude/channels//.env.'], - ['', 'Current-process TELEGRAM_BOT_TOKEN / DISCORD_BOT_TOKEN also work for that launch.'], - ['', 'iMessage is macOS-only and requires local OS permissions instead of a bot token.'], - ], - writeLine - ); - - // CCS Environment Variables - printSubSection( - 'Environment Variables', - [ - ['CCS_DIR', 'Override CCS config directory (default: ~/.ccs)'], - ['CCS_HOME', 'Override home directory (legacy, appends .ccs)'], - ['CCS_DEBUG', 'Enable debug logging'], - ['CCS_THINKING', 'Override thinking level (flag > env > config)'], - ], - writeLine - ); - - // CLI Proxy env vars - printSubSection( - 'CLI Proxy Environment Variables', - [ - ['CCS_PROXY_HOST', 'Remote proxy hostname'], - ['CCS_PROXY_PORT', 'Proxy port'], - ['CCS_PROXY_PROTOCOL', 'Protocol (http/https)'], - ['CCS_PROXY_AUTH_TOKEN', 'Auth token'], - ['CCS_PROXY_TIMEOUT', 'Connection timeout in ms'], - ['CCS_PROXY_FALLBACK_ENABLED', 'Enable local fallback (1/0)'], - ['CCS_ALLOW_SELF_SIGNED', 'Allow self-signed certs (1/0)'], - ], - writeLine - ); - - // CLI Proxy paths - writeLine(subheader('CLI Proxy:')); - writeLine(` Binary: ${color(`${dirDisplay}/cliproxy/bin/cli-proxy-api-plus`, 'path')}`); - writeLine(` Config: ${color(`${dirDisplay}/cliproxy/config.yaml`, 'path')}`); - writeLine(` Auth: ${color(`${dirDisplay}/cliproxy/auth/`, 'path')}`); - writeLine(` ${dim(`Port: ${CLIPROXY_DEFAULT_PORT} (default)`)}`); - writeLine(''); - - // Shared Data - writeLine(subheader('Shared Data:')); - writeLine(` Commands: ${color(`${dirDisplay}/shared/commands/`, 'path')}`); - writeLine(` Skills: ${color(`${dirDisplay}/shared/skills/`, 'path')}`); - writeLine(` Agents: ${color(`${dirDisplay}/shared/agents/`, 'path')}`); - writeLine(` ${dim('Note: Symlinked across all profiles')}`); - writeLine(''); - - // Examples (aligned with consistent spacing) - writeLine(subheader('Examples:')); - writeLine(` $ ${color('ccs', 'command')} ${dim('# Use default account')}`); - writeLine( - ` $ ${color('ccs gemini', 'command')} ${dim('# OAuth (browser opens first time)')}` - ); - writeLine(` $ ${color('ccs glm "implement API"', 'command')} ${dim('# API key model')}`); - writeLine(` $ ${color('ccs config', 'command')} ${dim('# Open web dashboard')}`); - writeLine(''); - - // Update examples - writeLine(subheader('Update:')); - writeLine( - ` $ ${color('ccs update', 'command')} ${dim('# Update to latest stable')}` - ); - writeLine( - ` $ ${color('ccs update --force', 'command')} ${dim('# Force reinstall current')}` - ); - writeLine(` $ ${color('ccs update --beta', 'command')} ${dim('# Install dev channel')}`); - writeLine(''); - - // Docs link - writeLine(` ${dim('Docs: https://github.com/kaitranntt/ccs')}`); - writeLine(''); - - // Uninstall - writeLine(subheader('Uninstall:')); - writeLine(` ${color('npm uninstall -g @kaitranntt/ccs', 'command')}`); - writeLine(''); - - // License - writeLine(dim('License: MIT')); + writeLine(` ${dim('Flags: -h/--help, -v/--version, -sc/--shell-completion, --target ')}`); writeLine(''); } + +function listHelpTargets(): string { + return ROOT_HELP_TOPICS.map((topic) => topic.name).join(', '); +} + +export async function handleHelpRoute( + args: string[], + writeLine: HelpWriter = console.log +): Promise { + const topic = args[0] as HelpTopicName | undefined; + if (!topic) { + await handleHelpCommand(writeLine); + return; + } + + if (topic === 'profiles') { + await showProfilesHelp(writeLine); + return; + } + if (topic === 'providers') { + await showProvidersHelp(writeLine); + return; + } + if (topic === 'targets') { + await showTargetsHelp(writeLine); + return; + } + if (topic === 'completion') { + const { showShellCompletionHelp } = await import('./shell-completion-command'); + showShellCompletionHelp(writeLine); + return; + } + + const commandHandlers: Partial Promise>> = { + api: async () => (await import('./api-command/help')).showApiCommandHelp(writeLine), + auth: async () => { + const authModule = await import('../auth/auth-commands'); + const AuthCommands = authModule.default; + await new AuthCommands().showHelp(); + }, + cleanup: async () => (await import('./cleanup-command')).handleCleanupCommand(['--help']), + cliproxy: async () => (await import('./cliproxy/help-subcommand')).showHelp(), + copilot: async () => + process.exit(await (await import('./copilot-command')).handleCopilotCommand(['--help'])), + cursor: async () => + process.exit(await (await import('./cursor-command')).handleCursorCommand(['--help'])), + docker: async () => (await import('./docker/help-subcommand')).showHelp(), + migrate: async () => (await import('./migrate-command')).printMigrateHelp(), + setup: async () => (await import('./setup-command')).handleSetupCommand(['--help']), + tokens: async () => + process.exit(await (await import('./tokens-command')).handleTokensCommand(['--help'])), + }; + + const handler = commandHandlers[topic]; + if (handler) { + await handler(); + return; + } + + await initUI(); + writeLine(color(`Unknown help topic or command: ${topic}`, 'error')); + writeLine(''); + writeLine(` ${dim(`Available help topics: ${listHelpTargets()}`)}`); + writeLine(''); + process.exitCode = 1; +} + +export function getRootHelpVisibleCommands(): string[] { + return getPublicRootCommands().map((entry) => entry.name); +} + +export function getRootHelpCatalogEntries(): readonly RootCommandEntry[] { + return ROOT_COMMAND_CATALOG; +} diff --git a/src/commands/root-command-router.ts b/src/commands/root-command-router.ts index 450dec5b..be45acc7 100644 --- a/src/commands/root-command-router.ts +++ b/src/commands/root-command-router.ts @@ -16,7 +16,7 @@ async function printUpdateCommandHelp(): Promise { console.log(''); } -const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ +export const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ { name: 'migrate', aliases: ['--migrate'], @@ -55,9 +55,9 @@ const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ { name: 'help', aliases: ['--help', '-h'], - handle: async () => { - const { handleHelpCommand } = await import('./help-command'); - await handleHelpCommand(); + handle: async (args) => { + const { handleHelpRoute } = await import('./help-command'); + await handleHelpRoute(args); }, }, { @@ -82,6 +82,13 @@ const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ await handleShellCompletionCommand(args); }, }, + { + name: '__complete', + handle: async (args) => { + const { handleCompletionCommand } = await import('./completion-backend'); + await handleCompletionCommand(args); + }, + }, { name: 'doctor', aliases: ['--doctor'], diff --git a/src/commands/shell-completion-command.ts b/src/commands/shell-completion-command.ts index 65fe4c58..310789f0 100644 --- a/src/commands/shell-completion-command.ts +++ b/src/commands/shell-completion-command.ts @@ -28,6 +28,24 @@ interface ShellCompletionInstallerLike { install(shell: ShellTarget, options: { force: boolean }): ShellCompletionInstallResult; } +export function showShellCompletionHelp(writeLine: (line: string) => void = console.log): void { + writeLine(header('Shell Completion')); + writeLine(''); + writeLine(color('Usage:', 'info')); + writeLine(' ccs --shell-completion # Auto-detect shell and install'); + writeLine(' ccs --shell-completion --bash # Install for bash'); + writeLine(' ccs --shell-completion --zsh # Install for zsh'); + writeLine(' ccs --shell-completion --fish # Install for fish'); + writeLine(' ccs --shell-completion --powershell # Install for PowerShell'); + writeLine(' ccs --shell-completion --force # Reinstall/refresh the active shell setup'); + writeLine(''); + writeLine(color('Test:', 'info')); + writeLine(' ccs '); + writeLine(' ccs help '); + writeLine(' ccs auth '); + writeLine(''); +} + export function parseShellCompletionArgs(args: string[]): ShellCompletionParsedArgs { let targetShell: ShellTarget = null; const force = args.includes('--force') || args.includes('-f'); @@ -77,12 +95,15 @@ export function createShellCompletionCommandContract( */ export async function handleShellCompletionCommand(args: string[]): Promise { await initUI(); - const { ShellCompletionInstaller } = await import('../utils/shell-completion'); - - console.log(header('Shell Completion Installer')); - console.log(''); + if (args.includes('--help') || args.includes('-h')) { + showShellCompletionHelp(); + return; + } try { + const { ShellCompletionInstaller } = await import('../utils/shell-completion'); + console.log(header('Shell Completion Installer')); + console.log(''); const installer = new ShellCompletionInstaller(); const contract = createShellCompletionCommandContract(installer); await runCommandWithContract(args, contract); @@ -90,13 +111,7 @@ export async function handleShellCompletionCommand(args: string[]): Promise { + test('covers every routed root command and alias', () => { + const catalogTokens = new Set(getAllRootCommandTokens()); + + for (const route of ROOT_COMMAND_ROUTES) { + expect(catalogTokens.has(route.name)).toBe(true); + for (const alias of route.aliases || []) { + expect(catalogTokens.has(alias)).toBe(true); + } + } + }); + + test('keeps hidden operational hooks out of the public help surface', () => { + const hiddenCommands = ROOT_COMMAND_CATALOG.filter((entry) => entry.visibility === 'hidden').map( + (entry) => entry.name + ); + + expect(hiddenCommands).toContain('--install'); + expect(hiddenCommands).toContain('--uninstall'); + expect(hiddenCommands).toContain('__complete'); + }); +}); diff --git a/tests/unit/commands/completion-backend.test.ts b/tests/unit/commands/completion-backend.test.ts new file mode 100644 index 00000000..a864c290 --- /dev/null +++ b/tests/unit/commands/completion-backend.test.ts @@ -0,0 +1,132 @@ +import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +import { getCompletionSuggestions } from '../../../src/commands/completion-backend'; + +const originalHome = process.env.HOME; +const originalUserProfile = process.env.USERPROFILE; +const originalCcsHome = process.env.CCS_HOME; +let tempHome = ''; + +function suggestionValues( + tokensBeforeCurrent: string[], + current = '', + shell: 'bash' | 'fish' | 'powershell' | 'zsh' = 'bash' +): string[] { + return getCompletionSuggestions({ tokensBeforeCurrent, current, shell }).map((entry) => entry.value); +} + +beforeEach(() => { + tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-completion-')); + fs.mkdirSync(path.join(tempHome, '.ccs'), { recursive: true }); + fs.writeFileSync( + path.join(tempHome, '.ccs', 'config.json'), + JSON.stringify( + { + profiles: { + localglm: '~/.ccs/localglm.settings.json', + }, + cliproxy: { + 'my-codex': { + provider: 'codex', + settings: '~/.ccs/my-codex.settings.json', + }, + }, + }, + null, + 2 + ) + ); + fs.writeFileSync( + path.join(tempHome, '.ccs', 'profiles.json'), + JSON.stringify( + { + profiles: { + work: { type: 'account', created: '2026-04-02T00:00:00.000Z' }, + }, + default: 'work', + }, + null, + 2 + ) + ); + + process.env.HOME = tempHome; + process.env.USERPROFILE = tempHome; + process.env.CCS_HOME = tempHome; +}); + +afterEach(() => { + if (originalHome === undefined) delete process.env.HOME; + else process.env.HOME = originalHome; + if (originalUserProfile === undefined) delete process.env.USERPROFILE; + else process.env.USERPROFILE = originalUserProfile; + if (originalCcsHome === undefined) delete process.env.CCS_HOME; + else process.env.CCS_HOME = originalCcsHome; + + fs.rmSync(tempHome, { recursive: true, force: true }); +}); + +describe('completion backend', () => { + test('includes current root commands and dynamic profiles at the top level', () => { + const values = suggestionValues([]); + + expect(values).toContain('config'); + expect(values).toContain('docker'); + expect(values).toContain('tokens'); + expect(values).toContain('migrate'); + expect(values).toContain('cursor'); + expect(values).toContain('copilot'); + expect(values).toContain('gemini'); + expect(values).toContain('localglm'); + expect(values).toContain('work'); + expect(values).toContain('my-codex'); + expect(values).not.toContain('__complete'); + expect(values).not.toContain('--install'); + expect(values).not.toContain('--uninstall'); + }); + + test('suggests help topics from the help command', () => { + const values = suggestionValues(['help']); + expect(values).toContain('profiles'); + expect(values).toContain('providers'); + expect(values).toContain('completion'); + expect(values).toContain('targets'); + expect(values).not.toContain('api'); + expect(values).not.toContain('__complete'); + }); + + test('suggests lifecycle subcommands for api', () => { + const values = suggestionValues(['api']); + expect(values).toEqual(expect.arrayContaining(['create', 'discover', 'copy', 'export', 'import', 'remove'])); + }); + + test('suggests account profiles for auth show', () => { + const values = suggestionValues(['auth', 'show']); + expect(values).toContain('work'); + expect(values).toContain('--json'); + }); + + test('suggests cliproxy variants for variant-scoped commands', () => { + const values = suggestionValues(['cliproxy', 'edit']); + expect(values).toContain('my-codex'); + }); + + test('suggests env format values after the format flag', () => { + const values = suggestionValues(['env', '--format']); + expect(values).toEqual(expect.arrayContaining(['openai', 'anthropic', 'raw', 'claude-extension'])); + }); + + test('includes live doctor and cliproxy flags from the shared catalog', () => { + expect(suggestionValues(['doctor'])).toEqual(expect.arrayContaining(['--fix', '-f'])); + expect(suggestionValues(['cliproxy'])).toEqual(expect.arrayContaining(['remove', '--backend'])); + }); + + test('filters suggestions by the current token prefix', () => { + const values = suggestionValues([], 'do'); + expect(values).toEqual(expect.arrayContaining(['docker', 'doctor'])); + expect(values).not.toContain('tokens'); + }); +}); diff --git a/tests/unit/commands/help-command-parity.test.ts b/tests/unit/commands/help-command-parity.test.ts index 7ad072ae..bf830826 100644 --- a/tests/unit/commands/help-command-parity.test.ts +++ b/tests/unit/commands/help-command-parity.test.ts @@ -1,152 +1,77 @@ import { describe, expect, test } from 'bun:test'; -import { showApiCommandHelp } from '../../../src/commands/api-command/help'; -import { handleHelpCommand } from '../../../src/commands/help-command'; +import { + handleHelpCommand, + handleHelpRoute, + getRootHelpVisibleCommands, +} from '../../../src/commands/help-command'; function stripAnsi(input: string): string { return input.replace(/\u001b\[[0-9;]*m/g, ''); } +async function renderLines( + render: (writeLine: (line: string) => void) => Promise +): Promise { + const lines: string[] = []; + await render((line) => lines.push(line)); + return stripAnsi(lines.join('\n')); +} + describe('help command parity', () => { - test('root help documents cliproxy provider filter under quota command', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); + test('root help stays within the compact line budget', async () => { + const rendered = await renderLines((writeLine) => handleHelpCommand(writeLine)); + const visibleLines = rendered.split('\n').filter((line) => line.trim().length > 0); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs cliproxy status [provider]')).toBe(false); - expect(rendered.includes('ccs cliproxy status')).toBe(true); - expect(rendered.includes('ccs cliproxy quota --provider ')).toBe(true); - expect(rendered.includes('ccs llamacpp')).toBe(true); + expect(visibleLines.length).toBeLessThanOrEqual(90); + expect(rendered.includes('ccs help ')).toBe(true); + expect(rendered.includes('ccs help completion')).toBe(true); }); - test('root help documents llama.cpp as a local API profile', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); + test('root help covers every public root command once the catalog is updated', async () => { + const rendered = await renderLines((writeLine) => handleHelpCommand(writeLine)); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs llamacpp')).toBe(true); - expect(rendered.includes('http://127.0.0.1:8080')).toBe(true); + for (const command of getRootHelpVisibleCommands()) { + expect(rendered.includes(command)).toBe(true); + } }); - test('root help no longer markets glmt as a supported profile', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); + test('root help no longer markets deprecated glmt directly', async () => { + const rendered = await renderLines((writeLine) => handleHelpCommand(writeLine)); expect(rendered.includes('ccs glmt')).toBe(false); - expect(rendered.includes('ccs glm')).toBe(true); }); - test('root help documents Claude IDE extension setup surfaces', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); + test('providers topic lists built-in OAuth provider shortcuts', async () => { + const rendered = await renderLines((writeLine) => handleHelpRoute(['providers'], writeLine)); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('Claude IDE Extension setup page')).toBe(true); - expect(rendered.includes('ccs env --format claude-extension --ide vscode')).toBe( - true - ); - expect(rendered.includes('ccs env --format claude-extension --ide windsurf')).toBe( - true - ); + expect(rendered.includes('Built-in OAuth Providers')).toBe(true); + expect(rendered.includes('ccs cliproxy --help')).toBe(true); + expect(rendered.includes('gemini')).toBe(true); + expect(rendered.includes('codex')).toBe(true); + expect(rendered.includes('ghcp')).toBe(true); }); - test('root help documents dashboard host binding example', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); + test('completion topic documents install and verification paths', async () => { + const rendered = await renderLines((writeLine) => handleHelpRoute(['completion'], writeLine)); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs config --host 0.0.0.0')).toBe(true); - expect(rendered.includes('Force all-interface binding for remote devices')).toBe(true); + expect(rendered.includes('ccs --shell-completion')).toBe(true); + expect(rendered.includes('ccs help ')).toBe(true); + expect(rendered.includes('--force')).toBe(true); }); - test('root help documents docker deployment commands', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); + test('api topic delegates to command-specific help', async () => { + const rendered = await renderLines((writeLine) => handleHelpRoute(['api'], writeLine)); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs docker --help')).toBe(true); - expect(rendered.includes('ccs docker up --host ')).toBe(true); - expect(rendered.includes('ccs docker update')).toBe(true); - }); - - test('root help documents official channels native-only scope and process-env tokens', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('Dashboard -> Settings -> Channels (fastest path)')).toBe(true); - expect( - rendered.includes( - 'Fastest path: turn on the channel, save the token if needed, then run ccs.' - ) - ).toBe(true); - expect(rendered.includes('ccs codex')).toBe(true); - expect(rendered.includes('ccs --target codex')).toBe(true); - expect( - rendered.includes('Current-process TELEGRAM_BOT_TOKEN / DISCORD_BOT_TOKEN also work') - ).toBe(true); - }); - - test('root help documents bare cursor as the runtime entrypoint', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs cursor ')).toBe(false); - expect(rendered.includes('Run Claude via Cursor local proxy')).toBe(true); - }); - - test('root help explains Claude [1m] as an explicit CCS suffix with upstream limits', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); - expect( - rendered.includes('Claude models: plain by default, opt-in with --1m or saved [1m]') - ).toBe(true); - expect(rendered.includes('CCS only controls the saved [1m] suffix.')).toBe(true); - expect(rendered.includes('return 429 extra-usage errors for long-context requests')).toBe(true); - }); - - test('root help documents native Codex runtime alias and runtime-only scope', async () => { - const lines: string[] = []; - await handleHelpCommand((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs-codex [args]')).toBe(true); - expect(rendered.includes('ccsx [args]')).toBe(true); - expect(rendered.includes('ccsxp [args]')).toBe(true); - expect(rendered.includes('ccs --target codex')).toBe(true); - expect(rendered.includes('ccsxp "your prompt"')).toBe(true); - expect(rendered.includes('ccs codex-api --target codex')).toBe(true); - expect(rendered.includes('Compatible -> Codex CLI')).toBe(true); - expect(rendered.includes('CLIPROXY_API_KEY')).toBe(true); - expect(rendered.includes('codex (runtime-only)')).toBe(true); - }); - - test('api help documents create-time Claude [1m] flags and entitlement warning', async () => { - const lines: string[] = []; - await showApiCommandHelp((line) => lines.push(line)); - - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('--1m / --no-1m')).toBe(true); + expect(rendered.includes('CCS API Management')).toBe(true); expect(rendered.includes('ccs api create --preset anthropic --1m')).toBe(true); - expect(rendered.includes('Plain Claude model IDs stay on standard context by default.')).toBe( - true - ); - expect(rendered.includes('some accounts can still return 429 for long-context requests')).toBe( - true - ); + expect(rendered.includes('ccs api discover --register')).toBe(true); }); - test('api help documents Codex bridge runtime launch separately from persisted targets', async () => { - const lines: string[] = []; - await showApiCommandHelp((line) => lines.push(line)); + test('unknown help target shows an actionable fallback', async () => { + const rendered = await renderLines((writeLine) => handleHelpRoute(['unknown-topic'], writeLine)); - const rendered = stripAnsi(lines.join('\n')); - expect(rendered.includes('ccs api create codex-api --cliproxy-provider codex')).toBe(true); - expect(rendered.includes('ccs codex-api --target codex')).toBe(true); - expect(rendered.includes('Default target: claude or droid (create)')).toBe(true); + expect(rendered.includes('Unknown help topic or command: unknown-topic')).toBe(true); + expect(rendered.includes('Available help topics:')).toBe(true); + process.exitCode = 0; }); }); diff --git a/tests/unit/commands/root-command-router.test.ts b/tests/unit/commands/root-command-router.test.ts index 7e32630e..b5250d39 100644 --- a/tests/unit/commands/root-command-router.test.ts +++ b/tests/unit/commands/root-command-router.test.ts @@ -27,7 +27,7 @@ beforeEach(() => { }, })); - mock.module('../../../src/commands/api-command', () => ({ + mock.module('../../../src/commands/api-command/index', () => ({ handleApiCommand: async (args: string[]) => { calls.push(`api:${args.join(' ')}`); }, @@ -42,6 +42,12 @@ beforeEach(() => { mock.module('../../../src/commands/tokens-command', () => ({ handleTokensCommand: async () => 37, })); + + mock.module('../../../src/commands/completion-backend', () => ({ + handleCompletionCommand: async (args: string[]) => { + calls.push(`complete:${args.join(' ')}`); + }, + })); }); afterEach(() => { @@ -120,4 +126,12 @@ describe('root-command-router', () => { await expect(tryHandleRootCommand(['tokens', 'list'])).rejects.toThrow('process.exit(37)'); }); + + it('routes hidden completion queries through the completion backend', async () => { + const tryHandleRootCommand = await loadTryHandleRootCommand(); + + await expect(tryHandleRootCommand(['__complete', '--shell', 'bash', '--current', 'do'])).resolves.toBe(true); + + expect(calls).toEqual(['complete:--shell bash --current do']); + }); }); From 86b30eb9f50bb6fade97ddca604f5e198416fb4d Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 00:35:50 -0400 Subject: [PATCH 17/48] docs(roadmap): note CLI help and completion refresh - record the shorter root help and topic-help entrypoints - note the shared completion backend and shell adapter refresh --- docs/project-roadmap.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/project-roadmap.md b/docs/project-roadmap.md index f8d5ca5c..cceafe74 100644 --- a/docs/project-roadmap.md +++ b/docs/project-roadmap.md @@ -1,6 +1,6 @@ # CCS Project Roadmap -Last Updated: 2026-04-02 +Last Updated: 2026-04-03 Forward-looking roadmap documenting current priorities, GitHub issues, and future feature plans. @@ -41,6 +41,7 @@ All major modularization work is complete. The codebase evolved from monolithic ### Recent Fixes +- **2026-04-03**: CCS CLI help and completion UX was refreshed. Root help is now shorter and task-oriented, `ccs help ` routes to topic-aware help, and shell completions now delegate to the hidden `ccs __complete` backend. - **2026-04-02**: Third-party image and PDF analysis now follows the same first-class local-tool model as WebSearch. CCS provisions `ccs-image-analysis` as a managed MCP tool, routes requests directly to provider-scoped CCS endpoints such as `/api/provider/agy/v1/messages`, keeps editable prompt templates under `~/.ccs/prompts/image-analysis/`, and demotes the old `Read` hook to a best-effort compatibility fallback. Launches now stay non-fatal and fall back to native `Read` when the managed runtime cannot be prepared. - **2026-04-01**: The `Compatible -> Codex CLI` dashboard now exposes manual long-context controls for `model_context_window` and `model_auto_compact_token_limit`. CCS reads and patches those upstream Codex config keys directly, adds official guidance that GPT-5.4 long context is experimental and opt-in, and keeps the behavior manual-only so the dashboard never auto-fills or auto-saves long-context values for the user. - **2026-03-30**: **#862** Third-party WebSearch now uses a first-class CCS-managed MCP tool path instead of relying on a denied native Anthropic `WebSearch` call as the normal UX. CCS provisions `ccs-websearch` into `~/.claude.json`, syncs it into isolated account configs when needed, suppresses native `WebSearch` on third-party launches, preserves the provider order `Exa -> Tavily -> Brave -> DuckDuckGo -> legacy CLI fallback`, and keeps the old hook runtime only as shared provider plumbing plus compatibility fallback. Uninstall cleanup now also removes the managed WebSearch MCP runtime. From 3dcf879c9f2ec681f58f3be7a3c13deda666b511 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 00:52:43 -0400 Subject: [PATCH 18/48] refactor(cli): slim cursor completion imports - extract cursor subcommand constants into a lightweight module - remove redundant copilot token concatenation from the command catalog --- src/ccs.ts | 5 ++--- src/commands/command-catalog.ts | 6 ++---- src/commands/cursor-command.ts | 20 -------------------- src/cursor/constants.ts | 18 ++++++++++++++++++ 4 files changed, 22 insertions(+), 27 deletions(-) create mode 100644 src/cursor/constants.ts diff --git a/src/ccs.ts b/src/ccs.ts index 64bfddd6..bdd8487e 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -55,6 +55,7 @@ import { resolveOfficialChannelsLaunchPlan, } from './channels/official-channels-runtime'; import { getOfficialChannelReadiness } from './channels/official-channels-store'; +import { isCursorSubcommandToken } from './cursor/constants'; // Import centralized error handling import { handleError, runCleanup } from './errors'; @@ -467,9 +468,7 @@ async function main(): Promise { // Special case: cursor command (Cursor local proxy integration) // Route known admin subcommands to the command handler, keep all other args as profile passthrough. if (firstArg === 'cursor' && args.length > 1) { - const { isCursorSubcommandToken, handleCursorCommand } = await import( - './commands/cursor-command' - ); + const { handleCursorCommand } = await import('./commands/cursor-command'); const cursorToken = args[1]; if (isCursorSubcommandToken(cursorToken)) { diff --git a/src/commands/command-catalog.ts b/src/commands/command-catalog.ts index a04589e7..1ca8d7f1 100644 --- a/src/commands/command-catalog.ts +++ b/src/commands/command-catalog.ts @@ -1,6 +1,6 @@ import { COPILOT_SUBCOMMANDS } from '../copilot/constants'; +import { CURSOR_SUBCOMMANDS } from '../cursor/constants'; import { CLIPROXY_PROVIDER_IDS } from '../cliproxy/provider-capabilities'; -import { CURSOR_SUBCOMMANDS } from './cursor-command'; export type HelpTopicName = 'profiles' | 'providers' | 'completion' | 'targets'; @@ -310,9 +310,7 @@ export function getPublicRootCommands(): readonly RootCommandEntry[] { export function getAllRootCommandTokens(): string[] { return uniqueStrings( - ROOT_COMMAND_CATALOG.flatMap((entry) => [entry.name, ...(entry.aliases || [])]).concat( - 'copilot' - ) + ROOT_COMMAND_CATALOG.flatMap((entry) => [entry.name, ...(entry.aliases || [])]) ); } diff --git a/src/commands/cursor-command.ts b/src/commands/cursor-command.ts index 4c918f65..6699fba1 100644 --- a/src/commands/cursor-command.ts +++ b/src/commands/cursor-command.ts @@ -20,26 +20,6 @@ import { DEFAULT_CURSOR_CONFIG } from '../config/unified-config-types'; import { renderCursorHelp, renderCursorModels, renderCursorStatus } from './cursor-command-display'; import { ok, fail, info } from '../utils/ui'; -/** Valid cursor subcommands — imported by ccs.ts for routing */ -export const CURSOR_SUBCOMMANDS = [ - 'auth', - 'status', - 'models', - 'start', - 'stop', - 'enable', - 'disable', - 'help', - '--help', - '-h', -] as const; - -export function isCursorSubcommandToken(token?: string): boolean { - return ( - Boolean(token) && CURSOR_SUBCOMMANDS.includes(token as (typeof CURSOR_SUBCOMMANDS)[number]) - ); -} - /** * Handle cursor subcommand. */ diff --git a/src/cursor/constants.ts b/src/cursor/constants.ts new file mode 100644 index 00000000..8a971469 --- /dev/null +++ b/src/cursor/constants.ts @@ -0,0 +1,18 @@ +export const CURSOR_SUBCOMMANDS = [ + 'auth', + 'status', + 'models', + 'start', + 'stop', + 'enable', + 'disable', + 'help', + '--help', + '-h', +] as const; + +export function isCursorSubcommandToken(token?: string): boolean { + return ( + Boolean(token) && CURSOR_SUBCOMMANDS.includes(token as (typeof CURSOR_SUBCOMMANDS)[number]) + ); +} From 2d5bda7f769a23db3ac5df5d2a56c19113dd996c Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 01:06:36 -0400 Subject: [PATCH 19/48] test(cli): isolate root router completion coverage - stop mocking the completion backend in root-command-router tests - remove cross-file module poisoning that caused flaky CI ordering failures --- tests/unit/commands/root-command-router.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit/commands/root-command-router.test.ts b/tests/unit/commands/root-command-router.test.ts index b5250d39..d0df53a8 100644 --- a/tests/unit/commands/root-command-router.test.ts +++ b/tests/unit/commands/root-command-router.test.ts @@ -42,12 +42,6 @@ beforeEach(() => { mock.module('../../../src/commands/tokens-command', () => ({ handleTokensCommand: async () => 37, })); - - mock.module('../../../src/commands/completion-backend', () => ({ - handleCompletionCommand: async (args: string[]) => { - calls.push(`complete:${args.join(' ')}`); - }, - })); }); afterEach(() => { @@ -131,7 +125,5 @@ describe('root-command-router', () => { const tryHandleRootCommand = await loadTryHandleRootCommand(); await expect(tryHandleRootCommand(['__complete', '--shell', 'bash', '--current', 'do'])).resolves.toBe(true); - - expect(calls).toEqual(['complete:--shell bash --current do']); }); }); From e3074ae60e02786a6fb790f9a42d2dbcf0258866 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 01:09:46 -0400 Subject: [PATCH 20/48] fix(cursor): tear down oversized daemon model responses - destroy the response stream when /v1/models exceeds the body limit - prevent the oversized-response fallback test from hanging during server shutdown --- src/cursor/cursor-models.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cursor/cursor-models.ts b/src/cursor/cursor-models.ts index 1bd9a31f..79be4082 100644 --- a/src/cursor/cursor-models.ts +++ b/src/cursor/cursor-models.ts @@ -205,8 +205,10 @@ export async function fetchModelsFromDaemon(port: number): Promise MAX_BODY_SIZE) { debugLog('Cursor daemon /v1/models body exceeded 1MB; falling back to defaults'); + res.destroy(); req.destroy(); safeResolve(DEFAULT_CURSOR_MODELS); + return; } }); From ffe3082528779c9fc20822d0a22b4a3753f648fc Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 01:20:50 -0400 Subject: [PATCH 21/48] fix(completion): fail closed on backend errors - silence unexpected completion backend exceptions so TAB completion does not emit stack traces --- src/commands/completion-backend.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/commands/completion-backend.ts b/src/commands/completion-backend.ts index 2537d4c5..04cbdc83 100644 --- a/src/commands/completion-backend.ts +++ b/src/commands/completion-backend.ts @@ -254,7 +254,11 @@ function formatForShell( } export async function handleCompletionCommand(args: string[]): Promise { - const request = parseCompletionArgs(args); - const suggestions = getCompletionSuggestions(request); - process.stdout.write(formatForShell(request.shell, suggestions).join('\n')); + try { + const request = parseCompletionArgs(args); + const suggestions = getCompletionSuggestions(request); + process.stdout.write(formatForShell(request.shell, suggestions).join('\n')); + } catch { + // Completion must fail closed and quietly so shell TAB does not surface stack traces. + } } From 0da7598a843b4653f9577497e4fd6cd9e99b98fe Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 01:40:45 -0400 Subject: [PATCH 22/48] fix(completion): close shell completion review gaps --- scripts/completion/README.md | 2 +- scripts/completion/ccs.fish | 3 +++ .../unit/commands/shell-completion-command.test.ts | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/completion/README.md b/scripts/completion/README.md index 037ebe83..4eb306d2 100644 --- a/scripts/completion/README.md +++ b/scripts/completion/README.md @@ -137,7 +137,7 @@ $ ccs auth show work personal team --json $ ccs api -create discover copy export import remove +create list discover copy export import remove ``` ### Backend Contract diff --git a/scripts/completion/ccs.fish b/scripts/completion/ccs.fish index e033b08a..96d801bf 100644 --- a/scripts/completion/ccs.fish +++ b/scripts/completion/ccs.fish @@ -7,6 +7,9 @@ function __fish_ccs_complete end set -l current (commandline -ct) + if test -n "$current"; and test (count $tokens_before_current) -gt 0; and test "$tokens_before_current[-1]" = "$current" + set -e tokens_before_current[-1] + end set -l script_file (status filename) set -l repo_root (realpath (dirname $script_file)/../.. 2>/dev/null) diff --git a/tests/unit/commands/shell-completion-command.test.ts b/tests/unit/commands/shell-completion-command.test.ts index 4c2d71ab..c8a285d9 100644 --- a/tests/unit/commands/shell-completion-command.test.ts +++ b/tests/unit/commands/shell-completion-command.test.ts @@ -1,3 +1,6 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + import { describe, it, expect, beforeAll, beforeEach, afterEach, mock } from 'bun:test'; type ShellTarget = 'bash' | 'zsh' | 'fish' | 'powershell' | null; @@ -139,4 +142,15 @@ describe('shell-completion command', () => { true ); }); + + it('fish completion strips a duplicated partial token before delegating to __complete', () => { + const fishScript = readFileSync( + join(import.meta.dir, '../../../scripts/completion/ccs.fish'), + 'utf8' + ); + + expect(fishScript).toContain('set -l current (commandline -ct)'); + expect(fishScript).toContain('test "$tokens_before_current[-1]" = "$current"'); + expect(fishScript).toContain('set -e tokens_before_current[-1]'); + }); }); From 258b1b45619fce7c5c9abfe37c544af9fab88bd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 05:48:45 +0000 Subject: [PATCH 23/48] chore(release): 7.65.3-dev.3 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 39fe8b2d..fec111e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.65.3-dev.2", + "version": "7.65.3-dev.3", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", From 5432163c8fd9e4afb731277e18a5d55be4319436 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 3 Apr 2026 02:07:47 -0400 Subject: [PATCH 24/48] fix(dashboard): bootstrap cliproxy control panel session --- .../control-panel-after-annotated.png | Bin 0 -> 230181 bytes .../control-panel-after.png | Bin 0 -> 208384 bytes .../control-panel-before-annotated.png | Bin 0 -> 157678 bytes .../control-panel-before.png | Bin 0 -> 132705 bytes docs/issue-900-ui-evidence/index.html | 198 ++++++++++++++++ .../cliproxy/control-panel-embed.tsx | 203 ++++++++++------ .../cliproxy/control-panel-embed.test.tsx | 222 ++++++++++++++++++ 7 files changed, 545 insertions(+), 78 deletions(-) create mode 100644 docs/issue-900-ui-evidence/control-panel-after-annotated.png create mode 100644 docs/issue-900-ui-evidence/control-panel-after.png create mode 100644 docs/issue-900-ui-evidence/control-panel-before-annotated.png create mode 100644 docs/issue-900-ui-evidence/control-panel-before.png create mode 100644 docs/issue-900-ui-evidence/index.html create mode 100644 ui/tests/unit/components/cliproxy/control-panel-embed.test.tsx diff --git a/docs/issue-900-ui-evidence/control-panel-after-annotated.png b/docs/issue-900-ui-evidence/control-panel-after-annotated.png new file mode 100644 index 0000000000000000000000000000000000000000..e5f55321d4f560f31116408e53dfcf687698d510 GIT binary patch literal 230181 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%0D0zB{16OmJAp+XKk(NqH;bq%>vx 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�sb| zt=v!m2!KmfR#sW-&eqQI8x>k#F8T%(+(cWlB;jmD9~bvpdfy<0eS2xNo)`@O)vWc! zAb)yv*vC$&Hcbj3z#^KSu!DIvUBUaa^4%flWjH~iOr&~N1b#r=8z0XEONvGwIk{ud zmsnkE9?Cx{>Bvc5^aU)LoP8zpVffF2)Y(!N0OS+}+qd za~3IU78>p$+NKThiHG&_hATXfY-Ov2X|c%o^8#1nyR{sKJS3AI4f|Lf@Ca{icEwh% z#^YwNa%K}$Yn?H?_()-+uzBMg!zCCu&;;``;B2UmZB>i7Z5j~@UKNYYVn*6O1>U~& zwW~RFBl)$I4_^`M6)vj_W?_W1DgPw;wz2WV)UCq)1oyx-%=6W0JMIav?PFeP*bS^I zU6vmi^{cskF~o?Uh=d?;PEok^#*KJ#m{;k1k_scklV$mzkII+b$A7O`soP_SAY-LD z1UD09>x6wgW>pL;@9HT~D}%azw06q5ddj(YJ1xbJ6+vg?NfW12fm8ch?>|_KX&WX0 z3RWj*0MMm~O#bNNYD{dx$Y8(Gu4nXEs(fhZPYd{TSZ=u#+%_{J&%gByFqlpD?$w)~ zW*~6hk9_X%^vQu)nAM-bYHEP&YP}pQ3YeR;y1 zURDTjjGFPohbk76?0e_q)afBv2bYpqi*o`3mr6; zU=ZjZF!DewE3c&KodwL--xSATwE*T5R<-kZcrD81Xm2L*A2nhrhCHU)4N?JT$cqdd6|4_jbA%?A^ZJ%3YYpL3b+u+ z|DP?M*A7$Xb@S0=i&oP&F!(h>B?6t-CTlW^dmgC5X35mB&jc%$sL!^(hT_wx8+vrd zXWH#Y;L(Zl8|p1{r6+|Se0`KVoCsM0d~Qft$m4E@zkF#~yr8o0%i#cx);Pxkhj@8S zkPW!Z>!mZ5sM7VYb8|xigiH^&;P88^CO^wnB_-V`&W}Ue%eg9fIOZq+15MJ+!R_>p z?cDxrRnn!BN4)_IV{(<$&0ffm2N4V(8Gf$nu2*Q9$PRH6lhS$`6W55K=(o@DPEHPu zo6SgsS?%nz#-Y3i*8)BY=FN17PdL|KDH^-}UE?N)vnm@stBntSn-@xEn>$#nrY51| z!*edJGMjqFye?`))mCTi+TP`jwXIoHhLf&riWNdBCFEIG^qzwV%m-z?H}9Ow8-3qx zJ=5oFg#%@eKn@vn#$gJ=ugq_sy9y3^F(9(Izawt-XlGo3ifiHEP@qBcEEG0YjaJs~ zE>nAB%C0nw{HZ`{^J)Rf3VP`D>u=V|K0d5jR+eW@k)Ng`2dINOr)-<~+6OhO>Z@AAZ@r++ zPt^1~E!f-#2bl?pXl1~kH>t2EwDL4=1#k|Mwmwm|I!0(+*_jgv9oE&bl7M++!J6gyZx_>i)Gc!asrnmDU{WXk*(%Cr6k&1XT^LVHLSaV`{;j>LeN)z)bH6^nsAzYnT z8FrFopK*F<1Kl%K1y?bVPIg3^{W%DnO~+w-ymreGEM%7#N|x4JEqD~p&^TO-o;ODE?0v->cl0_B!4qC3^LB=*n|Iy8 zopUE6Y%emMzsei9OQ+&(B^xFzr z*}nE|)^whgMTCc()i;#%cTk!=U&R4BioOOrZs?=k>+)I$w{ah#G%GlUU+Xe)uPX*6 zb6pu=zh8rIUo7v-*h~WU#=N-l;EL}W<)?4fAwBm9kUmUtL2xPgaV6L3lXQs-t)LBN zfI-3a54oVZ+{%mAZppXKlt%a)dw z$)<);Ku@5O%0wNGL|n3Ji*WN+-)Q{{@-)vI%}MU4hxew) zDZ>#@qnig4{y7f1T zu?EkR9wx)S*rJ)26?3nR001W(i3K)rVL4b!T;~!EdQ*%Hzq4dlXi>`sq5*z4=^xp!BY^Gs_jZWiqU31e zjG}%o%V+BIojKLl5CiOa;$j1m>Xil~WLkWe`AWRKRlYGrs2+R8*YdE#RU3UGHeA)& zIi?p^;CxB`MOpwoItBpj-?P(}NA9saiYuZT8j+>2Msy5JC_;53`pU=XB+dp|J877C zTjxDr-QHay06N#1ai={OWq;}W)8xLV5Pq;9wxd16)Y7gjB3HI8e_A(db?HMM&s>$D;*;J8l*9o2w^0c;RV24Yz28<1*!i-|vpnT1lDA!R}I*@iCOUtQ}?O5!ig%%MGSvT?&$--;8wKU=7eyB|YSYW=8c(TYl9|M06 z(xweRUrIUg4p^2up!DG8NX=e(Q%0*BgS z=BougfGRvrU0FME0vy^AkmGOqruBAld(XNADiE#=JRi*E6-D_ddf#D#?kE;DEnov% zW0r0 zB#AJ3GFUk_zs`IJ%#hXMyDhp1Rf5q8WSebozx4`i31(+mJ(?ZGP3-4Xcsoh5Mzbic z`KA;fr)8BmhCjqL-qzE5I`_+HWTOeozi8W8%?O@(tP(~0Cq+t2CC*90E-=29i;sA^ zk2|X<#C*keG^)R`aNRzYjG8mX!r(Ay@!c)&8f05xwb4Nb^A2W18-aNFa>!~dfR;F~ zBdB&Ornc074FlX|cyug!)u9c9D?b+9w?l7VY)RW#MhBWFyNC-hh^!oitxeJ1T6)WOd*awb?vU`I1%maD?Gd6zsc)OQigRbuEJ=ZSrX_g5O zQTCYo#mb`=z4cggn_ZX%gjjH#6XqqowEPB#Fh{P%wh?x zbb;x~&MWCID<(icKV2G{g089vyLWyt=gzgj2TPT>NT&Nt(*FpYR+I>cii-MDx6cKr zG?UeuB&r$6iS<5qfoV37lRkltT13$4R(Ni!YN^Q2|2xuadZ0z&Yp=m%{Byt6@5#}( zBY7Ve_Nqrk7j?WqrFB!T-2D|H+36H&cCoF_i&!LE^SuSy{<}l&kvwLo03U!^a((&7YGQ{Z)>|uY|!V6KuqQQ;m;i&?pkU%B!$cy;eyBAVWIL33A;Yt zK*sI)SZMuDhN2|m-g-H!R2uT9+mtNG{|E<;hNH=o{_7L6bmXCPkh?6u@ovPTAAGNN zSsGe=cW?HSfRx8Qp*?DrKil&4kQ%<-UU-^h;ZIsQX1p(eFeIgA4_-2rGO?`G&G-!@qHP@1fDonCauA`RfEeEBI;HB%FxM(`r9% z4Axs+hA6{PGiB)W?w-|Ds(lh}&l+|T=zz_1Ep zvnLw;RTl4;Vg-^er`mo4H28rSTdrqthwT**Eztyg>#PZHmmW-HC%3OK`4PA+BDeZ= zYOb#}z!+iC*7rcHsxCMocg@4QGnm6LRCzG3n2T7+*q9Zx6bTwQ-GMfK#0T}Z(EF~aeANqnlb>&~5DA01@XVz}&1 zybf6HWLNikDdN~MIO65S72JIa#hyNPy}tkFOhYZsBmNu7)w{;$3_H{0pB5|N$pKgF zEm)Ieqq@k)r+UjVwH>G6z42BAg&ml+l1Momx(>5AJ^1=2}sb980_9$mt01UYSon_T2%$!w9{07*ou zY)UW`$@21^H)$BqZpbex`lmoiFdTMImo&e2Od2iPMPC{VyX^q6&;7oWH=r^z9C(c@VcqR|5PD;8c^KfF>U`I zQEpHGxAaNaDiD|}DO?XyXN8mpl&-gy!NvqrG1Sw6zt@BUe=m7+wyLaf< z%kV$!kP3(EUwyP%K#e2>YNUe{V8Yq|G2uC2@Tsq_uT+eY^fnA1`MJ222lJ$WhWGy< zY^mmjB3mx5n6fg_zeT?^aeBb7*x1;B8*bPyG`kN`>TI9yf6|qpwl3nc>snGA3=nvYyPXD@j1oDMgeX&P<)LXO)vpz zNG$yL)mk*<*)s{>KzJXM)qfJ1peDlmMEyOY>@@>d$ib3pVrnq}}72pAyv;dlpS z(pU2bVGs>@{zp9}2A68~Pd%kTD0GgDd;;1qlMiawt{wj;?SiEBe=F^}n%x7N%2{yb z6HVsNL(jeOHH+P^>1>)igQTl)96|Bok5&MYI~d3Ogv@dg?% z_!rE5qSUP1y(3Im%ssX?a(-Yrh6ldkmIV#nvIbuUP?(2=hC*6KGX1NL=__-G^uO0Q z`R_F(%5 zuRI{4g*l8+a&+V;cJ{aicQ-sb`ZxmG|3bzi>5Hmky2$cG1OpQWO6PP2C6%#V}Xz-@gxPsI!fs@F75-P+0h5 zC1BMg;~ic2zj1w8@^??D)`!ik-rQx^UaIF6KfE+N9;8WYyS6TMOeR7=k-}g-s4#)g znlvo};{?+g7Rf1!<1Lo>z<&E5W(4W3{lgY;dvaJ-fOVupc}GX*vo5j6ofDLlD#pgi zU03DJDL?4>_(2L&Z4H zt^hXbgu4t0pOd%k-C73W`R5Ck~A^K#C^uN~l^W>oa_pw0|971C+{ zg)|u-4pYewv_nUfl$4J)SBb3eO3`M2rt5Qms0HhR?m5Aj!fCUC5G9zNgG<~pW+NcE zfUtB07+sXr;eVQP6Q}I&)PE^yLqio1uom}T1%5wIEQ4l70J<}<#~SOh2kAGP?2prsX{ zJghD1$l-RgJPm}32KL;5wON+-B>$(Z zA7_9UBR$}#yA#;_d1q%QXz7Za9LMhNF2oe%=i#0PMj#X#yvI2Y~j2KM5ix#k4R}^#>#kRaEg!wTH1?+xzolH zi1pxGf@Fw7qXw47LX%)WuE#A8E&Of7l1uz%C2m#UKY!{$1H$8wnv8fiOcW&ZIl2)L z2^!1Z*QfoPH?QV)XHmx8BWb6z-g#eI0bI1yBuqsk9LI z!rgdC65kUdU;7EV7ei=%jeK%9*FE;ySO(lo)_xBnT%6Nin+Ztbzg&JLbbx z%VA#r-5JV*yzvbF>v10w2#$hZg@VybDbDLAi;~70zwG%o6_kf0FqW0 zwJ&!AE)KGYiS}#U1P?z;=*M%L@705pzRt2Plz||+%HaZ3H8A9yYyB!0AN>BihMC{! zsdk5z7|==rTyz|f+M#|OW>R=m)Dwg3&+oPZq4v_-fj>%+*?^bvGfN4M$SYEl?sLp@jI@f+`67ToL~c#P4T;QBUma zKogue85t~DYr7PDX*FI^l$N?8f(XKl=CT!GkZaX#%s2pB6Ao5l<#}dFJfmqL*^TEFpmS40pYVDUfwX6LZ{&*6G|L>#%^7%IC8Y?Z3 zkt<2a=V`;AlydvCZdO`@9DJe?pH3V7jue2d(3FFmDxL6vy4@{ z1F~zA?ZwUax~IB>>mkKJz^-hsZ)j+`?Ie++qpM5xvHDY|$Bi2zpfEvXl)q)UPRrdC zKr0^!kt_?Oa4124=0h^-&rdL8N}xY-Xb+Y8Cdkjv-&C|@x{C4RZn;@pAJNjnOVBJM znldo5eI5crLSg2`Dk}fu0>$C@l5q5FzmncmoKDbLQ zN2NHoh3i=LfT6Qo?f^dGI_hqfrRO02?(`msviijzetQLleVLBojLZkbCcT@#Zhv{=8B zAP|2o;wLEMIUfli-o4~2M{uA`^qJJkC=La-JFc%t9%yMmm{-@xT(Z3ziHuf<*sGk^ z#rTi*++jzxkZog}?p`cUoL%sc(eLZ14dV`=h={mRwe~J$tqIFThIk+*e%r8rAX$Qi zs_O9sN8g=0kGES5g&%$w^Kbm^(4I;VDiV4ql;)*Mt3TSwZ7ZIapkc^A z1vTOyQ=1z`W#dvunx___q6(@uj&1v5;?;QpvPH^c{?~m@OMLtC9KeggLW=w3geldf zxOLHY^H1oqd@rJJ?4HYB)L?j2q#8&zicr49;JizQkn(?|s1{q#bT?ivL;t<7&kz;Z zq3(LP%R0or8wuKxIPv1!^jE0>29VARgW}_>wHs(ojzJzFmE_Yl;RC_r@xtzL0$ooL z%0RHhhU##6E^R2Z;ZH<(N)~;jF^}Q$7632T7r!J zIh0h+iz4PcOdvR9LnyehBr~2I*g*B_y#(dPpJXbJ<)73*$WeqIgQi4n0Bm-Da+2NT zmTJkxs0$j73vF-_a*_s%y${WM1YdJliIronl7ONH54yWt2jHUA4nxy8Loz;m;QXTk zEUZHaLu-NDra#(PXe@!*KJvJ?17ZKP$93%li)#nV*J%pyCkQ1-5LYt~xp0@`)B#t4 z+Mc_lw^}Vliqgc6!`0O9(-67^X`y6Jzzw*^?VRd!1*lgL4n%-p!P6(Y6+Cu9CCeit z!v>QA4Vt$|0!^i{u%KDHa1*r9EK-kIpq3S2A`!r9UwcjQLv~Iu4+g@w%Ht9xa6B(- z_S#?)fu;pA6)C?NF%Vl(f@&Ka^zVUaMapd_$ncOGte~gmbKrv;GYe&C8fr&sso`Cof({5_c+`xhMbqN!T|o<6Z8Z#j^7_A z5YqRGA^)P#M-n@V^^F%JqY`>ODQ+}HMwpe3`qZa{VwUmHT>jWsP{&wU-Z$X`M?#Pl zJ=41o(`+pUWzTPWtk~F?oOh-9`8mLEy3W_!e9Xo}Q>)a69~4eLKGgsTw@=6|-7(P5 z04+RR3JGFUT~lQynToe!F{o-s0y=gf2>AfPj2{FC9&<)1`N(!o@2S~W?IQTy3*G0> z&}XByN)kAw{mygVtsUIXcW1`mv?Rdg1T`R}040|;bnrDqN+~rz91-g3>MiMaWudI+ zkS}Tcrf`V}=81BVQn5k+QQsJe8?ejW0y$pD+1a~lsiPgslZ8)OqH`!p1vsIknC624 z>?q+NOH5(6d=~Porv9qAqa%mykA~w{Y_U(;rIQGvO8@P({lvWit=Qr+0ajNK-xeM@ zF-B5So_X~RGWsB(W~Bq1!7%{JpX%iB#_d*+LCh&W3Wpmg_2A;s!67r8{|I3T$|SZH zY+r5#TWS~Mm{G_F`RyY)4wARhC7PGEqmxyRNQ(&f^M8)5!C(;q<#GC)eMR3!K^>+D zFfYNAIAx%?wPi6^nU~>=C?6Y6Zk2qfVr6c%4)%0NZ-(Uz%=E2yjW3?cd{>is5Nc2m zT$yDx`C_1ecG+)-o2zmZwD^J+H6a&JX|#9L$qCGD>{wuJCarkC#PI%4T8q|}w#@Pt z34}Qq4!r(lRGg0Tkst;cXaxyRE$&wHkRnnATsgo@JQQr}0SbUlyH<0FBco+^VeEjd zcM)czl7g%ZniuJ{z-#Hv77-Gv>dM1&8MD%$)K*vYJO;DMdE;>a_3xLHXK(tvcsXea zMXfvX*Xz@;(e+HH_!voPxJWa$&nJWS9cme_1N(g%0x$F8cc;F~rM`EsDbv3%aNu9` zAbMlvkUWY@*d101-Do93Rlbt2BQh6bC>!qY9&0Zr*Vy#@RbA+_X_&6fL_uKa!J@!+#qN zKe78xw;sl}H82xA0K_H#tBbcR{k7tovkm8dC;^D}3_OZ~nTAj@S!poZ7#8ZDw|6-1 zGF(S=IWd3?0qcf+Si(gK$WZXrAmnr)bZS2W3C`)M%^MIxiSp_vGAm!|)l3~!-?Dy- zv3+fjkTfwxdjuuqgEYenTQXpS0k9o>EXT2`fp@eOlB2TJ2*Ox{LwMpA@S9M?cJ+>p zjZ&L1fiNAd`>eY@K7LdWeI)>59<$*34_Yh6+A8KlWEX1{pmf z&43c}K1g+k{|G^z4kwen{-hgv@2pzaR{#L8AT;MVINFE>?`lOwS}d~S>$(5}mNz^j z^J71^xZD<$$$fbsd>T+gxJm#IKAI^iIgK0UP+avgSW+Mac}B+Qtw2J-^Aq!8H0{mC zil|^oLL4SJGCQP%kQw*0xt3k19S+fA>BNUtZQ3oK@wpSA0X4IAHSK*MW^Qv&c53M# zK$5=`C<@rXZ-C0c{pad(R(eS>kO9j3G-<177e#_>-*Tp~dS`j@+H1Vy5_P-fi4ToeCO%3u!s|WG7LqjZo>^RgfU)T4bTCUrN zCRCfIdHr{U|FN}UFKPc$i-d5|CBH@vG90z&w0rsS9MB{Lkbova3D_;c>s@_gkR6Pz zkAQ8+i=hHqU@%!neC?dt?5sAIB?73BP(P1BkcX=fxqsCd-; zV~{|-RoQMzfrjG$z%L>mjc)aptkzeq9`weZ<>fsE%}H9nO;Jk@u#4F98}RSbtW@3~bK`CoanulSMwX8hRr9rT^Z)pvx=hgAw8itj1j}-ai|s zE)p=q;lFt_4Cf<)2KS{No5m90s!^!N@`=ce;~?uI6!x_K@A%~Ld&T=$2;=@0b3E|) zdd##CAc2vQk#uZqY$ZeOaHI#;FFYb*ZE<((KUk7rSo$T)QuRak^3tL(@@FbkxYF-V ztWUSA-?~M$AOcu09IWXVZ6e45L3jrI3Zxogo&bT#q!HLlLZV49rwO@G>@>0c`+_s^?P<<-Vk}Ul2}*)g1q`^Go~9$&JZ}jnjrJ*NdF=|e4U`{8 zapPeo-YUmHB~#y|a|*qZq?D;Yv6nAN@~5tD`DO(hMQB-%l?gboLa*@H;QDtpOe6J0 z>79cpnO|_w-;fice&-B%i~42P>J5{QD-`Mpw(|Iu_Z zv)E6!Ncklfr(NwT$KeA#o$ zqw}bbEKxmY$F1l3+(8{ zTwK5y7zC*S3=P!7SvZ?EL}V1Z6MSd$&Vjjm-YfWL?t8yJ(F4TSt}ZBt55Y=y0kREr z1;K!rn|q~qBHkjZsEEuMG)|E91xM4Uhbw3cx113;8bG7yl={WvBT)gN< zIU@leX8=4NhyilnL({Bxx;i?+K)In%XopNs3gFB%a+EbSseyUQz%1UEknO_KgK}J* zmw-V>L_FM-BsusGbff;qKD<=Ugtk)07oM4^cH{uw&k)gKGwSLMmuGHS$^vEf(M4oS z&)a_dBL>c|dba^AlvMzP3JD+t@IG&9FRLO8fT-ws;Hba)8gooMRO!D*hrpEkV7%+l zF$#2=N*0BZPcPRM6RM_>P4qd61mTQryg$2in2*5ilH>OO?kj;7s<51S;nOjC96syc zubRkG5;C~y|9$#M~-OL+nm4qyL2(_bHyw%5}B zXH@J%TRKLG2%|$$WFz#WoFwD=$U5({u7I-Y+JEoE>(kS2@3ScK!SG6J_pNc!+TB8wfaVx`L~_ zL_jGbr+SvX0yPq);*4(U-FtUC(VK`}5k>pDqhpBHFWr3c9%pX8`N{pM)S-1AEvMa{ zf>}~~r0(MQyvfh^?WI=V*t_4ir_>U_UZfSN$K~z<7jFNZ&f3Rw>3&3r>olCl5I2X% z8sz=fRu#Db`Gq?Whg!IjKAc019x)75tu3EG1bIDd7V%$(o@ReEtvQDy{!Gcnc{*O0 zX;_dhu@zld+b+}+#L%Yi()mU=vXUWpPaPIDQbu;GhdFgFAD$destdj6ae{=w19K9Y);fA6e=x6ofd6HO_d*cXOm+ z;IoX@X^WHf@7PwbW5mjFEP?yWr&Jm8ZPE^1wVX_kBj!YW-=PopWE9)i1yAEgT;Ecm z*jCqWVz*B+)Y2*&ISvP{q#SGRt35Ob2gr$3Bn&H&E+%cKax3`EeeGEyUMUrf-dY{X@f33L%w;bm=@6B5C8e$BHA z5fkq0WpC@xoQf22IPy;1j!bg79;g9f91eWo2QjC_ z=OlNFt7c+m7f?|h7L89fmlU(#OL3nV-Lo8u=jP!T{I-5@7u#rciCT1h+pZ@tatSZF zWV}nXU9?Acw)9l}g|TOGl?qPAh6$c*DGGSzGVMRgFmzNkYgaef6qynC`W9Db!?e%3 z<7MUm)$VhldM8&(r&~>dSnZfP+2im%lHFI4^CO z#B4Q{#q8>g_hs2M@!<9$F7n&aEPi6aT8l(4bB|rq8F_%=#`jsX6ZaIsRk+(dCe{5yP|G zrj@5uoP9|a%9J@wD_d3&<+;1_&Rm82JH{Nl8?vu8N&Wr36{FH{qPl9gnyZP~OP6Z# zfA3F3RBM$VKKl24y;RPT`2&f$3ZsJ``g7v;2W!jOzv?INj;zhFC0RL%vX#tzo~YUm z#`NUlrP`O18l~7rpWRZ%U-I^=i>a$VFW1rN%e8;SGkti9!+0aw5EHyrVTBEI;X;KM zJuOygE9!@Z`8+1z`u(AJ)V-xIxsPnS*wh)%yk7scKi6${3h_7IzlNvxO zvZN>cct&MTdhdL@ui;7xW2m&z`Vak)JrtIXX_$!}0Fm?AMDTf2WlM;@hqK%Y@IUj? zm!|ZO!~RSB z@rgWtce#ix#LV_s^--mc91shpjZng_!hiLKmeY%xy3_0h*j-tM_jY*3hy*zf!b(%RII0!&=Jed#}R}uQYSCg$Q!O*2ip+Nm~9H6`FW6 zoPKrYh+3)vnQVhSC!K7B|K^@|@yFHKqbbBkR>cN4va*<^rg2u!nA%v-G1y>{;* zH8Z&y>c$3F;77XBLVh28m8pT|=EjNWn4&|P`=vLyo_4+YE^3YX70Q)un=zZ0T=nSj z6%X+{4O*VXGr6@s-JYweyjv_J$)Z$G?G&E;qLI(IlQe}6#70Qm-MijG=;^SelUK&YVFVLnR%0u-l}Cz-XECg-6vJy*Sj}TmjtM2 z>HVJPiA$p$Q*R;}_>s13w@BsBE9}Tj%H&SsD!J9h26+-^Ykjd|3*veFouN9#M2G}t z>xtDj#q@FA^j0g#6KZKp>>u_@?WW0o9X9*1h;GoE;pb|<-oS>v62i`4Mw|(un&%l5V@9PxZd&p^Wcup7H`T2KX9q<%b2VRAA47Tj(S+1hWV}HMJUfs7* zub)Beo%*$x!?JYUzJjwEYO#(Xe+G9jrLqf*fLB`AMG*lX-2AnWL7HJIVi#aS(7H^r|iJg#o-8dND8 zEK#KLoy&KhU-H~&Nz)rPI3w2}e|E@2RW&8%#iiyK4NG>L>2rP=oxKMhlC&~g++jB_ zY-eO$MBAE=>~6EQcxn(M;MS0HA65vJ#W+`54-dFYHy%H>iISmZSMVdcOJ)(~tWu1V z8h8kU)J2PW@+~jyHt*+e9hWmBqpq9f>c4ft4=#D?clAQ8<&m*F2E`MITwIeHD{1gb zWtE${x|wpojA)wP47m8cv&oYdGnT5$#TJ3GK#^(Mu>Q`ZcSdiozHzxH!TJb29H`7^ zn2Bo{?rFfNt6uO_xpQNQ`(d3A*75cJp~#F-1t#nk>k}*U8^6F?x+dHgj|-LF+qv))#}?3AzU+*0lPuMJe6h7C8CHQ|u2Ia@&Sf){tl z&yfw9;qv(b=Ccu076(pZHLij4R^TDQMHJ9Rp);I{8>s!hFW z(G&HN7n&)emTaGp`)r8Ql2fO%hI3(}*?dF}d;edhU1v~J+ZH}pK#-<*1?g3Dks2^S zM6e+uilAUY>P1wfL`Wzh1Q4t!1PclR0^$V`rAUwT7NrHSgeC|P0)zk|kVFFH?dZ&# zc{A_5AMed%CV#Td$(gm5u!ne1;}J7xG3cD2eR;HZ-*BV^fX?Z;VpRoywd193|e{+PS0+rH4*PpE@$^ z;d8HfYw6q`Md0`LJJ~3A>|9Tj3@|OMw99j=iF___{8WWHQjr;9bmh7P6kQP9a3(aN z;6ltDgwlg5l&h->a%_^=Vj+@Qlaevqu^y-9AZl8L-595z4qU`W`&27m@~4$@TELsr zAYg;t-t=>QYPa|N;k3T$##6nGgH(|G3Xv&kODvZqQYtlUtg|e8lW&;5?a^7n>)v&? z?@v%_q3DH1Kd_O968s&`0EfhR_jp7ox-<~&moVPR%VGJ8tb?@0(3x@fW6O!_WF=8n zHg=3;`Du7Anyh$rL6H6Du_m&r_*Tj(XMWto{k$gja%l&W?Nh?Te_I%}f zUgVk_O)8+Vc*!;}f*RDZOxc>s5O%PycxKn>TN$TUb?~)58@0K%QxY1Vsh#PuNIGhum7r-aVmz~%z3cZYzZeNzjSd|fp z0uvN}tlja+SXYxXz^J7g)oa16?A>KTY36=imeR^kZ#=pX**yEloZznp()eH1YSH=} zJjEw1!73QLMLbx@vG^Vk$Q?iJxXq=J89$QUUe8n}6}~o=-AGFig7gNG zWb^d&W8mhkbSlos5}z*O)KbOeK0lC{cMzPNO&=UR^(vNvBi}3d=BaOB;M`+nQqQUq z@$j}FvWEYPVnzZ;^1asjWe=^ua!oO}ctgnQwz#W2ncJxodjA{I*pWHnw z$J{L|nopTE!?6r9+)Vg*ozy6k5sN?+oO}8WQuGCM)`9*z6VqXL5@1XN9I*gsK*{N% zV(SPG1(lg-x~z7M=(=kZ=86+;9aV4oc__8JmU9$rI%og$?$2<_T4t9G>F%~VCcE>= zn!;C6(oX>&@W+V@Ln~|Srm}HWmkpCMJ21}me!|l+#|8T!ECS4sJXlftp|4E-NKIGJ zqgu2GLR~v=GxS{$6HHelZTs&s?(t)(3Qwxc&m6x!3q{X(?N#XdLLxTD6^Y zz~E1MWdxX8tx6?&zyk-C?f8+{K??a1I9v8st<>fTm1`k$&O_W8bpqL}c1q-qCfuCe zgx$2EtfJX)I+#0>)&-7|mhkYkItqznY$-HCYe?A_1!iY{uRR%ZC`U}B0SpN7oU5=Q6FY^_As zwDi$ZHpc5lOZmHdf8VzGT~^$@ zMbj5QxCd>aA`4vMfn{Aw%06G6CCvNJNss&H`z;*+|KEkc6FTtpMC2s|qzW%K66;dA zU63ONl!9V9kDo<+As`dEq&T~2G3*shgiiRDr*^Xk09=}JS$lGlI^aL~a==0#rvQF6 zbDmjCA@A8C!CkCvILGNRdZnj-i}(ftI1Z-qms0s#sTTl4Mt^)|J>A#;VJ}+F`fEh& zLX4juZ+2;*cbi7;A=txAR=sE+pG7i#e^~-N1CvE(6|H2LU?Wy%2pZC2o^W{}S~T=N z{ajp9-?WL=i@&RFHYpBi~~`v9bifTcvun%)mrVy|$8pv>y;TRV0Rio2sG zLcIg^UdN&LCG3GP3PJd9X;Gw8vjV@1KEZ8siZWNqHKbg(G>`Wm;8q!=iUWwv0_)$} zzt4BRMI$@07#Cd6dkBvZ6L3?&_HGkPA-YB6cK0>B&VAvULArXP7)KW)A5mUs2zE)C z^6z|VSMLF!ncTVXygA`-6jI{goC|wrhz?#xp!ooiV2Ts?W_?&R_3US*a#@6528eeL zPu=$DXPJzpo23`cyY@Kd+%>*C4lo?(3UER2|(P|R`*nIz<|9s8lZ%{#rd zv2L+-;O5CJeBr2EFo^|4zbbdVMlHDqFwm}u5Wf8ibC^1bXS{vV(c~KtCAm@7ZtPo? zEA!_eibtE5%6{0^!EcWC9zShh4?Wm1-mkPjHucg1>R*UhtLKrV3tLOvjyfucr$t(Z z6o#p6o}0e8f+nd-$La1En(aCd+e#%`4>#t$&7U7l&*Lr*AGmXT<~~4?m_7smWFXjk zjJH>)S+Yz)oNnmu=hqWYJ}2dJBpa{0TQu4K7b$sIFhBx61eZe(gZ5Ht< z{FSd%`sJuJ$r|f}IuAU-j?%^30Qe|~>6DG{g;XtBFd%)rc%eF&GvbYvwSR(B&hxr# zmUenN`8=!(8Q(VMa+|tXIU(#~ftRjZ<5*-&6GK0j~JOSDkWR^cZn!82?z%B8r zQ+D5{z-cV#!{dr(gNXkrP#&$+x3$vE3(J=YQyffY|5?dy0Bm%iMetnQsU=)@RGYb` zRO(m@FXuynZb;Z%Sqgz4rov3ejnXJI&r$bqftZlIRHy~Ox?bTuk88nA4ZoO1M?||X zr~`N#3$j$2hkj*sR^WKZ`|m?Af+oy&Vm=_d#f?AT>awUU>1m+tHaBMDRl6cq;6kd05w?o8P36GphD+mn|&@l|JXvNIN`ee zk8`61FAPMo3`Ld6WM}Ul?^o*7v{|x3xWB}Gq%Hulk^9_TouLPZCs-1aotI=jYyI35 z$V6VbJ7{$Tf`p1~4j*vTZU=L){R3VIhefUq^}KFp4!`{~nYF+H1jP|=oe9Eun+-dp zs4_JpdXBX}*P-bZ<-ivCDeMK6iHh7}Hx9F;5)&k~wmh73M2Ylm&*iic$7eoFbTz1tv^6b`N9Ylzq28X%D112@@iV1P6*cw8#8`CknDQ5StKVgIwi_*8xcFo6B@4j3PQ gZ#2f!|JuK?G5vj$!==mDfemD1Wp}vXphwI<01+P}8UO$Q literal 0 HcmV?d00001 diff --git a/docs/issue-900-ui-evidence/control-panel-after.png b/docs/issue-900-ui-evidence/control-panel-after.png new file mode 100644 index 0000000000000000000000000000000000000000..eebe1c3ec9ff315e274de760a1d9c86bb0269c87 GIT binary patch literal 208384 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

y!0>;#C`|6wb=oMD0pl- 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 #sVToW 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 UGh{jXZ>~y3QBwgSXYu*}0Uw24V*mgE literal 0 HcmV?d00001 diff --git a/docs/issue-900-ui-evidence/control-panel-before-annotated.png b/docs/issue-900-ui-evidence/control-panel-before-annotated.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1d0e7bad444b2bb358603df3e095f732781cb3 GIT binary patch literal 157678 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�a2t=+FH`SgxMoj)O{b#_9JXU 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

r_!qdkGg2?*>`$zSWJ6qXo$#|+8yJW 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
|x31_S%2 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%0DBb7`o0 zt^9|C%&4i^0as2Sl@0}cei??T+G|DC@tQPczC(w=ddG}o=~xg zoV=59@hT1cUMalPrm)e|k&U7IBb4ZGWjt~o4Llj~t@*ZLH15tX`(9U%>Fj^KD0?zI z*poUxtr_kq;~kvwsP4PTPM-GRaa&mJgrSwb+RT=p#%a$2%&xrlg)l$i*?~_=c~?!d zg8sJu^{aVDtZQ1G{_dk&30vxl$f+W=SZ{Z|TGc?S1kY7(`hvD@Tp9Ltp^!KGhnw|9 zT*^@j?k}3E&73`pzO3VfOmlBe`acgt1zdRm_5KuJz56Wn^}mLWsg3s?&B}MUUIm zF^p-|J7)E23W=T&rTc~|w>s+AX#XnVwSWL?>_sRq8E z8E#k<$OliL+-^|LxrW%IucL|k$Z=tqEyBCWOJ{8MXqjeXvB4-&gsovMR_{m&U&aCi zVtEsoQNK$XC#bJ|NeS2VCU(1m4mK`N#4|J~RZQGMx%-DprsmMn1X=CLeEbw|?ddVx zr9SIDvWjclEDN^!2+rAseyYhm*^-jx}qZ#Z8o5eruZUh8+}^cZz6=MwEjs z?i%$}S6{G3(=LcN#nQ`YswzkPkPzDuVmj*nyrzp;w^&rF%M9QB#hy4=M^5D`Z&4r` zo3n15e>zoKd^jX?r(n0nS~B@y?7Lh8<<9$S?$^CJT6)H_(dX8ralAU)Qo;;qHlx<3 z$q%DWiN*|6yQ=hryiGSlgQ!?mT}Y(@2i}Rrrp{;A2$n4Jm~|flq%yEkc{a%X@Ei04>z5RCIU04u+`JI{nT@-GAc6+#B}G z%iC+OG;SG(&|49yG%un;Rbh9K#O*nCx==AfhRF>xi8xI>{_%>WPLhp=O3zCDcjf6I z^;BD6UG4n+gP!jSvQ-pi?kC#L3^X8O{dEQJ!ClnesY89zqe}=?GHjjt{KrKh?owSF ziHdf2?q&-O-#jX#&z%T zN98(E30}@Jco%LW*V1bq{LZZ)jZ`oCh)yIgvtA&y6FT}?MoZSRu`7o*pIc`86dHOI zSdrMa72hOyC+3~g>5qY83BtSsIbHM1W&#e%=t~o7mCO^$3E?t*TAzp_2g{giJ+hh& z>`whDQAXw!g1YVc%x=X7YzbS|6LLFRbfyy~aS^$mw#92&>!o2PH9h(5p;O($)57G} zY+SaN^QIFf>Xzr6>gl!(eHYR`^Q`O9>4uD2&%>R!}#V)NTf=EaNmqh7gv zr>w0=-+m{iUBZO-nYxx9I>u|LH9f0d+vk0qZ$1g!?l=t8+ushis2IT{Q;c$;zjtI? ziDp!5&miSh#)hOK_DYGLdmNSP6UZYnX52i^`S(9%+O-E8WG1jD*MYg#~ zn8e7^kS#Tq43niX#xyg}<$3;w=eO&J>-GBn@O@pM^FHTuu5*CU3PduN1DA|^iz0g2 zIM_Sxh8b~?D#M7Y0n|WlA$s6Ag+%#OebB6Ngj|n)fHNax<_FrFk^;dU;f36i4|T~j z#ycy!-C0}PqH}gv*@J^InoBGS5ghGt4O0B!lKntrhEED}*9(Sv?{o%SQam02aZ~h+ z3_Cfa@^&o6H$8$clzbSp^UKDKsgNWYiS9gCL246jI2~as^p4Fmd*fl^=TPC2!R&$+ zcq(Bn9v@|{<6h!*B)Z1{`a&r99&swWP!j?p9caUlBb4O!)rwkAN!ZGiVq{A1YTCw^ z27%`6v$FBURk3+{_e|??FxM)3B#-#-vVYmn@r49Ve3DxyjbCu^^NRM@cKgkAu|mwC z^Ins_JzYUG#E5uS<*dEnRS$CD&*QbT=6`OzkW#bOkTMscLF54r!-WqXzbf1b+bs19 zy}Pkiq+&^o)J>c}t0lx9iCN9p=VL_W1B_PZO zMFeK!g;qgjN&5MOct}Zf+;rkwXwvn`QfeF2TO0uoPB*zcV=er3^=D^|itv21*{!X) zS<(2@Dj&->VJdB(u6TB|i;oeBD2&^YRFL0mh(Sz@tec_^s2SNbd!1OZC>weHl>zcQ zGg5Q>wlOw_l|1&E`GM6M7KY=<^Q3=WTs)nFVjoW z90tt9NKv-Z|ywKdr01Z*ALlNd6=Z0~BP)2vNF((@e(PvU99w7S3z${-Nktk%+ zo4&)Z--MfflTd+R5mH-cT!+P!ZiyXk+oQwpfaW@8!cnXKhcbOSmNDWp4$LnL^^C*J z8@0$tI@eED2M+`@0d+|q(lF>kUPVQlan`~fo+lkYNKRC6U>Xt~13=)Y4=aGq(`hOM zr}mL7#gXe8wMHs$)0e)@YD+>ddzS)uVtNLKY+3&}h30TUy?0oUWm}8N$+0)HynVQr z#jXh{Xw_4>^N&%TBsqQSTm_Vpr54nyS>YVT>Zcc+KGbV+|5uZnhM_?Wig^?{mtg4> z`;W8fbAG{XWb-7{8gpxA;F+>8Vkm;s7?n`-IlSVSVUC$kZy$IqLAe6ZqEtNdlN`2q zihN^O*61;Qsb`DE`i@zwYX}bJFIUkug*R^!fG>c0m>)pzg>Yg#hyq3UvJd$FYD#4M zs99srJO+^3oeDlYpb(~ViL_vvJT~t*yt*%zD-*RIp7g(q$?RaCj0i&rmGf+(rQ7?o zX%6ryvX7qf!`6iQvuq8-5q~?Q7?}Ob)01nVB6f?#!z&pFq7)85Bo^*0{cgv;6)Mc5E-=$1q$&hI`% z+f22`wxM9r-9u4ffb(4+HwF&3{<}4Da?4gUWqf1W5IllVdN~ z6<9nEue*Q@W>eP1`NwPe?d&f-#2FS*dl;?v95t~i=0HL499S&u34uNllRd3e0 zhAH}aM8kB3sFhD2o%sWZe);HH{)X0=Ctq2(xo}xKUe{G=|1f&tRhob4gb0`PxhAA# zW$1e=BGWpV@Ifz;_iN{m^guje_l|1i?J|}Pi`i0hUCmp(S$4Kd6M`sf8F6E_Uj+Y< zB`sosWgLuCb7Hu{aCm}n03PF~%H@yVnS30)=da(+DW2&TfA)_$3S25%BXvoZV8~5 zH`TA{7iDSH6+++BteJRDqJ3906KS!SR^UPsiQ4;nX$egT@i-aTYspDkU3?of)Md&K zMB)B+yp4zm3>&ypS+IfYln$EuD_1^#7N=s{pVWvEeuZl-HFtfQY!*I{j{hh?ZFMR; zhwO!AyVN+lu!4IXq@i0sRxiuaO7?F}3f0R<(}@F8?_^km)ce?h-&D?n-ByDwjM4s} z5+C6*%y4!}YkFVe@0eQr=s6r5{cFW@b86BMI`{1%z(glG3992Do=tV#VS@rTG1e zo6=nD=DXCIx-~6W|aG)}qAzYdB!LA$*8dv2(}wq3%PAG^EI|VtGv@6y|w~2h<5iv#RWI{VBLwi1%)TrWJ>%= z13we`6!8f#`^@&ZwSQdoch`xyMXm0OJ-ZbDqDs}bzx6Djz+VxI{9d8GuZ}$}Cl0o> zAgiLCpQJr;u|ahJsI+Bs*lo4D!45Uh)@T{J#!$k`W5;rq#bQWVbel74S64pj_T`E3By#o7~q-Qj4lEtgrS_0bT|?G_uNndr$#?3g~tRDtZ5J%a4Zz mju4A$5B=i=JnY(5D9IftOM|^Zha%wH$J<-OFTOe-aQ8nlOy0%- literal 0 HcmV?d00001 diff --git a/docs/issue-900-ui-evidence/index.html b/docs/issue-900-ui-evidence/index.html new file mode 100644 index 00000000..078293ee --- /dev/null +++ b/docs/issue-900-ui-evidence/index.html @@ -0,0 +1,198 @@ + + + + + + Issue 900 UI Evidence + + + +
+
+

Issue 900

+

CLIProxy Control Panel Embed Fix

+

+ Before the fix, the embedded upstream management center landed on + #/login and asked for credentials again. After the fix, the same surface + boots straight into the authenticated dashboard and starts reading + /api/cliproxy-local/v0/management/*. +

+
+ +
+

Embedded Control Panel

+

+ Captures use the same route and light theme. The colored callout box marks the iframe + review area. +

+ +
+
+
+ + Before: iframe falls back to login +
+ Before fix screenshot +
+ +
+
+ + After: iframe opens management dashboard +
+ After fix screenshot +
+
+ +
+

Broken state: iframe URL ended at /management.html#/login.

+

Fixed state: iframe URL ends at /management.html#/.

+

+ Fixed build verified live against the patched worktree dashboard on + localhost:3001. +

+
+
+
+ + diff --git a/ui/src/components/cliproxy/control-panel-embed.tsx b/ui/src/components/cliproxy/control-panel-embed.tsx index 67abc74e..704dab5a 100644 --- a/ui/src/components/cliproxy/control-panel-embed.tsx +++ b/ui/src/components/cliproxy/control-panel-embed.tsx @@ -1,12 +1,12 @@ /** * CLIProxy Control Panel Embed * - * Embeds the CLIProxy management.html with auto-authentication. - * Uses postMessage to inject credentials into the iframe. - * Supports both local and remote CLIProxy server connections. + * Embeds the CLIProxy management.html with dashboard-aware authentication. + * Local embeds run through the dashboard reverse proxy and need the upstream + * management app's auth state pre-seeded before the iframe loads. */ -import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; +import { useState, useEffect, useMemo, useRef } from 'react'; import { RefreshCw, AlertCircle, Gauge } from 'lucide-react'; import { useQuery } from '@tanstack/react-query'; import { api, withApiBase } from '@/lib/api-client'; @@ -22,9 +22,41 @@ interface ControlPanelEmbedProps { port?: number; } +const CONTROL_PANEL_AUTH_STORAGE_KEY = 'cli-proxy-auth'; +const CONTROL_PANEL_LOGIN_FLAG_KEY = 'isLoggedIn'; +const CONTROL_PANEL_API_BASE_KEY = 'apiBase'; +const CONTROL_PANEL_API_URL_KEY = 'apiUrl'; +const CONTROL_PANEL_MANAGEMENT_KEY = 'managementKey'; + +function resolveEmbeddedApiBase(checkUrl: string): string { + if (checkUrl.startsWith('/')) { + return new URL(checkUrl.replace(/\/$/, ''), window.location.origin).href; + } + + return checkUrl.replace(/\/$/, ''); +} + +function seedLocalControlPanelSession(apiBase: string, managementKey: string): void { + // The upstream management-center app restores auth from these legacy keys. + // Clear the persisted auth snapshot first so stale iframe state cannot win. + window.localStorage.removeItem(CONTROL_PANEL_AUTH_STORAGE_KEY); + window.localStorage.setItem(CONTROL_PANEL_API_BASE_KEY, apiBase); + window.localStorage.setItem(CONTROL_PANEL_API_URL_KEY, apiBase); + window.localStorage.setItem(CONTROL_PANEL_MANAGEMENT_KEY, managementKey); + window.localStorage.setItem(CONTROL_PANEL_LOGIN_FLAG_KEY, 'true'); +} + +function clearLocalControlPanelSession(): void { + window.localStorage.removeItem(CONTROL_PANEL_AUTH_STORAGE_KEY); + window.localStorage.removeItem(CONTROL_PANEL_API_BASE_KEY); + window.localStorage.removeItem(CONTROL_PANEL_API_URL_KEY); + window.localStorage.removeItem(CONTROL_PANEL_MANAGEMENT_KEY); + window.localStorage.removeItem(CONTROL_PANEL_LOGIN_FLAG_KEY); +} + export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanelEmbedProps) { const iframeRef = useRef(null); - const [loadedUrl, setLoadedUrl] = useState(null); + const [loadedFrameKey, setLoadedFrameKey] = useState(null); const [iframeRevision, setIframeRevision] = useState(0); const [error, setError] = useState(null); const [isConnected, setIsConnected] = useState(false); @@ -36,17 +68,6 @@ export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanel staleTime: 30000, // 30 seconds }); - // Fetch auth tokens for local mode (gets effective management secret) - const { data: authTokens } = useQuery({ - queryKey: ['auth-tokens-raw'], - queryFn: async () => { - const response = await fetch(withApiBase('/settings/auth/tokens/raw')); - if (!response.ok) throw new Error('Failed to fetch auth tokens'); - return response.json(); - }, - staleTime: 30000, // 30 seconds - }); - // Log config fetch errors (fallback to local mode on error) useEffect(() => { if (configError) { @@ -54,6 +75,35 @@ export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanel } }, [configError]); + const isRemoteConfig = Boolean(cliproxyConfig?.remote?.enabled && cliproxyConfig?.remote?.host); + + // Fetch auth tokens for local mode and seed the upstream auth keys before the iframe mounts. + const { data: authTokens, error: authTokensError } = useQuery({ + queryKey: ['auth-tokens-raw', isRemoteConfig ? 'remote' : 'local'], + enabled: cliproxyConfig !== undefined && !isRemoteConfig, + queryFn: async () => { + try { + const response = await fetch(withApiBase('/settings/auth/tokens/raw')); + if (!response.ok) throw new Error('Failed to fetch auth tokens'); + + const tokens = (await response.json()) as AuthTokensResponse; + const managementSecret = tokens.managementSecret.value.trim(); + if (!managementSecret) throw new Error('Management secret missing'); + + seedLocalControlPanelSession( + resolveEmbeddedApiBase(withApiBase('/cliproxy-local/')), + managementSecret + ); + + return tokens; + } catch (error) { + clearLocalControlPanelSession(); + throw error; + } + }, + staleTime: 30000, // 30 seconds + }); + // Calculate URLs and settings based on remote or local mode const { managementUrl, checkUrl, authToken, isRemote, displayHost } = useMemo(() => { const remote = cliproxyConfig?.remote; @@ -81,18 +131,58 @@ export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanel // Local mode - proxy through dashboard server to avoid cross-origin/port issues // (e.g., in Docker the browser cannot reach the internal CLIProxy port directly) - const effectiveSecret = authTokens?.managementSecret?.value || 'ccs'; return { managementUrl: withApiBase('/cliproxy-local/management.html'), checkUrl: withApiBase('/cliproxy-local/'), - authToken: effectiveSecret, + authToken: authTokens?.managementSecret?.value || undefined, isRemote: false, displayHost: `localhost:${localPort}`, }; }, [cliproxyConfig, authTokens, port]); - const iframeLoaded = loadedUrl === managementUrl; - const isLoading = !iframeLoaded; + const iframeKey = `${managementUrl}:${isRemote ? 'remote' : 'local'}:${checkUrl}:${authToken ?? 'missing'}:${iframeRevision}`; + const isSessionReady = + cliproxyConfig !== undefined && (isRemote || Boolean(authTokens) || Boolean(authTokensError)); + + useEffect(() => { + if (authTokensError) { + console.warn( + '[ControlPanelEmbed] Failed to preload local control panel session, falling back to manual login:', + authTokensError + ); + } + }, [authTokensError]); + + const iframeLoaded = loadedFrameKey === iframeKey; + const isLoading = !isSessionReady || !iframeLoaded; + + const postRemoteAutoLoginCredentials = () => { + if (!isRemote || !iframeRef.current?.contentWindow || !authToken) { + return; + } + + try { + const apiBase = resolveEmbeddedApiBase(checkUrl); + const targetOrigin = new URL(`${apiBase}/`).origin; + const iframeUrl = new URL(iframeRef.current.src, window.location.origin); + + if (iframeUrl.origin !== targetOrigin) { + console.warn('[ControlPanelEmbed] Remote iframe origin mismatch, skipping postMessage'); + return; + } + + iframeRef.current.contentWindow.postMessage( + { + type: 'ccs-auto-login', + apiBase, + managementKey: authToken, + }, + targetOrigin + ); + } catch (error) { + console.debug('[ControlPanelEmbed] Remote postMessage bootstrap failed:', error); + } + }; // Check if CLIProxy is running useEffect(() => { @@ -159,58 +249,13 @@ export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanel }; }, [checkUrl, isRemote, displayHost, cliproxyConfig]); - const postAutoLoginCredentials = useCallback(() => { - // Auto-login can only run when iframe has loaded and authToken is available. - if (!iframeLoaded || !iframeRef.current?.contentWindow || !authToken) { - return; - } - - try { - // Derive apiBase and targetOrigin from checkUrl. - // Local mode uses the same-origin dashboard proxy; remote mode stays absolute. - const apiBase = checkUrl.startsWith('/') - ? new URL(checkUrl.replace(/\/$/, ''), window.location.origin).href - : checkUrl.replace(/\/$/, ''); - const apiBaseUrl = new URL(`${apiBase}/`); - const targetOrigin = apiBaseUrl.origin; - - // Security: Validate iframe src matches the expected origin/path before sending credentials. - const iframeUrl = new URL(iframeRef.current.src, window.location.origin); - if ( - iframeUrl.origin !== apiBaseUrl.origin || - !iframeUrl.pathname.startsWith(apiBaseUrl.pathname) - ) { - console.warn('[ControlPanelEmbed] Iframe origin mismatch, skipping postMessage'); - return; - } - - // Send credentials to iframe - iframeRef.current.contentWindow.postMessage( - { - type: 'ccs-auto-login', - apiBase, - managementKey: authToken, - }, - targetOrigin - ); - } catch (e) { - // Cross-origin restriction - expected if not same origin - console.debug('[ControlPanelEmbed] postMessage failed - cross-origin:', e); - } - }, [authToken, checkUrl, iframeLoaded]); - - // Retry auto-login when token/checkUrl arrive after iframe onLoad. - useEffect(() => { - postAutoLoginCredentials(); - }, [postAutoLoginCredentials]); - - // Handle iframe load - mark ready then let effect post credentials. - const handleIframeLoad = useCallback(() => { - setLoadedUrl(managementUrl); - }, [managementUrl]); + const handleIframeLoad = () => { + setLoadedFrameKey(iframeKey); + postRemoteAutoLoginCredentials(); + }; const handleRefresh = () => { - setLoadedUrl(null); + setLoadedFrameKey(null); setIframeRevision((value) => value + 1); setError(null); setIsConnected(false); @@ -269,14 +314,16 @@ export function ControlPanelEmbed({ port = CLIPROXY_DEFAULT_PORT }: ControlPanel )} {/* Iframe */} -