dsh-grok-subscription 1.1.0 → 1.1.2

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.2 — 2026-09-28
4
+
5
+ - Fix **Windows reporting "no grok CLI"** with the official CLI installed. Detection probed for a file literally named `grok`, but `fs.existsSync` never applies Windows' `PATHEXT` resolution, so the installed `grok.exe` was invisible: `resolveGrokBin()` fell back to the bare name, `grokCliAvailable()` returned `false`, and every CLI-backed path — session pull, CLI/device-code login, automatic renewal — was short-circuited before it ran. Settings showed the orange "CLI not found" warning and the plugin stayed on its built-in fallback adapter, even though `spawn("grok")` itself works on Windows.
6
+ - Probe every name the platform would run. The Windows branch now tries the `PATHEXT` suffixes (defaulting to `.COM;.EXE;.BAT;.CMD` when `PATHEXT` is absent) for an explicit `DSH_GROK_BIN` path, for the `${GROK_HOME:-%USERPROFILE%\.grok}\bin\grok` install, and for every `PATH` entry, and it reads quoted entries such as `"C:\Program Files\…"`. Resolution and execution agree, because the existing `grok.exe` found by the probe is the path that gets spawned.
7
+ - Make the platform injectable in both helpers so the Windows branch runs on any CI OS. `tests/cli-detection.test.mjs` adds 13 cases — the two requested by the report, `PATHEXT` handling, quoted `PATH`, and a real-temp-directory `grok.exe` smoke test — and asserts unchanged POSIX behaviour.
8
+ - No session, login, renewal, model-routing, proxy, or quota behaviour changes. A repository-wide sweep of the remaining `existsSync`/`spawn` path joins found no other defect of this class. Restart DSH fully after updating.
9
+
10
+ ## 1.1.1 — 2026-09-28
11
+
12
+ - Fix **Update plugin** failing in the desktop app. The update built its command from `process.argv[1]`, which is the entry script of whichever process loaded the plugin — the `dsh` CLI only when the host *is* the CLI. The desktop app loads plugins inside `@deepseek-ai/dsh-desktop-host`, so that argument was the desktop host entry and the update relaunched the application with `plugin …` as script arguments instead of installing anything. The version check kept working, so the update button appeared to run and then reported failure.
13
+ - Install updates into the owning profile with pnpm directly, because `dsh plugin --profile desktop` is refused outright: `profile "desktop" is managed exclusively by the Electron application`. The bundled pnpm entry is located by file name in the host's own launch arguments, with the runtime directory carrying the `@deepseek-ai/dsh` package as the fallback, so `PATH` never decides which package manager runs.
14
+ - Pin the install (`--save-exact`) and disable pnpm's release-age gate (`--config.minimumReleaseAge=0`), which would otherwise quietly keep the installed version for a release younger than 24 hours — exactly the release this button exists to fetch.
15
+ - Read the installed manifest back and compare it with the requested version before reporting success, so an update that installs nothing is reported as a failure rather than a success.
16
+ - No model-routing, authentication, proxy, or quota behavior changes. Restart DSH fully after updating so the new version loads.
17
+
3
18
  ## 1.1.0 — 2026-09-28
4
19
 
5
20
  - Add the original Comet Navigator mascot and align the Chinese/English homepages around features, installation, daily use, screenshots, and maintenance.
package/README.en.md CHANGED
@@ -130,6 +130,8 @@ dsh plugin --profile web add dsh-grok-subscription@latest
130
130
 
131
131
  For a GitHub-source install, use `dsh plugin --profile web update dsh-grok-subscription`. For a local `link:` install, pull and rebuild the checkout instead of replacing its development link.
132
132
 
133
+ The desktop app owns its `desktop` profile exclusively, so `dsh plugin --profile desktop …` is refused there. The desktop **Update plugin** button therefore installs the exact version inside that profile directory with DSH's own bundled pnpm; before `1.1.1` that button always failed in the desktop app. Manual steps: [Updates and sign-out](docs/guide.en.md#updates-and-sign-out).
134
+
133
135
  **Uninstall:**
134
136
 
135
137
  ```sh
package/README.md CHANGED
@@ -130,6 +130,8 @@ dsh plugin --profile web add dsh-grok-subscription@latest
130
130
 
131
131
  GitHub 来源安装可使用 `dsh plugin --profile web update dsh-grok-subscription`;本地 `link:` 安装应拉取对应仓库并重新构建,不要替换开发链接。
132
132
 
133
+ 桌面版由 Electron 应用独占 `desktop` profile,`dsh plugin --profile desktop …` 会被拒绝,因此桌面版的「更新插件」由插件自己在该 profile 目录中用 DSH 自带的 pnpm 安装精确版本;`1.1.1` 之前该按钮在桌面版必然失败,手动步骤见[更新与登出](docs/guide.zh-CN.md#更新与登出)。
134
+
133
135
  **卸载:**
134
136
 
135
137
  ```sh
package/docs/guide.en.md CHANGED
@@ -18,7 +18,7 @@ dsh plugin --profile web list dsh-grok-subscription --depth 0
18
18
  For reproducibility, pin a published version, for example:
19
19
 
20
20
  ```sh
21
- dsh plugin --profile web add dsh-grok-subscription@1.1.0
21
+ dsh plugin --profile web add dsh-grok-subscription@1.1.2
22
22
  ```
23
23
 
24
24
  **Install from GitHub:**
@@ -40,7 +40,7 @@ Restart the target DSH instance after installation. For local diagnosis, `dsh --
40
40
 
41
41
  ## Sign-in and credentials
42
42
 
43
- **The official Grok Build CLI must be available on the DSH Host**, not just on the phone or another computer viewing the Web UI. The plugin checks `DSH_GROK_BIN`, then `${GROK_HOME:-$HOME/.grok}/bin/grok`, then `grok` on `PATH`. See [session.js](../src/session.js).
43
+ **The official Grok Build CLI must be available on the DSH Host**, not just on the phone or another computer viewing the Web UI. The plugin checks `DSH_GROK_BIN`, then `${GROK_HOME:-$HOME/.grok}/bin/grok`, then `grok` on `PATH`. On Windows the `PATHEXT` suffixes (`.exe`, `.cmd`, …) are completed too, so a `grok.exe` install is recognised. See [session.js](../src/session.js).
44
44
 
45
45
  Use **CLI login** or **Device-code login** in Settings. The plugin invokes the official CLI and shows the available authorization link and one-time code in the panel. Alternatively, run this locally:
46
46
 
@@ -110,7 +110,7 @@ That task makes a real model request and uses the selected service's quota; it i
110
110
 
111
111
  **`dsh` not found.** Use the full `npx` prefix you use to launch DSH, or check the installed launcher's path.
112
112
 
113
- **`grok` not found or no login window.** Check that the CLI is installed on the DSH Host and its process sees `DSH_GROK_BIN` / `PATH`. Look for a clickable login link in the panel. A browser failing to open does not by itself prove the authorization service is unreachable.
113
+ **`grok` not found or no login window.** Check that the CLI is installed on the DSH Host and its process sees `DSH_GROK_BIN` / `PATH`. Look for a clickable login link in the panel. A browser failing to open does not by itself prove the authorization service is unreachable. On Windows, if the CLI is not on `PATH`, put the absolute `grok.exe` path in the user-level `DSH_GROK_BIN` (for example `setx DSH_GROK_BIN "C:\Users\<user>\.grok\bin\grok.exe"`) and restart DSH completely; note that `DSH_*` is a DSH bootstrap prefix, so declaring it in `~/.dsh/.env` makes that environment layer fail.
114
114
 
115
115
  **`fetch failed` or `Billing request timed out`.** Check access to the account service and `cli-chat-proxy.grok.com` separately. When a proxy is needed, set standard `https_proxy` / `http_proxy` variables in DSH's launch environment or `~/.dsh/.env`, not the project directory's `.env`, then manually restart DSH. The plugin uses the host network setup rather than providing its own proxy service. Network conditions are one possibility; also check authentication and backend errors.
116
116
 
@@ -128,6 +128,15 @@ That task makes a real model request and uses the selected service's quota; it i
128
128
 
129
129
  For npm installs, update in Settings or run `dsh plugin --profile web add dsh-grok-subscription@latest`. For GitHub-source installs, run `dsh plugin --profile web update dsh-grok-subscription`. For local links, pull, test, and rebuild the checkout. Restart DSH afterwards.
130
130
 
131
+ In the desktop app the `desktop` profile is owned exclusively by the Electron application, and `dsh plugin --profile desktop …` is refused outright (`profile "desktop" is managed exclusively by the Electron application`), so the desktop app can only be updated by the plugin itself. From 1.1.1 the **Update plugin** button installs the exact version inside that profile directory with DSH's own bundled pnpm. If you are still on an older version and the button reports an error, run the equivalent by hand (pnpm ≥ 11):
132
+
133
+ ```sh
134
+ cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
135
+ pnpm add --save-exact dsh-grok-subscription@1.1.2
136
+ ```
137
+
138
+ 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.
139
+
131
140
  Uninstall with `dsh plugin --profile web remove dsh-grok-subscription`. This leaves the profile, other plugins, and the CLI's `auth.json` intact.
132
141
 
133
142
  The Settings logout action clears plugin-side session state; it does not revoke the official CLI session. If the CLI file remains, a later pull or startup may sync it again. To end the official session, use the official CLI / account's sign-out and authorization-management flow. Do not delete an entire DSH profile to handle one account.
@@ -18,7 +18,7 @@ dsh plugin --profile web list dsh-grok-subscription --depth 0
18
18
  需要可重复安装时,固定已发布版本,例如:
19
19
 
20
20
  ```sh
21
- dsh plugin --profile web add dsh-grok-subscription@1.1.0
21
+ dsh plugin --profile web add dsh-grok-subscription@1.1.2
22
22
  ```
23
23
 
24
24
  **GitHub 来源安装:**
@@ -40,7 +40,7 @@ npx -y @deepseek-ai/dsh@0.1.5-rc.2 plugin --profile web list dsh-grok-subscripti
40
40
 
41
41
  ## 登录与凭据
42
42
 
43
- **官方 Grok Build CLI 必须在 DSH Host 上可用**,不是只安装在访问网页的手机或另一台电脑上。插件按以下顺序寻找命令:`DSH_GROK_BIN` 指定的路径、`${GROK_HOME:-$HOME/.grok}/bin/grok`、系统 `PATH` 中的 `grok`。实现见 [session.js](../src/session.js)。
43
+ **官方 Grok Build CLI 必须在 DSH Host 上可用**,不是只安装在访问网页的手机或另一台电脑上。插件按以下顺序寻找命令:`DSH_GROK_BIN` 指定的路径、`${GROK_HOME:-$HOME/.grok}/bin/grok`、系统 `PATH` 中的 `grok`。Windows 上按 `PATHEXT` 补全后缀(`.exe`、`.cmd` 等),所以安装成 `grok.exe` 也能识别。实现见 [session.js](../src/session.js)。
44
44
 
45
45
  可在设置页发起 **CLI 登录** 或 **设备码登录**;插件会调用官方 CLI,并将可用的授权链接和一次性验证码显示在面板中。也可以自行在本机运行:
46
46
 
@@ -110,7 +110,7 @@ dsh --profile headless "只回复:ok"
110
110
 
111
111
  **找不到 `dsh`。** 使用你运行 DSH 时的完整 `npx` 前缀,或检查已安装启动器的命令路径。
112
112
 
113
- **找不到 `grok` / 不弹出登录窗口。** 检查 CLI 是否安装在 DSH Host、`DSH_GROK_BIN` / `PATH` 是否对该进程可见,以及面板是否已显示可点击授权链接。浏览器没有弹出,不一定等于授权服务不可达。
113
+ **找不到 `grok` / 不弹出登录窗口。** 检查 CLI 是否安装在 DSH Host、`DSH_GROK_BIN` / `PATH` 是否对该进程可见,以及面板是否已显示可点击授权链接。浏览器没有弹出,不一定等于授权服务不可达。Windows 上若 CLI 不在 `PATH` 中,可把 `grok.exe` 的绝对路径写入用户级 `DSH_GROK_BIN`(例如 `setx DSH_GROK_BIN "C:\Users\<用户>\.grok\bin\grok.exe"`),再完全重启 DSH;注意 `DSH_*` 是 DSH 的 bootstrap 前缀,写进 `~/.dsh/.env` 会让该环境层直接报错。
114
114
 
115
115
  **`fetch failed` / `Billing request timed out`。** 分别检查账号服务和 `cli-chat-proxy.grok.com` 的网络连接。需要代理时,在 DSH 启动环境或 `~/.dsh/.env` 中设置标准 `https_proxy` / `http_proxy`,而不是放进项目目录的 `.env`;再手动重启 DSH。插件沿用宿主网络配置,不提供单独代理服务。网络只是可能原因,还应检查认证和服务端错误。
116
116
 
@@ -128,6 +128,15 @@ dsh --profile headless "只回复:ok"
128
128
 
129
129
  npm 安装可在设置页更新,或运行 `dsh plugin --profile web add dsh-grok-subscription@latest`。GitHub 来源安装可运行 `dsh plugin --profile web update dsh-grok-subscription`。本地链接安装则拉取对应 checkout、测试并构建。更新后手动重启 DSH。
130
130
 
131
+ 桌面版的 `desktop` profile 由 Electron 应用独占,`dsh plugin --profile desktop …` 会被直接拒绝(`profile "desktop" is managed exclusively by the Electron application`),所以桌面版只能由插件自己更新。1.1.1 起「更新插件」会在该 profile 目录中用 DSH 自带的 pnpm 安装精确版本;若你仍停留在旧版本且按钮报错,可在终端手动执行(pnpm 需 ≥ 11):
132
+
133
+ ```sh
134
+ cd "$HOME/.dsh/profiles/desktop" # Windows: cd "$env:USERPROFILE\.dsh\profiles\desktop"
135
+ pnpm add --save-exact dsh-grok-subscription@1.1.2
136
+ ```
137
+
138
+ 然后完全退出并重启桌面版。更新成功后该 profile 的 `package.json` 里依赖应变为目标版本,`dsh.profile.bundles` 条目保持不变。仅刷新网页不够:Host 未重启时仍会加载旧版本。
139
+
131
140
  卸载使用 `dsh plugin --profile web remove dsh-grok-subscription`。这不删除整个 profile、其他插件或 Grok CLI 的 `auth.json`。
132
141
 
133
142
  设置页「登出」清理的是插件侧会话状态,不等于撤销官方 CLI 会话。CLI 文件仍然存在时,之后重新拉取或启动可能再次同步登录;彻底结束官方会话应使用官方 CLI / 账号提供的登出与授权管理流程。不要靠删除整个 DSH profile 来处理一个账号。
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 1.1.0",
42
+ subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.1.2",
43
43
  signedIn: "\u5DF2\u767B\u5F55",
44
44
  signedOut: "\u672A\u767B\u5F55",
45
45
  account: "\u8D26\u6237",
@@ -119,7 +119,7 @@ var zh = {
119
119
  var en = {
120
120
  nav: "Grok Subscription",
121
121
  title: "Grok Subscription",
122
- subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.1.0",
122
+ subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.1.2",
123
123
  signedIn: "Signed in",
124
124
  signedOut: "Signed out",
125
125
  account: "Account",
package/lib/index.js CHANGED
@@ -1636,17 +1636,38 @@ async function createGrokBuildAdapter(session, options = {}) {
1636
1636
  }
1637
1637
 
1638
1638
  // src/session.js
1639
- function resolveGrokBin(env = process.env, exists = existsSync3) {
1640
- if (typeof env.DSH_GROK_BIN === "string" && env.DSH_GROK_BIN.trim()) return env.DSH_GROK_BIN.trim();
1639
+ var WINDOWS_EXEC_EXTENSIONS = [".COM", ".EXE", ".BAT", ".CMD"];
1640
+ var hasPathSeparator = (value) => value.includes("/") || value.includes("\\");
1641
+ function executableNames(bin, platform, env) {
1642
+ if (platform !== "win32") return [bin];
1643
+ const declared = String(env.PATHEXT ?? "").split(";").map((part) => part.trim()).filter(Boolean).map((part) => part.startsWith(".") ? part : `.${part}`);
1644
+ const extensions = declared.length > 0 ? declared : WINDOWS_EXEC_EXTENSIONS;
1645
+ return [bin, ...extensions.map((extension) => `${bin}${extension}`)];
1646
+ }
1647
+ function firstExisting(names, exists) {
1648
+ for (const name2 of names) if (exists(name2)) return name2;
1649
+ return void 0;
1650
+ }
1651
+ function pathEntry(dir, platform) {
1652
+ return platform === "win32" && dir.length > 1 && dir.startsWith('"') && dir.endsWith('"') ? dir.slice(1, -1) : dir;
1653
+ }
1654
+ function resolveGrokBin(env = process.env, exists = existsSync3, platform = process.platform) {
1655
+ const override = typeof env.DSH_GROK_BIN === "string" && env.DSH_GROK_BIN.trim() ? env.DSH_GROK_BIN.trim() : void 0;
1656
+ if (override !== void 0) {
1657
+ if (!hasPathSeparator(override)) return override;
1658
+ return firstExisting(executableNames(override, platform, env), exists) ?? override;
1659
+ }
1641
1660
  const local = join3(grokHome(env), "bin", "grok");
1642
- if (exists(local)) return local;
1661
+ const installed = firstExisting(executableNames(local, platform, env), exists);
1662
+ if (installed !== void 0) return installed;
1643
1663
  return "grok";
1644
1664
  }
1645
- function grokCliAvailable(env = process.env, exists = existsSync3) {
1646
- const bin = resolveGrokBin(env, exists);
1647
- if (bin.includes("/") || bin.includes("\\")) return exists(bin);
1648
- const delimiter = process.platform === "win32" ? ";" : ":";
1649
- return String(env.PATH ?? "").split(delimiter).some((dir) => dir && exists(join3(dir, bin)));
1665
+ function grokCliAvailable(env = process.env, exists = existsSync3, platform = process.platform) {
1666
+ const bin = resolveGrokBin(env, exists, platform);
1667
+ if (hasPathSeparator(bin)) return exists(bin);
1668
+ const delimiter = platform === "win32" ? ";" : ":";
1669
+ const names = executableNames(bin, platform, env);
1670
+ return String(env.PATH ?? "").split(delimiter).some((dir) => dir && names.some((name2) => exists(join3(pathEntry(dir, platform), name2))));
1650
1671
  }
1651
1672
  function openExternal(url, options = {}) {
1652
1673
  const spawnFn = options.spawn ?? spawn;
@@ -2121,6 +2142,7 @@ function createSessionService({
2121
2142
 
2122
2143
  // src/plugin-version.js
2123
2144
  import { spawn as spawn2 } from "node:child_process";
2145
+ import { existsSync as existsSync4 } from "node:fs";
2124
2146
  import { readdir, readFile, realpath } from "node:fs/promises";
2125
2147
  import { homedir as homedir3 } from "node:os";
2126
2148
  import { join as join4 } from "node:path";
@@ -2130,6 +2152,8 @@ var NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest
2130
2152
  var DEFAULT_VERSION_TTL_MS = 5 * 6e4;
2131
2153
  var DEFAULT_VERSION_TIMEOUT_MS = 1e4;
2132
2154
  var DEFAULT_UPDATE_TIMEOUT_MS = 18e4;
2155
+ var DEFAULT_SETTLE_DELAY_MS = 250;
2156
+ var UPDATE_RELEASE_AGE_MINUTES = 0;
2133
2157
  var clone = (value) => structuredClone(value);
2134
2158
  function parseSemver(value) {
2135
2159
  if (typeof value !== "string") return void 0;
@@ -2163,6 +2187,16 @@ var readManifest = async (path) => {
2163
2187
  return void 0;
2164
2188
  }
2165
2189
  };
2190
+ async function confirmInstalled({ profileDir, expected, settleDelayMs, attempts = 3 }) {
2191
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
2192
+ const installed = (await readManifest(join4(profileDir, "node_modules", PACKAGE_NAME, "package.json")))?.version;
2193
+ if (installed === expected) return;
2194
+ if (attempt < attempts) await new Promise((resolve) => {
2195
+ setTimeout(resolve, settleDelayMs);
2196
+ });
2197
+ }
2198
+ throw new Error("Grok plugin update did not install the requested version");
2199
+ }
2166
2200
  async function findInstall({ dshHome, ownPackageJsonUrl }) {
2167
2201
  let ownReal;
2168
2202
  try {
@@ -2193,7 +2227,28 @@ async function findInstall({ dshHome, ownPackageJsonUrl }) {
2193
2227
  if (declared.length === 1) return { kind: classifySpec(declared[0].spec), profile: declared[0].profile };
2194
2228
  return { kind: "unknown" };
2195
2229
  }
2196
- function spawnRunCommand(argv, { signal, timeoutMs }) {
2230
+ var isExistingPath = (value) => typeof value === "string" && value !== "" && existsSync4(value);
2231
+ var PNPM_SCRIPT = /[\\/]pnpm\.(?:mjs|cjs|js)$/u;
2232
+ var dshRuntimeRoot = (value) => isExistingPath(value) && existsSync4(join4(value, "node_modules", "@deepseek-ai", "dsh", "package.json")) ? value : void 0;
2233
+ function resolvePnpm({ argv } = {}) {
2234
+ const entries = Array.isArray(argv) ? argv : [];
2235
+ for (const entry of entries) {
2236
+ if (isExistingPath(entry) && PNPM_SCRIPT.test(entry)) return entry;
2237
+ }
2238
+ for (const entry of entries) {
2239
+ const root = dshRuntimeRoot(entry);
2240
+ if (root === void 0) continue;
2241
+ const script = join4(root, "..", "pnpm", "bin", "pnpm.mjs");
2242
+ if (existsSync4(script)) return script;
2243
+ }
2244
+ return void 0;
2245
+ }
2246
+ function resolveLauncher({ argv, execPath }) {
2247
+ const pnpm = resolvePnpm({ argv });
2248
+ if (pnpm === void 0) return { command: "pnpm", prefix: [] };
2249
+ return { command: execPath, prefix: [pnpm] };
2250
+ }
2251
+ function spawnRunCommand({ command, args, cwd }, { signal, timeoutMs }) {
2197
2252
  return new Promise((resolve, reject) => {
2198
2253
  if (signal?.aborted) {
2199
2254
  reject(signal.reason instanceof Error ? signal.reason : new Error("Grok plugin update aborted"));
@@ -2213,7 +2268,11 @@ function spawnRunCommand(argv, { signal, timeoutMs }) {
2213
2268
  };
2214
2269
  let child;
2215
2270
  try {
2216
- child = spawn2(argv[0], argv.slice(1), { stdio: ["ignore", "pipe", "pipe"], signal: controller.signal });
2271
+ child = spawn2(command, args, {
2272
+ stdio: ["ignore", "pipe", "pipe"],
2273
+ signal: controller.signal,
2274
+ ...cwd === void 0 ? {} : { cwd }
2275
+ });
2217
2276
  } catch (error) {
2218
2277
  settle(reject, error);
2219
2278
  return;
@@ -2241,18 +2300,20 @@ function createGrokPluginManager({
2241
2300
  fetchImpl = globalThis.fetch,
2242
2301
  runCommand = spawnRunCommand,
2243
2302
  execPath = process.execPath,
2244
- binPath = process.argv[1],
2303
+ argv = process.argv,
2245
2304
  env = process.env,
2246
2305
  ownPackageJsonUrl = new URL("../package.json", import.meta.url),
2247
2306
  registryUrl = NPM_REGISTRY_LATEST_URL,
2248
2307
  now = Date.now,
2249
2308
  ttlMs = DEFAULT_VERSION_TTL_MS,
2250
2309
  timeoutMs = DEFAULT_VERSION_TIMEOUT_MS,
2251
- updateTimeoutMs = DEFAULT_UPDATE_TIMEOUT_MS
2310
+ updateTimeoutMs = DEFAULT_UPDATE_TIMEOUT_MS,
2311
+ settleDelayMs = DEFAULT_SETTLE_DELAY_MS
2252
2312
  } = {}) {
2253
2313
  if (typeof fetchImpl !== "function") throw new Error("Grok plugin manager requires fetch");
2254
2314
  if (typeof runCommand !== "function") throw new Error("Grok plugin manager requires runCommand");
2255
2315
  const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME !== "" ? env.DSH_HOME : join4(homedir3(), ".dsh");
2316
+ const launcher = resolveLauncher({ argv, execPath });
2256
2317
  let cached;
2257
2318
  let inFlight;
2258
2319
  let generation = 0;
@@ -2341,15 +2402,30 @@ function createGrokPluginManager({
2341
2402
  throw new Error("Grok plugin update could not find the owning profile");
2342
2403
  }
2343
2404
  const latest = await fetchLatest(signal);
2344
- const argv = binPath === void 0 ? ["dsh", "plugin", "--profile", install.profile, "add", `${PACKAGE_NAME}@${latest}`] : [execPath, binPath, "plugin", "--profile", install.profile, "add", `${PACKAGE_NAME}@${latest}`];
2405
+ const profileDir = join4(dshHome, "profiles", install.profile);
2345
2406
  let result;
2346
2407
  try {
2347
- result = await runCommand(argv, { signal, timeoutMs: updateTimeoutMs });
2408
+ result = await runCommand({
2409
+ command: launcher.command,
2410
+ // `pnpm add <exact version>` is what `dsh plugin add` performs for a
2411
+ // registry spec. `--save-exact` keeps the profile dependency pinned,
2412
+ // and the release-age override stops a just-published version from
2413
+ // being skipped as too new (pnpm ≥ 10.16; unknown keys are ignored).
2414
+ args: [
2415
+ ...launcher.prefix,
2416
+ "add",
2417
+ "--save-exact",
2418
+ `--config.minimumReleaseAge=${UPDATE_RELEASE_AGE_MINUTES}`,
2419
+ `${PACKAGE_NAME}@${latest}`
2420
+ ],
2421
+ cwd: profileDir
2422
+ }, { signal, timeoutMs: updateTimeoutMs });
2348
2423
  } catch (error) {
2349
2424
  if (signal?.aborted) throw error;
2350
2425
  throw new Error("Grok plugin update failed");
2351
2426
  }
2352
2427
  if (result.code !== 0) throw new Error("Grok plugin update failed");
2428
+ await confirmInstalled({ profileDir, expected: latest, settleDelayMs });
2353
2429
  generation += 1;
2354
2430
  cached = void 0;
2355
2431
  return { version: latest, profile: install.profile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-grok-subscription",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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",