fix: usage API rate-limit guard and cleanup debug logging

This commit is contained in:
Eyejoker
2026-03-24 03:47:46 +09:00
parent 6a73421d4f
commit f6ba879e8a
4 changed files with 37 additions and 34 deletions

View File

@@ -58,13 +58,17 @@ function loadUsageDiskCache(): void {
if (fs.existsSync(USAGE_CACHE_FILE)) { if (fs.existsSync(USAGE_CACHE_FILE)) {
usageDiskCache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, 'utf-8')); usageDiskCache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, 'utf-8'));
} }
} catch { /* start fresh */ } } catch {
/* start fresh */
}
} }
function saveUsageDiskCache(): void { function saveUsageDiskCache(): void {
try { try {
fs.writeFileSync(USAGE_CACHE_FILE, JSON.stringify(usageDiskCache)); fs.writeFileSync(USAGE_CACHE_FILE, JSON.stringify(usageDiskCache));
} catch { /* best effort */ } } catch {
/* best effort */
}
} }
function cacheKey(token: string): string { function cacheKey(token: string): string {
@@ -104,7 +108,9 @@ async function fetchUsageForToken(
return null; return null;
} }
if (res.status === 429) { if (res.status === 429) {
logger.warn('Claude usage API: rate limited (429), returning cached data'); logger.warn(
'Claude usage API: rate limited (429), returning cached data',
);
return usageDiskCache[cacheKey(token)]?.usage ?? null; return usageDiskCache[cacheKey(token)]?.usage ?? null;
} }
if (!res.ok) { if (!res.ok) {

View File

@@ -48,15 +48,13 @@ interface FallbackConfig {
// ── State ──────────────────────────────────────────────────────── // ── State ────────────────────────────────────────────────────────
let cooldown: CooldownState | null = null; let cooldown: CooldownState | null = null;
let lastUsageAvailabilityCheck: let lastUsageAvailabilityCheck: {
| { checkedAt: number;
checkedAt: number; result: 'available' | 'exhausted' | 'unknown';
result: 'available' | 'exhausted' | 'unknown'; } | null = null;
} let usageAvailabilityCheckPromise: Promise<
| null = null; 'available' | 'exhausted' | 'unknown'
let usageAvailabilityCheckPromise: > | null = null;
| Promise<'available' | 'exhausted' | 'unknown'>
| null = null;
const USAGE_RECOVERY_RECHECK_MS = 30_000; const USAGE_RECOVERY_RECHECK_MS = 30_000;
@@ -213,7 +211,10 @@ export async function getActiveProvider(): Promise<string> {
} }
if (usageAvailability === 'exhausted') { if (usageAvailability === 'exhausted') {
// Current token exhausted — try rotating to another token (ignore cooldowns) // Current token exhausted — try rotating to another token (ignore cooldowns)
if (getTokenCount() > 1 && rotateToken(undefined, { ignoreRateLimits: true })) { if (
getTokenCount() > 1 &&
rotateToken(undefined, { ignoreRateLimits: true })
) {
logger.info( logger.info(
'Claude current token exhausted, rotated to next token — retrying', 'Claude current token exhausted, rotated to next token — retrying',
); );

View File

@@ -307,14 +307,14 @@ Check the run.
phase: 'intermediate', phase: 'intermediate',
result: "You're out of extra usage · resets 4am (Asia/Seoul)", result: "You're out of extra usage · resets 4am (Asia/Seoul)",
}); });
await onOutput?.({ await onOutput?.({
status: 'success', status: 'success',
result: "You're out of extra usage · resets 4am (Asia/Seoul)", result: "You're out of extra usage · resets 4am (Asia/Seoul)",
}); });
return { return {
status: 'success', status: 'success',
result: null, result: null,
}; };
}, },
) )
.mockImplementationOnce( .mockImplementationOnce(
@@ -324,14 +324,14 @@ Check the run.
_onProcess: unknown, _onProcess: unknown,
onOutput?: (output: Record<string, unknown>) => Promise<void>, onOutput?: (output: Record<string, unknown>) => Promise<void>,
) => { ) => {
await onOutput?.({ await onOutput?.({
status: 'success', status: 'success',
result: 'rotated scheduled task response', result: 'rotated scheduled task response',
}); });
return { return {
status: 'success', status: 'success',
result: null, result: null,
}; };
}, },
); );

View File

@@ -156,11 +156,7 @@ export function rotateToken(
for (let i = 1; i < tokens.length; i++) { for (let i = 1; i < tokens.length; i++) {
const idx = (currentIndex + i) % tokens.length; const idx = (currentIndex + i) % tokens.length;
const state = tokens[idx]; const state = tokens[idx];
if ( if (ignoreRL || !state.rateLimitedUntil || state.rateLimitedUntil <= now) {
ignoreRL ||
!state.rateLimitedUntil ||
state.rateLimitedUntil <= now
) {
state.rateLimitedUntil = null; state.rateLimitedUntil = null;
currentIndex = idx; currentIndex = idx;
logger.info( logger.info(