authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-03 19:47:39-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-03 21:03:16-08:00
log9db54c9b19b136526baa97677e244b4b0071ee5b
tree6a3e97c6303020c54ad122bdaa977fe08b3e09eb
parent34430e3514041935898aba5282392fac605afa71

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

2 files changed, 85 insertions(+), 11 deletions(-)

apps/server/src/provider/Layers/ProviderHealth.test.ts+50
......@@ -79,6 +79,56 @@ describe("checkProviderStatusesOnStartup", () => {
7979 ]);
8080 });
8181
82 it("returns unauthenticated when auth probe JSON array includes authenticated=false", async () => {
83 const status = await checkProviderStatusesOnStartup(async (_command, args) => {
84 if (args.join(" ") === "--version") {
85 return result({ stdout: "codex 1.0.0\n" });
86 }
87 if (args.join(" ") === "auth status --json") {
88 return result({
89 stdout: '[{"authenticated":false}]\n',
90 });
91 }
92 throw new Error(`Unexpected command: ${args.join(" ")}`);
93 });
94
95 expect(status).toEqual([
96 expect.objectContaining({
97 provider: "codex",
98 status: "error",
99 available: true,
100 authStatus: "unauthenticated",
101 message: "Codex CLI is not authenticated. Run `codex login` and try again.",
102 }),
103 ]);
104 });
105
106 it("returns warning when auth probe JSON array has no auth marker", async () => {
107 const status = await checkProviderStatusesOnStartup(async (_command, args) => {
108 if (args.join(" ") === "--version") {
109 return result({ stdout: "codex 1.0.0\n" });
110 }
111 if (args.join(" ") === "auth status --json") {
112 return result({
113 code: 0,
114 stdout: '[{"ok":true}]\n',
115 });
116 }
117 throw new Error(`Unexpected command: ${args.join(" ")}`);
118 });
119
120 expect(status).toEqual([
121 expect.objectContaining({
122 provider: "codex",
123 status: "warning",
124 available: true,
125 authStatus: "unknown",
126 message:
127 "Could not verify Codex authentication status from JSON output (missing auth marker).",
128 }),
129 ]);
130 });
131
82132 it("returns warning when auth status command is unsupported", async () => {
83133 const status = await checkProviderStatusesOnStartup(async (_command, args) => {
84134 if (args.join(" ") === "--version") {
apps/server/src/provider/Layers/ProviderHealth.ts+35-11
......@@ -54,7 +54,17 @@ function detailFromResult(result: ProcessRunResult): string | undefined {
5454}
5555
5656function extractAuthBoolean(value: unknown): boolean | undefined {
57 if (!value || typeof value !== "object" || Array.isArray(value)) {
57 if (Array.isArray(value)) {
58 for (const entry of value) {
59 const nested = extractAuthBoolean(entry);
60 if (nested !== undefined) {
61 return nested;
62 }
63 }
64 return undefined;
65 }
66
67 if (!value || typeof value !== "object") {
5868 return undefined;
5969 }
6070
......@@ -122,19 +132,28 @@ function parseAuthStatusFromOutput(
122132 (() => {
123133 const trimmed = result.stdout.trim();
124134 if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) {
125 return undefined;
135 return {
136 attemptedJsonParse: false as const,
137 auth: undefined as boolean | undefined,
138 };
126139 }
127140 try {
128 return extractAuthBoolean(JSON.parse(trimmed));
141 return {
142 attemptedJsonParse: true as const,
143 auth: extractAuthBoolean(JSON.parse(trimmed)),
144 };
129145 } catch {
130 return undefined;
146 return {
147 attemptedJsonParse: false as const,
148 auth: undefined as boolean | undefined,
149 };
131150 }
132 })() ?? undefined;
151 })();
133152
134 if (parsedAuth === true) {
153 if (parsedAuth.auth === true) {
135154 return { status: "ready", authStatus: "authenticated" };
136155 }
137 if (parsedAuth === false) {
156 if (parsedAuth.auth === false) {
138157 return {
139158 status: "error",
140159 authStatus: "unauthenticated",
......@@ -142,6 +161,15 @@ function parseAuthStatusFromOutput(
142161 };
143162 }
144163
164 if (parsedAuth.attemptedJsonParse) {
165 return {
166 status: "warning",
167 authStatus: "unknown",
168 message:
169 "Could not verify Codex authentication status from JSON output (missing auth marker).",
170 };
171 }
172
145173 if (result.code === 0) {
146174 // Successful command with no explicit auth marker is still a pass.
147175 return { status: "ready", authStatus: "authenticated" };
......@@ -240,7 +268,3 @@ const makeProviderHealth = (run: CommandRunner = runProcess) =>
240268 });
241269
242270export const ProviderHealthLive = Layer.effect(ProviderHealth, makeProviderHealth());
243
244export function makeProviderHealthLive(run?: CommandRunner) {
245 return Layer.effect(ProviderHealth, makeProviderHealth(run));
246}