From 9db54c9b19b136526baa97677e244b4b0071ee5b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Tue, 3 Mar 2026 19:47:39 -0800 Subject: [PATCH] Handle Codex auth JSON arrays and flag missing auth markers - Parse nested/array JSON auth status output from `codex auth status --json` - Return `unauthenticated` when JSON contains `authenticated: false` - Return warning when JSON is parseable but lacks an auth marker --- .../provider/Layers/ProviderHealth.test.ts | 50 +++++++++++++++++++ .../src/provider/Layers/ProviderHealth.ts | 46 +++++++++++++---- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/apps/server/src/provider/Layers/ProviderHealth.test.ts b/apps/server/src/provider/Layers/ProviderHealth.test.ts index 2530ae1293dcc2f61c7083e835472660c59df603..b75c358baa0e7d8bfa5dcfd8ea3520c7158daf7f 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.test.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.test.ts @@ -79,6 +79,56 @@ describe("checkProviderStatusesOnStartup", () => { ]); }); + it("returns unauthenticated when auth probe JSON array includes authenticated=false", async () => { + const status = await checkProviderStatusesOnStartup(async (_command, args) => { + if (args.join(" ") === "--version") { + return result({ stdout: "codex 1.0.0\n" }); + } + if (args.join(" ") === "auth status --json") { + return result({ + stdout: '[{"authenticated":false}]\n', + }); + } + throw new Error(`Unexpected command: ${args.join(" ")}`); + }); + + expect(status).toEqual([ + expect.objectContaining({ + provider: "codex", + status: "error", + available: true, + authStatus: "unauthenticated", + message: "Codex CLI is not authenticated. Run `codex login` and try again.", + }), + ]); + }); + + it("returns warning when auth probe JSON array has no auth marker", async () => { + const status = await checkProviderStatusesOnStartup(async (_command, args) => { + if (args.join(" ") === "--version") { + return result({ stdout: "codex 1.0.0\n" }); + } + if (args.join(" ") === "auth status --json") { + return result({ + code: 0, + stdout: '[{"ok":true}]\n', + }); + } + throw new Error(`Unexpected command: ${args.join(" ")}`); + }); + + expect(status).toEqual([ + expect.objectContaining({ + provider: "codex", + status: "warning", + available: true, + authStatus: "unknown", + message: + "Could not verify Codex authentication status from JSON output (missing auth marker).", + }), + ]); + }); + it("returns warning when auth status command is unsupported", async () => { const status = await checkProviderStatusesOnStartup(async (_command, args) => { if (args.join(" ") === "--version") { diff --git a/apps/server/src/provider/Layers/ProviderHealth.ts b/apps/server/src/provider/Layers/ProviderHealth.ts index 020554561da2c1eab111827e7d5cb1c900783167..3bab83f19204a173b35155e0183cb5e2a8c14086 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.ts @@ -54,7 +54,17 @@ function detailFromResult(result: ProcessRunResult): string | undefined { } function extractAuthBoolean(value: unknown): boolean | undefined { - if (!value || typeof value !== "object" || Array.isArray(value)) { + if (Array.isArray(value)) { + for (const entry of value) { + const nested = extractAuthBoolean(entry); + if (nested !== undefined) { + return nested; + } + } + return undefined; + } + + if (!value || typeof value !== "object") { return undefined; } @@ -122,19 +132,28 @@ function parseAuthStatusFromOutput( (() => { const trimmed = result.stdout.trim(); if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) { - return undefined; + return { + attemptedJsonParse: false as const, + auth: undefined as boolean | undefined, + }; } try { - return extractAuthBoolean(JSON.parse(trimmed)); + return { + attemptedJsonParse: true as const, + auth: extractAuthBoolean(JSON.parse(trimmed)), + }; } catch { - return undefined; + return { + attemptedJsonParse: false as const, + auth: undefined as boolean | undefined, + }; } - })() ?? undefined; + })(); - if (parsedAuth === true) { + if (parsedAuth.auth === true) { return { status: "ready", authStatus: "authenticated" }; } - if (parsedAuth === false) { + if (parsedAuth.auth === false) { return { status: "error", authStatus: "unauthenticated", @@ -142,6 +161,15 @@ function parseAuthStatusFromOutput( }; } + if (parsedAuth.attemptedJsonParse) { + return { + status: "warning", + authStatus: "unknown", + message: + "Could not verify Codex authentication status from JSON output (missing auth marker).", + }; + } + if (result.code === 0) { // Successful command with no explicit auth marker is still a pass. return { status: "ready", authStatus: "authenticated" }; @@ -240,7 +268,3 @@ const makeProviderHealth = (run: CommandRunner = runProcess) => }); export const ProviderHealthLive = Layer.effect(ProviderHealth, makeProviderHealth()); - -export function makeProviderHealthLive(run?: CommandRunner) { - return Layer.effect(ProviderHealth, makeProviderHealth(run)); -} -- 2.54.0