@narumitw/pi-usage 0.60.3 β†’ 0.60.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,17 +9,7 @@ xAI OAuth subscription reporting follows the reviewed Grok Build contract and ru
9
9
  ## ✨ Features
10
10
 
11
11
  - Shows active-account usage and next actions through `/usage`.
12
- - Reports OpenAI Codex subscription windows, credits, resets, and model-specific buckets.
13
- - Reports Kimi For Coding plan windows, resets, and separately labeled booster-wallet currency.
14
- - Reports Moonshot AI Global and China API account balances in their native currencies.
15
- - Reports MiniMax Global and China Token Plan windows or pay-as-you-go API balance.
16
- - Reports GitHub Copilot allowances and OpenRouter per-key limits and spending windows.
17
- - Reports exact DeepSeek API balances with separate CNY and USD values.
18
- - Reports OpenCode Go plan windows and Z.AI Coding Plan quotas.
19
- - Reports Fireworks rated API spend for the last 30 days with per-series subtotals and account selection.
20
- - Reports Vercel AI Gateway credit balance and lifetime spend.
21
- - Reports Baseten organization Model APIs spend after credits for the last 30 days.
22
- - Reports xAI OAuth subscription allowances and credits.
12
+ - Reports subscription allowances, API balances, and spending for the supported providers listed below without mixing their billing semantics.
23
13
  - Toggles persistent Codex Fast routing through `/fast` or the usage menu.
24
14
  - Redeems eligible Codex resets only after fresh account matching and explicit confirmation.
25
15
  - Refreshes one or all configured providers with bounded concurrency while preserving partial results.
@@ -61,51 +51,18 @@ Run `/fast` to toggle Fast mode for a supported active Codex model.
61
51
 
62
52
  ## πŸ’¬ Commands
63
53
 
64
- Open the manager with:
54
+ | Command | Purpose |
55
+ | --- | --- |
56
+ | `/usage` | Query the active provider's usage, then manage provider queries, preferences, or eligible Codex resets. |
57
+ | `/fast` | Toggle Fast mode for the active supported Codex model. |
65
58
 
66
- ```text
67
- /usage
68
- ```
59
+ Both commands support TUI and RPC, accept no arguments, and reject print and JSON modes.
60
+ Cross-provider queries require an explicit interactive choice; there are no provider-ID, `--refresh`, or `--all` arguments.
61
+ Requests use the matched provider credentials; see [Security and privacy](#-security-and-privacy).
62
+ Fast mode uses more plan allowance; see [Codex Fast mode](#codex-fast-mode) for eligibility and when changes apply.
69
63
 
70
- In TUI or RPC mode, the menu first queries the active model provider and then offers these actions:
71
-
72
- ```text
73
- Refresh current usage
74
- Settings
75
- Turn Fast mode on/off # Supported current Codex models only
76
- Redeem usage limit reset… # Current Codex OAuth accounts only
77
- View another configured provider…
78
- View all configured providers…
79
- Close
80
- ```
81
-
82
- `/usage` accepts no arguments, including `--refresh`, a provider ID, or `--all`.
83
- Cross-provider requests require an explicit interactive choice.
84
- Escape returns from provider selection or closes the root menu.
85
- Print and JSON modes reject `/usage` because they cannot host the interactive flow.
86
- The extension owns the cancellable live-query progress view because it streams provider work and supports in-flight abort.
87
-
88
- ### Provider targets
89
-
90
- A target is the provider-owned account, organization, project, team, or workspace used for one usage query.
91
- Providers without target discovery query immediately, and a single returned target is selected automatically without writing settings.
92
- When several targets are available, `/usage` remembers an explicit selection by provider and reuses it only while it remains in a fresh listing.
93
- A missing remembered target returns **Selection required** instead of querying another target silently.
94
- The current provider then offers **Select <target>…**, while a ready current or individually viewed provider offers **Change <target>…**.
95
-
96
- Selecting another provider may open one Pi target prompt after that provider is queried lazily.
97
- Cancelling the prompt changes nothing.
98
- Auth and target membership are revalidated before an explicit selection is saved, then both are resolved again before billing is queried.
99
- **View all configured providers…** never opens nested target prompts: unresolved providers remain visible with guidance to view them individually.
100
- Background status refresh also stays non-interactive and shows `selection required` until `/usage` completes the choice.
101
- Fireworks accounts are the first implementation of this provider-neutral flow.
102
-
103
- For the current OpenAI Codex provider, **Redeem usage limit reset…** first checks fresh earned-reset details.
104
- When details are available, you select a reset and review its exact effect before confirmation.
105
- **No, go back** is the safe default and cancellation before confirmation sends no mutation.
106
- After confirmation, the reset operation cannot be cancelled from its progress view; session replacement or shutdown still aborts owned work.
107
- A transport failure offers **Try again** with the same redemption request ID so the backend can treat an uncertain retry idempotently.
108
- Successful, already-completed, not-needed, and no-credit outcomes are reported separately, then usage and the statusline are refreshed for the still-current account.
64
+ Codex reset redemption requires a freshly matched current OAuth account and explicit confirmation; **after confirmation, its progress view cannot cancel the reset**.
65
+ Read the [query and reset guide](./docs/operations.md) for target selection, cancellation, and safe retry behavior.
109
66
 
110
67
  ## βš™οΈ Settings
111
68
 
@@ -135,7 +92,6 @@ It sends `service_tier: "priority"` while enabled and explicit `service_tier: "d
135
92
  The statusline adds `fast` only while the preference is effective, for example `codex fast 59% ↻ 2h30m` with the default reset countdown.
136
93
  Unsupported models and custom or proxy origins are left unchanged.
137
94
 
138
- `/fast` supports TUI and RPC mode, accepts no arguments, and rejects print or JSON mode before mutation.
139
95
  A toggle affects provider requests whose payload hook starts after the save; a request already sent is unchanged.
140
96
  Repair or remove an invalid file, then run `/reload` before trying the toggle again.
141
97
 
@@ -152,245 +108,27 @@ Turn **Codex reset countdown** Off in the TUI Settings screen, or set it to `fal
152
108
 
153
109
  ## πŸ“‹ Provider semantics
154
110
 
155
- ### OpenAI Codex
156
-
157
- - Provider ID: `openai-codex`
158
- - Semantics: ChatGPT consumer subscription limits
159
- - Source: the Codex usage and earned-reset endpoints using Pi's resolved runtime authorization
160
- - Displayed data: returned duration-based windows, resets, credits, earned usage-limit resets, and additional model buckets
161
- - Reset mutation: `POST /wham/rate-limit-reset-credits/consume` with a unique redemption request ID and, when available, the selected opaque credit ID
162
- - Statusline examples: `codex 59% ↻ 2h30m 61% ↻ 2d15m`, `codex fast 59% ↻ 2h30m`, or `codex spark 100% ↻ 2h30m`. Set `codexStatusResetCountdown` to `false` for the legacy `5h` and `wk` labels.
163
-
164
- The statusline selects a returned bucket that matches the current Codex model when one is available.
165
- Unlike `pi-codex-usage`, this successor intentionally has no Codex CLI fallback because the CLI may be logged into a different account than Pi's active runtime account.
166
-
167
- Reset redemption is available only when Codex is the current provider.
168
- Pi's freshly resolved access token must exactly match an OAuth credential from Pi's stored login or a compatible credential source.
169
- `pi-usage` forwards only the bearer authorization and matching `chatgpt-account-id` to the official ChatGPT origin.
170
- API-key credentials, configured-but-not-current Codex accounts, account changes during the flow, and custom/proxy origins fail before mutation.
171
- Backend-provided titles and descriptions are sanitized for terminal display.
172
- Opaque credit and account IDs are never shown or persisted by the extension.
173
-
174
- ### Kimi For Coding
175
-
176
- - Provider ID: `kimi-coding`
177
- - Semantics: Kimi Coding Plan request windows plus a separate Extra Usage booster wallet
178
- - Source: `GET https://api.kimi.com/coding/v1/usages` using Pi's freshly resolved runtime Bearer credential
179
- - Displayed plan data: the weekly summary, returned sub-windows, used and remaining request percentages, and valid reset times
180
- - Displayed wallet data: balance, monthly spend, and monthly charge limit
181
- - Statusline examples: `kimi 99% 5h 96% wk` or `kimi 95% 1d`
182
-
183
- Both Pi API-key credentials and Pi OAuth credentials are accepted because current Pi resolves each form as Bearer authorization for the same official Kimi inference origin.
184
- The extension queries the fixed usage endpoint only when both the selected model origin and the effective resolved-auth origin are `https://api.kimi.com`.
185
- Custom and proxy origins fail before network access, redirects are rejected, and the credential is never sent to an override from Kimi Code's environment-specific development path.
186
-
187
- Plan buckets remain integer request counts and are rendered with their source-defined windows.
188
- Unknown units, duplicate windows, missing counts, invalid timestamps, and malformed rows remain unavailable rather than receiving guessed semantics.
189
- Booster-wallet `amount` and `amountLeft` values use Kimi's first-party conversion of 1,000,000 fixed-point units per cent, while monthly values already arrive in cents.
190
- Wallet values retain their currency and stay separate from plan requests and percentages in reports and the statusline.
191
- Wallet fields remain unavailable unless the response supplies one consistent currency; missing monthly values are omitted, and an enabled zero cap is shown as zero.
192
-
193
- The contract was revalidated on 2026-08-27 against [Pi `c49906ec77788625aacbdc53ebca6fbe65bd20f5`](https://github.com/earendil-works/pi/tree/c49906ec77788625aacbdc53ebca6fbe65bd20f5), including [`kimi-coding.ts`](https://github.com/earendil-works/pi/blob/c49906ec77788625aacbdc53ebca6fbe65bd20f5/packages/ai/src/providers/kimi-coding.ts) and [`auth/oauth/kimi-coding.ts`](https://github.com/earendil-works/pi/blob/c49906ec77788625aacbdc53ebca6fbe65bd20f5/packages/ai/src/auth/oauth/kimi-coding.ts).
194
- It was also revalidated against [Kimi Code `676e4d82240855044fe809fea89ce1dbe8e512cf`](https://github.com/MoonshotAI/kimi-code/tree/676e4d82240855044fe809fea89ce1dbe8e512cf), including [`managed-usage.ts`](https://github.com/MoonshotAI/kimi-code/blob/676e4d82240855044fe809fea89ce1dbe8e512cf/packages/oauth/src/managed-usage.ts) and its [tests](https://github.com/MoonshotAI/kimi-code/blob/676e4d82240855044fe809fea89ce1dbe8e512cf/packages/oauth/test/managed-usage.test.ts).
195
- The pinned Pi source at `e86823096c5bad39e1ca282ec24bc5eb9bec745b` has no changes in either reviewed Kimi file at the selected revision.
196
- The pinned Kimi managed-usage source at `cd7c97b377a77f7ae1b9d541cafe314e986ec074` is an ancestor of that selected revision and has no changes in the reviewed source or tests.
197
-
198
- ### Moonshot AI API balance
199
-
200
- - Provider IDs: `moonshotai` and `moonshotai-cn`
201
- - Semantics: current API account balance, not Kimi For Coding subscription usage
202
- - Global source: `GET https://api.moonshot.ai/v1/users/me/balance`
203
- - China source: `GET https://api.moonshot.cn/v1/users/me/balance`
204
- - Displayed data: available, voucher, and cash balance in USD for Global or CNY for China
205
- - Statusline examples: `moonshot USD 49.58894` or `moonshot CNY 49.58894`
206
-
207
- Each endpoint uses Pi's resolved inference Bearer key for the matching region.
208
- Pi maps both built-in providers to `MOONSHOT_API_KEY`, so that shared environment credential is eligible only for the currently selected region.
209
- Querying the sibling region requires a provider-specific stored, runtime, or `models.json` credential.
210
- The extension rejects custom, proxy, and cross-region origins before network access and refuses redirects.
211
- Available and voucher balances must be nonnegative, while cash balance may be negative when the account owes money.
212
- The endpoint does not provide historical spend, token totals, quota windows, or reset times.
213
- These API-platform balances are independent from the `kimi-coding` subscription and booster wallet.
214
-
215
- The contracts were verified on 2026-08-30 against the official [Global balance reference](https://platform.kimi.ai/docs/api/balance), [China balance reference](https://platform.moonshot.cn/docs/api/balance), and first-party [`MoonshotAI-Cookbook` balance client and DTO](https://github.com/MoonshotAI/MoonshotAI-Cookbook/tree/25a9e46d2391dd4817d28ab980dac69eb59b582c/examples/golang_demo).
216
- ### MiniMax Token Plan and API balance
217
-
218
- - Provider IDs: `minimax` and `minimax-cn`
219
- - Token Plan source: `GET {region-api-root}/v1/token_plan/remains`
220
- - Pay-as-you-go source: `GET {region-api-root}/account/query_balance`
221
- - Region API roots: `https://api.minimax.io` and `https://api.minimaxi.com`
222
- - Statusline examples: `minimax 15% 5h 80% wk` or `minimax USD 98.00001`
223
-
224
- Pi's resolved MiniMax API key selects exactly one endpoint before network access.
225
- Keys with the first-party `sk-api-` prefix query pay-as-you-go balance; other MiniMax API keys query Token Plan quota.
226
- The extension never probes both endpoints with one credential.
227
- Token Plan reports preserve provider rows, rolling and weekly windows, counts, reset times, unlimited status, and first-party handling for legacy versus current `*_usage_count` semantics.
228
- Pay-as-you-go reports keep available, cash, voucher, credit, and owed amounts separate in USD for Global or CNY for China.
229
- Custom, proxy, and cross-region origins fail before network access, and redirects are rejected.
230
-
231
- The contract was verified on 2026-08-30 against MiniMax's [Token Plan FAQ](https://platform.minimax.io/docs/token-plan/faq#how-to-check-token-plan-usage) and the first-party [`MiniMax-AI/cli`](https://github.com/MiniMax-AI/cli/tree/b78eccea80a0f9692e186d98906cff26931464f3), including endpoint selection, response types, quota normalization, and SDK tests.
232
-
233
- ### GitHub Copilot
234
-
235
- - Provider ID: `github-copilot`
236
- - Semantics: the allowance reported for the active Copilot plan
237
- - Allowance labels: AI credits for usage-based billing, premium requests for legacy annual billing, or chat requests for Copilot Free
238
- - Source: GitHub's undocumented `GET /copilot_internal/user` endpoint
239
- - Displayed data: entitlement, remaining allowance, percentage, reset time, plan, and any additional usage beyond the included allowance
240
- - Statusline examples: `copilot credits 1200/1500 80%`, `copilot 245/300 82%`, or `copilot chat 40/50 80%`
241
-
242
- GitHub's quota endpoint requires the original GitHub OAuth token rather than the short-lived Copilot inference token exposed by runtime auth.
243
- `pi-usage` supports Copilot accounts created through Pi's `/login` flow and named accounts offered by a compatible `oauth:credential-source:v1` owner.
244
- It uses a candidate only when its short-lived access token exactly matches the freshly resolved active runtime credential.
245
- Duplicate equivalent candidates are harmless, while conflicting matches fail closed without choosing by extension load order.
246
- API-key credentials, account mismatches, GitHub Enterprise accounts, and proxy/custom provider origins fail closed.
247
- The detailed report follows the endpoint's `token_based_billing` marker so AI credits are not mislabeled as legacy premium requests.
248
- It reports overage without treating a negative included balance as malformed.
249
-
250
- ### OpenRouter
251
-
252
- - Provider ID: `openrouter`
253
- - Semantics: API-key spend and per-key credit limitsβ€”not consumer subscription quota
254
- - Source: OpenRouter's documented [`GET /api/v1/key`](https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key) endpoint using Pi's resolved inference API key
255
- - Displayed data: key label when safely returned, optional per-key limit and remaining amount, reset period, and daily/weekly/monthly/all-time spend
256
- - Statusline examples: `openrouter $74.50 left` or `openrouter $25.50 used`
257
-
258
- The extension does not call OpenRouter's account-level `/credits` endpoint because that operation requires a separate management key.
259
- OpenRouter documents the distinction between credit and rate limits in its [API limits guide](https://openrouter.ai/docs/api_reference/limits).
260
-
261
- ### DeepSeek API balance
262
-
263
- - Provider ID: `deepseek`
264
- - Semantics: current API account balance, not historical usage or quota
265
- - Source: documented `GET https://api.deepseek.com/user/balance` using Pi's freshly resolved runtime API key
266
- - Displayed data: whether API calls are available plus separate total, granted, and topped-up balances for each returned CNY or USD currency
267
- - Statusline examples: `deepseek CNY 110.00` or `deepseek CNY 110.00 Β· USD 20.00`
268
-
269
- The extension queries the fixed balance endpoint only when the selected model origin is `https://api.deepseek.com` and any resolved-auth origin override, when present, has the same official origin.
270
- Pi's built-in DeepSeek API-key resolver does not attach a redundant auth origin, so the validated model origin remains authoritative when no override exists.
271
- Custom and proxy origins fail before network access, redirects are rejected, and only the resolved Bearer credential is forwarded from Pi's runtime auth.
272
- Monetary decimal strings remain exact from the response through display.
273
- CNY and USD stay separate and are never converted or added together.
274
- The balance endpoint does not provide historical spend, request windows, reset times, or aggregate token usage, so `pi-usage` does not claim those DeepSeek capabilities.
275
-
276
- The contract was verified on 2026-08-28 against [DeepSeek's Get User Balance documentation](https://api-docs.deepseek.com/api/get-user-balance) and Pi's [`deepseek.ts`](https://github.com/earendil-works/pi/blob/c49906ec77788625aacbdc53ebca6fbe65bd20f5/packages/ai/src/providers/deepseek.ts) at `c49906ec77788625aacbdc53ebca6fbe65bd20f5`.
277
- DeepSeek Harness `cd5ef8148158c3a752a658978873241fdf8e2bbc` reports only per-request model token usage and does not provide account balance data.
278
-
279
- ### Fireworks API spend
280
-
281
- - Provider ID: `fireworks`
282
- - Semantics: rated 30-day account spend, not credit balance or spend-cap quota
283
- - Source: documented `GET https://api.fireworks.ai/v1/accounts` account discovery and `GET https://api.fireworks.ai/v1/accounts/{account_id}/billing/summary` rated costs using Pi's resolved inference API key
284
- - Displayed data: exact rated spend per currency with serverless, dedicated-deployment, and training subtotals for the trailing 30 days
285
- - Statusline example: `fireworks USD 12.345678901`
286
-
287
- The extension queries the fixed endpoints only when the selected model origin is `https://api.fireworks.ai` and any resolved-auth origin override, when present, has the same official origin.
288
- The account slug is discovered through the documented account listing.
289
- One visible account is selected automatically; several visible accounts use the remembered selection or ask through `/usage`, and a disappeared selection returns **Selection required** without a billing request.
290
- Monetary `units` and `nanos` values are summed exactly with integer arithmetic and stay exact through display.
291
- Fireworks does not expose credit balance, spend caps, per-window quota, or reset times through its API, so `pi-usage` does not claim those Fireworks capabilities; the web console remains the authoritative balance source.
292
- Rated line items may differ from the final invoice once credits or adjustments are applied.
293
-
294
- The contract was verified on 2026-07-31 against Fireworks' [Usage & Cost Breakdown](https://docs.fireworks.ai/accounts/exporting-usage-and-costs), [Get billing summary](https://docs.fireworks.ai/api-reference/get-billing-summary), and [List Accounts](https://docs.fireworks.ai/api-reference/list-accounts) API references.
295
-
296
- ### Vercel AI Gateway credits
297
-
298
- - Provider ID: `vercel-ai-gateway`
299
- - Semantics: current team credit balance and lifetime spend, not rate-limit quota
300
- - Source: documented `GET https://ai-gateway.vercel.sh/v1/credits` using Pi's resolved AI Gateway API key
301
- - Displayed data: exact decimal-string credit balance and lifetime spend in USD
302
- - Statusline example: `vercel USD 95.50 left`
303
-
304
- The extension queries the fixed endpoint only when the selected model origin and any resolved-auth origin are `https://ai-gateway.vercel.sh`.
305
- Custom and proxy origins fail before network access, redirects are rejected, and only the resolved Bearer credential is forwarded.
306
- The credits endpoint does not provide reset times, request-rate counters, or date-window usage, so `pi-usage` does not claim those capabilities.
307
- Vercel's separate Custom Reporting API is limited to eligible paid plans and is intentionally outside this first integration.
308
-
309
- The contract was verified on 2026-08-30 against Vercel's [REST API Reference](https://vercel.com/docs/ai-gateway/sdks-and-apis/rest-api#check-credit-balance) and the first-party [`vercel/ai` Gateway implementation](https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/gateway/src/gateway-fetch-metadata.ts).
310
-
311
- ### Baseten Model APIs spend
312
-
313
- - Provider ID: `baseten`
314
- - Semantics: organization-wide Model APIs spend, not per-key quota or account balance
315
- - Source: `GET https://api.baseten.co/v1/billing/usage_summary` using Pi's resolved Baseten API key
316
- - Displayed data: trailing 30-day gross usage, credits used, and net subtotal in USD
317
- - Statusline example: `baseten USD 166.15 net`
318
-
319
- The extension intentionally ignores Dedicated deployment and Training categories because they do not represent Pi's Model APIs provider usage.
320
- The query window is a precise trailing 30 days and stays below the endpoint's 31-day maximum.
321
- The fixed Management API endpoint is queried only for an official `https://inference.baseten.co` model and an official resolved-auth origin.
322
- Custom and proxy origins fail before network access, redirects are rejected, and only the resolved Bearer credential is forwarded.
323
- An empty `model_apis_usage` result is reported as no Model APIs usage rather than zero account-wide spend.
324
-
325
- The contract was verified on 2026-08-30 against Baseten's [Billing and usage](https://docs.baseten.co/organization/billing#view-usage), [Model APIs usage](https://docs.baseten.co/inference/model-apis/pricing-and-limits#usage), first-party [`baseten-go` Management OpenAPI](https://github.com/basetenlabs/baseten-go/blob/f028e27beb4bde106d984833313c055ddd6fefa4/internal/tools/apigen/specs/management.json), and [`baseten-cli` billing behavior](https://github.com/basetenlabs/baseten-cli/blob/e3d002b465f49a7295ea44b5988dbfeb8197896d/internal/cmd/command.org.go).
326
-
327
- ### OpenCode Go (Zen)
328
-
329
- - Provider ID: `opencode-go`
330
- - Semantics: OpenCode Zen plan usage windowsβ€”rolling, weekly, and monthly
331
- - Source: `GET https://opencode.ai/zen/go/v1/usage` using Pi's resolved inference API key
332
- - Displayed data: used percentage and reset time for each window
333
- - Status handling: `rate-limited` windows remain visible, while unknown statuses become unavailable notes
334
- - Statusline examples: `zen 0% r 4% w 2% m`
335
-
336
- The fixed endpoint is queried only when the OpenCode Go model uses the official `https://opencode.ai` origin.
337
- When resolved provider auth includes a base URL, that URL must use the same origin.
338
- Other origins fail before the credential is sent.
339
-
340
- ### xAI consumer subscriptions
341
-
342
- - Provider ID: `xai`
343
- - Semantics: consumer subscription allowance and credits, not xAI API-team billing
344
- - Identity route: `GET https://cli-chat-proxy.grok.com/v1/user?include=subscription`
345
- - Billing route: `GET https://cli-chat-proxy.grok.com/v1/billing?format=credits`
346
- - Displayed data: included allowance or legacy monetary limit, period and reset, on-demand spend and cap, prepaid balance, and a sanitized optional plan tier
347
- - Statusline: not published; xAI is queried only through an explicit `/usage` action
348
-
349
- The adapter accepts only the official Pi inference origin `https://api.x.ai` and a freshly resolved bearer that exactly matches one complete Pi OAuth credential.
350
- Pi's reviewed OAuth scope is `openid profile email offline_access grok-cli:access api:access`.
351
- The adapter rejects `XAI_API_KEY`, duplicate or conflicting OAuth candidates, account mismatches, and incomplete OAuth records.
352
- It also rejects custom or proxy-resolved origins before consumer-proxy access.
353
- API-key users can review API-team spend through [console.x.ai](https://console.x.ai/) instead.
354
- The public Management API requires a separate management key and team ID and is intentionally outside this runtime-credential integration.
355
-
356
- The identity response supplies a transient proxy-canonical `userId` that is validated and sent as `x-userid` only on the billing request.
357
- The extension sends the matched bearer as `Authorization` plus Grok Build's source-defined non-secret `X-XAI-Token-Auth`, client-version, and interactive client-mode headers.
358
- It does not read Grok Build files, device state, names, email, or other profile fields.
359
- Responses are body-bounded, redirects are rejected, raw identity and billing payloads are not retained, and secrets are redacted from errors.
360
- Included allowance, on-demand usage, and prepaid balance remain distinct because they represent different billing concepts.
361
-
362
- The current official Grok Build implementation is the ground truth for the xAI integration contract.
363
- The implementation contract was verified against these first-party revisions:
364
-
365
- - Pi [`providers/xai.ts`](https://github.com/earendil-works/pi/blob/e86823096c5bad39e1ca282ec24bc5eb9bec745b/packages/ai/src/providers/xai.ts) and [`auth/oauth/xai.ts`](https://github.com/earendil-works/pi/blob/e86823096c5bad39e1ca282ec24bc5eb9bec745b/packages/ai/src/auth/oauth/xai.ts) at `e868230`, revalidated byte-for-byte for those files at [`ccfe79e`](https://github.com/earendil-works/pi/tree/ccfe79ed238674f760c986e3a61493aab794000a).
366
- - Grok Build [`UserInfo`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-shell/src/auth/model.rs), [`subscription_check.rs`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-shell/src/agent/subscription_check.rs), [`billing.rs`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-shell/src/extensions/billing.rs), [`auth/config.rs`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-shell/src/auth/config.rs), [`xai-grok-http`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-http/src/lib.rs), and [`xai-grok-version`](https://github.com/xai-org/grok-build/blob/9684fa3cdbf2995e30ea8b9b637f1db008f144fc/crates/codegen/xai-grok-version/Cargo.toml) at `9684fa3`.
367
- - [xAI Management API team billing boundary at `723dd2a`](https://github.com/xai-org/xai-proto/blob/723dd2aa22d17be35617463837dc47cda008d90e/proto/xai/management_api/v1/billing.proto).
368
-
369
- The approved 2026-08-27 protocol smoke used only Pi's OAuth bearer and read no Grok-local files.
370
- A disposable or maintainer account received HTTP 200 without redirects from both routes.
371
- The implementation also sends the non-secret client headers present on both routes in current Grok Build source, with `x-userid` added only for billing.
372
- The sanitized identity shape contained a string `userId` and nullable `subscriptionTier`.
373
- The billing shape contained a `config` object with period and distinct on-demand and prepaid wrappers, without retaining field values.
374
-
375
- xAI identity and billing requests occur only after an explicit current, configured-provider, or all-provider `/usage` action.
376
-
377
- ### Z.AI (GLM Coding Plan)
378
-
379
- - Provider ID: `zai` and `zai-coding-cn`
380
- - Semantics: GLM Coding Plan quota windowsβ€”the rolling 5-hour and weekly plan-usage windows plus the monthly MCP allowance
381
- - Source: the undocumented `GET {origin}/api/monitor/usage/quota/limit` endpoint also used by Z.AI's official coding plugin, plus the undocumented `GET {origin}/api/biz/subscription/list` plan endpoint
382
- - Allowed origins: the model base URL must resolve to `https://api.z.ai` or `https://open.bigmodel.cn`
383
- - Displayed data: explicit used and remaining values, reset times, provider-reported per-tool MCP details, and the plan name with its renewal date
384
- - Percentage-only windows remain percent-based
385
- - Statusline: publishes remaining plan percentages such as `zai 87% 5h 76% wk`; monthly MCP details remain available through `/usage`
386
-
387
- The monitor endpoint is not a published API contract and may return legacy `TOKENS_LIMIT` or newer `CREDIT_LIMIT` window names.
388
- The extension classifies both forms by the provider's window unit and does not label provider-reported counts as tokens or calls.
389
- The quota monitor expects a raw API key without a `Bearer` prefix.
390
- The extension removes that prefix from resolved authorization before sending it to the monitor endpoint.
391
- Fingerprinting and redaction keep using the original resolved credential.
392
- The plan endpoint only contributes the plan name and renewal date; when it is unavailable or fails, the quota windows remain reported and the plan note falls back to the quota response's plan level.
393
- Only the official `api.z.ai` and `open.bigmodel.cn` origins are queried; other origins fail before sending the credential.
111
+ Usage is provider-specific: a subscription allowance, current balance, and rated spend are not interchangeable.
112
+ Currencies and billing targets remain separate.
113
+
114
+ | Provider | Reported data |
115
+ | --- | --- |
116
+ | OpenAI Codex | Subscription windows, credits, resets, and model buckets |
117
+ | Kimi For Coding | Plan request windows and a separate booster wallet |
118
+ | Moonshot AI Global/China | Current API balance in USD/CNY |
119
+ | MiniMax Global/China | Token Plan windows or pay-as-you-go API balance |
120
+ | GitHub Copilot | AI credits, premium requests, or Free-plan chat allowance |
121
+ | OpenRouter | Per-key credit limits and spending windows |
122
+ | DeepSeek | Exact current CNY and USD API balances |
123
+ | Fireworks | Rated trailing 30-day spend for one selected account |
124
+ | Vercel AI Gateway | Team credit balance and lifetime spend |
125
+ | Baseten | Organization-wide trailing 30-day Model APIs spend after credits |
126
+ | OpenCode Go | Rolling, weekly, and monthly plan windows |
127
+ | xAI | OAuth subscription allowance and credits; explicit menu queries only |
128
+ | Z.AI | Coding Plan quota windows, MCP allowance, plan name, and renewal date |
129
+
130
+ Read the [provider reference](./docs/providers.md) for provider IDs, exact endpoints, authentication requirements, normalization rules, statusline examples, limitations, and pinned contract evidence.
131
+ Codex reset redemption requires a freshly matched current OAuth account and explicit confirmation; custom or proxy origins fail before mutation.
394
132
 
395
133
  ## 🧭 Current and configured accounts
396
134
 
@@ -478,36 +216,19 @@ An absent or incompatible peer preserves standalone fallback and fail-closed mis
478
216
 
479
217
  ## πŸ—‚οΈ Package layout
480
218
 
481
- ```txt
219
+ ```text
482
220
  packages/pi-usage/
483
- β”œβ”€β”€ dist/ # Generated TypeScript runtime loaded by Jiti
484
- β”œβ”€β”€ scripts/
485
- β”‚ └── build-runtime.mjs # Deterministic runtime builder and boundary validator
486
- β”œβ”€β”€ src/
487
- β”‚ β”œβ”€β”€ index.ts # Pi package entrypoint and helper export barrel
488
- β”‚ β”œβ”€β”€ usage.ts # Menu, cache, and usage lifecycle orchestration
489
- β”‚ β”œβ”€β”€ usage-settings-ui.ts # Pi SettingsList interaction and save rollback
490
- β”‚ β”œβ”€β”€ codex-fast.ts # Fast eligibility, request tier, and cost correction
491
- β”‚ β”œβ”€β”€ codex-fast-runtime.ts # Fast command, persistence lifecycle, and request hooks
492
- β”‚ β”œβ”€β”€ settings.ts # Validated user settings and atomic persistence
493
- β”‚ β”œβ”€β”€ usage-helpers.ts # Small orchestration helpers
494
- β”‚ β”œβ”€β”€ usage-targets.ts # Provider-neutral target resolution and safe picker descriptors
495
- β”‚ β”œβ”€β”€ query.ts # Runtime auth resolution and bounded provider queries
496
- β”‚ β”œβ”€β”€ oauth-credential-source.ts # Ephemeral OAuth candidate collection
497
- β”‚ β”œβ”€β”€ codex-resets.ts # Codex reset auth, API contracts, and normalization
498
- β”‚ β”œβ”€β”€ format.ts # Provider-aware notifications and statusline text
499
- β”‚ β”œβ”€β”€ core.ts # Cache, concurrency, fingerprint, and redaction helpers
500
- β”‚ β”œβ”€β”€ providers/ # Provider-specific usage normalization adapters
501
- β”‚ └── types.ts # Common presentation and adapter contracts
502
- β”œβ”€β”€ test/
503
- β”œβ”€β”€ README.md
504
- β”œβ”€β”€ LICENSE
505
- β”œβ”€β”€ tsconfig.json
506
- └── package.json
221
+ β”œβ”€β”€ src/ # Provider adapters, auth, settings, and presentation
222
+ β”‚ β”œβ”€β”€ index.ts # Thin Pi entrypoint
223
+ β”‚ └── usage.ts # Provider queries, cache, and menu
224
+ β”œβ”€β”€ dist/ # Generated Jiti runtime
225
+ β”œβ”€β”€ scripts/build-runtime.mjs # Runtime builder
226
+ β”œβ”€β”€ docs/ # Published reference documentation
227
+ └── test/ # Behavior and lifecycle coverage
507
228
  ```
508
229
 
509
- `index.ts` is the Pi entrypoint and forwards the default factory from `usage.ts` while retaining the package's named helper exports; other source modules are internal.
510
- The generated runtime is built from the authoritative `src/index.ts` graph and does not import back into `src`.
230
+ `src/index.ts` forwards the default factory from `usage.ts` and retains the package's named helper exports; other source modules are internal.
231
+ The generated runtime is built from `src/index.ts` and does not import back into `src`.
511
232
 
512
233
  ## πŸ”Ž Keywords
513
234
 
package/dist/index.ts CHANGED
@@ -239,6 +239,8 @@ function redactUsageError(value, secrets = []) {
239
239
  function errorMessage(error) {
240
240
  return sanitizeDisplayText(error instanceof Error ? error.message : String(error), 600);
241
241
  }
242
+ var UsageUnsupportedError = class extends Error {
243
+ };
242
244
  function abortError() {
243
245
  return Object.assign(new Error("Usage query aborted."), { name: "AbortError" });
244
246
  }
@@ -1735,7 +1737,12 @@ var FIVE_HOUR_WINDOW_MINUTES2 = 300;
1735
1737
  var WEEKLY_WINDOW_MINUTES2 = 10080;
1736
1738
  function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt, plan) {
1737
1739
  const data = asObject11(payload.data);
1738
- if (!data) throw new Error("Z.AI quota response data was not an object.");
1740
+ if (!data) {
1741
+ const reason = asString5(payload.msg);
1742
+ throw new UsageUnsupportedError(
1743
+ `No GLM Coding Plan on this Z.AI credential; API usage is not metered.${reason ? ` Z.AI reported: ${reason}` : ""}`
1744
+ );
1745
+ }
1739
1746
  const limits = Array.isArray(data.limits) ? data.limits : [];
1740
1747
  const buckets = [];
1741
1748
  const metrics = [];
@@ -2448,7 +2455,8 @@ async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard, targe
2448
2455
  );
2449
2456
  } catch (error) {
2450
2457
  if (isStaleExtensionContextError(error) || isAbortError(error)) throw error;
2451
- throw new Error(redactUsageError(errorMessage(error), auth.secrets));
2458
+ const message = redactUsageError(errorMessage(error), auth.secrets);
2459
+ throw error instanceof UsageUnsupportedError ? new UsageUnsupportedError(message) : new Error(message);
2452
2460
  }
2453
2461
  }
2454
2462
  function providerIsConfigured(ctx, providerId) {
@@ -4560,7 +4568,7 @@ function usageExtension(pi, dependencies = {}) {
4560
4568
  providerId: adapter.id,
4561
4569
  providerName,
4562
4570
  displayState,
4563
- status: "query-failed",
4571
+ status: previousDiscoveryFailure.status,
4564
4572
  message: previousDiscoveryFailure.message
4565
4573
  },
4566
4574
  fingerprint: auth.fingerprint,
@@ -4619,7 +4627,7 @@ function usageExtension(pi, dependencies = {}) {
4619
4627
  providerId: adapter.id,
4620
4628
  providerName,
4621
4629
  displayState,
4622
- status: "query-failed",
4630
+ status: previousFailure.status,
4623
4631
  message: previousFailure.message
4624
4632
  },
4625
4633
  fingerprint: auth.fingerprint,
@@ -4672,6 +4680,7 @@ function usageExtension(pi, dependencies = {}) {
4672
4680
  );
4673
4681
  }
4674
4682
  const message = errorMessage(error);
4683
+ const status = error instanceof UsageUnsupportedError ? "unsupported" : "query-failed";
4675
4684
  const now = Date.now();
4676
4685
  for (const [key, failure] of failureBackoff) {
4677
4686
  if (failure.until <= now) failureBackoff.delete(key);
@@ -4680,7 +4689,7 @@ function usageExtension(pi, dependencies = {}) {
4680
4689
  setBoundedMap(
4681
4690
  failureBackoff,
4682
4691
  failureKey,
4683
- { until: now + FAILURE_BACKOFF_MS, message },
4692
+ { until: now + FAILURE_BACKOFF_MS, message, status },
4684
4693
  MAX_ACCOUNT_STATES
4685
4694
  );
4686
4695
  }
@@ -4689,7 +4698,7 @@ function usageExtension(pi, dependencies = {}) {
4689
4698
  providerId: adapter.id,
4690
4699
  providerName,
4691
4700
  displayState,
4692
- status: "query-failed",
4701
+ status,
4693
4702
  message
4694
4703
  },
4695
4704
  fingerprint: auth.fingerprint,