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