From 085652560abf79def71f328b24d4ecbe5ce969ac Mon Sep 17 00:00:00 2001 From: catlog22 Date: Thu, 15 Jan 2026 22:30:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20ccw=20cli=20?= =?UTF-8?q?=E5=86=85=E9=83=A8=E8=B6=85=E6=97=B6=E5=8F=82=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E7=94=B1=E5=A4=96=E9=83=A8=20bash=20=E6=8E=A7?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 --timeout 命令行选项和内部超时处理逻辑 - 进程生命周期跟随父进程(bash)状态 - 简化代码,超时控制交由外部调用者管理 --- ccw/src/cli.ts | 2 +- ccw/src/commands/cli.ts | 8 +++--- ccw/src/tools/cli-executor-core.ts | 40 ++++++------------------------ 3 files changed, 12 insertions(+), 38 deletions(-) diff --git a/ccw/src/cli.ts b/ccw/src/cli.ts index e53c84f1..dec035ca 100644 --- a/ccw/src/cli.ts +++ b/ccw/src/cli.ts @@ -177,7 +177,7 @@ export function run(argv: string[]): void { .option('--model ', 'Model override') .option('--cd ', 'Working directory') .option('--includeDirs ', 'Additional directories (--include-directories for gemini/qwen, --add-dir for codex/claude)') - .option('--timeout ', 'Timeout in milliseconds (0=disabled, controlled by external caller)', '0') + // --timeout removed - controlled by external caller (bash timeout) .option('--stream', 'Enable streaming output (default: non-streaming with caching)') .option('--limit ', 'History limit') .option('--status ', 'Filter by status') diff --git a/ccw/src/commands/cli.ts b/ccw/src/commands/cli.ts index a1c6465b..1f9f22bc 100644 --- a/ccw/src/commands/cli.ts +++ b/ccw/src/commands/cli.ts @@ -116,7 +116,7 @@ interface CliExecOptions { model?: string; cd?: string; includeDirs?: string; - timeout?: string; + // timeout removed - controlled by external caller (bash timeout) stream?: boolean; // Enable streaming (default: false, caches output) resume?: string | boolean; // true = last, string = execution ID, comma-separated for merge id?: string; // Custom execution ID (e.g., IMPL-001-step1) @@ -535,7 +535,7 @@ async function statusAction(debug?: boolean): Promise { * @param {Object} options - CLI options */ async function execAction(positionalPrompt: string | undefined, options: CliExecOptions): Promise { - const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, timeout, stream, resume, id, noNative, cache, injectMode, debug } = options; + const { prompt: optionPrompt, file, tool = 'gemini', mode = 'analysis', model, cd, includeDirs, stream, resume, id, noNative, cache, injectMode, debug } = options; // Enable debug mode if --debug flag is set if (debug) { @@ -842,7 +842,7 @@ async function execAction(positionalPrompt: string | undefined, options: CliExec model, cd, includeDirs, - timeout: timeout ? parseInt(timeout, 10) : 0, // 0 = no internal timeout, controlled by external caller + // timeout removed - controlled by external caller (bash timeout) resume, id, // custom execution ID noNative, @@ -1221,7 +1221,7 @@ export async function cliCommand( console.log(chalk.gray(' --model Model override')); console.log(chalk.gray(' --cd Working directory')); console.log(chalk.gray(' --includeDirs Additional directories')); - console.log(chalk.gray(' --timeout Timeout (default: 0=disabled)')); + // --timeout removed - controlled by external caller (bash timeout) console.log(chalk.gray(' --resume [id] Resume previous session')); console.log(chalk.gray(' --cache Cache: comma-separated @patterns and text')); console.log(chalk.gray(' --inject-mode Inject mode: none, full, progressive')); diff --git a/ccw/src/tools/cli-executor-core.ts b/ccw/src/tools/cli-executor-core.ts index 0046b025..58ed39bf 100644 --- a/ccw/src/tools/cli-executor-core.ts +++ b/ccw/src/tools/cli-executor-core.ts @@ -356,7 +356,7 @@ const ParamsSchema = z.object({ model: z.string().optional(), cd: z.string().optional(), includeDirs: z.string().optional(), - timeout: z.number().default(0), // 0 = no internal timeout, controlled by external caller (e.g., bash timeout) + // timeout removed - controlled by external caller (bash timeout) resume: z.union([z.boolean(), z.string()]).optional(), // true = last, string = single ID or comma-separated IDs id: z.string().optional(), // Custom execution ID (e.g., IMPL-001-step1) noNative: z.boolean().optional(), // Force prompt concatenation instead of native resume @@ -388,7 +388,7 @@ async function executeCliTool( throw new Error(`Invalid params: ${parsed.error.message}`); } - const { tool, prompt, mode, format, model, cd, includeDirs, timeout, resume, id: customId, noNative, category, parentExecutionId, outputFormat } = parsed.data; + const { tool, prompt, mode, format, model, cd, includeDirs, resume, id: customId, noNative, category, parentExecutionId, outputFormat } = parsed.data; // Validate and determine working directory early (needed for conversation lookup) let workingDir: string; @@ -862,7 +862,6 @@ async function executeCliTool( let stdout = ''; let stderr = ''; - let timedOut = false; // Handle stdout child.stdout!.on('data', (data: Buffer) => { @@ -924,18 +923,14 @@ async function executeCliTool( debugLog('CLOSE', `Process closed`, { exitCode: code, duration: `${duration}ms`, - timedOut, stdoutLength: stdout.length, stderrLength: stderr.length, outputUnitsCount: allOutputUnits.length }); // Determine status - prioritize output content over exit code - let status: 'success' | 'error' | 'timeout' = 'success'; - if (timedOut) { - status = 'timeout'; - debugLog('STATUS', `Execution timed out after ${duration}ms`); - } else if (code !== 0) { + let status: 'success' | 'error' = 'success'; + if (code !== 0) { // Non-zero exit code doesn't always mean failure // Check if there's valid output (AI response) - treat as success const hasValidOutput = stdout.trim().length > 0; @@ -1169,25 +1164,8 @@ async function executeCliTool( reject(new Error(`Failed to spawn ${tool}: ${error.message}\n Command: ${command} ${args.join(' ')}\n Working Dir: ${workingDir}`)); }); - // Timeout handling (timeout=0 disables internal timeout, controlled by external caller) - let timeoutId: NodeJS.Timeout | null = null; - if (timeout > 0) { - timeoutId = setTimeout(() => { - timedOut = true; - child.kill('SIGTERM'); - setTimeout(() => { - if (!child.killed) { - child.kill('SIGKILL'); - } - }, 5000); - }, timeout); - } - - child.on('close', () => { - if (timeoutId) { - clearTimeout(timeoutId); - } - }); + // Timeout controlled by external caller (bash timeout) + // When parent process terminates, child will be cleaned up via process exit handler }); } @@ -1228,12 +1206,8 @@ Modes: includeDirs: { type: 'string', description: 'Additional directories (comma-separated). Maps to --include-directories for gemini/qwen, --add-dir for codex' - }, - timeout: { - type: 'number', - description: 'Timeout in milliseconds (default: 0 = disabled, controlled by external caller)', - default: 0 } + // timeout removed - controlled by external caller (bash timeout) }, required: ['tool', 'prompt'] }