@@ -51,6 +51,16 @@ const DEFAULTS: Pick<Config, "enabled" | "base_url" | "max_chars" | "debug" | "f
5151 fail_on_error : false ,
5252} ;
5353
54+ const CodexAuthSchema = z
55+ . object ( {
56+ tokens : z
57+ . object ( {
58+ id_token : z . string ( ) . optional ( ) ,
59+ } )
60+ . optional ( ) ,
61+ } )
62+ . passthrough ( ) ;
63+
5464function parseBoolean ( value : unknown ) : boolean | undefined {
5565 if ( typeof value === "boolean" ) return value ;
5666 if ( typeof value !== "string" ) return undefined ;
@@ -129,6 +139,36 @@ async function readConfigFile(file: string): Promise<Partial<Config> | undefined
129139 }
130140}
131141
142+ function readJwtPayload ( token : string ) : Record < string , unknown > | undefined {
143+ const payload = token . split ( "." ) [ 1 ] ;
144+ if ( ! payload ) return undefined ;
145+
146+ try {
147+ const parsed = JSON . parse ( Buffer . from ( payload , "base64url" ) . toString ( "utf-8" ) ) as unknown ;
148+ if ( parsed == null || typeof parsed !== "object" || Array . isArray ( parsed ) ) return undefined ;
149+ return parsed as Record < string , unknown > ;
150+ } catch {
151+ return undefined ;
152+ }
153+ }
154+
155+ async function readCodexUserEmail ( authFile : string ) : Promise < string | undefined > {
156+ try {
157+ const raw = JSON . parse ( await fs . readFile ( authFile , "utf-8" ) ) as unknown ;
158+ const auth = CodexAuthSchema . parse ( raw ) ;
159+ const token = auth . tokens ?. id_token ;
160+ if ( ! token ) return undefined ;
161+
162+ const email = readJwtPayload ( token ) ?. email ;
163+ if ( typeof email !== "string" ) return undefined ;
164+
165+ const trimmed = email . trim ( ) ;
166+ return trimmed . length > 0 ? trimmed : undefined ;
167+ } catch {
168+ return undefined ;
169+ }
170+ }
171+
132172function getVar ( suffix : string , env : Record < string , string | undefined > ) : string | undefined {
133173 return env [ `LANGFUSE_CODEX_${ suffix } ` ] ?? env [ `LANGFUSE_${ suffix } ` ] ;
134174}
@@ -153,6 +193,11 @@ function readEnvConfig(env: Record<string, string | undefined>): Partial<Config>
153193
154194const getHomeDir = ( ) => process . env . HOME ?? os . homedir ( ) ;
155195
196+ function getCodexAuthFile ( home : string , env : Record < string , string | undefined > ) : string {
197+ const codexHome = env . CODEX_HOME ?. trim ( ) ;
198+ return codexHome ? path . join ( codexHome , "auth.json" ) : path . join ( home , ".codex" , "auth.json" ) ;
199+ }
200+
156201export async function getConfig ( options ?: {
157202 home ?: string ;
158203 cwd ?: string ;
@@ -167,9 +212,14 @@ export async function getConfig(options?: {
167212 readConfigFile ( path . join ( cwd , ".codex" , "langfuse.json" ) ) ,
168213 ] ) ;
169214 const envConfig = readEnvConfig ( env ) ;
215+ const explicitUserId = globalConfig ?. user_id ?? localConfig ?. user_id ?? envConfig . user_id ;
216+ const codexUserId = explicitUserId
217+ ? undefined
218+ : await readCodexUserEmail ( getCodexAuthFile ( home , env ) ) ;
170219
171220 return ConfigSchema . parse ( {
172221 ...DEFAULTS ,
222+ ...( codexUserId ? { user_id : codexUserId } : { } ) ,
173223 ...globalConfig ,
174224 ...localConfig ,
175225 ...envConfig ,
0 commit comments