dsh-grok-subscription 1.0.9 → 1.0.11
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 +4 -3
- package/README.md +4 -3
- package/lib/client.js +144 -7
- package/lib/index.js +365 -19
- package/package.json +1 -1
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.
|
|
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.10`.
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -91,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
91
91
|
You can also install the version published on npm:
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.10
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
DSH handles target selection, the profile lock, dependency resolution, and bundle activation.
|
|
@@ -146,7 +146,8 @@ Restart DSH manually afterwards, then:
|
|
|
146
146
|
- Settings can show the used and remaining percentages the backend reports, and says so plainly instead of guessing when the read fails;
|
|
147
147
|
- Supports CLI login, device-code login, pull from the Grok CLI, and logout;
|
|
148
148
|
- Renews the access token automatically: near the end of its 6-hour life, or on a 401, it runs the official `grok` CLI and retries once, so a session never lapses mid-use; the chat model list refreshes whenever sign-in state changes;
|
|
149
|
-
- When subscription routing is unavailable it reports an error instead of silently switching to another paid route
|
|
149
|
+
- When subscription routing is unavailable it reports an error instead of silently switching to another paid route;
|
|
150
|
+
- Prefix caching follows xAI's automatic prompt-cache semantics: one DSH session keeps a stable `prompt_cache_key`, tool definitions stay in a stable order, and the fallback adapter preserves and replays `reasoning.encrypted_content` unchanged. Server-side eviction can still miss, and the plugin never invents a hit rate.
|
|
150
151
|
|
|
151
152
|
### Composer quota
|
|
152
153
|
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
## 三步开始
|
|
27
27
|
|
|
28
|
-
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.
|
|
28
|
+
1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.10`。
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
31
|
dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
@@ -91,7 +91,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
|
|
|
91
91
|
也可以按 npm 上的已发布版本安装:
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
|
-
dsh plugin --profile web add dsh-grok-subscription@1.0.
|
|
94
|
+
dsh plugin --profile web add dsh-grok-subscription@1.0.10
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
目标选择、profile 锁、依赖解析和 bundle 激活均由 DSH 负责。
|
|
@@ -146,7 +146,8 @@ dsh --profile web --dump-config
|
|
|
146
146
|
- 设置页可查看服务端返回的用量与剩余百分比,失败时明确提示而不是显示猜测值;
|
|
147
147
|
- 支持 CLI 登录、设备码登录、从 Grok CLI 拉取和登出;登录状态变化后 Chat 模型列表自动刷新;
|
|
148
148
|
- access token 到期自动续期:临近 6 小时有效期或收到 401 时,调用官方 `grok` CLI 刷新会话并重试一次,不会中途要求手动重新登录;
|
|
149
|
-
-
|
|
149
|
+
- 订阅路由不可用时明确报错,不会静默切换到其他付费路由;
|
|
150
|
+
- 前缀缓存按 xAI 的自动缓存语义优化:同一 DSH 会话使用稳定的 `prompt_cache_key`,工具定义保持稳定顺序,回退适配器会保存并原样回放 `reasoning.encrypted_content`。缓存仍可能因服务端逐出而失效,插件不虚构命中率。
|
|
150
151
|
|
|
151
152
|
### 输入框额度
|
|
152
153
|
|
package/lib/client.js
CHANGED
|
@@ -26,6 +26,7 @@ __export(client_exports, {
|
|
|
26
26
|
RPC_CALL_TIMEOUT_MS: () => RPC_CALL_TIMEOUT_MS,
|
|
27
27
|
SectionBoundary: () => SectionBoundary,
|
|
28
28
|
UsagePanel: () => UsagePanel,
|
|
29
|
+
VersionCard: () => VersionCard,
|
|
29
30
|
apply: () => apply,
|
|
30
31
|
callRpc: () => callRpc,
|
|
31
32
|
inject: () => inject,
|
|
@@ -38,7 +39,7 @@ var import_react2 = require("react");
|
|
|
38
39
|
var zh = {
|
|
39
40
|
nav: "Grok \u8BA2\u9605",
|
|
40
41
|
title: "Grok \u8BA2\u9605",
|
|
41
|
-
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.
|
|
42
|
+
subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.11",
|
|
42
43
|
signedIn: "\u5DF2\u767B\u5F55",
|
|
43
44
|
signedOut: "\u672A\u767B\u5F55",
|
|
44
45
|
account: "\u8D26\u6237",
|
|
@@ -90,12 +91,31 @@ var zh = {
|
|
|
90
91
|
composerQuotaDetails: "\u6BCF\u5468\u989D\u5EA6",
|
|
91
92
|
composerQuotaRemaining: "\u6BCF\u5468\u989D\u5EA6 \u5269\u4F59 {remaining}%",
|
|
92
93
|
composerQuotaResets: "\u91CD\u7F6E\u4E8E {reset}",
|
|
93
|
-
composerQuotaResetUnknown: "\u91CD\u7F6E\u65F6\u95F4\u672A\u77E5"
|
|
94
|
+
composerQuotaResetUnknown: "\u91CD\u7F6E\u65F6\u95F4\u672A\u77E5",
|
|
95
|
+
versionTitle: "\u63D2\u4EF6\u7248\u672C",
|
|
96
|
+
versionCurrent: "\u5F53\u524D\u7248\u672C",
|
|
97
|
+
versionLatest: "\u6700\u65B0\u7248\u672C",
|
|
98
|
+
versionCheck: "\u68C0\u67E5\u66F4\u65B0",
|
|
99
|
+
versionChecking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026",
|
|
100
|
+
versionFailed: "\u65E0\u6CD5\u68C0\u67E5\u6700\u65B0\u7248\u672C\u3002",
|
|
101
|
+
versionUpToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
|
|
102
|
+
versionAvailable: "\u53D1\u73B0\u65B0\u7248\u672C v{version}\u3002",
|
|
103
|
+
versionLinked: "\u5F53\u524D\u4E3A\u672C\u5730\u5F00\u53D1\u5B89\u88C5\uFF0C\u8BF7\u5728\u63D2\u4EF6\u4ED3\u5E93\u62C9\u53D6\u6700\u65B0\u4EE3\u7801\u5E76\u91CD\u65B0\u6784\u5EFA\u3002",
|
|
104
|
+
versionManual: "\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C dsh plugin --profile web add dsh-grok-subscription@latest \u5B8C\u6210\u66F4\u65B0\u3002",
|
|
105
|
+
versionUpdate: "\u66F4\u65B0\u63D2\u4EF6",
|
|
106
|
+
versionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
|
|
107
|
+
versionUpdateFailed: "\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u6216\u5728\u7EC8\u7AEF\u624B\u52A8\u66F4\u65B0\u3002",
|
|
108
|
+
versionUpdated: "\u5DF2\u66F4\u65B0\u5230 v{version}\u3002",
|
|
109
|
+
versionUpdatedHint: "\u65B0\u7248\u672C\u9700\u8981\u91CD\u542F DSH \u670D\u52A1\u624D\u80FD\u5B8C\u5168\u751F\u6548\uFF1B\u4EC5\u5237\u65B0\u754C\u9762\u4E0D\u4F1A\u66F4\u65B0\u5BBF\u4E3B\u8FDB\u7A0B\u4E2D\u5DF2\u52A0\u8F7D\u7684\u63D2\u4EF6\u3002",
|
|
110
|
+
versionRestart: "\u91CD\u542F DSH \u670D\u52A1",
|
|
111
|
+
versionRestartHint: "\u63D2\u4EF6\u65E0\u6CD5\u5B89\u5168\u5730\u91CD\u542F\u5BBF\u4E3B\u8FDB\u7A0B\uFF1A\u8BF7\u5728\u8FD0\u884C DSH \u7684\u7EC8\u7AEF\u6309 Ctrl+C \u7ED3\u675F\u8FDB\u7A0B\uFF0C\u518D\u91CD\u65B0\u8FD0\u884C dsh web\u3002",
|
|
112
|
+
versionRefresh: "\u5237\u65B0\u754C\u9762",
|
|
113
|
+
versionLater: "\u7A0D\u540E"
|
|
94
114
|
};
|
|
95
115
|
var en = {
|
|
96
116
|
nav: "Grok Subscription",
|
|
97
117
|
title: "Grok Subscription",
|
|
98
|
-
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.
|
|
118
|
+
subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.11",
|
|
99
119
|
signedIn: "Signed in",
|
|
100
120
|
signedOut: "Signed out",
|
|
101
121
|
account: "Account",
|
|
@@ -147,7 +167,26 @@ var en = {
|
|
|
147
167
|
composerQuotaDetails: "Weekly quota",
|
|
148
168
|
composerQuotaRemaining: "Weekly quota \xB7 {remaining}% left",
|
|
149
169
|
composerQuotaResets: "Resets {reset}",
|
|
150
|
-
composerQuotaResetUnknown: "Reset time unknown"
|
|
170
|
+
composerQuotaResetUnknown: "Reset time unknown",
|
|
171
|
+
versionTitle: "Plugin version",
|
|
172
|
+
versionCurrent: "Current version",
|
|
173
|
+
versionLatest: "Latest version",
|
|
174
|
+
versionCheck: "Check for updates",
|
|
175
|
+
versionChecking: "Checking for updates\u2026",
|
|
176
|
+
versionFailed: "Could not check the latest version.",
|
|
177
|
+
versionUpToDate: "You are on the latest version.",
|
|
178
|
+
versionAvailable: "A new version v{version} is available.",
|
|
179
|
+
versionLinked: "Installed from a local checkout; pull and rebuild the plugin repository to update.",
|
|
180
|
+
versionManual: "Run dsh plugin --profile web add dsh-grok-subscription@latest in a terminal to update.",
|
|
181
|
+
versionUpdate: "Update plugin",
|
|
182
|
+
versionUpdating: "Updating\u2026",
|
|
183
|
+
versionUpdateFailed: "The update failed. Try again or update manually in a terminal.",
|
|
184
|
+
versionUpdated: "Updated to v{version}.",
|
|
185
|
+
versionUpdatedHint: "The new version takes full effect after a DSH restart; refreshing the page alone does not reload the plugin inside the host process.",
|
|
186
|
+
versionRestart: "Restart DSH",
|
|
187
|
+
versionRestartHint: "The plugin cannot safely restart its host process: press Ctrl+C in the terminal running DSH, then run dsh web again.",
|
|
188
|
+
versionRefresh: "Refresh page",
|
|
189
|
+
versionLater: "Later"
|
|
151
190
|
};
|
|
152
191
|
|
|
153
192
|
// src/rpc-contract.js
|
|
@@ -160,7 +199,9 @@ var RPC_ENDPOINTS = Object.freeze([
|
|
|
160
199
|
"logout",
|
|
161
200
|
"catalog/refresh",
|
|
162
201
|
"usage",
|
|
163
|
-
"usage/refresh"
|
|
202
|
+
"usage/refresh",
|
|
203
|
+
"plugin/version",
|
|
204
|
+
"plugin/update"
|
|
164
205
|
]);
|
|
165
206
|
function createRpcClient(transport) {
|
|
166
207
|
return Object.freeze({
|
|
@@ -620,7 +661,7 @@ function formatPercent(value) {
|
|
|
620
661
|
const rounded = Math.round(value * 10) / 10;
|
|
621
662
|
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
622
663
|
}
|
|
623
|
-
async function callRpc(rpc, endpoint, payload = {}) {
|
|
664
|
+
async function callRpc(rpc, endpoint, payload = {}, timeoutMs = RPC_CALL_TIMEOUT_MS) {
|
|
624
665
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
625
666
|
let timer;
|
|
626
667
|
try {
|
|
@@ -632,7 +673,7 @@ async function callRpc(rpc, endpoint, payload = {}) {
|
|
|
632
673
|
} catch {
|
|
633
674
|
}
|
|
634
675
|
reject(new Error(`Request timed out after ${RPC_CALL_TIMEOUT_MS}ms`));
|
|
635
|
-
},
|
|
676
|
+
}, timeoutMs);
|
|
636
677
|
});
|
|
637
678
|
return unwrap(await Promise.race([call, timeout]));
|
|
638
679
|
} finally {
|
|
@@ -687,6 +728,90 @@ function DiagnosticsRows({ diagnostics, t }) {
|
|
|
687
728
|
] })
|
|
688
729
|
] });
|
|
689
730
|
}
|
|
731
|
+
function fillTemplate2(template, values) {
|
|
732
|
+
return Object.entries(values).reduce(
|
|
733
|
+
(text, [key, value]) => text.replaceAll(`{${key}}`, value),
|
|
734
|
+
template
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
function VersionCard({ call, t }) {
|
|
738
|
+
const [info, setInfo] = (0, import_react2.useState)();
|
|
739
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
740
|
+
const [error, setError] = (0, import_react2.useState)(false);
|
|
741
|
+
const [updating, setUpdating] = (0, import_react2.useState)(false);
|
|
742
|
+
const [updateFailed, setUpdateFailed] = (0, import_react2.useState)(false);
|
|
743
|
+
const [updated, setUpdated] = (0, import_react2.useState)();
|
|
744
|
+
const [restartHint, setRestartHint] = (0, import_react2.useState)(false);
|
|
745
|
+
const generation = (0, import_react2.useRef)(0);
|
|
746
|
+
const load = (force) => {
|
|
747
|
+
const current = ++generation.current;
|
|
748
|
+
setLoading(true);
|
|
749
|
+
setError(false);
|
|
750
|
+
void call("plugin/version", { force }).then((value) => {
|
|
751
|
+
if (generation.current === current) setInfo(value);
|
|
752
|
+
}).catch(() => {
|
|
753
|
+
if (generation.current === current) setError(true);
|
|
754
|
+
}).finally(() => {
|
|
755
|
+
if (generation.current === current) setLoading(false);
|
|
756
|
+
});
|
|
757
|
+
};
|
|
758
|
+
(0, import_react2.useEffect)(() => {
|
|
759
|
+
load(false);
|
|
760
|
+
return () => {
|
|
761
|
+
generation.current += 1;
|
|
762
|
+
};
|
|
763
|
+
}, []);
|
|
764
|
+
const update = () => {
|
|
765
|
+
setUpdating(true);
|
|
766
|
+
setUpdateFailed(false);
|
|
767
|
+
void call("plugin/update").then((value) => {
|
|
768
|
+
setUpdated(value);
|
|
769
|
+
setRestartHint(false);
|
|
770
|
+
}).catch(() => setUpdateFailed(true)).finally(() => setUpdating(false));
|
|
771
|
+
};
|
|
772
|
+
const installKind = info?.install?.kind;
|
|
773
|
+
const showStatus = info !== void 0 && updated === void 0;
|
|
774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
|
|
775
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCardHead", children: [
|
|
776
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("versionTitle") }),
|
|
777
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsSpacer" }),
|
|
778
|
+
updated === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: loading || updating, onClick: () => load(true), children: loading ? t("versionChecking") : t("versionCheck") }) : null
|
|
779
|
+
] }),
|
|
780
|
+
loading && info === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--busy", role: "status", children: t("versionChecking") }) : null,
|
|
781
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--error", role: "alert", children: t("versionFailed") }) : null,
|
|
782
|
+
info !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRows", children: [
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("versionCurrent") }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsRowValue", children: [
|
|
786
|
+
"v",
|
|
787
|
+
info.current
|
|
788
|
+
] })
|
|
789
|
+
] }),
|
|
790
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
|
|
791
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("versionLatest") }),
|
|
792
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsRowValue", children: [
|
|
793
|
+
"v",
|
|
794
|
+
info.latest
|
|
795
|
+
] })
|
|
796
|
+
] })
|
|
797
|
+
] }) : null,
|
|
798
|
+
showStatus ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--ok", role: "status", children: info.updateAvailable ? fillTemplate2(t("versionAvailable"), { version: info.latest }) : t("versionUpToDate") }) : null,
|
|
799
|
+
showStatus && info.updateAvailable && installKind === "npm" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsActions", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--primary", type: "button", disabled: updating, onClick: update, children: updating ? t("versionUpdating") : t("versionUpdate") }) }) : null,
|
|
800
|
+
showStatus && installKind === "link" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("versionLinked") }) : null,
|
|
801
|
+
showStatus && info.updateAvailable && installKind === "unknown" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("versionManual") }) : null,
|
|
802
|
+
updateFailed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--error", role: "alert", children: t("versionUpdateFailed") }) : null,
|
|
803
|
+
updated !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { role: "status", children: [
|
|
804
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--ok", children: fillTemplate2(t("versionUpdated"), { version: updated.version }) }),
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("versionUpdatedHint") }),
|
|
806
|
+
restartHint ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("versionRestartHint") }) : null,
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsActions", children: [
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", onClick: () => setRestartHint(true), children: t("versionRestart") }),
|
|
809
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--primary", type: "button", onClick: () => window.location.reload(), children: t("versionRefresh") }),
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", onClick: () => setUpdated(void 0), children: t("versionLater") })
|
|
811
|
+
] })
|
|
812
|
+
] }) : null
|
|
813
|
+
] });
|
|
814
|
+
}
|
|
690
815
|
function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
|
|
691
816
|
const ok = usage?.status === "ok";
|
|
692
817
|
const usedNumber = ok && Number.isFinite(usage.usedPercent) ? usage.usedPercent : void 0;
|
|
@@ -877,6 +1002,18 @@ function GrokSubscriptionSection({ rpc, t }) {
|
|
|
877
1002
|
] })
|
|
878
1003
|
] })
|
|
879
1004
|
] }),
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1006
|
+
VersionCard,
|
|
1007
|
+
{
|
|
1008
|
+
t,
|
|
1009
|
+
call: (endpoint, payload) => callRpc(
|
|
1010
|
+
rpc,
|
|
1011
|
+
endpoint,
|
|
1012
|
+
payload,
|
|
1013
|
+
endpoint === "plugin/update" ? 19e4 : RPC_CALL_TIMEOUT_MS
|
|
1014
|
+
)
|
|
1015
|
+
}
|
|
1016
|
+
),
|
|
880
1017
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
881
1018
|
UsagePanel,
|
|
882
1019
|
{
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/constants.js
|
|
2
|
+
var PLUGIN_ID = "dsh-grok-subscription";
|
|
2
3
|
var CORDIS_ID = "grok-subscription";
|
|
3
4
|
var PROVIDER_ID = "grok-build";
|
|
4
5
|
var DISPLAY_NAME = "Grok Build (subscription)";
|
|
@@ -936,6 +937,37 @@ function imageContentParts(blocks, imageParts) {
|
|
|
936
937
|
}
|
|
937
938
|
return parts;
|
|
938
939
|
}
|
|
940
|
+
var PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
941
|
+
var REPLAY_KIND = "grok-build-responses";
|
|
942
|
+
var REPLAY_VERSION = 1;
|
|
943
|
+
function promptCacheKey(options) {
|
|
944
|
+
const sessionId = typeof options?.sessionId === "string" ? options.sessionId.trim() : "";
|
|
945
|
+
if (!sessionId) return void 0;
|
|
946
|
+
const suffix = options.purpose === "compaction" || options.purpose === "session-title" ? `:${options.purpose}` : "";
|
|
947
|
+
const key = `grok${suffix}:${sessionId}`;
|
|
948
|
+
return key.length <= PROMPT_CACHE_KEY_MAX_LENGTH ? key : key.slice(0, PROMPT_CACHE_KEY_MAX_LENGTH);
|
|
949
|
+
}
|
|
950
|
+
function cacheStableTools(tools) {
|
|
951
|
+
if (!Array.isArray(tools) || tools.length === 0) return [];
|
|
952
|
+
return tools.map((tool, index) => ({ tool, index })).sort((left, right) => {
|
|
953
|
+
const byName = String(left.tool?.name ?? "").localeCompare(String(right.tool?.name ?? ""));
|
|
954
|
+
return byName === 0 ? left.index - right.index : byName;
|
|
955
|
+
}).map(({ tool }) => ({
|
|
956
|
+
type: "function",
|
|
957
|
+
name: tool.name,
|
|
958
|
+
description: tool.description,
|
|
959
|
+
parameters: tool.parameters
|
|
960
|
+
}));
|
|
961
|
+
}
|
|
962
|
+
function replayEnvelope(message) {
|
|
963
|
+
const state = message?.source?.replayState;
|
|
964
|
+
return state?.response?.kind === REPLAY_KIND && state.response.version === REPLAY_VERSION && Array.isArray(state.blocks) ? state : void 0;
|
|
965
|
+
}
|
|
966
|
+
function replayReasoningItem(message, contentIndex) {
|
|
967
|
+
const entry = replayEnvelope(message)?.blocks[contentIndex];
|
|
968
|
+
const item = entry?.type === "reasoning" ? entry.item : void 0;
|
|
969
|
+
return item?.type === "reasoning" && typeof item.encrypted_content === "string" && item.encrypted_content ? item : void 0;
|
|
970
|
+
}
|
|
939
971
|
function responsesInput(options, imageParts) {
|
|
940
972
|
const input = [];
|
|
941
973
|
const system = typeof options.system === "string" && options.system ? options.system : void 0;
|
|
@@ -955,7 +987,15 @@ function responsesInput(options, imageParts) {
|
|
|
955
987
|
continue;
|
|
956
988
|
}
|
|
957
989
|
if (role === "assistant" && toolCalls.length) {
|
|
958
|
-
|
|
990
|
+
const content = message.content ?? [];
|
|
991
|
+
for (let index = 0; index < content.length; index++) {
|
|
992
|
+
const block = content[index];
|
|
993
|
+
if (block?.type === "reasoning") {
|
|
994
|
+
const item = replayReasoningItem(message, index);
|
|
995
|
+
if (item) input.push(item);
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
if (block?.type !== "tool-call") continue;
|
|
959
999
|
input.push({
|
|
960
1000
|
type: "function_call",
|
|
961
1001
|
call_id: block.id,
|
|
@@ -973,6 +1013,23 @@ function responsesInput(options, imageParts) {
|
|
|
973
1013
|
if (parts.length > 0) input.push({ role, content: parts });
|
|
974
1014
|
continue;
|
|
975
1015
|
}
|
|
1016
|
+
if (role === "assistant") {
|
|
1017
|
+
const content = message.content ?? [];
|
|
1018
|
+
const output = [];
|
|
1019
|
+
let text2 = "";
|
|
1020
|
+
for (let index = 0; index < content.length; index++) {
|
|
1021
|
+
const block = content[index];
|
|
1022
|
+
if (block?.type === "reasoning") {
|
|
1023
|
+
const item = replayReasoningItem(message, index);
|
|
1024
|
+
if (item) output.push(item);
|
|
1025
|
+
} else if (block?.type === "text" && block.text) {
|
|
1026
|
+
text2 += block.text;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (text2) output.push({ role: "assistant", content: text2 });
|
|
1030
|
+
input.push(...output);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
976
1033
|
const text = textOf(message.content);
|
|
977
1034
|
if (!text) continue;
|
|
978
1035
|
input.push({ role, content: text });
|
|
@@ -1022,22 +1079,18 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1022
1079
|
const body = {
|
|
1023
1080
|
model: options.model,
|
|
1024
1081
|
input: responsesInput(options, await collectImageParts(options, options.resolveAttachments)),
|
|
1025
|
-
stream: true
|
|
1082
|
+
stream: true,
|
|
1083
|
+
// grok-4.7 returns ciphertext by default, but naming it keeps replay explicit
|
|
1084
|
+
// for every reasoning model on this proxy.
|
|
1085
|
+
include: ["reasoning.encrypted_content"]
|
|
1026
1086
|
};
|
|
1087
|
+
const cacheKey = promptCacheKey(options);
|
|
1088
|
+
if (cacheKey) body.prompt_cache_key = cacheKey;
|
|
1027
1089
|
if (typeof options.maxTokens === "number") body.max_output_tokens = options.maxTokens;
|
|
1028
1090
|
if (typeof options.temperature === "number") body.temperature = options.temperature;
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
name: tool.name,
|
|
1033
|
-
description: tool.description,
|
|
1034
|
-
parameters: tool.parameters
|
|
1035
|
-
}));
|
|
1036
|
-
}
|
|
1037
|
-
if (options.reasoningEffort) {
|
|
1038
|
-
body.reasoning = { effort: options.reasoningEffort };
|
|
1039
|
-
body.include = ["reasoning.encrypted_content"];
|
|
1040
|
-
}
|
|
1091
|
+
const tools = cacheStableTools(options.tools);
|
|
1092
|
+
if (tools.length) body.tools = tools;
|
|
1093
|
+
if (options.reasoningEffort) body.reasoning = { effort: options.reasoningEffort };
|
|
1041
1094
|
const response = await fetch(RESPONSES_URL, {
|
|
1042
1095
|
method: "POST",
|
|
1043
1096
|
headers,
|
|
@@ -1058,6 +1111,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1058
1111
|
let textContent = "";
|
|
1059
1112
|
let reasoningContent = "";
|
|
1060
1113
|
let nextIndex = 0;
|
|
1114
|
+
const emittedBlocks = [];
|
|
1061
1115
|
const toolBlocks = /* @__PURE__ */ new Map();
|
|
1062
1116
|
const toolNames = /* @__PURE__ */ new Map();
|
|
1063
1117
|
let usage;
|
|
@@ -1099,6 +1153,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1099
1153
|
if (!delta) return;
|
|
1100
1154
|
if (textIndex === void 0) {
|
|
1101
1155
|
textIndex = nextIndex++;
|
|
1156
|
+
emittedBlocks[textIndex] = { type: "text" };
|
|
1102
1157
|
yield { type: "block-start", index: textIndex, blockType: "text" };
|
|
1103
1158
|
}
|
|
1104
1159
|
textContent += delta;
|
|
@@ -1110,6 +1165,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1110
1165
|
if (!delta) return;
|
|
1111
1166
|
if (reasoningIndex === void 0) {
|
|
1112
1167
|
reasoningIndex = nextIndex++;
|
|
1168
|
+
emittedBlocks[reasoningIndex] = { type: "reasoning" };
|
|
1113
1169
|
yield { type: "block-start", index: reasoningIndex, blockType: "reasoning" };
|
|
1114
1170
|
}
|
|
1115
1171
|
reasoningContent += delta;
|
|
@@ -1118,6 +1174,14 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1118
1174
|
}
|
|
1119
1175
|
if (type === "response.output_item.added" || type === "response.output_item.done") {
|
|
1120
1176
|
const item = payload.item;
|
|
1177
|
+
if (item?.type === "reasoning" && typeof item.encrypted_content === "string" && item.encrypted_content) {
|
|
1178
|
+
if (reasoningIndex === void 0) {
|
|
1179
|
+
reasoningIndex = nextIndex++;
|
|
1180
|
+
emittedBlocks[reasoningIndex] = { type: "reasoning" };
|
|
1181
|
+
yield { type: "block-start", index: reasoningIndex, blockType: "reasoning" };
|
|
1182
|
+
}
|
|
1183
|
+
emittedBlocks[reasoningIndex] = { type: "reasoning", item };
|
|
1184
|
+
}
|
|
1121
1185
|
if (item?.type === "function_call") {
|
|
1122
1186
|
learnToolName(item.id, item.name);
|
|
1123
1187
|
learnToolName(item.call_id, item.name);
|
|
@@ -1143,6 +1207,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1143
1207
|
name: toolNames.get(id) ?? "tool",
|
|
1144
1208
|
arguments: ""
|
|
1145
1209
|
};
|
|
1210
|
+
emittedBlocks[tool.index] = { type: "tool-call" };
|
|
1146
1211
|
toolBlocks.set(id, tool);
|
|
1147
1212
|
yield { type: "block-start", index: tool.index, blockType: "tool-call" };
|
|
1148
1213
|
}
|
|
@@ -1160,6 +1225,7 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1160
1225
|
if (id && !toolBlocks.has(id)) {
|
|
1161
1226
|
const tool = { index: nextIndex++, id, name: toolNames.get(id) ?? "tool", arguments: "" };
|
|
1162
1227
|
toolBlocks.set(id, tool);
|
|
1228
|
+
emittedBlocks[tool.index] = { type: "tool-call" };
|
|
1163
1229
|
yield { type: "block-start", index: tool.index, blockType: "tool-call" };
|
|
1164
1230
|
}
|
|
1165
1231
|
adoptToolArguments(id, item.arguments);
|
|
@@ -1230,7 +1296,14 @@ async function* streamResponsesUnsafe(options, token) {
|
|
|
1230
1296
|
};
|
|
1231
1297
|
}
|
|
1232
1298
|
if (usage) yield { type: "usage", usage };
|
|
1233
|
-
|
|
1299
|
+
const finishChunk = { type: "finish", reason: finish };
|
|
1300
|
+
if (finish.kind !== "error" && finish.kind !== "aborted" && emittedBlocks.length === nextIndex) {
|
|
1301
|
+
finishChunk.replayState = {
|
|
1302
|
+
response: { kind: REPLAY_KIND, version: REPLAY_VERSION },
|
|
1303
|
+
blocks: emittedBlocks
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
yield finishChunk;
|
|
1234
1307
|
}
|
|
1235
1308
|
async function* streamResponses(options, token) {
|
|
1236
1309
|
for await (const chunk of streamResponsesUnsafe(options, token)) {
|
|
@@ -1902,6 +1975,251 @@ function createSessionService({
|
|
|
1902
1975
|
};
|
|
1903
1976
|
}
|
|
1904
1977
|
|
|
1978
|
+
// src/plugin-version.js
|
|
1979
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1980
|
+
import { readdir, readFile, realpath } from "node:fs/promises";
|
|
1981
|
+
import { homedir as homedir3 } from "node:os";
|
|
1982
|
+
import { join as join4 } from "node:path";
|
|
1983
|
+
import { fileURLToPath } from "node:url";
|
|
1984
|
+
var PACKAGE_NAME = PLUGIN_ID;
|
|
1985
|
+
var NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
1986
|
+
var DEFAULT_VERSION_TTL_MS = 5 * 6e4;
|
|
1987
|
+
var DEFAULT_VERSION_TIMEOUT_MS = 1e4;
|
|
1988
|
+
var DEFAULT_UPDATE_TIMEOUT_MS = 18e4;
|
|
1989
|
+
var clone = (value) => structuredClone(value);
|
|
1990
|
+
function parseSemver(value) {
|
|
1991
|
+
if (typeof value !== "string") return void 0;
|
|
1992
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u.exec(value.trim());
|
|
1993
|
+
if (match === null) return void 0;
|
|
1994
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]), pre: match[4] };
|
|
1995
|
+
}
|
|
1996
|
+
function compareSemver(a, b) {
|
|
1997
|
+
const left = parseSemver(a);
|
|
1998
|
+
const right = parseSemver(b);
|
|
1999
|
+
if (left === void 0 || right === void 0) return void 0;
|
|
2000
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
2001
|
+
if (left[key] !== right[key]) return left[key] < right[key] ? -1 : 1;
|
|
2002
|
+
}
|
|
2003
|
+
if (left.pre === right.pre) return 0;
|
|
2004
|
+
if (left.pre === void 0) return 1;
|
|
2005
|
+
if (right.pre === void 0) return -1;
|
|
2006
|
+
return left.pre < right.pre ? -1 : 1;
|
|
2007
|
+
}
|
|
2008
|
+
function classifySpec(spec) {
|
|
2009
|
+
if (typeof spec !== "string" || spec === "") return "unknown";
|
|
2010
|
+
if (/^(?:link|file):/u.test(spec)) return "link";
|
|
2011
|
+
if (/^(?:\d|[~^*])/u.test(spec)) return "npm";
|
|
2012
|
+
return "unknown";
|
|
2013
|
+
}
|
|
2014
|
+
var readManifest = async (path) => {
|
|
2015
|
+
try {
|
|
2016
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
2017
|
+
return parsed !== null && typeof parsed === "object" ? parsed : void 0;
|
|
2018
|
+
} catch {
|
|
2019
|
+
return void 0;
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
async function findInstall({ dshHome, ownPackageJsonUrl }) {
|
|
2023
|
+
let ownReal;
|
|
2024
|
+
try {
|
|
2025
|
+
ownReal = await realpath(fileURLToPath(ownPackageJsonUrl));
|
|
2026
|
+
} catch {
|
|
2027
|
+
ownReal = void 0;
|
|
2028
|
+
}
|
|
2029
|
+
let entries = [];
|
|
2030
|
+
try {
|
|
2031
|
+
entries = await readdir(join4(dshHome, "profiles"), { withFileTypes: true });
|
|
2032
|
+
} catch {
|
|
2033
|
+
entries = [];
|
|
2034
|
+
}
|
|
2035
|
+
const declared = [];
|
|
2036
|
+
for (const entry of entries) {
|
|
2037
|
+
if (!entry.isDirectory()) continue;
|
|
2038
|
+
const profileDir = join4(dshHome, "profiles", entry.name);
|
|
2039
|
+
const spec = (await readManifest(join4(profileDir, "package.json")))?.dependencies?.[PACKAGE_NAME];
|
|
2040
|
+
if (typeof spec !== "string") continue;
|
|
2041
|
+
declared.push({ profile: entry.name, spec });
|
|
2042
|
+
if (ownReal === void 0) continue;
|
|
2043
|
+
try {
|
|
2044
|
+
const installed = await realpath(join4(profileDir, "node_modules", PACKAGE_NAME, "package.json"));
|
|
2045
|
+
if (installed === ownReal) return { kind: classifySpec(spec), profile: entry.name };
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
if (declared.length === 1) return { kind: classifySpec(declared[0].spec), profile: declared[0].profile };
|
|
2050
|
+
return { kind: "unknown" };
|
|
2051
|
+
}
|
|
2052
|
+
function spawnRunCommand(argv, { signal, timeoutMs }) {
|
|
2053
|
+
return new Promise((resolve, reject) => {
|
|
2054
|
+
if (signal?.aborted) {
|
|
2055
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error("Grok plugin update aborted"));
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
const controller = new AbortController();
|
|
2059
|
+
const onAbort = () => controller.abort(signal.reason);
|
|
2060
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2061
|
+
const timer = setTimeout(() => controller.abort(new Error("Grok plugin update timed out")), timeoutMs);
|
|
2062
|
+
let settled = false;
|
|
2063
|
+
const settle = (callback, value) => {
|
|
2064
|
+
if (settled) return;
|
|
2065
|
+
settled = true;
|
|
2066
|
+
clearTimeout(timer);
|
|
2067
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2068
|
+
callback(value);
|
|
2069
|
+
};
|
|
2070
|
+
let child;
|
|
2071
|
+
try {
|
|
2072
|
+
child = spawn2(argv[0], argv.slice(1), { stdio: ["ignore", "pipe", "pipe"], signal: controller.signal });
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
settle(reject, error);
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
let stdout = "";
|
|
2078
|
+
let stderr = "";
|
|
2079
|
+
child.stdout?.on("data", (chunk) => {
|
|
2080
|
+
stdout = (stdout + chunk).slice(-8192);
|
|
2081
|
+
});
|
|
2082
|
+
child.stderr?.on("data", (chunk) => {
|
|
2083
|
+
stderr = (stderr + chunk).slice(-8192);
|
|
2084
|
+
});
|
|
2085
|
+
child.on("error", (error) => settle(reject, error));
|
|
2086
|
+
child.on("close", (code) => {
|
|
2087
|
+
if (controller.signal.aborted) {
|
|
2088
|
+
const reason = controller.signal.reason;
|
|
2089
|
+
settle(reject, reason instanceof Error ? reason : new Error("Grok plugin update aborted"));
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
settle(resolve, { code: code ?? 1, stdout, stderr });
|
|
2093
|
+
});
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
function createGrokPluginManager({
|
|
2097
|
+
fetchImpl = globalThis.fetch,
|
|
2098
|
+
runCommand = spawnRunCommand,
|
|
2099
|
+
execPath = process.execPath,
|
|
2100
|
+
binPath = process.argv[1],
|
|
2101
|
+
env = process.env,
|
|
2102
|
+
ownPackageJsonUrl = new URL("../package.json", import.meta.url),
|
|
2103
|
+
registryUrl = NPM_REGISTRY_LATEST_URL,
|
|
2104
|
+
now = Date.now,
|
|
2105
|
+
ttlMs = DEFAULT_VERSION_TTL_MS,
|
|
2106
|
+
timeoutMs = DEFAULT_VERSION_TIMEOUT_MS,
|
|
2107
|
+
updateTimeoutMs = DEFAULT_UPDATE_TIMEOUT_MS
|
|
2108
|
+
} = {}) {
|
|
2109
|
+
if (typeof fetchImpl !== "function") throw new Error("Grok plugin manager requires fetch");
|
|
2110
|
+
if (typeof runCommand !== "function") throw new Error("Grok plugin manager requires runCommand");
|
|
2111
|
+
const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME !== "" ? env.DSH_HOME : join4(homedir3(), ".dsh");
|
|
2112
|
+
let cached;
|
|
2113
|
+
let inFlight;
|
|
2114
|
+
let generation = 0;
|
|
2115
|
+
let updating = false;
|
|
2116
|
+
const fetchLatest = async (signal) => {
|
|
2117
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
2118
|
+
const requestSignal = signal === void 0 ? timeoutSignal : AbortSignal.any([signal, timeoutSignal]);
|
|
2119
|
+
let response;
|
|
2120
|
+
try {
|
|
2121
|
+
response = await fetchImpl(registryUrl, {
|
|
2122
|
+
headers: { Accept: "application/json" },
|
|
2123
|
+
redirect: "error",
|
|
2124
|
+
signal: requestSignal
|
|
2125
|
+
});
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
if (signal?.aborted) throw error;
|
|
2128
|
+
throw new Error("Grok plugin version check failed");
|
|
2129
|
+
}
|
|
2130
|
+
if (!response.ok) throw new Error("Grok plugin version check failed");
|
|
2131
|
+
let version;
|
|
2132
|
+
try {
|
|
2133
|
+
version = (await response.json())?.version;
|
|
2134
|
+
} catch {
|
|
2135
|
+
version = void 0;
|
|
2136
|
+
}
|
|
2137
|
+
if (parseSemver(version) === void 0) throw new Error("Grok plugin version check failed");
|
|
2138
|
+
return version;
|
|
2139
|
+
};
|
|
2140
|
+
const readOwnVersion = async () => {
|
|
2141
|
+
const version = (await readManifest(fileURLToPath(ownPackageJsonUrl)))?.version;
|
|
2142
|
+
if (parseSemver(version) === void 0) throw new Error("Grok plugin version is unavailable");
|
|
2143
|
+
return version;
|
|
2144
|
+
};
|
|
2145
|
+
const load = async (signal) => {
|
|
2146
|
+
signal?.throwIfAborted();
|
|
2147
|
+
const [current, install, latest] = await Promise.all([
|
|
2148
|
+
readOwnVersion(),
|
|
2149
|
+
findInstall({ dshHome, ownPackageJsonUrl }),
|
|
2150
|
+
fetchLatest(signal)
|
|
2151
|
+
]);
|
|
2152
|
+
return {
|
|
2153
|
+
current,
|
|
2154
|
+
latest,
|
|
2155
|
+
updateAvailable: compareSemver(latest, current) === 1,
|
|
2156
|
+
install,
|
|
2157
|
+
fetchedAt: now()
|
|
2158
|
+
};
|
|
2159
|
+
};
|
|
2160
|
+
return Object.freeze({
|
|
2161
|
+
async read({ force = false, signal } = {}) {
|
|
2162
|
+
signal?.throwIfAborted();
|
|
2163
|
+
if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return clone(cached);
|
|
2164
|
+
const createPending = (requestSignal) => {
|
|
2165
|
+
const observedGeneration = generation;
|
|
2166
|
+
return load(requestSignal).then((value) => {
|
|
2167
|
+
if (generation === observedGeneration) cached = value;
|
|
2168
|
+
return value;
|
|
2169
|
+
});
|
|
2170
|
+
};
|
|
2171
|
+
if (force) return clone(await createPending(signal));
|
|
2172
|
+
const pending = inFlight ?? createPending();
|
|
2173
|
+
if (inFlight === void 0) {
|
|
2174
|
+
inFlight = pending;
|
|
2175
|
+
const clear = () => {
|
|
2176
|
+
if (inFlight === pending) inFlight = void 0;
|
|
2177
|
+
};
|
|
2178
|
+
void pending.then(clear, clear);
|
|
2179
|
+
}
|
|
2180
|
+
return clone(await (signal === void 0 ? pending : Promise.race([
|
|
2181
|
+
pending,
|
|
2182
|
+
new Promise((_, reject) => {
|
|
2183
|
+
const onAbort = () => reject(signal.reason instanceof Error ? signal.reason : new Error("Grok plugin version check aborted"));
|
|
2184
|
+
if (signal.aborted) onAbort();
|
|
2185
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
2186
|
+
})
|
|
2187
|
+
])));
|
|
2188
|
+
},
|
|
2189
|
+
async update({ signal } = {}) {
|
|
2190
|
+
signal?.throwIfAborted();
|
|
2191
|
+
if (updating) throw new Error("Grok plugin update is already running");
|
|
2192
|
+
updating = true;
|
|
2193
|
+
try {
|
|
2194
|
+
const install = await findInstall({ dshHome, ownPackageJsonUrl });
|
|
2195
|
+
if (install.kind === "link") throw new Error("Grok plugin is installed from a local checkout");
|
|
2196
|
+
if (install.kind !== "npm" || install.profile === void 0) {
|
|
2197
|
+
throw new Error("Grok plugin update could not find the owning profile");
|
|
2198
|
+
}
|
|
2199
|
+
const latest = await fetchLatest(signal);
|
|
2200
|
+
const argv = binPath === void 0 ? ["dsh", "plugin", "--profile", install.profile, "add", `${PACKAGE_NAME}@${latest}`] : [execPath, binPath, "plugin", "--profile", install.profile, "add", `${PACKAGE_NAME}@${latest}`];
|
|
2201
|
+
let result;
|
|
2202
|
+
try {
|
|
2203
|
+
result = await runCommand(argv, { signal, timeoutMs: updateTimeoutMs });
|
|
2204
|
+
} catch (error) {
|
|
2205
|
+
if (signal?.aborted) throw error;
|
|
2206
|
+
throw new Error("Grok plugin update failed");
|
|
2207
|
+
}
|
|
2208
|
+
if (result.code !== 0) throw new Error("Grok plugin update failed");
|
|
2209
|
+
generation += 1;
|
|
2210
|
+
cached = void 0;
|
|
2211
|
+
return { version: latest, profile: install.profile };
|
|
2212
|
+
} finally {
|
|
2213
|
+
updating = false;
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
invalidate() {
|
|
2217
|
+
generation += 1;
|
|
2218
|
+
cached = void 0;
|
|
2219
|
+
}
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
|
|
1905
2223
|
// src/rpc-contract.js
|
|
1906
2224
|
var RPC_ENDPOINTS = Object.freeze([
|
|
1907
2225
|
"status",
|
|
@@ -1911,7 +2229,9 @@ var RPC_ENDPOINTS = Object.freeze([
|
|
|
1911
2229
|
"logout",
|
|
1912
2230
|
"catalog/refresh",
|
|
1913
2231
|
"usage",
|
|
1914
|
-
"usage/refresh"
|
|
2232
|
+
"usage/refresh",
|
|
2233
|
+
"plugin/version",
|
|
2234
|
+
"plugin/update"
|
|
1915
2235
|
]);
|
|
1916
2236
|
function publicResult(value) {
|
|
1917
2237
|
return { ok: true, value };
|
|
@@ -1985,7 +2305,18 @@ function sanitizeUsage(usage) {
|
|
|
1985
2305
|
}
|
|
1986
2306
|
return out;
|
|
1987
2307
|
}
|
|
1988
|
-
|
|
2308
|
+
function publicPluginVersion(value) {
|
|
2309
|
+
const installKind = value?.install?.kind;
|
|
2310
|
+
return {
|
|
2311
|
+
current: value.current,
|
|
2312
|
+
latest: value.latest,
|
|
2313
|
+
updateAvailable: value.updateAvailable === true,
|
|
2314
|
+
install: {
|
|
2315
|
+
kind: installKind === "npm" || installKind === "link" ? installKind : "unknown"
|
|
2316
|
+
}
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
async function dispatch(session, endpoint, diagnostics, payload, signal, pluginManager) {
|
|
1989
2320
|
if (endpoint === "status") {
|
|
1990
2321
|
const status = await session.status();
|
|
1991
2322
|
return publicResult(stripSecrets({
|
|
@@ -2011,14 +2342,27 @@ async function dispatch(session, endpoint, diagnostics) {
|
|
|
2011
2342
|
}
|
|
2012
2343
|
if (endpoint === "login/cli") return publicResult(stripSecrets(await session.login({ device: false })));
|
|
2013
2344
|
if (endpoint === "login/device") return publicResult(stripSecrets(await session.login({ device: true })));
|
|
2345
|
+
if (endpoint === "plugin/version" || endpoint === "plugin/update") {
|
|
2346
|
+
if (!pluginManager) return publicError(new Error("Grok plugin update is unavailable"));
|
|
2347
|
+
if (endpoint === "plugin/version") {
|
|
2348
|
+
return publicResult(publicPluginVersion(await pluginManager.read({
|
|
2349
|
+
force: payload?.force === true,
|
|
2350
|
+
signal
|
|
2351
|
+
})));
|
|
2352
|
+
}
|
|
2353
|
+
const updated = await pluginManager.update({ signal });
|
|
2354
|
+
return publicResult({ version: updated.version });
|
|
2355
|
+
}
|
|
2014
2356
|
return publicError(new Error(`Unknown Grok subscription RPC: ${endpoint}`));
|
|
2015
2357
|
}
|
|
2016
2358
|
function createRpcHandler(session, options = {}) {
|
|
2017
2359
|
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : RPC_HANDLER_TIMEOUT_MS;
|
|
2018
|
-
return async function handle(endpoint,
|
|
2360
|
+
return async function handle(endpoint, payload, signal) {
|
|
2019
2361
|
try {
|
|
2362
|
+
const operation = dispatch(session, endpoint, options.diagnostics, payload, signal, options.pluginManager);
|
|
2363
|
+
if (endpoint === "plugin/update") return await operation;
|
|
2020
2364
|
return await withTimeout(
|
|
2021
|
-
|
|
2365
|
+
operation,
|
|
2022
2366
|
timeoutMs,
|
|
2023
2367
|
`Grok subscription RPC "${endpoint}" timed out after ${timeoutMs}ms`,
|
|
2024
2368
|
{ unref: false }
|
|
@@ -2164,7 +2508,9 @@ function apply(ctx, options = {}) {
|
|
|
2164
2508
|
onCatalogChange: notifyCatalogChange
|
|
2165
2509
|
});
|
|
2166
2510
|
const adapterState = { kind: "custom-mvp", upgraded: false };
|
|
2511
|
+
const pluginManager = options.pluginManager ?? createGrokPluginManager();
|
|
2167
2512
|
const handler = createRpcHandler(session, {
|
|
2513
|
+
pluginManager,
|
|
2168
2514
|
diagnostics: () => ({
|
|
2169
2515
|
adapter: adapterState.upgraded ? "pi-ai" : "fallback",
|
|
2170
2516
|
adapterKind: adapterState.kind,
|