// @generated by scripts/build-runtime.mjs; do not edit. // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader. // src/codex-fast.ts import { calculateCost, hasApi } from "@earendil-works/pi-ai"; var CODEX_FAST_SERVICE_TIER = "priority"; var CODEX_STANDARD_SERVICE_TIER = "default"; var CODEX_FAST_MODEL_IDS = /* @__PURE__ */ new Set([ "gpt-5.4", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra" ]); function codexFastAvailability(model, enabled) { if (model?.provider !== "openai-codex") return { kind: "not-codex" }; if (!isOfficialCodexModel(model)) { return { kind: "unavailable", reason: "Fast mode requires the official OpenAI Codex Responses endpoint." }; } if (!CODEX_FAST_MODEL_IDS.has(model.id)) { return { kind: "unavailable", reason: `${model.id} does not advertise Codex Fast support.` }; } return { kind: "available", enabled }; } function codexFastIsEffective(model, enabled) { return codexFastAvailability(model, enabled).kind === "available" && enabled; } function codexFastRequestTier(model, enabled) { if (!isOfficialCodexModel(model)) return void 0; return enabled && CODEX_FAST_MODEL_IDS.has(model.id) ? CODEX_FAST_SERVICE_TIER : CODEX_STANDARD_SERVICE_TIER; } function rewriteCodexFastPayload(payload, model, enabled) { const serviceTier = codexFastRequestTier(model, enabled); if (!serviceTier || !isRecord(payload)) return void 0; return { ...payload, service_tier: serviceTier }; } function correctCodexFastMessageCost(message, model, fastRequested) { if (!codexFastIsEffective(model, fastRequested) || !isRecord(message) || message.role !== "assistant" || message.provider !== model?.provider || message.model !== model?.id) { return void 0; } const usage = isRecord(message.usage) ? message.usage : void 0; const cost = usage && isRecord(usage.cost) ? usage.cost : void 0; if (!usage || !cost || !hasCompleteUsage(usage) || !isOfficialCodexModel(model)) return void 0; const correctedUsage = structuredClone(usage); calculateCost(model, correctedUsage); const multiplier = model.id === "gpt-5.5" ? 2.5 : 2; const correctedCost = correctedUsage.cost; for (const key of ["input", "output", "cacheRead", "cacheWrite", "total"]) { correctedCost[key] *= multiplier; } if (costsEqual(cost, correctedCost)) return void 0; return { ...message, usage: correctedUsage }; } function codexFastStatusLabel(status, enabled) { if (!enabled || !/^codex(?:\s|$)/u.test(status)) return status; return status === "codex" ? "codex fast" : `codex fast${status.slice("codex".length)}`; } function isOfficialCodexModel(model) { if (model?.provider !== "openai-codex" || !hasApi(model, "openai-codex-responses")) { return false; } try { return new URL(model.baseUrl).origin === "https://chatgpt.com"; } catch { return false; } } function isRecord(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function hasCompleteUsage(value) { return ["input", "output", "cacheRead", "cacheWrite"].every( (key) => typeof value[key] === "number" && Number.isFinite(value[key]) ); } function costsEqual(left, right) { return ["input", "output", "cacheRead", "cacheWrite", "total"].every( (key) => left[key] === right[key] ); } // src/codex-resets.ts import { readStoredCredential as readStoredCredential2 } from "@earendil-works/pi-coding-agent"; // src/core.ts import { createHmac } from "node:crypto"; var UsageCache = class { entries = /* @__PURE__ */ new Map(); ttlMs; maxEntries; constructor(ttlMs, maxEntries = 32) { if (!Number.isFinite(ttlMs) || ttlMs <= 0) throw new Error("Cache TTL must be positive."); if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) { throw new Error("Cache entry limit must be a positive integer."); } this.ttlMs = ttlMs; this.maxEntries = maxEntries; } get size() { return this.entries.size; } get(providerId, fingerprint, now = Date.now()) { this.sweepExpired(now); return this.entries.get(cacheKey(providerId, fingerprint))?.report; } set(providerId, fingerprint, report, now = Date.now()) { this.sweepExpired(now); const key = cacheKey(providerId, fingerprint); this.entries.delete(key); while (this.entries.size >= this.maxEntries) { const oldest = this.entries.keys().next().value; if (oldest === void 0) break; this.entries.delete(oldest); } this.entries.set(key, { createdAt: now, report }); } clearProvider(providerId) { for (const key of this.entries.keys()) { if (key.startsWith(`${providerId}:`)) this.entries.delete(key); } } clear() { this.entries.clear(); } sweepExpired(now) { for (const [key, entry] of this.entries) { if (now - entry.createdAt >= this.ttlMs) this.entries.delete(key); } } }; function fingerprintResolvedAuth(auth, salt) { const headers = Object.entries(auth.headers ?? {}).map(([name, value]) => [name.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right)); const canonical = JSON.stringify({ apiKey: auth.apiKey ?? "", headers }); return createHmac("sha256", salt).update(canonical).digest("hex"); } async function runWithConcurrency(items, limit, worker, signal) { if (signal.aborted) throw abortError(); if (!Number.isSafeInteger(limit) || limit < 1) throw new Error("Concurrency limit must be positive."); const results = new Array(items.length); let nextIndex = 0; const runners = Array.from({ length: Math.min(limit, items.length) }, async () => { while (nextIndex < items.length) { if (signal.aborted) throw abortError(); const index = nextIndex; nextIndex += 1; try { results[index] = { status: "fulfilled", value: await worker(items[index], index, signal) }; } catch (reason) { results[index] = { status: "rejected", reason }; } } }); await Promise.all(runners); if (signal.aborted) throw abortError(); return results; } async function awaitWithDeadline(operation, signal, timeoutMs, description) { if (signal.aborted) throw abortError(); const controller = new AbortController(); const abortFromCaller = () => controller.abort(); signal.addEventListener("abort", abortFromCaller, { once: true }); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { return await Promise.race([ operation, new Promise((_resolve, reject) => { controller.signal.addEventListener( "abort", () => { reject( signal.aborted ? abortError() : Object.assign( new Error(`Timed out after ${Math.round(timeoutMs / 1e3)}s ${description}.`), { name: "TimeoutError" } ) ); }, { once: true } ); }) ]); } finally { clearTimeout(timeout); signal.removeEventListener("abort", abortFromCaller); } } function sanitizeDisplayText(value, maxChars = 160) { let result = ""; for (let index = 0; index < value.length; ) { const codePoint = value.codePointAt(index) ?? 0; const character = String.fromCodePoint(codePoint); if (codePoint === 27 || codePoint === 155 || codePoint === 157) { index = skipTerminalEscape(value, index, codePoint); continue; } if (codePoint <= 31 || codePoint >= 127 && codePoint <= 159) { if (codePoint === 9 || codePoint === 10 || codePoint === 13) result += " "; index += character.length; continue; } result += character; index += character.length; } return truncate(result.replace(/\s+/gu, " ").trim(), maxChars); } function redactUsageError(value, secrets = []) { let redacted = value; for (const secret of [...new Set(secrets)].filter(Boolean).sort((a, b) => b.length - a.length)) { redacted = redacted.replace(new RegExp(escapeRegExp(secret), "g"), ""); } redacted = redacted.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer ").replace(/"(?:access_token|refresh_token|api_key)"\s*:\s*"[^"]+"/gi, (match) => { const separator = match.indexOf(":"); return `${match.slice(0, separator + 1)}""`; }); return sanitizeDisplayText(redacted, 600); } function errorMessage(error) { return sanitizeDisplayText(error instanceof Error ? error.message : String(error), 600); } function abortError() { return Object.assign(new Error("Usage query aborted."), { name: "AbortError" }); } function skipTerminalEscape(value, start, codePoint) { let index = start + 1; const next = value.charCodeAt(index); const isOsc = codePoint === 157 || codePoint === 27 && next === 93; if (isOsc) { if (codePoint === 27) index += 1; while (index < value.length) { const current = value.charCodeAt(index); if (current === 7) return index + 1; if (current === 27 && value.charCodeAt(index + 1) === 92) return index + 2; index += 1; } return index; } const isCsi = codePoint === 155 || codePoint === 27 && next === 91; if (isCsi) { if (codePoint === 27) index += 1; while (index < value.length) { const current = value.charCodeAt(index); index += 1; if (current >= 64 && current <= 126) break; } return index; } return Math.min(value.length, start + (codePoint === 27 ? 2 : 1)); } function cacheKey(providerId, fingerprint) { return `${providerId}:${fingerprint}`; } function escapeRegExp(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function truncate(value, maxChars) { if (value.length <= maxChars) return value; return `${value.slice(0, maxChars - 1)}\u2026`; } // src/query.ts import { randomBytes } from "node:crypto"; import { readStoredCredential } from "@earendil-works/pi-coding-agent"; // src/providers/codex.ts function normalizeCodexBackendPayload(payload, capturedAt) { const buckets = []; normalizeRateLimitGroup(buckets, "codex", "Codex", payload.rate_limit, false); const additional = Array.isArray(payload.additional_rate_limits) ? payload.additional_rate_limits : []; for (const item of additional) { const value = asObject(item); const id = asString(value?.metered_feature) ?? asString(value?.limit_name); if (!value || !id) continue; try { normalizeRateLimitGroup( buckets, id, asString(value.limit_name) ?? id, value.rate_limit, true ); } catch { } } const metrics = []; const credits = asObject(payload.credits); if (credits?.has_credits === true) { if (credits.unlimited === true) { metrics.push({ id: "credits", label: "Credits", value: "unlimited" }); } else { const balance = asNumber(credits.balance); if (balance !== void 0) { metrics.push({ id: "credits", label: "Credits", value: balance, unit: "count" }); } else { metrics.push({ id: "credits", label: "Credits", value: "available" }); } } } else if (credits?.has_credits === false) { metrics.push({ id: "credits", label: "Credits", value: "none" }); } const resetCredits = asObject(payload.rate_limit_reset_credits); const resetCount = asNonnegativeInteger(resetCredits?.available_count); if (resetCount !== void 0) { metrics.push({ id: "reset-credits", label: "Usage limit resets", value: resetCount, unit: "count" }); } if (buckets.length === 0 && metrics.length === 0) { throw new Error("Codex usage endpoint returned no displayable usage data."); } const planType = asString(payload.plan_type); return { providerId: "openai-codex", providerName: "OpenAI Codex", capturedAt, source: "codex-pi-auth", semantics: { kind: "consumer-subscription", label: "ChatGPT subscription limits" }, buckets, metrics, ...planType ? { notes: [`Plan: ${planType}`] } : {} }; } function normalizeRateLimitGroup(buckets, groupId, groupLabel, raw, optional) { if (raw === void 0 || raw === null) return; const details = asObject(raw); if (!details) { if (optional) return; throw new Error("Codex rate limit was not an object."); } addWindow(buckets, groupId, groupLabel, "primary", details.primary_window); addWindow(buckets, groupId, groupLabel, "secondary", details.secondary_window); } function addWindow(buckets, groupId, groupLabel, position, raw) { if (raw === void 0 || raw === null) return; const value = asObject(raw); if (!value) throw new Error("Codex rate-limit window was not an object."); const used = asNumber(value.used_percent); if (used === void 0) return; const seconds = asNumber(value.limit_window_seconds); const resetsAt = asNumber(value.reset_at); buckets.push({ id: `${groupId}:${position}`, label: position === "primary" ? "Primary limit" : "Secondary limit", groupId, groupLabel, modelKeys: [groupId, groupLabel], used, remaining: 100 - clampPercent(used), limit: 100, unit: "percent", ...seconds !== void 0 && seconds > 0 ? { windowMinutes: Math.ceil(seconds / 60) } : {}, ...resetsAt !== void 0 ? { resetsAt } : {} }); } function asObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function asString(value) { if (typeof value !== "string") return void 0; return sanitizeDisplayText(value, 160) || void 0; } function asNumber(value) { if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string" && value.trim()) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : void 0; } return void 0; } function asNonnegativeInteger(value) { const parsed = asNumber(value); if (parsed === void 0 || !Number.isSafeInteger(parsed)) return void 0; return Math.max(0, parsed); } function clampPercent(value) { return Math.min(100, Math.max(0, value)); } // src/providers/github-copilot.ts function normalizeGitHubCopilotUsagePayload(payload, capturedAt) { const snapshots = asObject2(payload.quota_snapshots); const premium = asObject2(snapshots?.premium_interactions); const metrics = []; let semanticsLabel; let bucket; if (premium) { const tokenBasedBilling = premium.token_based_billing === true; const id = tokenBasedBilling ? "ai-credits" : "premium-requests"; const label = tokenBasedBilling ? "AI credits" : "Premium requests"; semanticsLabel = tokenBasedBilling ? "GitHub Copilot AI Credits allowance" : "GitHub Copilot premium request quota"; if (premium.unlimited === true) { bucket = { id, label, unit: "count" }; } else { const entitlement = asNonnegativeNumber(premium.entitlement); const rawRemaining = asFiniteNumber(premium.remaining) ?? asFiniteNumber(premium.quota_remaining); if (entitlement === void 0 || rawRemaining === void 0) { throw new Error(`GitHub Copilot ${label.toLowerCase()} quota was incomplete.`); } const overageUsed = Math.max( asNonnegativeNumber(premium.overage_count) ?? 0, Math.max(0, -rawRemaining) ); if (overageUsed > 0) { metrics.push({ id: "overage-used", label: "Additional usage", value: overageUsed, unit: "count" }); } bucket = { id, label, used: asNonnegativeNumber(premium.credits_used) ?? Math.max(0, entitlement - rawRemaining), remaining: Math.max(0, rawRemaining), limit: entitlement, unit: "count", period: "monthly", ...resetTimestamp(payload) }; } } else { const limited = asObject2(payload.limited_user_quotas); const monthly = asObject2(payload.monthly_quotas); const remaining = asNonnegativeNumber(limited?.chat); const entitlement = asNonnegativeNumber(monthly?.chat); if (remaining === void 0 || entitlement === void 0) { throw new Error("GitHub Copilot usage response contained no supported quota."); } semanticsLabel = "GitHub Copilot Free chat quota"; bucket = { id: "chat-requests", label: "Chat requests", used: Math.max(0, entitlement - remaining), remaining, limit: entitlement, unit: "count", period: "monthly", ...resetTimestamp(payload) }; } const notes = []; const plan = asString2(payload.copilot_plan) ?? asString2(payload.access_type_sku); if (plan) notes.push(`Plan: ${plan}`); return { providerId: "github-copilot", providerName: "GitHub Copilot", capturedAt, source: "github-copilot-user", semantics: { kind: "consumer-subscription", label: semanticsLabel }, accountLabel: asString2(payload.login), buckets: [bucket], metrics, ...notes.length > 0 ? { notes } : {} }; } function resetTimestamp(payload) { const raw = asString2(payload.quota_reset_date_utc) ?? asString2(payload.quota_reset_date) ?? asString2(payload.limited_user_reset_date); if (!raw) return {}; const milliseconds = Date.parse(raw); return Number.isNaN(milliseconds) ? {} : { resetsAt: Math.floor(milliseconds / 1e3) }; } function asObject2(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function asString2(value) { if (typeof value !== "string") return void 0; return sanitizeDisplayText(value, 80) || void 0; } function asFiniteNumber(value) { if (typeof value !== "number" || !Number.isFinite(value)) return void 0; return value; } function asNonnegativeNumber(value) { const number = asFiniteNumber(value); return number === void 0 || number < 0 ? void 0 : number; } // src/providers/opencode-zen.ts var ZEN_WINDOWS = [ { key: "rolling", label: "Rolling" }, { key: "weekly", label: "Weekly" }, { key: "monthly", label: "Monthly" } ]; function normalizeOpenCodeZenPayload(payload, capturedAt) { const usage = asObject3(payload.usage); if (!usage) throw new Error("OpenCode Zen usage response was not an object."); const buckets = []; const notes = []; for (const window of ZEN_WINDOWS) { const raw = asObject3(usage[window.key]); if (!raw) continue; const status = asString3(raw.status); if (status !== "ok" && status !== "rate-limited") { notes.push(`${window.label} window unavailable (${status ?? "unknown status"}).`); continue; } const used = asNonnegativeNumber2(raw.percent); if (used === void 0) continue; const resetsAt = asEpochSeconds(raw.resetsAt); buckets.push({ id: window.key, label: `${window.label} window`, used, remaining: 100 - clampPercent2(used), limit: 100, unit: "percent", ...resetsAt !== void 0 ? { resetsAt } : {} }); } if (buckets.length === 0) { throw new Error("OpenCode Zen usage endpoint returned no displayable usage data."); } return { providerId: "opencode-go", providerName: "OpenCode Go", capturedAt, source: "opencode-zen-usage", semantics: { kind: "consumer-subscription", label: "OpenCode Zen plan usage" }, buckets, metrics: [], ...notes.length > 0 ? { notes } : {} }; } function asObject3(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function asString3(value) { if (typeof value !== "string") return void 0; return sanitizeDisplayText(value, 80) || void 0; } function asNonnegativeNumber2(value) { if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0; return value; } function asEpochSeconds(value) { if (typeof value !== "string" || !value.trim()) return void 0; const parsed = Date.parse(value); if (Number.isNaN(parsed)) return void 0; return Math.floor(parsed / 1e3); } function clampPercent2(value) { return Math.min(100, Math.max(0, value)); } // src/providers/openrouter.ts function normalizeOpenRouterKeyPayload(payload, capturedAt) { const data = asObject4(payload.data); if (!data) throw new Error("OpenRouter key response data was not an object."); const limit = asNonnegativeNumber3(data.limit); const remaining = asNonnegativeNumber3(data.limit_remaining); const period = asString4(data.limit_reset); const totalUsage = asNonnegativeNumber3(data.usage); const buckets = []; if (limit !== void 0) { buckets.push({ id: "key-limit", label: "Key limit", ...remaining !== void 0 ? { used: Math.max(0, limit - remaining), remaining } : {}, limit, unit: "usd", ...period ? { period } : {} }); } const metrics = []; addUsageMetric(metrics, "usage-daily", "Usage today", data.usage_daily); addUsageMetric(metrics, "usage-weekly", "Usage this week", data.usage_weekly); addUsageMetric(metrics, "usage-monthly", "Usage this month", data.usage_monthly); addUsageMetric(metrics, "usage-total", "All-time usage", totalUsage); if (buckets.length === 0 && metrics.length === 0) { throw new Error("OpenRouter key response returned no displayable usage data."); } const notes = []; if (data.limit === null) notes.push("No per-key spend cap"); else if (limit === void 0) notes.push("Per-key spend cap unavailable"); if (data.is_free_tier === true) notes.push("Free-tier API key"); return { providerId: "openrouter", providerName: "OpenRouter", capturedAt, source: "openrouter-key", semantics: { kind: "api-key", label: "API-key spend limits" }, accountLabel: asString4(data.label), buckets, metrics, ...notes.length > 0 ? { notes } : {} }; } function addUsageMetric(metrics, id, label, value) { const amount = typeof value === "number" ? asNonnegativeNumber3(value) : void 0; if (amount === void 0) return; metrics.push({ id, label, value: amount, unit: "usd" }); } function asObject4(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function asString4(value) { if (typeof value !== "string") return void 0; return sanitizeDisplayText(value, 80) || void 0; } function asNonnegativeNumber3(value) { if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0; return value; } // src/query.ts var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage"; var GITHUB_COPILOT_USAGE_URL = "https://api.github.com/copilot_internal/user"; var OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key"; var MAX_SUCCESS_BODY_BYTES = 64 * 1024; var MAX_ERROR_BODY_BYTES = 4 * 1024; var AUTH_FINGERPRINT_SALT = randomBytes(32); var SUPPORTED_ADAPTERS = [ { id: "openai-codex", displayName: "OpenAI Codex", semantics: { kind: "consumer-subscription", label: "ChatGPT subscription limits" }, async query(auth, signal, timeoutMs) { const payload = await fetchProviderJson( CODEX_USAGE_URL, auth, signal, timeoutMs, "Codex usage endpoint" ); return normalizeCodexBackendPayload(payload, Date.now()); } }, { id: "github-copilot", displayName: "GitHub Copilot", semantics: { kind: "consumer-subscription", label: "GitHub Copilot account allowance" }, async query(auth, signal, timeoutMs) { const payload = await fetchProviderJson( GITHUB_COPILOT_USAGE_URL, auth, signal, timeoutMs, "GitHub Copilot usage endpoint" ); return normalizeGitHubCopilotUsagePayload(payload, Date.now()); } }, { id: "openrouter", displayName: "OpenRouter", semantics: { kind: "api-key", label: "API-key spend limits" }, async query(auth, signal, timeoutMs) { const payload = await fetchProviderJson( OPENROUTER_KEY_URL, auth, signal, timeoutMs, "OpenRouter key endpoint" ); return normalizeOpenRouterKeyPayload(payload, Date.now()); } }, { id: "opencode-go", displayName: "OpenCode Go", semantics: { kind: "consumer-subscription", label: "OpenCode Zen plan usage" }, async query(auth, signal, timeoutMs) { const payload = await fetchProviderJson( opencodeUsageUrl(auth.model.baseUrl), auth, signal, timeoutMs, "OpenCode Zen usage endpoint" ); return normalizeOpenCodeZenPayload(payload, Date.now()); } } ]; function adapterForProvider(providerId) { return SUPPORTED_ADAPTERS.find((adapter) => adapter.id === providerId); } function isStaleExtensionContextError(error) { return error instanceof Error && error.message.includes("This extension ctx is stale after session replacement or reload"); } async function resolveUsageAuth(ctx, adapter, salt = AUTH_FINGERPRINT_SALT, credentialReader = readStoredCredential) { if (ctx.model?.provider === adapter.id && !hasOfficialOrigin(ctx.model, adapter.id)) { throw new Error( `${adapter.displayName} usage cannot send a custom provider base URL credential to the official usage endpoint.` ); } const model = candidateModels(ctx, adapter.id).find( (candidate) => hasOfficialOrigin(candidate, adapter.id) ); if (!model) return void 0; const registry = ctx.modelRegistry; let modelAuth; if (ctx.model?.provider === adapter.id && typeof registry.getApiKeyAndHeaders === "function") { const result = await registry.getApiKeyAndHeaders(ctx.model); if (!result.ok) throw new Error(redactUsageError(result.error)); if (authorizationFrom(result)) modelAuth = result; } if (typeof registry.getProviderAuth !== "function") { throw new Error("pi-usage requires Pi 0.81.0 or newer to validate resolved provider auth."); } const providerResult = await registry.getProviderAuth(adapter.id); if (providerResult?.auth.baseUrl && !hasOfficialUrlOrigin(providerResult.auth.baseUrl, adapter.id)) { throw new Error( `${adapter.displayName} usage cannot send a proxy-resolved credential to the official usage endpoint.` ); } const auth = modelAuth ?? providerResult?.auth; if (!auth) return void 0; if (adapter.id === "github-copilot") { return resolveGitHubCopilotUsageAuth(auth, model, salt, credentialReader); } const authorization = authorizationFrom(auth); if (!authorization) return void 0; const headers = { Authorization: authorization }; const secrets = [auth.apiKey, headerValue(auth.headers, "Authorization"), authorization].filter( (value) => Boolean(value) ); return { apiKey: auth.apiKey, headers, fingerprint: fingerprintResolvedAuth({ headers }, salt), secrets, model }; } async function queryProviderUsage(adapter, auth, signal, timeoutMs) { try { return await adapter.query(auth, signal, timeoutMs); } catch (error) { if (isStaleExtensionContextError(error) || isAbortError(error)) throw error; throw new Error(redactUsageError(errorMessage(error), auth.secrets)); } } function providerIsConfigured(ctx, providerId) { try { return ctx.modelRegistry.getProviderAuthStatus(providerId).configured; } catch { return candidateModels(ctx, providerId).length > 0; } } function candidateModels(ctx, providerId) { const candidates = []; const seen = /* @__PURE__ */ new Set(); const add = (model) => { if (!model || model.provider !== providerId) return; const key = `${model.provider}/${model.id}`; if (seen.has(key)) return; seen.add(key); candidates.push(model); }; add(ctx.model); for (const model of ctx.modelRegistry.getAvailable()) add(model); for (const model of ctx.modelRegistry.getAll()) add(model); return candidates; } async function fetchProviderJson(url, auth, signal, timeoutMs, description, request = {}) { const controller = new AbortController(); let timedOut = false; const abortFromCaller = () => controller.abort(); if (signal.aborted) controller.abort(); else signal.addEventListener("abort", abortFromCaller, { once: true }); const timeout = setTimeout(() => { timedOut = true; controller.abort(); }, timeoutMs); try { const headers = { ...auth.headers }; if (!hasHeader(headers, "User-Agent")) headers["User-Agent"] = "pi-usage"; if (request.body && !hasHeader(headers, "Content-Type")) { headers["Content-Type"] = "application/json"; } const response = await fetch(url, { method: request.method ?? "GET", headers, ...request.body ? { body: JSON.stringify(request.body) } : {}, signal: controller.signal }); if (controller.signal.aborted) throw Object.assign(new Error("Usage query aborted."), { name: "AbortError" }); const text = await readBoundedResponse( response, response.ok ? MAX_SUCCESS_BODY_BYTES : MAX_ERROR_BODY_BYTES, !response.ok, description ); if (controller.signal.aborted) throw Object.assign(new Error("Usage query aborted."), { name: "AbortError" }); if (!response.ok) { throw new Error( `${description} returned ${response.status} ${response.statusText}: ${redactUsageError(text, auth.secrets)}` ); } let parsed; try { parsed = JSON.parse(text); } catch (error) { throw new Error(`${description} returned invalid JSON: ${errorMessage(error)}`); } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error(`${description} response was not an object.`); } return parsed; } catch (error) { if (timedOut) { throw new Error(`Timed out after ${Math.round(timeoutMs / 1e3)}s while fetching usage.`); } if (signal.aborted) throw Object.assign(new Error("Usage query aborted."), { name: "AbortError" }); throw error; } finally { clearTimeout(timeout); signal.removeEventListener("abort", abortFromCaller); } } async function readBoundedResponse(response, maxBytes, truncateOverflow, description) { if (!response.body) return ""; const reader = response.body.getReader(); const chunks = []; let total = 0; let truncated = false; try { while (true) { const { done, value } = await reader.read(); if (done) break; const remaining = maxBytes - total; if (value.byteLength > remaining) { if (remaining > 0) chunks.push(value.subarray(0, remaining)); total = maxBytes; truncated = true; await reader.cancel(); break; } chunks.push(value); total += value.byteLength; } } finally { reader.releaseLock(); } if (truncated && !truncateOverflow) { throw new Error(`${description} response exceeded ${maxBytes} bytes.`); } const body = new Uint8Array(total); let offset = 0; for (const chunk of chunks) { body.set(chunk, offset); offset += chunk.byteLength; } const text = new TextDecoder().decode(body); return truncated ? `${text}\u2026` : text; } function resolveGitHubCopilotUsageAuth(auth, model, salt, credentialReader) { const credential = asObject5(credentialReader("github-copilot")); if (credential?.type !== "oauth") { throw new Error( "GitHub Copilot usage requires the OAuth account configured through Pi /login." ); } if (typeof credential.enterpriseUrl === "string" && credential.enterpriseUrl && !isPublicGitHubDomain(credential.enterpriseUrl)) { throw new Error("GitHub Copilot usage does not yet support GitHub Enterprise accounts."); } const refresh = typeof credential.refresh === "string" ? credential.refresh : void 0; const storedAccess = typeof credential.access === "string" ? credential.access : void 0; const resolvedAccess = bearerToken(headerValue(auth.headers, "Authorization")) ?? auth.apiKey; if (!refresh || !storedAccess || !resolvedAccess) { throw new Error("GitHub Copilot OAuth credentials were incomplete."); } if (storedAccess !== resolvedAccess) { throw new Error( "The active GitHub Copilot runtime account does not match Pi's stored OAuth account." ); } const authorization = `Bearer ${refresh}`; const headers = { Authorization: authorization, "X-GitHub-Api-Version": "2025-05-01" }; return { apiKey: refresh, headers, fingerprint: fingerprintResolvedAuth({ headers }, salt), secrets: [refresh, storedAccess, resolvedAccess, authorization], model }; } function authorizationFrom(auth) { return headerValue(auth.headers, "Authorization") ?? (auth.apiKey ? `Bearer ${auth.apiKey}` : void 0); } function bearerToken(authorization) { const match = /^Bearer\s+(.+)$/iu.exec(authorization ?? ""); return match?.[1]; } function asObject5(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function isPublicGitHubDomain(value) { try { const url = new URL(value.includes("://") ? value : `https://${value}`); return url.hostname.toLowerCase() === "github.com"; } catch { return false; } } function hasOfficialOrigin(model, providerId) { return hasOfficialUrlOrigin(model.baseUrl, providerId); } function hasOfficialUrlOrigin(value, providerId) { try { const url = new URL(value); if (providerId === "openai-codex") return url.origin === "https://chatgpt.com"; if (providerId === "openrouter") return url.origin === "https://openrouter.ai"; if (providerId === "opencode-go") return url.origin === "https://opencode.ai"; if (providerId === "github-copilot") { return url.protocol === "https:" && /^api\.[a-z0-9-]+\.githubcopilot\.com$/u.test(url.hostname); } return false; } catch { return false; } } function headerValue(headers, name) { const entry = Object.entries(headers ?? {}).find( ([candidate]) => candidate.toLowerCase() === name.toLowerCase() ); return entry?.[1] ?? void 0; } function hasHeader(headers, name) { return Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase()); } function opencodeUsageUrl(baseUrl) { const base = baseUrl?.trim().replace(/\/+$/u, ""); if (!base) throw new Error("OpenCode Go model base URL is unavailable."); return `${base}/usage`; } function isAbortError(error) { return error instanceof Error && error.name === "AbortError"; } // src/codex-resets.ts var CODEX_RESET_CREDITS_URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits"; var CODEX_RESET_CONSUME_URL = `${CODEX_RESET_CREDITS_URL}/consume`; var MAX_RESET_OPTIONS = 32; var MAX_CREDIT_ID_CHARS = 1024; function codexResetCount(report) { const value = report.metrics.find((metric) => metric.id === "reset-credits")?.value; return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0; } function codexResetActionDescription(report) { const count = codexResetCount(report); if (count === void 0) return "Check reset availability."; if (count === 0) return "No usage limit resets available."; return `You have ${count} ${resetLabel(count)} available.`; } function genericCodexResetOption() { return { title: "Full reset", description: "Reset your current usage limits." }; } function resetOptionExpiration(option) { if (option.expiresAt === void 0) return "Does not expire."; const expiration = new Date(option.expiresAt * 1e3); if (Number.isNaN(expiration.getTime())) return "Expiration unavailable."; return `Expires ${expiration.toLocaleString()}.`; } function resetConfirmationLines(option) { if (!option) return ["The selected reset is unavailable."]; return [ option.title, resetOptionExpiration(option), option.description, "This consumes one earned reset for the current OpenAI Codex account." ]; } function formatCodexResetOutcome(outcome, remainingCount) { const remaining = remainingCount === void 0 ? "" : ` You have ${remainingCount} ${resetLabel(remainingCount)} left.`; if (!outcome) return "You don't have any usage limit resets available."; if (outcome.code === "reset") return `Usage reset.${remaining}`.trim(); if (outcome.code === "already_redeemed") { return `Usage reset was already completed.${remaining}`.trim(); } if (outcome.code === "nothing_to_reset") { return "Your usage does not need a reset right now."; } return "No usage limit resets are available."; } function resetLabel(count) { return count === 1 ? "usage limit reset" : "usage limit resets"; } async function resolveCodexResetAuth(ctx, salt = AUTH_FINGERPRINT_SALT, credentialReader = readStoredCredential2) { const model = ctx.model; if (model?.provider !== "openai-codex") { throw new Error("Usage limit resets require the current model to use OpenAI Codex."); } const expectedModel = `${model.provider}/${model.id}`; const adapter = adapterForProvider("openai-codex"); if (!adapter) throw new Error("OpenAI Codex usage support is unavailable."); const auth = await resolveUsageAuth(ctx, adapter, salt, credentialReader); if (`${ctx.model?.provider}/${ctx.model?.id}` !== expectedModel) { throw new Error("The current model changed while resolving Codex reset authentication."); } if (!auth) throw new Error("No runtime credential is configured for OpenAI Codex."); const credential = asObject6(credentialReader("openai-codex")); if (credential?.type !== "oauth") { throw new Error( "Usage limit resets require the OpenAI Codex OAuth account configured through Pi /login." ); } const storedAccess = asNonemptyString(credential.access); const resolvedAccess = bearerToken2(headerValue2(auth.headers, "Authorization")) ?? auth.apiKey; if (!storedAccess || !resolvedAccess) { throw new Error("OpenAI Codex OAuth credentials were incomplete."); } if (storedAccess !== resolvedAccess) { throw new Error( "The active OpenAI Codex runtime account does not match Pi's stored OAuth account." ); } const accountId = validHeaderValue(credential.accountId); if (!accountId) throw new Error("The OpenAI Codex OAuth credential did not include a valid account ID."); const authorization = `Bearer ${resolvedAccess}`; const headers = { Authorization: authorization, "chatgpt-account-id": accountId }; return { apiKey: resolvedAccess, headers, fingerprint: fingerprintResolvedAuth({ headers }, salt), secrets: [ .../* @__PURE__ */ new Set([...auth.secrets, storedAccess, resolvedAccess, authorization, accountId]) ], model: auth.model }; } async function listCodexResetCredits(auth, signal, timeoutMs) { const payload = await fetchProviderJson( CODEX_RESET_CREDITS_URL, auth, signal, timeoutMs, "Codex usage-limit reset endpoint" ); return normalizeCodexResetCreditsPayload(payload); } async function consumeCodexResetCredit(auth, option, redeemRequestId, signal, timeoutMs) { if (!redeemRequestId) throw new Error("Codex reset redemption request ID must not be empty."); const payload = await fetchProviderJson( CODEX_RESET_CONSUME_URL, auth, signal, timeoutMs, "Codex usage-limit reset consume endpoint", { method: "POST", body: { redeem_request_id: redeemRequestId, ...option.creditId ? { credit_id: option.creditId } : {} } } ); const code = payload.code; if (!isCodexResetOutcomeCode(code)) { throw new Error("Codex reset consume endpoint returned an unknown outcome code."); } const windowsReset = payload.windows_reset === void 0 ? 0 : nonnegativeInteger(payload.windows_reset); if (windowsReset === void 0) { throw new Error("Codex reset consume endpoint returned an invalid windows_reset value."); } return { code, windowsReset }; } function normalizeCodexResetCreditsPayload(payload) { const availableCount = nonnegativeInteger(payload.available_count); if (availableCount === void 0) { throw new Error("Codex reset credits response returned an invalid available_count."); } const rawCredits = payload.credits; if (rawCredits !== void 0 && !Array.isArray(rawCredits)) { throw new Error("Codex reset credits response returned invalid credits."); } const options = (rawCredits ?? []).map(asObject6).filter((credit) => Boolean(credit)).filter((credit) => credit.status === "available" && credit.reset_type === "codex_rate_limits").map(normalizeResetOption).sort( (left, right) => (left.expiresAt ?? Number.MAX_SAFE_INTEGER) - (right.expiresAt ?? Number.MAX_SAFE_INTEGER) ).slice(0, Math.min(availableCount, MAX_RESET_OPTIONS)); if (availableCount > 0 && options.length === 0) { options.push(genericCodexResetOption()); } return { availableCount, options }; } function normalizeResetOption(credit) { const creditId = asOpaqueId(credit.id); if (!creditId) throw new Error("Codex reset credits response returned an invalid credit ID."); let expiresAt; if (credit.expires_at !== void 0 && credit.expires_at !== null) { if (typeof credit.expires_at !== "string") { throw new Error("Codex reset credits response returned an invalid expiration time."); } const parsed = Date.parse(credit.expires_at); if (!Number.isFinite(parsed)) { throw new Error("Codex reset credits response returned an invalid expiration time."); } expiresAt = Math.floor(parsed / 1e3); } const title = displayString(credit.title) ?? "Full reset"; const description = displayString(credit.description) ?? "Reset your current usage limits."; return { creditId, title, description, ...expiresAt === void 0 ? {} : { expiresAt } }; } function isCodexResetOutcomeCode(value) { return value === "reset" || value === "nothing_to_reset" || value === "no_credit" || value === "already_redeemed"; } function asObject6(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return void 0; return value; } function asNonemptyString(value) { return typeof value === "string" && value.length > 0 ? value : void 0; } function asOpaqueId(value) { if (typeof value !== "string" || value.length === 0 || value.length > MAX_CREDIT_ID_CHARS) { return void 0; } return value; } function displayString(value) { if (typeof value !== "string") return void 0; return sanitizeDisplayText(value, 160) || void 0; } function validHeaderValue(value) { if (typeof value !== "string" || !value || value.length > 512) return void 0; if (/[^\x20-\x7e]/u.test(value)) return void 0; return value; } function nonnegativeInteger(value) { const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN; if (!Number.isSafeInteger(parsed) || parsed < 0) return void 0; return parsed; } function bearerToken2(authorization) { return /^Bearer\s+(.+)$/iu.exec(authorization ?? "")?.[1]; } function headerValue2(headers, name) { return Object.entries(headers).find( ([candidate]) => candidate.toLowerCase() === name.toLowerCase() )?.[1]; } // src/format.ts var MUTED_USAGE_COLORS = { success: "\x1B[38;2;82;119;82m", // #527752 warning: "\x1B[38;2;120;107;58m", // #786b3a error: "\x1B[38;2;144;92;87m" // #905c57 }; var BAR_SEGMENTS = 20; var VALUE_COLUMN = 29; function formatUsageReport(report, displayState) { const stateLabel = displayState === "current" ? "Current" : "Configured"; const lines = [`${report.providerName} Usage \xB7 ${stateLabel}`]; if (report.accountLabel) lines.push(`Account: ${report.accountLabel}`); lines.push(`Semantics: ${report.semantics.label}`, ""); if (report.providerId === "openai-codex") formatCodexReport(lines, report); else if (report.providerId === "github-copilot") formatGitHubCopilotReport(lines, report); else if (report.providerId === "openrouter") formatOpenRouterReport(lines, report); else if (report.providerId === "opencode-go") formatOpenCodeZenReport(lines, report); else formatGenericReport(lines, report); if (report.notes) { for (const note of report.notes) lines.push(note); } return lines.join("\n").trimEnd(); } function formatUsageStatusline(report, model, now = Date.now()) { if (report.providerId === "openai-codex") return formatCodexStatusline(report, model, now); if (report.providerId === "github-copilot") return formatGitHubCopilotStatusline(report); if (report.providerId === "openrouter") { const limit = report.buckets.find((bucket) => bucket.id === "key-limit"); if (limit?.remaining !== void 0) return `${formatUsd(limit.remaining)} left`; const total = report.metrics.find((metric) => metric.id === "usage-total"); if (typeof total?.value === "number") return `${formatUsd(total.value)} used`; } if (report.providerId === "opencode-go") return formatOpenCodeZenStatusline(report); return void 0; } function styleUsageStatusline(value, report, styler) { const percentagePattern = /(\d+(?:\.\d+)?)%/gu; const isUsedPercentage = report.providerId === "opencode-go"; let cursor = 0; let styled = ""; for (const match of value.matchAll(percentagePattern)) { const index = match.index ?? 0; const numeric = Number(match[1]); const remaining = isUsedPercentage ? 100 - numeric : numeric; const color = usageStatusColor(remaining); styled += styler.dim(value.slice(cursor, index)); styled += styler.color(color, match[0]); cursor = index + match[0].length; } return `${styled}${styler.dim(value.slice(cursor))}`; } function usageStatusColor(remaining) { return remaining >= 70 ? "success" : remaining >= 30 ? "warning" : "error"; } function formatProviderStates(states) { return states.map((state) => { if (state.status === "ready") return formatUsageReport(state.report, state.displayState); const label = state.displayState === "current" ? "Current" : "Configured"; const status = state.status === "auth-unavailable" ? "Authentication unavailable" : state.status === "unsupported" ? "Unsupported" : "Query failed"; return `${state.providerName} \xB7 ${label} ${status}: ${state.message}`; }).join("\n\n"); } function formatCodexReport(lines, report) { let previousGroup; for (const bucket of report.buckets) { const group = bucket.groupId ?? bucket.id; if (group !== previousGroup && group !== "codex") { lines.push(`${bucket.groupLabel ?? group} limit:`); } previousGroup = group; const fallback = bucket.id.endsWith(":secondary") ? "weekly" : "5h"; const label = `${formatWindowLabel(bucket.windowMinutes, fallback, false)} limit:`; lines.push(`${label.padEnd(VALUE_COLUMN)}${formatPercentBucket(bucket)}`); } for (const metric of report.metrics) { if (metric.id === "reset-credits") { lines.push(`${"Usage limit resets:".padEnd(VALUE_COLUMN)}${metric.value} available`); } else if (metric.id === "credits") { lines.push( `${"Credits:".padEnd(VALUE_COLUMN)}${formatMetricValue(metric.value, metric.unit)}` ); } } } function formatGitHubCopilotReport(lines, report) { const quota = findGitHubCopilotQuota(report); if (!quota || quota.limit === void 0 || quota.remaining === void 0) { lines.push(`${`${quota?.label ?? "Copilot quota"}:`.padEnd(VALUE_COLUMN)}unlimited`); return; } const percent = percentRemaining(quota); const reset = quota.resetsAt ? ` (resets ${formatReset(quota.resetsAt)})` : ""; lines.push( `${`${quota.label}:`.padEnd(VALUE_COLUMN)}${quota.remaining} of ${quota.limit} left \xB7 ${percent}%${reset}` ); const overage = report.metrics.find((metric) => metric.id === "overage-used"); if (typeof overage?.value === "number" && overage.value > 0) { lines.push(`${"Additional usage:".padEnd(VALUE_COLUMN)}${overage.value} ${quota.label}`); } } function formatGitHubCopilotStatusline(report) { const quota = findGitHubCopilotQuota(report); const kind = compactGitHubCopilotQuotaKind(quota); if (!quota || quota.limit === void 0 || quota.remaining === void 0) { return `${kind} unlimited`; } const overage = report.metrics.find((metric) => metric.id === "overage-used"); const overageSuffix = typeof overage?.value === "number" && overage.value > 0 ? ` +${overage.value} over` : ""; return `${kind === "premium" ? "" : `${kind} `}${quota.remaining}/${quota.limit} ${percentRemaining(quota)}%${overageSuffix}`; } function findGitHubCopilotQuota(report) { return report.buckets.find( (bucket) => ["ai-credits", "premium-requests", "chat-requests"].includes(bucket.id) ); } function compactGitHubCopilotQuotaKind(bucket) { if (bucket?.id === "ai-credits") return "credits"; if (bucket?.id === "chat-requests") return "chat"; return "premium"; } function percentRemaining(bucket) { if (!bucket.limit || bucket.remaining === void 0) return 0; return Math.round(clampPercent3(bucket.remaining / bucket.limit * 100)); } function formatOpenRouterReport(lines, report) { const limit = report.buckets.find((bucket) => bucket.id === "key-limit"); if (limit) { const period = limit.period ? ` (${limit.period})` : ""; const value = limit.remaining === void 0 ? `${formatUsd(limit.limit ?? 0)} cap; remaining unavailable` : `${formatUsd(limit.remaining)} of ${formatUsd(limit.limit ?? 0)} left`; lines.push(`${`Key limit${period}:`.padEnd(VALUE_COLUMN)}${value}`); } for (const metric of report.metrics) { lines.push( `${`${metric.label}:`.padEnd(VALUE_COLUMN)}${formatMetricValue(metric.value, metric.unit)}` ); } } function formatOpenCodeZenReport(lines, report) { for (const bucket of report.buckets) { const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : ""; const used = bucket.used ?? "unavailable"; lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${used}% used${reset}`); } } function formatOpenCodeZenStatusline(report) { const parts = []; for (const bucket of report.buckets) { if (bucket.used === void 0) continue; const compact = bucket.id === "rolling" ? "r" : bucket.id === "weekly" ? "w" : "m"; parts.push(`${clampPercent3(bucket.used).toFixed(0)}% (${compact})`); } return parts.length > 0 ? parts.join(" ") : void 0; } function formatGenericReport(lines, report) { for (const bucket of report.buckets) { lines.push( `${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${formatMetricValue(bucket.remaining ?? bucket.used ?? "unavailable", bucket.unit)}` ); } for (const metric of report.metrics) { lines.push( `${`${metric.label}:`.padEnd(VALUE_COLUMN)}${formatMetricValue(metric.value, metric.unit)}` ); } } function formatCodexStatusline(report, model, now) { const group = selectCodexGroup(report, model); if (!group) return formatCodexCreditsStatus(report); const buckets = report.buckets.filter((bucket) => (bucket.groupId ?? bucket.id) === group); const parts = []; for (const bucket of buckets) { if (bucket.remaining === void 0) continue; const fallback = bucket.id.endsWith(":secondary") ? "weekly" : "5h"; parts.push( `${clampPercent3(bucket.remaining).toFixed(0)}% (${formatResetCountdown(bucket, fallback, now)})` ); } return parts.length > 0 ? parts.join(" ") : formatCodexCreditsStatus(report); } function formatCodexCreditsStatus(report) { const credits = report.metrics.find((metric) => metric.id === "credits"); if (!credits) return "usage unavailable"; if (credits.value === "none") return "no credits"; if (credits.value === "available") return "credits available"; if (credits.value === "unlimited") return "credits unlimited"; return `${formatMetricValue(credits.value, "count")} credits`; } function selectCodexGroup(report, model) { const groups = [...new Set(report.buckets.map((bucket) => bucket.groupId ?? bucket.id))]; if (model?.provider !== "openai-codex") { return groups.includes("codex") ? "codex" : groups[0]; } const modelKeys = normalizedModelKeys(model); for (const group of groups) { const bucket = report.buckets.find( (candidate) => (candidate.groupId ?? candidate.id) === group ); const keys = [group, bucket?.groupLabel, ...bucket?.modelKeys ?? []].map(normalizeKey).filter((key) => key !== void 0); if (keys.some((key) => modelKeys.has(key))) return group; } const variants = [...modelKeys].map((key) => key.match(/(?:^|-)codex-(.+)$/)?.[1]).filter((value) => Boolean(value)); for (const variant of variants) { const matches = groups.filter((group) => { if (group === "codex") return false; const key = normalizeKey(group); return key ? normalizedKeyHasToken(key, variant) : false; }); if (matches.length === 1) return matches[0]; } return groups.includes("codex") ? "codex" : groups[0]; } function normalizedModelKeys(model) { const keys = /* @__PURE__ */ new Set(); for (const value of [model.id, model.name]) { const key = normalizeKey(value); if (!key) continue; keys.add(key); const index = key.indexOf("codex"); if (index >= 0) keys.add(key.slice(index)); } return keys; } function normalizeKey(value) { const separated = value?.toLowerCase().replace(/[^a-z0-9]+/g, "-"); if (!separated) return void 0; let start = 0; let end = separated.length; while (separated[start] === "-") start += 1; while (end > start && separated[end - 1] === "-") end -= 1; return separated.slice(start, end) || void 0; } function normalizedKeyHasToken(key, token) { return key === token || key.startsWith(`${token}-`) || key.endsWith(`-${token}`) || key.includes(`-${token}-`); } function formatPercentBucket(bucket) { const remaining = clampPercent3(bucket.remaining ?? 0); const filled = Math.round(remaining / 100 * BAR_SEGMENTS); const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : ""; return `[${"\u2588".repeat(filled)}${"\u2591".repeat(BAR_SEGMENTS - filled)}] ${remaining.toFixed(0)}% left${reset}`; } function formatWindowLabel(minutes, fallback, compact) { if (!minutes || !Number.isFinite(minutes) || minutes <= 0) { return compact && fallback === "weekly" ? "wk" : capitalize(fallback); } if (minutes === 10080) return compact ? "wk" : "Weekly"; if (minutes % 10080 === 0) return `${minutes / 10080}w`; if (minutes % 1440 === 0) return `${minutes / 1440}d`; if (minutes % 60 === 0) return `${minutes / 60}h`; return `${minutes}m`; } function formatResetCountdown(bucket, fallback, now) { if (bucket.resetsAt === void 0) { return formatWindowLabel(bucket.windowMinutes, fallback, true); } const remainingMs = bucket.resetsAt * 1e3 - now; if (!Number.isFinite(remainingMs)) { return formatWindowLabel(bucket.windowMinutes, fallback, true); } if (remainingMs <= 0) return "reset due"; const totalMinutes = Math.floor(remainingMs / 6e4); const days = Math.floor(totalMinutes / 1440); const hours = Math.floor(totalMinutes % 1440 / 60); const minutes = totalMinutes % 60; return `${days}d ${hours}h ${minutes}m`; } function formatMetricValue(value, unit) { if (unit === "usd" && typeof value === "number") return formatUsd(value); return String(value); } function formatUsd(value) { return `$${value.toFixed(2)}`; } function formatReset(epochSeconds) { const reset = new Date(epochSeconds * 1e3); if (Number.isNaN(reset.getTime())) return "at an unknown time"; const time = `${reset.getHours().toString().padStart(2, "0")}:${reset.getMinutes().toString().padStart(2, "0")}`; const now = /* @__PURE__ */ new Date(); if (reset.toDateString() === now.toDateString()) return time; return `${time} on ${reset.getDate()} ${reset.toLocaleDateString(void 0, { month: "short" })}`; } function capitalize(value) { return `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}`; } function clampPercent3(value) { return Math.min(100, Math.max(0, value)); } // src/settings.ts import { randomUUID } from "node:crypto"; import { constants } from "node:fs"; import { chmod, mkdir, open, rename, rm, writeFile } from "node:fs/promises"; import { basename, dirname, join } from "node:path"; import { getAgentDir } from "@earendil-works/pi-coding-agent"; var USAGE_SETTINGS_FILE = "pi-usage.json"; var MAX_USAGE_SETTINGS_BYTES = 64 * 1024; var DEFAULT_USAGE_SETTINGS = Object.freeze({ codexFastMode: false }); function usageSettingsPath() { return join(getAgentDir(), USAGE_SETTINGS_FILE); } function normalizeUsageSettings(value) { if (!isRecord2(value)) return void 0; if (Object.hasOwn(value, "codexFastMode") && typeof value.codexFastMode !== "boolean") { return void 0; } return { codexFastMode: typeof value.codexFastMode === "boolean" ? value.codexFastMode : DEFAULT_USAGE_SETTINGS.codexFastMode }; } async function loadUsageSettings(path = usageSettingsPath(), signal) { throwIfAborted(signal); try { const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW); let text; try { const stats = await handle.stat(); throwIfAborted(signal); if (!stats.isFile()) throw new Error("settings path is not a regular file"); if (stats.size > MAX_USAGE_SETTINGS_BYTES) { throw new Error("settings file exceeds 64 KiB"); } text = await handle.readFile("utf8"); } finally { await handle.close(); } throwIfAborted(signal); const document = JSON.parse(text); const settings = normalizeUsageSettings(document); if (!settings || !isRecord2(document)) throw new Error("invalid settings shape"); return { kind: "loaded", path, settings, document }; } catch (error) { if (signal?.aborted) throw error; if (isNodeError(error) && error.code === "ENOENT") { return { kind: "missing", path, settings: { ...DEFAULT_USAGE_SETTINGS }, document: {} }; } return { kind: "invalid", path, settings: { ...DEFAULT_USAGE_SETTINGS }, issue: isNodeError(error) && error.code === "ELOOP" ? "symbolic links are not accepted" : error instanceof Error ? error.message : String(error) }; } } function createUsageSettingsRuntime(options = {}) { const path = typeof options === "string" ? options : options.path ?? usageSettingsPath(); const operations = { rename, writeFile, ...typeof options === "string" ? void 0 : options.operations }; let state = { kind: "missing", path, settings: { ...DEFAULT_USAGE_SETTINGS }, document: {} }; let queue = Promise.resolve(); const enqueue = (operation) => { const result = queue.then(operation, operation); queue = result.then( () => void 0, () => void 0 ); return result; }; return { get: () => structuredClone(state), reload: (signal) => enqueue(async () => { const loaded = await loadUsageSettings(path, signal); state = loaded; return structuredClone(state); }), update: (patch, signal) => enqueue(async () => { const saved = await saveUsageSettingsPatch(path, patch, operations, signal); state = saved; return structuredClone(state); }), flush: () => queue }; } async function saveUsageSettingsPatch(path, patch, operations, signal) { const latest = await loadUsageSettings(path, signal); if (latest.kind === "invalid") { throw new Error("Cannot overwrite an invalid pi-usage.json; repair it and reload first"); } const document = { ...latest.document, ...patch }; const settings = normalizeUsageSettings(document); if (!settings) throw new Error("Refusing to save invalid pi-usage settings"); const directory = dirname(path); const temporaryPath = join(directory, `.${basename(path)}.${randomUUID()}.tmp`); await mkdir(directory, { recursive: true, mode: 448 }); throwIfAborted(signal); try { await operations.writeFile(temporaryPath, `${JSON.stringify(document, null, 2)} `, { encoding: "utf8", flag: "wx", mode: 384 }); if (process.platform !== "win32") await chmodPrivate(temporaryPath); throwIfAborted(signal); const current = await loadUsageSettings(path, signal); if (current.kind === "invalid" || current.kind !== latest.kind || JSON.stringify(current.document) !== JSON.stringify(latest.document)) { throw new Error("pi-usage.json changed while saving; retry the action"); } throwIfAborted(signal); await operations.rename(temporaryPath, path); } finally { await rm(temporaryPath, { force: true }).catch(() => void 0); } return { kind: "loaded", path, settings, document }; } async function chmodPrivate(path) { await chmod(path, 384); } function throwIfAborted(signal) { if (signal?.aborted) throw new DOMException("Settings operation aborted", "AbortError"); } function isRecord2(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function isNodeError(error) { return error instanceof Error && "code" in error; } // src/usage.ts import { randomUUID as randomUUID2 } from "node:crypto"; // src/codex-fast-runtime.ts var NO_FAST_REQUEST = /* @__PURE__ */ Symbol("no-fast-request"); var FAST_USAGE_WARNING = "Fast is about 1.5\xD7 faster and uses more of your plan allowance."; function registerCodexFastMode(pi, settingsRuntime, refreshStatus) { let sessionController = new AbortController(); let generation = 0; const pendingFastRequests = /* @__PURE__ */ new Map(); const toggle = async (ctx, enabled, callerSignal) => { const ownerGeneration = generation; const sessionId = ctx.sessionManager.getSessionId(); const signal = callerSignal ? AbortSignal.any([callerSignal, sessionController.signal]) : sessionController.signal; try { await settingsRuntime.update({ codexFastMode: enabled }, signal); } catch (error) { if (isAbortError2(error) || isStaleExtensionContextError(error)) return false; ctx.ui.notify(`Could not save pi-usage.json: ${errorMessage(error)}`, "error"); return false; } if (signal.aborted || ownerGeneration !== generation || ctx.sessionManager.getSessionId() !== sessionId) { return false; } refreshStatus(ctx); ctx.ui.notify( enabled ? `Codex Fast mode enabled. ${FAST_USAGE_WARNING}` : "Codex Fast mode disabled; standard routing will be used.", "info" ); return true; }; pi.registerCommand("fast", { description: "Toggle Codex Fast mode", handler: async (args, ctx) => { if (args.trim()) { if (!ctx.hasUI) throw new Error("/fast does not accept arguments."); ctx.ui.notify("/fast does not accept arguments.", "warning"); return; } if (!ctx.hasUI) throw new Error("/fast requires TUI or RPC mode."); const availability = codexFastAvailability( ctx.model, settingsRuntime.get().settings.codexFastMode ); if (availability.kind === "not-codex") { ctx.ui.notify("/fast is available only for the active OpenAI Codex model.", "warning"); return; } if (availability.kind === "unavailable") { ctx.ui.notify(availability.reason, "warning"); return; } if (settingsRuntime.get().kind === "invalid") { ctx.ui.notify( "pi-usage.json is invalid; repair it and run /reload before changing Fast mode.", "error" ); return; } await toggle(ctx, !availability.enabled); } }); pi.on("session_start", async (_event, ctx) => { generation += 1; sessionController.abort(); pendingFastRequests.clear(); sessionController = new AbortController(); const ownerGeneration = generation; const sessionId = ctx.sessionManager.getSessionId(); let state; try { state = await settingsRuntime.reload(sessionController.signal); } catch (error) { if (sessionController.signal.aborted || ownerGeneration !== generation) return; if (ctx.hasUI) { ctx.ui.notify( `Could not load pi-usage.json; using defaults. ${errorMessage(error)}`, "warning" ); } return; } if (sessionController.signal.aborted || ownerGeneration !== generation || ctx.sessionManager.getSessionId() !== sessionId) { return; } if (ctx.hasUI && state.kind === "invalid") { ctx.ui.notify( `Invalid pi-usage.json; using defaults without overwriting it. ${state.issue}`, "warning" ); } refreshStatus(ctx); }); pi.on("before_provider_request", (event, ctx) => { const rewritten = rewriteCodexFastPayload( event.payload, ctx.model, settingsRuntime.get().settings.codexFastMode ); const key = activeRequestKey(ctx); if (key && ctx.model) { pendingFastRequests.set(key, { fastRequested: isRecord3(rewritten) && rewritten.service_tier === "priority", model: ctx.model }); } return rewritten; }); pi.on("message_end", (event, ctx) => { const request = consumeFastRequest(ctx, event.message, pendingFastRequests); if (request === NO_FAST_REQUEST) return void 0; const message = correctCodexFastMessageCost( event.message, request.model, request.fastRequested ); return message ? { message } : void 0; }); pi.on("session_shutdown", async () => { generation += 1; sessionController.abort(); pendingFastRequests.clear(); await settingsRuntime.flush(); }); return { availability(model) { return codexFastAvailability(model, settingsRuntime.get().settings.codexFastMode); }, decorateStatus(model, status) { return codexFastStatusLabel( status, codexFastIsEffective(model, settingsRuntime.get().settings.codexFastMode) ); }, toggle }; } function activeRequestKey(ctx) { const model = ctx.model; return model ? `${ctx.sessionManager.getSessionId()}:${model.provider}/${model.id}` : void 0; } function consumeFastRequest(ctx, message, pending) { if (!isRecord3(message) || message.role !== "assistant") return NO_FAST_REQUEST; const key = messageRequestKey(ctx, message); if (!key) return NO_FAST_REQUEST; const request = pending.get(key); pending.delete(key); return request ?? NO_FAST_REQUEST; } function messageRequestKey(ctx, message) { if (typeof message.provider !== "string" || typeof message.model !== "string") return void 0; return `${ctx.sessionManager.getSessionId()}:${message.provider}/${message.model}`; } function isRecord3(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function isAbortError2(error) { return error instanceof Error && error.name === "AbortError"; } // src/footer.ts import { FooterComponent } from "@earendil-works/pi-coding-agent"; import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui"; var USAGE_STATUS_KEY = "usage"; function installUsageFooter(ctx) { if (ctx.mode !== "tui") return; ctx.ui.setFooter((tui, theme, footerData) => { const footerDataWithoutUsage = { getGitBranch: () => footerData.getGitBranch(), getAvailableProviderCount: () => 1, onBranchChange: (callback) => footerData.onBranchChange(callback), getExtensionStatuses: () => { const statuses = new Map(footerData.getExtensionStatuses()); statuses.delete(USAGE_STATUS_KEY); return statuses; } }; const session = { state: { get model() { return ctx.model; }, get thinkingLevel() { return ctx.thinkingLevel; } }, sessionManager: ctx.sessionManager, getContextUsage: () => ctx.getContextUsage(), modelRuntime: { isUsingSubscription: (provider) => provider === "kimi-coding" } }; const builtInFooter = new FooterComponent(session, footerDataWithoutUsage); const unsubscribe = footerData.onBranchChange(() => tui.requestRender()); return { dispose() { unsubscribe(); builtInFooter.dispose(); }, invalidate() { }, render(width) { const lines = builtInFooter.render(width); const secondLine = stripTerminalSequences(lines[1] ?? ""); const model = ctx.model; const modelName = model?.id || "no-model"; const thinkingLevel = ctx.thinkingLevel || "off"; const modelText = model?.reasoning ? `${modelName} \u2022 ${thinkingLevel === "off" ? "thinking off" : thinkingLevel}` : modelName; const stats = secondLine.endsWith(modelText) ? secondLine.slice(0, -modelText.length).trimEnd() : secondLine.trimEnd(); const truncatedStats = truncateToWidth(stats, width, ""); const contextPercent = /(\d+(?:\.\d+)?)%(?=\/\S)/u.exec(truncatedStats); if (contextPercent?.index !== void 0) { const percentage = contextPercent[0]; const remaining = 100 - Number(contextPercent[1]); const before = truncatedStats.slice(0, contextPercent.index); const after = truncatedStats.slice(contextPercent.index + percentage.length); lines[1] = `${theme.fg("dim", before)}${MUTED_USAGE_COLORS[usageStatusColor(remaining)]}${percentage}\x1B[39m${theme.fg("dim", after)}`; } else { lines[1] = theme.fg("dim", truncatedStats); } const usageText = footerData.getExtensionStatuses().get(USAGE_STATUS_KEY); if (usageText) lines.splice(2, 0, truncateToWidth(usageText, width, "")); return lines; } }; }); } // src/usage-helpers.ts function configuredAdapters(ctx) { return SUPPORTED_ADAPTERS.filter( (adapter) => adapter.id === ctx.model?.provider || providerIsConfigured(ctx, adapter.id) ); } function providerDisplayName(ctx, providerId) { try { return sanitizeDisplayText(ctx.modelRegistry.getProviderDisplayName(providerId), 80); } catch { return sanitizeDisplayText(providerId, 80); } } function setBoundedMap(map, key, value, limit) { map.delete(key); while (map.size >= limit) { const oldest = map.keys().next().value; if (oldest === void 0) break; map.delete(oldest); } map.set(key, value); } function modelIdentity(model) { return model ? `${model.provider}/${model.id}` : void 0; } function isAbortError3(error) { return error instanceof Error && error.name === "AbortError"; } function isTimeoutError(error) { return error instanceof Error && error.name === "TimeoutError"; } // src/usage.ts var CACHE_TTL_MS = 5 * 60 * 1e3; var DEFAULT_TIMEOUT_MS = 15e3; var ALL_PROVIDER_CONCURRENCY = 2; var FAILURE_BACKOFF_MS = 3e4; var MAX_ACCOUNT_STATES = 32; var STATUS_KEY = "usage"; var REFRESH_CURRENT = "Refresh current usage"; var VIEW_ANOTHER = "View another configured provider\u2026"; var VIEW_ALL = "View all configured providers\u2026"; var CLOSE = "Close"; var REDEEM_CODEX_RESET = "Redeem usage limit reset\u2026"; function usageExtension(pi, dependencies = {}) { const credentialReader = dependencies.credentialReader; const createRedemptionId = dependencies.createRedemptionId ?? randomUUID2; const settingsRuntime = dependencies.settingsRuntime ?? createUsageSettingsRuntime(); const cache = new UsageCache(CACHE_TTL_MS); const failureBackoff = /* @__PURE__ */ new Map(); const latestQueries = /* @__PURE__ */ new Map(); const activeControllers = /* @__PURE__ */ new Set(); let querySequence = 0; let activeCurrentIdentity; let sessionActive = false; let hasSuccessfulStatus = false; let statusGeneration = 0; let statusController; let fastRuntime; const safeSetStatus = (ctx, value, styled = false) => { try { ctx.ui.setStatus( STATUS_KEY, value === void 0 || styled ? value : ctx.ui.theme?.fg("dim", value) ?? value ); return true; } catch (error) { if (isStaleExtensionContextError(error)) return false; throw error; } }; const clearStatus = (ctx) => { hasSuccessfulStatus = false; statusGeneration += 1; statusController?.abort(); statusController = void 0; safeSetStatus(ctx, void 0); }; const publishStatus = (ctx, outcome, model) => { if (outcome.state.status === "unsupported") { safeSetStatus(ctx, void 0); return; } if (outcome.state.status !== "ready") { if (!hasSuccessfulStatus) { safeSetStatus( ctx, outcome.state.status === "auth-unavailable" ? "auth unavailable" : "usage error" ); } return; } const rawValue = formatUsageStatusline(outcome.state.report, model); const decoratedValue = rawValue ? fastRuntime.decorateStatus(model, rawValue) : void 0; const value = decoratedValue && ctx.mode === "tui" && ctx.ui.theme ? styleUsageStatusline(decoratedValue, outcome.state.report, { color: (color, text) => `${MUTED_USAGE_COLORS[color]}${text}\x1B[39m`, dim: (text) => ctx.ui.theme.fg("dim", text) }) : decoratedValue; if (safeSetStatus(ctx, value, ctx.mode === "tui")) hasSuccessfulStatus = true; }; const invalidateProviderState = (providerId) => { cache.clearProvider(providerId); for (const key of failureBackoff.keys()) { if (key.startsWith(`${providerId}:`)) failureBackoff.delete(key); } for (const key of latestQueries.keys()) { if (key.startsWith(`${providerId}:`)) latestQueries.delete(key); } }; const transitionCurrentIdentity = (nextIdentity, providerId) => { if (!activeCurrentIdentity || activeCurrentIdentity === nextIdentity) { activeCurrentIdentity = nextIdentity; return; } hasSuccessfulStatus = false; const previousProviderId = activeCurrentIdentity.split(":", 1)[0] ?? ""; for (const id of /* @__PURE__ */ new Set([previousProviderId, providerId])) { if (id) invalidateProviderState(id); } activeCurrentIdentity = nextIdentity; }; const queryAdapterState = async (ctx, adapter, displayState, force, signal) => { const startedAt = Date.now(); let auth; try { auth = await awaitWithDeadline( resolveUsageAuth(ctx, adapter), signal, DEFAULT_TIMEOUT_MS, `resolving ${adapter.displayName} runtime auth` ); } catch (error) { if (isStaleExtensionContextError(error) || isAbortError3(error)) throw error; if (displayState === "current") { transitionCurrentIdentity(`${adapter.id}:auth-error`, adapter.id); } return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: isTimeoutError(error) ? "query-failed" : "auth-unavailable", message: errorMessage(error) } }; } if (!auth) { if (displayState === "current") { transitionCurrentIdentity(`${adapter.id}:unavailable`, adapter.id); } return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: "auth-unavailable", message: `No runtime credential is configured for ${adapter.displayName}.` }, authState: "unavailable" }; } if (displayState === "current") { transitionCurrentIdentity(`${adapter.id}:${auth.fingerprint}`, adapter.id); } const cached = !force ? cache.get(adapter.id, auth.fingerprint) : void 0; const now = Date.now(); const cachedResetPassed = cached?.buckets.some( (bucket) => bucket.resetsAt !== void 0 && bucket.resetsAt * 1e3 <= now ); if (cached && !cachedResetPassed) { return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: "ready", report: cached }, fingerprint: auth.fingerprint }; } if (cachedResetPassed) cache.clearProvider(adapter.id); const failureKey = `${adapter.id}:${auth.fingerprint}`; const previousFailure = failureBackoff.get(failureKey); if (!force && previousFailure && previousFailure.until > Date.now()) { return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: "query-failed", message: previousFailure.message }, fingerprint: auth.fingerprint }; } failureBackoff.delete(failureKey); querySequence += 1; const queryId = querySequence; setBoundedMap(latestQueries, failureKey, queryId, MAX_ACCOUNT_STATES); try { const remainingMs = Math.max(1, DEFAULT_TIMEOUT_MS - (Date.now() - startedAt)); const report = await queryProviderUsage(adapter, auth, signal, remainingMs); if (latestQueries.get(failureKey) === queryId) { cache.set(adapter.id, auth.fingerprint, report); failureBackoff.delete(failureKey); } return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: "ready", report }, fingerprint: auth.fingerprint }; } catch (error) { if (isStaleExtensionContextError(error) || isAbortError3(error)) throw error; const message = errorMessage(error); const now2 = Date.now(); for (const [key, failure] of failureBackoff) { if (failure.until <= now2) failureBackoff.delete(key); } if (latestQueries.get(failureKey) === queryId) { setBoundedMap( failureBackoff, failureKey, { until: now2 + FAILURE_BACKOFF_MS, message }, MAX_ACCOUNT_STATES ); } return { state: { providerId: adapter.id, providerName: adapter.displayName, displayState, status: "query-failed", message }, fingerprint: auth.fingerprint }; } }; const queryCurrentState = async (ctx, model, force, signal) => { const adapter = adapterForProvider(model?.provider); if (!adapter) { const providerId = model?.provider ?? "none"; transitionCurrentIdentity(`unsupported:${providerId}`, providerId); return { state: { providerId, providerName: providerDisplayName(ctx, providerId), displayState: "current", status: "unsupported", message: model ? `Usage reporting is not supported for ${providerDisplayName(ctx, providerId)}.` : "No model is selected." } }; } return queryAdapterState(ctx, adapter, "current", force, signal); }; const refreshCurrentStatus = async (ctx, model, force) => { const adapter = adapterForProvider(model?.provider); if (!adapter || !model) { const providerId = model?.provider ?? "none"; transitionCurrentIdentity(`unsupported:${providerId}`, providerId); clearStatus(ctx); return; } statusGeneration += 1; const generation = statusGeneration; statusController?.abort(); const controller = new AbortController(); statusController = controller; activeControllers.add(controller); try { if (!hasSuccessfulStatus && !safeSetStatus(ctx, "checking")) return; const outcome = await queryCurrentState(ctx, model, force, controller.signal); if (!sessionActive || generation !== statusGeneration || controller.signal.aborted) return; if (!await outcomeStillCurrent(ctx, model, generation, outcome, controller.signal)) { if (sessionActive && generation === statusGeneration) { queueMicrotask(() => startStatusRefresh(ctx, ctx.model, false)); } return; } publishStatus(ctx, outcome, model); } finally { activeControllers.delete(controller); if (statusController === controller) statusController = void 0; } }; const startStatusRefresh = (ctx, model, force) => { void refreshCurrentStatus(ctx, model, force).catch((error) => { if (isStaleExtensionContextError(error) || isAbortError3(error)) return; if (!hasSuccessfulStatus) safeSetStatus(ctx, "usage error"); }); }; const runMenuOperation = async (ctx, label, parentSignal, operation, cancellable = true) => { const { runTask } = await import("@narumitw/pi-tui-kit"); if (parentSignal.aborted) return void 0; const result = await runTask(ctx, { label, signal: parentSignal, cancellable, onError: () => void 0, task: ({ signal }) => operation(signal) }); switch (result.kind) { case "completed": return result.value; case "cancelled": case "stale": return void 0; case "error": throw result.error; } }; const outcomeStillCurrent = async (ctx, model, generation, outcome, signal) => { if (generation !== statusGeneration || modelIdentity(ctx.model) !== modelIdentity(model)) { return false; } const adapter = adapterForProvider(model?.provider); if (outcome.authState === "unavailable") { if (!adapter) return false; try { const auth = await awaitWithDeadline( resolveUsageAuth(ctx, adapter), signal, DEFAULT_TIMEOUT_MS, `revalidating ${adapter.displayName} runtime auth` ); return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth === void 0; } catch (error) { if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error; return false; } } if (!outcome.fingerprint) return true; if (!adapter) return false; try { const auth = await awaitWithDeadline( resolveUsageAuth(ctx, adapter), signal, DEFAULT_TIMEOUT_MS, `revalidating ${adapter.displayName} runtime auth` ); return generation === statusGeneration && modelIdentity(ctx.model) === modelIdentity(model) && auth?.fingerprint === outcome.fingerprint; } catch (error) { if (isAbortError3(error) || isStaleExtensionContextError(error)) throw error; return false; } }; const queryStableCurrent = async (ctx, force, controller, label) => { for (let attempt = 0; attempt < 3; attempt += 1) { const model = ctx.model; const generation = statusGeneration; const result = await runMenuOperation(ctx, label, controller.signal, async (signal) => { const outcome = await queryCurrentState(ctx, model, force, signal); return { outcome, stable: await outcomeStillCurrent(ctx, model, generation, outcome, signal) }; }); if (!result) return void 0; if (result.stable) return { outcome: result.outcome, model }; force = false; } ctx.ui.notify("The active model or account kept changing; reopen /usage to retry.", "warning"); return void 0; }; const publishStableCurrent = (ctx, current) => { if (current.model) publishStatus(ctx, current.outcome, current.model); else safeSetStatus(ctx, void 0); }; const showMenu = async (ctx) => { if (!ctx.hasUI) throw new Error("/usage requires TUI or RPC mode."); statusGeneration += 1; const menuGeneration = statusGeneration; statusController?.abort(); statusController = void 0; const controller = new AbortController(); activeControllers.add(controller); try { let stableCurrent = await queryStableCurrent( ctx, false, controller, "Checking current usage\u2026" ); if (!stableCurrent) return; publishStableCurrent(ctx, stableCurrent); let current = stableCurrent.outcome; let visibleStates = [current.state]; let fastState = settingsRuntime.get(); let resetAvailability; let selectedReset; let resetAuthFingerprint; let resetModelIdentity; let redemptionId; let resetOutcome; let resetFailure; const { defineMenu, runMenu } = await import("@narumitw/pi-tui-kit"); if (controller.signal.aborted || statusGeneration !== menuGeneration) return; const menu = defineMenu({ start: "main", screens: { main: () => { const fastAvailability = fastRuntime.availability(ctx.model); const fastLines = fastAvailability.kind === "available" ? [`Fast mode: ${fastAvailability.enabled ? "On" : "Off"}`, FAST_USAGE_WARNING] : fastAvailability.kind === "unavailable" ? [`Fast mode: Unavailable \xB7 ${fastAvailability.reason}`] : []; return { kind: "actions", title: "Provider usage", lines: [...formatProviderStates(visibleStates).split("\n"), ...fastLines], items: [ { id: "refresh", label: REFRESH_CURRENT, action: "refresh" }, ...fastAvailability.kind === "available" ? [ { id: "toggle-fast", label: fastAvailability.enabled ? "Turn Fast mode off" : "Turn Fast mode on", description: fastState.kind === "invalid" ? "Repair pi-usage.json and reload before changing Fast mode." : FAST_USAGE_WARNING, disabled: fastState.kind === "invalid", action: "toggle-fast" } ] : [], ...current.state.status === "ready" && current.state.providerId === "openai-codex" ? [ { id: "open-resets", label: REDEEM_CODEX_RESET, description: codexResetActionDescription(current.state.report), disabled: codexResetCount(current.state.report) === 0, action: "open-resets" } ] : [], { id: "another", label: VIEW_ANOTHER, action: "another" }, { id: "all", label: VIEW_ALL, action: "all" }, { id: "close", label: CLOSE, close: true } ], hint: "close" }; }, providers: () => ({ kind: "actions", title: "Select a configured provider", items: configuredAdapters(ctx).filter((adapter) => adapter.id !== ctx.model?.provider).map((adapter) => ({ id: adapter.id, label: adapter.displayName, action: "provider" })), hint: "back" }), "reset-picker": () => ({ kind: "choice", title: "Usage limit resets", lines: [ `${resetAvailability?.availableCount ?? 0} ${resetLabel(resetAvailability?.availableCount ?? 0)} available.` ], items: (resetAvailability?.options ?? []).map((option, index) => ({ id: `reset-${index}`, label: option.title, description: resetOptionExpiration(option), details: [option.description] })), action: "select-reset", initialItemId: "reset-0", hint: "back" }), "reset-confirm": () => ({ kind: "actions", title: "Use this reset?", lines: resetConfirmationLines(selectedReset), items: [ { id: "cancel-reset", label: "No, go back", action: "cancel-reset" }, { id: "consume-reset", label: "Yes, use reset", action: "consume-reset" } ], hint: "back" }), "reset-result": () => ({ kind: "actions", title: "Usage limit resets", lines: [ formatCodexResetOutcome( resetOutcome, current.state.status === "ready" ? codexResetCount(current.state.report) : void 0 ) ], items: [ { id: "back-to-usage", label: "Back to usage", action: "back-to-usage" }, { id: "close", label: CLOSE, close: true } ], hint: "back" }), "reset-error": () => ({ kind: "actions", title: "Usage limit resets", lines: [resetFailure ?? "Couldn't reset usage. Please try again."], items: [ { id: "consume-reset", label: "Try again", action: "consume-reset" }, { id: "back-to-resets", label: "Back", action: "back-to-resets" } ], hint: "back" }) }, actions: { "toggle-fast": async () => { const availability = fastRuntime.availability(ctx.model); if (availability.kind !== "available" || fastState.kind === "invalid") { return { kind: "rejected" }; } const changed = await fastRuntime.toggle(ctx, !availability.enabled, controller.signal); if (!changed) return { kind: "rejected" }; fastState = settingsRuntime.get(); return { kind: "stay" }; }, "open-resets": async () => { const summaryCount = current.state.status === "ready" && current.state.providerId === "openai-codex" ? codexResetCount(current.state.report) : void 0; try { const loaded = await runMenuOperation( ctx, "Checking usage limit resets\u2026", controller.signal, async (signal) => { const expectedModel = modelIdentity(ctx.model); const auth = await awaitWithDeadline( resolveCodexResetAuth(ctx, void 0, credentialReader), signal, DEFAULT_TIMEOUT_MS, "resolving current Codex reset authentication" ); let availability; try { availability = await listCodexResetCredits(auth, signal, DEFAULT_TIMEOUT_MS); } catch (error) { if (isAbortError3(error) || summaryCount === void 0 || summaryCount <= 0) { throw error; } availability = { availableCount: summaryCount, options: [genericCodexResetOption()] }; } const revalidated = await awaitWithDeadline( resolveCodexResetAuth(ctx, void 0, credentialReader), signal, DEFAULT_TIMEOUT_MS, "revalidating current Codex reset authentication" ); if (modelIdentity(ctx.model) !== expectedModel || revalidated.fingerprint !== auth.fingerprint) { throw new Error( "The active Codex model or account changed while loading usage limit resets." ); } return { availability, auth, expectedModel }; } ); if (!loaded) return { kind: "stay" }; resetAvailability = loaded.availability; resetAuthFingerprint = loaded.auth.fingerprint; resetModelIdentity = loaded.expectedModel; selectedReset = void 0; redemptionId = void 0; resetOutcome = void 0; resetFailure = void 0; return { kind: "to", screen: loaded.availability.availableCount > 0 ? "reset-picker" : "reset-result" }; } catch (error) { if (isAbortError3(error) || isStaleExtensionContextError(error)) { return { kind: "stay" }; } ctx.ui.notify(`Couldn't load usage limit resets: ${errorMessage(error)}`, "error"); return { kind: "stay" }; } }, "select-reset": ({ itemId }) => { const index = Number(itemId.replace(/^reset-/u, "")); const option = Number.isSafeInteger(index) ? resetAvailability?.options[index] : void 0; if (!option) return { kind: "rejected" }; selectedReset = option; redemptionId = void 0; resetFailure = void 0; return { kind: "to", screen: "reset-confirm" }; }, "cancel-reset": () => ({ kind: "back" }), "consume-reset": async () => { if (!selectedReset || !resetAuthFingerprint || !resetModelIdentity) { return { kind: "rejected" }; } try { redemptionId ??= createRedemptionId(); const attemptId = redemptionId; const option = selectedReset; const expectedFingerprint = resetAuthFingerprint; const expectedModel = resetModelIdentity; const result = await runMenuOperation( ctx, "Resetting your usage\u2026", controller.signal, async (signal) => { const auth = await awaitWithDeadline( resolveCodexResetAuth(ctx, void 0, credentialReader), signal, DEFAULT_TIMEOUT_MS, "revalidating current Codex reset authentication" ); if (modelIdentity(ctx.model) !== expectedModel || auth.fingerprint !== expectedFingerprint) { throw new Error( "The active Codex model or account changed; the reset was not used." ); } const outcome = await consumeCodexResetCredit( auth, option, attemptId, signal, DEFAULT_TIMEOUT_MS ); invalidateProviderState("openai-codex"); const model = ctx.model; if (modelIdentity(model) !== expectedModel) return { outcome }; const refreshed = await queryCurrentState(ctx, model, true, signal); const stable = await outcomeStillCurrent( ctx, model, menuGeneration, refreshed, signal ); return stable ? { outcome, refreshed, model } : { outcome }; }, false ); if (!result) return { kind: "close" }; resetOutcome = result.outcome; resetFailure = void 0; if (result.refreshed && result.model) { stableCurrent = { outcome: result.refreshed, model: result.model }; current = result.refreshed; visibleStates = [current.state]; publishStableCurrent(ctx, stableCurrent); } return { kind: "to", screen: "reset-result" }; } catch (error) { if (isAbortError3(error) || isStaleExtensionContextError(error)) { return { kind: "close" }; } resetFailure = `Couldn't reset usage: ${errorMessage(error)}. Try again with the same request.`; return { kind: "to", screen: "reset-error" }; } }, "back-to-usage": () => ({ kind: "to", screen: "main" }), "back-to-resets": () => { redemptionId = void 0; resetFailure = void 0; return { kind: "to", screen: "reset-picker" }; }, refresh: async () => { const refreshed = await queryStableCurrent( ctx, true, controller, "Refreshing current usage\u2026" ); if (!refreshed) return { kind: "stay" }; stableCurrent = refreshed; publishStableCurrent(ctx, refreshed); current = refreshed.outcome; visibleStates = [current.state]; return { kind: "stay" }; }, another: async () => { const others = configuredAdapters(ctx).filter( (adapter) => adapter.id !== ctx.model?.provider ); if (others.length === 0) { ctx.ui.notify("No other supported provider has configured runtime auth.", "info"); return { kind: "stay" }; } return { kind: "to", screen: "providers" }; }, provider: async ({ itemId }) => { const adapter = configuredAdapters(ctx).find( (candidate) => candidate.id === itemId && candidate.id !== ctx.model?.provider ); if (!adapter) return { kind: "back" }; const outcome = await runMenuOperation( ctx, `Checking ${adapter.displayName} usage\u2026`, controller.signal, (signal) => queryAdapterState(ctx, adapter, "configured", false, signal) ); if (!outcome) return { kind: "back" }; const revalidated = await queryStableCurrent( ctx, false, controller, "Revalidating current usage\u2026" ); if (!revalidated) return { kind: "back" }; stableCurrent = revalidated; current = revalidated.outcome; visibleStates = [ outcome.state.providerId === current.state.providerId ? current.state : { ...outcome.state, displayState: "configured" } ]; return { kind: "back" }; }, all: async () => { const adapters = configuredAdapters(ctx); const currentProviderId = ctx.model?.provider; const settled = await runMenuOperation( ctx, "Checking configured provider usage\u2026", controller.signal, (signal) => runWithConcurrency( adapters, ALL_PROVIDER_CONCURRENCY, (adapter, _index, workerSignal) => queryAdapterState( ctx, adapter, adapter.id === currentProviderId ? "current" : "configured", true, workerSignal ), signal ) ); if (!settled) return { kind: "stay" }; const queriedStates = settled.map((result, index) => { if (result.status === "fulfilled") { return { ...result.value.state, displayState: "configured" }; } const adapter = adapters[index]; return { providerId: adapter.id, providerName: adapter.displayName, displayState: "configured", status: "query-failed", message: errorMessage(result.reason) }; }); const revalidated = await queryStableCurrent( ctx, false, controller, "Revalidating current usage\u2026" ); if (!revalidated) return { kind: "stay" }; stableCurrent = revalidated; current = revalidated.outcome; visibleStates = [ current.state, ...queriedStates.filter((state) => state.providerId !== current.state.providerId) ]; return { kind: "stay" }; } } }); await runMenu(ctx, menu, { getState: () => void 0, signal: controller.signal, isCurrent: () => statusGeneration === menuGeneration && !controller.signal.aborted }); } finally { controller.abort(new DOMException("Usage menu closed", "AbortError")); activeControllers.delete(controller); } }; pi.registerCommand("usage", { description: "Show usage for the current runtime account", handler: async (args, ctx) => { if (args.trim()) { ctx.ui.notify( "/usage does not accept arguments; choose an action from its menu.", "warning" ); return; } try { installUsageFooter(ctx); await showMenu(ctx); } catch (error) { if (isStaleExtensionContextError(error) || isAbortError3(error)) return; throw error; } } }); pi.on("session_start", (_event, ctx) => { statusGeneration += 1; for (const controller of activeControllers) controller.abort(); activeControllers.clear(); statusController = void 0; sessionActive = true; installUsageFooter(ctx); startStatusRefresh(ctx, ctx.model, false); }); pi.on("session_tree", (_event, ctx) => { installUsageFooter(ctx); startStatusRefresh(ctx, ctx.model, false); }); pi.on("model_select", (event, ctx) => { installUsageFooter(ctx); startStatusRefresh(ctx, event.model, false); }); pi.on("turn_start", (_event, ctx) => { installUsageFooter(ctx); startStatusRefresh(ctx, ctx.model, false); }); pi.on("agent_settled", (_event, ctx) => { installUsageFooter(ctx); const adapter = adapterForProvider(ctx.model?.provider); if (adapter) cache.clearProvider(adapter.id); startStatusRefresh(ctx, ctx.model, false); }); pi.on("session_shutdown", (_event, ctx) => { sessionActive = false; hasSuccessfulStatus = false; statusGeneration += 1; for (const controller of activeControllers) controller.abort(); activeControllers.clear(); statusController = void 0; cache.clear(); failureBackoff.clear(); latestQueries.clear(); activeCurrentIdentity = void 0; safeSetStatus(ctx, void 0); }); fastRuntime = registerCodexFastMode( pi, settingsRuntime, (ctx) => startStatusRefresh(ctx, ctx.model, false) ); } export { CODEX_FAST_MODEL_IDS, CODEX_FAST_SERVICE_TIER, CODEX_STANDARD_SERVICE_TIER, DEFAULT_USAGE_SETTINGS, SUPPORTED_ADAPTERS, UsageCache, abortError, adapterForProvider, awaitWithDeadline, codexFastAvailability, codexFastIsEffective, codexFastRequestTier, codexFastStatusLabel, consumeCodexResetCredit, correctCodexFastMessageCost, createUsageSettingsRuntime, usageExtension as default, errorMessage, fingerprintResolvedAuth, formatProviderStates, formatUsageReport, formatUsageStatusline, isStaleExtensionContextError, listCodexResetCredits, loadUsageSettings, normalizeCodexBackendPayload, normalizeCodexResetCreditsPayload, normalizeGitHubCopilotUsagePayload, normalizeOpenCodeZenPayload, normalizeOpenRouterKeyPayload, normalizeUsageSettings, providerIsConfigured, queryProviderUsage, redactUsageError, resolveCodexResetAuth, resolveUsageAuth, rewriteCodexFastPayload, runWithConcurrency, sanitizeDisplayText, styleUsageStatusline, usageSettingsPath }; //# sourceMappingURL=index.ts.map