mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
feat(codex): show reset credit expiry details (#2290)
Add read-only GET to inspect per-credit reset inventory (status, granted, expiry, remaining) with a Quota Tracker modal. DRY the route via shared connection/refresh helpers; keep existing consume POST unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
cd557a2552
commit
5cc4f222f8
@@ -8,9 +8,23 @@ import { U, parseResetTime, toFiniteNumber } from "./shared.js";
|
||||
// Codex (OpenAI) API config
|
||||
const CODEX_CONFIG = {
|
||||
usageUrl: U("codex").url,
|
||||
resetCreditsUrl: U("codex").resetCreditsUrl,
|
||||
resetCreditsConsumeUrl: U("codex").resetCreditsConsumeUrl,
|
||||
};
|
||||
|
||||
function toIsoDate(value) {
|
||||
if (!value) return null;
|
||||
const date = value instanceof Date
|
||||
? value
|
||||
: new Date(typeof value === "number" && value < 1e12 ? value * 1000 : value);
|
||||
const time = date.getTime();
|
||||
return Number.isFinite(time) ? date.toISOString() : null;
|
||||
}
|
||||
|
||||
function getCodexAccountId(providerSpecificData) {
|
||||
return providerSpecificData?.workspaceId || providerSpecificData?.accountId || providerSpecificData?.chatgptAccountId || null;
|
||||
}
|
||||
|
||||
function getCodexRateLimitBody(snapshot) {
|
||||
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) return null;
|
||||
return snapshot.rate_limit && typeof snapshot.rate_limit === "object"
|
||||
@@ -101,6 +115,48 @@ export async function getCodexUsage(accessToken, proxyOptions = null) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCodexRateLimitResetCredits(accessToken, proxyOptions = null, providerSpecificData = null) {
|
||||
if (!accessToken) {
|
||||
throw new Error("No Codex access token available. Please re-authorize the connection.");
|
||||
}
|
||||
|
||||
const accountId = getCodexAccountId(providerSpecificData);
|
||||
const headers = {
|
||||
"Authorization": `Bearer ${accessToken}`,
|
||||
"Accept": "application/json",
|
||||
"OpenAI-Beta": "codex-1",
|
||||
"originator": "codex_cli_rs",
|
||||
};
|
||||
if (accountId) headers["ChatGPT-Account-ID"] = accountId;
|
||||
|
||||
const response = await proxyAwareFetch(CODEX_CONFIG.resetCreditsUrl, {
|
||||
method: "GET",
|
||||
headers,
|
||||
}, proxyOptions);
|
||||
|
||||
let data = null;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const message = data?.message || data?.error || data?.detail || `Codex reset credits API unavailable (${response.status}).`;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const credits = Array.isArray(data?.credits) ? data.credits : [];
|
||||
return {
|
||||
availableCount: Math.max(0, toFiniteNumber(data?.available_count ?? data?.availableCount, 0)),
|
||||
credits: credits.map((credit) => ({
|
||||
status: String(credit?.status || "unknown"),
|
||||
grantedAt: toIsoDate(credit?.granted_at ?? credit?.grantedAt),
|
||||
expiresAt: toIsoDate(credit?.expires_at ?? credit?.expiresAt),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// Consume one Codex rate-limit reset credit (irreversible, spends 1 credit)
|
||||
export async function consumeCodexRateLimitResetCredit(accessToken, redeemRequestId, proxyOptions = null) {
|
||||
if (!accessToken) {
|
||||
|
||||
Reference in New Issue
Block a user