menubar: surface Codex limit-reset credits (count and next expiry) in the Plan tab#724
Conversation
144f7cd to
d612869
Compare
d612869 to
fadea37
Compare
iamtoruk
left a comment
There was a problem hiding this comment.
Verified locally: swift build green, full run green under both test frameworks, and the parser semantics are mutation-pinned (max-instead-of-min expiry fails a test, dropping the available-status filter fails a test). The lifecycle integration answers the #723 constraint better than I phrased it: fetching inside the usage 200 branch means same single-flight, same cadence, and during a usage 429 block the credits endpoint is never touched at all, so Retry-After is honored transitively. Zero-count hides the row as asked.
One accepted softness, noting for the record rather than blocking: credits ride the usage snapshot, so a credits-only failure on an otherwise successful refresh hides the row instead of showing last-good. Honest omission of an informational row is fine; if users ever report flicker we can add a short-lived carry-over then.
Closes #723.
What changed
The menubar's Codex Plan tab now shows the limit-reset credits the account is holding: a
Limit resets · N available · next expires <relative>row under the quota windows, hidden when the count is zero.CodexSubscriptionServicegains a companion fetch ofGET /backend-api/wham/rate-limit-reset-credits(same bearer token andChatGPT-Account-Idheader as the existing/wham/usagecall, plus theOpenAI-Beta: codex-1gate header, 4s timeout). It is strictly best-effort: any network/HTTP/decode failure yieldsniland the Plan view renders exactly as before — this endpoint can never break the quota display.parseResetCreditsis a small internal parser: takes the server'savailable_countas authoritative, and derives "next expiry" as the soonest futureexpires_atamong credits withstatus == "available"(redeemed/unknown-status credits and already-expired timestamps are excluded). ISO-8601 with and without fractional seconds both accepted.CodexUsagegains an optionalresetCreditsfield; nothing else about the usage decode changes.How tested
swift buildclean via a local swift.org 6.3.3 toolchain.CodexResetCreditsTests(10 assertions) cover: full payload picking the soonest available expiry (fractional-seconds timestamp) over an earlier-expiring redeemed credit; missingexpires_at; unknownstatusvalues; already-expired credits; empty list; malformed document / missing count / negative count →nil.mac/Testssuite, not just this file), so CI is the test gate here. The parser logic was additionally verified locally by running the identical fixtures through a standalone script under the same toolchain: 10/10 pass.Not covered: a live end-to-end fetch against chatgpt.com (no test account plumbing in the repo); the endpoint contract matches what the Codex desktop clients use.