import type { CodexAuth, CodexUsageResponse } from "@/types/codex"; const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage"; function isNumber(value: unknown): value is number { return typeof value === "number" && Number.isFinite(value); } export function parseCodexUsageResponse(data: unknown): CodexUsageResponse { if (!data || typeof data !== "object") { throw new Error("INVALID_CODEX_RESPONSE"); } const value = data as Record; const rateLimit = value.rate_limit as Record | undefined; const primary = rateLimit?.primary_window as Record | undefined; const secondary = rateLimit?.secondary_window as Record | undefined; const credits = value.credits as Record | undefined; const validWindow = (window: Record | undefined) => !!window && isNumber(window.used_percent) && isNumber(window.reset_at) && isNumber(window.limit_window_seconds); if ( typeof value.plan_type !== "string" || !validWindow(primary) || !validWindow(secondary) || !credits || typeof credits.has_credits !== "boolean" || typeof credits.unlimited !== "boolean" || !isNumber(credits.balance) ) { throw new Error("INVALID_CODEX_RESPONSE"); } const primaryWindow = primary as Record; const secondaryWindow = secondary as Record; return { plan_type: value.plan_type, rate_limit: { primary_window: { used_percent: primaryWindow.used_percent as number, reset_at: primaryWindow.reset_at as number, limit_window_seconds: primaryWindow.limit_window_seconds as number, }, secondary_window: { used_percent: secondaryWindow.used_percent as number, reset_at: secondaryWindow.reset_at as number, limit_window_seconds: secondaryWindow.limit_window_seconds as number, }, }, credits: { has_credits: credits.has_credits, unlimited: credits.unlimited, balance: credits.balance, }, }; } export async function fetchCodexUsage(auth: CodexAuth): Promise { const headers: Record = { Authorization: `Bearer ${auth.accessToken}`, "Content-Type": "application/json", }; if (auth.accountId) { headers["ChatGPT-Account-Id"] = auth.accountId; } const response = await fetch(USAGE_URL, { headers }); if (response.status === 401 || response.status === 403) { throw new Error("TOKEN_EXPIRED"); } if (!response.ok) { await response.text().catch(() => null); throw new Error(`HTTP_ERROR_${response.status}`); } const data: unknown = await response.json(); return parseCodexUsageResponse(data); }