| ... | @@ -54,7 +54,17 @@ function detailFromResult(result: ProcessRunResult): string | undefined { | ... | @@ -54,7 +54,17 @@ function detailFromResult(result: ProcessRunResult): string | undefined { |
| 54 | } | 54 | } |
| 55 | | 55 | |
| 56 | function extractAuthBoolean(value: unknown): boolean | undefined { | 56 | function 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") { |
| 58 | return undefined; | 68 | return undefined; |
| 59 | } | 69 | } |
| 60 | | 70 | |
| ... | @@ -122,19 +132,28 @@ function parseAuthStatusFromOutput( | ... | @@ -122,19 +132,28 @@ function parseAuthStatusFromOutput( |
| 122 | (() => { | 132 | (() => { |
| 123 | const trimmed = result.stdout.trim(); | 133 | const trimmed = result.stdout.trim(); |
| 124 | if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) { | 134 | if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) { |
| 125 | return undefined; | 135 | return { |
| | 136 | attemptedJsonParse: false as const, |
| | 137 | auth: undefined as boolean | undefined, |
| | 138 | }; |
| 126 | } | 139 | } |
| 127 | try { | 140 | try { |
| 128 | return extractAuthBoolean(JSON.parse(trimmed)); | 141 | return { |
| | 142 | attemptedJsonParse: true as const, |
| | 143 | auth: extractAuthBoolean(JSON.parse(trimmed)), |
| | 144 | }; |
| 129 | } catch { | 145 | } catch { |
| 130 | return undefined; | 146 | return { |
| | 147 | attemptedJsonParse: false as const, |
| | 148 | auth: undefined as boolean | undefined, |
| | 149 | }; |
| 131 | } | 150 | } |
| 132 | })() ?? undefined; | 151 | })(); |
| 133 | | 152 | |
| 134 | if (parsedAuth === true) { | 153 | if (parsedAuth.auth === true) { |
| 135 | return { status: "ready", authStatus: "authenticated" }; | 154 | return { status: "ready", authStatus: "authenticated" }; |
| 136 | } | 155 | } |
| 137 | if (parsedAuth === false) { | 156 | if (parsedAuth.auth === false) { |
| 138 | return { | 157 | return { |
| 139 | status: "error", | 158 | status: "error", |
| 140 | authStatus: "unauthenticated", | 159 | authStatus: "unauthenticated", |
| ... | @@ -142,6 +161,15 @@ function parseAuthStatusFromOutput( | ... | @@ -142,6 +161,15 @@ function parseAuthStatusFromOutput( |
| 142 | }; | 161 | }; |
| 143 | } | 162 | } |
| 144 | | 163 | |
| | 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 | |
| 145 | if (result.code === 0) { | 173 | if (result.code === 0) { |
| 146 | // Successful command with no explicit auth marker is still a pass. | 174 | // Successful command with no explicit auth marker is still a pass. |
| 147 | return { status: "ready", authStatus: "authenticated" }; | 175 | return { status: "ready", authStatus: "authenticated" }; |
| ... | @@ -240,7 +268,3 @@ const makeProviderHealth = (run: CommandRunner = runProcess) => | ... | @@ -240,7 +268,3 @@ const makeProviderHealth = (run: CommandRunner = runProcess) => |
| 240 | }); | 268 | }); |
| 241 | | 269 | |
| 242 | export const ProviderHealthLive = Layer.effect(ProviderHealth, makeProviderHealth()); | 270 | export const ProviderHealthLive = Layer.effect(ProviderHealth, makeProviderHealth()); |
| 243 | | | |
| 244 | export function makeProviderHealthLive(run?: CommandRunner) { | | |
| 245 | return Layer.effect(ProviderHealth, makeProviderHealth(run)); | | |
| 246 | } | | |