@@ -3,8 +3,8 @@ import { getAnthropicAccountHealthSnapshot } from "./anthropic-routing";
33import { isAccountNeedsReauth } from "../codex/account-runtime-state" ;
44import { getCodexAccountCredential , listCodexAccountIds } from "../codex/account-store" ;
55import { MAIN_CODEX_ACCOUNT_ID } from "../codex/main-account" ;
6+ import { configuredAdminToken } from "../lib/admin-secrets" ;
67import { maskAccountId } from "../lib/privacy" ;
7- import { loadServiceTokenFromFile } from "../lib/service-secrets" ;
88import { findLiveProxy , probeHostname } from "../server/proxy-liveness" ;
99import { loadAuthStore , peekAuthStore , peekOAuthRefreshIntent , readOAuthRefreshIntent } from "./store" ;
1010import type { ProviderAccount } from "./types" ;
@@ -320,36 +320,50 @@ function coerceRemoteAccountHealth(
320320 return projectOAuthAccountHealth ( { needsReauth : account . needsReauth === true } ) ;
321321}
322322
323+ type LiveProxyCodexHealthResult = {
324+ source : CodexHealthSource ;
325+ entries : OAuthHealthEntry [ ] | null ;
326+ } ;
327+
323328async function fetchCodexHealthFromLiveProxy (
324329 fetchImpl : typeof fetch = fetch ,
325330 findLiveProxyImpl : typeof findLiveProxy = findLiveProxy ,
326- ) : Promise < OAuthHealthEntry [ ] | null > {
331+ ) : Promise < LiveProxyCodexHealthResult > {
327332 const live = await findLiveProxyImpl ( ) ;
328- if ( ! live ) return null ;
329- const token = process . env . OPENCODEX_API_AUTH_TOKEN ?? loadServiceTokenFromFile ( process . env ) ;
333+ if ( ! live ) return { source : "unavailable" , entries : null } ;
334+ // This is a management-plane endpoint. A data-plane service token is intentionally not
335+ // interchangeable with the admin credential even on loopback.
336+ const token = configuredAdminToken ( ) ;
330337 const headers : Record < string , string > = { } ;
331338 if ( token ) headers . Authorization = `Bearer ${ token } ` ;
332339 try {
333340 const res = await fetchImpl (
334341 `http://${ probeHostname ( live . hostname ) } :${ live . port } /api/codex-auth/accounts` ,
335342 { headers, signal : AbortSignal . timeout ( 4000 ) } ,
336343 ) ;
337- if ( ! res . ok ) return null ;
344+ if ( res . status === 401 || res . status === 403 ) {
345+ return { source : "management-auth-failed" , entries : null } ;
346+ }
347+ if ( ! res . ok ) return { source : "management-api-unavailable" , entries : null } ;
338348 const json = await res . json ( ) as { accounts ?: ProxyCodexAccountHealth [ ] } ;
339- if ( ! Array . isArray ( json . accounts ) ) return null ;
349+ if ( ! Array . isArray ( json . accounts ) ) return { source : "management-api-unavailable" , entries : null } ;
340350 const entries : OAuthHealthEntry [ ] = [ ] ;
341351 for ( const account of json . accounts ) {
342352 if ( ! account ?. id || typeof account . id !== "string" ) continue ;
343353 pushEntry ( entries , "codex" , account . id , coerceRemoteAccountHealth ( account ) ) ;
344354 }
345- return entries ;
355+ return { source : "management-api" , entries } ;
346356 } catch {
347- return null ;
357+ return { source : "management-api-unavailable" , entries : null } ;
348358 }
349359}
350360
351361/** How CLI/doctor obtained Codex cooldown/reauth (proxy memory only lives in the proxy). */
352- export type CodexHealthSource = "management-api" | "unavailable" ;
362+ export type CodexHealthSource =
363+ | "management-api"
364+ | "unavailable"
365+ | "management-auth-failed"
366+ | "management-api-unavailable" ;
353367
354368export type OAuthCliHealthReport = {
355369 entries : OAuthHealthEntry [ ] ;
@@ -359,6 +373,10 @@ export type OAuthCliHealthReport = {
359373/** Shown by `ocx status` / `ocx doctor` when the proxy management API is unreachable. */
360374export const CODEX_HEALTH_UNAVAILABLE_NOTE =
361375 "Codex health: unavailable (proxy not running; live cooldown/reauth requires the management API)" ;
376+ export const CODEX_HEALTH_AUTH_FAILED_NOTE =
377+ "Codex health: unavailable (proxy running; management authentication failed)" ;
378+ export const CODEX_HEALTH_MANAGEMENT_API_UNAVAILABLE_NOTE =
379+ "Codex health: unavailable (proxy running; management API did not return account health)" ;
362380
363381/**
364382 * CLI/doctor collector: observe-only OAuth store reads, and Codex health only from the
@@ -373,9 +391,9 @@ export async function collectOAuthHealthEntriesForCli(
373391) : Promise < OAuthCliHealthReport > {
374392 const entries = collectOAuthHealthEntries ( now , { observeOnly : true , includeLocalCodex : false } ) ;
375393 const remote = await fetchCodexHealthFromLiveProxy ( deps . fetchImpl , deps . findLiveProxyImpl ) ;
376- if ( remote ) {
377- for ( const entry of remote ) entries . push ( entry ) ;
394+ if ( remote . entries ) {
395+ for ( const entry of remote . entries ) entries . push ( entry ) ;
378396 return { entries, codexHealthSource : "management-api" } ;
379397 }
380- return { entries, codexHealthSource : "unavailable" } ;
398+ return { entries, codexHealthSource : remote . source } ;
381399}
0 commit comments