dsh-grok-subscription 1.0.2 → 1.0.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.en.md CHANGED
@@ -25,7 +25,7 @@ Models, reasoning effort, and weekly quota all stay inside DSH.
25
25
 
26
26
  ## Three-step start
27
27
 
28
- 1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.2`.
28
+ 1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.4`.
29
29
 
30
30
  ```sh
31
31
  dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
@@ -57,13 +57,13 @@ These capabilities reuse the same local Grok Build sign-in.
57
57
  <img src="docs/assets/settings-account.webp" width="820" alt="The Grok Subscription page in DSH Settings: sign-in status, account, login buttons and credential notes">
58
58
  </p>
59
59
 
60
- The **Settings → Grok Subscription** screen shows sign-in status and a masked account, offers CLI login, device-code login, pull from the Grok CLI, and logout, and explains how credentials are read. Account and time values in the screenshot are demo data.
60
+ The **Settings → Grok Subscription** screen uses a status chip for the sign-in state, and an account card offering CLI login, device-code login, pull from the Grok CLI, and logout; how credentials are read lives in a collapsible "How signing in works" disclosure. It is styled with DSH's own design tokens, so it follows the light and dark theme. Account and time values in the screenshot are demo data.
61
61
 
62
62
  <p align="center">
63
63
  <img src="docs/assets/settings-usage.webp" width="820" alt="The experimental usage panel in Grok Subscription settings: 6% used, about 94% remaining">
64
64
  </p>
65
65
 
66
- The experimental usage panel comes from an undocumented subscription billing endpoint (`/v1/billing?format=credits`). It is informational: the endpoint may change or disappear, a failed read never shows an invented percentage, and chat is unaffected.
66
+ The experimental usage panel comes from an undocumented subscription billing endpoint (`/v1/billing?format=credits`): the remaining share is drawn as a progress bar and per-product rows are listed separately. It is informational: the endpoint may change or disappear, a failed read never shows an invented percentage, and chat is unaffected.
67
67
 
68
68
  ## Prepare DSH
69
69
 
@@ -89,7 +89,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
89
89
  You can also install the version published on npm:
90
90
 
91
91
  ```sh
92
- dsh plugin --profile web add dsh-grok-subscription@1.0.2
92
+ dsh plugin --profile web add dsh-grok-subscription@1.0.4
93
93
  ```
94
94
 
95
95
  DSH handles target selection, the profile lock, dependency resolution, and bundle activation.
package/README.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  ## 三步开始
27
27
 
28
- 1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.2`。
28
+ 1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.4`。
29
29
 
30
30
  ```sh
31
31
  dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
@@ -57,13 +57,13 @@
57
57
  <img src="docs/assets/settings-account.webp" width="820" alt="DSH 设置中的 Grok 订阅页面:登录状态、账号、登录按钮与凭据说明">
58
58
  </p>
59
59
 
60
- 上图为 **设置 → Grok 订阅** 主界面:显示登录状态与脱敏账号,提供 CLI 登录、设备码登录、从 Grok CLI 拉取和登出,并说明凭据的读取方式。截图中的账号与时间均为演示数据。
60
+ 上图为 **设置 → Grok 订阅** 主界面:顶部状态胶囊显示登录状态,账户卡片提供 CLI 登录、设备码登录、从 Grok CLI 拉取和登出,凭据读取方式收在可展开的「登录方式说明」里。样式沿用 DSH 自身的设计变量,因此跟随主题明暗切换。截图中的账号与时间均为演示数据。
61
61
 
62
62
  <p align="center">
63
63
  <img src="docs/assets/settings-usage.webp" width="820" alt="Grok 订阅设置页中的实验性用量面板:已用 6%、剩余约 94%">
64
64
  </p>
65
65
 
66
- 实验性用量面板来自未公开的订阅计费接口(`/v1/billing?format=credits`)。它仅供参考:接口可能随时变化或消失,读取失败时不会显示编造的百分比,也不影响聊天。
66
+ 实验性用量面板来自未公开的订阅计费接口(`/v1/billing?format=credits`):剩余比例以进度条呈现,产品明细逐行列出。它仅供参考:接口可能随时变化或消失,读取失败时不会显示编造的百分比,也不影响聊天。
67
67
 
68
68
  ## 准备 DSH
69
69
 
@@ -89,7 +89,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
89
89
  也可以按 npm 上的已发布版本安装:
90
90
 
91
91
  ```sh
92
- dsh plugin --profile web add dsh-grok-subscription@1.0.2
92
+ dsh plugin --profile web add dsh-grok-subscription@1.0.4
93
93
  ```
94
94
 
95
95
  目标选择、profile 锁、依赖解析和 bundle 激活均由 DSH 负责。
Binary file
Binary file
package/lib/client.js CHANGED
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/client.jsx
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
+ GrokSubscriptionPanel: () => GrokSubscriptionPanel,
23
24
  GrokSubscriptionSection: () => GrokSubscriptionSection,
24
25
  RPC_CALL_TIMEOUT_MS: () => RPC_CALL_TIMEOUT_MS,
26
+ SectionBoundary: () => SectionBoundary,
27
+ UsagePanel: () => UsagePanel,
25
28
  apply: () => apply,
26
29
  callRpc: () => callRpc,
27
30
  inject: () => inject,
@@ -34,7 +37,7 @@ var import_react2 = require("react");
34
37
  var zh = {
35
38
  nav: "Grok \u8BA2\u9605",
36
39
  title: "Grok \u8BA2\u9605",
37
- subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.2",
40
+ subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.4",
38
41
  signedIn: "\u5DF2\u767B\u5F55",
39
42
  signedOut: "\u672A\u767B\u5F55",
40
43
  account: "\u8D26\u6237",
@@ -52,6 +55,9 @@ var zh = {
52
55
  loginHint: "\u4F1A\u5728\u542F\u52A8 DSH \u7684\u7EC8\u7AEF\u4E2D\u8FD0\u884C grok login\u3002\u8BF7\u5728\u7EC8\u7AEF\u6216\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\uFF0C\u7136\u540E\u7B49\u5F85\u540C\u6B65\u3002",
53
56
  deviceHint: "\u8BBE\u5907\u7801\u63D0\u793A\u4F1A\u51FA\u73B0\u5728 DSH \u7EC8\u7AEF\u3002\u4E5F\u53EF\u624B\u52A8\u8FD0\u884C grok login --device-auth\uFF0C\u5B8C\u6210\u540E\u70B9\u201C\u4ECE Grok CLI \u62C9\u53D6\u201D\u3002",
54
57
  pullHint: "\u5B89\u5168\u8BFB\u53D6 ~/.grok/auth.json\uFF08\u62D2\u7EDD\u7B26\u53F7\u94FE\u63A5\u4E0E\u7EC4/\u5176\u4ED6\u4EBA\u53EF\u8BFB\uFF09\uFF0C\u53EA\u628A\u77ED\u671F access token \u5199\u5165 DSH\u3002",
58
+ loginHelp: "\u767B\u5F55\u65B9\u5F0F\u8BF4\u660E",
59
+ refreshCatalog: "\u5237\u65B0\u6A21\u578B\u76EE\u5F55",
60
+ renderError: "\u754C\u9762\u6E32\u67D3\u5931\u8D25",
55
61
  busy: "\u5904\u7406\u4E2D\u2026",
56
62
  busyCatalog: "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u76EE\u5F55\u2026",
57
63
  busyUsage: "\u6B63\u5728\u5237\u65B0\u7528\u91CF\u2026",
@@ -81,7 +87,7 @@ var zh = {
81
87
  var en = {
82
88
  nav: "Grok Subscription",
83
89
  title: "Grok Subscription",
84
- subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.2",
90
+ subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.4",
85
91
  signedIn: "Signed in",
86
92
  signedOut: "Signed out",
87
93
  account: "Account",
@@ -99,6 +105,9 @@ var en = {
99
105
  loginHint: "Runs grok login in the terminal that started DSH. Finish the browser or CLI prompt there, then wait for sync.",
100
106
  deviceHint: "The device-code prompt appears in the DSH terminal. You can also run grok login --device-auth yourself, then Pull from Grok CLI.",
101
107
  pullHint: "Reads ~/.grok/auth.json securely (refuses symlinks and group/other-readable files) and stores only the short-lived access token in DSH.",
108
+ loginHelp: "How signing in works",
109
+ refreshCatalog: "Refresh model catalog",
110
+ renderError: "Failed to render this panel",
102
111
  busy: "Working\u2026",
103
112
  busyCatalog: "Refreshing model catalog\u2026",
104
113
  busyUsage: "Refreshing usage\u2026",
@@ -165,6 +174,83 @@ var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
165
174
  var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
166
175
  var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
167
176
 
177
+ // src/client-settings-style.js
178
+ var SETTINGS_STYLE = `
179
+ .grokSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
180
+ .grokSubscription h2,.grokSubscription h3,.grokSubscription p{margin:0}
181
+ .grokSubscription h2{font-size:16px;line-height:24px;font-weight:500}
182
+ .grokSubscription h3{font-size:15px;line-height:22px;font-weight:600}
183
+
184
+ .gsHead{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
185
+ .gsLead{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}
186
+
187
+ .gsCard{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;padding:14px 16px;display:flex;flex-direction:column;gap:12px}
188
+ .gsCardHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
189
+ .gsCardHead .gsSpacer{flex:1;min-width:0}
190
+
191
+ .gsChip{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:2px 10px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);white-space:nowrap}
192
+ .gsChip--ok{color:var(--dsw-alias-state-success-primary)}
193
+ .gsChip--off{color:var(--dsw-alias-label-tertiary)}
194
+ .gsChip--warn{color:var(--dsw-alias-state-warn-label)}
195
+ .gsChip--error{color:var(--dsw-alias-label-error)}
196
+ .gsDot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
197
+
198
+ .gsAccount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
199
+
200
+ .gsActions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
201
+ .gsBtn{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:.5px solid var(--dsw-alias-border-l3);border-radius:18px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 14px;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);background:0 0;transition:background .16s,border-color .16s,color .16s}
202
+ .gsBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
203
+ .gsBtn:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
204
+ .gsBtn:disabled{opacity:.45;cursor:not-allowed}
205
+ .gsBtn--primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:transparent}
206
+ .gsBtn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
207
+ .gsBtn--danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}
208
+
209
+ .gsStatus{display:flex;align-items:flex-start;gap:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
210
+ .gsStatus--ok{color:var(--dsw-alias-state-success-primary)}
211
+ .gsStatus--error{color:var(--dsw-alias-label-error)}
212
+ .gsStatus--warn{color:var(--dsw-alias-state-warn-label)}
213
+ .gsStatus--busy{color:var(--dsw-alias-label-tertiary)}
214
+
215
+ .gsDisclosure{margin:0}
216
+ .gsDisclosure>summary{display:flex;align-items:center;gap:8px;min-height:28px;cursor:pointer;list-style:none;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
217
+ .gsDisclosure>summary::-webkit-details-marker{display:none}
218
+ .gsDisclosure>summary:hover{color:var(--dsw-alias-label-primary)}
219
+ .gsDisclosure>summary:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:4px;border-radius:4px}
220
+ .gsChevron{flex:none;transition:transform .16s}
221
+ .gsDisclosure[open] .gsChevron{transform:rotate(180deg)}
222
+ .gsDisclosureBody{display:flex;flex-direction:column;gap:6px;padding-top:8px;margin-top:8px;border-top:.5px solid var(--dsw-alias-border-l2)}
223
+ .gsDisclosureBody p{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
224
+
225
+ .gsGauge{display:flex;flex-direction:column;gap:8px}
226
+ .gsGaugeTop{display:flex;align-items:baseline;gap:8px}
227
+ .gsGaugeValue{font-size:24px;line-height:30px;font-weight:600;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
228
+ .gsGaugeLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
229
+ .gsBar{height:6px;border-radius:999px;background:color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent);overflow:hidden}
230
+ .gsBar>span{display:block;height:100%;border-radius:999px;background:var(--dsw-alias-brand-primary);transition:width .2s}
231
+ .gsGaugeMeta{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
232
+ .gsGaugeMeta code{font-size:11px;color:var(--dsw-alias-label-tertiary)}
233
+
234
+ .gsRows{display:flex;flex-direction:column;gap:0}
235
+ .gsRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 0;font-size:13px;line-height:20px;border-top:.5px solid var(--dsw-alias-border-l2)}
236
+ .gsRow:first-child{border-top:none}
237
+ .gsRowValue{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}
238
+
239
+ .gsModels{display:flex;flex-wrap:wrap;gap:6px}
240
+ .gsModel{display:inline-flex;align-items:baseline;gap:6px;border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:4px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}
241
+ .gsModel code{font-size:11px;color:var(--dsw-alias-label-tertiary)}
242
+
243
+ .gsHint,.gsEmpty,.gsCaveat{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
244
+ .gsCaveat{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
245
+ .gsLink{color:var(--dsw-alias-link);font-size:13px;line-height:20px;text-decoration:none;align-self:center}
246
+ .gsLink:hover{text-decoration:underline}
247
+
248
+ @container (max-width: 420px){
249
+ .grokSubscription .gsGaugeValue{font-size:20px;line-height:26px}
250
+ .grokSubscription .gsBtn{flex:1 1 auto}
251
+ }
252
+ `;
253
+
168
254
  // src/client-composer-quota.jsx
169
255
  var import_react = require("react");
170
256
  var import_react_dom = require("react-dom");
@@ -533,60 +619,96 @@ async function callRpc(rpc, endpoint, payload = {}) {
533
619
  if (timer) clearTimeout(timer);
534
620
  }
535
621
  }
622
+ var SectionBoundary = class extends import_react2.Component {
623
+ constructor(props) {
624
+ super(props);
625
+ this.state = { error: void 0 };
626
+ }
627
+ static getDerivedStateFromError(error) {
628
+ return { error };
629
+ }
630
+ render() {
631
+ const { error } = this.state;
632
+ if (!error) return this.props.children;
633
+ const t = this.props.t;
634
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "grokSubscription", children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: SETTINGS_STYLE }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsHead", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t("title") }) }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsCard", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsStatus gsStatus--error", children: [
638
+ t("renderError"),
639
+ ": ",
640
+ error instanceof Error ? error.message : String(error)
641
+ ] }) })
642
+ ] });
643
+ }
644
+ };
645
+ function GrokSubscriptionPanel(props) {
646
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionBoundary, { t: props.t, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GrokSubscriptionSection, { ...props }) });
647
+ }
648
+ function Chevron() {
649
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "gsChevron", width: "12", height: "12", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
650
+ }
651
+ function Chip({ tone = "off", children }) {
652
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: `gsChip gsChip--${tone}`, children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsDot" }),
654
+ children
655
+ ] });
656
+ }
536
657
  function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
537
658
  const ok = usage?.status === "ok";
538
- const used = ok ? formatPercent(usage.usedPercent) : void 0;
539
- const remaining = ok ? formatPercent(usage.remainingPercent) : void 0;
659
+ const usedNumber = ok && Number.isFinite(usage.usedPercent) ? usage.usedPercent : void 0;
660
+ const remainingNumber = ok && Number.isFinite(usage.remainingPercent) ? usage.remainingPercent : void 0;
661
+ const used = usedNumber !== void 0 ? formatPercent(usedNumber) : void 0;
662
+ const remaining = remainingNumber !== void 0 ? formatPercent(remainingNumber) : void 0;
540
663
  const resetLabel = usage?.periodEndLocal || usage?.periodEnd;
541
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "usageBlock", children: [
542
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("usageTitle") }),
543
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageSubtitle") }),
544
- ok && used !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
545
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
546
- t("usageUsed"),
547
- ": ",
548
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
549
- used,
664
+ const gaugeNumber = remainingNumber ?? (usedNumber !== void 0 ? 100 - usedNumber : void 0);
665
+ const gaugeValue = gaugeNumber !== void 0 ? formatPercent(gaugeNumber) : void 0;
666
+ const gaugeLabel = remainingNumber !== void 0 ? t("usageRemaining") : t("usageUsed");
667
+ const fill = gaugeNumber === void 0 ? 0 : Math.min(100, Math.max(0, gaugeNumber));
668
+ const products = Array.isArray(usage?.productUsage) ? usage.productUsage : [];
669
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
670
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsCardHead", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("usageTitle") }) }),
671
+ ok && (used !== void 0 || remaining !== void 0) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGauge", children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGaugeTop", children: [
673
+ gaugeValue !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsGaugeValue", children: [
674
+ gaugeValue,
550
675
  "%"
551
- ] }),
552
- remaining !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
553
- " \xB7 ",
554
- t("usageRemaining"),
676
+ ] }) : null,
677
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsGaugeLabel", children: gaugeLabel })
678
+ ] }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsBar", role: "img", "aria-label": `${gaugeLabel} ${gaugeValue ?? 0}%`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: `${fill}%` } }) }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGaugeMeta", children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
682
+ t("usageUsed"),
555
683
  " ",
556
- remaining,
684
+ used ?? "\u2014",
557
685
  "%"
558
- ] }) : null
559
- ] }),
560
- resetLabel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
561
- t("usageReset"),
562
- ": ",
563
- resetLabel,
564
- usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
565
- " (",
566
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }),
567
- ")"
568
- ] }) : null
569
- ] }) : null,
570
- Array.isArray(usage.productUsage) && usage.productUsage.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageProduct") }),
572
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: usage.productUsage.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
573
- row.name ?? "\u2014",
574
- typeof row.usedPercent === "number" ? ` \xB7 ${formatPercent(row.usedPercent)}%` : ""
575
- ] }, `${row.name ?? "row"}-${index}`)) })
576
- ] }) : null,
577
- usage.fetchedAt ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
578
- t("usageFetchedAt"),
579
- ": ",
580
- usage.fetchedAt
581
- ] }) : null
582
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
686
+ ] }),
687
+ resetLabel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
688
+ t("usageReset"),
689
+ " ",
690
+ resetLabel
691
+ ] }) : null,
692
+ usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }) : null
693
+ ] })
694
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsEmpty", children: [
583
695
  t("usageUnavailable"),
584
696
  usage?.reason ? `: ${usage.reason}` : ""
585
697
  ] }),
586
- usageBusy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busyUsage") }) : null,
587
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
588
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(usageBusy) || !signedIn, onClick: onRefresh, children: t("usageRefresh") }),
589
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: USAGE_PAGE_URL, target: "_blank", rel: "noreferrer", children: t("usageOpenGrok") })
698
+ 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: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: row.name ?? "\u2014" }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsRowValue", children: typeof row.usedPercent === "number" ? `${formatPercent(row.usedPercent)}%` : "\u2014" })
701
+ ] }, `${row.name ?? "row"}-${index}`)) }) : null,
702
+ usage?.fetchedAt ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsHint", children: [
703
+ t("usageFetchedAt"),
704
+ ": ",
705
+ usage.fetchedAt
706
+ ] }) : null,
707
+ usageBusy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--busy", children: t("busyUsage") }) : null,
708
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("usageSubtitle") }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsActions", children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(usageBusy) || !signedIn, onClick: onRefresh, children: t("usageRefresh") }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { className: "gsLink", href: USAGE_PAGE_URL, target: "_blank", rel: "noreferrer", children: t("usageOpenGrok") })
590
712
  ] })
591
713
  ] });
592
714
  }
@@ -660,7 +782,8 @@ function GrokSubscriptionSection({ rpc, t }) {
660
782
  "login/cli": "loginOk",
661
783
  "login/device": "loginOk",
662
784
  logout: "logoutOk",
663
- "usage/refresh": "usageRefreshOk"
785
+ "usage/refresh": "usageRefreshOk",
786
+ "catalog/refresh": "catalogRefreshOk"
664
787
  }[endpoint];
665
788
  if (successKey) setNotice(t(successKey));
666
789
  if (endpoint === "usage/refresh" && value?.ok !== false && !value?.error) {
@@ -682,47 +805,43 @@ function GrokSubscriptionSection({ rpc, t }) {
682
805
  const models = catalog?.models ?? [];
683
806
  const signedIn = account?.signedIn === true;
684
807
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "grokSubscription", children: [
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
686
- .grokSubscription { display: grid; gap: 12px; max-width: 42rem; }
687
- .grokSubscription h2 { margin: 0 0 4px; font-size: 1.15rem; }
688
- .grokSubscription p, .grokSubscription li { line-height: 1.5; }
689
- .grokSubscription .muted { opacity: 0.78; font-size: 0.92rem; }
690
- .grokSubscription .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
691
- .grokSubscription button { cursor: pointer; }
692
- .grokSubscription .error { color: #b42318; }
693
- .grokSubscription .notice { color: #067647; }
694
- .grokSubscription ul { margin: 0; padding-left: 1.2rem; }
695
- .grokSubscription .usageBlock { display: grid; gap: 8px; padding: 10px 0; border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent); }
696
- .grokSubscription a { color: inherit; }
697
- ` }),
698
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: SETTINGS_STYLE }),
809
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsHead", children: [
699
810
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t("title") }),
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("subtitle") })
811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chip, { tone: signedIn ? "ok" : "off", children: signedIn ? t("signedIn") : t("signedOut") })
701
812
  ] }),
702
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
703
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
704
- t("account"),
705
- ": "
813
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsLead", children: t("subtitle") }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCardHead", children: [
816
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("account") }),
817
+ signedIn && account?.maskedAccount ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsAccount", children: account.maskedAccount }) : null
706
818
  ] }),
707
- signedIn ? `${t("signedIn")} \xB7 ${account.maskedAccount ?? t("unknownAccount")}` : t("signedOut")
708
- ] }),
709
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
710
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/cli"), children: t("loginCli") }),
711
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/device"), children: t("loginDevice") }),
712
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("pull"), children: t("pull") }),
713
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy) || !signedIn, onClick: () => void run("logout"), children: t("logout") })
819
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsActions", children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--primary", type: "button", disabled: Boolean(busy), onClick: () => void run("login/cli"), children: t("loginCli") }),
821
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(busy), onClick: () => void run("login/device"), children: t("loginDevice") }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(busy), onClick: () => void run("pull"), children: t("pull") }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--danger", type: "button", disabled: Boolean(busy) || !signedIn, onClick: () => void run("logout"), children: t("logout") })
824
+ ] }),
825
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--busy", children: t("busy") }) : null,
826
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--ok", children: notice }) : null,
827
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsStatus gsStatus--error", children: [
828
+ t("error"),
829
+ ": ",
830
+ error
831
+ ] }) : null,
832
+ status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--warn", children: t("cliMissing") }) : null,
833
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "gsDisclosure", children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { children: [
835
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chevron, {}),
836
+ t("loginHelp")
837
+ ] }),
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsDisclosureBody", children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("loginHint") }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("deviceHint") }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("pullHint") })
842
+ ] })
843
+ ] })
714
844
  ] }),
715
- busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busy") }) : null,
716
- notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: notice }) : null,
717
- error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
718
- t("error"),
719
- ": ",
720
- error
721
- ] }) : null,
722
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("loginHint") }),
723
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("deviceHint") }),
724
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("pullHint") }),
725
- status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("cliMissing") }) : null,
726
845
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
727
846
  UsagePanel,
728
847
  {
@@ -733,21 +852,25 @@ function GrokSubscriptionSection({ rpc, t }) {
733
852
  onRefresh: () => void run("usage/refresh")
734
853
  }
735
854
  ),
736
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
737
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("models") }),
738
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: sourceLabel(catalog?.source, t) }),
739
- models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("noModels") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCardHead", children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("models") }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsSpacer" }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chip, { tone: catalog?.source === "live" ? "ok" : catalog?.source === "fallback" ? "warn" : "off", children: sourceLabel(catalog?.source, t) })
860
+ ] }),
861
+ models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsEmpty", children: t("noModels") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsModels", children: models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsModel", children: [
740
862
  model.name,
741
863
  " ",
742
864
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: model.id })
743
865
  ] }, model.id)) }),
744
- catalog?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
866
+ catalog?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsStatus gsStatus--error", children: [
745
867
  t("catalogError"),
746
868
  ": ",
747
869
  catalog.error
748
- ] }) : null
870
+ ] }) : null,
871
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsActions", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: !signedIn, onClick: () => void run("catalog/refresh"), children: t("refreshCatalog") }) })
749
872
  ] }),
750
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("caveats") })
873
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsCaveat", children: t("caveats") })
751
874
  ] });
752
875
  }
753
876
  function apply(ctx) {
@@ -791,7 +914,7 @@ function apply(ctx) {
791
914
  label: () => t("nav"),
792
915
  locale: LOCALE_NS,
793
916
  inject: () => ({ rpc, t })
794
- }, GrokSubscriptionSection));
917
+ }, GrokSubscriptionPanel));
795
918
  const installDirectorySlots = (scope) => {
796
919
  let modelDirectories;
797
920
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-grok-subscription",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "scripts": {
41
41
  "build": "node scripts/build.mjs",
42
+ "pretest": "npm run build",
42
43
  "test": "node --test tests/*.test.mjs",
43
44
  "check": "npm test && npm run build"
44
45
  },
@@ -55,8 +56,12 @@
55
56
  "node": "^22.19.0 || >=24.0.0"
56
57
  },
57
58
  "peerDependencies": {
59
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
58
60
  "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
59
61
  "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
62
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2",
63
+ "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.5-rc.2",
64
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
60
65
  "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
61
66
  "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
62
67
  "@deepseek-ai/dsh-credentials": "^0.1.5-rc.2",
@@ -66,11 +71,7 @@
66
71
  "@deepseek-ai/dsh-web": "^0.1.5-rc.2",
67
72
  "@earendil-works/pi-ai": "^0.82.1",
68
73
  "react": "^18.2.0",
69
- "react-dom": "^18.2.0",
70
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
71
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2",
72
- "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.5-rc.2",
73
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2"
74
+ "react-dom": "^18.2.0"
74
75
  },
75
76
  "peerDependenciesMeta": {
76
77
  "@deepseek-ai/dsh-client-connection": {
@@ -123,7 +124,9 @@
123
124
  }
124
125
  },
125
126
  "devDependencies": {
126
- "esbuild": "^0.25.0"
127
+ "esbuild": "^0.25.0",
128
+ "react": "^18.3.1",
129
+ "react-dom": "^18.3.1"
127
130
  },
128
131
  "author": "BaronCyrus",
129
132
  "repository": {