codex: capture rate-limit snapshots, serve GET /usage#62
Conversation
The upstream pushes a codex.rate_limits snapshot on responses whether or not a limit is reached, but limit_reached:false snapshots were discarded. The only way to check remaining quota was the unofficial chatgpt.com/backend-api/wham/usage side channel with hand-extracted tokens. Record the latest snapshot at both stream choke points (live WebSocket payload translation and the buffered reducer) and serve it verbatim, with a captured_at stamp, from GET /usage. Verbatim because the payload shape is undocumented and re-modeling it field by field would break on upstream changes. Operators can now preflight long runs with: curl -s localhost:18765/usage | jq .codex.rate_limits
|
Prior art note: this is the second attempt at this feature. #16 proposed the same approach in May and was closed by its author because, in his testing then, subscription-auth sessions never emitted That premise no longer holds. Captured today (2026-07-19) on a plain ChatGPT Pro subscription login (browser PKCE, Keychain storage, default WebSocket transport, {
"codex": {
"captured_at": "2026-07-19T06:32:42.650243Z",
"rate_limits": {
"allowed": true,
"limit_reached": false,
"primary": {
"reset_after_seconds": 518773,
"reset_at": 1784961534,
"used_percent": 55,
"window_minutes": 10080
},
"secondary": null
}
}
}I can't say whether the difference from May is the WebSocket-first transport, the July quota changes on the backend, or both; but on current subscription traffic the snapshots flow on every response, which is exactly the case #16 was closed over. Happy to adjust shape/naming if you'd prefer something closer to what you had in mind with tmux-agent-usage. |
Completes the /usage feature so quota survives a proxy restart and is visible in the monitor: - rate_limits.rs mirrors each snapshot to state_dir/codex-usage.json and loads it on a cold read, so GET /usage answers immediately after a restart instead of returning null until the first upstream request. Writes are best-effort and never block request handling. - The monitor TUI footer shows the weekly window as remaining percent (Codex-app convention), color-coded, with a LIMIT flag when reached. Tests isolate XDG_STATE_HOME so the persisted mirror never touches the real state dir.
Problem
The Codex backend pushes a
codex.rate_limitssnapshot on responses whether or not a limit is reached, but the proxy only acts onlimit_reached: true(correctly turning it into a stream error) and discards every other snapshot. So there's no way to ask the proxy "how much quota do I have left" before starting a long run; the community workaround is the unofficialchatgpt.com/backend-api/wham/usageside channel with hand-extracted OAuth tokens.Change
Single concern: capture the latest snapshot and expose it.
providers/codex/rate_limits.rs: a process-wide latest-snapshot store. Therate_limitsobject is stored and served verbatim (plus acaptured_atstamp) because the upstream payload shape is undocumented; re-modeling it field by field would break on upstream changes.translate_live_stream_payload) and the buffered reducer. Existinglimit_reached: trueerror behavior is untouched.GET /usage:{"codex": {"rate_limits": {...}, "captured_at": "..."}},{"codex": null}until a request has produced a snapshot.Testing
cargo testall green;cargo fmt --all --checkclean;cargo clippy --all-targets -- -D warningspre-existing error count unchanged (34), none in touched filesPossible follow-ups (kept out of this PR deliberately)