From db0bc9cd675cf001acbb2a7d43d475bda1d61979 Mon Sep 17 00:00:00 2001 From: "Alfred (Clawdbot)" Date: Tue, 18 Aug 2026 20:35:08 -0400 Subject: [PATCH] Fix Codex login status detection --- server/drivers/codex.test.ts | 8 ++++++++ server/drivers/codex.ts | 4 ++-- server/procs.ts | 6 +++--- server/testing/fake-codex-app-server.ts | 9 ++++++--- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/drivers/codex.test.ts b/server/drivers/codex.test.ts index cbe78f78d5..d1df50ca35 100644 --- a/server/drivers/codex.test.ts +++ b/server/drivers/codex.test.ts @@ -283,6 +283,14 @@ describe("CodexDriver turns (fake app-server)", () => { }); }); + it("also accepts login status from older Codex versions that used stdout", async () => { + await create({ mode: "logged-in-stdout" }); + await expect(instance.snapshot()).resolves.toMatchObject({ + state: "available", + authenticated: true, + }); + }); + it("marks a Codex 401 as setup so the UI offers sign-in instead of Retry", async () => { await create({ mode: "unauthorized" }); await instance.adapter.sendTurn({ threadId: "t-unauthorized", text: "hi" }); diff --git a/server/drivers/codex.ts b/server/drivers/codex.ts index 7dc87dc90b..326ac9bbf1 100644 --- a/server/drivers/codex.ts +++ b/server/drivers/codex.ts @@ -478,8 +478,8 @@ export const CodexDriver: ProviderDriver = { }); if (!version) return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` }; const authenticated = await new Promise((resolve) => { - execCli(config.cli, ["login", "status"], { timeout: 8000, env }, (err, stdout) => - resolve(!err && /logged in/i.test(stdout)), + execCli(config.cli, ["login", "status"], { timeout: 8000, env }, (err, stdout, stderr) => + resolve(!err && /^logged in\b/im.test(`${stdout}\n${stderr ?? ""}`)), ); }); // childEnv drops OPENAI_API_KEY on purpose — turns run on the ChatGPT login diff --git a/server/procs.ts b/server/procs.ts index 2ae70b0da4..b44671fdad 100644 --- a/server/procs.ts +++ b/server/procs.ts @@ -55,11 +55,11 @@ export function execCli( cli: string, args: string[], opts: ExecFileOptions, - cb: (err: Error | null, stdout: string) => void, + cb: (err: Error | null, stdout: string, stderr?: string) => void, ): void { const resolved = resolveCli(cli, args); - execFile(resolved.command, resolved.args, { ...opts, windowsHide: true }, (err, stdout) => - cb(err, typeof stdout === "string" ? stdout : String(stdout)), + execFile(resolved.command, resolved.args, { ...opts, windowsHide: true, encoding: "utf8" }, (err, stdout, stderr) => + cb(err, stdout, stderr), ); } diff --git a/server/testing/fake-codex-app-server.ts b/server/testing/fake-codex-app-server.ts index 2af12bad34..1e27e81126 100755 --- a/server/testing/fake-codex-app-server.ts +++ b/server/testing/fake-codex-app-server.ts @@ -5,7 +5,7 @@ // real app-server, it never exits on its own — the driver kills it. // // FAKE_CODEX_MODE happy (default) | approval | resume | stream | -// logged-out | unauthorized +// logged-in-stdout | logged-out | unauthorized // FAKE_CODEX_DUMP path to write {argv, env, calls, decision} as JSON // // Keep this file dependency-free — it runs as a bare `node` subprocess. @@ -14,7 +14,7 @@ import { writeFileSync } from "node:fs"; const mode = process.env.FAKE_CODEX_MODE ?? "happy"; if (process.argv[2] === "--version") { - process.stdout.write("codex-cli 0.146.0\n"); + process.stdout.write("codex-cli 0.147.0\n"); process.exit(0); } if (process.argv[2] === "login" && process.argv[3] === "status") { @@ -22,7 +22,10 @@ if (process.argv[2] === "login" && process.argv[3] === "status") { process.stderr.write("Not logged in\n"); process.exit(1); } - process.stdout.write("Logged in using ChatGPT\n"); + // Codex 0.147.0 reports a successful login on stderr; retain a mode for + // older versions that wrote the same status on stdout. + const statusStream = mode === "logged-in-stdout" ? process.stdout : process.stderr; + statusStream.write("Logged in using ChatGPT\n"); process.exit(0); } const calls: Array<{ method: string; params: unknown }> = [];