feat(codex): add subscription usage provider#6
Conversation
Reuse the Codex CLI's ~/.codex/auth.json OAuth login and poll the private chatgpt.com/backend-api/wham/usage endpoint, rendering Codex's session and weekly windows next to Claude's in the popover. core: - codex provider: lossy wham/usage parser (primary/secondary windows classified by length, additive model-specific limits), JWT-claims account identity, ChatGPT-Account-Id header, 401/429 handling. - generalize the Claude token refresher into a shared providers::oauth::OAuthRefresher (configurable endpoint/client/cache/scope) used by both Claude and Codex; add OAuthTokens.account_id. - register Codex in the source catalog. adapters: - CodexCredentials (auth.json reader + metadata-only presence; expiry from the access token's JWT exp), codex_strategy wiring, LocalSourceProbe entry, and a [ignore]-style codex_live example. app: - generic fetch_usage(id) command + codex in fetch_for; the usage-error event now carries the provider id so a failure is siloed to its own tile. ui: - per-provider usage/error state: each provider shows only its own windows, keeps last-known values with a stale banner on a failed fetch, and never renders another provider's data. Existing providers are unchanged. Tests use recorded fixtures + fakes (no live accounts, no Keychain prompts); the live check is the [ignore]d codex_live example. Completes task 005.
Codex logins aren't only in ~/.codex/auth.json — when Codex is driven through Oh My Pi, each profile keeps its own OAuth token in a per-profile SQLite store (~/.omp[/profiles/*]/agent/agent.db, provider 'openai-codex'). Single-path discovery missed all of them and only ever found the (often stale) CLI file. Discover both stores, dedupe by ChatGPT account id (freshest token wins), and surface each distinct account as its own connectable source. core: - sources: SourceDescriptor owns its strings; add CodexAccount + codex_account_descriptor + codex_source_id; discover_sources/active_sources take the discovered accounts and treat them as present-by-discovery (no file probe), seeding each account's email from discovery. - codex: account_cache_key(account_id) namespaces each account's refreshed-token cache (oauth.codex.<id>) so two logins never collide; the catalog and the strategy derive the key from one place. adapters: - codex: enumerate the Codex CLI auth.json + every Oh My Pi profile DB (read-only rusqlite), dedupe by account id, and resolve per-account credentials (freshest token, re-read each load so MLT always uses Oh My Pi's latest token). Never writes either store. app: - per-account routing: fetch_for handles codex:<id>; descriptor_for / discover_all / active_all thread the discovered accounts through every command and the refresh loop; disconnect purges only the targeted account's namespaced token. ui: - per-account tiles: reportsUsage covers codex:<id>; the switcher disambiguates the otherwise identical \"Codex\" tabs by account email; Codex gets its own icon. Each account's usage, identity, and stale/error state stay siloed by its unique source id. Verified live against two real logins (a personal Plus and a Team account) through the [ignore]-style codex_live example: both discovered, both fetch their own windows. Unit tests cover parsing, dedup, dynamic discovery, and per-account disconnect; no live accounts in tests.
…he same multi-account treatment
The multi-account model built for Codex was Codex-specific. Generalize it into one shared
mechanism and apply it to Claude Code, so every reused-login (OAuth-subscription) provider gets
the same treatment — and a new one plugs in with a registry row plus a strategy, no bespoke code.
core:
- CodexAccount -> DiscoveredAccount { base, account_id, email, origin }; codex_source_id /
codex_account_descriptor -> account_source_id / account_cache_key / account_descriptor, driven
by an ACCOUNT_PROVIDERS registry (codex, claude-code). discover_sources/active_sources expand
any provider's accounts uniformly.
- claude: the usage scope guard now fires only when scopes are *known and insufficient*; an
Oh My Pi credential omits scopes, so such a token is trusted and the endpoint is the authority.
adapters:
- new accounts.rs: the provider-agnostic Oh My Pi reader (one PROVIDERS table maps base ->
Oh My Pi provider id), dedup, and AccountCredentials shared by every provider. codex.rs keeps
only its CLI auth.json reader + strategy; claude.rs adds claude_account_strategy. anthropic
OAuth logins are read the same way Codex's are.
app + ui:
- descriptor_for / fetch_for route any <base>:<account_id> (codex:, claude-code:) to the right
strategy; the front-end recognizes per-account ids and disambiguates same-named tabs by email.
docs:
- ADR 0019 records the pattern; the shared Definition of Done now requires every reused-login
provider to use it (so the next provider is multi-account by default).
Verified live (accounts_live example): 4 accounts discovered — 2 Codex + 2 Claude Code, across
the gmail and bigshotpictures logins — each fetching its own windows through its provider's real
strategy. Tests cover the generic discovery, dedup, per-account disconnect, and the relaxed scope
guard; no live accounts in tests.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (30)
📝 WalkthroughWalkthroughThis PR implements multi-account OAuth credential discovery from local stores (Oh My Pi profiles, Codex CLI), introduces a reusable OAuth token refresh framework, adds complete Codex provider support with usage parsing, and refactors the Tauri backend and SvelteKit frontend to support per-provider usage snapshots with per-account source isolation. ChangesMulti-account OAuth discovery and Codex provider integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Closing this fork-based PR; recreating from the ogrodev-owned branch. |
Summary
Verification
make checkmake tasksmake depsCI=true make qacargo test --workspaceNotes
ogrodev/MLT.Summary by cubic
Adds Codex usage tracking and multi-account discovery, so each Codex and Claude Code login appears as its own source with siloed usage, stale, and error states. Completes task 005 by reusing local OAuth logins from Oh My Pi and vendor CLIs and refreshing tokens safely.
New Features
~/.codex/auth.json, fetchchatgpt.com/backend-api/wham/usage, parse session/weekly windows, and sendChatGPT-Account-Id.agent.db) and vendor stores, dedupe by account id, and expose each account ascodex:<id>/claude-code:<id>.account_source_idandaccount_cache_key, with per-provider usage/errors kept fully siloed in the UI.fetch_usage(id), per-provider error event payloads, switcher labels disambiguated by account email.Refactors
providers::oauth::OAuthRefresherfor reused-login providers; refreshed tokens cached under our keychain, never written back to vendor stores.vitestUI state tests andui-testtomake check; addrusqlitefor read-only Oh My Pi discovery.Written for commit 15da8e1. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
Improvements