@@ -6,7 +6,7 @@ import type { CodexAuthState, ResponseInputItem } from "./types.ts";
66const CODEX_REFRESH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann" ;
77const CODEX_REFRESH_TOKEN_URL = "https://auth.openai.com/oauth/token" ;
88const CODEX_ORIGINATOR = "codex_cli_rs" ;
9- const CODEX_USER_AGENT = "codex_cli_rs/0.99 .0 (ai.ubq.fi)" ;
9+ const CODEX_USER_AGENT = "codex_cli_rs/0.100 .0 (ai.ubq.fi)" ;
1010const CODEX_CLIENT_VERSION = ( ( ) => {
1111 const match = CODEX_USER_AGENT . match ( / c o d e x _ c l i _ r s \/ ( [ 0 - 9 ] + (?: \. [ 0 - 9 ] + ) { 1 , 2 } ) / ) ;
1212 return match ? match [ 1 ] : null ;
@@ -111,44 +111,106 @@ export const cacheCodexAuth = (auth: CodexAuthState): void => {
111111} ;
112112
113113const loadAuthSeedFromEnv = ( ) : CodexAuthState => {
114- if ( ! config . codexAuthJsonB64 ) {
114+ if ( config . isDeploy ) {
115+ if ( ! config . codexAuthJsonB64 ) {
116+ throw new CodexError (
117+ "Codex auth missing: CODEX_AUTH_JSON_B64 unset and no KV entry." ,
118+ "codex_auth_missing" ,
119+ 503 ,
120+ ) ;
121+ }
122+ }
123+
124+ if ( config . codexAuthJsonB64 ) {
125+ let decoded : string ;
126+ try {
127+ decoded = decodeBase64ToString ( config . codexAuthJsonB64 ) ;
128+ } catch ( error ) {
129+ throw new CodexError (
130+ "Codex auth invalid: CODEX_AUTH_JSON_B64 is not valid base64." ,
131+ "codex_auth_invalid" ,
132+ 503 ,
133+ error ,
134+ ) ;
135+ }
136+ let parsed : unknown ;
137+ try {
138+ parsed = JSON . parse ( decoded ) as unknown ;
139+ } catch ( error ) {
140+ throw new CodexError (
141+ "Codex auth invalid: CODEX_AUTH_JSON_B64 is not valid JSON." ,
142+ "codex_auth_invalid" ,
143+ 503 ,
144+ error ,
145+ ) ;
146+ }
147+ const tokenData = parseCodexAuthFromAuthJson ( parsed ) ;
148+ if ( ! tokenData ) {
149+ throw new CodexError (
150+ "Codex auth invalid: CODEX_AUTH_JSON_B64 does not look like a Codex auth.json." ,
151+ "codex_auth_invalid" ,
152+ 503 ,
153+ ) ;
154+ }
155+ return { ...tokenData , updated_at_ms : Date . now ( ) } ;
156+ }
157+
158+ if ( config . isDeploy ) {
115159 throw new CodexError (
116160 "Codex auth missing: CODEX_AUTH_JSON_B64 unset and no KV entry." ,
117161 "codex_auth_missing" ,
118162 503 ,
119163 ) ;
120164 }
121- let decoded : string ;
122- try {
123- decoded = decodeBase64ToString ( config . codexAuthJsonB64 ) ;
124- } catch ( error ) {
125- throw new CodexError (
126- "Codex auth invalid: CODEX_AUTH_JSON_B64 is not valid base64." ,
127- "codex_auth_invalid" ,
128- 503 ,
129- error ,
130- ) ;
165+
166+ return loadAuthSeedFromDisk ( ) ;
167+ } ;
168+
169+ const loadAuthSeedFromDisk = ( ) : CodexAuthState => {
170+ if ( ! config . isDeploy ) {
171+ const home = ( Deno as unknown as { homeDir ?: ( ) => string | null } ) . homeDir ?.( ) ?? Deno . env . get ( "HOME" ) ;
172+ if ( ! home ) {
173+ throw new CodexError ( "Could not resolve home directory for ~/.codex/auth.json." , "codex_auth_invalid" , 503 ) ;
174+ }
175+ try {
176+ const raw = Deno . readTextFileSync ( `${ home } /.codex/auth.json` ) ;
177+ const parsed = JSON . parse ( raw ) as unknown ;
178+ const tokenData = parseCodexAuthFromAuthJson ( parsed ) ;
179+ if ( ! tokenData ) {
180+ throw new CodexError (
181+ "Codex auth invalid: ~/.codex/auth.json does not look like a Codex auth.json." ,
182+ "codex_auth_invalid" ,
183+ 503 ,
184+ ) ;
185+ }
186+ return { ...tokenData , updated_at_ms : Date . now ( ) } ;
187+ } catch ( error ) {
188+ if ( error instanceof CodexError ) throw error ;
189+ throw new CodexError (
190+ "Codex auth invalid: ~/.codex/auth.json is missing or unreadable." ,
191+ "codex_auth_invalid" ,
192+ 503 ,
193+ error ,
194+ ) ;
195+ }
131196 }
132- let parsed : unknown ;
133- try {
134- parsed = JSON . parse ( decoded ) as unknown ;
135- } catch ( error ) {
136- throw new CodexError (
137- "Codex auth invalid: CODEX_AUTH_JSON_B64 is not valid JSON." ,
138- "codex_auth_invalid" ,
139- 503 ,
140- error ,
141- ) ;
197+ throw new CodexError ( "Codex auth missing: CODEX_AUTH_JSON_B64 unset and no KV entry." , "codex_auth_missing" , 503 ) ;
198+ } ;
199+
200+ const getConfiguredCodexAuthSeed = ( ) : CodexAuthState | null => {
201+ if ( ! config . codexAuthJsonB64 ) {
202+ return loadAuthSeedFromEnv ( ) ; // throws when unavailable in deploy or returns fallback in local
142203 }
143- const tokenData = parseCodexAuthFromAuthJson ( parsed ) ;
144- if ( ! tokenData ) {
145- throw new CodexError (
146- "Codex auth invalid: CODEX_AUTH_JSON_B64 does not look like a Codex auth.json." ,
147- "codex_auth_invalid" ,
148- 503 ,
149- ) ;
204+ try {
205+ return loadAuthSeedFromEnv ( ) ;
206+ } catch {
207+ if ( config . isDeploy ) return null ;
208+ try {
209+ return loadAuthSeedFromDisk ( ) ;
210+ } catch {
211+ return null ;
212+ }
150213 }
151- return { ...tokenData , updated_at_ms : Date . now ( ) } ;
152214} ;
153215
154216const getAuthEntry = async ( ) : Promise < {
@@ -158,18 +220,44 @@ const getAuthEntry = async (): Promise<{
158220} > => {
159221 const kv = await kvPromise ;
160222 if ( ! kv ) {
161- const auth = cachedAuth ?? loadAuthSeedFromEnv ( ) ;
223+ const auth = cachedAuth ?? getConfiguredCodexAuthSeed ( ) ;
224+ if ( ! auth ) {
225+ throw new CodexError (
226+ "Codex auth missing: CODEX_AUTH_JSON_B64 unset and no KV entry." ,
227+ "codex_auth_missing" ,
228+ 503 ,
229+ ) ;
230+ }
162231 cachedAuth = auth ;
163232 return { kv : null , entry : null , auth } ;
164233 }
165234
166235 const entry = await kv . get < CodexAuthState > ( CODEX_KV_KEY ) ;
167236 if ( entry . value ) {
237+ if ( ! config . isDeploy ) {
238+ try {
239+ const localSeed = getConfiguredCodexAuthSeed ( ) ;
240+ if ( localSeed ) {
241+ cachedAuth = localSeed ;
242+ await kv . set ( CODEX_KV_KEY , localSeed ) ;
243+ return { kv, entry, auth : localSeed } ;
244+ }
245+ } catch {
246+ // Keep working from persisted KV credentials when local seed loading fails.
247+ }
248+ }
168249 cachedAuth = entry . value ;
169250 return { kv, entry, auth : entry . value } ;
170251 }
171252
172- const seed = loadAuthSeedFromEnv ( ) ;
253+ const seed = getConfiguredCodexAuthSeed ( ) ;
254+ if ( ! seed ) {
255+ throw new CodexError (
256+ "Codex auth missing: CODEX_AUTH_JSON_B64 unset and no KV entry." ,
257+ "codex_auth_missing" ,
258+ 503 ,
259+ ) ;
260+ }
173261 await kv . set ( CODEX_KV_KEY , seed ) ;
174262 cachedAuth = seed ;
175263 return { kv, entry : null , auth : seed } ;
0 commit comments