@@ -10,20 +10,25 @@ import {
1010 resolveGithubCloneAuthToken
1111} from "@effect-template/lib/usecases/github-token-preflight"
1212import { validateGithubToken , type GithubTokenValidationResult } from "@effect-template/lib/usecases/github-token-validation"
13+ import { normalizeAccountLabel } from "@effect-template/lib/usecases/auth-helpers"
1314import { resolvePathFromCwd } from "@effect-template/lib/usecases/path-helpers"
1415import { Effect , Match } from "effect"
1516
1617import type {
18+ CodexAuthImportRequest ,
19+ CodexAuthLogoutRequest ,
20+ CodexAuthStatus ,
1721 GithubAuthLoginRequest ,
1822 GithubAuthLogoutRequest ,
1923 GithubAuthStatus ,
2024 GithubAuthTokenStatus
2125} from "../api/contracts.js"
22- import { ApiAuthRequiredError } from "../api/errors.js"
26+ import { ApiAuthRequiredError , ApiBadRequestError } from "../api/errors.js"
2327
2428export const githubAuthRequiredCommand = "docker-git auth github login --web"
2529export const githubAuthRequiredMessage = "GitHub authentication is required. Run: docker-git auth github login --web"
2630export const githubAuthEnvGlobalPath = defaultTemplateConfig . envGlobalPath
31+ export const codexAuthPath = defaultTemplateConfig . codexAuthPath
2732
2833const githubTokenKey = "GITHUB_TOKEN"
2934const githubTokenPrefix = "GITHUB_TOKEN__"
@@ -90,6 +95,29 @@ const resolveControllerEnvPath = (
9095) : string =>
9196 resolvePathFromCwd ( path , process . cwd ( ) , envGlobalPath )
9297
98+ const resolveControllerCodexPath = ( path : Path . Path , authPath : string ) : string =>
99+ resolvePathFromCwd ( path , process . cwd ( ) , authPath )
100+
101+ const resolveCodexLabel = ( label : string | null | undefined ) : string =>
102+ normalizeAccountLabel ( label ?? null , "default" )
103+
104+ const resolveCodexAccountPath = (
105+ path : Path . Path ,
106+ authPath : string ,
107+ label : string | null | undefined
108+ ) : string => {
109+ const basePath = resolveControllerCodexPath ( path , authPath )
110+ const normalizedLabel = resolveCodexLabel ( label )
111+ return normalizedLabel === "default" ? basePath : path . join ( basePath , normalizedLabel )
112+ }
113+
114+ const resolveCodexAuthFilePath = (
115+ path : Path . Path ,
116+ authPath : string ,
117+ label : string | null | undefined
118+ ) : string =>
119+ path . join ( resolveCodexAccountPath ( path , authPath , label ) , "auth.json" )
120+
93121const readGithubAuthTokens = (
94122 envGlobalPath : string
95123) : Effect . Effect < GithubAuthStatus , PlatformError , FileSystem . FileSystem | Path . Path > =>
@@ -144,6 +172,89 @@ export const logoutGithubAuth = (request: GithubAuthLogoutRequest) =>
144172 return yield * _ ( readGithubAuthTokens ( githubAuthEnvGlobalPath ) )
145173 } )
146174
175+ const codexAuthStatus = (
176+ present : boolean ,
177+ label : string ,
178+ authPath : string
179+ ) : CodexAuthStatus => ( {
180+ label,
181+ message : present
182+ ? "Codex auth imported into controller state."
183+ : "Codex auth not found in controller state." ,
184+ present,
185+ authPath
186+ } )
187+
188+ export const readCodexAuthStatus = (
189+ label ?: string | null | undefined
190+ ) : Effect . Effect < CodexAuthStatus , PlatformError , FileSystem . FileSystem | Path . Path > =>
191+ Effect . gen ( function * ( _ ) {
192+ const fs = yield * _ ( FileSystem . FileSystem )
193+ const path = yield * _ ( Path . Path )
194+ const resolvedLabel = resolveCodexLabel ( label )
195+ const resolvedAuthPath = resolveCodexAuthFilePath ( path , codexAuthPath , label )
196+ const exists = yield * _ ( fs . exists ( resolvedAuthPath ) )
197+ if ( ! exists ) {
198+ return codexAuthStatus ( false , resolvedLabel , resolvedAuthPath )
199+ }
200+
201+ const info = yield * _ ( fs . stat ( resolvedAuthPath ) )
202+ if ( info . type !== "File" ) {
203+ return codexAuthStatus ( false , resolvedLabel , resolvedAuthPath )
204+ }
205+
206+ return codexAuthStatus ( true , resolvedLabel , resolvedAuthPath )
207+ } )
208+
209+ export const importCodexAuth = (
210+ request : CodexAuthImportRequest
211+ ) : Effect . Effect < CodexAuthStatus , ApiBadRequestError | PlatformError , FileSystem . FileSystem | Path . Path > =>
212+ Effect . gen ( function * ( _ ) {
213+ const fs = yield * _ ( FileSystem . FileSystem )
214+ const path = yield * _ ( Path . Path )
215+ const resolvedAuthPath = resolveCodexAuthFilePath ( path , codexAuthPath , request . label )
216+ const parsed = yield * _ (
217+ Effect . try ( {
218+ try : ( ) => JSON . parse ( request . authText ) ,
219+ catch : ( cause ) =>
220+ new ApiBadRequestError ( {
221+ message : "Invalid Codex auth JSON." ,
222+ details : cause
223+ } )
224+ } )
225+ )
226+
227+ if ( typeof parsed !== "object" || parsed === null || Array . isArray ( parsed ) ) {
228+ return yield * _ (
229+ Effect . fail (
230+ new ApiBadRequestError ( {
231+ message : "Codex auth JSON must be an object."
232+ } )
233+ )
234+ )
235+ }
236+
237+ yield * _ ( fs . makeDirectory ( path . dirname ( resolvedAuthPath ) , { recursive : true } ) )
238+ yield * _ ( fs . writeFileString ( resolvedAuthPath , JSON . stringify ( parsed , null , 2 ) ) )
239+ return yield * _ ( readCodexAuthStatus ( request . label ) )
240+ } )
241+
242+ export const logoutCodexAuth = (
243+ request : CodexAuthLogoutRequest
244+ ) : Effect . Effect < CodexAuthStatus , PlatformError , FileSystem . FileSystem | Path . Path > =>
245+ Effect . gen ( function * ( _ ) {
246+ const fs = yield * _ ( FileSystem . FileSystem )
247+ const path = yield * _ ( Path . Path )
248+ const resolvedAuthPath = resolveCodexAuthFilePath ( path , codexAuthPath , request . label )
249+ const exists = yield * _ ( fs . exists ( resolvedAuthPath ) )
250+
251+ if ( exists ) {
252+ yield * _ ( fs . remove ( resolvedAuthPath ) )
253+ }
254+
255+ return yield * _ ( readCodexAuthStatus ( request . label ) )
256+ } )
257+
147258export const ensureGithubAuthForCreate = ( config : {
148259 readonly repoUrl : string
149260 readonly gitTokenLabel ?: string | undefined
0 commit comments