import { fetchJson } from "./provider-usage.fetch.shared.js"; import { clampPercent, PROVIDER_LABELS } from "./provider-usage.shared.js"; import type { ProviderUsageSnapshot, UsageWindow } from "./provider-usage.types.js"; type CodexUsageResponse = { rate_limit?: { primary_window?: { limit_window_seconds?: number; used_percent?: number; reset_at?: number; }; secondary_window?: { limit_window_seconds?: number; used_percent?: number; reset_at?: number; }; }; plan_type?: string; credits?: { balance?: number | string | null }; }; export async function fetchCodexUsage( token: string, accountId: string | undefined, timeoutMs: number, fetchFn: typeof fetch, ): Promise { const headers: Record = { Authorization: `Bearer ${token}`, "User-Agent": "CodexBar", Accept: "application/json", }; if (accountId) headers["ChatGPT-Account-Id"] = accountId; const res = await fetchJson( "https://chatgpt.com/backend-api/wham/usage", { method: "GET", headers }, timeoutMs, fetchFn, ); if (res.status === 401 || res.status === 403) { return { provider: "openai-codex", displayName: PROVIDER_LABELS["openai-codex"], windows: [], error: "Token expired", }; } if (!res.ok) { return { provider: "openai-codex", displayName: PROVIDER_LABELS["openai-codex"], windows: [], error: `HTTP ${res.status}`, }; } const data = (await res.json()) as CodexUsageResponse; const windows: UsageWindow[] = []; if (data.rate_limit?.primary_window) { const pw = data.rate_limit.primary_window; const windowHours = Math.round((pw.limit_window_seconds || 10800) / 3600); windows.push({ label: `${windowHours}h`, usedPercent: clampPercent(pw.used_percent || 0), resetAt: pw.reset_at ? pw.reset_at * 1000 : undefined, }); } if (data.rate_limit?.secondary_window) { const sw = data.rate_limit.secondary_window; const windowHours = Math.round((sw.limit_window_seconds || 86400) / 3600); const label = windowHours >= 24 ? "Day" : `${windowHours}h`; windows.push({ label, usedPercent: clampPercent(sw.used_percent || 0), resetAt: sw.reset_at ? sw.reset_at * 1000 : undefined, }); } let plan = data.plan_type; if (data.credits?.balance !== undefined && data.credits.balance !== null) { const balance = typeof data.credits.balance === "number" ? data.credits.balance : parseFloat(data.credits.balance) || 0; plan = plan ? `${plan} ($${balance.toFixed(2)})` : `$${balance.toFixed(2)}`; } return { provider: "openai-codex", displayName: PROVIDER_LABELS["openai-codex"], windows, plan, }; }