export const CODEX_OAUTH_ISSUER = 'https://auth.openai.com' export const CODEX_REFRESH_URL = `${CODEX_OAUTH_ISSUER}/oauth/token` export const DEFAULT_CODEX_OAUTH_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann' export const DEFAULT_CODEX_OAUTH_CALLBACK_PORT = 1455 export const CODEX_OAUTH_SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke' export const CODEX_OAUTH_ORIGINATOR = 'codex_cli_rs' export const CODEX_API_KEY_TOKEN_NAME = 'openai-api-key' export const CODEX_ID_TOKEN_SUBJECT_TYPE = 'urn:ietf:params:oauth:token-type:id_token' export const CODEX_TOKEN_EXCHANGE_GRANT = 'urn:ietf:params:oauth:grant-type:token-exchange' export function asTrimmedString(value: unknown): string | undefined { if (typeof value !== 'string') return undefined const trimmed = value.trim() return trimmed ? trimmed : undefined } export function getCodexOAuthClientId( env: NodeJS.ProcessEnv = process.env, ): string { return asTrimmedString(env.CODEX_OAUTH_CLIENT_ID) ?? DEFAULT_CODEX_OAUTH_CLIENT_ID } export function getCodexOAuthCallbackPort( env: NodeJS.ProcessEnv = process.env, ): number { const rawPort = asTrimmedString(env.CODEX_OAUTH_CALLBACK_PORT) if (!rawPort) { return DEFAULT_CODEX_OAUTH_CALLBACK_PORT } const parsed = Number.parseInt(rawPort, 10) if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) { return parsed } return DEFAULT_CODEX_OAUTH_CALLBACK_PORT } export function decodeJwtPayload( token: string, ): Record | undefined { const parts = token.split('.') if (parts.length < 2) return undefined try { const normalized = parts[1].replace(/-/g, '+').replace(/_/g, '/') const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4) const json = Buffer.from(padded, 'base64').toString('utf8') const parsed = JSON.parse(json) return parsed && typeof parsed === 'object' ? (parsed as Record) : undefined } catch { return undefined } } export function parseChatgptAccountId( token: string | undefined, ): string | undefined { if (!token) return undefined const payload = decodeJwtPayload(token) const nestedAuth = payload?.['https://api.openai.com/auth'] && typeof payload['https://api.openai.com/auth'] === 'object' ? (payload['https://api.openai.com/auth'] as Record) : undefined return ( asTrimmedString( nestedAuth?.chatgpt_account_id ?? payload?.['https://api.openai.com/auth.chatgpt_account_id'] ?? payload?.chatgpt_account_id, ) ?? undefined ) } export function escapeHtml(value: string): string { return value.replace(/[&<>"']/g, char => { switch (char) { case '&': return '&' case '<': return '<' case '>': return '>' case '"': return '"' case '\'': return ''' default: return char } }) } export async function exchangeCodexIdTokenForApiKey( idToken: string, ): Promise { const body = new URLSearchParams({ grant_type: CODEX_TOKEN_EXCHANGE_GRANT, client_id: getCodexOAuthClientId(), requested_token: CODEX_API_KEY_TOKEN_NAME, subject_token: idToken, subject_token_type: CODEX_ID_TOKEN_SUBJECT_TYPE, }) const response = await fetch(CODEX_REFRESH_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body, signal: AbortSignal.timeout(15_000), }) if (!response.ok) { const bodyText = await response.text().catch(() => '') throw new Error( bodyText.trim() ? `Codex API key exchange failed (${response.status}): ${bodyText.trim()}` : `Codex API key exchange failed with status ${response.status}.`, ) } const payload = (await response.json()) as { access_token?: string } const apiKey = asTrimmedString(payload.access_token) if (!apiKey) { throw new Error( 'Codex API key exchange completed, but no API key token was returned.', ) } return apiKey }