@bacnh85/pi-sub 0.1.32 → 0.1.35

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,12 +1,30 @@
1
+ # Changelog
2
+
3
+ ## 0.1.35 (2026-09-05)
4
+
5
+ ### Added
6
+
7
+ - **`zai-anthropic` provider usage tracking** — GLM through the Anthropic-compatible endpoint (`api.z.ai/api/anthropic`, registered by pi-model-tools) now shows the same quota footer as `zai`/`zai-coding-cn`: 5-hour/weekly windows, MCP monthly allowance, and model/tool breakdowns, keyed by the auth.json `zai-anthropic` credential, labeled `Z.ai (Anthropic)`.
8
+
9
+ ## 0.1.34 (2026-09-05)
10
+
11
+ - Widen Pi SDK peer range to `>=0.80.8 <0.86.0` for Pi 0.85.0 compatibility (no breaking changes; peer cap widening only).
12
+
13
+ ## 0.1.33 (2026-08-29)
14
+
15
+ ### Added
16
+
17
+ - `/sub` argument completion offers `refresh`.
18
+
1
19
  ## 0.1.32 (2026-08-22)
2
20
 
3
21
  - **DeepSeek via OmniRoute now shows the real USD balance** (e.g. `M:$18.25`)
4
22
  instead of the misleading aggregate windows (R:100%/5H W:0%/2D). Credit-based
5
23
  upstreams report "Unavailable" session/weekly in the usage text; pi-sub now
6
24
  recognizes that shape and fetches `credits_usd` from the management usage
7
- API. Needs `ROUTER_MGMT_TOKEN` (an `oma_` CLI token or manage-scope key) in
8
- env / `~/.pi/agent/.env.local`; without it, the footer degrades to the clean
9
- endpoint display (no aggregate leak).
25
+ API grant the router key the `manage` scope in the OmniRoute dashboard
26
+ (done), or set `ROUTER_MGMT_TOKEN`. Without it, the footer degrades to the
27
+ clean endpoint display (no aggregate leak).
10
28
  - The plain-retry fallback (drop `?provider=`) now fires only on "No cached
11
29
  usage data" (wrong/unknown slug), not on "Unavailable" windows.
12
30
  - pi-sub now reads `.env.local`/`.env` (cwd then `~/.pi/agent`) for its own
@@ -66,6 +84,12 @@
66
84
 
67
85
  # Changelog
68
86
 
87
+ ## 0.1.33 (2026-08-29)
88
+
89
+ ### Added
90
+
91
+ - `/sub` argument completion offers `refresh`.
92
+
69
93
  ## 0.1.29 (2026-08-15)
70
94
 
71
95
  ### Features
package/README.md CHANGED
@@ -51,6 +51,14 @@ The built-in `zai-coding-cn` provider targets the domestic BigModel endpoint (`o
51
51
  (Z.ai (CN) key#1a2b3c4d) R:55%/2H W:80%/3D 42 tok/s
52
52
  ```
53
53
 
54
+ ### Z.ai via Anthropic endpoint
55
+
56
+ The `zai-anthropic` provider (registered by pi-model-tools, GLM through `api.z.ai/api/anthropic`) is tracked the same way — same api.z.ai quota monitor as the international `zai` provider, keyed by the auth.json `zai-anthropic` credential, labeled `Z.ai (Anthropic)`:
57
+
58
+ ```text
59
+ (Z.ai (Anthropic) key#1a2b3c4d) R:55%/2H W:80%/3D 42 tok/s
60
+ ```
61
+
54
62
  ### Router (pi-router — formerly 9router)
55
63
 
56
64
  For **OmniRoute** instances the footer shows real usage: `GET <origin>/api/usage/om-usage`
@@ -38,6 +38,13 @@ const ZAI_PROVIDER = "zai";
38
38
  const ZAI_USAGE_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
39
39
  const ZAI_CODING_CN_PROVIDER = "zai-coding-cn";
40
40
  const ZAI_CODING_CN_USAGE_URL = "https://open.bigmodel.cn/api/monitor/usage/quota/limit";
41
+ // GLM via the Anthropic-compatible endpoint (pi-model-tools `zai-anthropic`
42
+ // provider). Same api.z.ai host and quota monitor as the `zai` provider.
43
+ // ponytail: usage URL is fixed to api.z.ai — if ZAI_ANTHROPIC_BASE_URL is
44
+ // overridden to BigModel/zcode-plan, quota still reads from api.z.ai (correct
45
+ // for the z.ai coding-plan key; BigModel-plan keys should use zai-coding-cn).
46
+ const ZAI_ANTHROPIC_PROVIDER = "zai-anthropic";
47
+ const ZAI_ANTHROPIC_USAGE_URL = ZAI_USAGE_URL;
41
48
  const ROUTER_PROVIDER = "router";
42
49
  const LEGACY_9ROUTER_PROVIDER = "9router";
43
50
  // pi-router (formerly pi-9router): URL lives in settings.json `router.baseUrl`
@@ -142,6 +149,10 @@ function isZaiCodingCnModel(model: ModelLike): boolean {
142
149
  return (model?.provider?.toLowerCase() ?? "") === ZAI_CODING_CN_PROVIDER;
143
150
  }
144
151
 
152
+ function isZaiAnthropicModel(model: ModelLike): boolean {
153
+ return (model?.provider?.toLowerCase() ?? "") === ZAI_ANTHROPIC_PROVIDER;
154
+ }
155
+
145
156
  function isRouterModel(model: ModelLike, provider: string = ROUTER_PROVIDER): boolean {
146
157
  return (model?.provider?.toLowerCase() ?? "") === provider;
147
158
  }
@@ -362,11 +373,13 @@ function routerOrigin(baseUrl: string): string {
362
373
  return baseUrl.replace(/\/v1\/?$/, "");
363
374
  }
364
375
 
365
- /** OmniRoute management token (oma_ CLI token or manage-scope key) from env —
366
- * unlocks /api/usage/<connectionId> which carries the raw USD balance for
367
- * credit-based upstreams (deepseek) that the key-authable endpoints strip. */
368
- function readRouterMgmtToken(): string | undefined {
369
- return process.env.ROUTER_MGMT_TOKEN || process.env.OMNIROUTE_MGMT_TOKEN || undefined;
376
+ /** OmniRoute management credential (manage-scope key or oma_ CLI token) from
377
+ * env optional override. The router key itself works when it holds the
378
+ * `manage` scope (API Keys dashboard), which unlocks
379
+ * /api/usage/<connectionId> carrying the raw USD balance for credit-based
380
+ * upstreams (deepseek) that the key-authable endpoints normalize away. */
381
+ function readRouterMgmtToken(apiKey: string | undefined): string | undefined {
382
+ return process.env.ROUTER_MGMT_TOKEN || process.env.OMNIROUTE_MGMT_TOKEN || apiKey;
370
383
  }
371
384
 
372
385
  /** Parse OmniRoute's `/api/usage/om-usage` plain-text report into windows.
@@ -667,15 +680,15 @@ interface RouterCredits {
667
680
 
668
681
  /** Fetch the raw USD balance for credit-based upstreams (deepseek: `credits_usd`)
669
682
  * via OmniRoute's management usage API. Only called when the key-authable
670
- * om-usage text reports no usable windows — the API-key surface normalizes
671
- * credits to meaningless percentages, so this needs ROUTER_MGMT_TOKEN.
672
- * Uses connection discovery from /api/v1/me/status (key-authable) +
673
- * /api/usage/<id> (management token). */
683
+ * om-usage text reports no usable windows — that surface normalizes credits
684
+ * to meaningless percentages. Needs the router key to hold the `manage`
685
+ * scope (or ROUTER_MGMT_TOKEN as override). Connection discovery comes from
686
+ * /api/v1/me/status (key-authable); the balance from /api/usage/<id>. */
674
687
  async function fetchRouterCredits(provider: string | undefined, w: ReturnType<typeof parseOmniUsageText>): Promise<RouterCredits | undefined> {
675
688
  if (!provider || w.session || w.providerWeekly) return undefined;
676
689
  const cfg = readRouterConfig();
677
690
  const apiKey = readRouterApiKey();
678
- const mgmtToken = readRouterMgmtToken();
691
+ const mgmtToken = readRouterMgmtToken(apiKey);
679
692
  if (!cfg || !apiKey || !mgmtToken) return undefined;
680
693
  const origin = routerOrigin(cfg.baseUrl);
681
694
  try {
@@ -989,11 +1002,14 @@ function zaiUsageAdapter(providerId: string, usageUrl: string, displayName: stri
989
1002
  return { fetchUsage };
990
1003
  }
991
1004
 
992
- function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undefined {
1005
+ // Exported for the adapter-wiring regression test (provider-id string ↔
1006
+ // adapter id ↔ usage URL are exactly what a typo silently breaks).
1007
+ export function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undefined {
993
1008
  if (isCodexModel(model)) return { id: CODEX_PROVIDER, displayName: "Codex", fetchUsage: fetchCodexUsage };
994
1009
  if (isOpenCodeGoModel(model)) return { id: OPC_PROVIDER, displayName: "OpenCode Go", fetchUsage: fetchOpenCodeGoUsage };
995
1010
  if (isZaiModel(model)) return { id: ZAI_PROVIDER, displayName: "Z.ai", ...zaiUsageAdapter(ZAI_PROVIDER, ZAI_USAGE_URL, "Z.ai") };
996
1011
  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)") };
1012
+ if (isZaiAnthropicModel(model)) return { id: ZAI_ANTHROPIC_PROVIDER, displayName: "Z.ai (Anthropic)", ...zaiUsageAdapter(ZAI_ANTHROPIC_PROVIDER, ZAI_ANTHROPIC_USAGE_URL, "Z.ai (Anthropic)") };
997
1013
  if (isRouterModel(model)) {
998
1014
  const prefix = routerUpstreamPrefix(model);
999
1015
  return {
@@ -1293,6 +1309,12 @@ export default function (pi: ExtensionAPI) {
1293
1309
 
1294
1310
  pi.registerCommand("sub", {
1295
1311
  description: "Show subscription usage for the current supported model provider (use /sub refresh to force refresh).",
1312
+ getArgumentCompletions: (prefix) => {
1313
+ const items = ["refresh"]
1314
+ .filter((k) => k.startsWith(String(prefix || "").trim().toLowerCase()))
1315
+ .map((k) => ({ value: k, label: k, description: "force refresh" }));
1316
+ return items.length > 0 ? items : null;
1317
+ },
1296
1318
  handler: async (args, ctx) => {
1297
1319
  updateActiveAdapter(ctx, state, ctx.model);
1298
1320
  const command = args.trim().toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacnh85/pi-sub",
3
- "version": "0.1.32",
3
+ "version": "0.1.35",
4
4
  "description": "Pi extension showing subscription usage for supported model providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,6 +39,9 @@
39
39
  "node": ">=20.3.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "@earendil-works/pi-coding-agent": ">=0.80.8 <0.85.0"
42
+ "@earendil-works/pi-coding-agent": ">=0.80.8 <0.86.0"
43
+ },
44
+ "devDependencies": {
45
+ "@earendil-works/pi-server": "^0.85.1"
43
46
  }
44
47
  }