dsh-grok-subscription 1.0.18 → 1.0.19

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
@@ -209,7 +209,7 @@ npx -y @deepseek-ai/dsh@0.1.5-rc.2 plugin --profile web remove dsh-grok-subscrip
209
209
 
210
210
  - **`dsh` is not recognized**: the official npm route never creates a global `dsh` command — use the full `npx -y @deepseek-ai/dsh@0.1.5-rc.2 ...` command above;
211
211
  - **The model list is empty**: nothing is exposed while signed out. Finish signing in, then click **Pull from Grok CLI**;
212
- - **Sign-in opens no browser / chat fails with `fetch failed` / usage says "Billing request timed out"**: all three have one cause — `accounts.x.ai` and `cli-chat-proxy.grok.com` need a tunnel on some networks. Add `GROK_PROXY=http://127.0.0.1:10808` to `~/.dsh/.env`: it applies to **this plugin's Grok requests** (catalog, usage, chat) and to the **`grok` CLI** it spawns (sign-in, renewal), while DSH and every other provider stay direct — an `http_proxy` line there would route them all through the tunnel and break them whenever it is down. Restart the client afterwards; the sign-in link is handed to your browser and the panel also shows the one-time code and a clickable link. Standard proxy variables already present in the environment are inherited by the CLI too;
212
+ - **Sign-in opens no browser / chat fails with `fetch failed` / usage says "Billing request timed out"**: all three have one cause — `accounts.x.ai` and `cli-chat-proxy.grok.com` need a tunnel on some networks. Add `GROK_PROXY=http://127.0.0.1:10808` to `~/.dsh/.env`: it applies only to **this plugin's own Grok requests** (the host undici's `fetch`; the process-wide dispatcher is never replaced) and to the **`grok` CLI** it spawns. Every other plugin, DSH itself, and every other provider keep the host's original `fetch`. To proxy the whole Harness, set the standard `https_proxy` (in `~/.dsh/.env`, never in a project `.env`): `@deepseek-ai/dsh-http-proxy` takes over and this plugin adds no tunnel of its own. Restart the client afterwards; the sign-in link is handed to your browser and the panel also shows the one-time code and a clickable link.
213
213
  - **Nothing changed after upgrading**: the plugin keeps running the old `lib/` — reinstall the plugin, restart `dsh web`, and hard-refresh (Ctrl+Shift+R);
214
214
  - **It reports `auth.json` permissions**: apply the `chmod 600` above; the plugin refuses symlinks and group- or other-readable files;
215
215
  - **"No reply" after sending a message**: upgrade to `1.0.1` or later. `1.0.0` had a defect where tool-calling turns aborted with a non-serializable stream chunk, leaving no reply in the UI at all.
package/README.md CHANGED
@@ -209,7 +209,7 @@ npx -y @deepseek-ai/dsh@0.1.5-rc.2 plugin --profile web remove dsh-grok-subscrip
209
209
 
210
210
  - **`dsh` 无法识别**:官方 npm 方式本来就不会创建全局 `dsh` 命令,请使用上面的完整 `npx -y @deepseek-ai/dsh@0.1.5-rc.2 ...` 命令;
211
211
  - **模型列表是空的**:未登录时插件不暴露任何模型。先完成登录,再点 **从 Grok CLI 拉取**;
212
- - **登录不弹浏览器 / 聊天报 `fetch failed` / 用量「Billing request timed out」**:这三件事都指向同一个原因——`accounts.x.ai` 与 `cli-chat-proxy.grok.com` 在部分网络里必须走隧道。在 `~/.dsh/.env` 写一行 `GROK_PROXY=http://127.0.0.1:10808` 即可:它只作用于**本插件的 Grok 请求**(模型目录、用量、聊天)和它启动的 **`grok` CLI**(登录、续期),DSH 自身与其他 provider 仍走直连——把 `http_proxy` 写在这里会让所有 provider 一起走隧道,隧道一停就全连不上。写好后重启客户端;登录链接会交给系统浏览器打开,面板里同时显示一次性验证码与可点链接。环境里已有的标准代理变量同样会被 CLI 继承;
212
+ - **登录不弹浏览器 / 聊天报 `fetch failed` / 用量「Billing request timed out」**:这三件事都指向同一个原因——`accounts.x.ai` 与 `cli-chat-proxy.grok.com` 在部分网络里必须走隧道。在 `~/.dsh/.env` 写一行 `GROK_PROXY=http://127.0.0.1:10808` 即可:它只作用于**本插件自己的 Grok 请求**(走宿主 undici 的 fetch,不替换进程的全局调度器)和它启动的 **`grok` CLI**。其他插件、DSH 自身和其他 provider 继续用宿主原来的 `fetch`。想让整个 Harness 走代理时,用标准的 `https_proxy`(可写在 `~/.dsh/.env`,不要写进项目目录),宿主的 `@deepseek-ai/dsh-http-proxy` 会接管,本插件不再另加隧道。写好后重启客户端;登录链接会交给系统浏览器打开,面板里同时显示一次性验证码与可点链接。
213
213
  - **升级后界面没变化**:插件会继续运行旧的 `lib/`,请重新安装插件并重启 `dsh web`,然后硬刷新(Ctrl+Shift+R);
214
214
  - **提示 `auth.json` 权限不正确**:按上面的 `chmod 600` 处理;插件拒绝读取符号链接或组/其他用户可读的文件;
215
215
  - **出现「没有回复」**:请升级到 `1.0.1` 或更高版本。`1.0.0` 存在一个缺陷:工具调用轮次会因流片段无法无损序列化而整轮中止,界面上完全没有回复。
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.0.18",
42
+ subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.19",
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.0.18",
122
+ subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.19",
123
123
  signedIn: "Signed in",
124
124
  signedOut: "Signed out",
125
125
  account: "Account",
package/lib/index.js CHANGED
@@ -326,11 +326,11 @@ async function readResponseJson(response, options = {}) {
326
326
  import { existsSync as existsSync2 } from "node:fs";
327
327
  import { dirname, join as join2 } from "node:path";
328
328
  import { pathToFileURL } from "node:url";
329
- var GROK_API_HOSTS = Object.freeze(["cli-chat-proxy.grok.com"]);
330
329
  var HTTP_PROXY_PATTERN = /^https?:\/\//iu;
331
330
  var HOST_PROXY_PACKAGE = "@deepseek-ai/dsh-http-proxy";
332
- var installed;
333
- var dependencies = { undici: void 0, hostProxies: void 0 };
331
+ var GROK_ORIGIN = "https://cli-chat-proxy.grok.com/";
332
+ var cached;
333
+ var dependencies = { undici: void 0, hostProxies: void 0, hostProxyEnv: void 0 };
334
334
  function apiProxyUrl(env = process.env) {
335
335
  const named = [env?.GROK_API_PROXY, env?.GROK_PROXY];
336
336
  for (const candidate of named) {
@@ -338,13 +338,6 @@ function apiProxyUrl(env = process.env) {
338
338
  }
339
339
  return void 0;
340
340
  }
341
- function hostOf(origin) {
342
- try {
343
- return new URL(origin).hostname;
344
- } catch {
345
- return "";
346
- }
347
- }
348
341
  async function loadHostUndici() {
349
342
  let dir = dirname(process.argv[1] ?? "");
350
343
  for (let depth = 0; depth < 6 && dir && dir !== dirname(dir); depth++) {
@@ -364,55 +357,51 @@ async function loadHostUndici() {
364
357
  return void 0;
365
358
  }
366
359
  }
367
- async function hostProxiesGrok() {
360
+ async function hostProxyModule() {
361
+ try {
362
+ return await import(HOST_PROXY_PACKAGE);
363
+ } catch {
364
+ return void 0;
365
+ }
366
+ }
367
+ async function hostAlreadyProxiesGrok() {
368
368
  if (typeof dependencies.hostProxies === "function") return dependencies.hostProxies();
369
+ const mod = await hostProxyModule();
369
370
  try {
370
- const mod = await import(HOST_PROXY_PACKAGE);
371
- const route = mod?.proxyRouteFor?.(`https://${GROK_API_HOSTS[0]}/`);
372
- return route?.proxied === true;
371
+ return mod?.proxyRouteFor?.(GROK_ORIGIN)?.proxied === true;
373
372
  } catch {
374
373
  return false;
375
374
  }
376
375
  }
377
- async function ensureApiRouting(env = process.env) {
378
- const url = apiProxyUrl(env);
379
- if (!url || !HTTP_PROXY_PATTERN.test(url)) return "direct";
380
- if (installed?.url === url) return "plugin";
381
- if (await hostProxiesGrok()) return "host";
382
- const undici = dependencies.undici ?? await loadHostUndici();
383
- const { Agent, Pool, ProxyAgent, getGlobalDispatcher, setGlobalDispatcher } = undici ?? {};
384
- if (typeof Agent !== "function" || typeof setGlobalDispatcher !== "function") return "direct";
376
+ var CHILD_PROXY_NAMES = ["http_proxy", "https_proxy", "all_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"];
377
+ function proxyOverlay(value) {
378
+ if (!value || typeof value !== "object") return void 0;
379
+ return CHILD_PROXY_NAMES.some((name2) => typeof value[name2] === "string" && value[name2].trim()) ? value : void 0;
380
+ }
381
+ async function hostProxyEnvironment() {
382
+ if (typeof dependencies.hostProxyEnv === "function") return proxyOverlay(dependencies.hostProxyEnv());
383
+ const mod = await hostProxyModule();
385
384
  try {
386
- const tunnel = new ProxyAgent(url);
387
- const routing = new Agent({
388
- factory(origin, options) {
389
- return GROK_API_HOSTS.includes(hostOf(origin)) ? tunnel : new Pool(origin, options);
390
- }
391
- });
392
- const previous = typeof getGlobalDispatcher === "function" ? getGlobalDispatcher() : void 0;
393
- setGlobalDispatcher(routing);
394
- installed = {
395
- url,
396
- dispatcher: routing,
397
- restore: () => {
398
- if (previous !== void 0) setGlobalDispatcher(previous);
399
- installed = void 0;
400
- }
401
- };
402
- return "plugin";
385
+ const overlay = typeof mod?.proxyEnvironmentForChild === "function" ? mod.proxyEnvironmentForChild() : void 0;
386
+ return proxyOverlay(overlay);
403
387
  } catch {
404
- return "direct";
388
+ return void 0;
405
389
  }
406
390
  }
407
- function resetApiRouting() {
408
- const current = installed;
409
- installed = void 0;
410
- if (!current) return false;
391
+ async function grokFetch(env = process.env) {
392
+ const url = apiProxyUrl(env);
393
+ if (!url || !HTTP_PROXY_PATTERN.test(url)) return void 0;
394
+ if (await hostAlreadyProxiesGrok()) return void 0;
395
+ if (cached?.url === url) return cached.fetch;
396
+ const undici = dependencies.undici ?? await loadHostUndici();
397
+ if (typeof undici?.fetch !== "function" || typeof undici?.ProxyAgent !== "function") return void 0;
411
398
  try {
412
- current.restore();
413
- return true;
399
+ const dispatcher = new undici.ProxyAgent(url);
400
+ const fetchImpl = (input, init) => undici.fetch(input, { ...init, dispatcher });
401
+ cached = { url, fetch: fetchImpl };
402
+ return fetchImpl;
414
403
  } catch {
415
- return false;
404
+ return void 0;
416
405
  }
417
406
  }
418
407
 
@@ -538,8 +527,7 @@ function toLlmModels(models) {
538
527
  });
539
528
  }
540
529
  async function fetchLiveCatalog(accessToken, options = {}) {
541
- await ensureApiRouting(options.env);
542
- const fetchImpl = options.fetch ?? globalThis.fetch;
530
+ const fetchImpl = options.fetch ?? await grokFetch(options.env) ?? globalThis.fetch;
543
531
  const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? CATALOG_TIMEOUT_MS);
544
532
  const response = await fetchImpl(MODELS_V2_URL, {
545
533
  method: "GET",
@@ -784,9 +772,9 @@ async function fetchBillingUsageOnce(accessToken, options = {}) {
784
772
  }
785
773
  let response;
786
774
  try {
787
- await ensureApiRouting(options.env);
788
775
  const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? USAGE_TIMEOUT_MS);
789
- response = await fetchImpl(BILLING_CREDITS_URL, {
776
+ const tunnelFetch = options.fetch ? void 0 : await grokFetch(options.env);
777
+ response = await (tunnelFetch ?? fetchImpl)(BILLING_CREDITS_URL, {
790
778
  method: "GET",
791
779
  headers: {
792
780
  Accept: "application/json",
@@ -1206,7 +1194,7 @@ function jsonSafeChunk(value) {
1206
1194
  return out;
1207
1195
  }
1208
1196
  async function* streamResponsesUnsafe(options, token) {
1209
- await ensureApiRouting();
1197
+ const fetchImpl = await grokFetch() ?? globalThis.fetch;
1210
1198
  const headers = {
1211
1199
  Accept: "text/event-stream",
1212
1200
  "Content-Type": "application/json",
@@ -1232,7 +1220,7 @@ async function* streamResponsesUnsafe(options, token) {
1232
1220
  const tools = cacheStableTools(options.tools);
1233
1221
  if (tools.length) body.tools = tools;
1234
1222
  if (options.reasoningEffort) body.reasoning = { effort: options.reasoningEffort };
1235
- const response = await fetch(RESPONSES_URL, {
1223
+ const response = await fetchImpl(RESPONSES_URL, {
1236
1224
  method: "POST",
1237
1225
  headers,
1238
1226
  body: JSON.stringify(body),
@@ -1570,9 +1558,10 @@ function buildAuthConfig() {
1570
1558
  }
1571
1559
  };
1572
1560
  }
1573
- function withEncryptedReasoningInclude(api) {
1561
+ function withEncryptedReasoningInclude(api, fetchImpl) {
1574
1562
  const inject2 = (options) => ({
1575
1563
  ...options,
1564
+ ...typeof fetchImpl === "function" ? { fetch: fetchImpl } : {},
1576
1565
  samplingParams: {
1577
1566
  ...options?.samplingParams,
1578
1567
  include: ["reasoning.encrypted_content"]
@@ -1642,8 +1631,7 @@ async function createGrokBuildAdapter(session, options = {}) {
1642
1631
  if (!responsesApi) {
1643
1632
  return { adapter: asHostAdapter(duck), kind: "custom-mvp", note: "openai-responses API module is unavailable; using the custom adapter." };
1644
1633
  }
1645
- await ensureApiRouting();
1646
- responsesApi = withEncryptedReasoningInclude(responsesApi);
1634
+ responsesApi = withEncryptedReasoningInclude(responsesApi, await grokFetch());
1647
1635
  const store = createStore(session);
1648
1636
  const authModels = piAi.createModels({
1649
1637
  credentials: store,
@@ -1772,6 +1760,17 @@ function cliProxyEnv(env = process.env) {
1772
1760
  no_proxy: bypass
1773
1761
  };
1774
1762
  }
1763
+ async function resolveCliEnv(env = process.env) {
1764
+ const base = env ?? process.env;
1765
+ const overlay = await hostProxyEnvironment();
1766
+ if (!overlay) return cliProxyEnv(base);
1767
+ const next = { ...base };
1768
+ for (const [key, value] of Object.entries(overlay)) {
1769
+ if (value === void 0) delete next[key];
1770
+ else next[key] = value;
1771
+ }
1772
+ return next;
1773
+ }
1775
1774
  function openExternal(url, options = {}) {
1776
1775
  const spawnFn = options.spawn ?? spawn;
1777
1776
  const platform = options.platform ?? process.platform;
@@ -1797,18 +1796,19 @@ function openExternal(url, options = {}) {
1797
1796
  }
1798
1797
  var LOGIN_URL_PATTERN = /https?:\/\/[^\s<>"'`\\]+/u;
1799
1798
  var DEVICE_CODE_PATTERN = /\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/u;
1800
- function spawnGrokLogin(options = {}) {
1799
+ async function spawnGrokLogin(options = {}) {
1801
1800
  const spawnFn = options.spawn ?? spawn;
1802
1801
  const bin = resolveGrokBin(options.env, options.exists);
1803
1802
  const args = options.device ? ["login", "--device-auth"] : ["login"];
1804
1803
  const startTimeoutMs = typeof options.startTimeoutMs === "number" && options.startTimeoutMs > 0 ? options.startTimeoutMs : LOGIN_START_TIMEOUT_MS;
1805
1804
  const openUrl = options.openUrl ?? ((url) => openExternal(url, { spawn: options.openSpawn }));
1805
+ const childEnv = await resolveCliEnv(options.env);
1806
1806
  return new Promise((resolve, reject) => {
1807
1807
  let child;
1808
1808
  try {
1809
1809
  child = spawnFn(bin, args, {
1810
1810
  stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
1811
- env: cliProxyEnv(options.env)
1811
+ env: childEnv
1812
1812
  });
1813
1813
  } catch (error) {
1814
1814
  reject(new Error(`Could not start grok CLI (${bin})`, { cause: error }));
@@ -1871,16 +1871,17 @@ function spawnGrokLogin(options = {}) {
1871
1871
  });
1872
1872
  });
1873
1873
  }
1874
- function spawnGrokRefresh(options = {}) {
1874
+ async function spawnGrokRefresh(options = {}) {
1875
1875
  const spawnFn = options.spawn ?? spawn;
1876
1876
  const bin = resolveGrokBin(options.env, options.exists);
1877
1877
  const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : CLI_REFRESH_TIMEOUT_MS;
1878
+ const childEnv = await resolveCliEnv(options.env);
1878
1879
  return new Promise((resolve, reject) => {
1879
1880
  let child;
1880
1881
  try {
1881
1882
  child = spawnFn(bin, ["models"], {
1882
1883
  stdio: options.stdio ?? "ignore",
1883
- env: cliProxyEnv(options.env)
1884
+ env: childEnv
1884
1885
  });
1885
1886
  } catch (error) {
1886
1887
  reject(new Error(`Could not start grok CLI (${bin})`, { cause: error }));
@@ -2309,8 +2310,8 @@ async function findInstall({ dshHome, ownPackageJsonUrl }) {
2309
2310
  declared.push({ profile: entry.name, spec });
2310
2311
  if (ownReal === void 0) continue;
2311
2312
  try {
2312
- const installed2 = await realpath(join5(profileDir, "node_modules", PACKAGE_NAME, "package.json"));
2313
- if (installed2 === ownReal) return { kind: classifySpec(spec), profile: entry.name };
2313
+ const installed = await realpath(join5(profileDir, "node_modules", PACKAGE_NAME, "package.json"));
2314
+ if (installed === ownReal) return { kind: classifySpec(spec), profile: entry.name };
2314
2315
  } catch {
2315
2316
  }
2316
2317
  }
@@ -2377,7 +2378,7 @@ function createGrokPluginManager({
2377
2378
  if (typeof fetchImpl !== "function") throw new Error("Grok plugin manager requires fetch");
2378
2379
  if (typeof runCommand !== "function") throw new Error("Grok plugin manager requires runCommand");
2379
2380
  const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME !== "" ? env.DSH_HOME : join5(homedir3(), ".dsh");
2380
- let cached;
2381
+ let cached2;
2381
2382
  let inFlight;
2382
2383
  let generation = 0;
2383
2384
  let updating = false;
@@ -2428,11 +2429,11 @@ function createGrokPluginManager({
2428
2429
  return Object.freeze({
2429
2430
  async read({ force = false, signal } = {}) {
2430
2431
  signal?.throwIfAborted();
2431
- if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return clone(cached);
2432
+ if (!force && cached2 !== void 0 && now() - cached2.fetchedAt < ttlMs) return clone(cached2);
2432
2433
  const createPending = (requestSignal) => {
2433
2434
  const observedGeneration = generation;
2434
2435
  return load(requestSignal).then((value) => {
2435
- if (generation === observedGeneration) cached = value;
2436
+ if (generation === observedGeneration) cached2 = value;
2436
2437
  return value;
2437
2438
  });
2438
2439
  };
@@ -2475,7 +2476,7 @@ function createGrokPluginManager({
2475
2476
  }
2476
2477
  if (result.code !== 0) throw new Error("Grok plugin update failed");
2477
2478
  generation += 1;
2478
- cached = void 0;
2479
+ cached2 = void 0;
2479
2480
  return { version: latest, profile: install.profile };
2480
2481
  } finally {
2481
2482
  updating = false;
@@ -2483,7 +2484,7 @@ function createGrokPluginManager({
2483
2484
  },
2484
2485
  invalidate() {
2485
2486
  generation += 1;
2486
- cached = void 0;
2487
+ cached2 = void 0;
2487
2488
  }
2488
2489
  });
2489
2490
  }
@@ -2760,18 +2761,6 @@ function apply(ctx, options = {}) {
2760
2761
  "grok-subscription: startup lifetime"
2761
2762
  );
2762
2763
  }
2763
- void ensureApiRouting().then((mode) => {
2764
- if (mode === "direct") return;
2765
- ctx.logger?.debug?.("Grok subscription API route: %s", mode);
2766
- }).catch((error) => {
2767
- ctx.logger?.debug?.(
2768
- "Grok subscription API route skipped: %s",
2769
- error instanceof Error ? error.message : "unknown"
2770
- );
2771
- });
2772
- if (typeof ctx.effect === "function") {
2773
- ctx.effect(() => () => resetApiRouting(), "grok-subscription: API proxy route");
2774
- }
2775
2764
  const notifyCatalogChange = () => {
2776
2765
  scheduleDeferred2(() => {
2777
2766
  if (!active) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-grok-subscription",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
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",