import { dirname, join } from "jsr:@std/path@1"; export type CodexAuthentication = | { kind: "api-key"; token: string } | { kind: "persistent" }; export function codexAuthentication(token?: string): CodexAuthentication { return token === undefined ? { kind: "persistent" } : { kind: "api-key", token }; } export async function retryInvalidToken< T extends { status: { success: boolean }; error: string }, >(attempt: () => Promise, relogin: () => Promise): Promise { let result = await attempt(); if ( !result.status.success && result.error.includes("invalid_refresh_token") ) { await relogin(); result = await attempt(); } return result; } async function readCodexAuth(path: string): Promise { const stat = await Deno.lstat(path); if (!stat.isFile || stat.isSymlink) { throw new Error("Codex login must be a regular file"); } if ((stat.mode! & 0o077) !== 0) { throw new Error("Codex login must be private"); } const text = await Deno.readTextFile(path); let value: unknown; try { value = JSON.parse(text); } catch { throw new Error("invalid Codex login"); } const record = value as Record | null; const tokens = record?.tokens as Record | null; if ( record === null || typeof record !== "object" || typeof record.auth_mode !== "string" || record.auth_mode === "" || tokens === null || typeof tokens !== "object" || !["access_token", "refresh_token", "id_token", "account_id"].every( (name) => typeof tokens[name] === "string" && tokens[name] !== "", ) ) { throw new Error("invalid Codex login"); } return text; } export async function stageCodexAuth( canonicalPath: string, profileHome: string, uid: number, gid: number, ): Promise { const codexHome = join(profileHome, ".codex"); await Deno.mkdir(codexHome, { recursive: true, mode: 0o700 }); await Deno.chmod(codexHome, 0o700); await Deno.chown(codexHome, uid, gid); const stagedPath = join(codexHome, "auth.json"); try { await Deno.writeTextFile(stagedPath, await readCodexAuth(canonicalPath), { mode: 0o600, }); await Deno.chmod(stagedPath, 0o600); await Deno.chown(stagedPath, uid, gid); } catch (error) { if (!(error instanceof Deno.errors.NotFound)) throw error; } return stagedPath; } export async function persistCodexAuth( stagedPath: string, canonicalPath: string, ): Promise { const text = await readCodexAuth(stagedPath); const directory = dirname(canonicalPath); await Deno.mkdir(directory, { recursive: true, mode: 0o700 }); const temporary = await Deno.makeTempFile({ dir: directory }); try { await Deno.writeTextFile(temporary, text, { mode: 0o600 }); await Deno.chmod(temporary, 0o600); await Deno.rename(temporary, canonicalPath); } catch (error) { await Deno.remove(temporary).catch(() => undefined); throw error; } }