From bcddb4260827c9e3f430631a06336825f0526450 Mon Sep 17 00:00:00 2001 From: Stefano Fiorini Date: Tue, 19 May 2026 19:54:27 -0500 Subject: [PATCH] feat(M1): Codex Reliability Fix --- tools/ai-cli-dispatch/src/cli.ts | 8 +++-- tools/ai-cli-dispatch/src/dispatch.ts | 3 +- tools/ai-cli-dispatch/src/types.ts | 19 ----------- tools/ai-cli-dispatch/tests/cli.test.ts | 45 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/tools/ai-cli-dispatch/src/cli.ts b/tools/ai-cli-dispatch/src/cli.ts index f619b09..5be1ef3 100644 --- a/tools/ai-cli-dispatch/src/cli.ts +++ b/tools/ai-cli-dispatch/src/cli.ts @@ -118,8 +118,10 @@ export async function main( } const config = resolveConfig(); + const parsedTimeout = + typeof args.timeout === "string" ? Number(args.timeout) : undefined; const timeoutMs = - typeof args.timeout === "string" ? Number(args.timeout) : config.timeout; + Number.isFinite(parsedTimeout) ? parsedTimeout : config.timeout; try { const result = await executePrompt(client, prompt, { @@ -178,8 +180,10 @@ export async function main( return 1; } + const parsedTimeout = + typeof args.timeout === "string" ? Number(args.timeout) : undefined; const timeoutMs = - typeof args.timeout === "string" ? Number(args.timeout) : config.timeout; + Number.isFinite(parsedTimeout) ? parsedTimeout : config.timeout; try { const result = await executePrompt(client, prompt, { diff --git a/tools/ai-cli-dispatch/src/dispatch.ts b/tools/ai-cli-dispatch/src/dispatch.ts index 9bc2a4c..3c2c6a2 100644 --- a/tools/ai-cli-dispatch/src/dispatch.ts +++ b/tools/ai-cli-dispatch/src/dispatch.ts @@ -1,3 +1,4 @@ +import { CLIENT_NAMES } from "./constants.js"; import type { ClientName } from "./types.js"; export interface DispatchConfig { @@ -5,8 +6,6 @@ export interface DispatchConfig { client?: ClientName; } -const CLIENT_NAMES: ClientName[] = ["codex", "claude", "opencode"]; - export function resolveClient( prompt: string, config?: DispatchConfig diff --git a/tools/ai-cli-dispatch/src/types.ts b/tools/ai-cli-dispatch/src/types.ts index 82008ce..0bf167d 100644 --- a/tools/ai-cli-dispatch/src/types.ts +++ b/tools/ai-cli-dispatch/src/types.ts @@ -27,25 +27,6 @@ export interface DebugInfo { noisySuccess: boolean; } -export interface DispatchConfigFile { - paths?: Partial>; - defaultClient?: ClientName; - timeout?: number; -} - -export interface DispatchEnv { - AI_CLI_DEFAULT_CLIENT?: string; - AI_CLI_TIMEOUT?: string; - AI_CLI_CODEX_PATH?: string; - AI_CLI_CLAUDE_PATH?: string; - AI_CLI_OPENCODE_PATH?: string; -} - -export interface ToolConfig { - clients: ClientName[]; - defaultClient?: ClientName; -} - export class ClientNotFoundError extends Error { constructor(client: string) { super(`Client "${client}" not found or not installed.`); diff --git a/tools/ai-cli-dispatch/tests/cli.test.ts b/tools/ai-cli-dispatch/tests/cli.test.ts index 03b412b..5d0d846 100644 --- a/tools/ai-cli-dispatch/tests/cli.test.ts +++ b/tools/ai-cli-dispatch/tests/cli.test.ts @@ -452,6 +452,51 @@ describe("main", () => { } }); + it("run ignores non-numeric --timeout and falls back to config timeout", async () => { + const out = captureOutput(); + try { + let receivedTimeout: number | undefined; + const code = await main( + ["node", "cli.ts", "run", "--client", "codex", "--prompt", "hello", "--timeout", "not-a-number"], + { + detectClients: () => mockClients, + executePrompt: async (_client, _prompt, options?) => { + receivedTimeout = options?.timeoutMs; + return mockResult; + }, + resolveConfig: () => ({ paths: {}, timeout: 600_000 }), + } + ); + assert.strictEqual(code, 0); + assert.strictEqual(receivedTimeout, 600_000); + } finally { + out.restore(); + } + }); + + it("dispatch ignores non-numeric --timeout and falls back to config timeout", async () => { + const out = captureOutput(); + try { + let receivedTimeout: number | undefined; + const code = await main( + ["node", "cli.ts", "dispatch", "do something", "--timeout", "bad"], + { + detectClients: () => mockClients, + executePrompt: async (_client, _prompt, options?) => { + receivedTimeout = options?.timeoutMs; + return mockResult; + }, + resolveClient: () => "codex", + resolveConfig: () => ({ paths: {}, timeout: 600_000 }), + } + ); + assert.strictEqual(code, 0); + assert.strictEqual(receivedTimeout, 600_000); + } finally { + out.restore(); + } + }); + it("dispatch prints debug diagnostic JSON to stderr with --debug", async () => { const out = captureOutput(); const stderrChunks: string[] = [];