dsh-grok-subscription 2.0.0 → 2.0.1

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,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.1 — 2026-09-29
4
+
5
+ - Fix **the quota card reporting a raw parser error for an account the upstream API stopped describing with a percentage**. Since the weekly period rolled over on 2026-09-28T16:46Z, `GET /v1/billing?format=credits` answers with a `config` object that carries `isUnifiedBillingUser`, `prepaidBalance`, `onDemandCap`, `onDemandUsed`, `topUpMethod` and the period bounds — and no `creditUsagePercent`, no `productUsage`, and no used/total pair. Every reader in the plugin is unchanged since 1.0.20, so the card simply failed closed with `不可用: Missing creditUsagePercent (keys: config)`, which reads like a broken plugin rather than an upstream shape the plugin does not recognise. Reproduced on a second machine with the same account: same build, same headers, only the clock moved.
6
+ - Name the shape instead of a missing field. The unavailable reading now carries a `code` (`unified-billing` or `missing-percent`) plus the period bounds, the panel renders localized copy for a named code and keeps the raw diagnostic as a hint underneath, and an unnamed failure still shows its own reason.
7
+ - **Show the window even when there is no percentage.** The period start and end come from the same response, so the card now prints `本期窗口: 2026/09/29 00:46:29 → 2026/10/06 00:46:29` whether or not a reading came back. A reset time is never lost to a shape change.
8
+ - Still no invented numbers. A zero prepaid balance and a zero on-demand cap are not a percentage, so the card shows no gauge and the composer badge stays hidden; `parseBillingCredits` is asserted against the captured live body to keep it that way.
9
+ - No adapter, routing, session or RPC-surface changes. `sanitizeUsage` carries the new diagnostic fields through the RPC without exposing anything else.
10
+
11
+ ## 2.0.0 — 2026-09-28
12
+
3
13
  ## 2.0.0 — 2026-09-28
4
14
 
5
15
  - **Remove the bundled Responses adapter. The `grok-build` route is now always the host's own pi-ai adapter.** The bundled implementation and everything that existed only to support it are gone: its request building, encrypted-reasoning envelope, prompt-cache and usage mapping and image collection (`src/adapter.js` drops from 1081 to 467 lines), the synchronous duck registration, `createGrokBuildAdapterSync` / `createDuckHostAdapter`, the release-then-re-register upgrade 1.2.0 added, and the `DSH_GROK_ADAPTER=fallback` switch. `apply` still returns immediately and builds the adapter from a deferred task, but registers exactly once.
package/README.en.md CHANGED
@@ -61,13 +61,13 @@ Pick a Grok model your account can use, adjust reasoning effort as needed, and s
61
61
  <details>
62
62
  <summary>Check the version / pin one / fall back when the UI install fails</summary>
63
63
 
64
- **Settings → Plugins** lists the installed plugins and their versions, and the plugin's own **Settings → Grok Subscription** page shows a version card. To pin a version, put the full `package@version` in the install field, for example `dsh-grok-subscription@2.0.0`.
64
+ **Settings → Plugins** lists the installed plugins and their versions, and the plugin's own **Settings → Grok Subscription** page shows a version card. To pin a version, put the full `package@version` in the install field, for example `dsh-grok-subscription@2.0.1`.
65
65
 
66
66
  If the UI install fails (an unusual profile directory, say), the equivalent runs against the desktop profile itself (pnpm ≥ 11):
67
67
 
68
68
  ```sh
69
69
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
70
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
70
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
71
71
  ```
72
72
 
73
73
  `dsh plugin --profile desktop …` is refused: the Electron application owns that profile exclusively. CLI paths, sign-in, and troubleshooting are in the [full user guide](docs/guide.en.md).
package/README.md CHANGED
@@ -61,13 +61,13 @@
61
61
  <details>
62
62
  <summary>确认版本 / 固定版本 / 界面安装失败时的兜底</summary>
63
63
 
64
- **设置 → 插件** 会列出已安装插件与版本,插件自己的 **设置 → Grok 订阅** 页也有版本卡片。要固定版本,在安装框里填入完整的 `包名@版本`,例如 `dsh-grok-subscription@2.0.0`。
64
+ **设置 → 插件** 会列出已安装插件与版本,插件自己的 **设置 → Grok 订阅** 页也有版本卡片。要固定版本,在安装框里填入完整的 `包名@版本`,例如 `dsh-grok-subscription@2.0.1`。
65
65
 
66
66
  界面安装失败时(例如 profile 目录权限异常),可以对桌面版 profile 手动执行等价操作(pnpm ≥ 11):
67
67
 
68
68
  ```sh
69
69
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
70
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
70
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
71
71
  ```
72
72
 
73
73
  `dsh plugin --profile desktop …` 会被拒绝:桌面版 profile 由 Electron 应用独占。CLI 路径、登录方式与故障排查见 [完整使用指南](docs/guide.zh-CN.md)。
package/docs/guide.en.md CHANGED
@@ -8,7 +8,7 @@ For quick setup, see the [README](../README.en.md#install). This guide covers de
8
8
 
9
9
  You need the DeepSeek Harness desktop app, the official Grok Build CLI, and an account with Grok Build access. The plugin declares Node.js `^22.19.0 || >=24.0.0`; DSH peers use `>=0.1.5-rc.2 <0.3.0-0`, which spans the whole 0.1 and 0.2 lines. See [package.json](../package.json) for the full requirements. Declared compatibility is not a claim that every version has passed live verification.
10
10
 
11
- **Install:** in the desktop app open **Settings → Plugins**, type the package name `dsh-grok-subscription` into the install field, and install it. Then **quit and restart the desktop app completely**; refreshing the page alone does not reload the Host adapter. To pin a version, put the full `package@version` in that field, for example `dsh-grok-subscription@2.0.0`.
11
+ **Install:** in the desktop app open **Settings → Plugins**, type the package name `dsh-grok-subscription` into the install field, and install it. Then **quit and restart the desktop app completely**; refreshing the page alone does not reload the Host adapter. To pin a version, put the full `package@version` in that field, for example `dsh-grok-subscription@2.0.1`.
12
12
 
13
13
  **The plugin depends on the host's own pi-ai adapter.** From 2.0.0 the `grok-build` route is served entirely by the host's `@deepseek-ai/dsh-llm-pi-ai` / `@earendil-works/pi-ai`, which every DSH installation carries (`@deepseek-ai/dsh` → `dsh-base` → `dsh-llm-pi-ai`, with the `llm-pi-ai` row mounted unconditionally). If those packages cannot be resolved from the DSH installation, the plugin registers no route at all and **Active path** in Settings reads **unavailable**.
14
14
 
@@ -16,7 +16,7 @@ If the UI install fails (an unusual profile directory, say), the equivalent runs
16
16
 
17
17
  ```sh
18
18
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
19
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
19
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
20
20
  ```
21
21
 
22
22
  `dsh plugin --profile desktop …` is refused: the Electron application owns that profile exclusively.
@@ -101,7 +101,7 @@ In the desktop app the `desktop` profile is owned exclusively by the Electron ap
101
101
 
102
102
  ```sh
103
103
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
104
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
104
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
105
105
  ```
106
106
 
107
107
  Then quit and restart the desktop app completely. After a successful update the profile's `package.json` dependency should show the target version while its `dsh.profile.bundles` entry is unchanged. Refreshing the browser is not enough: without a Host restart the old version keeps loading.
@@ -8,7 +8,7 @@
8
8
 
9
9
  需要 DeepSeek Harness 桌面版、官方 Grok Build CLI,以及当前具有 Grok Build 使用资格的订阅账号。插件的 Node.js 要求为 `^22.19.0 || >=24.0.0`,DSH peer 声明以 [package.json](../package.json) 为准;其中 DSH 依赖使用 `>=0.1.5-rc.2 <0.3.0-0`,覆盖 0.1 与 0.2 全线。声明范围不等于所有版本都已经过实机验证。
10
10
 
11
- **安装:** 在桌面版打开 **设置 → 插件**,在安装输入框里填入包名 `dsh-grok-subscription` 并安装,然后**完全退出并重启桌面应用**。仅刷新页面不会重新加载 Host 中的适配器。需要可重复安装时,填入完整的 `包名@版本`,例如 `dsh-grok-subscription@2.0.0`。
11
+ **安装:** 在桌面版打开 **设置 → 插件**,在安装输入框里填入包名 `dsh-grok-subscription` 并安装,然后**完全退出并重启桌面应用**。仅刷新页面不会重新加载 Host 中的适配器。需要可重复安装时,填入完整的 `包名@版本`,例如 `dsh-grok-subscription@2.0.1`。
12
12
 
13
13
  **插件依赖宿主自带的 pi-ai 适配器。** 从 2.0.0 起,`grok-build` 路由完全由宿主的 `@deepseek-ai/dsh-llm-pi-ai` / `@earendil-works/pi-ai` 服务;DSH 的任何安装方式都带有它们(`@deepseek-ai/dsh` → `dsh-base` → `dsh-llm-pi-ai`,且 `llm-pi-ai` 行无条件挂载)。这两个包无法从 DSH 安装目录解析时,插件不会注册任何路由,设置页的「运行通路」显示**不可用**。
14
14
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  ```sh
18
18
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
19
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
19
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
20
20
  ```
21
21
 
22
22
  `dsh plugin --profile desktop …` 会被直接拒绝:桌面版 profile 由 Electron 应用独占。
@@ -101,7 +101,7 @@ grok-4.6
101
101
 
102
102
  ```sh
103
103
  cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
104
- pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.0
104
+ pnpm add --save-exact --config.minimumReleaseAge=0 dsh-grok-subscription@2.0.1
105
105
  ```
106
106
 
107
107
  然后完全退出并重启桌面版。更新成功后该 profile 的 `package.json` 里依赖应变为目标版本,`dsh.profile.bundles` 条目保持不变。仅刷新网页不够:Host 未重启时仍会加载旧版本。
package/lib/client.js CHANGED
@@ -39,7 +39,7 @@ var import_react2 = require("react");
39
39
  var zh = {
40
40
  nav: "Grok \u8BA2\u9605",
41
41
  title: "Grok \u8BA2\u9605",
42
- subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 2.0.0",
42
+ subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 2.0.1",
43
43
  signedIn: "\u5DF2\u767B\u5F55",
44
44
  signedOut: "\u672A\u767B\u5F55",
45
45
  account: "\u8D26\u6237",
@@ -86,6 +86,9 @@ var zh = {
86
86
  usageUsed: "\u5DF2\u7528",
87
87
  usageRemaining: "\u5269\u4F59\u7EA6",
88
88
  usageReset: "\u5468\u671F\u7ED3\u675F / \u91CD\u7F6E",
89
+ usageWindow: "\u672C\u671F\u7A97\u53E3",
90
+ usageUnifiedBilling: "\u4E0A\u6E38\u672A\u8FD4\u56DE\u989D\u5EA6\u767E\u5206\u6BD4\uFF1A\u8BE5\u8D26\u53F7\u5DF2\u662F\u7EDF\u4E00\u8BA1\u8D39\u5F62\u6001\uFF08\u4EE5\u9884\u4ED8\u4F59\u989D\u4E0E\u6309\u91CF\u4E0A\u9650\u8BA1\u91CF\uFF09\uFF0C\u56E0\u6B64\u6CA1\u6709\u53EF\u5C55\u793A\u7684\u5468\u989D\u5EA6\u8BFB\u6570\u3002",
91
+ usageMissingPercent: "\u4E0A\u6E38\u672A\u8FD4\u56DE\u989D\u5EA6\u767E\u5206\u6BD4\uFF0C\u672C\u671F\u6682\u65E0\u53EF\u7528\u8BFB\u6570\u3002",
89
92
  usageUnavailable: "\u4E0D\u53EF\u7528",
90
93
  usageRefresh: "\u5237\u65B0\u7528\u91CF",
91
94
  usageOpenGrok: "\u5728 grok.com \u67E5\u770B\u7528\u91CF",
@@ -120,7 +123,7 @@ var zh = {
120
123
  var en = {
121
124
  nav: "Grok Subscription",
122
125
  title: "Grok Subscription",
123
- subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 2.0.0",
126
+ subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 2.0.1",
124
127
  signedIn: "Signed in",
125
128
  signedOut: "Signed out",
126
129
  account: "Account",
@@ -167,6 +170,9 @@ var en = {
167
170
  usageUsed: "Used",
168
171
  usageRemaining: "Remaining \u2248",
169
172
  usageReset: "Period end / reset",
173
+ usageWindow: "Current window",
174
+ usageUnifiedBilling: "The upstream API returned no credit percentage: this account is on unified billing (measured by prepaid balance and an on-demand cap), so there is no weekly credit reading to show.",
175
+ usageMissingPercent: "The upstream API returned no credit percentage for this period.",
170
176
  usageUnavailable: "Unavailable",
171
177
  usageRefresh: "Refresh usage",
172
178
  usageOpenGrok: "View usage on grok.com",
@@ -830,6 +836,7 @@ function VersionCard({ call, t }) {
830
836
  ] }) : null
831
837
  ] });
832
838
  }
839
+ var EXPLAINED_USAGE_CODES = /* @__PURE__ */ new Set(["unified-billing", "missing-percent"]);
833
840
  function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
834
841
  const ok = usage?.status === "ok";
835
842
  const usedNumber = ok && Number.isFinite(usage.usedPercent) ? usage.usedPercent : void 0;
@@ -867,9 +874,16 @@ function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
867
874
  ] }) : null,
868
875
  usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }) : null
869
876
  ] })
870
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsEmpty", children: [
871
- t("usageUnavailable"),
872
- usage?.reason ? `: ${usage.reason}` : ""
877
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsEmpty", children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: EXPLAINED_USAGE_CODES.has(usage?.code) ? t(usage.code === "unified-billing" ? "usageUnifiedBilling" : "usageMissingPercent") : `${t("usageUnavailable")}${usage?.reason ? `: ${usage.reason}` : ""}` }),
879
+ usage?.periodStartLocal || usage?.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsHint", children: [
880
+ t("usageWindow"),
881
+ ": ",
882
+ usage?.periodStartLocal ?? usage?.periodStart ?? "\u2014",
883
+ " \u2192 ",
884
+ usage?.periodEndLocal ?? usage?.periodEnd ?? "\u2014"
885
+ ] }) : null,
886
+ EXPLAINED_USAGE_CODES.has(usage?.code) && usage?.reason ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.reason }) }) : null
873
887
  ] }),
874
888
  products.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsRows", children: products.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
875
889
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: row.name ?? "\u2014" }),
package/lib/index.js CHANGED
@@ -457,10 +457,11 @@ async function loadCatalog(accessToken, options = {}) {
457
457
  // src/usage.js
458
458
  var USAGE_SOURCE = "billing-credits-undocumented";
459
459
  var SHANGHAI_TZ = "Asia/Shanghai";
460
- function unavailable(reason) {
460
+ function unavailable(reason, extra = {}) {
461
461
  return Object.freeze({
462
462
  status: "unavailable",
463
463
  reason: typeof reason === "string" && reason.trim() ? reason.trim() : "Usage unavailable",
464
+ ...extra,
464
465
  experimental: true,
465
466
  source: USAGE_SOURCE
466
467
  });
@@ -516,6 +517,23 @@ function missingPercentReason(body) {
516
517
  }
517
518
  return `Missing creditUsagePercent (keys: ${keys.join(",")})`;
518
519
  }
520
+ function isUnifiedBillingBody(body) {
521
+ const config = isPlainObject(body.config) ? body.config : void 0;
522
+ return config?.isUnifiedBillingUser === true || isPlainObject(config?.onDemandCap) || isPlainObject(config?.prepaidBalance);
523
+ }
524
+ function unifiedBillingReason(body) {
525
+ const config = isPlainObject(body.config) ? body.config : {};
526
+ const balance = asFiniteNumber(config.prepaidBalance);
527
+ const cap = asFiniteNumber(config.onDemandCap);
528
+ return `No credit percentage in this billing response (unified billing: prepaidBalance ${balance ?? "n/a"}, onDemandCap ${cap ?? "n/a"})`;
529
+ }
530
+ function periodFields(body) {
531
+ const { start, end } = resolvePeriodBounds(body);
532
+ return {
533
+ ...start ? { periodStart: start, periodStartLocal: formatShanghai(start) } : {},
534
+ ...end ? { periodEnd: end, periodEndLocal: formatShanghai(end) } : {}
535
+ };
536
+ }
519
537
  function firstDefined(...values) {
520
538
  for (const value of values) {
521
539
  if (value !== void 0 && value !== null) return value;
@@ -629,7 +647,14 @@ function parseBillingCredits(body) {
629
647
  }
630
648
  const usedPercent = resolveUsedPercent(body);
631
649
  if (usedPercent === void 0) {
632
- return unavailable(missingPercentReason(body));
650
+ const unified = isUnifiedBillingBody(body);
651
+ return unavailable(
652
+ unified ? unifiedBillingReason(body) : missingPercentReason(body),
653
+ {
654
+ code: unified ? "unified-billing" : "missing-percent",
655
+ ...periodFields(body)
656
+ }
657
+ );
633
658
  }
634
659
  const { start: periodStart, end: periodEnd } = resolvePeriodBounds(body);
635
660
  const productUsage = sanitizeProductUsage(resolveProductUsage(body));
@@ -1936,6 +1961,10 @@ function sanitizeUsage(usage) {
1936
1961
  };
1937
1962
  if (out.status !== "ok") {
1938
1963
  out.reason = typeof usage.reason === "string" ? usage.reason : "Usage unavailable";
1964
+ if (typeof usage.code === "string") out.code = usage.code;
1965
+ for (const field of ["periodStart", "periodStartLocal", "periodEnd", "periodEndLocal"]) {
1966
+ if (typeof usage[field] === "string") out[field] = usage[field];
1967
+ }
1939
1968
  return out;
1940
1969
  }
1941
1970
  if (typeof usage.usedPercent === "number" && Number.isFinite(usage.usedPercent)) out.usedPercent = usage.usedPercent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-grok-subscription",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Use a SuperGrok or X Premium Grok Build subscription in DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",