@ljwei-stak/dsh-model-router 0.20.2 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,7 +80,8 @@ function scriptMethod(id2, label, {
80
80
  legacySharedInstallDir = false,
81
81
  sharedDefaultBin = false,
82
82
  uninstallUnsupportedReason,
83
- scriptFallbackUrls
83
+ scriptFallbackUrls,
84
+ extraArgs = []
84
85
  }) {
85
86
  return Object.freeze({
86
87
  id: id2,
@@ -107,6 +108,7 @@ function scriptMethod(id2, label, {
107
108
  ...scriptBinaryNames ? { scriptBinaryNames: Object.freeze(scriptBinaryNames) } : {},
108
109
  ...scriptFallbackUrls ? { scriptFallbackUrls: Object.freeze(scriptFallbackUrls) } : {},
109
110
  ...registryEnv ? { registryEnv } : {},
111
+ ...extraArgs.length ? { extraArgs: Object.freeze(extraArgs) } : {},
110
112
  supportsRegistry: Boolean(registryEnv)
111
113
  });
112
114
  }
@@ -397,24 +399,24 @@ var OFFICIAL_TOOLS = Object.freeze([
397
399
  id: "opencode",
398
400
  label: "OpenCode",
399
401
  vendor: "anomalyco / SST",
400
- purpose: "\u5F00\u6E90\u7EC8\u7AEF\u7F16\u7A0B\u4EE3\u7406\uFF0C\u63D0\u4F9B opencode \u547D\u4EE4\uFF1B\u53EF\u7528 npm/pnpm \u6216\u5B98\u65B9\u5B89\u88C5\u811A\u672C\u83B7\u53D6\u3002",
401
- package: "opencode-ai",
402
+ purpose: "\u5F00\u6E90\u7EC8\u7AEF\u7F16\u7A0B\u4EE3\u7406\uFF0C\u63D0\u4F9B opencode \u547D\u4EE4\uFF1Bnpm/pnpm \u5B89\u88C5 OpenCode 2\uFF0C\u65E7\u7248\u811A\u672C\u5B89\u88C5 OpenCode 1\u3002",
403
+ package: "@opencode/cli",
402
404
  manager: "npm",
403
- installArgs: ["install", "-g", "opencode-ai@latest", "--registry=https://registry.npmjs.org/"],
404
- uninstallArgs: ["uninstall", "-g", "opencode-ai"],
405
+ installArgs: ["install", "-g", "@opencode/cli@latest", "--registry=https://registry.npmjs.org/"],
406
+ uninstallArgs: ["uninstall", "-g", "@opencode/cli"],
405
407
  probeExecutables: ["opencode"],
406
- probeNote: "opencode \u547D\u4EE4\u7531 npm \u5168\u5C40\u5305\u6216\u5B98\u65B9\u811A\u672C\u5B89\u88C5\u7684\u4E8C\u8FDB\u5236\u63D0\u4F9B\uFF1B\u811A\u672C\u5B89\u88C5\u56FA\u5B9A\u843D\u5728 ~/.opencode/bin\u3002",
408
+ probeNote: "npm/pnpm \u4F7F\u7528 OpenCode 2 \u5B98\u65B9\u5305 @opencode/cli\uFF1B\u65E7\u7248 V1 \u811A\u672C\u56FA\u5B9A\u5B89\u88C5\u5230 ~/.opencode/bin\uFF0C\u4E0D\u80FD\u8BFB\u53D6\u5DF2\u8FC1\u79FB\u7684 V2 \u6570\u636E\u5E93\u3002",
407
409
  providerHints: ["opencode", "anomalyco"],
408
410
  /** File an uninstall may delete, when the copy came from a script install. */
409
411
  scriptBinaryNames: Object.freeze(["opencode", "opencode.exe"]),
410
412
  /** Where the vendor script puts the binary when the user sets no directory. */
411
413
  defaultScriptInstallDir: ".opencode/bin",
412
414
  installMethods: Object.freeze([
413
- packageManagerMethod("npm", "npm", "npm", "opencode-ai@latest"),
414
- packageManagerMethod("pnpm", "pnpm", "pnpm", "opencode-ai@latest"),
415
+ packageManagerMethod("npm", "npm", "npm", "@opencode/cli@latest"),
416
+ packageManagerMethod("pnpm", "pnpm", "pnpm", "@opencode/cli@latest"),
415
417
  // The published script hardcodes $HOME/.opencode/bin, so it cannot honour
416
418
  // a custom install directory; the npm/pnpm methods above can.
417
- scriptMethod("script-bash", "curl | bash", {
419
+ scriptMethod("script-bash", "curl | bash\uFF08OpenCode 1\uFF09", {
418
420
  shell: "bash",
419
421
  scriptUrl: "https://opencode.ai/install",
420
422
  platforms: ["linux", "darwin"],
@@ -476,6 +478,63 @@ var OFFICIAL_TOOLS = Object.freeze([
476
478
  })
477
479
  ])
478
480
  }),
481
+ Object.freeze({
482
+ id: "pi",
483
+ label: "Pi",
484
+ vendor: "Earendil Works",
485
+ purpose: "Pi \u7F16\u7A0B\u4EE3\u7406\uFF0C\u63D0\u4F9B pi \u547D\u4EE4\uFF1B\u652F\u6301 npm/pnpm \u5B89\u88C5\uFF0C\u8981\u6C42 Node.js 22.19 \u53CA\u4EE5\u4E0A\u3002",
486
+ manager: "npm",
487
+ package: "@earendil-works/pi-coding-agent",
488
+ installArgs: ["install", "-g", "@earendil-works/pi-coding-agent@latest", "--ignore-scripts", "--registry=https://registry.npmjs.org/"],
489
+ uninstallArgs: ["uninstall", "-g", "@earendil-works/pi-coding-agent"],
490
+ probeExecutables: ["pi"],
491
+ probeNote: "\u4F7F\u7528\u5B98\u65B9 Pi \u5305\u53CA\u5176\u58F0\u660E\u7684\u547D\u4EE4\u5165\u53E3\uFF1B\u666E\u901A\u5B89\u88C5\u65E0\u9700\u751F\u547D\u5468\u671F\u811A\u672C\uFF0C\u767B\u5F55\u8BF7\u5728 Pi \u4E2D\u8F93\u5165 /login\u3002",
492
+ providerHints: ["earendil-works", "pi-coding-agent"],
493
+ installMethods: Object.freeze([
494
+ packageManagerMethod("npm", "npm", "npm", "@earendil-works/pi-coding-agent@latest", { extraArgs: ["--ignore-scripts"] }),
495
+ packageManagerMethod("pnpm", "pnpm", "pnpm", "@earendil-works/pi-coding-agent@latest", { extraArgs: ["--ignore-scripts"] })
496
+ ])
497
+ }),
498
+ Object.freeze({
499
+ id: "hermes",
500
+ label: "Hermes Agent",
501
+ vendor: "Nous Research",
502
+ purpose: "Nous Research \u7684 Hermes Agent\uFF1B\u652F\u6301 Windows \u4E0E POSIX \u5B98\u65B9\u6E90\u7801\u5B89\u88C5\u5668\u548C\u4EA4\u4E92\u7EC8\u7AEF\u3002",
503
+ manager: "script-installer",
504
+ installArgs: [],
505
+ uninstallArgs: [],
506
+ probeExecutables: ["hermes"],
507
+ probeNote: "\u652F\u6301\u5B98\u65B9\u6E90\u7801\u811A\u672C\u5B89\u88C5\uFF1B\u81EA\u5B9A\u4E49\u6E90\u7801\u5B89\u88C5\u5230\u7EDF\u4E00\u6839/hermes-agent\uFF0C\u63D2\u4EF6\u4F7F\u7528\u8BE5\u6E90\u7801\u7684 .hermes/bin \u5165\u53E3\u3002\u914D\u7F6E\u3001\u767B\u5F55\u548C\u5386\u53F2\u4FDD\u7559\u539F\u4F4D\u7F6E\u3002\u684C\u9762/MSIX \u522B\u540D\u6682\u4E0D\u7528\u4E8E\u6B64\u5165\u53E3\u3002\u767B\u5F55\u4F7F\u7528 hermes auth\u3002",
508
+ providerHints: ["hermes", "nousresearch"],
509
+ scriptBinaryNames: Object.freeze(["hermes", "hermes.exe"]),
510
+ installMethods: Object.freeze([
511
+ scriptMethod("script-bash", "curl | bash", {
512
+ shell: "bash",
513
+ scriptUrl: "https://hermes-agent.nousresearch.com/install.sh",
514
+ platforms: ["linux", "darwin"],
515
+ supportsInstallDir: true,
516
+ sharedDefaultBin: true,
517
+ installDirSubdir: "hermes-agent",
518
+ installDirSuffix: ".hermes/bin",
519
+ installArg: ["--dir"],
520
+ extraArgs: ["--non-interactive"],
521
+ verify: Object.freeze({ mustInclude: ["https://github.com/NousResearch/hermes-agent.git", "hermes_cli/source_completion.py"] }),
522
+ uninstallUnsupportedReason: "\u8BF7\u5728\u5B98\u65B9\u5DE5\u5177\u7EC8\u7AEF\u8FD0\u884C hermes uninstall \u5378\u8F7D\u6E90\u7801\u5B89\u88C5\uFF1B\u4FDD\u7559\u914D\u7F6E\u4E0E\u5386\u53F2\u65F6\u6309\u5411\u5BFC\u9009\u62E9\u3002\u684C\u9762/MSIX \u5B89\u88C5\u8BF7\u4F7F\u7528\u7CFB\u7EDF\u5E94\u7528\u7BA1\u7406\u3002"
523
+ }),
524
+ scriptMethod("script-powershell", "irm | iex", {
525
+ shell: "powershell",
526
+ scriptUrl: "https://hermes-agent.nousresearch.com/install.ps1",
527
+ platforms: ["win32"],
528
+ supportsInstallDir: true,
529
+ installDirSubdir: "hermes-agent",
530
+ installDirSuffix: ".hermes/bin",
531
+ installArg: ["-InstallDir"],
532
+ extraArgs: ["-NonInteractive"],
533
+ verify: Object.freeze({ mustInclude: ["https://github.com/NousResearch/hermes-agent.git", "hermes_cli/source_completion.py"] }),
534
+ uninstallUnsupportedReason: "\u8BF7\u5728\u5B98\u65B9\u5DE5\u5177\u7EC8\u7AEF\u8FD0\u884C hermes uninstall \u5378\u8F7D\u6E90\u7801\u5B89\u88C5\uFF1B\u4FDD\u7559\u914D\u7F6E\u4E0E\u5386\u53F2\u65F6\u6309\u5411\u5BFC\u9009\u62E9\u3002\u684C\u9762/MSIX \u5B89\u88C5\u8BF7\u4F7F\u7528\u7CFB\u7EDF\u5E94\u7528\u7BA1\u7406\u3002"
535
+ })
536
+ ])
537
+ }),
479
538
  Object.freeze({
480
539
  id: "zcode",
481
540
  label: "ZCode",
@@ -522,6 +581,10 @@ function installCommandLineFor(tool, method) {
522
581
  if (!tool || tool.unsupported) return null;
523
582
  if (!method) return null;
524
583
  if (method.kind === "script") {
584
+ if (method.extraArgs?.length) {
585
+ const args = method.extraArgs.join(" ");
586
+ return method.shell === "powershell" ? `& ([scriptblock]::Create((irm ${method.scriptUrl}))) ${args}` : `curl -fsSL ${method.scriptUrl} | ${method.shell} -s -- ${args}`;
587
+ }
525
588
  const piped = method.shell === "powershell" ? `irm ${method.scriptUrl} | iex` : `curl -fsSL ${method.scriptUrl} | ${method.shell}`;
526
589
  return piped;
527
590
  }
@@ -12807,7 +12870,7 @@ var xterm_default = `/**
12807
12870
  `;
12808
12871
 
12809
12872
  // .dsh-plugin/client/host-version.mjs
12810
- var ROUTER_CLIENT_VERSION = true ? "0.20.2" : "";
12873
+ var ROUTER_CLIENT_VERSION = true ? "0.21.1" : "";
12811
12874
  var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
12812
12875
  function isMissingRemoteMethod(message) {
12813
12876
  const value = String(message ?? "");
@@ -12858,6 +12921,8 @@ var TERMINAL_LOGIN_ARGS = Object.freeze({
12858
12921
  gemini: Object.freeze([]),
12859
12922
  opencode: Object.freeze(["auth", "login"]),
12860
12923
  stepcode: Object.freeze(["login"]),
12924
+ hermes: Object.freeze(["auth"]),
12925
+ pi: Object.freeze([]),
12861
12926
  zcode: Object.freeze([])
12862
12927
  });
12863
12928
  function terminalCanLogin(target) {
@@ -14516,7 +14581,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14516
14581
  void workbench.rerun(runId, packageId, override, choice);
14517
14582
  }
14518
14583
  }
14519
- )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view", role: "tabpanel", id: "mr-panel-models", "aria-labelledby": "mr-tab-models", hidden: view !== "models" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u6A21\u578B\u914D\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u4ECE\u5B98\u65B9\u76EE\u5F55\u9009\u62E9\u8DEF\u7EBF\uFF0C\u8865\u9F50\u6BD4\u8F83\u6240\u9700\u7684\u4EF7\u683C\u3001\u80FD\u529B\u4E0E\u6267\u884C\u65B9\u5F0F\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-grid mr-model-grid" }, /* @__PURE__ */ import_react5.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, routes.length, " \u6761\u8DEF\u7EBF \xB7 \u53EA\u8BFB\u53D6\u5B98\u65B9\u76EE\u5F55\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: catalogState.status === "loading", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react5.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002"))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-tools", "aria-labelledby": "mr-tab-tools", hidden: view !== "tools" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u7BA1\u7406\u672C\u673A CLI\u3001\u68C0\u67E5\u767B\u5F55\u72B6\u6001\uFF0C\u6216\u6253\u5F00\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002")), workbench.health.report && !workbench.health.report.onboarding?.completedAt && /* @__PURE__ */ import_react5.default.createElement(
14584
+ )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view", role: "tabpanel", id: "mr-panel-models", "aria-labelledby": "mr-tab-models", hidden: view !== "models" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u6A21\u578B\u914D\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u4ECE\u5B98\u65B9\u76EE\u5F55\u9009\u62E9\u8DEF\u7EBF\uFF0C\u8865\u9F50\u6BD4\u8F83\u6240\u9700\u7684\u4EF7\u683C\u3001\u80FD\u529B\u4E0E\u6267\u884C\u65B9\u5F0F\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-grid mr-model-grid" }, /* @__PURE__ */ import_react5.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, routes.length, " \u6761\u8DEF\u7EBF \xB7 \u53EA\u8BFB\u53D6\u5B98\u65B9\u76EE\u5F55\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: catalogState.status === "loading", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react5.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002"))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-tools", "aria-labelledby": "mr-tab-tools", hidden: view !== "tools" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u7BA1\u7406\u672C\u673A CLI\u3001\u68C0\u67E5\u767B\u5F55\u72B6\u6001\uFF0C\u6216\u6253\u5F00\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002")), terminalApi && /* @__PURE__ */ import_react5.default.createElement(
14585
+ CliTerminalCard,
14586
+ {
14587
+ api: terminalApi,
14588
+ health: workbench.health.report,
14589
+ settingsSignature: toolInstallSettingsSignature(settingsScope?.getSnapshot?.()?.value)
14590
+ }
14591
+ ), workbench.health.report && !workbench.health.report.onboarding?.completedAt && /* @__PURE__ */ import_react5.default.createElement(
14520
14592
  OnboardingBanner,
14521
14593
  {
14522
14594
  health: workbench.health.report,
@@ -14546,13 +14618,6 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14546
14618
  void workbench.refreshHealth(true);
14547
14619
  }
14548
14620
  }
14549
- ), terminalApi && /* @__PURE__ */ import_react5.default.createElement(
14550
- CliTerminalCard,
14551
- {
14552
- api: terminalApi,
14553
- health: workbench.health.report,
14554
- settingsSignature: toolInstallSettingsSignature(settingsScope?.getSnapshot?.()?.value)
14555
- }
14556
14621
  )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-controls", "aria-labelledby": "mr-tab-controls", hidden: view !== "controls" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u9884\u7B97\u4E0E\u5B89\u5168"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u8BBE\u7F6E\u6210\u672C\u4E0E\u8D28\u91CF\u7B56\u7565\uFF0C\u6838\u5BF9\u8BA2\u9605\u8BA1\u8D39\u548C\u6267\u884C\u8FB9\u754C\u3002")), /* @__PURE__ */ import_react5.default.createElement(
14557
14622
  CostControlCard,
14558
14623
  {
@@ -50,6 +50,8 @@ export const TERMINAL_LOGIN_ARGS = Object.freeze({
50
50
  gemini: Object.freeze([]),
51
51
  opencode: Object.freeze(['auth', 'login']),
52
52
  stepcode: Object.freeze(['login']),
53
+ hermes: Object.freeze(['auth']),
54
+ pi: Object.freeze([]),
53
55
  zcode: Object.freeze([]),
54
56
  })
55
57
 
@@ -196,14 +196,16 @@ export async function resolveTerminalLaunch(target, mode = 'interactive', {
196
196
  const selectedPreferences = selectedScript ? { ...preferences, allowScriptInstall: true } : preferences
197
197
  const plan = resolveInstallPlan(tool, undefined, selectedPreferences, { platform, env, home })
198
198
  const configured = Boolean(preferences.installDir || preferences.methods?.[target])
199
- if (plan.kind === 'package-manager' && configured) {
199
+ if (plan.kind === 'package-manager' && (configured || target === 'pi')) {
200
200
  const boundPlan = plan.installDir ? plan : await (await import('./official-tools-runtime.mjs')).resolvePackageJobPlan(tool, plan, selectedPreferences, { options: { platform, env, home } })
201
201
  const found = await packageEntry(tool, boundPlan, { platform })
202
202
  if (!found) throw new Error(`未在所选 ${boundPlan.manager} 安装目录中找到 ${tool.label} 的真实命令入口;请重新体检或修复。`)
203
203
  const extension = extname(found.entry).toLowerCase()
204
204
  if (['.js', '.mjs', '.cjs'].includes(extension) || (platform === 'win32' && !extension)) {
205
- const node = await findNode(cwd)
206
- if (!node) throw new Error(`未找到可核验的 Node.js 运行时,无法启动 ${tool.label}。`)
205
+ const node = target === 'pi' ? await findNode(cwd, [22, 19, 0]) : await findNode(cwd)
206
+ if (!node) throw new Error(target === 'pi'
207
+ ? 'Pi 需要 Node.js 22.19.0 及以上;未找到符合要求的运行时。'
208
+ : `未找到可核验的 Node.js 运行时,无法启动 ${tool.label}。`)
207
209
  return { file: node, args: [found.entry, ...args], verbatim: false, display }
208
210
  }
209
211
  return executableLaunch(found.entry, args, display, { platform, env })
@@ -79,6 +79,7 @@ export function latestSourceFor(tool) {
79
79
  if (tool.manager === 'npm' && typeof tool.package === 'string') return { kind: 'npm', package: tool.package }
80
80
  if (tool.id === 'zcode') return { kind: 'zcode-download-page' }
81
81
  if (tool.id === 'stepcode') return { kind: 'stepcode-release-manifest' }
82
+ if (tool.id === 'hermes') return { kind: 'hermes-github-release', url: 'https://api.github.com/repos/NousResearch/hermes-agent/releases/latest' }
82
83
  return null
83
84
  }
84
85
 
@@ -97,6 +98,13 @@ async function lookupSource(source, { fetchImpl, timeoutMs }) {
97
98
  const found = parseStepCodeManifest(await boundedText(fetchImpl, `${STEPCODE_RELEASE_BASE}/latest.json`, timeoutMs, 'application/json'))
98
99
  return { ...found, source: 'stepcode-release-manifest' }
99
100
  }
101
+ if (source.kind === 'hermes-github-release') {
102
+ const body = JSON.parse(await boundedText(fetchImpl, source.url, timeoutMs, 'application/json'))
103
+ if (body?.draft === true || body?.prerelease === true) throw new Error('Hermes 发布不是正式版本')
104
+ const version = String(body?.tag_name ?? '').replace(/^v/, '')
105
+ if (!isReleaseVersion(version)) throw new Error('Hermes 发布标签不是有效版本号')
106
+ return { version, source: 'hermes-github-release' }
107
+ }
100
108
  throw new Error('不支持的版本来源')
101
109
  }
102
110
 
@@ -81,6 +81,8 @@ const CAPABILITIES = Object.freeze({
81
81
  supported: false,
82
82
  reason: 'Step Code 尚未完成受限执行入口核验;插件负责安装、检测与登录体检,执行仍需在终端中直接使用。',
83
83
  }),
84
+ hermes: Object.freeze({ supported: false, modes: Object.freeze([]), reason: 'Hermes 尚未核验托管受限执行;请在官方工具终端中交互使用。' }),
85
+ pi: Object.freeze({ supported: false, modes: Object.freeze([]), reason: 'Pi 尚未核验托管受限执行;请在官方工具终端中交互使用。' }),
84
86
  })
85
87
 
86
88
  /** A UI/Host can show this without implying that installation means execution readiness. */
@@ -45,7 +45,7 @@ function scriptMethod(id, label, {
45
45
  shell, scriptUrl, verify, installArg = [], env = [], platforms,
46
46
  supportsInstallDir = false, installDirEnv = '', installDirSuffix = '',
47
47
  defaultScriptInstallDir, defaultScriptInstallDirEnv, scriptBinaryNames,
48
- registryEnv, installDirSubdir, legacySharedInstallDir = false, sharedDefaultBin = false, uninstallUnsupportedReason, scriptFallbackUrls,
48
+ registryEnv, installDirSubdir, legacySharedInstallDir = false, sharedDefaultBin = false, uninstallUnsupportedReason, scriptFallbackUrls, extraArgs = [],
49
49
  }) {
50
50
  return Object.freeze({
51
51
  id,
@@ -72,6 +72,7 @@ function scriptMethod(id, label, {
72
72
  ...(scriptBinaryNames ? { scriptBinaryNames: Object.freeze(scriptBinaryNames) } : {}),
73
73
  ...(scriptFallbackUrls ? { scriptFallbackUrls: Object.freeze(scriptFallbackUrls) } : {}),
74
74
  ...(registryEnv ? { registryEnv } : {}),
75
+ ...(extraArgs.length ? { extraArgs: Object.freeze(extraArgs) } : {}),
75
76
  supportsRegistry: Boolean(registryEnv),
76
77
  })
77
78
  }
@@ -324,24 +325,24 @@ export const OFFICIAL_TOOLS = Object.freeze([
324
325
  id: 'opencode',
325
326
  label: 'OpenCode',
326
327
  vendor: 'anomalyco / SST',
327
- purpose: '开源终端编程代理,提供 opencode 命令;可用 npm/pnpm 或官方安装脚本获取。',
328
- package: 'opencode-ai',
328
+ purpose: '开源终端编程代理,提供 opencode 命令;npm/pnpm 安装 OpenCode 2,旧版脚本安装 OpenCode 1。',
329
+ package: '@opencode/cli',
329
330
  manager: 'npm',
330
- installArgs: ['install', '-g', 'opencode-ai@latest', '--registry=https://registry.npmjs.org/'],
331
- uninstallArgs: ['uninstall', '-g', 'opencode-ai'],
331
+ installArgs: ['install', '-g', '@opencode/cli@latest', '--registry=https://registry.npmjs.org/'],
332
+ uninstallArgs: ['uninstall', '-g', '@opencode/cli'],
332
333
  probeExecutables: ['opencode'],
333
- probeNote: 'opencode 命令由 npm 全局包或官方脚本安装的二进制提供;脚本安装固定落在 ~/.opencode/bin。',
334
+ probeNote: 'npm/pnpm 使用 OpenCode 2 官方包 @opencode/cli;旧版 V1 脚本固定安装到 ~/.opencode/bin,不能读取已迁移的 V2 数据库。',
334
335
  providerHints: ['opencode', 'anomalyco'],
335
336
  /** File an uninstall may delete, when the copy came from a script install. */
336
337
  scriptBinaryNames: Object.freeze(['opencode', 'opencode.exe']),
337
338
  /** Where the vendor script puts the binary when the user sets no directory. */
338
339
  defaultScriptInstallDir: '.opencode/bin',
339
340
  installMethods: Object.freeze([
340
- packageManagerMethod('npm', 'npm', 'npm', 'opencode-ai@latest'),
341
- packageManagerMethod('pnpm', 'pnpm', 'pnpm', 'opencode-ai@latest'),
341
+ packageManagerMethod('npm', 'npm', 'npm', '@opencode/cli@latest'),
342
+ packageManagerMethod('pnpm', 'pnpm', 'pnpm', '@opencode/cli@latest'),
342
343
  // The published script hardcodes $HOME/.opencode/bin, so it cannot honour
343
344
  // a custom install directory; the npm/pnpm methods above can.
344
- scriptMethod('script-bash', 'curl | bash', {
345
+ scriptMethod('script-bash', 'curl | bash(OpenCode 1)', {
345
346
  shell: 'bash',
346
347
  scriptUrl: 'https://opencode.ai/install',
347
348
  platforms: ['linux', 'darwin'],
@@ -401,6 +402,54 @@ export const OFFICIAL_TOOLS = Object.freeze([
401
402
  }),
402
403
  ]),
403
404
  }),
405
+ Object.freeze({
406
+ id: 'pi',
407
+ label: 'Pi',
408
+ vendor: 'Earendil Works',
409
+ purpose: 'Pi 编程代理,提供 pi 命令;支持 npm/pnpm 安装,要求 Node.js 22.19 及以上。',
410
+ manager: 'npm',
411
+ package: '@earendil-works/pi-coding-agent',
412
+ installArgs: ['install', '-g', '@earendil-works/pi-coding-agent@latest', '--ignore-scripts', '--registry=https://registry.npmjs.org/'],
413
+ uninstallArgs: ['uninstall', '-g', '@earendil-works/pi-coding-agent'],
414
+ probeExecutables: ['pi'],
415
+ probeNote: '使用官方 Pi 包及其声明的命令入口;普通安装无需生命周期脚本,登录请在 Pi 中输入 /login。',
416
+ providerHints: ['earendil-works', 'pi-coding-agent'],
417
+ installMethods: Object.freeze([
418
+ packageManagerMethod('npm', 'npm', 'npm', '@earendil-works/pi-coding-agent@latest', { extraArgs: ['--ignore-scripts'] }),
419
+ packageManagerMethod('pnpm', 'pnpm', 'pnpm', '@earendil-works/pi-coding-agent@latest', { extraArgs: ['--ignore-scripts'] }),
420
+ ]),
421
+ }),
422
+ Object.freeze({
423
+ id: 'hermes',
424
+ label: 'Hermes Agent',
425
+ vendor: 'Nous Research',
426
+ purpose: 'Nous Research 的 Hermes Agent;支持 Windows 与 POSIX 官方源码安装器和交互终端。',
427
+ manager: 'script-installer',
428
+ installArgs: [],
429
+ uninstallArgs: [],
430
+ probeExecutables: ['hermes'],
431
+ probeNote: '支持官方源码脚本安装;自定义源码安装到统一根/hermes-agent,插件使用该源码的 .hermes/bin 入口。配置、登录和历史保留原位置。桌面/MSIX 别名暂不用于此入口。登录使用 hermes auth。',
432
+ providerHints: ['hermes', 'nousresearch'],
433
+ scriptBinaryNames: Object.freeze(['hermes', 'hermes.exe']),
434
+ installMethods: Object.freeze([
435
+ scriptMethod('script-bash', 'curl | bash', {
436
+ shell: 'bash', scriptUrl: 'https://hermes-agent.nousresearch.com/install.sh',
437
+ platforms: ['linux', 'darwin'], supportsInstallDir: true, sharedDefaultBin: true,
438
+ installDirSubdir: 'hermes-agent', installDirSuffix: '.hermes/bin', installArg: ['--dir'],
439
+ extraArgs: ['--non-interactive'],
440
+ verify: Object.freeze({ mustInclude: ['https://github.com/NousResearch/hermes-agent.git', 'hermes_cli/source_completion.py'] }),
441
+ uninstallUnsupportedReason: '请在官方工具终端运行 hermes uninstall 卸载源码安装;保留配置与历史时按向导选择。桌面/MSIX 安装请使用系统应用管理。',
442
+ }),
443
+ scriptMethod('script-powershell', 'irm | iex', {
444
+ shell: 'powershell', scriptUrl: 'https://hermes-agent.nousresearch.com/install.ps1',
445
+ platforms: ['win32'], supportsInstallDir: true,
446
+ installDirSubdir: 'hermes-agent', installDirSuffix: '.hermes/bin', installArg: ['-InstallDir'],
447
+ extraArgs: ['-NonInteractive'],
448
+ verify: Object.freeze({ mustInclude: ['https://github.com/NousResearch/hermes-agent.git', 'hermes_cli/source_completion.py'] }),
449
+ uninstallUnsupportedReason: '请在官方工具终端运行 hermes uninstall 卸载源码安装;保留配置与历史时按向导选择。桌面/MSIX 安装请使用系统应用管理。',
450
+ }),
451
+ ]),
452
+ }),
404
453
  Object.freeze({
405
454
  id: 'zcode',
406
455
  label: 'ZCode',
@@ -478,6 +527,12 @@ export function installCommandLineFor(tool, method) {
478
527
  if (!tool || tool.unsupported) return null
479
528
  if (!method) return null
480
529
  if (method.kind === 'script') {
530
+ if (method.extraArgs?.length) {
531
+ const args = method.extraArgs.join(' ')
532
+ return method.shell === 'powershell'
533
+ ? `& ([scriptblock]::Create((irm ${method.scriptUrl}))) ${args}`
534
+ : `curl -fsSL ${method.scriptUrl} | ${method.shell} -s -- ${args}`
535
+ }
481
536
  const piped = method.shell === 'powershell' ? `irm ${method.scriptUrl} | iex` : `curl -fsSL ${method.scriptUrl} | ${method.shell}`
482
537
  return piped
483
538
  }
@@ -516,13 +516,14 @@ const POWERSHELL = join(process.env.SystemRoot ?? 'C:\\Windows', 'System32',
516
516
  export function vendorScriptInvocation(plan, scriptPath) {
517
517
  // `installArg` is the vendor's own single flag (`--install-dir` / `-InstallDir`).
518
518
  const directoryArgs = plan.installArg?.[0] && plan.installDir ? [plan.installArg[0], plan.installDir] : []
519
+ const extraArgs = Array.isArray(plan.extraArgs) ? plan.extraArgs : []
519
520
  if (plan.shell === 'powershell') {
520
521
  return {
521
522
  file: POWERSHELL,
522
- args: ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, ...directoryArgs],
523
+ args: ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, ...directoryArgs, ...extraArgs],
523
524
  }
524
525
  }
525
- return { file: plan.shell === 'sh' ? 'sh' : 'bash', args: [scriptPath, ...directoryArgs] }
526
+ return { file: plan.shell === 'sh' ? 'sh' : 'bash', args: [scriptPath, ...directoryArgs, ...extraArgs] }
526
527
  }
527
528
 
528
529
  /** Resolve an existing ancestor too, so a missing child under a junction is checked. */
@@ -593,6 +594,7 @@ export async function assertVendorInstallDirectory(tool, plan, selectedPreferenc
593
594
  let name
594
595
  try { name = JSON.parse(packageText).name } catch { throw new Error('安装目录已有无法核验归属的 package.json,已停止运行官方脚本。') }
595
596
  const ownPackage = tool.package || (tool.id === 'stepcode' ? '@step-harness/coding-agent' : '')
597
+ || (tool.id === 'hermes' ? 'hermes-agent' : '')
596
598
  if (!name || name !== ownPackage) throw new Error('安装目录已有其他工具或项目的 package.json,已停止运行官方脚本。')
597
599
  }
598
600
  for (const other of OFFICIAL_TOOLS) {
@@ -821,7 +823,7 @@ async function stripPathProfileBlocks(tool) {
821
823
 
822
824
  /** Tools whose ready-check failure can be repaired by reinstalling the same version. */
823
825
  const CAPABILITY_REPAIRABLE = new Set([
824
- 'claude-code', 'codex', 'kimi-code', 'minimax-code', 'mimo-code', 'grok-build', 'opencode', 'stepcode',
826
+ 'claude-code', 'codex', 'kimi-code', 'minimax-code', 'mimo-code', 'grok-build', 'opencode', 'stepcode', 'hermes', 'pi',
825
827
  ])
826
828
 
827
829
  /** Extract the first version-looking token from a CLI banner. */
@@ -1263,13 +1265,15 @@ export function planCommandLine(plan, operation = 'install') {
1263
1265
  const environment = Object.entries(plan.env ?? {}).map(([name, value]) => plan.shell === 'powershell'
1264
1266
  ? `$env:${name}=${quote(value)}; ` : `${name}=${quote(value)} `).join('')
1265
1267
  const directoryArgs = plan.installDir && plan.installArg?.length ? ` ${plan.installArg.join(' ')} ${quote(plan.installDir)}` : ''
1268
+ const extraArgs = plan.extraArgs?.length ? ` ${plan.extraArgs.map(arg => /^-{1,2}[A-Za-z][\w-]*$/.test(arg) ? arg : quote(arg)).join(' ')}` : ''
1269
+ const scriptArgs = directoryArgs + extraArgs
1266
1270
  const piped = plan.shell === 'powershell'
1267
1271
  ? `${environment}irm ${plan.scriptUrl} | iex`
1268
- : `curl -fsSL ${plan.scriptUrl} | ${environment}${plan.shell === 'sh' ? 'sh' : 'bash'}${directoryArgs ? ` -s --${directoryArgs}` : ''}`
1272
+ : `curl -fsSL ${plan.scriptUrl} | ${environment}${plan.shell === 'sh' ? 'sh' : 'bash'}${scriptArgs ? ` -s --${scriptArgs}` : ''}`
1269
1273
  return operation === 'uninstall'
1270
1274
  ? `删除 ${plan.dir} 下的 ${(plan.binaries ?? []).join('、') || '可执行文件'}`
1271
- : plan.shell === 'powershell' && directoryArgs
1272
- ? `${environment}& ([scriptblock]::Create((irm ${plan.scriptUrl})))${directoryArgs}` : piped
1275
+ : plan.shell === 'powershell' && scriptArgs
1276
+ ? `${environment}& ([scriptblock]::Create((irm ${plan.scriptUrl})))${scriptArgs}` : piped
1273
1277
  }
1274
1278
  return `${plan.manager} ${plan.args.map(arg => /\s/.test(arg) ? JSON.stringify(arg) : arg).join(' ')}`
1275
1279
  }
@@ -31,6 +31,8 @@ export const LOGIN_GUIDES = Object.freeze({
31
31
  zcode: Object.freeze({ command: null, steps: '打开 ZCode 桌面版,在欢迎页选择“连接 BigModel 继续使用”或“连接 Z.ai 继续使用”完成授权;已订阅 GLM Coding Plan 时在“模型设置 → BigModel”右上角选择“编程套餐”绑定。改用 API Key 时 OpenAI 地址须填 Coding 专用端点 https://open.bigmodel.cn/api/coding/paas/v4。ZCode 没有可调用的登录状态命令。' }),
32
32
  opencode: Object.freeze({ command: 'opencode auth login', steps: '在终端运行 opencode auth login,按提示完成登录;OpenCode 支持多家供应商,用 /connect 在会话内选择并保存凭据。也可在 opencode.json 中配置 provider 的 API Key。' }),
33
33
  stepcode: Object.freeze({ command: 'step login', steps: '在终端运行 step login(step login status --json 可查看状态),按提示完成阶跃账号登录;登录信息保存在 ~/.stepcode/auth.json。卸载本工具不会删除该文件。' }),
34
+ hermes: Object.freeze({ command: 'hermes auth', steps: '在终端运行 hermes auth,选择模型供应商并完成 OAuth 或 API Key 配置。插件暂未核验自动登录检测,不读取 Hermes 凭据,以 CLI 实际结果为准。' }),
35
+ pi: Object.freeze({ command: 'pi', steps: '启动 pi 后输入 /login,选择模型供应商完成登录。Pi 的认证状态按供应商区分,插件不读取凭据或自动刷新令牌。' }),
34
36
  })
35
37
 
36
38
  /** Environment variable names whose presence means a CLI can authenticate without a session. */
@@ -229,6 +231,8 @@ export async function checkLogin(toolId, {
229
231
  if (states.length) return { state: 'unknown', source: 'file', detail: `MiniMax Code 账号未登录(auth-state.json 为 ${states.map(item => `${item.region}: ${item.status}`).join('、')});如果用 mcode set-minimax-key 保存了 API Key 仍可使用,否则请运行 mcode login。` }
230
232
  return { state: 'unknown', detail: '未找到 MiniMax Code 登录状态文件(~/.minimax/auth/prod/<cn|global>/mcode-public/auth-state.json),登录状态未知;如未登录请运行 mcode login。' }
231
233
  }
234
+ if (toolId === 'hermes') return { state: 'unknown', detail: '插件暂未核验 Hermes 自动登录检测;请在终端运行 hermes auth,并以 CLI 实际结果为准。' }
235
+ if (toolId === 'pi') return { state: 'unknown', detail: 'Pi 认证按供应商区分;启动 pi 后输入 /login 查看或配置,插件不读取凭据。' }
232
236
  if (toolId === 'zcode') return viaKey ?? { state: 'unknown', detail: 'ZCode 是桌面应用,没有可调用的登录状态命令;登录与 GLM Coding Plan 状态请在 ZCode 内查看。' }
233
237
  return viaKey ?? { state: 'unknown', detail: '该 CLI 没有可安全调用的登录状态命令;首次运行时以实际结果为准。' }
234
238
  }
@@ -166,6 +166,10 @@ export function parseInstallPreferences(config = {}, { tools = [], home = '', en
166
166
 
167
167
  /** Where a script install puts the binary: the user's directory, else the vendor default. */
168
168
  export function scriptInstallDir(tool, preferences, { home = '', env = {}, method } = {}) {
169
+ // Only a chosen root overrides Hermes' source location. Its user home and
170
+ // default global command directory remain owned by the vendor installer.
171
+ if (tool?.id === 'hermes') return preferences?.installDir
172
+ ? joinPath(preferences.installDir, method?.installDirSubdir || 'hermes-agent') : ''
169
173
  const binaryDir = vendorScriptDir(tool, { home, env, method })
170
174
  if (!binaryDir) return ''
171
175
  if (preferences?.installDir) return method?.installDirSubdir ? joinPath(preferences.installDir, method.installDirSubdir) : preferences.installDir
@@ -176,6 +180,15 @@ export function scriptInstallDir(tool, preferences, { home = '', env = {}, metho
176
180
 
177
181
  /** The location the vendor script uses on its own, ignoring any configured directory. */
178
182
  export function vendorScriptDir(tool, { home = '', env = {}, method } = {}) {
183
+ if (tool?.id === 'hermes') {
184
+ const value = name => typeof env?.[name] === 'string' ? env[name].trim() : ''
185
+ if (method?.shell === 'powershell') {
186
+ if (value('HERMES_HOME')) return joinPath(value('HERMES_HOME'), 'bin')
187
+ if (value('LOCALAPPDATA')) return joinPath(value('LOCALAPPDATA'), 'hermes', 'bin')
188
+ return home ? joinPath(home, 'AppData', 'Local', 'hermes', 'bin') : ''
189
+ }
190
+ return home ? joinPath(home, '.local', 'bin') : ''
191
+ }
179
192
  const value = method?.defaultScriptInstallDir ?? tool?.defaultScriptInstallDir
180
193
  const root = method?.defaultScriptInstallDirEnv ? env[method.defaultScriptInstallDirEnv] || home : home
181
194
  return value ? joinPath(root, ...String(value).split('/').filter(Boolean)) : ''
@@ -276,6 +289,9 @@ export function resolveInstallPlan(tool, methodId, preferences = {}, { platform
276
289
  : target ? [] : installDir
277
290
  ? [`${method.label} 无法使用自定义安装目录(官方脚本写死了安装路径),将安装到 ${vendorScriptDir(tool, { home, env, method })}。`]
278
291
  : []
292
+ if (tool.id === 'hermes' && target) {
293
+ notices.push(`Hermes 源码安装目录:${target};插件启动器目录:${binDir}。配置、登录和历史保留原位置,不迁移现有数据;官方安装器可能更新全局命令使其指向所选源码。`)
294
+ }
279
295
  if (method.registryEnv === 'MCODE_NPM_REGISTRY' && preferences.registry && !minimaxScriptRegistry(preferences.registry)) {
280
296
  notices.push('MiniMax 官方脚本只接受 https://registry.npmjs.org/ 或 https://registry.npmmirror.com/,本次不改写它的 npm 源。')
281
297
  }
@@ -293,6 +309,7 @@ export function resolveInstallPlan(tool, methodId, preferences = {}, { platform
293
309
  downloadUrls: preferences?.scriptUrls?.[tool.id] ? [scriptUrl] : [...new Set([scriptUrl, ...(method.scriptFallbackUrls ?? [])])],
294
310
  verify: method.verify,
295
311
  installArg: method.installArgs,
312
+ ...(method.extraArgs?.length ? { extraArgs: method.extraArgs } : {}),
296
313
  installDir: target,
297
314
  binDir,
298
315
  env: scriptEnv(method, { installDir: target, scriptUrl, registry: preferences.registry }),
@@ -350,10 +367,11 @@ export function resolveInstallLocation(tool, method, preferences = {}, { home =
350
367
  if (method.kind === 'script') {
351
368
  // A method that cannot honour the configured directory always lands in the
352
369
  // vendor's own location, whatever the user typed.
353
- const dir = methodHonoursInstallDir(method)
370
+ const target = methodHonoursInstallDir(method)
354
371
  ? scriptInstallDir(tool, preferences, { home, env, method })
355
- : vendorScriptDir(tool, { home, env, method })
356
- const binDir = methodHonoursInstallDir(method) && method.installDirSuffix ? joinPath(dir, method.installDirSuffix) : dir
372
+ : ''
373
+ const dir = target || vendorScriptDir(tool, { home, env, method })
374
+ const binDir = target && method.installDirSuffix ? joinPath(target, method.installDirSuffix) : dir
357
375
  return { kind: 'script', dir: binDir, installDir: dir, binaries: [...(method.scriptBinaryNames ?? tool.scriptBinaryNames ?? [])] }
358
376
  }
359
377
  return { kind: 'package-manager', dir: packageManagerBinDir(preferences?.installDir || '', { platform }), installDir: preferences?.installDir || '', binaries: [] }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.1 — 2026-10-18
4
+
5
+ - Honor Hermes custom program directories on Windows and POSIX through the official `-InstallDir` / `--dir` parameter. Place the checkout in the shared root's `hermes-agent` child and bind probes and terminals to its source-local `.hermes/bin` launcher; an old global command cannot satisfy a missing selected installation.
6
+ - Retain existing Hermes configuration, login and history locations. Keep vendor-default discovery when the shared root is empty, preserve old source trees and explain the separate program/data locations. Installation does not migrate user data; the official installer may refresh its global convenience command.
7
+ - Move the official-tools interactive terminal above onboarding and tool maintenance, preserving the existing component and tab/session lifecycle.
8
+ - Add offline directory, installer-argument and selected-entry regressions. Vendor installation and model calls are not part of this release's acceptance.
9
+
10
+ ## 0.21.0 — 2026-10-18
11
+
12
+ - Added support for Hermes Agent (NousResearch source installer): includes native Windows PowerShell and POSIX Bash non-interactive flags, vendor-specific launcher and data paths, `hermes auth` authentication flow, and manual uninstall via `hermes uninstall`.
13
+ - Added support for Pi coding agent (official `@earendil-works/pi-coding-agent` npm package): supports installation via npm/pnpm with `--ignore-scripts`, requires Node >=22.19.0, and uses `pi` followed by `/login` for credential setup. Discovery, version checks, install/update/repair, and interactive terminal functionality are integrated for both new tools; login status is unknown without credential reads, and no verified managed restricted execution is available for either tool.
14
+ - Updated OpenCode dependency from legacy V1 `opencode-ai` to official V2 `@opencode/cli`; legacy V1 scripts are clearly labeled as OpenCode 1, and existing V2 databases are preserved for compatibility.
15
+ - Added official GitHub release lookup for Hermes and official npm version lookup for Pi. Installation and login checks remain separate from managed execution readiness.
16
+
17
+ Automated validation: 844 tests, 841 passed, 3 conditional skips, no failures; strict frozen pnpm install and real client build/check passed. UI checks used mock fixtures. Hermes/Pi installation, authentication and model execution were not performed.
18
+
19
+ To upgrade, fully quit Harness including its tray icon and Host background process, update the dsh-model-router plugin, then restart Harness.
20
+
3
21
  ## 0.20.2 — 2026-10-17
4
22
 
5
23
  - Add explicit compatibility with official DeepSeek Harness Desktop 0.2.1-rc.2, retaining the two supported 0.2.0 release candidates. Older router versions are rejected by the updated Host before plugin code loads. No exact-version exemption or Host modification is needed.
package/README.i18n.yaml CHANGED
@@ -1,4 +1,4 @@
1
1
  # README translation pairing for this repository.
2
2
  # Reviewed together; Git blob hashes identify the exact synchronized texts.
3
- README.md: 35a07201eadc2e87356b584455d83ae15621e861
4
- README.zh.md: 31b5020fdf849479bd284d2cf755577901045546
3
+ README.md: 68dacf84020e0d213120ee9f151640ee2be4a62d
4
+ README.zh.md: 0b6dc3ab7e26e03dd0652f161198f8fa03f19711
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A cost-aware model-routing workbench for **DeepSeek Harness Desktop**. Plan locally, compare configured routes, inspect execution permissions, and run dependent work packages through model APIs or supported official vendor CLIs.
6
6
 
7
- Current version: **0.20.2** (`@ljwei-stak/dsh-model-router`). This patch adds compatibility with **DeepSeek Harness Desktop 0.2.1-rc.2**, while retaining **0.2.0-rc.1 / 0.2.0-rc.2**. It uses the updated official SDK for development and regression checks. Real vendor execution evidence remains tied to **0.20.0**; this patch adds no new model experiments. The router and [GAL](https://github.com/Alice-Marx/deepseek-harness-galgame) have been independent plugins since 0.12.0.
7
+ Current version: **0.21.1** (`@ljwei-stak/dsh-model-router`). Hermes source installs now honor the shared install root without relocating existing user data, and the interactive terminal appears first on the official-tools page. This follows the Hermes/Pi integration and OpenCode V2 package update in 0.21.0. Compatibility remains **DeepSeek Harness Desktop 0.2.0-rc.1 / 0.2.0-rc.2 / 0.2.1-rc.2**. Hermes and Pi do not yet have verified managed runners; real vendor execution evidence remains tied to **0.20.0**, with no new model experiments in this release. The router and [GAL](https://github.com/Alice-Marx/deepseek-harness-galgame) have been independent plugins since 0.12.0.
8
8
 
9
9
  Official CLI teamwork runs packages sequentially with dependency context and no automatic model-directory API fallback. Editable teams explicitly choose the Harness sandbox or the vendor CLI's normal environment. In the latter, Git worktrees collect edits but do not restrict OS access. CLIs retain their configured model unless explicitly mapped. See [real Windows validation and its limits](docs/releases/0.20.0-validation.md).
10
10
 
@@ -23,8 +23,8 @@ Both README versions describe the same interfaces, formulas, defaults and limits
23
23
  | Routing presets | Economy, balanced and quality-first presets; quality floors, estimated token costs and bounded assignment search. Missing prices stay unknown. |
24
24
  | Read-only execution | Preview and confirm a routed run from the workbench, or invoke it in an official Harness session. Supported headless CLIs and model-directory API calls return recorded results. |
25
25
  | Editable CLI execution | A sequential CLI team from the workbench, or one supported CLI from a session, using approved isolated Git worktrees and conditional patch integration. |
26
- | Official tools | Detect, install/update, check launch readiness and inspect login status for ten registry tools; installation jobs have logs and cancellation. |
27
- | Interactive terminal | PowerShell/system shell and all ten registry CLI targets (ZCode TUI on verified Windows desktop installs), including fixed login commands, live output and multiple sessions. |
26
+ | Official tools | Detect, install/update, check launch readiness and inspect login status for twelve registry tools; installation jobs have logs and cancellation. |
27
+ | Interactive terminal | PowerShell/system shell and all twelve registry CLI targets (ZCode TUI on verified Windows desktop installs), including fixed login commands, live output and multiple sessions. |
28
28
  | Costs and subscriptions | Daily/monthly budget checks, economy downgrade or pause, subscription-first routing, quota cooldown and explicit handling of other subscription failures. |
29
29
  | History and quality | Per-package results, dependency views, failure details, rerun/reassignment, ratings and optional stronger-model review. |
30
30
  | Execution boundaries | Shows what each route can read/write and whether its process uses the Harness sandbox. |
@@ -40,16 +40,16 @@ Generating a route recommendation is local: it does not start a vendor CLI or ma
40
40
  Open **Plugins → Add plugin** and enter:
41
41
 
42
42
  ```text
43
- @ljwei-stak/dsh-model-router@0.20.2
43
+ @ljwei-stak/dsh-model-router@0.21.1
44
44
  ```
45
45
 
46
- Use the official HTTPS npm source `https://registry.npmjs.org/` when a mirror lacks that exact version. Enable the plugin, then fully quit Harness (including its tray process) and restart it. Plugin details should show **0.20.2** and the sidebar should contain **模型路由** (Model Router). A global `npm install -g` alone does not register a Desktop plugin.
46
+ Use the official HTTPS npm source `https://registry.npmjs.org/` when a mirror lacks that exact version. Enable the plugin, then fully quit Harness (including its tray process) and restart it. Plugin details should show **0.21.1** and the sidebar should contain **模型路由** (Model Router). A global `npm install -g` alone does not register a Desktop plugin.
47
47
 
48
48
  You can instead install a local `.tgz` archive or its extracted inner `package` directory through the plugin manager. Both must contain `package.json` and `.dsh-plugin`. See [releases](https://github.com/Alice-Marx/dsh-model-router/releases) for archived builds, or build your own checkout below.
49
49
 
50
50
  ### Upgrade from the old package name
51
51
 
52
- The old name `@ljwei-stak/model-router-galgame` ended at 0.13.0. Remove that plugin entry while retaining your profile/application data, then add `@ljwei-stak/dsh-model-router@0.20.2`. Do not keep both entries installed: they share the internal plugin id `model-router-galgame` and tool names. The retained id lets existing router settings and history carry over.
52
+ The old name `@ljwei-stak/model-router-galgame` ended at 0.13.0. Remove that plugin entry while retaining your profile/application data, then add `@ljwei-stak/dsh-model-router@0.21.1`. Do not keep both entries installed: they share the internal plugin id `model-router-galgame` and tool names. The retained id lets existing router settings and history carry over.
53
53
 
54
54
  For the combined 0.11.x package, back up the profile and export the GAL saves you want to preserve before upgrading. Story playback, artwork, music and saves now belong to the independent `@ljwei-stak/dsh-galgame` package. The detailed save limitations are in [MIGRATION.md](docs/archive/MIGRATION.md).
55
55
 
@@ -216,8 +216,12 @@ The CLI team runner is the plugin's own sequential dependency executor. It does
216
216
  | `gemini` | Gemini CLI | Routed headless adapter; not the managed single-tool/team runner |
217
217
  | `opencode` | OpenCode | Install, probe and login check; no verified restricted execution entry yet |
218
218
  | `stepcode` | Step Code | Install, probe and login check; no verified restricted execution entry yet |
219
+ | `hermes` | Hermes Agent | Official Windows/POSIX installer, version checks and interactive terminal; authenticate with `hermes auth`; no verified managed runner |
220
+ | `pi` | Pi | Official npm/pnpm package and interactive terminal; enter `/login` inside Pi; no verified managed runner |
219
221
  | `zcode` | ZCode | Windows; approved `workspace-write` |
220
222
 
223
+ Hermes uses its [official source installer](https://hermes-agent.nousresearch.com/docs/getting-started/installation) in non-interactive mode, then you run `hermes auth`. A configured shared root places its source in `<root>/hermes-agent`; the plugin binds that checkout's `.hermes/bin` launcher and refuses a different old global copy. Existing configuration, login and history stay in their original Hermes home. Without a shared root, vendor defaults remain in use. The installer may refresh its global convenience command to the selected checkout. Uninstall source installs through `hermes uninstall`. Pi uses [`@earendil-works/pi-coding-agent`](https://github.com/earendil-works/pi/tree/main/packages/coding-agent) through npm/pnpm with `--ignore-scripts` and requires Node.js 22.19 or later. Start `pi`, then enter `/login`. Both tools keep login status unknown until confirmed in their own interface; the plugin does not read their credentials.
224
+
221
225
  Installers use fixed official npm packages at each vendor's `@latest`, the vendor's own installer script, or the official publisher-signed Windows ZCode installer. An installation newer than the reported latest is not downgraded. Latest-version lookups are cached for about 12 hours; unavailable lookups leave the version unknown. New vendor releases are not automatically validated by this project. ZCode's installation window still requires you to choose a directory and finish installation.
222
226
 
223
227
  **Unified install settings.** The top of the **官方工具** page (also in the plugin settings form) sets the install address for every tool at once: one install directory (empty means each vendor's default location), an npm source, and per-tool overrides for vendor installer-script mirrors. Directories must be absolute; sources must be https. Each row picks its own install method from `npm`, `pnpm` or the vendor script (`curl | bash` or `irm | iex`); a method that cannot run on this platform says so on the row instead of being silently swapped. The exact command is shown above the buttons before anything runs.
package/README.zh.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  面向 **DeepSeek Harness Desktop** 的成本感知模型路由工作台。在本地规划任务、比较已配置路线、检查执行权限,并通过模型 API 或支持的厂商官方 CLI 执行有依赖关系的工作包。
6
6
 
7
- 当前版本:**0.20.2**(`@ljwei-stak/dsh-model-router`)。本版增加 **DeepSeek Harness Desktop 0.2.1-rc.2** 兼容支持,并保留 **0.2.0-rc.1 / 0.2.0-rc.2**;开发与回归检查使用更新后的官方 SDK。真实厂商执行证据仍见 **0.20.0** 报告;本版未新增模型实测。路由器与 [GAL](https://github.com/Alice-Marx/deepseek-harness-galgame) 自 0.12.0 起已拆分为独立插件。
7
+ 当前版本:**0.21.1**(`@ljwei-stak/dsh-model-router`)。本版修复 Hermes 源码安装无法使用统一根目录的问题,保留现有用户数据位置,并将官方工具终端放到工具页顶部。沿用 0.21.0 的 Hermes/Pi 接入和 OpenCode V2 包更新。继续兼容 **DeepSeek Harness Desktop 0.2.0-rc.1 / 0.2.0-rc.2 / 0.2.1-rc.2**。Hermes 与 Pi 暂无经过验证的托管执行器;真实厂商执行证据仍见 **0.20.0** 报告,本版未新增模型实测。路由器与 [GAL](https://github.com/Alice-Marx/deepseek-harness-galgame) 自 0.12.0 起已拆分为独立插件。
8
8
 
9
9
  官方 CLI 协作按依赖顺序逐包执行、传递依赖上下文,不会自动回退到模型目录 API。可编辑团队需明确选择 Harness 沙箱或厂商 CLI 正常环境。后者通过 Git worktree 收集修改,但不限制操作系统访问。未显式设置模型映射时,各 CLI 沿用自身配置的模型。参见[真实 Windows 验证及其局限](docs/releases/0.20.0-validation.md)。
10
10
 
@@ -23,8 +23,8 @@
23
23
  | 路由预设 | 省钱优先、均衡和效果优先;结合质量下限、估计 token 费用和有界分配搜索。缺失价格保持未知。 |
24
24
  | 只读执行 | 在工作台预览并确认路由执行,或在 Harness 官方会话中调用。支持的无头 CLI 与模型目录 API 调用均记录结果。 |
25
25
  | 可编辑 CLI 执行 | 从工作台执行顺序 CLI 团队,或在会话中调用单个受支持 CLI;使用获批的隔离 Git worktree,并有条件地合入补丁。 |
26
- | 官方工具 | 对注册表中的十种工具检测、安装/更新、检查启动就绪和登录状态;安装任务提供日志和取消功能。 |
27
- | 交互终端 | PowerShell/系统 shell 与十种注册表 CLI 目标(经过验证的 Windows 桌面安装支持 ZCode TUI);包含固定登录命令、实时输出和多会话。 |
26
+ | 官方工具 | 对注册表中的十二种工具检测、安装/更新、检查启动就绪和登录状态;安装任务提供日志和取消功能。 |
27
+ | 交互终端 | PowerShell/系统 shell 与十二种注册表 CLI 目标(经过验证的 Windows 桌面安装支持 ZCode TUI);包含固定登录命令、实时输出和多会话。 |
28
28
  | 费用与订阅 | 日/月预算检查、降级为省钱预设或暂停、订阅优先路由、额度冷却,以及对其他订阅失败的显式处理。 |
29
29
  | 历史与质量 | 逐包结果、依赖视图、失败详情、重跑/改派、评分与可选的更强模型复核。 |
30
30
  | 执行边界 | 显示每条路线可读写的范围,以及进程是否使用 Harness 沙箱。 |
@@ -40,16 +40,16 @@
40
40
  打开 **插件 → 添加插件**,输入:
41
41
 
42
42
  ```text
43
- @ljwei-stak/dsh-model-router@0.20.2
43
+ @ljwei-stak/dsh-model-router@0.21.1
44
44
  ```
45
45
 
46
- 镜像缺少该精确版本时,使用官方 HTTPS npm 源 `https://registry.npmjs.org/`。启用插件,然后完全退出 Harness(包括托盘进程)并重启。插件详情应显示 **0.20.2**,侧栏应出现 **模型路由**。仅执行全局 `npm install -g` 不会把插件注册到 Desktop。
46
+ 镜像缺少该精确版本时,使用官方 HTTPS npm 源 `https://registry.npmjs.org/`。启用插件,然后完全退出 Harness(包括托盘进程)并重启。插件详情应显示 **0.21.1**,侧栏应出现 **模型路由**。仅执行全局 `npm install -g` 不会把插件注册到 Desktop。
47
47
 
48
48
  也可以通过插件管理器安装本地 `.tgz` 压缩包,或解压后的内部 `package` 目录。两者都必须包含 `package.json` 和 `.dsh-plugin`。历史构建见 [Releases](https://github.com/Alice-Marx/dsh-model-router/releases),也可按下文从源码构建。
49
49
 
50
50
  ### 从旧包名升级
51
51
 
52
- 旧名称 `@ljwei-stak/model-router-galgame` 停留在 0.13.0。保留配置档/应用数据,移除旧插件条目,再添加 `@ljwei-stak/dsh-model-router@0.20.2`。不要同时保留两条插件:它们共用内部插件 id `model-router-galgame` 和工具名。保留该 id 能让现有路由设置与历史记录继续使用。
52
+ 旧名称 `@ljwei-stak/model-router-galgame` 停留在 0.13.0。保留配置档/应用数据,移除旧插件条目,再添加 `@ljwei-stak/dsh-model-router@0.21.1`。不要同时保留两条插件:它们共用内部插件 id `model-router-galgame` 和工具名。保留该 id 能让现有路由设置与历史记录继续使用。
53
53
 
54
54
  从合包 0.11.x 升级前,先备份配置档,并导出需要保留的 GAL 存档。剧情播放、立绘、音乐与存档现属于独立的 `@ljwei-stak/dsh-galgame`。详细存档限制见 [MIGRATION.md](docs/archive/MIGRATION.md)。
55
55
 
@@ -216,8 +216,12 @@ CLI 团队运行器是插件自身的顺序依赖执行器,不控制 Harness
216
216
  | `gemini` | Gemini CLI | 路由无头适配器;不属于托管单工具/团队运行器 |
217
217
  | `opencode` | OpenCode | 安装、探测和登录检查;尚无经过验证的受限执行入口 |
218
218
  | `stepcode` | Step Code | 安装、探测和登录检查;尚无经过验证的受限执行入口 |
219
+ | `hermes` | Hermes Agent | Windows/POSIX 官方安装脚本、版本检测、交互终端;使用 `hermes auth` 登录;尚无经过验证的托管执行器 |
220
+ | `pi` | Pi | 官方 npm/pnpm 安装、版本检测、交互终端;启动后输入 `/login`;尚无经过验证的托管执行器 |
219
221
  | `zcode` | ZCode | Windows;获批的 `workspace-write` |
220
222
 
223
+ Hermes 使用[官方源码安装器](https://hermes-agent.nousresearch.com/docs/getting-started/installation)的非交互模式,安装后运行 `hermes auth`。设置统一根目录后,源码安装到 `<根目录>/hermes-agent`,插件绑定该源码的 `.hermes/bin` 入口,不会改用其他旧的全局副本。配置、登录和历史仍保留在原 Hermes 数据目录;未设置统一根时沿用厂商默认位置。官方安装器可能将全局便利命令更新为指向所选源码。源码安装请通过 `hermes uninstall` 卸载。Pi 使用 [`@earendil-works/pi-coding-agent`](https://github.com/earendil-works/pi/tree/main/packages/coding-agent),npm/pnpm 安装带 `--ignore-scripts`,要求 Node.js 22.19 及以上。启动 `pi` 后输入 `/login`。这两种工具的登录状态显示为未知,以各自界面为准;插件不会读取它们的凭据。
224
+
221
225
  安装器使用各厂商 `@latest` 对应的固定官方 npm 包、厂商自有安装脚本,或带官方发行者签名的 Windows ZCode 安装器。已安装版本高于查询到的最新版本时不降级。最新版本查询约缓存 12 小时;查询不可用时版本保持未知。厂商新版本不会自动获得本项目的验证。ZCode 安装窗口仍需用户选择目录并完成安装。
222
226
 
223
227
  **统一安装设置。** **官方工具** 页顶部(插件设置表单中也有)可一次设置所有工具的安装地址:统一安装目录(留空表示各厂商默认位置)、npm 源,以及各工具的厂商安装脚本镜像覆盖项。目录必须是绝对路径,源必须是 HTTPS。每一行分别选择 `npm`、`pnpm` 或厂商脚本(`curl | bash` 或 `irm | iex`);当前平台无法运行的方法会在该行说明,不会静默换成其他方法。执行前会在按钮上方显示精确命令。
@@ -1,8 +1,8 @@
1
1
  # DeepSeek Harness Desktop · 独立插件安装与验证
2
2
 
3
- 模型路由从 **0.12.0** 起独立发布;GAL 是独立的 **0.1.0** 插件。路由 **0.20.2** 声明支持官方 **DeepSeek Harness Desktop 0.2.0-rc.1 / 0.2.0-rc.2 / 0.2.1-rc.2**。GAL 的兼容范围以其自身安装包为准。
3
+ 模型路由从 **0.12.0** 起独立发布;GAL 是独立的 **0.1.0** 插件。路由 **0.21.1** 声明支持官方 **DeepSeek Harness Desktop 0.2.0-rc.1 / 0.2.0-rc.2 / 0.2.1-rc.2**。GAL 的兼容范围以其自身安装包为准。
4
4
 
5
- 当前路由版本为 **0.20.2**,修复 DSH 升级至 **0.2.1-rc.2** 后旧插件被版本检查拒绝的问题。兼容声明列出已核对的宿主版本,不设置兼容豁免。保留原插件 id、配置与运行记录;安装后完全退出 Harness(包括托盘)再重启。官方终端、ZCode TUI 与统一安装设置继续沿用,公开数据默认关闭。
5
+ 当前路由版本为 **0.21.1**,修复 Hermes 自定义程序目录支持,并将官方工具终端移到工具页顶部;沿用 Hermes/Pi 接入和 OpenCode 官方 V2 包。兼容声明列出已核对的宿主版本,不设置兼容豁免。保留原插件 id、配置与运行记录;安装后完全退出 Harness(包括托盘)再重启。官方终端、ZCode TUI 与统一安装设置继续沿用,公开数据默认关闭。
6
6
 
7
7
  [路由 README](../README.zh.md) · [迁移说明](archive/MIGRATION.md) · [独立 GAL README](https://github.com/Alice-Marx/deepseek-harness-galgame#readme)
8
8
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  | 需求 | 添加插件输入框 | 安装后的入口 |
12
12
  | --- | --- | --- |
13
- | 分析复杂度、按质量和费用选模型、调用官方工具 | `@ljwei-stak/dsh-model-router@0.20.2` | 模型路由 |
13
+ | 分析复杂度、按质量和费用选模型、调用官方工具 | `@ljwei-stak/dsh-model-router@0.21.1` | 模型路由 |
14
14
  | 剧情、立绘、音乐、存档、自由对话 | `@ljwei-stak/dsh-galgame@0.1.0` | Gal 模块 |
15
15
  | 两者都要 | 分别安装上面两个包 | 模型路由 + Gal 模块 |
16
16
 
@@ -24,7 +24,7 @@
24
24
  2. 在“包名、GitHub 仓库地址或本地目录路径”输入框填一个完整包名,例如:
25
25
 
26
26
  ```text
27
- @ljwei-stak/dsh-model-router@0.20.2
27
+ @ljwei-stak/dsh-model-router@0.21.1
28
28
  ```
29
29
 
30
30
  只想安装 GAL 时填写:
@@ -40,7 +40,7 @@
40
40
  ```
41
41
 
42
42
  4. 核对预览的包名、版本和宿主兼容信息,安装并启用。完全退出 Harness(包括托盘进程)再启动,确保 Host 加载新代码。
43
- 5. 检查插件详情与侧边栏:路由应为 **0.20.2 / 模型路由**;GAL 应为 **0.1.0 / Gal 模块**。仅装路由时没有 GAL 入口是预期行为。
43
+ 5. 检查插件详情与侧边栏:路由应为 **0.21.1 / 模型路由**;GAL 应为 **0.1.0 / Gal 模块**。仅装路由时没有 GAL 入口是预期行为。
44
44
 
45
45
  普通用户无需 `npm install -g`,该命令不会把插件注册到当前 Harness profile。
46
46
 
@@ -48,13 +48,13 @@
48
48
 
49
49
  进入对应 Release 下载 `.tgz`:
50
50
 
51
- - [模型路由 Releases](https://github.com/Alice-Marx/dsh-model-router/releases)(也可运行 `npm pack @ljwei-stak/dsh-model-router@0.20.2` 获取同一安装包)
51
+ - [模型路由 Releases](https://github.com/Alice-Marx/dsh-model-router/releases)(也可运行 `npm pack @ljwei-stak/dsh-model-router@0.21.1` 获取同一安装包)
52
52
  - [独立 GAL v0.1.0](https://github.com/Alice-Marx/deepseek-harness-galgame/releases/tag/v0.1.0)
53
53
 
54
54
  在“添加插件”填写下载文件的**绝对路径**,例如:
55
55
 
56
56
  ```text
57
- D:\Plugins\ljwei-stak-dsh-model-router-0.20.2.tgz
57
+ D:\Plugins\ljwei-stak-dsh-model-router-0.21.1.tgz
58
58
  ```
59
59
 
60
60
  ```text
@@ -64,7 +64,7 @@ D:\Plugins\ljwei-stak-dsh-galgame-0.1.0.tgz
64
64
  如附有 `.sha256` 文件,用 PowerShell 计算并比较摘要:
65
65
 
66
66
  ```powershell
67
- Get-FileHash -Algorithm SHA256 -LiteralPath 'D:\Plugins\ljwei-stak-dsh-model-router-0.20.2.tgz'
67
+ Get-FileHash -Algorithm SHA256 -LiteralPath 'D:\Plugins\ljwei-stak-dsh-model-router-0.21.1.tgz'
68
68
  Get-FileHash -Algorithm SHA256 -LiteralPath 'D:\Plugins\ljwei-stak-dsh-galgame-0.1.0.tgz'
69
69
  ```
70
70
 
@@ -74,7 +74,7 @@ Get-FileHash -Algorithm SHA256 -LiteralPath 'D:\Plugins\ljwei-stak-dsh-galgame-0
74
74
 
75
75
  1. 在旧合并插件的 GAL 播放器导出需要保留的剧情进度,并备份整个 Harness profile。每份 JSON 仅保存**当前剧情状态**,不含所有手动槽、设置、已读记录或本地音乐;重要槽位可依次读档再导出。
76
76
  2. 在**同一个 profile**安装独立 `@ljwei-stak/dsh-galgame@0.1.0`。
77
- 3. 0.13.0 起包名改为 `@ljwei-stak/dsh-model-router`(旧包名最后版本为 0.13.0,内容相同,之后不再更新):在插件管理器卸载原 `@ljwei-stak/model-router-galgame`(保留 profile 与应用数据),再添加 `@ljwei-stak/dsh-model-router@0.20.2`。不要两个同时安装(条目 id 与工具名相同);条目 id 未变,路由设置与执行记录会保留。完成两项更新后再玩剧情,避免同时操作旧合并入口和新入口。
77
+ 3. 0.13.0 起包名改为 `@ljwei-stak/dsh-model-router`(旧包名最后版本为 0.13.0,内容相同,之后不再更新):在插件管理器卸载原 `@ljwei-stak/model-router-galgame`(保留 profile 与应用数据),再添加 `@ljwei-stak/dsh-model-router@0.21.1`。不要两个同时安装(条目 id 与工具名相同);条目 id 未变,路由设置与执行记录会保留。完成两项更新后再玩剧情,避免同时操作旧合并入口和新入口。
78
78
  4. 现在路由与 GAL 各有一个入口。独立 GAL 沿用旧存档 localStorage 键,核对当前进度和三槽;更换 profile 或进度未恢复时,先切换到匹配剧目,再导入 JSON 备份。
79
79
  5. 后续分别更新需要的插件;只用路由可以不启用或不安装 GAL。保留 profile 与应用数据,卸载插件时不要清理故事数据。
80
80
 
@@ -82,7 +82,7 @@ GAL 核心只包含**《回声之城·正篇》**与**《旧城迁移篇:未
82
82
 
83
83
  ## 5. 模型路由第一次使用
84
84
 
85
- 工作台自 0.15.0 起分成**任务与执行、模型配置、官方工具、预算与安全**四页;0.20.2 沿用该布局。切换页面保留安装任务、终端会话和未完成的输入。
85
+ 工作台自 0.15.0 起分成**任务与执行、模型配置、官方工具、预算与安全**四页;0.21.1 沿用该布局。切换页面保留安装任务、终端会话和未完成的输入。
86
86
 
87
87
  1. 在 Harness 官方**模型**页配置供应商、模型与凭据。插件读取准确的 `provider/model` 目录,模型档案不保存 API Key。
88
88
  2. 在**模型配置**刷新目录,逐条填写自报质量评分、核对过的输入/输出单价(USD / 百万 token)和擅长方向;缺价留空。可选 `cliModel` 必须是该厂商 CLI 已验证接受的名字。可选 LiveBench 名称须准确对应版本与推理档位;它是用户确认的映射,不自动证明实际执行档位。
@@ -114,7 +114,7 @@ GAL 核心只包含**《回声之城·正篇》**与**《旧城迁移篇:未
114
114
  | 检查 | 预期 |
115
115
  | --- | --- |
116
116
  | 路由宿主版本 | 0.2.0-rc.1、0.2.0-rc.2 或 0.2.1-rc.2;包括 `rc` 后缀。 |
117
- | 路由独立安装 | 0.20.2 详情、模型路由入口、目录和模型档案可用;不出现旧合并 GAL 入口。 |
117
+ | 路由独立安装 | 0.21.1 详情、模型路由入口、目录和模型档案可用;不出现旧合并 GAL 入口。 |
118
118
  | GAL 独立安装 | 0.1.0 详情、Gal 模块入口、标题与两部核心剧情可用;不要求安装路由。 |
119
119
  | 共同安装 | 两个入口各一份,路由工具和 GAL 播放器均能使用。 |
120
120
  | 存档迁移 | 同 profile 可读旧键;跨 profile 用匹配剧目 JSON 导入;旧篇目的数据不覆盖。 |
@@ -129,7 +129,7 @@ GAL 核心只包含**《回声之城·正篇》**与**《旧城迁移篇:未
129
129
 
130
130
  ## 8. 开发者从完整源码构建
131
131
 
132
- 路由需要 Node.js **22.19+** 与 **pnpm 10.34.6**。从完整源码仓库根目录构建;当前源码版本为 0.20.2,复现已发布版本时检出对应发布标签。旧版(如 0.15.0)标签保留其当时内容。运行:
132
+ 路由需要 Node.js **22.19+** 与 **pnpm 10.34.6**。从完整源码仓库根目录构建;当前源码版本为 0.21.1,复现已发布版本时检出对应发布标签。旧版(如 0.15.0)标签保留其当时内容。运行:
133
133
 
134
134
  ```powershell
135
135
  pnpm install --frozen-lockfile --strict-peer-dependencies
@@ -1,6 +1,10 @@
1
1
  # 模型路由工作台:配置、规划与实际执行
2
2
 
3
- 适用版本:`@ljwei-stak/dsh-model-router@0.20.0`。声明兼容 DeepSeek Harness Desktop `0.2.0-rc.1 / 0.2.0-rc.2`。安装与开发见[中文 README](../README.zh.md),旧包迁移见[迁移说明](archive/MIGRATION.md)。
3
+ 适用版本:`@ljwei-stak/dsh-model-router@0.21.1`。声明兼容 DeepSeek Harness Desktop `0.2.0-rc.1 / 0.2.0-rc.2 / 0.2.1-rc.2`。安装与开发见[中文 README](../README.zh.md),旧包迁移见[迁移说明](archive/MIGRATION.md)。
4
+
5
+ 0.21.1 支持在统一根目录的 `hermes-agent` 子目录安装 Hermes 源码,插件使用该源码内的独立命令入口;保留现有配置、登录及历史数据的位置。官方工具终端移到本页顶部。
6
+
7
+ 0.21.0 新增 Hermes Agent 与 Pi 的安装更新、版本检测及交互终端,OpenCode npm/pnpm 切换至官方 V2 包。Hermes 与 Pi 的登录由各自 CLI 完成,暂不提供经过验证的托管执行器。
4
8
 
5
9
  0.20.0 在工作台增加**官方工具协作**和明确的执行环境选择。默认保留 CLI 自身配置的模型,分别记录推荐、请求和实际回报模型。Windows Claude 单包执行及 Claude → MiniMax 依赖交接、实际文件整合已通过;具体入口、费用和未验证范围见[验收记录](releases/0.20.0-validation.md)。
6
10
 
@@ -162,6 +166,8 @@ MiniMax 官方 Windows 脚本的 `<# ... #>` 是 PowerShell 帮助注释。0.18.
162
166
  | Gemini CLI | ✓ | — | — | 支持 |
163
167
  | OpenCode | ✓ | bash | — | 包管理器支持;脚本固定 ~/.opencode/bin |
164
168
  | Step Code | — | bash | PowerShell | 脚本使用根目录/stepcode |
169
+ | Hermes Agent | — | bash | PowerShell | 源码放统一根/hermes-agent;配置和数据仍保留原位置 |
170
+ | Pi | npm/pnpm | — | — | `@earendil-works/pi-coding-agent`;Node.js ≥22.19,安装带 `--ignore-scripts` |
165
171
  | ZCode | — | — | — | 官方 Windows 桌面安装窗口中选择 |
166
172
 
167
173
  “—”表示当前厂商未提供相应渠道。每个工具的安装方式下拉框保留所有声明方式,并说明本机不适用的原因。MiniMax 官方脚本只接受官方 npm 源或 npmmirror;其他 npm 镜像请选 npm/pnpm 方式。
@@ -187,13 +193,15 @@ MiniMax 官方 Windows 脚本的 `<# ... #>` 是 PowerShell 帮助注释。0.18.
187
193
  | `codex` | Windows 托管只读;暂不支持可编辑执行,也不进入可编辑团队计划。 |
188
194
  | `kimi-code`、`minimax-code`、`zcode` | 经审批的可编辑执行。 |
189
195
  | `gemini` | 路由无界面适配器,不进入单工具/团队托管执行器。 |
190
- | `opencode`、`stepcode` | 尚未核验受限执行入口;插件负责安装、检测与登录体检,执行请在终端中直接进行。 |
196
+ | `opencode`、`stepcode`、`hermes`、`pi` | 尚未核验受限执行入口;插件负责安装、检测与登录指引,执行请在终端中直接进行。 |
191
197
 
192
198
  ### 交互终端
193
199
 
194
200
  在**官方工具终端**选择系统终端或已安装的 CLI,填写存在的绝对目录;支持时可选登录方式。启动确认显示命令、目录与权限范围。
195
201
 
196
- 0.19.0 起,终端清单覆盖全部十个注册表工具,包含 OpenCode、Step Code 和 Windows ZCode 内置 TUI。“刷新”重新检测安装状态;启动沿用已保存的安装方式与目录,选定副本不存在时会提示,不改用 PATH 上的其他副本。
202
+ 终端清单现覆盖全部十二个注册表工具,包含 Hermes Agent、Pi、OpenCode、Step Code 和 Windows ZCode 内置 TUI。“刷新”重新检测安装状态;启动沿用已保存的安装方式与目录,选定副本不存在时会提示,不改用 PATH 上的其他副本。
203
+
204
+ Hermes 安装后运行 `hermes auth`;Pi 启动后输入 `/login`,没有独立的 `pi login` 命令。两者的登录状态显示为未知,插件不读取凭据。Hermes 的 Windows 命令目录默认为 `%LOCALAPPDATA%/hermes/bin`(配置 `HERMES_HOME` 时使用其 `bin` 子目录),POSIX 使用 `~/.local/bin`;未设置统一目录时沿用这些厂商命令目录;设置后,源码安装到统一根/hermes-agent,插件绑定其中的 `.hermes/bin` 入口,不迁移现有配置与历史。卸载 Hermes 源码安装请在终端使用 `hermes uninstall`,桌面/MSIX 版使用系统应用管理。
197
205
 
198
206
  已安装 **ZCode 桌面版**时,在 ZCode 工具行的“ZCode TUI 独立配置”填写其桌面目录,例如 `D:/Program Files/ZCode`,点击“保存并刷新 TUI 入口”;留空恢复自动检测。核验成功后在官方工具终端选择 **ZCode TUI(桌面版内置)**,使用现有登录环境自行配置模型和插件。无需额外独立 CLI 安装包。内置 CLI 随桌面版更新;ZCode 行可单独“检查并更新桌面版”或“重新安装桌面版”,在官方窗口完成后重新体检。桌面版本与内置 CLI 版本不同,工具行显示桌面版本。
199
207
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ljwei-stak/dsh-model-router",
3
- "version": "0.20.2",
3
+ "version": "0.21.1",
4
4
  "type": "module",
5
5
  "description": "Model Router for official DeepSeek Harness Desktop: cost-aware task routing, dependent work packages and official vendor CLI discovery, installation and execution. GAL is available separately as @ljwei-stak/dsh-galgame.",
6
6
  "repository": {
@@ -105,7 +105,7 @@
105
105
  "experiment:routing-summary": "node scripts/summarize-routing-search.mjs",
106
106
  "experiment:routing-adaptive": "node scripts/adaptive-routing-study.mjs",
107
107
  "audit:livebench": "node scripts/livebench-aggregate-audit.mjs",
108
- "test": "node --test tests/adaptive-*.test.mjs tests/dynamic-data.test.mjs tests/router.test.mjs tests/routing-optimizer.test.mjs tests/routing-evaluation.test.mjs tests/routing-paired-statistics.test.mjs tests/routing-selftest-tools.test.mjs tests/livebench-aggregate-audit.test.mjs tests/livebench-prior-study.test.mjs tests/routing-search-experiment.test.mjs tests/routing-regression-study.test.mjs tests/routing-search-summary.test.mjs tests/harness-*.test.mjs tests/official-tools.test.mjs tests/official-tool-result.test.mjs tests/native-environment.test.mjs tests/native-entry-trust.test.mjs tests/tool-failure-ledger.test.mjs tests/official-team-runtime.test.mjs tests/model-profiles.test.mjs tests/model-profile-editor.test.mjs tests/tool-install-state.test.mjs tests/tool-install-sdk-ui.test.mjs tests/vendor-adapters.test.mjs tests/desktop-compat.test.mjs tests/plugin-split.test.mjs tests/install-settings-host.test.mjs tests/task-executors.test.mjs tests/tool-install-preferences.test.mjs tests/roadmap.test.mjs tests/insights-state.test.mjs tests/billing-team-ledger.test.mjs tests/subscription-billing.test.mjs tests/subscription-host.test.mjs tests/test-pass-regressions.test.mjs tests/roadmap-gaps.test.mjs tests/cordis-inject.test.mjs tests/cli-terminal.test.mjs tests/cli-terminal-gateway.test.mjs tests/latest-versions.test.mjs tests/vendor-installer-validation.test.mjs tests/install-entry-selection.test.mjs tests/script-directory-safety.test.mjs tests/zcode-config.test.mjs tests/zcode-runtime.test.mjs tests/client-loader.test.mjs",
108
+ "test": "node --test tests/adaptive-*.test.mjs tests/dynamic-data.test.mjs tests/router.test.mjs tests/routing-optimizer.test.mjs tests/routing-evaluation.test.mjs tests/routing-paired-statistics.test.mjs tests/routing-selftest-tools.test.mjs tests/livebench-aggregate-audit.test.mjs tests/livebench-prior-study.test.mjs tests/routing-search-experiment.test.mjs tests/routing-regression-study.test.mjs tests/routing-search-summary.test.mjs tests/harness-*.test.mjs tests/official-tools.test.mjs tests/official-tool-result.test.mjs tests/native-environment.test.mjs tests/native-entry-trust.test.mjs tests/tool-failure-ledger.test.mjs tests/official-team-runtime.test.mjs tests/model-profiles.test.mjs tests/model-profile-editor.test.mjs tests/tool-install-state.test.mjs tests/tool-install-sdk-ui.test.mjs tests/vendor-adapters.test.mjs tests/desktop-compat.test.mjs tests/plugin-split.test.mjs tests/install-settings-host.test.mjs tests/task-executors.test.mjs tests/tool-install-preferences.test.mjs tests/roadmap.test.mjs tests/insights-state.test.mjs tests/billing-team-ledger.test.mjs tests/subscription-billing.test.mjs tests/subscription-host.test.mjs tests/test-pass-regressions.test.mjs tests/roadmap-gaps.test.mjs tests/cordis-inject.test.mjs tests/cli-terminal.test.mjs tests/cli-terminal-gateway.test.mjs tests/latest-versions.test.mjs tests/vendor-installer-validation.test.mjs tests/install-entry-selection.test.mjs tests/script-directory-safety.test.mjs tests/zcode-config.test.mjs tests/zcode-runtime.test.mjs tests/client-loader.test.mjs tests/hermes-pi.test.mjs",
109
109
  "prepack": "node scripts/prepack.mjs",
110
110
  "pack:local": "npm pack --pack-destination dist"
111
111
  },