import type { ProviderId, QuotaResult, QuotaWindow } from "../types.js"; const TIMEOUT_MS = 15_000, WINDOW_5H = 18_000, WINDOW_7D = 604_800, WINDOW_30D = 2_592_000; const ANTIGRAVITY_ENDPOINTS = [ "https://daily-cloudcode-pa.googleapis.com", "https://daily-cloudcode-pa.sandbox.googleapis.com", "https://cloudcode-pa.googleapis.com", ]; const ANTIGRAVITY_HEADERS = { Accept: "application/json", "Content-Type": "application/json", "User-Agent": "antigravity/hub/2.8.0 (aidev_client; os_type=linux; arch=x64; cl=963137146)", }; const KIMI_CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098"; function toIsoDate(val: unknown): string | null { if (!val) { return null; } if (typeof val === "number") { const ms = val > 10_000_000_000 ? val : val * 1000; return new Date(ms).toISOString(); } if (typeof val === "string") { return val; } return null; } async function fetchJson( url: string, init?: RequestInit, signal?: AbortSignal, ): Promise<{ body: string; ok: false; status: number } | { data: any; ok: true }> { for (let attempt = 0; attempt < 2; attempt++) { const signals: AbortSignal[] = [AbortSignal.timeout(TIMEOUT_MS)]; if (signal) { signals.push(signal); } const combined = AbortSignal.any(signals); try { const res = await fetch(url, { ...init, signal: combined }); if (!res.ok) { const text = await res.text().catch(() => ""); return { body: text, ok: false, status: res.status }; } return { data: await res.json(), ok: true }; } catch (error) { if (attempt === 0 && !signal?.aborted) { await new Promise((r) => setTimeout(r, 600)); continue; } const msg = error instanceof Error ? error.message : String(error); return { body: msg, ok: false, status: 0 }; } } return { body: "Max retries reached", ok: false, status: 0 }; } function extractError(status: number, body?: string): string { if (status === 0) { return body || "Network error"; } const prefix = `HTTP ${status}`; if (!body) { return prefix; } try { const data = JSON.parse(body); let msg: string | undefined; if (typeof data.error === "string") { msg = data.error; } else if (typeof data.error?.message === "string") { msg = data.error.message; } else if (typeof data.detail === "string") { msg = data.detail; } else if (typeof data.message === "string") { msg = data.message; } else if (typeof data.details?.[0]?.debug?.localizedMessage?.message === "string") { msg = data.details[0].debug.localizedMessage.message; } else if (typeof data.error_description === "string") { msg = data.error_description; } if (msg) { return `${prefix}: ${msg}`; } } catch { const trimmed = body.trim(); if (trimmed && trimmed.length < 150 && !trimmed.startsWith("<")) { return `${prefix}: ${trimmed}`; } } return prefix; } function fail(provider: ProviderId, msg: string): QuotaResult { return { error: msg, ok: false, provider }; } // ── Antigravity ────────────────────────────────────────── async function fetchAntigravity(token: string, signal?: AbortSignal): Promise { const authHeaders = { ...ANTIGRAVITY_HEADERS, Authorization: `Bearer ${token}`, }; let lastError = "Failed to retrieve quota from Antigravity endpoints"; for (const endpoint of ANTIGRAVITY_ENDPOINTS) { // 1. Try retrieveUserQuotaSummary const summaryRes = await fetchJson( `${endpoint}/v1internal:retrieveUserQuotaSummary`, { body: "{}", headers: authHeaders, method: "POST", }, signal, ); if (summaryRes.ok && Array.isArray(summaryRes.data?.groups)) { const windows: QuotaWindow[] = []; const sortedGroups = [...summaryRes.data.groups].toSorted((a, _b) => a.displayName?.includes("Gemini") ? -1 : 1, ); for (const group of sortedGroups) { const isGemini = group.displayName?.includes("Gemini"); const prefix = isGemini ? "Gemini" : "Claude"; const sortedBuckets = [...(group.buckets ?? [])].toSorted((a, _b) => a.window === "5h" ? -1 : 1, ); for (const bucket of sortedBuckets) { const rem = typeof bucket.remainingFraction === "number" ? bucket.remainingFraction : 1; const usedPct = Math.max(0, Math.min(100, Math.round((1 - rem) * 1000) / 10)); const timeHorizon = bucket.window === "5h" ? "5-Hour" : "Weekly"; windows.push({ isCurrency: false, label: `${prefix} ${timeHorizon}`, limitValue: 100, limited: rem <= 0, resetsAt: toIsoDate(bucket.resetTime), usedPercent: usedPct, usedValue: usedPct, windowSeconds: bucket.window === "5h" ? WINDOW_5H : WINDOW_7D, }); } } if (windows.length > 0) { return { ok: true, provider: "antigravity", windows }; } } else if (!summaryRes.ok) { lastError = extractError(summaryRes.status, summaryRes.body); } // 2. Fallback: fetchAvailableModels const modelsRes = await fetchJson( `${endpoint}/v1internal:fetchAvailableModels`, { body: JSON.stringify({ project: "antigravity-default" }), headers: authHeaders, method: "POST", }, signal, ); if (modelsRes.ok && typeof modelsRes.data?.models === "object") { const models = modelsRes.data.models ?? {}; const windows: QuotaWindow[] = []; const seen = new Set(); for (const [mid, info] of Object.entries(models)) { if (!info || info.isInternal || mid.startsWith("chat_")) { continue; } const qi = info.quotaInfo ?? {}; const rem = qi.remainingFraction; const reset = qi.resetTime ?? null; const isGemini = mid.startsWith("gemini"); const prefix = isGemini ? "Gemini" : "Claude"; if (rem !== undefined && !seen.has(prefix)) { seen.add(prefix); const usedPct = Math.max(0, Math.min(100, Math.round((1 - rem) * 1000) / 10)); windows.push({ isCurrency: false, label: `${prefix} 5-Hour`, limitValue: 100, limited: rem <= 0, resetsAt: toIsoDate(reset), usedPercent: usedPct, usedValue: usedPct, windowSeconds: WINDOW_5H, }); } } if (windows.length > 0) { return { ok: true, provider: "antigravity", windows }; } } else if (!modelsRes.ok) { lastError = extractError(modelsRes.status, modelsRes.body); } } return fail("antigravity", lastError); } // ── Codex ──────────────────────────────────────────────── async function fetchCodex( accessToken: string, accountId: string, signal?: AbortSignal, ): Promise { const res = await fetchJson( "https://chatgpt.com/backend-api/wham/usage", { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, "ChatGPT-Account-Id": accountId, Origin: "https://chatgpt.com", Referer: "https://chatgpt.com/", "User-Agent": "Mozilla/5.0", }, }, signal, ); if (!res.ok) { return fail("openai-codex", extractError(res.status, res.body)); } const { data } = res, rl = data.rate_limit ?? {}, pw = rl.primary_window ?? {}, sw = rl.secondary_window ?? {}, windows = [ { label: "5-Hour", limitValue: 100, limited: rl.limit_reached === true, resetsAt: toIsoDate(pw.reset_at), usedPercent: pw.used_percent ?? 0, usedValue: pw.used_percent ?? 0, windowSeconds: pw.limit_window_seconds ?? WINDOW_5H, }, { label: "Weekly", limitValue: 100, limited: false, resetsAt: toIsoDate(sw.reset_at), usedPercent: sw.used_percent ?? 0, usedValue: sw.used_percent ?? 0, windowSeconds: sw.limit_window_seconds ?? WINDOW_7D, }, ]; return { ok: true, provider: "openai-codex", windows }; } // ── DeepSeek ───────────────────────────────────────────── async function fetchDeepSeek(apiKey: string, signal?: AbortSignal): Promise { const res = await fetchJson( "https://api.deepseek.com/user/balance", { headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}`, }, }, signal, ); if (!res.ok) { return fail("deepseek", extractError(res.status, res.body)); } const info = (res.data.balance_infos ?? [])[0] ?? {}, total = parseFloat(info.total_balance ?? "0"), currency = info.currency ?? "CNY"; return { ok: true, provider: "deepseek", windows: [ { isCurrency: true, label: `Balance (${currency})`, limitValue: 0, resetsAt: null, usedPercent: 0, // No cap usedValue: total, windowSeconds: 0, }, ], }; } // ── OpenRouter ─────────────────────────────────────────── async function fetchOpenRouter(apiKey: string, signal?: AbortSignal): Promise { const res = await fetchJson( "https://openrouter.ai/api/v1/auth/key", { headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}`, }, }, signal, ); if (!res.ok) { return fail("openrouter", extractError(res.status, res.body)); } const info = res.data?.data ?? {}, limit = info.limit ?? null, usage = info.usage ?? 0, usageMonthly = info.usage_monthly ?? 0, windows = [ { isCurrency: true, label: limit != null ? "Spending Limit" : "Monthly", limitValue: limit ?? 0, limited: limit != null && info.limit_remaining != null && info.limit_remaining <= 0, resetsAt: toIsoDate(info.limit_reset), usedPercent: limit ? Math.min(100, (usage / limit) * 100) : 0, usedValue: limit != null ? usage : usageMonthly, windowSeconds: 0, }, ]; return { ok: true, provider: "openrouter", windows }; } // ── xAI / Grok ─────────────────────────────────────────── async function fetchXai(accessToken: string, signal?: AbortSignal): Promise { const res = await fetchJson( "https://cli-chat-proxy.grok.com/v1/billing?format=credits", { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }, signal, ); if (!res.ok) { return fail("xai", extractError(res.status, res.body)); } const cfg = res.data?.config ?? {}, period = cfg.currentPeriod ?? {}, usedPct = cfg.creditUsagePercent ?? 0, resetsAt = period.end ?? null, windows = [ { label: "Weekly", limitValue: 100, limited: usedPct >= 100, resetsAt: toIsoDate(resetsAt), usedPercent: usedPct, usedValue: usedPct, windowSeconds: WINDOW_7D, }, ]; return { ok: true, provider: "xai", windows }; } // ── Kimi Coding ────────────────────────────────────────── async function refreshKimiToken( refreshToken: string, ): Promise<{ accessToken: string; expiresIn: number; refreshToken?: string } | null> { try { const body = new URLSearchParams({ client_id: KIMI_CLIENT_ID, grant_type: "refresh_token", refresh_token: refreshToken, }).toString(); const res = await fetch("https://auth.kimi.com/api/oauth/token", { body, headers: { "Content-Type": "application/x-www-form-urlencoded" }, method: "POST", }); if (!res.ok) { return null; } const data = (await res.json()) as any; if (typeof data?.access_token !== "string") { return null; } return { accessToken: data.access_token, expiresIn: Number(data.expires_in || 900), refreshToken: typeof data.refresh_token === "string" ? data.refresh_token : undefined, }; } catch { return null; } } async function fetchKimi(accessToken: string, signal?: AbortSignal): Promise { const res = await fetchJson( "https://api.kimi.com/coding/v1/usages", { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }, signal, ); if (!res.ok) { return fail("kimi-coding", extractError(res.status, res.body)); } const usage = res.data?.usage ?? {}, limit = parseInt(usage.limit ?? "0", 10), used = parseInt(usage.used ?? "0", 10), pct = limit > 0 ? Math.min(100, (used / limit) * 100) : 0, windows: QuotaWindow[] = []; // 1. Short-term rolling limit (5-Hour) first for (const lim of res.data?.limits ?? []) { const detail = lim.detail ?? {}, limLimit = parseInt(detail.limit ?? "0", 10), limUsed = parseInt(detail.used ?? "0", 10), durMin = Number(lim.window?.duration ?? 0), durSec = durMin * 60, windowTitle = durMin >= 240 ? "5-Hour" : `${durMin}m`; windows.push({ isCurrency: false, label: windowTitle, limitValue: limLimit, limited: limUsed >= limLimit, resetsAt: toIsoDate(detail.resetTime), usedPercent: limLimit > 0 ? Math.min(100, (limUsed / limLimit) * 100) : 0, usedValue: limUsed, windowSeconds: durSec, }); } // 2. Long-term Weekly limit second windows.push({ isCurrency: false, label: "Weekly", limitValue: limit, limited: used >= limit, resetsAt: toIsoDate(usage.resetTime), usedPercent: pct, usedValue: used, windowSeconds: WINDOW_7D, }); return { ok: true, provider: "kimi-coding", windows }; } // ── OpenCode Go ────────────────────────────────────────── const OPENCODE_WINDOW_SECONDS: Record = { monthly: WINDOW_30D, rolling: WINDOW_5H, weekly: WINDOW_7D, }; const OPENCODE_WINDOW_LABELS: Record = { monthly: "Monthly", rolling: "5-Hour", weekly: "Weekly", }; async function fetchOpenCodeGo(apiKey: string, signal?: AbortSignal): Promise { const res = await fetchJson( "https://opencode.ai/zen/go/v1/usage", { headers: { Authorization: `Bearer ${apiKey}`, "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", }, }, signal, ); if (!res.ok) { return fail("opencode-go", extractError(res.status, res.body)); } const usage = res.data?.usage ?? {}, windows = []; for (const key of ["rolling", "weekly", "monthly"] as const) { const win = usage[key]; if (!win) { continue; } windows.push({ label: OPENCODE_WINDOW_LABELS[key] || key, limitValue: 100, limited: win.status === "rate-limited" || (win.percent ?? 0) >= 100, resetsAt: toIsoDate(win.resetsAt), usedPercent: win.percent ?? 0, usedValue: win.percent ?? 0, windowSeconds: OPENCODE_WINDOW_SECONDS[key], }); } return { ok: true, provider: "opencode-go", windows }; } export { fetchAntigravity, fetchCodex, fetchDeepSeek, fetchKimi, fetchOpenCodeGo, fetchOpenRouter, fetchXai, refreshKimiToken, };