原始内容
quota-axi
Your agent needs to be aware of your quota
Quota CLI for agents - designed with AXI (Agent eXperience Interface).
Agents need quota state before they choose where work can safely run. Vendor dashboards are not shaped for shell automation, and local CLIs expose different windows, resets, and auth sources.
quota-axi reports local Claude, Codex, Cursor, GitHub Copilot, Grok, and Kimi quota windows in one AXI-shaped call. It is data only: it never routes, recommends, proxies, intercepts, logs in, imports browser cookies, or mutates provider state.
- Official sources - quota-axi reads local provider auth sources and calls the first-party quota, usage, billing, or entitlement endpoints used by the local agents, with a read-only Codex app-server probe as fallback.
- Local first - quota and auth reports run on the machine that holds the credentials; their network calls go to first-party provider endpoints, never a third-party relay.
The separate
updatecommand contacts npm only when the user runs it. - Token efficient - default stdout is compact TOON so agents spend fewer tokens parsing quota state, with
--jsonavailable when a caller needs the normalized model.
Quick Start
macOS + Claude note: Claude Code keeps its live token in the macOS Keychain.
quota-axi pins that lookup to the same current-user Keychain item Claude Code selects and will not read its value unless the user grants permission, so Claude quota reads can stay stale when no usable on-disk access token is available.
Run quota-axi --allow-keychain-prompt once and approve Keychain access with "Always Allow".
After a successful Keychain read, future non-interactive quota reads use that profile-and-account-scoped grant and refresh live Claude data without requiring the flag.
Legacy markers created before account-pinned lookup are not reused, so an upgrade may require this one-time grant again.
$ npx -y quota-axi
bin: ~/.npm/_npx/.../quota-axi
description: Report local agent-provider quota windows for routing-aware agents
generatedAt: "2026-03-15T16:42:00.000Z"
providers[6]{provider,plan,source,status,refreshedAt}:
claude,pro,oauth,fresh,"2026-03-15T16:41:55.000Z"
codex,plus,cli-rpc,fresh,"2026-03-15T16:41:58.000Z"
cursor,pro,api,fresh,"2026-03-15T16:41:59.000Z"
copilot,individual,api,fresh,"2026-03-15T16:42:00.000Z"
grok,unknown,web,fresh,"2026-03-15T16:42:00.000Z"
kimi,unknown,api,fresh,"2026-03-15T16:42:00.000Z"
windows[15]{provider,id,label,percentRemaining,resetsAt,pace,reserve,state}:
claude,five_hour,session,82,"2026-03-15T20:10:48.000Z",behind,12.4,fresh
claude,seven_day,week,64,"2026-03-20T17:59:45.600Z",ahead,-8.2,fresh
claude,seven_day_opus,opus week,93,"2026-03-20T17:29:31.200Z",behind,21.1,fresh
claude,"model:fable",Fable week,71,"2026-03-20T08:25:12.000Z",behind,4.5,fresh
codex,five_hour,session,58,"2026-03-15T19:36:54.000Z",on_pace,-0.3,fresh
codex,weekly,week,47,"2026-03-19T09:54:28.800Z",ahead,-6.1,fresh
codex,"model:gpt-5.1-codex:5h",GPT-5.1-Codex session,100,"2026-03-15T20:48:00.000Z",behind,18.0,fresh
cursor,included_usage,included usage,72,"2026-04-01T00:00:00.000Z",unknown,unknown,fresh
cursor,auto_usage,auto usage,91,"2026-04-01T00:00:00.000Z",unknown,unknown,fresh
cursor,api_usage,API usage,100,"2026-04-01T00:00:00.000Z",unknown,unknown,fresh
copilot,chat,chat,84,"2026-04-01T00:00:00.000Z",unknown,unknown,fresh
copilot,premium_interactions,premium interactions,53,"2026-04-01T00:00:00.000Z",unknown,unknown,fresh
grok,credits,credits,67,"2026-04-01T00:00:00.000Z",behind,3.6,fresh
kimi,weekly,week,74,"2026-03-20T12:17:02.400Z",behind,5.2,fresh
kimi,five_hour,session,88,"2026-03-15T20:45:00.000Z",behind,7.0,fresh
effective[9]{provider,scope,effectivePercentRemaining,boundedBy,limitingWindowIds,pace,aheadWindows,unknownPace,worstReserve,unresolvedWindowIds,relationshipStatus}:
claude,all_models,64,"five_hour + seven_day",seven_day,mixed,seven_day,none,-8.2,none,known
claude,"model:fable",64,"five_hour + seven_day + model:fable",seven_day,mixed,seven_day,none,-8.2,none,known
claude,seven_day_opus,64,"five_hour + seven_day + seven_day_opus",seven_day,mixed,seven_day,none,-8.2,none,known
codex,all_models,47,"five_hour + weekly",weekly,ahead,weekly,none,-6.1,none,known
codex,"model:gpt-5.1-codex",47,"five_hour + weekly + model:gpt-5.1-codex:5h",weekly,mixed,weekly,none,-6.1,none,known
cursor,unresolved,unknown,none,unknown,unknown,none,none,unknown,"included_usage + auto_usage + api_usage",unknown
copilot,unresolved,unknown,none,unknown,unknown,none,none,unknown,"chat + premium_interactions",unknown
grok,all_products,67,credits,credits,behind,none,none,3.6,none,known
kimi,all_models,74,"weekly + five_hour",weekly,behind,none,none,5.2,none,known
help[3]:
Run `quota-axi --provider claude --json` for JSON output
Run `quota-axi --full` to include account and source-attempt details
Run `quota-axi auth` to inspect local auth source availability without printing secrets
--json emits the same normalized model as structured JSON instead of TOON:
$ quota-axi --provider claude --json
{
"generatedAt": "2026-03-15T16:42:00.000Z",
"schemaVersion": 3,
"providers": [
{
"provider": "claude",
"label": "Claude",
"source": "oauth",
"plan": "pro",
"windows": [
{
"id": "five_hour",
"label": "session",
"kind": "session",
"percentUsed": 18,
"percentRemaining": 82,
"resetsAt": "2026-03-15T20:10:48.000Z",
"windowSeconds": 18000,
"pace": {
"status": "behind",
"timeRemainingPercent": 69.6,
"elapsedPercent": 30.4,
"reservePercentPoints": 12.4,
"burnMultiple": 0.5921,
"projectedExhaustedAt": "2026-03-15T23:37:28.000Z",
"projectionConfidence": "established",
"projectionBasis": "cycle_average",
"cycleBasis": "window_seconds",
"cycleSeconds": 18000
}
},
{
"id": "seven_day",
"label": "week",
"kind": "weekly",
"percentUsed": 36,
"percentRemaining": 64,
"resetsAt": "2026-03-20T17:59:45.600Z",
"windowSeconds": 604800,
"pace": {
"status": "ahead",
"timeRemainingPercent": 72.2,
"elapsedPercent": 27.8,
"reservePercentPoints": -8.2,
"burnMultiple": 1.295,
"projectedExhaustedAt": "2026-03-19T03:43:45.600Z",
"projectionConfidence": "established",
"projectionBasis": "cycle_average",
"cycleBasis": "window_seconds",
"cycleSeconds": 604800
}
},
{
"id": "model:fable",
"label": "Fable week",
"kind": "model",
"percentUsed": 29,
"percentRemaining": 71,
"resetsAt": "2026-03-20T08:25:12.000Z",
"windowSeconds": 604800,
"pace": {
"status": "behind",
"timeRemainingPercent": 66.5,
"elapsedPercent": 33.5,
"reservePercentPoints": 4.5,
"burnMultiple": 0.8657,
"projectedExhaustedAt": "2026-03-21T10:29:20.275Z",
"projectionConfidence": "established",
"projectionBasis": "cycle_average",
"cycleBasis": "window_seconds",
"cycleSeconds": 604800
}
}
],
"quotaSemantics": {
"status": "known",
"description": "Claude account windows bound every model. A model-specific window is an additional bound, so that model's effective remaining percentage is the minimum across the named windows.",
"effectiveAvailability": [
{
"scope": "all_models",
"status": "known",
"effectivePercentRemaining": 64,
"boundedBy": ["five_hour", "seven_day"],
"limitingWindowIds": ["seven_day"],
"pace": {
"status": "mixed",
"aheadWindowIds": ["seven_day"],
"behindWindowIds": ["five_hour"],
"worstReservePercentPoints": -8.2,
"worstReserveWindowId": "seven_day"
}
},
{
"scope": "model:fable",
"status": "known",
"effectivePercentRemaining": 64,
"boundedBy": ["five_hour", "seven_day", "model:fable"],
"limitingWindowIds": ["seven_day"],
"pace": {
"status": "mixed",
"aheadWindowIds": ["seven_day"],
"behindWindowIds": ["five_hour", "model:fable"],
"worstReservePercentPoints": -8.2,
"worstReserveWindowId": "seven_day"
}
}
]
},
"state": {
"status": "fresh",
"stale": false,
"sourcesTried": ["oauth", "oauth-profile"],
"refreshedAt": "2026-03-15T16:41:55.000Z"
}
}
]
}
$ quota-axi auth
bin: ~/.npm/_npx/.../quota-axi
description: Inspect local quota auth sources without printing secret values
auth[9]{provider,source,path,status,error}:
claude,oauth-file,~/.claude/.credentials.json,available,none
claude,keychain,none,skipped,keychain_prompt_required
codex,auth-json,~/.codex/auth.json,available,none
codex,cli-rpc,~/.local/bin/codex,available,none
cursor,state-vscdb,~/Library/Application Support/Cursor/User/globalStorage/state.vscdb,available,none
copilot,apps-json,~/.config/github-copilot/apps.json,available,none
grok,auth-json,~/.grok/auth.json,available,none
kimi,pi:kimi-coding,none,available,none
kimi,kimi-code-cli,none,available,none
help[1]:
Run `quota-axi --allow-keychain-prompt auth` to permit macOS Keychain access
Install
quota-axi requires Node.js 22.19 or newer.
Agent skill (recommended)
Install the skill in the Agent Skills format with npx skills:
npx skills add kunchenguid/quota-axi --skill quota-axi -g
The skill teaches your agent to run quota-axi through npx -y quota-axi on demand, so nothing needs to be installed ahead of time.
-g installs the skill for all projects (e.g. ~/.claude/skills/); drop it to install for the current project only (.claude/skills/).
Direct use
npx -y quota-axi
npm
npm install -g quota-axi
From source
git clone https://github.com/kunchenguid/quota-axi.git
cd quota-axi
pnpm install
pnpm run build
pnpm run dev
Agent Skill
The npm package includes skills/quota-axi/SKILL.md, the same installable skill recommended above.
It is generated from src/skill.ts; update it with pnpm run build:skill and verify it with pnpm run build:skill -- --check.
How It Works
┌────────────┐
│ quota-axi │
└─────┬──────┘
▼
┌───────────────┐
│ provider │
│ adapters │
└─────┬─────────┘
▼
┌───────────────┐ ┌──────────────┐
│ local auth │ ───▶ │ first-party │
│ sources │ │ provider APIs│
└─────┬─────────┘ └──────┬───────┘
▼ ▼
┌───────────────┐ ┌──────────────┐
│ read-only │ ───▶ │ normalized │
│ fallbacks │ │ quota model │
└─────┬─────────┘ └──────┬───────┘
▼ ▼
┌───────────────┐ ┌──────────────┐
│ stale cache │ ◀─── │ TOON or JSON │
└───────────────┘ └──────────────┘
- Live first - direct provider HTTP calls use 15 second request timeouts, Codex JSON-RPC reads use short per-call timeouts, and stale cache fallback is per provider.
- No first-run Keychain prompt - macOS Claude Keychain value reads are skipped on plain calls until
--allow-keychain-promptsucceeds once, then future plain calls reuse that existing grant. - Partial success is success - one provider can fail while another returns fresh or stale data, and the process still exits 0. Exit code 1 means every provider failed, and 2 means a usage error.
- No token equivalence - quota-axi does not claim that one provider percentage equals another provider percentage.
CLI Reference
| Command | Description |
|---|---|
quota-axi |
Report supported local quota windows |
auth |
Report local auth-source availability, no values |
update |
Upgrade quota-axi to the latest published version |
update --check |
Report current vs. latest without installing |
Flags
| Flag | Description |
|---|---|
--provider claude,codex,cursor,copilot,grok,kimi |
Scope providers |
--json |
Emit normalized JSON instead of TOON for quota or auth |
--full |
Include quota account identity and source attempts |
--allow-keychain-prompt |
Permit macOS Claude Keychain access that could prompt |
-h, --help |
Print terse AXI help |
-v, -V, --version |
Print version |
Output Model
--json emits schemaVersion: 3.
Quota report shape
| Object | Fields |
|---|---|
| Quota report | providers |
| Provider report | provider, label, source, windows, quotaSemantics, state, optional plan, and optional credits |
Provider report with --full |
Optional account identity and per-source attempts |
Account identity (--full) |
Optional email, organization, accountId, and identityStatus |
Account identity and per-source attempts are omitted unless --full is passed.
Claude identityStatus is verified only when Anthropic returns an authoritative account identifier; email and organization are display-only and must not be used for duplicate detection.
Provider state
| Field | Description |
|---|---|
status |
Provider status |
stale |
Whether the provider report is stale |
sourcesTried |
Sources tried for the provider |
refreshedAt |
Optional refresh timestamp |
error |
Optional error |
retryAfter |
Optional retry-after state |
reason |
Optional reason |
remedyCommand |
Optional remedy command |
untrustedWindowIds |
Optional identifiers for limits that could not be parsed authoritatively |
authStatus |
Optional machine-readable local auth usability: usable, expired_refreshable, or unusable. Distinct from quota freshness and from human error prose. |
When stale or unavailable quota is likely fixable by a one-time macOS Keychain grant, state.reason is keychain_access_required, state.remedyCommand is quota-axi --allow-keychain-prompt, and JSON includes an agent-directed help entry.
When every applicable Grok auth source is missing a usable access token but at least one still has a valid literal refresh token, state.authStatus is expired_refreshable and state.status is unavailable (not auth_required). If Grok CLI OIDC is refreshable, state.error is Grok access token expired, state.reason is credentials_expired, state.remedyCommand is grok, and JSON includes an agent-directed help entry telling the user to open the Grok CLI once. If only Pi xai OAuth is refreshable, state.error is Pi xAI access token expired and no Grok CLI remedy is emitted because Grok cannot refresh Pi-owned credentials. Default JSON and compact TOON expose authStatus; source-appropriate advice is included only when a remedy exists. Full output includes attempts[].error: credentials_expired.
True Grok sign-out or definitive remote rejection uses state.authStatus: unusable with state.status: auth_required and state.error: Grok sign-in required (no credentials_expired reason). authStatus: unusable by itself only means that no source established usability; for example, a Pi credential-resolution failure instead has state.status: error. Callers must branch on authStatus, status, and reason, not on human error prose alone, and must not treat expired_refreshable as logged out.
When Pi's xai credential (or a still-valid Grok CLI session) establishes model usability but consumer credit windows cannot be read, state.authStatus is usable, windows stay empty, and state.error is Grok consumer quota unavailable rather than sign-in required.
Claude credential failures without a usable access token preserve the precise credentials_missing or credentials_invalid error. A usage response with HTTP 401/403 reports Claude sign-in required. These definitive failures return no windows and retire the Claude cache instead of masking current authentication state with stale quota.
Quota windows
| Field set | Fields |
|---|---|
| Required | id, label, kind |
| Optional | Percentages, startsAt, reset fields, windowSeconds, credit-spend fields, and derived pace |
Do not interpret a model window's percentage in isolation. quotaSemantics.effectiveAvailability reports the effective percentage for each understood scope, the complete boundedBy window set used to compute it, and the currently limiting window IDs. all_models applies to any model without a more specific scope; a matching model:* scope includes both account and model-specific bounds. Grok uses the analogous all_products and product:* scopes.
A model-specific scope names the model window or the shared model prefix when multiple period windows describe one Codex model.
quotaSemantics.status is known only when quota-axi understands the relationships needed for the reported scopes. A non-definitive availability entry omits effectivePercentRemaining. Unfamiliar vendor windows produce partial or unknown semantics and are named in unresolvedWindowIds; an empty provider report is unknown without inventing an unresolved window.
For every stale provider report, raw windows remain available for diagnostics but effective availability is always unknown and omits effectivePercentRemaining and limitingWindowIds. Window pace is unknown with reason stale, and each effective pace summary is also unknown. Routing agents must not treat a stale raw percentage as current headroom.
Pace signals
Each window may include a derived pace object that compares cumulative usage to elapsed cycle time using the response generatedAt clock:
timeRemainingPercent = 100 * (resetsAt - generatedAt) / cycleDuration
reservePercentPoints = percentRemaining - timeRemainingPercent
reservePercentPoints |
Meaning |
|---|---|
| Negative | Usage is ahead of the reset clock (burning faster than linear); conserve |
| Positive | Usage is behind the reset clock |
| Within ±1.0 | on_pace deadband for API rounding noise |
| Pace field | Meaning |
|---|---|
status |
ahead, on_pace, behind, or unknown |
reason |
Why pace is unknown (stale, missing_usage, missing_cycle, invalid_cycle, future_cycle_start, expired_reset, unsupported_period) |
timeRemainingPercent / elapsedPercent |
Cycle progress from generatedAt |
reservePercentPoints |
Signed residual capacity vs the linear clock |
burnMultiple |
percentUsed / elapsedPercent when elapsed > 0 |
projectedExhaustedAt |
Linear cycle-average exhaustion timestamp when defined |
projectionConfidence |
early when elapsed < 10% of the cycle; otherwise established |
projectionBasis |
Currently always cycle_average |
cycleBasis |
starts_at_resets_at when both boundaries are trusted; otherwise window_seconds with resetsAt |
cycleSeconds |
Trusted cycle duration used for the math |
Pace is calculated only from trusted cycle evidence:
- Prefer provider-reported
startsAt+resetsAt(Grok current period). - Otherwise use provider-owned
windowSecondswithresetsAt(Codex durations; Claude fixed 5h/7d; Kimi fixed 5h/weekly). - Do not infer monthly, rolling, or unlabeled periods.
Default TOON keeps token cost low: window rows expose pace status and signed reserve only. Full projection evidence is in --json. Pace is recomputed on every report from generatedAt and is not written to the quota cache.
Each effectiveAvailability entry also carries a compact pace summary over every bounding window for that scope (not only the current lowest-remaining limiter): per-status window lists, including aheadWindowIds and unknownWindowIds, plus worstReservePercentPoints / worstReserveWindowId (most negative signed reserve among known-pace windows). Different windows keep their own reset horizons; quota-axi does not invent one synthetic reset for a scope. This is factual inspectable data, never a provider/model routing recommendation.
Quota enums
| Name | Values |
|---|---|
| Provider statuses | fresh, stale, unavailable, auth_required, rate_limited, or error |
| Provider sources | oauth, cli-rpc, api, web, cache, or unavailable |
| Current provider adapter sources | oauth, cli-rpc, api, web, cache, and unavailable |
| Window kinds | session, weekly, monthly, model, credits, or unknown |
| Window pace statuses | ahead, on_pace, behind, or unknown |
| Effective pace statuses | ahead, on_pace, behind, mixed, or unknown |
| Pace projection confidence | early or established |
| Pace cycle basis | starts_at_resets_at or window_seconds |
| Quota relationship statuses | known, partial, or unknown |
| Source attempt statuses | success, failed, or skipped |
Source attempts can include credentialPresent when a non-secret probe confirms a credential item exists.
Provider windows
| Provider | Windows and capabilities |
|---|---|
| Claude | Can report five_hour, seven_day, optional seven_day_opus, and optional extra_usage windows. Trusted session/weekly/model windows emit fixed windowSeconds (18,000 or 604,800) for pace; extra_usage does not invent a monthly duration. |
Claude scoped limits |
When the account's usage response includes a scoped limits list, quota-axi surfaces every active window it describes instead, including model-scoped ones (e.g. Fable) as a model:<slug> window with the same trusted weekly duration. |
| Codex | Identifies exact 18,000-second and 604,800-second periods as five_hour and weekly, regardless of source slot; periods without a duration retain their positional identity. Additional model- or feature-scoped limits use model:<id>:5h / model:<id>:7d, and code-review limits use code_review_five_hour / code_review_weekly. Unfamiliar durations remain honest <hours>h windows instead of being classified as known periods. Duplicate derived IDs are preserved with _2, _3, and later suffixes. Optional credit balance data can also appear. |
| Cursor | Can report included_usage, auto_usage, api_usage, and optional spend_limit windows. Monthly labels alone are not trusted cycle evidence, so pace stays unknown unless a future provider duration appears. |
| GitHub Copilot | Can report quota snapshot windows such as chat, completions, and premium_interactions; when the first-party endpoint exposes entitlement but no numeric quota windows, quota-axi reports a fresh provider state with an empty windows list rather than inventing percentages. Pace stays unknown without trusted cycle boundaries. |
| Grok | With a usable Grok CLI session bearer, can report the shared credits window, optional product-scoped product:<slug> windows, the current-period startsAt and reset, and optional prepaid credit balance from the consumer Usage-page operation. Pi xai auth alone establishes usability but cannot provide these consumer windows. Top-level credits.remaining is prepaid/on-demand balance, distinct from the shared period windows credits percentage used for effective availability. Pace prefers the startsAt/resetsAt pair. |
| Grok proto3 zero | For the exact consumer operation only, an omitted usage float is the official proto3 zero when a valid weekly or monthly current period proves the config is present; quota-axi reports 0 used and 100 remaining rather than deriving usage from money. |
| Kimi | Reports the principal weekly subscription window (with trusted 604,800s duration) plus every valid self-described limit in wire order. Only a limit whose normalized duration is exactly 18,000 seconds is identified as five_hour; future limits remain limit:<index> unknown windows. |
auth --json shape
| Object | Fields |
|---|---|
| Auth report | generatedAt, schemaVersion: 1, and auth |
| Provider auth report | provider and sources |
| Auth source entry | source, optional path, status, and optional error |
Auth source entries can include credentialPresent when a non-secret probe confirms a credential item exists.
| Name | Values |
|---|---|
| Auth source statuses | available, missing, invalid, expired, skipped, or error |
| Auth source names | oauth-file, keychain, auth-json, auth-env, apps-json, state-vscdb, cli-rpc, pi:kimi-coding, pi:xai, and kimi-code-cli |
Security Posture
Provider credential sources
| Provider | Credential sources read |
|---|---|
| Claude | $CLAUDE_CONFIG_DIR/.credentials.json or ~/.claude/.credentials.json; on macOS, the corresponding default or path-hashed Claude Code Keychain value pinned to Claude Code's validated current-user account, with --allow-keychain-prompt or, after a profile-and-account-scoped non-secret access marker exists, on plain calls |
| Codex | $CODEX_HOME/auth.json or ~/.codex/auth.json before the read-only CLI fallback; $QUOTA_AXI_CODEX_BINARY can pin that fallback to an absolute executable path |
| Cursor | $CURSOR_STATE_DB when set or the platform Cursor state database path |
| GitHub Copilot | $GITHUB_COPILOT_APPS_JSON when set or the local Copilot apps auth file |
| Grok | Grok CLI session auth from $GROK_AUTH_JSON, inline $GROK_AUTH, $GROK_AUTH_PATH, or $GROK_HOME/auth.json / ~/.grok/auth.json, plus Pi's independent $PI_CODING_AGENT_DIR/auth.json xai entry (default ~/.pi/agent/auth.json) for OAuth or literal API-key model auth |
| Kimi | Pi's $PI_CODING_AGENT_DIR/auth.json (default ~/.pi/agent/auth.json) for a literal kimi-coding API key first, then a fresh official Kimi Code CLI access token from $KIMI_CODE_HOME/credentials/kimi-code.json (default $HOME/.kimi-code/credentials/kimi-code.json) |
Provider notes
Claude
- quota-axi mirrors Claude Code's Keychain account selector: nonempty
USER, otherwise the operating-system username, validated against Claude Code's safe account pattern with the sameclaude-code-userfallback. Both presence and value reads require that account plus the resolved service. There is no ambiguous service-only fallback. - quota-axi records the non-secret access marker after any successful pinned Keychain value read.
- When that profile-and-account-scoped marker exists, plain calls read the pinned Keychain value again so an already-approved "Always Allow" grant keeps live Claude quota fresh. Legacy service-only markers remain untouched but do not authorize a value read.
- Without the flag or the current marker, quota-axi may perform a non-secret pinned Keychain item presence check so it only suggests Keychain access when the selected Claude credential item exists.
- In
--fulloutput, Claude usage attempts identifyoauth-fileorkeychainas the credential discovery source. They never include the Keychain account. - When an access token exists, local
expiresAtmetadata is advisory. quota-axi sends that token only to Anthropic's existing read-only usage request; success returns fresh quota, while HTTP 401/403 is the definitive authentication result. - Missing or invalid credentials without a usable access token and usage HTTP 401/403 bypass and best-effort retire Claude cache. Timeout, network, rate-limit, server, and response-compatibility failures may use only a formerly fresh Claude snapshot less than seven days old. Reset-expired windows are removed; resetless session, monthly, and credit windows expire after five hours, resetless weekly and model windows expire after seven days, and resetless unknown windows are rejected.
- After a successful usage read, quota-axi queries Anthropic's first-party OAuth profile endpoint with the same credential. Its authoritative root
account.uuidis exposed asaccount.accountIdonly in--fulloutput; if that field is absent,identityStatusisunverifiedinstead of deriving an identity from email, organization data, or cached account metadata.
Codex
- Codex
auth.jsonsupport is OAuth-token only; API key values such asOPENAI_API_KEYare treated as invalid for quota usage calls and are not sent to ChatGPT usage endpoints. - Access-token JWT usability is authoritative for the OAuth bearer probe. An expired
id_tokenalone does not markauth-jsonexpired or skip OAuth; identity-token expiry is diagnostic metadata only. A missing or expiredaccess_tokenstill skips OAuth and preserves the read-only CLI fallback. - It may run
codex -s read-only -a untrusted app-serverfor Codex JSON-RPC fallback. - Set
QUOTA_AXI_CODEX_BINARYto an absolute executable path when the fallback must use a specific Codex installation. Auth inspection and the app-server probe resolve the same path, and an invalid override fails closed instead of consultingPATH.
Cursor
- It uses
sqlite3 -readonlyto readcursorAuthvalues and calls Cursor's first-party dashboard usage endpoint. - If
sqlite3is unavailable, Cursor auth is reported as skipped withsqlite3_unavailable.
GitHub Copilot
- It calls GitHub's first-party Copilot user endpoint.
- It only sends tokens associated with public GitHub hosts to that public endpoint; host-specific GitHub Enterprise tokens are treated as unavailable there.
Grok
- It checks two independent usability sources: Grok CLI session auth and Pi's
xaicredential in$PI_CODING_AGENT_DIR/auth.json(default~/.pi/agent/auth.json). Grok is locally usable when either source is usable, including asymmetric cases where the other source is absent, malformed, stale, or expired. True sign-out requires every applicable source to be unavailable or definitively rejected;authStatus: unusablecan also accompany an indeterminate local credential-resolution failure, but that failure remainsstate.status: errorrather thanauth_required. - Grok CLI session-scoped auth is preferred for the consumer credits probe. It selects session-scoped entries instead of API-key entries and sends a read-only gRPC-web request to Grok's consumer
grok_api_v2.GrokBuildBilling.GetGrokCreditsConfigoperation. Observed Grok CLI OIDC access tokens are short-lived (about six hours on current CLI sessions) while a refresh token remains present for CLI-owned recovery. - Session-scoped Grok auth includes web/session scopes and OIDC records scoped to
auth.x.aiwithauth_modeorauthModeset tooidc, including scope keys with::<client id>suffixes. - Pi
xaiauth follows Pi's auth-file contract:type: "oauth"with literalaccess/ optionalrefresh/expires, ortype: "api_key"with a literalkey. Environment, template, and command references are not resolved. AmbientXAI_API_KEYis not a quota-axi credential source. A locally usable Pi credential establishes model usability even when it cannot expose consumer quota windows; that case isauthStatus: usablewith empty windows, not logged out. Because quota-axi makes no Pi model request, this local classification does not assert that the credential was accepted remotely. - The Grok CLI owns OIDC access-token refresh and rewrites
~/.grok/auth.json; Pi owns refresh of its ownauth.jsonOAuth entries. quota-axi only reads the resulting sessions and never refreshes tokens, launches Grok or Pi, or writes either auth file. Expired-session classification and recovery fields are documented under Providerstate. - Source availability is distinct from remote validity: local expiry gates which bearer is offered to the consumer probe, while HTTP 401/403 and auth-class gRPC codes are definitive rejection. Transient network/rate-limit failures stay non-auth and remain stale-cache eligible for same-source web snapshots.
- It does not send browser cookies, launch the Grok CLI, refresh credentials, perform OAuth, retain raw response bodies, or derive usage from monetary fields.
Kimi
- It opens Pi's
$PI_CODING_AGENT_DIR/auth.json(default~/.pi/agent/auth.json) read-only with a strict 64 KiB cap and guaranteed descriptor cleanup. It accepts only the exactkimi-codingentry withtype: "api_key"and a nonempty, control-byte-free literal stringkey; malformed or oversized files, unsafe shapes, and environment, template, or command references are unavailable without resolving or executing their values. Auth and quota inspection do not create, rewrite, or otherwise manage Pi provider state. - If Pi has no supported credential, it reads the official Kimi Code CLI credential at
$KIMI_CODE_HOME/credentials/kimi-code.json, defaulting to$HOME/.kimi-code/credentials/kimi-code.json. It accepts only a non-emptyaccess_tokenwhose Unix-secondsexpires_at(a JSON number or numeric string) is more than 60 seconds in the future. - The Pi source always has priority. Ambient API-key environment variables are not a credential source. Transport, decoding, timeout, cancellation, and server failures do not trigger credential switching.
- It sends one redirect-disabled
GETto the fixedhttps://api.kimi.com/coding/v1/usagesendpoint with a 15 second total deadline and a 262,144-byte decoded-body cap. - It never uses
refresh_token, accepts a custom Kimi origin, launches Pi or Kimi, makes a model request, refreshes or writes credentials, creates a device ID, imports cookies, sends device identity, retains raw responses, or exposes account, plan, token, or fingerprint data. - Definitive credential absence or rejection retires Kimi cache data. Transient fallback drops reset-expired windows and applies five-hour or seven-day age bounds to windows without resets.
Safety guarantees
- Quota and auth HTTP requests go only to first-party provider usage, quota, billing, or entitlement endpoints with the user's local credentials.
- The user-initiated
updatecommand is the only non-provider network surface, and it is not part of quota measurement. - It sends credential values only to the first-party provider request they authenticate.
- It never prints, logs, or caches credential values.
- It never launches the Claude, Grok, Pi, or Kimi CLIs, so it cannot spend quota or mutate provider credentials while measuring them.
Cache
| Item | Behavior |
|---|---|
| Quota cache | Lives at ~/.cache/quota-axi/quotas.json or under $XDG_CACHE_HOME/quota-axi/ when XDG_CACHE_HOME is set. |
| Quota cache permissions | Uses 0600 file permissions. |
| Quota cache contents | Stores normalized non-secret snapshots only. |
| Claude Keychain access marker | Lives alongside the quota cache as claude-keychain-access-granted[-<profile-hash>]-account-<account-hash>; the profile hash is eight hexadecimal characters when applicable and the account hash is sixteen. It uses 0600 file permissions, contains no credential material or raw account name, and legacy service-only markers are ignored rather than deleted. |
| Cached reports | Only fresh provider snapshots with windows are cached. |
| Fresh provider reports with no windows | Clear any cached snapshot for that provider, so entitlement-only reports do not leave stale quota windows behind. |
| Reports and details not cached | Failed providers, stale providers, account identity, and source attempts are not cached. |
| Claude cache fallback | Definitive missing/invalid credential and HTTP 401/403 failures retire the snapshot. Only transient failures may use a formerly fresh snapshot, with a seven-day provider bound plus reset and resetless-window pruning. |
| Codex cache identities | Cached Codex windows are accepted only when ID, label, kind, duration, and duplicate suffix order agree; stale snapshots with mismatched identities are rejected. |
| Grok cache provenance | Only snapshots produced by the current web consumer operation can be used as Grok stale fallback; legacy api billing-proxy snapshots are rejected. |
Development
pnpm install # Install dependencies
pnpm run build # Compile TypeScript to dist/
pnpm run lint # Run ESLint
pnpm run format:check # Check Prettier formatting
pnpm test # Run fixture parser and CLI tests
pnpm run build:skill -- --check # Verify the generated skill is current
pnpm run dev # Run the CLI with tsx
Contributing
See CONTRIBUTING.md for the no-mistakes PR workflow, generated-file rules, and release-please conventions.
License
MIT