fix: scan all Codex limits for max 5h/7d, persist d7 reset times
- Iterate all rate limits per account instead of just display[0] - Track and persist 7-day reset time separately (resetD7At) - Show both 5h and 7d cached usage for inactive Codex accounts
This commit is contained in:
@@ -29,9 +29,13 @@ interface CodexAccount {
|
||||
lastUsagePct?: number;
|
||||
lastUsageD7Pct?: number;
|
||||
resetAt?: string;
|
||||
resetD7At?: string;
|
||||
}
|
||||
|
||||
function parseJwtAuth(idToken: string): { planType: string; expiresAt: string | null } {
|
||||
function parseJwtAuth(idToken: string): {
|
||||
planType: string;
|
||||
expiresAt: string | null;
|
||||
} {
|
||||
try {
|
||||
const parts = idToken.split('.');
|
||||
if (parts.length < 2) return { planType: '?', expiresAt: null };
|
||||
@@ -108,6 +112,7 @@ function saveCodexState(): void {
|
||||
usagePcts: accounts.map((a) => a.lastUsagePct ?? null),
|
||||
usageD7Pcts: accounts.map((a) => a.lastUsageD7Pct ?? null),
|
||||
resetAts: accounts.map((a) => a.resetAt ?? null),
|
||||
resetD7Ats: accounts.map((a) => a.resetD7At ?? null),
|
||||
};
|
||||
fs.writeFileSync(STATE_FILE, JSON.stringify(state));
|
||||
} catch {
|
||||
@@ -149,19 +154,25 @@ function loadCodexState(): void {
|
||||
}
|
||||
}
|
||||
if (Array.isArray(state.usageD7Pcts)) {
|
||||
for (let i = 0; i < Math.min(state.usageD7Pcts.length, accounts.length); i++) {
|
||||
if (typeof state.usageD7Pcts[i] === 'number') accounts[i].lastUsageD7Pct = state.usageD7Pcts[i];
|
||||
for (
|
||||
let i = 0;
|
||||
i < Math.min(state.usageD7Pcts.length, accounts.length);
|
||||
i++
|
||||
) {
|
||||
if (typeof state.usageD7Pcts[i] === 'number')
|
||||
accounts[i].lastUsageD7Pct = state.usageD7Pcts[i];
|
||||
}
|
||||
}
|
||||
if (Array.isArray(state.resetAts)) {
|
||||
for (
|
||||
let i = 0;
|
||||
i < Math.min(state.resetAts.length, accounts.length);
|
||||
i++
|
||||
) {
|
||||
for (let i = 0; i < Math.min(state.resetAts.length, accounts.length); i++) {
|
||||
if (state.resetAts[i]) accounts[i].resetAt = state.resetAts[i];
|
||||
}
|
||||
}
|
||||
if (Array.isArray(state.resetD7Ats)) {
|
||||
for (let i = 0; i < Math.min(state.resetD7Ats.length, accounts.length); i++) {
|
||||
if (state.resetD7Ats[i]) accounts[i].resetD7At = state.resetD7Ats[i];
|
||||
}
|
||||
}
|
||||
logger.info(
|
||||
{ currentIndex, accountCount: accounts.length },
|
||||
'Codex rotation state restored',
|
||||
@@ -275,6 +286,7 @@ export function updateCodexAccountUsage(
|
||||
resetAt?: string,
|
||||
accountIndex?: number,
|
||||
d7Pct?: number,
|
||||
resetD7At?: string,
|
||||
): void {
|
||||
if (accounts.length === 0) return;
|
||||
const idx = accountIndex ?? currentIndex;
|
||||
@@ -283,6 +295,7 @@ export function updateCodexAccountUsage(
|
||||
acct.lastUsagePct = usagePct;
|
||||
if (d7Pct != null) acct.lastUsageD7Pct = d7Pct;
|
||||
if (resetAt) acct.resetAt = resetAt;
|
||||
if (resetD7At) acct.resetD7At = resetD7At;
|
||||
saveCodexState();
|
||||
}
|
||||
}
|
||||
@@ -309,6 +322,7 @@ export function getAllCodexAccounts(): {
|
||||
cachedUsagePct?: number;
|
||||
cachedUsageD7Pct?: number;
|
||||
resetAt?: string;
|
||||
resetD7At?: string;
|
||||
}[] {
|
||||
const now = Date.now();
|
||||
return accounts.map((a, i) => ({
|
||||
@@ -320,5 +334,6 @@ export function getAllCodexAccounts(): {
|
||||
cachedUsagePct: a.lastUsagePct,
|
||||
cachedUsageD7Pct: a.lastUsageD7Pct,
|
||||
resetAt: a.resetAt,
|
||||
resetD7At: a.resetD7At,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -646,17 +646,17 @@ async function buildUsageContent(): Promise<string> {
|
||||
}
|
||||
} else {
|
||||
// Show cached usage from last scan
|
||||
const pct =
|
||||
acct.cachedUsagePct != null ? acct.cachedUsagePct : -1;
|
||||
const pct = acct.cachedUsagePct != null ? acct.cachedUsagePct : -1;
|
||||
const d7pct =
|
||||
acct.cachedUsageD7Pct != null ? acct.cachedUsageD7Pct : -1;
|
||||
const reset = acct.resetAt || '';
|
||||
const d7reset = acct.resetD7At || '';
|
||||
rows.push({
|
||||
name: `${label} ${acct.planType}`,
|
||||
h5pct: pct,
|
||||
h5reset: reset,
|
||||
d7pct,
|
||||
d7reset: '',
|
||||
d7reset,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -794,20 +794,42 @@ async function refreshAllCodexAccountUsage(): Promise<void> {
|
||||
(l) => l.primary.usedPercent > 0 || l.secondary.usedPercent > 0,
|
||||
);
|
||||
const display = relevant.length > 0 ? relevant : usage.slice(0, 1);
|
||||
if (display.length > 0) {
|
||||
const pct = Math.round(display[0].primary.usedPercent);
|
||||
const resetVal = display[0].primary.resetsAt;
|
||||
const resetStr = resetVal ? formatResetRemaining(resetVal) : undefined;
|
||||
const d7Pct = Math.round(display[0].secondary.usedPercent);
|
||||
updateCodexAccountUsage(pct, resetStr, acct.index, d7Pct);
|
||||
if (usage.length > 0) {
|
||||
// Find max primary (5h) and secondary (7d) across all limits
|
||||
let maxH5 = 0;
|
||||
let maxD7 = 0;
|
||||
let h5Reset: string | number | undefined;
|
||||
let d7Reset: string | number | undefined;
|
||||
for (const limit of usage) {
|
||||
if (limit.primary.usedPercent > maxH5) {
|
||||
maxH5 = limit.primary.usedPercent;
|
||||
h5Reset = limit.primary.resetsAt;
|
||||
}
|
||||
if (limit.secondary.usedPercent > maxD7) {
|
||||
maxD7 = limit.secondary.usedPercent;
|
||||
d7Reset = limit.secondary.resetsAt;
|
||||
}
|
||||
}
|
||||
const pct = Math.round(maxH5);
|
||||
const d7Pct = Math.round(maxD7);
|
||||
const resetStr = h5Reset
|
||||
? formatResetRemaining(h5Reset)
|
||||
: undefined;
|
||||
const resetD7Str = d7Reset
|
||||
? formatResetRemaining(d7Reset)
|
||||
: undefined;
|
||||
updateCodexAccountUsage(pct, resetStr, acct.index, d7Pct, resetD7Str);
|
||||
logger.info(
|
||||
{ account: acct.index + 1, usagePct: pct, reset: resetStr },
|
||||
`Codex account #${acct.index + 1} usage: ${pct}%`,
|
||||
{ account: acct.index + 1, h5: pct, d7: d7Pct, reset: resetStr },
|
||||
`Codex account #${acct.index + 1} usage: 5h=${pct}% 7d=${d7Pct}%`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.debug({ err, account: acct.index + 1 }, 'Failed to fetch usage for Codex account');
|
||||
logger.debug(
|
||||
{ err, account: acct.index + 1 },
|
||||
'Failed to fetch usage for Codex account',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -881,7 +903,10 @@ export async function startUnifiedDashboard(
|
||||
setInterval(refreshUsageCache, opts.usageUpdateInterval);
|
||||
// Full scan of all Codex accounts on startup + hourly
|
||||
void refreshAllCodexAccountUsage();
|
||||
setInterval(() => void refreshAllCodexAccountUsage(), CODEX_FULL_SCAN_INTERVAL);
|
||||
setInterval(
|
||||
() => void refreshAllCodexAccountUsage(),
|
||||
CODEX_FULL_SCAN_INTERVAL,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
|
||||
Reference in New Issue
Block a user