@@ -5,30 +5,14 @@ import { Effect } from "effect"
55
66import type { AgentMode , ParseError , TemplateConfig } from "../core/domain.js"
77import { normalizeAccountLabel } from "./auth-helpers.js"
8+ import { hasNonEmptyFile } from "./auth-sync-helpers.js"
89
910const autoOptionError = ( reason : string ) : ParseError => ( {
1011 _tag : "InvalidOption" ,
1112 option : "--auto" ,
1213 reason
1314} )
1415
15- const isNonEmptyFile = (
16- fs : FileSystem . FileSystem ,
17- filePath : string
18- ) : Effect . Effect < boolean , PlatformError > =>
19- Effect . gen ( function * ( _ ) {
20- const exists = yield * _ ( fs . exists ( filePath ) )
21- if ( ! exists ) {
22- return false
23- }
24- const info = yield * _ ( fs . stat ( filePath ) )
25- if ( info . type !== "File" ) {
26- return false
27- }
28- const text = yield * _ ( fs . readFileString ( filePath ) , Effect . orElseSucceed ( ( ) => "" ) )
29- return text . trim ( ) . length > 0
30- } )
31-
3216const isRegularFile = (
3317 fs : FileSystem . FileSystem ,
3418 filePath : string
@@ -51,7 +35,7 @@ const hasCodexAuth = (
5135 const authPath = normalized === "default"
5236 ? `${ rootPath } /auth.json`
5337 : `${ rootPath } /${ normalized } /auth.json`
54- return isNonEmptyFile ( fs , authPath )
38+ return hasNonEmptyFile ( fs , authPath )
5539}
5640
5741const resolveClaudeAccountPath = ( rootPath : string , label : string | undefined ) : ReadonlyArray < string > => {
@@ -69,7 +53,7 @@ const hasClaudeAuth = (
6953) : Effect . Effect < boolean , PlatformError > =>
7054 Effect . gen ( function * ( _ ) {
7155 for ( const accountPath of resolveClaudeAccountPath ( rootPath , label ) ) {
72- const oauthToken = yield * _ ( isNonEmptyFile ( fs , `${ accountPath } /.oauth-token` ) )
56+ const oauthToken = yield * _ ( hasNonEmptyFile ( fs , `${ accountPath } /.oauth-token` ) )
7357 if ( oauthToken ) {
7458 return true
7559 }
@@ -88,6 +72,53 @@ const hasClaudeAuth = (
8872 return false
8973 } )
9074
75+ const resolveClaudeRoot = ( codexSharedAuthPath : string ) : string =>
76+ `${ codexSharedAuthPath . slice ( 0 , codexSharedAuthPath . lastIndexOf ( "/" ) ) } /claude`
77+
78+ const resolveAvailableAgentAuth = (
79+ fs : FileSystem . FileSystem ,
80+ config : Pick < TemplateConfig , "claudeAuthLabel" | "codexAuthLabel" | "codexSharedAuthPath" >
81+ ) : Effect . Effect < { readonly claudeAvailable : boolean ; readonly codexAvailable : boolean } , PlatformError > =>
82+ Effect . gen ( function * ( _ ) {
83+ const claudeAvailable = yield * _ (
84+ hasClaudeAuth ( fs , resolveClaudeRoot ( config . codexSharedAuthPath ) , config . claudeAuthLabel )
85+ )
86+ const codexAvailable = yield * _ ( hasCodexAuth ( fs , config . codexSharedAuthPath , config . codexAuthLabel ) )
87+ return { claudeAvailable, codexAvailable }
88+ } )
89+
90+ const resolveExplicitAutoAgentMode = (
91+ available : { readonly claudeAvailable : boolean ; readonly codexAvailable : boolean } ,
92+ mode : AgentMode | undefined
93+ ) : Effect . Effect < AgentMode | undefined , ParseError > => {
94+ if ( mode === "claude" ) {
95+ return available . claudeAvailable
96+ ? Effect . succeed ( "claude" )
97+ : Effect . fail ( autoOptionError ( "Claude auth not found" ) )
98+ }
99+ if ( mode === "codex" ) {
100+ return available . codexAvailable
101+ ? Effect . succeed ( "codex" )
102+ : Effect . fail ( autoOptionError ( "Codex auth not found" ) )
103+ }
104+ return Effect . sync ( ( ) => mode )
105+ }
106+
107+ const pickRandomAutoAgentMode = (
108+ available : { readonly claudeAvailable : boolean ; readonly codexAvailable : boolean }
109+ ) : Effect . Effect < AgentMode , ParseError > => {
110+ if ( ! available . claudeAvailable && ! available . codexAvailable ) {
111+ return Effect . fail ( autoOptionError ( "no Claude or Codex auth found" ) )
112+ }
113+ if ( available . claudeAvailable && ! available . codexAvailable ) {
114+ return Effect . succeed ( "claude" )
115+ }
116+ if ( ! available . claudeAvailable && available . codexAvailable ) {
117+ return Effect . succeed ( "codex" )
118+ }
119+ return Effect . sync ( ( ) => ( process . hrtime . bigint ( ) % 2n === 0n ? "claude" : "codex" ) )
120+ }
121+
91122export const resolveAutoAgentMode = (
92123 config : Pick < TemplateConfig , "agentAuto" | "agentMode" | "claudeAuthLabel" | "codexAuthLabel" | "codexSharedAuthPath" >
93124) : Effect . Effect < AgentMode | undefined , ParseError | PlatformError , FileSystem . FileSystem | Path . Path > =>
@@ -98,36 +129,11 @@ export const resolveAutoAgentMode = (
98129 return config . agentMode
99130 }
100131
101- if ( config . agentMode === "claude" ) {
102- const claudeRoot = `${ config . codexSharedAuthPath . slice ( 0 , config . codexSharedAuthPath . lastIndexOf ( "/" ) ) } /claude`
103- const available = yield * _ ( hasClaudeAuth ( fs , claudeRoot , config . claudeAuthLabel ) )
104- if ( ! available ) {
105- return yield * _ ( Effect . fail ( autoOptionError ( "Claude auth not found" ) ) )
106- }
107- return "claude"
108- }
109-
110- if ( config . agentMode === "codex" ) {
111- const available = yield * _ ( hasCodexAuth ( fs , config . codexSharedAuthPath , config . codexAuthLabel ) )
112- if ( ! available ) {
113- return yield * _ ( Effect . fail ( autoOptionError ( "Codex auth not found" ) ) )
114- }
115- return "codex"
116- }
117-
118- const claudeRoot = `${ config . codexSharedAuthPath . slice ( 0 , config . codexSharedAuthPath . lastIndexOf ( "/" ) ) } /claude`
119- const claudeAvailable = yield * _ ( hasClaudeAuth ( fs , claudeRoot , config . claudeAuthLabel ) )
120- const codexAvailable = yield * _ ( hasCodexAuth ( fs , config . codexSharedAuthPath , config . codexAuthLabel ) )
121-
122- if ( ! claudeAvailable && ! codexAvailable ) {
123- return yield * _ ( Effect . fail ( autoOptionError ( "no Claude or Codex auth found" ) ) )
124- }
125- if ( claudeAvailable && ! codexAvailable ) {
126- return "claude"
127- }
128- if ( ! claudeAvailable && codexAvailable ) {
129- return "codex"
132+ const available = yield * _ ( resolveAvailableAgentAuth ( fs , config ) )
133+ const explicitMode = yield * _ ( resolveExplicitAutoAgentMode ( available , config . agentMode ) )
134+ if ( explicitMode !== undefined ) {
135+ return explicitMode
130136 }
131137
132- return process . hrtime . bigint ( ) % 2n === 0n ? "claude" : "codex"
138+ return yield * _ ( pickRandomAutoAgentMode ( available ) )
133139 } )
0 commit comments