@@ -84,7 +84,39 @@ function extractAuthBoolean(value: unknown): boolean | undefined {
8484 return undefined ;
8585}
8686
87- export function parseAuthStatusFromOutput ( result : CommandResult ) : {
87+ interface ProviderAuthLabels {
88+ readonly unknownCommandMessage : string ;
89+ readonly unauthenticatedMessage : string ;
90+ readonly jsonParseWarning : string ;
91+ readonly verifyFailurePrefix : string ;
92+ readonly loginHints : ReadonlyArray < string > ;
93+ }
94+
95+ const CODEX_AUTH_LABELS : ProviderAuthLabels = {
96+ unknownCommandMessage :
97+ "Codex CLI authentication status command is unavailable in this Codex version." ,
98+ unauthenticatedMessage : "Codex CLI is not authenticated. Run `codex login` and try again." ,
99+ jsonParseWarning :
100+ "Could not verify Codex authentication status from JSON output (missing auth marker)." ,
101+ verifyFailurePrefix : "Could not verify Codex authentication status" ,
102+ loginHints : [ "run `codex login`" , "run codex login" ] ,
103+ } ;
104+
105+ const CLAUDE_AUTH_LABELS : ProviderAuthLabels = {
106+ unknownCommandMessage :
107+ "Claude Code authentication status command is unavailable in this version of Claude Code." ,
108+ unauthenticatedMessage :
109+ "Claude Code is not authenticated. Run `claude auth login` and try again." ,
110+ jsonParseWarning :
111+ "Could not verify Claude Code authentication status from JSON output (missing auth marker)." ,
112+ verifyFailurePrefix : "Could not verify Claude Code authentication status" ,
113+ loginHints : [ "run `claude login`" , "run claude login" ] ,
114+ } ;
115+
116+ function parseAuthStatusWithLabels (
117+ result : CommandResult ,
118+ labels : ProviderAuthLabels ,
119+ ) : {
88120 readonly status : ServerProviderStatusState ;
89121 readonly authStatus : ServerProviderAuthStatus ;
90122 readonly message ?: string ;
@@ -99,21 +131,20 @@ export function parseAuthStatusFromOutput(result: CommandResult): {
99131 return {
100132 status : "warning" ,
101133 authStatus : "unknown" ,
102- message : "Codex CLI authentication status command is unavailable in this Codex version." ,
134+ message : labels . unknownCommandMessage ,
103135 } ;
104136 }
105137
106138 if (
107139 lowerOutput . includes ( "not logged in" ) ||
108140 lowerOutput . includes ( "login required" ) ||
109141 lowerOutput . includes ( "authentication required" ) ||
110- lowerOutput . includes ( "run `codex login`" ) ||
111- lowerOutput . includes ( "run codex login" )
142+ labels . loginHints . some ( ( hint ) => lowerOutput . includes ( hint ) )
112143 ) {
113144 return {
114145 status : "error" ,
115146 authStatus : "unauthenticated" ,
116- message : "Codex CLI is not authenticated. Run `codex login` and try again." ,
147+ message : labels . unauthenticatedMessage ,
117148 } ;
118149 }
119150
@@ -139,15 +170,14 @@ export function parseAuthStatusFromOutput(result: CommandResult): {
139170 return {
140171 status : "error" ,
141172 authStatus : "unauthenticated" ,
142- message : "Codex CLI is not authenticated. Run `codex login` and try again." ,
173+ message : labels . unauthenticatedMessage ,
143174 } ;
144175 }
145176 if ( parsedAuth . attemptedJsonParse ) {
146177 return {
147178 status : "warning" ,
148179 authStatus : "unknown" ,
149- message :
150- "Could not verify Codex authentication status from JSON output (missing auth marker)." ,
180+ message : labels . jsonParseWarning ,
151181 } ;
152182 }
153183 if ( result . code === 0 ) {
@@ -158,12 +188,14 @@ export function parseAuthStatusFromOutput(result: CommandResult): {
158188 return {
159189 status : "warning" ,
160190 authStatus : "unknown" ,
161- message : detail
162- ? `Could not verify Codex authentication status. ${ detail } `
163- : "Could not verify Codex authentication status." ,
191+ message : detail ? `${ labels . verifyFailurePrefix } . ${ detail } ` : `${ labels . verifyFailurePrefix } .` ,
164192 } ;
165193}
166194
195+ export function parseAuthStatusFromOutput ( result : CommandResult ) {
196+ return parseAuthStatusWithLabels ( result , CODEX_AUTH_LABELS ) ;
197+ }
198+
167199// ── Codex CLI config detection ──────────────────────────────────────
168200
169201/**
@@ -239,10 +271,10 @@ const collectStreamAsString = <E>(stream: Stream.Stream<Uint8Array, E>): Effect.
239271 ( acc , chunk ) => acc + new TextDecoder ( ) . decode ( chunk ) ,
240272 ) ;
241273
242- const runCodexCommand = ( args : ReadonlyArray < string > ) =>
274+ const runCliCommand = ( binary : string , args : ReadonlyArray < string > ) =>
243275 Effect . gen ( function * ( ) {
244276 const spawner = yield * ChildProcessSpawner . ChildProcessSpawner ;
245- const command = ChildProcess . make ( "codex" , [ ...args ] , {
277+ const command = ChildProcess . make ( binary , [ ...args ] , {
246278 shell : process . platform === "win32" ,
247279 } ) ;
248280
@@ -260,26 +292,9 @@ const runCodexCommand = (args: ReadonlyArray<string>) =>
260292 return { stdout, stderr, code : exitCode } satisfies CommandResult ;
261293 } ) . pipe ( Effect . scoped ) ;
262294
263- const runClaudeCommand = ( args : ReadonlyArray < string > ) =>
264- Effect . gen ( function * ( ) {
265- const spawner = yield * ChildProcessSpawner . ChildProcessSpawner ;
266- const command = ChildProcess . make ( "claude" , [ ...args ] , {
267- shell : process . platform === "win32" ,
268- } ) ;
295+ const runCodexCommand = ( args : ReadonlyArray < string > ) => runCliCommand ( "codex" , args ) ;
269296
270- const child = yield * spawner . spawn ( command ) ;
271-
272- const [ stdout , stderr , exitCode ] = yield * Effect . all (
273- [
274- collectStreamAsString ( child . stdout ) ,
275- collectStreamAsString ( child . stderr ) ,
276- child . exitCode . pipe ( Effect . map ( Number ) ) ,
277- ] ,
278- { concurrency : "unbounded" } ,
279- ) ;
280-
281- return { stdout, stderr, code : exitCode } satisfies CommandResult ;
282- } ) . pipe ( Effect . scoped ) ;
297+ const runClaudeCommand = ( args : ReadonlyArray < string > ) => runCliCommand ( "claude" , args ) ;
283298
284299// ── Health check ────────────────────────────────────────────────────
285300
@@ -409,86 +424,8 @@ export const checkCodexProviderStatus: Effect.Effect<
409424
410425// ── Claude Code health check ────────────────────────────────────────
411426
412- export function parseClaudeAuthStatusFromOutput ( result : CommandResult ) : {
413- readonly status : ServerProviderStatusState ;
414- readonly authStatus : ServerProviderAuthStatus ;
415- readonly message ?: string ;
416- } {
417- const lowerOutput = `${ result . stdout } \n${ result . stderr } ` . toLowerCase ( ) ;
418-
419- if (
420- lowerOutput . includes ( "unknown command" ) ||
421- lowerOutput . includes ( "unrecognized command" ) ||
422- lowerOutput . includes ( "unexpected argument" )
423- ) {
424- return {
425- status : "warning" ,
426- authStatus : "unknown" ,
427- message :
428- "Claude Code authentication status command is unavailable in this version of Claude Code." ,
429- } ;
430- }
431-
432- if (
433- lowerOutput . includes ( "not logged in" ) ||
434- lowerOutput . includes ( "login required" ) ||
435- lowerOutput . includes ( "authentication required" ) ||
436- lowerOutput . includes ( "run `claude login`" ) ||
437- lowerOutput . includes ( "run claude login" )
438- ) {
439- return {
440- status : "error" ,
441- authStatus : "unauthenticated" ,
442- message : "Claude Code is not authenticated. Run `claude auth login` and try again." ,
443- } ;
444- }
445-
446- // `claude auth status` returns JSON with a `loggedIn` boolean.
447- const parsedAuth = ( ( ) => {
448- const trimmed = result . stdout . trim ( ) ;
449- if ( ! trimmed || ( ! trimmed . startsWith ( "{" ) && ! trimmed . startsWith ( "[" ) ) ) {
450- return { attemptedJsonParse : false as const , auth : undefined as boolean | undefined } ;
451- }
452- try {
453- return {
454- attemptedJsonParse : true as const ,
455- auth : extractAuthBoolean ( JSON . parse ( trimmed ) ) ,
456- } ;
457- } catch {
458- return { attemptedJsonParse : false as const , auth : undefined as boolean | undefined } ;
459- }
460- } ) ( ) ;
461-
462- if ( parsedAuth . auth === true ) {
463- return { status : "ready" , authStatus : "authenticated" } ;
464- }
465- if ( parsedAuth . auth === false ) {
466- return {
467- status : "error" ,
468- authStatus : "unauthenticated" ,
469- message : "Claude Code is not authenticated. Run `claude auth login` and try again." ,
470- } ;
471- }
472- if ( parsedAuth . attemptedJsonParse ) {
473- return {
474- status : "warning" ,
475- authStatus : "unknown" ,
476- message :
477- "Could not verify Claude Code authentication status from JSON output (missing auth marker)." ,
478- } ;
479- }
480- if ( result . code === 0 ) {
481- return { status : "ready" , authStatus : "authenticated" } ;
482- }
483-
484- const detail = detailFromResult ( result ) ;
485- return {
486- status : "warning" ,
487- authStatus : "unknown" ,
488- message : detail
489- ? `Could not verify Claude Code authentication status. ${ detail } `
490- : "Could not verify Claude Code authentication status." ,
491- } ;
427+ export function parseClaudeAuthStatusFromOutput ( result : CommandResult ) {
428+ return parseAuthStatusWithLabels ( result , CLAUDE_AUTH_LABELS ) ;
492429}
493430
494431export const checkClaudeCodeProviderStatus : Effect . Effect <
0 commit comments