pi-usage-meters

Pi extension: /usage shows subscription quota meters for Claude, Codex, Copilot, Kimi, Grok, GLM, and MiniMax plans.

Packages

Package details

extension

Install pi-usage-meters from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-usage-meters
Package
pi-usage-meters
Version
0.6.1
Published
Nov 4, 2026
Downloads
478/mo · 294/wk
Author
quigleybits
License
MIT
Types
extension
Size
61.6 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-usage-meters

A pi coding agent extension that adds a /usage command showing subscription quota for every provider you're connected to — one compact, colour-coded block instead of a dashboard per provider. Claude, Codex, Kimi, Grok, and GitHub Copilot authenticate via pi's OAuth; the GLM and MiniMax plans use the API keys pi already holds (ZAI_API_KEY / ZAI_CODING_CN_API_KEY, MINIMAX_API_KEY / MINIMAX_CN_API_KEY). Pay-as-you-go accounts without a plan (DeepSeek, MiniMax sk-api- keys) are out of scope by design: this meters plans. A credit balance that belongs to a metered plan, such as Codex credits, shows in that plan's block.

Colour-coded usage meters for Claude, Codex, and Grok, from a live /usage

Each provider block is colour-coded on your terminal's existing background, bars fill left-to-right with usage, and the pie glyph (○ ◔ ◑ ◕ ●) fills as the quota window elapses toward reset. The entry renders in the transcript but never enters LLM context.

Install

pi install npm:pi-usage-meters

Try without installing:

pi -e npm:pi-usage-meters

Then run /usage in any pi session. Results are cached in memory for 60 seconds; use /usage --refresh to bypass the cache. No extension-specific configuration is needed — OAuth credentials come from pi's own auth store (/login) and refresh automatically. The GLM meter resolves either zai / ZAI_API_KEY or zai-coding-cn / ZAI_CODING_CN_API_KEY from the same auth registry; if both are configured, the existing global zai provider takes precedence.

Only providers you are connected to get a block. Everything else collapses into one dim footer line — not connected: Codex · Kimi, Claude: login expired (/login anthropic), Kimi: no credits left — plan used up or expired, Codex: timed out — so an account you do not have never shows up as an error. /usage --all lists the per-provider login hints as blocks instead. Footer lines are plain words; the HTTP status or error code behind each one is kept in the entry's hidden debug field (visible in the session file), never on screen.

Providers & endpoints

Provider Plan label source Quota endpoint
Claude (anthropic) api.anthropic.com/api/oauth/profile api.anthropic.com/api/oauth/usage
Codex (openai-codex) plan from usage payload chatgpt.com/backend-api/wham/usage + .../rate-limit-reset-credits
Kimi (kimi-coding) membership level from usage payload api.kimi.com/coding/v1/usages
Grok (xai) cli-chat-proxy.grok.com/v1/settings cli-chat-proxy.grok.com/v1/billing?format=credits (one weekly SuperGrok pool; product rows indent under it when they sum to that pool; monthly fallback) + redeemed-reset detection
Copilot (github-copilot) copilot_plan / access_type_sku from the user payload api.github.com/copilot_internal/user (monthly premium requests from quota_snapshots; Copilot Free renders its chat/completions allowances); GitHub Enterprise logins use api.<your-host>
GLM (zai, zai-coding-cn) plan level from usage payload Global: api.z.ai/api/monitor/usage/quota/limit; China: open.bigmodel.cn/api/monitor/usage/quota/limit (coding-plan windows: 5h session plus weekly or monthly, classified from the payload's unit/number; an explicit 7-day window renders as Week)
MiniMax (minimax, minimax-cn) token plan Global: api.minimax.io/v1/token_plan/remains; China: api.minimaxi.com/v1/token_plan/remains (5h session + weekly window per model family, same contract as the official MiniMax CLI); pay-as-you-go sk-api- keys are skipped, so a China-region plan key still meters; the block reads not connected only when no plan key exists

Privacy & security

  • OAuth access tokens come from pi's auth store only (ctx.modelRegistry.getProviderAuth). The GLM meter resolves zai and zai-coding-cn API keys the same way and sends each key only to its matching issuer (api.z.ai or open.bigmodel.cn) — never to another provider's endpoint. The same holds for MiniMax (api.minimax.io / api.minimaxi.com, the hosts pi already sends those keys to).
  • The Grok reset-detection feature persists a small state file at ~/.pi/agent/usage-meters-state.json (override: PI_USAGE_METERS_STATE). It contains only the last-seen Grok weekly period boundaries, pool percentage and a timestamp, plus, after a detected reset, that reset's date and its two percents for the rest of the week — no tokens, no secrets, no other providers. It is written atomically, deletable at any time, and never leaves the machine.
  • Codex's non-secret account ID is decoded from the OAuth JWT.
  • One documented exception to "credentials come from getProviderAuth only" — Copilot. GitHub's quota endpoint authenticates the GitHub OAuth token, but pi hands extensions only the exchanged Copilot session token. So, only after getProviderAuth("github-copilot") confirms a live OAuth login (pi validates and refreshes it there), the Copilot meter reads pi's stored credential through pi's public readStoredCredential() API — this package never parses the credential file itself — and sends that token only to api.github.com (or your GitHub Enterprise API host). Nothing else in this package reads the credential store.
  • Credentials are used solely in the Authorization header on the provider's own HTTPS quota endpoints. OAuth providers and global Z.AI use Bearer; Z.AI Coding CN uses the raw API-key value required by its endpoint. Credentials are never logged, rendered, or written into session entries. Remote error bodies are not persisted — at most a short identifier-like error code (for example resource_exhausted) is kept in the entry's hidden debug field alongside the HTTP status. Fetches refuse redirects (redirect: "error"), so a credential can never follow a redirect to another host.
  • API responses are size-limited; untrusted strings are length-limited and stripped of terminal controls, ANSI, and bidirectional-text controls both before storage and again at render time.
  • Each provider fetch is isolated. Every request is capped at 8 s, and the whole provider, login lookup included, at 10 s: one failure or stall never blanks the others.

Privacy note: /usage stores plan labels, quota percentages, reset times, and any displayed spend figures in the current pi session. Exporting or sharing that session includes this output. Do not share the session if those account details are sensitive.

Notes & caveats

  • These quota endpoints are undocumented and may change without notice; each provider fetch is isolated and fails soft.
  • A request that stalls is cut off after 8 s — one budget per provider, optional follow-up calls included — and a provider whose login lookup stalls is cut off after 10 s. Both read timed out in the footer line; the other meters still render. A failed login refresh reads login expired only when the provider rejects the login; a network drop, a 5xx or a 429 during the refresh reads network error, provider error — try again later or rate limited — try again shortly. Other failures read rate limited — try again shortly, no credits left — plan used up or expired (HTTP 402, or 429 with an exhausted/quota code — Kimi answers this way once a plan's credits are gone), access denied, endpoint changed — update the extension, provider error — try again later, network error. A connected account with no quota windows (no active plan) shows as no plan data.
  • Claude: profile tier claude_max_20x is labelled Ultra and claude_max_5x is labelled Max, from the plan tier only. API readings alone do not establish Ultra verification or whether a 5h limit is enforced. On Ultra, a reported 5h reading carries the note API meter; may not be an enforced limit; on every plan, a missing or null five_hour field adds no Session row. Neither the note nor an absent row means unlimited or verified. If one limits entry has kind: "session" and a boolean is_active, Ultra shows that literal value as API session flag is_active: true|false. This flag is telemetry from an undocumented endpoint. It does not show whether a limit is enforced or whether the account is verified. The meter checks every limits entry for session flags. If the response contains several, the meter shows the total count and does not treat any one as authoritative. It lists up to ten literal values and ends the list with … when there are more. On any plan, a window with no numeric reading shows window reported, reading unavailable instead of 0%. The same rule holds for every provider: a missing reading is never drawn as 0% or 100%.
  • MiniMax's *_usage_count fields historically meant remaining; when the payload carries *_remaining_percent the meter uses it to pick the right reading (mirroring the official MiniMax CLI), and a weekly boost (weekly_boost_permille) is applied the same way. Models flagged "not in plan" are skipped; the shared general bucket is the primary meter and a window the server flags as exhausted (status 2) renders as 100% used even if its percent is stale. The API reports auth failures in-band (HTTP 200 with status_code 1004 or 2049), which the meter maps to API key rejected.
  • Copilot: quota_snapshots.premium_interactions drives the meter (percent_remaining → used, with entitlement/remaining counts and any overage); unlimited snapshots (chat and completions on paid plans) are skipped. Copilot Free renders monthly_quotas against limited_user_quotas. A lapsed subscription (subscription_ended) carries no snapshots and shows as no plan data. When percent_remaining is missing, the meter uses the entitlement/remaining counts; a snapshot with no reading shows as unavailable. The month pie assumes a 30-day window ending on quota_reset_date.
  • Codex: the usage payload's credits.balance is the flexible-usage balance, shown as Credits N remaining. unlimited shows unlimited. A missing balance, has_credits: false, or a non-numeric balance adds no row and is not treated as zero. This is separate from banked resets. The banked-reset lookup sends OpenAI-Beta: codex-1 and originator: Codex Desktop, matching the existing Codex client endpoint contract. Review this compatibility choice if OpenAI publishes an official replacement.
  • xAI reports SuperGrok usage as one weekly credit pool (creditUsagePercent). Product rows are a split of that pool when their percents add up to it, and then indent under the week line. GrokBot and GrokBuild render as Bot. Api renders as Grok API. That row is the pool's API slice. It is not the X developer credit balance, and it is not a console.x.ai prepaid balance. Chat and Imagine labels stay unchanged. Legacy monthly credit totals provide a fallback when xAI omits the weekly payload or its percent. Grok Bot usage on a SuperGrok plan is the Bot row of this pool, so /login xai covers it and no second login is needed.
  • Grok usage-reset credits are served only to the grok.com web app (a cookie-authenticated Connect RPC, prod_mc_billing.ConsumerUiSvc/GetRemainingResets); the OAuth-reachable billing API never reports them. The meter therefore detects redeemed resets by inference: within one weekly period the pool percentage only climbs, so a drop of 5+ points while the period is unchanged is reported as Reset used 21 Aug (35% → 5%). The line stays for the rest of that week, and a new week clears it. This needs a small state file (see Privacy). It is inference — an xAI-side recomputation could in theory produce the same signature. If xAI ever exposes explicit counts (resetsRemaining), they are rendered as Resets N available and take precedence in spirit. scripts/probe-grok-billing.mjs (repo only, not shipped) dumps the sanitized payload for re-checking.
  • The state file path defaults to ~/.pi/agent/usage-meters-state.json and can be overridden with the PI_USAGE_METERS_STATE environment variable. Delete the file at any time; the meter simply re-seeds a baseline on the next /usage (no reset line is reported for the first observation).
  • The pie glyph uses ○ ◔ ◑ ◕ ● from the Unicode Geometric Shapes block so a single font renders all states uniformly.
  • Requires a pi version with OAuth login support for the providers you use (/login anthropic, /login openai-codex, /login kimi-coding, /login xai, /login github-copilot). The Copilot meter needs pi ≥ 0.80.8, which added the readStoredCredential export.

Development

Runtime code has no installed dependencies: pi's extension loader supplies the @earendil-works/pi-tui and @earendil-works/pi-coding-agent imports from its own bundle. extensions/index.js is the small pi adapter (it also hands readStoredCredential to the loader); provider, formatting, caching, and security logic lives in extensions/core.js, which stays testable outside pi.

npm test
npm run check
pi -e ./extensions/index.js

License

MIT