@bacnh85/pi-sub 0.1.24 → 0.1.28

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/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.28 (2026-08-09)
4
+
5
+ ### Improvements
6
+
7
+ - **Command Code monthly balance now shown in the footer.** The footer adds a
8
+ compact `M:$X.XX` segment (monthly credit balance from
9
+ `/alpha/billing/credits`) next to the 5-hour/weekly windows, e.g.
10
+ `(Command Code key#…) R:100%/5H W:100%/7D M:$69.99`. The `/sub` detail view
11
+ keeps the `Monthly: $X remaining` breakdown line.
12
+
13
+ ## 0.1.27 (2026-08-09)
14
+
15
+ ### Features
16
+
17
+ - **Command Code now shows live usage windows.** The adapter fetches
18
+ `https://api.commandcode.ai/alpha/billing/credits` with the same Provider
19
+ API key used for `/provider/v1` models and renders the 5-hour and weekly
20
+ rolling windows (USD used/cap, remaining% + reset countdown) plus a
21
+ `Monthly: $X remaining` balance line — matching what the `cmd /usage` CLI
22
+ shows. Previously the footer showed session cost only.
23
+
24
+ ## 0.1.26 (2026-08-09)
25
+
26
+ ### Features
27
+
28
+ - Added Command Code (`commandcode`) provider support. When a `commandcode`
29
+ model is active, the footer shows the account label plus session cost and
30
+ tok/s, and `/sub` reports the provider. Command Code's Provider API does not
31
+ expose usage windows via its API key (the rolling-window data is
32
+ web-session-cookie only), so live 5h/weekly meters are not shown — behavior
33
+ matches `opencode-go` and `9router`.
34
+
35
+ ## 0.1.25 (2026-08-07)
36
+
37
+ ### Improvements
38
+
39
+ - Widen peer dependency range to support Pi 0.84.0 (`>=0.80.8 <0.85.0`).
40
+ No code changes — verified compatible against the 0.84.0 SDK types.
41
+
3
42
  ## 0.1.24 (2026-08-05)
4
43
 
5
44
  ### Improvements
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Pi extension that shows subscription usage for the currently selected supported model provider.
4
4
 
5
- Supports OpenAI Codex (`openai-codex`) with live usage windows from ChatGPT's usage endpoint, OpenCode Go (`opencode-go`) with session cost tracking, and Z.ai GLM Coding Plan — both the international (`zai`) and China (`zai-coding-cn`, `open.bigmodel.cn`) endpoints — with quota monitoring. Displays a subscription footer status after Pi's built-in status/token usage line.
5
+ Supports OpenAI Codex (`openai-codex`) with live usage windows from ChatGPT's usage endpoint, OpenCode Go (`opencode-go`) with session cost tracking, and Z.ai GLM Coding Plan — both the international (`zai`) and China (`zai-coding-cn`, `open.bigmodel.cn`) endpoints — with quota monitoring. Also tracks Command Code (`commandcode`) 5-hour/weekly windows and monthly credit balance. Displays a subscription footer status after Pi's built-in status/token usage line.
6
6
 
7
7
  ## Install
8
8
 
@@ -59,6 +59,18 @@ The built-in `zai-coding-cn` provider targets the domestic BigModel endpoint (`o
59
59
  9router (172.30.55.22:20128) 145 tok/s
60
60
  ```
61
61
 
62
+ ### Command Code
63
+
64
+ Command Code exposes live usage windows via its `/alpha/billing/credits` endpoint (same Provider API key used for `/provider/v1` models — no cookies). The footer shows the active account/key label, 5-hour and weekly remaining windows with reset countdowns, and last response speed:
65
+
66
+ ```text
67
+ (Command Code key#1a2b3c4d) R:99%/4H W:99%/6D M:$69.99 42 tok/s
68
+ ```
69
+
70
+ The `M:$X.XX` segment is the monthly credit balance (the plan's remaining
71
+ monthly allowance in USD). The `/sub` detail view also shows a
72
+ `Monthly: $X remaining` line.
73
+
62
74
  ### Tokens per second
63
75
 
64
76
  `pi-sub` tracks each response's tokens-per-second (tok/s) speed by measuring the time from provider request to message completion against the response's output token count. The last response's speed is shown in the footer next to usage data. The `/sub` detail view shows both the last response speed and the session-wide average.
@@ -98,6 +110,20 @@ Last response: 42 tok/s · Session avg: 39 tok/s
98
110
  OpenCode Go does not expose usage windows.
99
111
  ```
100
112
 
113
+ For Command Code, `/sub` shows the provider/model, active account/key label, rolling windows, and speed:
114
+
115
+ ```text
116
+ Provider: Command Code · Model: deepseek/deepseek-v4-flash · Fetched: 14:23
117
+ Session cost: $0.05
118
+ Last response: 42 tok/s · Session avg: 39 tok/s
119
+
120
+ ACCOUNT ROLLING WEEKLY LAST ACTIVITY
121
+ -----------------------------------------------------------------
122
+ * Command Code key#1a2b3c4d 99%/4H 99%/6D Now
123
+
124
+ Monthly: $69.99 remaining
125
+ ```
126
+
101
127
  For Z.ai, `/sub` shows the rolling and weekly quota windows and speed:
102
128
 
103
129
  ```text
@@ -130,6 +156,7 @@ Refreshes are cached briefly to avoid excessive usage endpoint calls.
130
156
 
131
157
  - **OpenAI Codex**: Pi auth must contain an `openai-codex` OAuth entry in `~/.pi/agent/auth.json` or `$PI_CODING_AGENT_DIR/auth.json`. The entry must include `access` and `accountId` fields.
132
158
  - **OpenCode Go**: Pi auth must contain an `opencode-go` API key entry (via `/login` or env var). The entry must have a `key` field or an `accountId` field. OpenCode Go/Zen usage windows and Zen balance are not shown because no public API is currently documented for those values.
159
+ - **Command Code**: Pi auth must contain a `commandcode` API key entry (via `/login commandcode` or the `COMMAND_CODE_API_KEY` env var). The entry must have a `key` field or an `accountId` field. Usage is read from `https://api.commandcode.ai/alpha/billing/credits` with the same key; 5-hour and weekly windows plus the monthly credit balance are displayed.
133
160
  - **Z.ai**: Pi auth must contain a `zai` entry in `auth.json` with a `key` field (the same API key used for Z.ai model access via `@czottmann/pi-zai-api`). The Z.ai provider must be registered (e.g., `pi install npm:@czottmann/pi-zai-api`).
134
161
  - **Z.ai Coding Plan (China)**: The built-in `zai-coding-cn` provider targets `https://open.bigmodel.cn/api/coding/paas/v4`. Pi auth must contain a `zai-coding-cn` entry with a `key` field (set via `/login` or the `ZAI_CODING_CN_API_KEY` env var). Quota is read from the BigModel endpoint `https://open.bigmodel.cn/api/monitor/usage/quota/limit`.
135
162
  - For API-key-only providers, account labels come from stored auth metadata (`email`, `label`, `name`, or `accountId`) when available; otherwise `pi-sub` displays a non-secret SHA-256 key fingerprint such as `Z.ai key#1a2b3c4d`.
@@ -141,4 +168,4 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
141
168
 
142
169
  ## Design notes
143
170
 
144
- The extension is named `pi-sub` rather than `pi-codex-usage` so future subscription providers can be added as separate adapters. Supports OpenAI Codex (live usage API), OpenCode Go (session cost only), and the Z.ai GLM Coding Plan (international `zai` and China `zai-coding-cn`, which share a quota response format and are served by one parameterized adapter).
171
+ The extension is named `pi-sub` rather than `pi-codex-usage` so future subscription providers can be added as separate adapters. Supports OpenAI Codex (live usage API), OpenCode Go (session cost only), the Z.ai GLM Coding Plan (international `zai` and China `zai-coding-cn`, which share a quota response format and are served by one parameterized adapter), and Command Code (live 5-hour/weekly windows + monthly balance via `/alpha/billing/credits`).
@@ -18,6 +18,8 @@ const ZAI_CODING_CN_PROVIDER = "zai-coding-cn";
18
18
  const ZAI_CODING_CN_USAGE_URL = "https://open.bigmodel.cn/api/monitor/usage/quota/limit";
19
19
  const NINE_ROUTER_PROVIDER = "9router";
20
20
  const NINE_ROUTER_CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "9router-config.json");
21
+ const COMMAND_CODE_PROVIDER = "commandcode";
22
+ const COMMAND_CODE_USAGE_URL = "https://api.commandcode.ai/alpha/billing/credits";
21
23
 
22
24
  type ModelLike = { provider?: string; id?: string } | undefined;
23
25
 
@@ -59,6 +61,8 @@ interface SubscriptionAccountSnapshot {
59
61
  plan?: string;
60
62
  fiveHour?: UsageWindow;
61
63
  weekly?: UsageWindow;
64
+ // Command Code-only: monthly credit balance in USD (not a rolling window).
65
+ monthlyCredits?: number;
62
66
  // Z.ai-only extras surfaced in the /sub detail view.
63
67
  mcpMonthly?: UsageWindow; // from TIME_LIMIT already present in the quota response
64
68
  usageBreakdown?: string; // per-model / per-tool summary line(s)
@@ -117,6 +121,10 @@ function isNineRouterModel(model: ModelLike): boolean {
117
121
  return (model?.provider?.toLowerCase() ?? "") === NINE_ROUTER_PROVIDER;
118
122
  }
119
123
 
124
+ function isCommandCodeModel(model: ModelLike): boolean {
125
+ return (model?.provider?.toLowerCase() ?? "") === COMMAND_CODE_PROVIDER;
126
+ }
127
+
120
128
  function piAuthPath(): string {
121
129
  const configDir = process.env.PI_CODING_AGENT_DIR?.trim() || path.join(os.homedir(), ".pi", "agent");
122
130
  return path.join(configDir, "auth.json");
@@ -340,6 +348,7 @@ function redactedError(error: unknown, provider = "Codex"): string {
340
348
  if (/missing openai-codex/i.test(message)) return "openai-codex auth not found";
341
349
  if (/missing opencode-go/i.test(message)) return "opencode-go auth not found";
342
350
  if (/missing zai/i.test(message)) return "zai auth not found";
351
+ if (/missing commandcode/i.test(message)) return "commandcode auth not found";
343
352
  if (/timed out|timeout|aborted/i.test(message)) return `${provider} usage refresh timed out`;
344
353
  if (/401|403|auth|token|unauthorized|forbidden/i.test(message)) return `${provider} auth unavailable`;
345
354
  return `${provider} usage unavailable`;
@@ -411,6 +420,60 @@ async function fetchNineRouterUsage(_signal?: AbortSignal): Promise<Subscription
411
420
  };
412
421
  }
413
422
 
423
+ // Command Code's /alpha/billing/credits endpoint (auth: same Provider API key
424
+ // as /provider/v1 models) returns live 5-hour and weekly rolling windows plus
425
+ // the monthly credit balance — the same data as the cmd /usage CLI.
426
+ async function fetchCommandCodeUsage(signal?: AbortSignal): Promise<SubscriptionUsageSnapshot> {
427
+ try {
428
+ const { key: apiKey, account: authAccount } = await readZaiAuth(COMMAND_CODE_PROVIDER, "Command Code");
429
+ const timeoutSignal = AbortSignal.timeout(7_000);
430
+ const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
431
+
432
+ const headers = {
433
+ Accept: "application/json",
434
+ Authorization: `Bearer ${apiKey}`,
435
+ "User-Agent": "pi-sub/0.1.27",
436
+ };
437
+ const response = await fetch(COMMAND_CODE_USAGE_URL, { headers, signal: combinedSignal });
438
+ if (!response.ok) {
439
+ throw new Error(`Command Code usage request failed with HTTP ${response.status}`);
440
+ }
441
+ const body = (await response.json()) as CommandCodeCreditsApiResponse;
442
+
443
+ const credits = body.credits;
444
+ const windowLimits = body.windowLimits;
445
+ const fiveHour = commandCodeWindowToUsageWindow(windowLimits?.fiveHour);
446
+ const weekly = commandCodeWindowToUsageWindow(windowLimits?.weekly);
447
+
448
+ // Monthly allowance is an absolute USD balance, not a rolling window.
449
+ const monthlyCredits = typeof credits?.monthlyCredits === "number" ? credits.monthlyCredits : undefined;
450
+ const monthlyLine = monthlyCredits !== undefined ? `Monthly: $${monthlyCredits.toFixed(2)} remaining` : undefined;
451
+ const breakdown = [monthlyLine].filter((s): s is string => !!s);
452
+
453
+ const account: SubscriptionAccountSnapshot = {
454
+ ...authAccount,
455
+ fiveHour,
456
+ weekly,
457
+ monthlyCredits,
458
+ usageBreakdown: breakdown.length > 0 ? breakdown.join("\n") : undefined,
459
+ };
460
+
461
+ return {
462
+ providerDisplayName: "Command Code",
463
+ accounts: [account],
464
+ activeAccount: account,
465
+ fetchedAt: Date.now(),
466
+ };
467
+ } catch (error) {
468
+ return {
469
+ providerDisplayName: "Command Code",
470
+ accounts: [],
471
+ fetchedAt: Date.now(),
472
+ error: redactedError(error, "Command Code"),
473
+ };
474
+ }
475
+ }
476
+
414
477
  // ---------------------------------------------------------------------------
415
478
  // Z.ai adapter
416
479
  // ---------------------------------------------------------------------------
@@ -438,6 +501,48 @@ interface ZaiUsageApiError {
438
501
  success?: boolean;
439
502
  }
440
503
 
504
+ // ---------------------------------------------------------------------------
505
+ // Command Code adapter
506
+ // ---------------------------------------------------------------------------
507
+ // Live-verified 2026-08-09: GET https://api.commandcode.ai/alpha/billing/credits
508
+ // with the Provider API key (same user_... key as /provider/v1 models) returns
509
+ // USD windows + monthly credit balance. resetAt is epoch milliseconds.
510
+
511
+ interface CommandCodeWindowApi {
512
+ used: number;
513
+ cap: number;
514
+ exceeded?: boolean | null;
515
+ resetAt?: number;
516
+ }
517
+
518
+ interface CommandCodeCreditsApiResponse {
519
+ credits?: {
520
+ monthlyCredits?: number;
521
+ purchasedCredits?: number;
522
+ freeCredits?: number;
523
+ belowThreshold?: boolean;
524
+ };
525
+ windowLimits?: {
526
+ limited?: boolean;
527
+ fiveHour?: CommandCodeWindowApi;
528
+ weekly?: CommandCodeWindowApi;
529
+ };
530
+ }
531
+
532
+ /** Map a Command Code USD window (used/cap in dollars, resetAt in ms) into
533
+ * the shared UsageWindow shape (remaining%, reset labels). */
534
+ function commandCodeWindowToUsageWindow(window: CommandCodeWindowApi | undefined): UsageWindow | undefined {
535
+ if (!window || typeof window.used !== "number" || typeof window.cap !== "number" || window.cap <= 0) return undefined;
536
+ const usedPct = Math.round((window.used / window.cap) * 100);
537
+ const percent = Math.min(100, usedPct);
538
+ const remaining = Math.max(0, 100 - percent);
539
+ // resetAt is epoch ms; format helpers expect seconds.
540
+ const resetAtSec = window.resetAt ? window.resetAt / 1000 : undefined;
541
+ const resetLabel = formatReset(resetAtSec);
542
+ const remainingLabel = formatRemainingTime(resetAtSec);
543
+ return { percent, remaining, remainingLabel, resetLabel };
544
+ }
545
+
441
546
  function zaiLimitToUsageWindow(limit: ZaiLimitEntry): UsageWindow | undefined {
442
547
  if (typeof limit.percentage !== "number") return undefined;
443
548
  const percent = Math.round(limit.percentage);
@@ -600,6 +705,7 @@ function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undef
600
705
  if (isZaiModel(model)) return { id: ZAI_PROVIDER, displayName: "Z.ai", ...zaiUsageAdapter(ZAI_PROVIDER, ZAI_USAGE_URL, "Z.ai") };
601
706
  if (isZaiCodingCnModel(model)) return { id: ZAI_CODING_CN_PROVIDER, displayName: "Z.ai (CN)", ...zaiUsageAdapter(ZAI_CODING_CN_PROVIDER, ZAI_CODING_CN_USAGE_URL, "Z.ai (CN)") };
602
707
  if (isNineRouterModel(model)) return { id: NINE_ROUTER_PROVIDER, displayName: "9router", fetchUsage: fetchNineRouterUsage };
708
+ if (isCommandCodeModel(model)) return { id: COMMAND_CODE_PROVIDER, displayName: "Command Code", fetchUsage: fetchCommandCodeUsage };
603
709
  return undefined;
604
710
  }
605
711
 
@@ -647,6 +753,8 @@ function renderSubscriptionLine(ctx: ExtensionContext, state: State): void {
647
753
  const segments = accountPart ? [accountPart, ...windowParts] : [...windowParts];
648
754
  const cost = state.cumulativeCost;
649
755
  const hasWindows = windowParts.length > 0;
756
+ // Command Code monthly balance: compact USD figure, e.g. M:$69.99.
757
+ if (typeof account?.monthlyCredits === "number") segments.push(`M:$${account.monthlyCredits.toFixed(2)}`);
650
758
  if (cost > 0) segments.push(`$${cost.toFixed(2)}`);
651
759
  if (state.lastTokPerSec !== undefined) segments.push(`${state.lastTokPerSec} tok/s`);
652
760
  if (segments.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacnh85/pi-sub",
3
- "version": "0.1.24",
3
+ "version": "0.1.28",
4
4
  "description": "Pi extension showing subscription usage for supported model providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,6 +39,6 @@
39
39
  "node": ">=20.3.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "@earendil-works/pi-coding-agent": ">=0.80.8 <0.84.0"
42
+ "@earendil-works/pi-coding-agent": ">=0.80.8 <0.85.0"
43
43
  }
44
44
  }