From cfb03f4c6365f4c41e59f41505717300a97a5ec5 Mon Sep 17 00:00:00 2001 From: moseoh Date: Tue, 14 Jul 2026 10:40:24 +0900 Subject: [PATCH] fix(rate-limits): keep Codex PTY reset text for weekly-only plans The PTY /status fallback parses '5h limit' and 'Weekly limit' lines by label, but the extracted reset text was only ever attached to the session window. Codex plans without a 5h session bucket (e.g. current Pro) produce a weekly-only parse, so the reset time the CLI printed was silently dropped. Fall back to the weekly window when no session window exists. --- .../codex-fetcher-pty-settle.test.ts | 36 +++++++++++++++++++ src/main/rate-limits/codex-fetcher.ts | 8 +++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/rate-limits/codex-fetcher-pty-settle.test.ts b/src/main/rate-limits/codex-fetcher-pty-settle.test.ts index c5088a2ae77..a193a229acd 100644 --- a/src/main/rate-limits/codex-fetcher-pty-settle.test.ts +++ b/src/main/rate-limits/codex-fetcher-pty-settle.test.ts @@ -76,4 +76,40 @@ describe('fetchCodexRateLimits PTY settle timers', () => { status: 'ok' }) }) + + it('keeps the reset text on the weekly window for weekly-only plans', async () => { + const ptyHandlers: { onData?: (data: string) => void } = {} + + childSpawnMock.mockImplementation(() => { + throw new Error('rpc unavailable') + }) + ptySpawnMock.mockReturnValue({ + onData: vi.fn((callback) => { + ptyHandlers.onData = callback + return makeDisposable() + }), + onExit: vi.fn(() => makeDisposable()), + write: vi.fn(), + kill: vi.fn() + }) + + const resultPromise = fetchCodexRateLimits() + await vi.advanceTimersByTimeAsync(0) + + const onPtyData = ptyHandlers.onData + if (!onPtyData) { + throw new Error('PTY data handler was not registered') + } + + onPtyData('>') + onPtyData('Weekly limit: 76%\nResets in 5d 23h\n') + + await vi.advanceTimersByTimeAsync(500) + + await expect(resultPromise).resolves.toMatchObject({ + session: null, + weekly: { usedPercent: 76, resetDescription: '5d 23h' }, + status: 'ok' + }) + }) }) diff --git a/src/main/rate-limits/codex-fetcher.ts b/src/main/rate-limits/codex-fetcher.ts index d606a12e850..26019c5e08e 100644 --- a/src/main/rate-limits/codex-fetcher.ts +++ b/src/main/rate-limits/codex-fetcher.ts @@ -827,10 +827,12 @@ function parsePtyStatus(output: string): { } : null - // Try to extract reset time from surrounding text + // Try to extract reset time from surrounding text. Weekly-only plans have + // no session window, so fall back to the weekly one instead of dropping it. const resetMatch = RESET_TEXT_RE.exec(output) - if (resetMatch && session) { - session.resetDescription = resetMatch[1].trim() + const resetTarget = session ?? weekly + if (resetMatch && resetTarget) { + resetTarget.resetDescription = resetMatch[1].trim() } return { session, weekly }