From a5dd98d58a37723bd881ea7b43ad368ba67a4e51 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Sat, 16 May 2026 17:03:50 -0400 Subject: [PATCH] fix(codex): normalize rate limit window labels Co-authored-by: Orca --- src/main/rate-limits/codex-fetcher.test.ts | 43 +++++++++++++++++++ src/main/rate-limits/codex-fetcher.ts | 13 ++++-- .../src/lib/window-label-formatter.test.ts | 4 +- .../src/lib/window-label-formatter.ts | 6 +-- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/main/rate-limits/codex-fetcher.test.ts b/src/main/rate-limits/codex-fetcher.test.ts index cdeb1fa7fc7..1eb64c38c3d 100644 --- a/src/main/rate-limits/codex-fetcher.test.ts +++ b/src/main/rate-limits/codex-fetcher.test.ts @@ -109,4 +109,47 @@ describe('fetchCodexRateLimits', () => { error: null }) }) + + it('normalizes Codex RPC remaining-minute windows to fixed display durations', async () => { + const rpcChild = makeRpcChild() + childSpawnMock.mockReturnValue(rpcChild) + rpcChild.stdin.write.mockImplementation((line: string) => { + const msg = JSON.parse(line) as { id?: number; method?: string } + if (msg.method === 'initialize') { + setTimeout(() => { + rpcChild.stdout.emit( + 'data', + Buffer.from(`${JSON.stringify({ jsonrpc: '2.0', id: msg.id, result: {} })}\n`) + ) + }, 0) + } + if (msg.method === 'account/rateLimits/read') { + setTimeout(() => { + rpcChild.stdout.emit( + 'data', + Buffer.from( + `${JSON.stringify({ + jsonrpc: '2.0', + id: msg.id, + result: { + rateLimits: { + primary: { usedPercent: 0, windowDurationMins: 299 }, + secondary: { usedPercent: 0, windowDurationMins: 10079 } + } + } + })}\n` + ) + ) + }, 0) + } + }) + + const resultPromise = fetchCodexRateLimits() + await vi.advanceTimersByTimeAsync(1) + await vi.advanceTimersByTimeAsync(1) + const result = await resultPromise + + expect(result.session?.windowMinutes).toBe(300) + expect(result.weekly?.windowMinutes).toBe(10080) + }) }) diff --git a/src/main/rate-limits/codex-fetcher.ts b/src/main/rate-limits/codex-fetcher.ts index 486e0e7486d..c3cc96d659f 100644 --- a/src/main/rate-limits/codex-fetcher.ts +++ b/src/main/rate-limits/codex-fetcher.ts @@ -44,7 +44,10 @@ function buildRpcMessage(id: number, method: string, params?: unknown): string { return `${JSON.stringify({ jsonrpc: '2.0', id, method, params: params ?? {} })}\n` } -function mapRpcWindow(raw: RpcRateWindow | undefined): RateLimitWindow | null { +function mapRpcWindow( + raw: RpcRateWindow | undefined, + expectedWindowMinutes: number +): RateLimitWindow | null { if (!raw || typeof raw.usedPercent !== 'number') { return null } @@ -70,7 +73,9 @@ function mapRpcWindow(raw: RpcRateWindow | undefined): RateLimitWindow | null { return { usedPercent: Math.min(100, Math.max(0, raw.usedPercent)), - windowMinutes: raw.windowDurationMins ?? 300, + // Why: Codex currently reports remaining minutes in `windowDurationMins`. + // Orca's UI needs the fixed bucket duration so labels stay "5h" / "7d". + windowMinutes: expectedWindowMinutes, resetsAt, resetDescription } @@ -199,8 +204,8 @@ async function fetchViaRpc(options?: FetchCodexRateLimitsOptions): Promise { expect(formatWindowLabel(60)).toBe('1h') }) - it('returns "wk" for 10080 minutes (7 days)', () => { - expect(formatWindowLabel(10080)).toBe('wk') + it('returns "7d" for 10080 minutes (7 days)', () => { + expect(formatWindowLabel(10080)).toBe('7d') }) it('returns "1d" for 1440 minutes (1 day)', () => { diff --git a/src/renderer/src/lib/window-label-formatter.ts b/src/renderer/src/lib/window-label-formatter.ts index 1fbbcdb54f5..c233c6df9f2 100644 --- a/src/renderer/src/lib/window-label-formatter.ts +++ b/src/renderer/src/lib/window-label-formatter.ts @@ -1,12 +1,12 @@ /** * Returns a short human-readable label for a usage window duration. * - * Why: 10080 minutes (7 days) is hard-coded as "wk" for backward - * compatibility with the original StatusBar implementation. + * Why: the status bar uses duration labels, so the weekly bucket is shown as + * "7d" instead of a calendar-period label like "wk". */ export function formatWindowLabel(windowMinutes: number): string { if (windowMinutes === 10080) { - return 'wk' + return '7d' } if (windowMinutes === 300) { return '5h'