ProductDesktopUsage provider APIs

01 — jackin-usage current provider coverage

Inventories current provider probes, credential sources, normalized fields, coverage gaps, and display flow.

Summary

Current probes cover seven providers through one normalized limits pipeline, with provider-specific credential and quota gaps documented below.

Question and scope

For each provider probe in crates/jackin-usage/src/usage/ (codex.rs, claude.rs, amp.rs, grok.rs, zai.rs, minimax.rs, kimi.rs): (1) which HTTP endpoints/URLs it calls for usage/quota; (2) which credential source it uses (location/type only); (3) which fields and UI labels it produces; (4) confirm/refute two suspected gaps — (a) no run-out/projection phrases in jackin-usage, (b) no Amp workspace credit balance; (5) how data flows to display surfaces (HostUsageRuntime, usage_snapshot_store, jackin-usage-ffi DTOs).

This chapter informs jackin-desktop.

Method

Method: codebase read Verification cutoff: 2026-07-24

All paths below are relative to the repository root unless absolute. usage/ means crates/jackin-usage/src/usage/.

Findings

Q1 — Endpoints/URLs per probe

  • Codex (usage/codex.rs) — dual source, RPC preferred over OAuth HTTP (codex_snapshot, usage/codex.rs:122-136):
    • CLI RPC: spawns codex -s read-only -a untrusted app-server (usage/codex.rs:719-720); JSON-RPC methods account/rateLimits/read (usage/codex.rs:771) and account/read (usage/codex.rs:781). (confidence: HIGH)
    • OAuth HTTP: base URL from $CODEX_HOME/config.toml chatgpt_base_url, default https://chatgpt.com/backend-api (usage/codex.rs:1040-1062); usage path /wham/usage (or /api/codex/usage for non-backend-api bases) via resolve_codex_usage_url (usage/codex.rs:1023-1031); reset-credits at {base}/wham/rate-limit-reset-credits via resolve_codex_reset_credits_url (usage/codex.rs:1033-1038). (confidence: HIGH)
    • Token refresh (401/403 retry only, never persisted back): POST https://auth.openai.com/oauth/token with public client id — CODEX_OAUTH_TOKEN_URL / CODEX_OAUTH_CLIENT_ID (crates/jackin-usage/src/usage.rs:1294-1295; refresh flow usage/codex.rs:930-986). (confidence: HIGH)
  • Claude (usage/claude.rs) — GET https://api.anthropic.com/api/oauth/usage with anthropic-beta: oauth-2025-04-20 and a claude-code/<version> User-Agent (endpoint gated to that UA) (usage/claude.rs:825-846; UA resolution 848-873, fallback claude-code/2.1.0 at crates/jackin-usage/src/usage.rs:179). Fallback when OAuth fetch fails but a token exists: CLI claude -p /usage (usage/claude.rs:901-911; command args crates/jackin-usage/src/usage.rs:1522). (confidence: HIGH)
  • Amp (usage/amp.rs) — POST https://ampcode.com/api/internal?userDisplayBalanceInfo with JSON body {"method":"userDisplayBalanceInfo","params":{}} (usage/amp.rs:214-243). Fallback: CLI amp --no-color usage (usage/amp.rs:339-343). (confidence: HIGH)
  • Grok (usage/grok.rs) — dual source (usage/grok.rs:336-353):
    • CLI ACP RPC preferred: spawns ~/.grok/bin/grok agent stdio (or grok on PATH) (usage/grok.rs:355-366, 440-447); JSON-RPC method x.ai/billing (usage/grok.rs:416-423). (confidence: HIGH)
    • Web fallback: POST https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig as gRPC-web+proto with bearer token; response parsed by a hand-rolled protobuf scanner (usage/grok.rs:461-506, 553-600). (confidence: HIGH)
  • Z.AI (usage/zai.rs) — GET {host}/api/monitor/usage/quota/limit, default host https://api.z.ai; overridable via ZAI_QUOTA_URL/Z_AI_QUOTA_URL (full URL) or ZAI_API_HOST/Z_AI_API_HOST (usage/zai.rs:257-290). (confidence: HIGH)
  • MiniMax (usage/minimax.rs) — GET with four-URL fallback: https://api.minimax.io/v1/token_plan/remains, https://api.minimax.io/v1/api/openplatform/coding_plan/remains, then same two paths on https://api.minimaxi.com (usage/minimax.rs:391-394); overrides MINIMAX_REMAINS_URL / MINIMAX_API_HOST / MINIMAX_HOST (usage/minimax.rs:371-374). Sends header MM-API-Source: jackin-capsule (usage/minimax.rs:349). (confidence: HIGH)
  • Kimi (usage/kimi.rs) — GET https://api.kimi.com/coding/v1/usages (usage/kimi.rs:258-267). (confidence: HIGH)

All bearer GETs route through the shared get_json_bearer helper (crates/jackin-usage/src/usage.rs:1451-1479). (confidence: HIGH)

Q2 — Credential sources (locations/types only; no values)

  • Codex: OAuth token file $CODEX_HOME/auth.json (env CODEX_HOME, default ~/.codex/), then container handoff /jackin/codex/auth.json (container_paths::CODEX_AUTH, crates/jackin-core/src/container_paths.rs:77) (usage/codex.rs:14-19, 96-100). auth.json may alternatively carry an OPENAI_API_KEY string (usage/codex.rs:262-274). Env OPENAI_API_KEY is presence-checked for status/origin only (usage/codex.rs:113-121). Account label decoded from the id_token JWT payload (email/preferred_username/name/sub) (usage/codex.rs:321-332). No keychain reader. (confidence: HIGH)
  • Claude: OAuth credential candidates, home-first: $CLAUDE_CONFIG_DIR/.credentials.json, ~/.claude/.credentials.json, ~/.claude.json (metadata only, never token), handoff /jackin/claude/credentials.json (container_paths::CLAUDE_CREDENTIALS, crates/jackin-core/src/container_paths.rs:73) (usage/claude.rs:19-26). Token key claudeAiOauth.accessToken (usage/claude.rs:235-257). Env ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN are presence/origin only — never used for the fetch (usage/claude.rs:63-89, comment 73-75). No keychain reader in the capsule (comment usage/claude.rs:48-49). (confidence: HIGH)
  • Amp: env AMP_API_KEY wins; else API key from ~/.local/share/amp/secrets.json (apiKey@* keys), then handoff /jackin/amp/secrets.json (container_paths::AMP_SECRETS, crates/jackin-core/src/container_paths.rs:79) (usage/amp.rs:15-34, 245-266). (confidence: HIGH)
  • Grok: OAuth bearer from ~/.grok/auth.json, else handoff /jackin/grok/auth.json (container_paths::GROK_AUTH, crates/jackin-core/src/container_paths.rs:83) (usage/grok.rs:21-28); token entries keyed by https://auth.x.ai:: (OIDC) or legacy sign-in scopes, with expiry check (usage/grok.rs:508-551). Env XAI_API_KEY / GROK_DEPLOYMENT_KEY are presence/origin only (usage/grok.rs:29-30, 59-71). (confidence: HIGH)
  • Z.AI: API key only — injected provider_keys map (Desktop set_provider_key), else env Z_AI_API_KEY / ZAI_API_KEY (crates/jackin-usage/src/usage.rs:901-907). No file/keychain source. (confidence: HIGH)
  • MiniMax: env MINIMAX_CODING_API_KEY, else injected provider_keys, else env MINIMAX_API_KEY (crates/jackin-usage/src/usage.rs:918-926). No file source. (confidence: HIGH)
  • Kimi: env KIMI_AUTH_TOKEN/kimi_auth_token, else local OAuth token file ~/.kimi-code/credentials/kimi-code.json or ~/.kimi/credentials/kimi-code.json (expiry-checked access_token), else same file under handoff home /jackin/kimi-code (container_paths::KIMI_CODE_DIR, crates/jackin-core/src/container_paths.rs:38), else injected provider_keys, else env KIMI_CODE_API_KEY (crates/jackin-usage/src/usage.rs:909-916; usage/kimi.rs:269-297). (confidence: HIGH)
  • The host credential matrix (docs/debug, no secret reads) mirrors this at crates/jackin-usage/src/host.rs:1035-1087. (confidence: HIGH)

Q3 — Extracted fields and UI labels

  • Codex: primary/secondary rate-limit windows (used_percent, resets_at, window duration) → buckets "Session" (StatusSlot::Session) and "Weekly" (StatusSlot::Weekly) (usage/codex.rs:567-584); per-limit-id extra limits (everything except limit id codex) → "<label> 5-hour" / "<label> Weekly" detail rows — the Codex-Spark entry yields "Codex Spark 5-hour"/"Codex Spark Weekly" (usage/codex.rs:446-448, 521-542, 586-609; fallback placeholders 163-204); "Limit Reset Credits" bucket with "N manual resets available · Next expires …" detail (usage/codex.rs:610-622, 649-675); "Credits" bucket with balance/unlimited (usage/codex.rs:624-637); plan label mapped following CodexBar — pro → "Pro 20x", pro-lite variants → "Pro 5x" (usage/codex.rs:36-76); account label from RPC email or JWT email (usage/codex.rs:501-511). (confidence: HIGH)
  • Claude: authoritative limits[] entries (kind session/weekly_all/weekly_scoped, percent, severity, resets_at, scope.model.display_name) → "Session", "All models" (Weekly slot), and per-model weekly windows labeled by model display name (Fable etc., no per-model code) (usage/claude.rs:264-307, 361-380, 548-594); legacy named windows backfill → "Session", "Weekly", "Sonnet", "Opus", "Daily Routines" (usage/claude.rs:596-689); fallback placeholder buckets "Session"/"Weekly"/"Daily Routines" (usage/claude.rs:110-138); money-cap surfaces: "Extra usage" spend bucket (StatusSlot::Spend, Money-typed used/limit, disabled-reason honesty) from spend{} or legacy extra_usage (usage/claude.rs:309-336, 382-399, 740-823) and rotating-codename dollar-budget windows (e.g. amber_ladder) → title-cased dollar buckets with used/limit money (usage/claude.rs:691-736); plan label from oauthAccount.organizationType else subscriptionType/rateLimitTier (usage/claude.rs:140-147, 210-257); CLI fallback parses "Current session:", "Current week (all models):", "Current week (Sonnet only):", and generic "Current week (<model>):" lines (crates/jackin-usage/src/usage.rs:1535-1566), marked UsageConfidence::Estimated (usage/claude.rs:160-171). (confidence: HIGH)
  • Amp: free_remaining/free_limit/hourly_replenishment/individual_credits (+account email) (usage/amp.rs:129-165) → buckets "Amp Free" (currency used/limit, remaining %, replenishment-derived "Resets in …" label) and "Individual credits" (usage/amp.rs:177-211, 278-337); plan label fixed "Amp Free" when data present (usage/amp.rs:99); CLI fallback parses "Signed in as", "Amp Free:", "Individual credits:" lines (usage/amp.rs:345-372). (confidence: HIGH)
  • Grok: RPC billing — monthlyLimit, onDemandCap, on_demand_enabled, usage includedUsed/onDemandUsed/totalUsed (cents), billing cycle start/end (usage/grok.rs:129-163) → headline bucket labeled "Weekly"/"Monthly"/"Credits" by cycle length (Weekly slot; Grok has no session window), plus "Included usage" and "On-demand usage" detail rows (usage/grok.rs:223-334); web fallback extracts only used_percent + reset epoch from protobuf (usage/grok.rs:171-221); plan label auth_mode oidc → "SuperGrok" (usage/grok.rs:793-802); account from email/user_id/team_id keys (usage/grok.rs:786-791). (confidence: HIGH)
  • Z.AI: limits[] with type TOKENS_LIMIT/TIME_LIMIT, unit/number windows, usage/currentValue/remaining/percentage, nextResetTime (ms) (usage/zai.rs:83-117) → buckets "5-hour" (Session slot, shortest token window), "Tokens" (Weekly slot, longest), "MCP" (time limit, count detail line) (usage/zai.rs:119-161); plan label from planName/aliases (usage/zai.rs:92-102, 163-170). (confidence: HIGH)
  • MiniMax: per-model model_remains[] interval + weekly counts/percents/status/end-times (usage/minimax.rs:116-144) → buckets "General · 5h" (Session slot) and "General · Weekly" (Weekly slot) for the general model; other models render as "<Model>" / "<Model> · Weekly" detail rows (usage/minimax.rs:166-199, 283-316); "Usage: X / Y" count detail (usage/minimax.rs:318-333); plan label from subscribe/plan/combo titles (usage/minimax.rs:201-217). (confidence: HIGH)
  • Kimi: usage/usages[] (scope FEATURE_CODING preferred) detail limit/used/remaining/resetTime + limits[] rate windows (usage/kimi.rs:88-125) → buckets "Rate Limit" (Session slot, window-paced) and "Weekly" (Weekly slot) (usage/kimi.rs:127-160); fallback placeholders "Weekly" and "5-hour rate limit" (usage/kimi.rs:29-52); no plan label (usage/kimi.rs:60). (confidence: HIGH)
  • Pace vocabulary (where emitted): "On pace" / "N% in reserve" / "N% in deficit" (quota_pace_label, usage/format.rs:164-191) — reserve/deficit against elapsed window, not a run-out projection. Grok buckets pass None for pace on both web and RPC branches (usage/grok.rs:215, 259 — verified 2026-07-24), even though the RPC path has period bounds. (confidence: HIGH)

Q4 — Confirmed gaps

  • (a) No run-out/projection phrases — CONFIRMED. Method: grep -rniE "runs out|projected empty|lasts until|run-out|projection" over crates/jackin-usage and crates/jackin-usage-ffi returned zero matches. The only forward-time strings produced are reset countdowns ("Resets in …", usage/amp.rs:332-336; compact_duration_label usage/format.rs:193-208), expiry labels (usage/format.rs:146-155), and the pace reserve/deficit tokens (usage/format.rs:184-190). (confidence: HIGH)
    • Adjacent signal outside the crate: the Swift Desktop shell can split a pace label of the form "On pace · Runs out in 4d 21h" (native/Sources/JackinDesktop/UsageWindow/ProviderCardView.swift:209 comment cites CodexBar's dual-column pace; tests native/Tests/JackinUsageBridgeTests/ArchitectureTests.swift:368-369 and native/Tools/StatusItemChipHarness/main.swift:396-397 use that exact fixture), but no Rust producer emits such a phrase — the display splitter is fed only the vocabulary above. (confidence: HIGH)
  • (b) No Amp workspace credit balance — CONFIRMED. AmpApiUsage.individual_credits (usage/amp.rs:135) is the only credit-balance field; the JSON parse looks for individualCredits/individualBalance only (usage/amp.rs:158-159), the CLI parse only for the "Individual credits:" line (usage/amp.rs:367-369). Method: grep -n "workspace" in usage/amp.rs → zero matches; no team/workspace key is requested or parsed, and the request asks only userDisplayBalanceInfo (usage/amp.rs:222-229). (confidence: HIGH)

Q5 — Data flow to display surfaces

  • Probe dispatch: build_snapshot selects the per-surface probe and resolves keys (crates/jackin-usage/src/usage.rs:887-931); each probe returns a FocusedUsageView via the shared usage_view builder.
  • Cache + persistence (capsule and host share one path): UsageCache::refresh_active_account_snapshots (crates/jackin-usage/src/usage.rs:434) runs due probes, then persists all refreshed views with usage_snapshot_store::store_usage_snapshots (crates/jackin-usage/src/usage.rs:536-538) and materializes accounts JSON (crates/jackin-usage/src/usage.rs:543-545, 630-634). Readers call UsageCache::focused_snapshot (crates/jackin-usage/src/usage.rs:394).
  • Durable store: store_usage_snapshots flattens each view into per-bucket StoredAccountUsageSnapshot rows (crates/jackin-usage/src/usage_snapshot_store.rs:80-90, row shape 47-73, SCHEMA_VERSION "4" at line 24). Capsule store lives under /jackin/state/ (module header, crates/jackin-usage/src/usage_snapshot_store.rs:4-9); host store at <data_dir>/usage-menu-bar/snapshots.db (crates/jackin-usage/src/host.rs:31, 233-235).
  • HostUsageRuntime (crates/jackin-usage/src/host.rs:274-290): open wires the store/accounts paths into the cache (host.rs:312-320); refresh drives cache.refresh_active_account_snapshots under a ≥60s floor (host.rs:425-465); snapshot returns the live focused view or a selected durable account view (host.rs:493-515); overview_rows builds Desktop popover/Usage-window rows (percent headline, reset countdown + exact clock, status word, worst severity) (host.rs:783-831); compact menu-bar labels pick the min-remaining bucket (host.rs:682-761, 988-1003). Provider keys for Z.AI/MiniMax/Kimi are injected credential-free via set_provider_key (host.rs:393-399).
  • FFI layer (crates/jackin-usage-ffi): bridge.rs wraps HostUsageRuntime with a coarse sync API — snapshot maps through view_dto (crates/jackin-usage-ffi/src/bridge.rs:95-100), overview_rows through overview_row_dto (bridge.rs:190-194). DTOs mirror the protocol views 1:1: UsageViewDto (crates/jackin-usage-ffi/src/dto.rs:62-80), QuotaBucketDto incl. used_money/limit_money Money mirrors and status_slot session/weekly/spend strings (dto.rs:44-58, 234-261), OverviewRowDto (dto.rs:93-101), AccountDescriptorDto (dto.rs:105-113). (all confidence: HIGH)
  • Settled-ground note (location only, not evaluated): monetary data in the pipeline is exclusively provider-supplied caps/balances (Claude "Extra usage" + dollar-budget windows, Grok billing cents, Amp dollars, Codex credits). A static USD-per-1M-token pricing table and per-session cost estimation exist in crates/jackin-usage/src/token_monitor/pricing.rs:1-34 and crates/jackin-usage/src/token_monitor.rs:180-187 (token-monitor internals, not part of the seven probes above). (confidence: HIGH)

Current provider gaps and confirmations

  • Grok current config fields dropped: official x.ai/billing carries config.prepaidBalance, onDemandCap, and onDemandUsed; jackin obsolete top-level decoder misses them. Extra Usage Credits and on-demand cap/used are quota bounds; history/productUsage remain excluded. (confidence: HIGH)
  • MiniMax documented host absent: jackin URL fan-out covers only api.minimax.io / api.minimaxi.com (usage/minimax.rs:391-394); the officially documented host www.minimax.io (chapter 06) is not probed. (confidence: HIGH)
  • Codex OAuth client-id parity OK: jackin CODEX_OAUTH_CLIENT_ID = app_EMoamEEZ73f0CkXaXp7hrann (crates/jackin-usage/src/usage.rs:1295) matches the official CLI's client id (chapter 02). No stale-constant risk. (confidence: HIGH)

Dead ends and contradictions

  • No probe has a macOS Keychain credential reader; comments state the origin always names a file (usage/claude.rs:48-49). Ruled out.
  • Claude env keys (ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN) are not used to fetch usage; presence/origin only (usage/claude.rs:73-89). Same for Codex OPENAI_API_KEY env and Grok XAI_API_KEY/GROK_DEPLOYMENT_KEY env (usage/codex.rs:113-121; usage/grok.rs:29-30, 59-71).
  • Telemetry route-template vs actual URL mismatch for Codex reset credits: the telemetry label says /backend-api/wham/usage/reset_credits (usage/codex.rs:1011) while the real request path is {base}/wham/rate-limit-reset-credits (usage/codex.rs:1033-1038). Label-only discrepancy; the HTTP call uses the latter.
  • Credential-origin label vs actual source for MiniMax/Z.AI: MiniMax always reports "API token · env MINIMAX_API_KEY" (usage/minimax.rs:49-56) and Z.AI "API token · env ZAI_API_KEY" (usage/zai.rs:58-62) even when the key came from MINIMAX_CODING_API_KEY, Z_AI_API_KEY, or the injected provider_keys map (crates/jackin-usage/src/usage.rs:901-926). Cosmetic origin inaccuracy, not a data-flow issue.
  • Codex RPC reset credits carry only availableCount (usage/codex.rs:464-467, 543-548); expiry detail ("Next expires …") is available only on the OAuth HTTP path (usage/codex.rs:988-1021, 649-675) — the two sources are not equivalent for that bucket.
  • No embedded instructions were encountered in any file read; comments referencing CodexBar/OpenUsage are provenance notes, not directives.

Implications for jackin

Keep one normalized limits pipeline, but correct provider-specific credential, freshness, reset-credit, and account-balance gaps before Desktop treats every probe as equivalent.

Limitations and unknowns

  • Whether the Amp userDisplayBalanceInfo endpoint could return a workspace/team balance field that the parser silently drops (the parser whitelists keys, so any such field would be ignored) — determining this requires observing a live response from a workspace-attached Amp account, not the codebase. (LOW lead)
  • Whether seven_day_oauth_apps (explicitly ignored, usage/claude.rs:268-271) carries limit data that would matter for Desktop; code states it is not a CodexBar quota window, but its live payload shape was not verified here. (LOW lead)
  • The Grok web-billing protobuf field mapping (path [.., 1] fixed32 = used percent, epoch varints = reset) is heuristic by construction (usage/grok.rs:553-600); which proto schema it actually corresponds to is unverified against any xAI IDL. (MED — code read is primary, wire schema is not)
  • OpenCode has a probe surface (opencode_snapshot, crates/jackin-usage/src/usage.rs:928) outside this chapter's seven-file scope; its endpoint/credential story is not covered here.

Sources

On this page