export type WeeklyQuota = { usedPercent: number; remainingPercent: number; resetsAt?: number; }; type QuotaWindow = { usedPercent: number; windowMinutes?: number; resetsAt?: number; }; function asRecord(value: unknown): Record | undefined { return typeof value === "object" && value !== null && !Array.isArray(value) ? (value as Record) : undefined; } function numberValue(record: Record, keys: readonly string[]): number | undefined { for (const key of keys) { const value = record[key]; if (typeof value === "number" && Number.isFinite(value)) return value; } return undefined; } function readWindow(value: unknown): QuotaWindow | undefined { const record = asRecord(value); if (!record) return undefined; const usedPercent = numberValue(record, ["used_percent", "usedPercent"]); if (usedPercent === undefined) return undefined; const windowSeconds = numberValue(record, ["limit_window_seconds", "window_seconds"]); return { usedPercent, windowMinutes: numberValue(record, ["window_minutes", "windowMinutes"]) ?? (windowSeconds === undefined ? undefined : windowSeconds / 60), resetsAt: numberValue(record, ["resets_at", "reset_at", "resetsAt", "resetAt"]), }; } function collectWindows(value: unknown, windows: QuotaWindow[] = []): QuotaWindow[] { const record = asRecord(value); if (!record) return windows; const window = readWindow(record); if (window) windows.push(window); for (const nested of Object.values(record)) collectWindows(nested, windows); return windows; } /** * Extract the rolling seven-day Codex quota from the undocumented ChatGPT * usage payload. OpenAI has used both `rate_limit` and `rate_limits` shapes. */ export function extractWeeklyQuota(payload: unknown): WeeklyQuota | undefined { const windows = collectWindows(payload); const weekly = windows .filter((window) => window.windowMinutes !== undefined && window.windowMinutes >= 6 * 24 * 60) .sort((a, b) => (b.windowMinutes ?? 0) - (a.windowMinutes ?? 0))[0]; if (!weekly) return undefined; const usedPercent = Math.max(0, Math.min(100, weekly.usedPercent)); return { usedPercent, remainingPercent: Math.max(0, 100 - usedPercent), resetsAt: weekly.resetsAt, }; }