Files
clawdbot/src/infra/provider-usage.fetch.codex.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

100 lines
2.7 KiB
TypeScript

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