@ljwei-stak/dsh-model-router 0.17.1 → 0.18.0

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.
@@ -47,7 +47,7 @@ var import_react6 = __toESM(require("react"), 1);
47
47
 
48
48
  // .dsh-plugin/shared/official-tool-registry.mjs
49
49
  var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/";
50
- function packageManagerMethod(id2, label, manager, spec, { registryFlag = "--registry=" } = {}) {
50
+ function packageManagerMethod(id2, label, manager, spec, { registryFlag = "--registry=", command = "install", extraArgs = [] } = {}) {
51
51
  return Object.freeze({
52
52
  id: id2,
53
53
  label,
@@ -55,9 +55,9 @@ function packageManagerMethod(id2, label, manager, spec, { registryFlag = "--reg
55
55
  manager,
56
56
  spec,
57
57
  registryFlag,
58
- installArgs: Object.freeze(["install", "-g", spec]),
58
+ installArgs: Object.freeze([command, "-g", spec, ...extraArgs]),
59
59
  uninstallArgs: Object.freeze(["uninstall", "-g", spec.replace(/@latest$/, "")]),
60
- /** npm/pnpm both accept --prefix to place a global install off the default. */
60
+ /** The resolver binds npm prefix or pnpm global-dir/global-bin-dir. */
61
61
  supportsInstallDir: true,
62
62
  supportsRegistry: true
63
63
  });
@@ -69,8 +69,16 @@ function scriptMethod(id2, label, {
69
69
  installArg = [],
70
70
  env = [],
71
71
  platforms,
72
+ supportsInstallDir = false,
72
73
  installDirEnv = "",
73
- defaultInstallDir = ""
74
+ installDirSuffix = "",
75
+ defaultScriptInstallDir,
76
+ defaultScriptInstallDirEnv,
77
+ scriptBinaryNames,
78
+ registryEnv,
79
+ installDirSubdir,
80
+ uninstallUnsupportedReason,
81
+ scriptFallbackUrls
74
82
  }) {
75
83
  return Object.freeze({
76
84
  id: id2,
@@ -83,14 +91,19 @@ function scriptMethod(id2, label, {
83
91
  installArgs: Object.freeze(installArg),
84
92
  /** Environment the vendor script itself documents for this install. */
85
93
  env: Object.freeze(env),
86
- /** When set, the configured directory is this environment variable, not an argv flag. */
87
- installDirEnv,
88
- /** Home-relative directory the vendor uses when the user sets none. Slashes only. */
89
- defaultInstallDir,
90
94
  platforms: Object.freeze(platforms),
91
95
  /** Whether the vendor script honours a caller-chosen directory at all. */
92
- supportsInstallDir: Boolean(installDirEnv) || installArg.length > 0,
93
- supportsRegistry: false
96
+ supportsInstallDir,
97
+ installDirEnv,
98
+ installDirSuffix,
99
+ ...installDirSubdir ? { installDirSubdir } : {},
100
+ ...uninstallUnsupportedReason ? { uninstallUnsupportedReason } : {},
101
+ ...defaultScriptInstallDir ? { defaultScriptInstallDir } : {},
102
+ ...defaultScriptInstallDirEnv ? { defaultScriptInstallDirEnv } : {},
103
+ ...scriptBinaryNames ? { scriptBinaryNames: Object.freeze(scriptBinaryNames) } : {},
104
+ ...scriptFallbackUrls ? { scriptFallbackUrls: Object.freeze(scriptFallbackUrls) } : {},
105
+ ...registryEnv ? { registryEnv } : {},
106
+ supportsRegistry: Boolean(registryEnv)
94
107
  });
95
108
  }
96
109
  var OFFICIAL_TOOLS = Object.freeze([
@@ -107,8 +120,7 @@ var OFFICIAL_TOOLS = Object.freeze([
107
120
  probeNote: "kimi \u4E0E\u65E7 Python \u7248 kimi-cli \u540C\u540D\uFF1B\u8BF7\u6838\u5BF9\u53EF\u6267\u884C\u6587\u4EF6\u6765\u6E90\u548C\u7248\u672C\u3002",
108
121
  providerHints: ["moonshot", "kimi"],
109
122
  scriptBinaryNames: Object.freeze(["kimi", "kimi.exe"]),
110
- scriptBinaryRelatives: Object.freeze(["bin/kimi", "bin/kimi.exe"]),
111
- defaultScriptInstallDir: ".kimi-code",
123
+ defaultScriptInstallDir: ".kimi-code/bin",
112
124
  installMethods: Object.freeze([
113
125
  packageManagerMethod("npm", "npm", "npm", "@moonshot-ai/kimi-code@latest"),
114
126
  packageManagerMethod("pnpm", "pnpm", "pnpm", "@moonshot-ai/kimi-code@latest"),
@@ -116,25 +128,23 @@ var OFFICIAL_TOOLS = Object.freeze([
116
128
  shell: "bash",
117
129
  scriptUrl: "https://code.kimi.com/kimi-code/install.sh",
118
130
  platforms: ["linux", "darwin"],
131
+ supportsInstallDir: true,
119
132
  installDirEnv: "KIMI_INSTALL_DIR",
120
- defaultInstallDir: ".kimi-code",
133
+ installDirSuffix: "bin",
121
134
  env: ["KIMI_INSTALL_DIR"],
122
- verify: Object.freeze({
123
- mustInclude: ["code.kimi.com/kimi-code", "KIMI_INSTALL_DIR"],
124
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "@minimax-ai/code"]
125
- })
135
+ scriptBinaryNames: ["kimi"],
136
+ verify: Object.freeze({ mustInclude: ["kimi-code", "code.kimi.com/kimi-code"] })
126
137
  }),
127
138
  scriptMethod("script-powershell", "irm | iex", {
128
139
  shell: "powershell",
129
140
  scriptUrl: "https://code.kimi.com/kimi-code/install.ps1",
130
141
  platforms: ["win32"],
142
+ supportsInstallDir: true,
131
143
  installDirEnv: "KIMI_INSTALL_DIR",
132
- defaultInstallDir: ".kimi-code",
144
+ installDirSuffix: "bin",
133
145
  env: ["KIMI_INSTALL_DIR"],
134
- verify: Object.freeze({
135
- mustInclude: ["code.kimi.com/kimi-code", "KIMI_INSTALL_DIR"],
136
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "@minimax-ai/code"]
137
- })
146
+ scriptBinaryNames: ["kimi.exe"],
147
+ verify: Object.freeze({ mustInclude: ["kimi-code", "code.kimi.com/kimi-code"] })
138
148
  })
139
149
  ])
140
150
  }),
@@ -149,31 +159,30 @@ var OFFICIAL_TOOLS = Object.freeze([
149
159
  uninstallArgs: ["uninstall", "-g", "@anthropic-ai/claude-code"],
150
160
  probeExecutables: ["claude"],
151
161
  providerHints: ["anthropic", "claude"],
152
- // The native installer always lands in ~/.local/bin; it has no directory flag.
153
162
  scriptBinaryNames: Object.freeze(["claude", "claude.exe"]),
154
163
  defaultScriptInstallDir: ".local/bin",
155
164
  installMethods: Object.freeze([
156
165
  packageManagerMethod("npm", "npm", "npm", "@anthropic-ai/claude-code@latest"),
157
- packageManagerMethod("pnpm", "pnpm", "pnpm", "@anthropic-ai/claude-code@latest"),
166
+ packageManagerMethod("pnpm", "pnpm", "pnpm", "@anthropic-ai/claude-code@latest", {
167
+ command: "add",
168
+ extraArgs: ["--ignore-scripts=false", "--allow-build=@anthropic-ai/claude-code"]
169
+ }),
170
+ // The native installer has no directory argument. npm/pnpm support a prefix.
158
171
  scriptMethod("script-bash", "curl | bash", {
159
172
  shell: "bash",
160
173
  scriptUrl: "https://claude.ai/install.sh",
174
+ scriptFallbackUrls: ["https://downloads.claude.ai/claude-code-releases/bootstrap.sh"],
161
175
  platforms: ["linux", "darwin"],
162
- defaultInstallDir: ".local/bin",
163
- verify: Object.freeze({
164
- mustInclude: ["downloads.claude.ai/claude-code-releases", "claude.ai/install.sh"],
165
- mustNotInclude: ["@minimax-ai/code", "releases.openai.com/codex"]
166
- })
176
+ scriptBinaryNames: ["claude"],
177
+ verify: Object.freeze({ mustInclude: ["downloads.claude.ai/claude-code-releases", "manifest.json"] })
167
178
  }),
168
179
  scriptMethod("script-powershell", "irm | iex", {
169
180
  shell: "powershell",
170
181
  scriptUrl: "https://claude.ai/install.ps1",
182
+ scriptFallbackUrls: ["https://downloads.claude.ai/claude-code-releases/bootstrap.ps1"],
171
183
  platforms: ["win32"],
172
- defaultInstallDir: ".local/bin",
173
- verify: Object.freeze({
174
- mustInclude: ["downloads.claude.ai/claude-code-releases"],
175
- mustNotInclude: ["@minimax-ai/code", "releases.openai.com/codex"]
176
- })
184
+ scriptBinaryNames: ["claude.exe"],
185
+ verify: Object.freeze({ mustInclude: ["downloads.claude.ai/claude-code-releases", "manifest.json"] })
177
186
  })
178
187
  ])
179
188
  }),
@@ -190,33 +199,34 @@ var OFFICIAL_TOOLS = Object.freeze([
190
199
  probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u53D6 npm \u6700\u65B0\u7248\u3002",
191
200
  providerHints: ["openai", "gpt", "codex"],
192
201
  scriptBinaryNames: Object.freeze(["codex", "codex.exe"]),
202
+ defaultScriptInstallDir: ".local/bin",
193
203
  installMethods: Object.freeze([
194
204
  packageManagerMethod("npm", "npm", "npm", "@openai/codex@latest"),
195
205
  packageManagerMethod("pnpm", "pnpm", "pnpm", "@openai/codex@latest"),
196
- scriptMethod("script-bash", "curl | bash", {
197
- shell: "bash",
206
+ scriptMethod("script-sh", "curl | sh", {
207
+ shell: "sh",
198
208
  scriptUrl: "https://chatgpt.com/codex/install.sh",
199
209
  platforms: ["linux", "darwin"],
210
+ supportsInstallDir: true,
200
211
  installDirEnv: "CODEX_INSTALL_DIR",
201
- defaultInstallDir: ".local/bin",
202
212
  env: ["CODEX_INSTALL_DIR"],
203
- verify: Object.freeze({
204
- mustInclude: ["releases.openai.com/codex", "CODEX_INSTALL_DIR"],
205
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "@minimax-ai/code"]
206
- })
213
+ scriptBinaryNames: ["codex"],
214
+ verify: Object.freeze({ mustInclude: ["releases.openai.com/codex", "CODEX_INSTALL_DIR"] })
207
215
  }),
208
216
  scriptMethod("script-powershell", "irm | iex", {
209
217
  shell: "powershell",
210
218
  scriptUrl: "https://chatgpt.com/codex/install.ps1",
211
219
  platforms: ["win32"],
220
+ supportsInstallDir: true,
212
221
  installDirEnv: "CODEX_INSTALL_DIR",
213
- // %LOCALAPPDATA%\Programs\OpenAI\Codex\bin under a normal user profile.
214
- defaultInstallDir: "AppData/Local/Programs/OpenAI/Codex/bin",
215
222
  env: ["CODEX_INSTALL_DIR"],
216
- verify: Object.freeze({
217
- mustInclude: ["releases.openai.com/codex", "CODEX_INSTALL_DIR"],
218
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "@minimax-ai/code"]
219
- })
223
+ defaultScriptInstallDirEnv: "LOCALAPPDATA",
224
+ defaultScriptInstallDir: "Programs/OpenAI/Codex/bin",
225
+ installDirSubdir: "codex/bin",
226
+ uninstallUnsupportedReason: "Codex \u5B98\u65B9 Windows \u811A\u672C\u4F7F\u7528\u76EE\u5F55\u8054\u63A5\u548C\u7248\u672C\u5316\u8D44\u6E90\uFF1B\u8BF7\u6309\u5B98\u65B9\u5378\u8F7D\u8BF4\u660E\u79FB\u9664\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u5220\u9664\u8054\u63A5\u76EE\u6807\u3002",
227
+ // The Windows bin directory is a junction to the versioned payload.
228
+ scriptBinaryNames: ["codex.exe"],
229
+ verify: Object.freeze({ mustInclude: ["releases.openai.com/codex", "CODEX_INSTALL_DIR"] })
220
230
  })
221
231
  ])
222
232
  }),
@@ -231,50 +241,39 @@ var OFFICIAL_TOOLS = Object.freeze([
231
241
  uninstallArgs: ["uninstall", "-g", "@minimax-ai/code"],
232
242
  probeExecutables: ["mcode"],
233
243
  providerHints: ["minimax"],
234
- scriptBinaryNames: Object.freeze([
235
- "mcode",
236
- "mcode.exe",
237
- "mcode.cmd",
238
- "mcode.ps1",
239
- "mcode-tools",
240
- "mcode-tools.cmd",
241
- "mcode-tools.ps1"
242
- ]),
243
- scriptBinaryRelatives: Object.freeze([
244
- "mcode.cmd",
245
- "mcode.ps1",
246
- "mcode-tools.cmd",
247
- "mcode-tools.ps1",
248
- "bin/mcode",
249
- "bin/mcode-tools"
250
- ]),
251
- defaultScriptInstallDir: ".minimax-code",
244
+ defaultMethodsByPlatform: Object.freeze({ win32: "script-powershell" }),
245
+ scriptBinaryNames: Object.freeze(["mcode", "mcode-tools"]),
246
+ defaultScriptInstallDir: ".minimax-code/bin",
252
247
  installMethods: Object.freeze([
253
- packageManagerMethod("npm", "npm", "npm", "@minimax-ai/code@latest"),
254
- packageManagerMethod("pnpm", "pnpm", "pnpm", "@minimax-ai/code@latest"),
248
+ packageManagerMethod("npm", "npm", "npm", "@minimax-ai/code@latest", {
249
+ extraArgs: ["--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"]
250
+ }),
251
+ packageManagerMethod("pnpm", "pnpm", "pnpm", "@minimax-ai/code@latest", {
252
+ command: "add",
253
+ extraArgs: ["--ignore-scripts=false", "--allow-build=@minimax-ai/code", "--allow-build=better-sqlite3"]
254
+ }),
255
255
  scriptMethod("script-bash", "curl | bash", {
256
256
  shell: "bash",
257
257
  scriptUrl: "https://filecdn.minimax.chat/public/install.sh",
258
258
  platforms: ["linux", "darwin"],
259
+ supportsInstallDir: true,
259
260
  installDirEnv: "MCODE_INSTALL_DIR",
260
- defaultInstallDir: ".minimax-code",
261
+ installDirSuffix: "bin",
262
+ registryEnv: "MCODE_NPM_REGISTRY",
261
263
  env: ["MCODE_INSTALL_DIR", "MCODE_NPM_REGISTRY"],
262
- verify: Object.freeze({
263
- mustInclude: ["@minimax-ai/code", "MCODE_INSTALL_DIR", "MiniMax Code"],
264
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "releases.openai.com/codex"]
265
- })
264
+ verify: Object.freeze({ mustInclude: ["MiniMax Code CLI", "@minimax-ai/code"] })
266
265
  }),
267
266
  scriptMethod("script-powershell", "irm | iex", {
268
267
  shell: "powershell",
269
268
  scriptUrl: "https://filecdn.minimax.chat/public/install.ps1",
270
269
  platforms: ["win32"],
270
+ supportsInstallDir: true,
271
271
  installDirEnv: "MCODE_INSTALL_DIR",
272
- defaultInstallDir: ".minimax-code",
272
+ registryEnv: "MCODE_NPM_REGISTRY",
273
273
  env: ["MCODE_INSTALL_DIR", "MCODE_NPM_REGISTRY"],
274
- verify: Object.freeze({
275
- mustInclude: ["@minimax-ai/code", "MCODE_INSTALL_DIR", "MiniMax Code"],
276
- mustNotInclude: ["downloads.claude.ai/claude-code-releases", "releases.openai.com/codex"]
277
- })
274
+ defaultScriptInstallDir: ".minimax-code",
275
+ scriptBinaryNames: ["mcode.cmd", "mcode.ps1", "mcode-tools.cmd", "mcode-tools.ps1"],
276
+ verify: Object.freeze({ mustInclude: ["MiniMax Code CLI", "@minimax-ai/code"] })
278
277
  })
279
278
  ])
280
279
  }),
@@ -294,28 +293,22 @@ var OFFICIAL_TOOLS = Object.freeze([
294
293
  installMethods: Object.freeze([
295
294
  packageManagerMethod("npm", "npm", "npm", "@mimo-ai/cli@latest"),
296
295
  packageManagerMethod("pnpm", "pnpm", "pnpm", "@mimo-ai/cli@latest"),
297
- // The Unix script hardcodes ~/.mimocode/bin and ignores MIMOCODE_INSTALL_DIR.
298
296
  scriptMethod("script-bash", "curl | bash", {
299
297
  shell: "bash",
300
298
  scriptUrl: "https://mimo.xiaomi.com/install",
301
299
  platforms: ["linux", "darwin"],
302
- defaultInstallDir: ".mimocode/bin",
303
- verify: Object.freeze({
304
- mustInclude: ["MiMoCode", "mimo.xiaomi.com"],
305
- mustNotInclude: ["@minimax-ai/code", "downloads.claude.ai/claude-code-releases"]
306
- })
300
+ scriptBinaryNames: ["mimo"],
301
+ verify: Object.freeze({ mustInclude: ["mimo.xiaomi.com", ".mimocode"] })
307
302
  }),
308
303
  scriptMethod("script-powershell", "irm | iex", {
309
304
  shell: "powershell",
310
305
  scriptUrl: "https://mimo.xiaomi.com/install.ps1",
311
306
  platforms: ["win32"],
307
+ supportsInstallDir: true,
312
308
  installDirEnv: "MIMOCODE_INSTALL_DIR",
313
- defaultInstallDir: ".mimocode/bin",
314
309
  env: ["MIMOCODE_INSTALL_DIR"],
315
- verify: Object.freeze({
316
- mustInclude: ["MiMoCode", "mimo.xiaomi.com", "MIMOCODE_INSTALL_DIR"],
317
- mustNotInclude: ["@minimax-ai/code", "downloads.claude.ai/claude-code-releases"]
318
- })
310
+ scriptBinaryNames: ["mimo.exe"],
311
+ verify: Object.freeze({ mustInclude: ["mimo.xiaomi.com", ".mimocode"] })
319
312
  })
320
313
  ])
321
314
  }),
@@ -330,8 +323,9 @@ var OFFICIAL_TOOLS = Object.freeze([
330
323
  uninstallArgs: ["uninstall", "-g", "@xai-official/grok"],
331
324
  probeExecutables: ["grok"],
332
325
  providerHints: ["xai", "grok"],
333
- scriptBinaryNames: Object.freeze(["grok", "grok.exe", "agent", "agent.exe"]),
326
+ scriptBinaryNames: Object.freeze(["grok", "agent", "grok.exe", "agent.exe", "grove.exe", "grove-fsmonitor.exe", "grove-credential.exe"]),
334
327
  defaultScriptInstallDir: ".grok/bin",
328
+ pathProfileMarkers: Object.freeze(["# >>> grok installer >>>", "# <<< grok installer <<<"]),
335
329
  installMethods: Object.freeze([
336
330
  packageManagerMethod("npm", "npm", "npm", "@xai-official/grok@latest"),
337
331
  packageManagerMethod("pnpm", "pnpm", "pnpm", "@xai-official/grok@latest"),
@@ -339,25 +333,21 @@ var OFFICIAL_TOOLS = Object.freeze([
339
333
  shell: "bash",
340
334
  scriptUrl: "https://x.ai/cli/install.sh",
341
335
  platforms: ["linux", "darwin"],
336
+ supportsInstallDir: true,
342
337
  installDirEnv: "GROK_BIN_DIR",
343
- defaultInstallDir: ".grok/bin",
344
338
  env: ["GROK_BIN_DIR"],
345
- verify: Object.freeze({
346
- mustInclude: ["x.ai/cli", "GROK_BIN_DIR"],
347
- mustNotInclude: ["@minimax-ai/code", "downloads.claude.ai/claude-code-releases"]
348
- })
339
+ scriptBinaryNames: ["grok", "agent"],
340
+ verify: Object.freeze({ mustInclude: ["Grok CLI installer", "x.ai/cli"] })
349
341
  }),
350
342
  scriptMethod("script-powershell", "irm | iex", {
351
343
  shell: "powershell",
352
344
  scriptUrl: "https://x.ai/cli/install.ps1",
353
345
  platforms: ["win32"],
346
+ supportsInstallDir: true,
354
347
  installDirEnv: "GROK_BIN_DIR",
355
- defaultInstallDir: ".grok/bin",
356
348
  env: ["GROK_BIN_DIR"],
357
- verify: Object.freeze({
358
- mustInclude: ["x.ai/cli", "GROK_BIN_DIR"],
359
- mustNotInclude: ["@minimax-ai/code", "downloads.claude.ai/claude-code-releases"]
360
- })
349
+ scriptBinaryNames: ["grok.exe", "agent.exe", "grove.exe", "grove-fsmonitor.exe", "grove-credential.exe"],
350
+ verify: Object.freeze({ mustInclude: ["Grok CLI installer", "x.ai/cli"] })
361
351
  })
362
352
  ])
363
353
  }),
@@ -405,7 +395,9 @@ var OFFICIAL_TOOLS = Object.freeze([
405
395
  shell: "bash",
406
396
  scriptUrl: "https://opencode.ai/install",
407
397
  platforms: ["linux", "darwin"],
408
- defaultInstallDir: ".opencode/bin",
398
+ // The published script hardcodes $HOME/.opencode/bin, so it cannot honour
399
+ // a custom install directory; the npm/pnpm methods above can.
400
+ supportsInstallDir: false,
409
401
  verify: Object.freeze({
410
402
  mustInclude: ["anomalyco/opencode", "opencode.ai"],
411
403
  mustNotInclude: ["static-openapi.stepfun.com"]
@@ -435,7 +427,7 @@ var OFFICIAL_TOOLS = Object.freeze([
435
427
  platforms: ["linux", "darwin"],
436
428
  installArg: ["--install-dir"],
437
429
  env: ["STEP_INSTALL_DIR", "STEP_RELEASE_BASE_URL"],
438
- defaultInstallDir: ".stepcode/bin",
430
+ supportsInstallDir: true,
439
431
  verify: Object.freeze({
440
432
  mustInclude: ["static-openapi.stepfun.com", "stepcode installer"],
441
433
  mustNotInclude: ["anomalyco/opencode"]
@@ -447,7 +439,7 @@ var OFFICIAL_TOOLS = Object.freeze([
447
439
  platforms: ["win32"],
448
440
  installArg: ["-InstallDir"],
449
441
  env: ["STEP_INSTALL_DIR", "STEP_RELEASE_BASE_URL"],
450
- defaultInstallDir: ".stepcode/bin",
442
+ supportsInstallDir: true,
451
443
  verify: Object.freeze({
452
444
  mustInclude: ["static-openapi.stepfun.com", "stepcode"],
453
445
  mustNotInclude: ["anomalyco/opencode"]
@@ -486,7 +478,8 @@ function installMethodsFor(tool) {
486
478
  function installMethodFor(tool, methodId) {
487
479
  const wanted = String(methodId ?? "").trim();
488
480
  if (!wanted) return null;
489
- return installMethodsFor(tool).find((method) => method.id === wanted) ?? null;
481
+ const canonical = tool?.id === "codex" && wanted === "script-bash" ? "script-sh" : wanted;
482
+ return installMethodsFor(tool).find((method) => method.id === canonical) ?? null;
490
483
  }
491
484
  function defaultInstallMethod(tool) {
492
485
  return installMethodsFor(tool)[0] ?? null;
@@ -500,8 +493,8 @@ function installCommandLineFor(tool, method) {
500
493
  if (!tool || tool.unsupported) return null;
501
494
  if (!method) return null;
502
495
  if (method.kind === "script") {
503
- const piped = method.shell === "powershell" ? `irm ${method.scriptUrl} | iex` : `curl -fsSL ${method.scriptUrl} | bash`;
504
- return method.installArgs.length ? `${piped} ${method.installArgs[0]} <\u5B89\u88C5\u76EE\u5F55>` : piped;
496
+ const piped = method.shell === "powershell" ? `irm ${method.scriptUrl} | iex` : `curl -fsSL ${method.scriptUrl} | ${method.shell}`;
497
+ return piped;
505
498
  }
506
499
  return `${method.manager} ${method.installArgs.join(" ")}`;
507
500
  }
@@ -831,9 +824,9 @@ function keepKnownToolIds(raw, known, subject) {
831
824
  }
832
825
  return raw;
833
826
  }
834
- function parseInstallPreferences(config = {}, { tools = [] } = {}) {
827
+ function parseInstallPreferences(config = {}, { tools = [], home = "", env = {} } = {}) {
835
828
  const known = new Set(tools.map((tool) => tool.id));
836
- const installDir = expandInstallDir(valueOf(config, "toolInstallDir", ""));
829
+ const installDir = expandInstallDir(valueOf(config, "toolInstallDir", ""), { home, env });
837
830
  const registry = normalizeSourceUrl(valueOf(config, "toolNpmRegistry", DEFAULT_NPM_REGISTRY), "npm \u6E90") || DEFAULT_NPM_REGISTRY;
838
831
  const methodRaw = keepKnownToolIds(
839
832
  parseJsonObject(valueOf(config, "toolInstallMethodsJson", "{}"), "\u5B89\u88C5\u65B9\u5F0F\u8986\u76D6"),
@@ -850,9 +843,11 @@ function parseInstallPreferences(config = {}, { tools = [] } = {}) {
850
843
  for (const [id2, methodId] of Object.entries(methodRaw)) methods[id2] = methodId.trim();
851
844
  for (const [id2, url] of Object.entries(scriptRaw)) scriptUrls[id2] = normalizeSourceUrl(url, `${id2} \u5B89\u88C5\u811A\u672C\u6E90`);
852
845
  for (const [id2, methodId] of Object.entries(methods)) {
853
- if (!installMethodFor({ id: id2, installMethods: tools.find((tool) => tool.id === id2)?.installMethods }, methodId)) {
846
+ const selected = installMethodFor({ id: id2, installMethods: tools.find((tool) => tool.id === id2)?.installMethods }, methodId);
847
+ if (!selected) {
854
848
  throw new Error(`${id2} \u4E0D\u652F\u6301\u5B89\u88C5\u65B9\u5F0F ${methodId}\u3002`);
855
849
  }
850
+ methods[id2] = selected.id;
856
851
  }
857
852
  return {
858
853
  installDir,
@@ -2347,6 +2342,23 @@ function compareReleaseVersions(left, right) {
2347
2342
  }
2348
2343
 
2349
2344
  // .dsh-plugin/client/tool-install-state.mjs
2345
+ var INSTALL_DEFAULTS = Object.freeze({
2346
+ toolInstallDir: "",
2347
+ toolNpmRegistry: DEFAULT_NPM_REGISTRY,
2348
+ toolScriptUrlsJson: "{}",
2349
+ toolAllowScriptInstall: true
2350
+ });
2351
+ function validateToolInstallSetting(key, value) {
2352
+ try {
2353
+ parseInstallPreferences({ [key]: value }, { tools: OFFICIAL_TOOLS });
2354
+ return "";
2355
+ } catch (error) {
2356
+ return error?.message || "\u5B89\u88C5\u8BBE\u7F6E\u65E0\u6548\u3002";
2357
+ }
2358
+ }
2359
+ function retainToolInstallDrafts(drafts, values = {}) {
2360
+ return Object.fromEntries(Object.entries(drafts).filter(([key, value]) => value !== (values[key] ?? INSTALL_DEFAULTS[key])));
2361
+ }
2350
2362
  function toolInstallAction({ tool, probe, readiness, job, probeStatus, latestVersion = null }) {
2351
2363
  const running = job?.status === "running";
2352
2364
  const installed = probe?.installed === true;
@@ -2381,7 +2393,7 @@ function toolMaintenanceActions({ tool, probe, job, probeStatus, summary }) {
2381
2393
  }
2382
2394
  };
2383
2395
  }
2384
- function installClickRefusal({ tool, submitting = false, running = false, operation = "install" } = {}) {
2396
+ function installClickRefusal({ tool, submitting = false, running = false, operation = "install", pendingSettings = false, installError = "" } = {}) {
2385
2397
  if (!tool) return "\u672A\u77E5\u5B98\u65B9\u5DE5\u5177\uFF0C\u65E0\u6CD5\u5F00\u59CB\u5B89\u88C5\u3002";
2386
2398
  if (tool.unsupported) return String(tool.unsupportedReason ?? "").trim() || "\u6B64\u5DE5\u5177\u6682\u4E0D\u652F\u6301\u4E00\u952E\u5B89\u88C5\u3002";
2387
2399
  if (tool.manager === "signed-windows-installer" && operation !== "install") {
@@ -2390,6 +2402,8 @@ function installClickRefusal({ tool, submitting = false, running = false, operat
2390
2402
  if (submitting || running) {
2391
2403
  return operation === "uninstall" ? "\u8BE5\u5DE5\u5177\u6B63\u5728\u5378\u8F7D\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u3002" : operation === "repair" ? "\u8BE5\u5DE5\u5177\u6B63\u5728\u4FEE\u590D\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u3002" : "\u8BE5\u5DE5\u5177\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7B49\u5F85\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u3002";
2392
2404
  }
2405
+ if (pendingSettings) return "\u8BF7\u5148\u4FDD\u5B58\u6216\u653E\u5F03\u5B89\u88C5\u8BBE\u7F6E\u66F4\u6539\uFF0C\u5E76\u7B49\u5F85\u5B89\u88C5\u547D\u4EE4\u5237\u65B0\u3002";
2406
+ if (installError) return installError;
2393
2407
  return "";
2394
2408
  }
2395
2409
  function payloadError(payload, fallback) {
@@ -3204,7 +3218,7 @@ function LaunchPreview({ value, changed, running, confirmRef, onConfirm, onCance
3204
3218
  // .dsh-plugin/client/cli-terminal.jsx
3205
3219
  var import_react4 = __toESM(require("react"), 1);
3206
3220
 
3207
- // node_modules/@xterm/xterm/lib/xterm.mjs
3221
+ // node_modules/.pnpm/@xterm+xterm@6.0.0/node_modules/@xterm/xterm/lib/xterm.mjs
3208
3222
  var zs = Object.defineProperty;
3209
3223
  var Rl = Object.getOwnPropertyDescriptor;
3210
3224
  var Ll = (s15, t) => {
@@ -12318,7 +12332,7 @@ var Dl = class extends D {
12318
12332
  }
12319
12333
  };
12320
12334
 
12321
- // node_modules/@xterm/addon-fit/lib/addon-fit.mjs
12335
+ // node_modules/.pnpm/@xterm+addon-fit@0.11.0/node_modules/@xterm/addon-fit/lib/addon-fit.mjs
12322
12336
  var h = 2;
12323
12337
  var _ = 1;
12324
12338
  var o = class {
@@ -12342,7 +12356,7 @@ var o = class {
12342
12356
  }
12343
12357
  };
12344
12358
 
12345
- // node_modules/@xterm/xterm/css/xterm.css
12359
+ // node_modules/.pnpm/@xterm+xterm@6.0.0/node_modules/@xterm/xterm/css/xterm.css
12346
12360
  var xterm_default = `/**
12347
12361
  * Copyright (c) 2014 The xterm.js authors. All rights reserved.
12348
12362
  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
@@ -12631,7 +12645,7 @@ var xterm_default = `/**
12631
12645
  `;
12632
12646
 
12633
12647
  // .dsh-plugin/client/host-version.mjs
12634
- var ROUTER_CLIENT_VERSION = true ? "0.17.1" : "";
12648
+ var ROUTER_CLIENT_VERSION = true ? "0.18.0" : "";
12635
12649
  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";
12636
12650
  function isMissingRemoteMethod(message) {
12637
12651
  const value = String(message ?? "");
@@ -13223,14 +13237,15 @@ var router_main_default = `.mr-workspace {
13223
13237
  .mr-tool-dot.running { background: #e9a640; }
13224
13238
  .mr-tool-command { display: block; margin-top: 6px; padding: 4px 8px; font-size: 12px; background: var(--dsw-alias-markdown-code-block); border-radius: 6px; overflow-wrap: anywhere; }
13225
13239
  .mr-tool-button { flex-shrink: 0; }
13226
- .mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
13240
+ .mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; flex: 0 1 420px; }
13227
13241
  .mr-tool-detail { margin: 7px 0 0; }
13228
13242
  .mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }
13229
13243
  .mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }
13230
13244
  .mr-tool-log summary { cursor: pointer; }
13231
13245
  .mr-tool-log pre { max-height: 140px; overflow: auto; padding: 8px; border-radius: 7px; background: var(--dsw-alias-markdown-code-block); font-size: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }
13232
- .mr-tool-methods { display: flex; align-items: center; gap: 6px; margin-right: 2px; }
13233
- .mr-tool-methods label { display: flex; align-items: center; gap: 6px; color: var(--mr-muted); font-size: 11px; }
13246
+ .mr-tool-methods { display: flex; align-items: center; gap: 6px; width: 100%; }
13247
+ .mr-tool-methods label { display: flex; align-items: center; justify-content: flex-end; gap: 6px; width: 100%; color: var(--mr-muted); font-size: 11px; }
13248
+ .mr-tool-methods .mr-mini-select { min-width: 0; max-width: calc(100% - 54px); }
13234
13249
  .mr-install-settings { margin-bottom: 14px; padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }
13235
13250
  .mr-install-settings-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
13236
13251
  .mr-install-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
@@ -13239,6 +13254,7 @@ var router_main_default = `.mr-workspace {
13239
13254
  .mr-install-field small { font-size: 11px; line-height: 1.6; }
13240
13255
  .mr-install-error { color: #d95360; }
13241
13256
  .mr-install-toggle-row { display: flex; align-items: center; gap: 7px; }
13257
+ .mr-install-save-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
13242
13258
  .mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
13243
13259
  .mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }
13244
13260
  .mr-pill-warn { border-color: var(--dsw-alias-warning, #c27c0e); color: var(--dsw-alias-warning, #c27c0e); }
@@ -13251,7 +13267,7 @@ var router_main_default = `.mr-workspace {
13251
13267
  .mr-profile-advanced summary { cursor: pointer; }
13252
13268
  .mr-profile-success { padding: 10px 12px; border-radius: 10px; background: #e5f5eb; color: #175f3a; font-size: 12px; }
13253
13269
  @media (prefers-color-scheme: dark) { .mr-profile-success { background: #193b2b; color: #b4f1cc; } }
13254
- @media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }
13270
+ @media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-actions { flex-basis: auto; justify-content: flex-start; } .mr-tool-methods label { justify-content: flex-start; } .mr-tool-button { align-self: flex-start; } }
13255
13271
 
13256
13272
  /* \u5F00\u7BB1\u4F53\u68C0\u3001\u6210\u672C\u63A7\u5236\u3001\u6267\u884C\u8BB0\u5F55\u3001\u5B89\u5168\u8FB9\u754C */
13257
13273
  .mr-onboarding, .mr-cost { margin-bottom: 18px; }
@@ -13465,41 +13481,24 @@ var INSTALL_SETTING_FIELDS = [
13465
13481
  label: "\u7EDF\u4E00\u5B89\u88C5\u76EE\u5F55",
13466
13482
  fallback: "",
13467
13483
  placeholder: "\u7559\u7A7A = \u5404\u5382\u5546\u9ED8\u8BA4\u76EE\u5F55",
13468
- hint: "npm / pnpm \u4F1A\u52A0 --prefix\u3002\u652F\u6301\u76EE\u5F55\u7684\u5B98\u65B9\u811A\u672C\u628A\u8FD9\u4E2A\u8DEF\u5F84\u4F20\u7ED9\u5382\u5546\u81EA\u5DF1\u7684\u5B89\u88C5\u76EE\u5F55\u53D8\u91CF\uFF1B\u5199\u6B7B\u8DEF\u5F84\u7684\u811A\u672C\u4F1A\u6807\u660E\u5B9E\u9645\u843D\u70B9\u3002\u66F4\u65B0\u4E5F\u4F7F\u7528\u8FD9\u91CC\uFF0C\u4E0D\u4F1A\u6539\u53BB\u522B\u7684\u9ED8\u8BA4\u4F4D\u7F6E\u3002",
13469
- validate: () => null
13484
+ hint: "\u586B\u5199\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 D:/AI/tools\u3002\u5404\u65B9\u5F0F\u7684\u5B9E\u9645\u76EE\u5F55\u4F1A\u663E\u793A\u5728\u5DE5\u5177\u547D\u4EE4\u65C1\uFF1B\u4E0D\u652F\u6301\u7EDF\u4E00\u76EE\u5F55\u7684\u65B9\u5F0F\u4F1A\u8BF4\u660E\u539F\u56E0\u3002"
13470
13485
  },
13471
13486
  {
13472
13487
  key: "toolNpmRegistry",
13473
13488
  label: "npm \u6E90\u5730\u5740",
13474
13489
  fallback: "https://registry.npmjs.org/",
13475
13490
  placeholder: "https://registry.npmjs.org/",
13476
- hint: "\u5FC5\u987B\u4EE5 https:// \u5F00\u5934\u3002\u6362\u6210\u56FD\u5185\u955C\u50CF\u540E\uFF0C\u6240\u6709\u5305\u7BA1\u7406\u5668\u65B9\u5F0F\u90FD\u4ECE\u8FD9\u91CC\u4E0B\u8F7D\u3002",
13477
- validate: (raw) => /^https:\/\/\S+$/i.test(raw.trim()) ? null : "\u9700\u8981 https \u5F00\u5934\u7684\u5B8C\u6574\u5730\u5740\uFF0C\u4F8B\u5982 https://registry.npmmirror.com/"
13491
+ hint: "\u5FC5\u987B\u4EE5 https:// \u5F00\u5934\u3002\u6240\u6709 npm / pnpm \u65B9\u5F0F\u5171\u7528\u6B64\u4E0B\u8F7D\u6E90\uFF0C\u4F8B\u5982 https://registry.npmmirror.com/\u3002"
13478
13492
  },
13479
13493
  {
13480
13494
  key: "toolScriptUrlsJson",
13481
13495
  label: "\u5B89\u88C5\u811A\u672C\u6E90\u8986\u76D6\uFF08JSON\uFF0C\u53EF\u9009\uFF09",
13482
13496
  fallback: "{}",
13483
13497
  placeholder: '{ "stepcode": "https://mirror/stepcode/install.ps1" }',
13484
- hint: "\u6309\u5DE5\u5177 ID \u6307\u5B9A\u5382\u5546\u5B89\u88C5\u811A\u672C\u7684\u955C\u50CF\u5730\u5740\u3002\u811A\u672C\u4ECD\u4F1A\u4E0B\u8F7D\u540E\u6821\u9A8C\u5382\u5546\u6807\u8BB0\u518D\u6267\u884C\uFF0C\u955C\u50CF\u4E0D\u80FD\u6362\u6210\u522B\u7684\u7A0B\u5E8F\u3002",
13485
- validate: (raw) => {
13486
- if (!raw.trim()) return null;
13487
- let parsed;
13488
- try {
13489
- parsed = JSON.parse(raw);
13490
- } catch {
13491
- return "JSON \u683C\u5F0F\u65E0\u6548\u3002";
13492
- }
13493
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return "\u9700\u8981 JSON \u5BF9\u8C61\u3002";
13494
- for (const [id2, url] of Object.entries(parsed)) {
13495
- if (!OFFICIAL_TOOLS.some((tool) => tool.id === id2)) return `${id2} \u4E0D\u662F\u5B98\u65B9\u5DE5\u5177 ID\u3002`;
13496
- if (typeof url !== "string" || !/^https:\/\/\S+$/i.test(url.trim())) return `${id2} \u7684\u5730\u5740\u5FC5\u987B\u662F https \u94FE\u63A5\u3002`;
13497
- }
13498
- return null;
13499
- }
13498
+ hint: "\u6309\u5DE5\u5177 ID \u6307\u5B9A https \u955C\u50CF\u5730\u5740\u3002\u811A\u672C\u4E0B\u8F7D\u540E\u4ECD\u4F1A\u6838\u5BF9\u5382\u5546\u6807\u8BB0\uFF0C\u518D\u6267\u884C\u672C\u5730\u526F\u672C\u3002"
13500
13499
  }
13501
13500
  ];
13502
- function InstallSettings({ settingsScope, install }) {
13501
+ function InstallSettings({ settingsScope, install, onPendingChange }) {
13503
13502
  const [snapshot, setSnapshot] = import_react5.default.useState(() => settingsScope?.getSnapshot?.() ?? { value: {}, writable: false });
13504
13503
  const [drafts, setDrafts] = import_react5.default.useState({});
13505
13504
  const [saving, setSaving] = import_react5.default.useState(false);
@@ -13507,36 +13506,48 @@ function InstallSettings({ settingsScope, install }) {
13507
13506
  import_react5.default.useEffect(() => {
13508
13507
  if (typeof settingsScope?.subscribe !== "function") return void 0;
13509
13508
  const unsubscribe = settingsScope.subscribe(() => {
13510
- setSnapshot(settingsScope.getSnapshot());
13511
- setDrafts({});
13509
+ const next = settingsScope.getSnapshot();
13510
+ setSnapshot(next);
13511
+ setDrafts((previous) => retainToolInstallDrafts(previous, next.value));
13512
13512
  });
13513
13513
  setSnapshot(settingsScope.getSnapshot());
13514
13514
  return unsubscribe;
13515
13515
  }, [settingsScope]);
13516
+ const dirty = Object.keys(drafts).length > 0;
13517
+ import_react5.default.useEffect(() => {
13518
+ onPendingChange?.(saving || dirty);
13519
+ }, [saving, dirty, onPendingChange]);
13516
13520
  if (!settingsScope) return null;
13517
13521
  const writable = snapshot.writable === true && !saving;
13518
13522
  const shown = (field2) => drafts[field2.key] ?? String(snapshot.value?.[field2.key] ?? field2.fallback);
13519
- const errors = Object.fromEntries(INSTALL_SETTING_FIELDS.map((field2) => [field2.key, field2.validate(shown(field2))]));
13520
- const apply2 = async (entries, successText) => {
13523
+ const errors = Object.fromEntries(INSTALL_SETTING_FIELDS.map((field2) => [field2.key, validateToolInstallSetting(field2.key, shown(field2))]));
13524
+ const invalid = Object.values(errors).some(Boolean);
13525
+ const edit = (key, value) => {
13526
+ const next = retainToolInstallDrafts({ ...drafts, [key]: value }, snapshot.value);
13527
+ onPendingChange?.(saving || Object.keys(next).length > 0);
13528
+ setNotice("");
13529
+ setDrafts(next);
13530
+ };
13531
+ const apply2 = async () => {
13532
+ if (!writable || !dirty || invalid) return;
13521
13533
  setSaving(true);
13534
+ onPendingChange?.(true);
13522
13535
  setNotice("");
13523
13536
  try {
13537
+ const entries = Object.entries(drafts).map(([key, value]) => ({ op: "set", path: [key], value }));
13524
13538
  const accepted = await settingsScope.mutate(entries, snapshot.revision);
13525
13539
  if (!accepted) throw new Error("\u8BBE\u7F6E\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF1B\u8F93\u5165\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9\u3002");
13526
- setNotice(successText);
13540
+ const next = settingsScope.getSnapshot();
13541
+ setSnapshot(next);
13542
+ setDrafts((previous) => retainToolInstallDrafts(previous, next.value));
13543
+ setNotice("\u5B89\u88C5\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002");
13527
13544
  } catch (error) {
13528
13545
  setNotice(text5(error?.message) || "\u5B89\u88C5\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
13529
13546
  } finally {
13530
13547
  setSaving(false);
13531
13548
  }
13532
13549
  };
13533
- const commit = (field2) => {
13534
- const saved = String(snapshot.value?.[field2.key] ?? field2.fallback);
13535
- const raw = shown(field2);
13536
- if (raw === saved || errors[field2.key]) return;
13537
- return apply2([{ op: "set", path: [field2.key], value: raw }], `\u5DF2\u4FDD\u5B58${field2.label}\uFF0C\u4E0B\u4E00\u6B21\u5B89\u88C5\u8D77\u751F\u6548\u3002`);
13538
- };
13539
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-settings", "aria-label": "\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-settings-head" }, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, "\u5BF9\u6240\u6709\u5B98\u65B9\u5DE5\u5177\u751F\u6548\uFF0C\u6539\u52A8\u540E\u4E0B\u4E00\u6B21\u4E00\u952E\u5B89\u88C5\u3001\u4FEE\u590D\u6216\u5378\u8F7D\u7ACB\u5373\u4F7F\u7528\u3002\u63D2\u4EF6\u8BBE\u7F6E\u9875\u91CC\u6709\u540C\u6837\u7684\u5B57\u6BB5\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-grid" }, INSTALL_SETTING_FIELDS.map((field2) => /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-install-field", key: field2.key }, /* @__PURE__ */ import_react5.default.createElement("span", null, field2.label), /* @__PURE__ */ import_react5.default.createElement(
13550
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-settings", "aria-label": "\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-settings-head" }, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, "\u4FDD\u5B58\u540E\u7528\u4E8E\u540E\u7EED\u5B89\u88C5\u4E0E\u66F4\u65B0\u3002\u6BCF\u4E2A\u5DE5\u5177\u4E0B\u65B9\u663E\u793A\u5B9E\u9645\u65B9\u5F0F\u548C\u76EE\u5F55\uFF0C\u63D2\u4EF6\u8BBE\u7F6E\u9875\u91CC\u4E5F\u80FD\u4FEE\u6539\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-grid" }, INSTALL_SETTING_FIELDS.map((field2) => /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-install-field", key: field2.key }, /* @__PURE__ */ import_react5.default.createElement("span", null, field2.label), /* @__PURE__ */ import_react5.default.createElement(
13540
13551
  "input",
13541
13552
  {
13542
13553
  className: "mr-input",
@@ -13544,37 +13555,47 @@ function InstallSettings({ settingsScope, install }) {
13544
13555
  placeholder: field2.placeholder,
13545
13556
  disabled: !writable,
13546
13557
  "aria-invalid": Boolean(errors[field2.key]),
13547
- onChange: (event) => setDrafts((previous) => ({ ...previous, [field2.key]: event.target.value })),
13548
- onBlur: () => {
13549
- void commit(field2);
13550
- }
13558
+ onChange: (event) => edit(field2.key, event.target.value)
13551
13559
  }
13552
13560
  ), errors[field2.key] ? /* @__PURE__ */ import_react5.default.createElement("small", { className: "mr-install-error", role: "alert" }, errors[field2.key]) : /* @__PURE__ */ import_react5.default.createElement("small", null, field2.hint))), /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-install-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, "\u5B89\u88C5\u811A\u672C\u65B9\u5F0F"), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-install-toggle-row" }, /* @__PURE__ */ import_react5.default.createElement(
13553
13561
  "input",
13554
13562
  {
13555
13563
  type: "checkbox",
13556
- checked: snapshot.value?.toolAllowScriptInstall !== false,
13564
+ checked: (drafts.toolAllowScriptInstall ?? snapshot.value?.toolAllowScriptInstall) !== false,
13557
13565
  disabled: !writable,
13558
13566
  onChange: (event) => {
13559
- const allowed = event.target.checked;
13560
- void apply2(
13561
- [{ op: "set", path: ["toolAllowScriptInstall"], value: allowed }],
13562
- allowed ? "\u5DF2\u5141\u8BB8\u811A\u672C\u5B89\u88C5\u65B9\u5F0F\uFF08curl / irm\uFF09\u3002" : "\u5DF2\u5173\u95ED\u811A\u672C\u5B89\u88C5\u65B9\u5F0F\uFF0C\u53EA\u80FD\u4F7F\u7528 npm / pnpm\u3002"
13563
- );
13567
+ edit("toolAllowScriptInstall", event.target.checked);
13564
13568
  }
13565
13569
  }
13566
- ), /* @__PURE__ */ import_react5.default.createElement("span", null, "\u5141\u8BB8 curl / irm \u5B89\u88C5\u811A\u672C")), /* @__PURE__ */ import_react5.default.createElement("small", null, "\u5173\u95ED\u540E\u53EA\u80FD\u4F7F\u7528 npm / pnpm \u65B9\u5F0F\uFF1B\u811A\u672C\u65B9\u5F0F\u9700\u8981\u5148\u4E0B\u8F7D\u5E76\u6821\u9A8C\u5382\u5546\u6807\u8BB0\u518D\u6267\u884C\u3002"))), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 8 }, role: notice ? "status" : void 0 }, "\u5F53\u524D\u751F\u6548\uFF1A\u5B89\u88C5\u76EE\u5F55 ", install?.installDir || "\uFF08\u5404\u5382\u5546\u9ED8\u8BA4\uFF09", " \xB7 npm \u6E90 ", install?.registry || "\uFF08\u9ED8\u8BA4\uFF09", notice ? ` \xB7 ${notice}` : ""));
13570
+ ), /* @__PURE__ */ import_react5.default.createElement("span", null, "\u5141\u8BB8 curl / irm \u5B89\u88C5\u811A\u672C")), /* @__PURE__ */ import_react5.default.createElement("small", null, "\u5173\u95ED\u540E\u53EA\u80FD\u4F7F\u7528 npm / pnpm \u65B9\u5F0F\uFF1B\u811A\u672C\u65B9\u5F0F\u9700\u8981\u5148\u4E0B\u8F7D\u5E76\u6821\u9A8C\u5382\u5546\u6807\u8BB0\u518D\u6267\u884C\u3002"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-install-save-row" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || !dirty || invalid, onClick: () => {
13571
+ void apply2();
13572
+ } }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5B89\u88C5\u8BBE\u7F6E"), dirty && /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: saving, onClick: () => {
13573
+ setDrafts({});
13574
+ setNotice("");
13575
+ onPendingChange?.(false);
13576
+ } }, "\u653E\u5F03\u66F4\u6539"), dirty && /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, invalid ? "\u8BF7\u4FEE\u6B63\u8F93\u5165\u540E\u4FDD\u5B58\u3002" : "\u66F4\u6539\u5C1A\u672A\u4FDD\u5B58\uFF0C\u5B89\u88C5\u4E0E\u66F4\u65B0\u6682\u4E0D\u53EF\u7528\u3002")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 8 }, role: notice ? "status" : void 0 }, "\u5F53\u524D\u751F\u6548\uFF1A\u5B89\u88C5\u76EE\u5F55 ", install?.installDir || "\uFF08\u5404\u5382\u5546\u9ED8\u8BA4\uFF09", " \xB7 npm \u6E90 ", install?.registry || "\uFF08\u9ED8\u8BA4\uFF09", notice ? ` \xB7 ${notice}` : ""));
13567
13577
  }
13568
13578
  function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOfficialTool, repairOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes, health, onRefreshHealth, settingsScope }) {
13569
13579
  const healthById = Object.fromEntries((health?.tools ?? []).map((item) => [item.id, item]));
13570
13580
  const [probeState, setProbeState] = import_react5.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], install: null, error: "" });
13571
13581
  const [jobs, setJobs] = import_react5.default.useState({});
13572
13582
  const [rowErrors, setRowErrors] = import_react5.default.useState({});
13583
+ const [settingsPending, setSettingsPending] = import_react5.default.useState(false);
13584
+ const [methodSaving, setMethodSaving] = import_react5.default.useState(null);
13585
+ const [previewPending, setPreviewPending] = import_react5.default.useState(false);
13586
+ const [settingsWritable, setSettingsWritable] = import_react5.default.useState(() => settingsScope?.getSnapshot?.()?.writable === true);
13587
+ const settingsPendingRef = import_react5.default.useRef(false);
13588
+ const methodSavingRef = import_react5.default.useRef(null);
13589
+ const previewPendingRef = import_react5.default.useRef(false);
13573
13590
  const mounted = import_react5.default.useRef(false);
13574
13591
  const request = import_react5.default.useRef(0);
13575
13592
  const submitting = import_react5.default.useRef(/* @__PURE__ */ new Set());
13576
13593
  const polling = import_react5.default.useRef(/* @__PURE__ */ new Set());
13577
13594
  const jobsRef = import_react5.default.useRef({});
13595
+ const handleSettingsPending = import_react5.default.useCallback((value) => {
13596
+ settingsPendingRef.current = value;
13597
+ setSettingsPending(value);
13598
+ }, []);
13578
13599
  const rememberJobs = (updater) => {
13579
13600
  setJobs((previous) => {
13580
13601
  const next = typeof updater === "function" ? updater(previous) : updater;
@@ -13599,6 +13620,8 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13599
13620
  const capabilities = Array.isArray(response.value?.executionCapabilities) ? response.value.executionCapabilities : [];
13600
13621
  const readiness = Array.isArray(response.value?.executionReadiness) ? response.value.executionReadiness : [];
13601
13622
  setProbeState({ status: "ready", probes, capabilities, readiness, install: response.value?.install ?? null, error: "" });
13623
+ previewPendingRef.current = false;
13624
+ setPreviewPending(false);
13602
13625
  onProbes({ probes, capabilities, readiness, hostVersion: typeof response.value?.hostVersion === "string" ? response.value.hostVersion : null });
13603
13626
  } catch (error) {
13604
13627
  if (!mounted.current || current !== request.current) return;
@@ -13643,6 +13666,29 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13643
13666
  request.current += 1;
13644
13667
  };
13645
13668
  }, []);
13669
+ const refreshRef = import_react5.default.useRef(refresh);
13670
+ refreshRef.current = refresh;
13671
+ import_react5.default.useEffect(() => {
13672
+ if (typeof settingsScope?.subscribe !== "function") return void 0;
13673
+ const signature2 = (snapshot) => JSON.stringify([
13674
+ snapshot?.value?.toolInstallDir,
13675
+ snapshot?.value?.toolNpmRegistry,
13676
+ snapshot?.value?.toolInstallMethodsJson,
13677
+ snapshot?.value?.toolScriptUrlsJson,
13678
+ snapshot?.value?.toolAllowScriptInstall
13679
+ ]);
13680
+ let previous = signature2(settingsScope.getSnapshot());
13681
+ return settingsScope.subscribe(() => {
13682
+ const snapshot = settingsScope.getSnapshot();
13683
+ setSettingsWritable(snapshot.writable === true);
13684
+ const next = signature2(snapshot);
13685
+ if (next === previous) return;
13686
+ previous = next;
13687
+ previewPendingRef.current = true;
13688
+ setPreviewPending(true);
13689
+ void refreshRef.current();
13690
+ });
13691
+ }, [settingsScope]);
13646
13692
  import_react5.default.useEffect(() => {
13647
13693
  const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
13648
13694
  if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
@@ -13683,18 +13729,24 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13683
13729
  }, [jobs, officialToolInstallStatus]);
13684
13730
  const run = async (id2, operation) => {
13685
13731
  const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
13732
+ const summary = (probeState.install?.tools ?? []).find((item) => item.id === id2);
13686
13733
  const bridge = { install: installOfficialTool, repair: repairOfficialTool, uninstall: uninstallOfficialTool }[operation];
13687
13734
  const refusal = installClickRefusal({
13688
13735
  tool,
13689
13736
  submitting: submitting.current.has(id2),
13690
13737
  running: jobsRef.current[id2]?.status === "running",
13691
- operation
13738
+ operation,
13739
+ pendingSettings: settingsPendingRef.current || Boolean(methodSavingRef.current) || previewPendingRef.current,
13740
+ installError: operation === "uninstall" ? summary?.uninstallError ?? (summary?.uninstallCommand ? "" : summary?.error ?? "") : summary?.installError ?? summary?.error ?? ""
13692
13741
  });
13693
13742
  if (refusal) {
13694
13743
  setRowErrors((previous) => ({ ...previous, [id2]: refusal }));
13695
13744
  return;
13696
13745
  }
13697
- if (typeof bridge !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
13746
+ if (typeof bridge !== "function") {
13747
+ setRowErrors((previous) => ({ ...previous, [id2]: "\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002" }));
13748
+ return;
13749
+ }
13698
13750
  submitting.current.add(id2);
13699
13751
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
13700
13752
  setRowErrors((previous) => ({ ...previous, [id2]: "" }));
@@ -13731,7 +13783,10 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13731
13783
  const uninstall = (id2) => run(id2, "uninstall");
13732
13784
  const setInstallMethod = async (toolId, methodId) => {
13733
13785
  const snapshot = settingsScope?.getSnapshot?.();
13734
- if (!snapshot) return;
13786
+ if (!snapshot?.writable || methodSavingRef.current || settingsPendingRef.current) return;
13787
+ methodSavingRef.current = toolId;
13788
+ setMethodSaving(toolId);
13789
+ setRowErrors((previous) => ({ ...previous, [toolId]: "" }));
13735
13790
  let map = {};
13736
13791
  try {
13737
13792
  map = JSON.parse(String(snapshot.value?.toolInstallMethodsJson ?? "{}")) || {};
@@ -13744,9 +13799,14 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13744
13799
  try {
13745
13800
  const accepted = await settingsScope.mutate([{ op: "set", path: ["toolInstallMethodsJson"], value: JSON.stringify(map) }], snapshot.revision);
13746
13801
  if (!accepted) throw new Error("\u5B89\u88C5\u65B9\u5F0F\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002");
13747
- void refresh();
13802
+ previewPendingRef.current = true;
13803
+ setPreviewPending(true);
13804
+ await refresh();
13748
13805
  } catch (error) {
13749
13806
  if (mounted.current) setRowErrors((previous) => ({ ...previous, [toolId]: text5(error?.message) || "\u5B89\u88C5\u65B9\u5F0F\u4FDD\u5B58\u5931\u8D25\u3002" }));
13807
+ } finally {
13808
+ methodSavingRef.current = null;
13809
+ if (mounted.current) setMethodSaving(null);
13750
13810
  }
13751
13811
  };
13752
13812
  const cancel = async (id2) => {
@@ -13768,10 +13828,12 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13768
13828
  const installById = Object.fromEntries((probeState.install?.tools ?? []).map((item) => [item.id, item]));
13769
13829
  return /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card", "aria-label": "\u5B98\u65B9\u5DE5\u5177" }, /* @__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" }, "\u5B98\u65B9\u5DE5\u5177 \xB7 \u4F53\u68C0"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, "\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u7684\u5B89\u88C5\u3001\u7248\u672C\u548C\u767B\u5F55\u72B6\u6001\uFF0C\u5E76\u4ECE\u56FA\u5B9A\u6CE8\u518C\u8868\u4E00\u952E\u5B89\u88C5\u6216\u66F4\u65B0\u5230\u5404\u5382\u5546\u6700\u65B0\u7248\uFF08\u65B0\u7248\u672C\u672A\u7ECF\u63D2\u4EF6\u6D4B\u8BD5\uFF09\u3002\u53EF\u6309\u5DE5\u5177\u9009\u62E9 npm\u3001pnpm \u6216\u5382\u5546\u5B89\u88C5\u811A\u672C\u65B9\u5F0F\uFF0C\u5E76\u7EDF\u4E00\u8BBE\u7F6E\u5B89\u88C5\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\uFF1B\u5378\u8F7D\u53EA\u5220\u9664\u7A0B\u5E8F\uFF0C\u914D\u7F6E\u4E0E\u767B\u5F55\u4FDD\u7559\u3002\u672A\u767B\u5F55\u7684\u5DE5\u5177\u70B9\u201C\u53BB\u767B\u5F55\u201D\u67E5\u770B\u767B\u5F55\u547D\u4EE4\u3002ZCode \u4F1A\u6253\u5F00\u5B98\u65B9\u5B89\u88C5\u7A97\u53E3\u4F9B\u4F60\u9009\u62E9\u76EE\u5F55\uFF1B\u5B8C\u6210\u540E\u91CD\u65B0\u4F53\u68C0\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: probeState.status === "loading", onClick: () => {
13770
13830
  void refresh();
13771
- } }, "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react5.default.createElement(InstallSettings, { settingsScope, install: probeState.install }), probeState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
13831
+ } }, "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react5.default.createElement(InstallSettings, { settingsScope, install: probeState.install, onPendingChange: handleSettingsPending }), previewPending && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", role: "status" }, "\u5B89\u88C5\u8BBE\u7F6E\u5DF2\u53D8\u66F4\uFF0C\u6B63\u5728\u5237\u65B0\u5B9E\u9645\u5B89\u88C5\u547D\u4EE4\uFF1B\u5237\u65B0\u5B8C\u6210\u540E\u53EF\u7EE7\u7EED\u5B89\u88C5\u4E0E\u66F4\u65B0\u3002"), probeState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
13772
13832
  const summary = installById[tool.id];
13773
- const command = summary?.command ?? installCommandLine(tool);
13833
+ const command = summary ? summary.command : installCommandLine(tool);
13834
+ const updateCommand = summary?.updateCommand ?? command;
13774
13835
  const methods = summary?.methods ?? installMethodsFor(tool).map((method) => ({ id: method.id, label: method.label }));
13836
+ const settingsBusy = settingsPending || Boolean(methodSaving) || previewPending;
13775
13837
  const probe = byId[tool.id];
13776
13838
  const capability = capabilitiesById[tool.id];
13777
13839
  const readiness = readinessById[tool.id];
@@ -13783,32 +13845,36 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOf
13783
13845
  probe,
13784
13846
  job,
13785
13847
  probeStatus: probeState.status,
13786
- summary: { installable: !summary?.error, removable: !summary?.error }
13848
+ summary: {
13849
+ installable: !(summary?.installError ?? summary?.error),
13850
+ removable: !(summary?.uninstallError ?? (summary?.uninstallCommand ? "" : summary?.error))
13851
+ }
13787
13852
  });
13788
13853
  const verified = job?.status === "succeeded" && (job.operation === "uninstall" ? job.postInstallProbe?.installed === false : job.postInstallProbe?.installed === true);
13789
13854
  const operation = job?.operation === "uninstall" ? "\u5378\u8F7D" : job?.operation === "repair" ? "\u4FEE\u590D" : "\u5B89\u88C5";
13790
13855
  const status = running ? job.cancelRequested ? `\u6B63\u5728\u53D6\u6D88${operation}\u2026` : `${operation}\u4E2D\u2026` : job?.status === "installer-opened" ? "\u5B98\u65B9\u5B89\u88C5\u5668\u5DF2\u6253\u5F00\uFF0C\u8BF7\u5B8C\u6210\u5B89\u88C5\u540E\u91CD\u65B0\u68C0\u6D4B" : job?.status === "cancelled" ? `${operation}\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B` : verified ? `${operation}\u6210\u529F\u5E76\u9A8C\u8BC1` : probeLabel(probe);
13791
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, probe?.installed && probe.version ? ` \xB7 ${probe.version}` : "", healthById[tool.id]?.latestVersion ? ` \xB7 \u6700\u65B0 ${healthById[tool.id].latestVersion}` : ""), /* @__PURE__ */ import_react5.default.createElement(ToolLoginLine, { entry: healthById[tool.id] }), probe?.installed && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, tool.headlessAdapter ? "\u5DF2\u53EF\u7531 model_router_execute \u4EE5\u65E0\u754C\u9762\u65B9\u5F0F\u8C03\u7528\u3002\u547D\u4EE4\u7F3A\u5931\u6216\u5931\u8D25\u65F6\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002\u7B7E\u540D\u6C99\u7BB1\u5165\u53E3\u4E0D\u542F\u52A8\u6B64 CLI\u3002" : readiness?.ready ? `\u5B98\u65B9\u6267\u884C\u5165\u53E3\u5DF2\u6838\u9A8C\uFF0C\u53EF\u5728\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_tool_run\uFF1B${capability?.modes?.includes("read-only") ? "\u652F\u6301\u53EA\u8BFB\u548C\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u4EFB\u52A1" : "\u4EC5\u652F\u6301\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u9694\u79BB\u5DE5\u4F5C\u533A\u4EFB\u52A1"}\uFF0C\u8D26\u53F7\u53CA\u6A21\u578B\u4ECD\u9700\u5B9E\u6D4B\u3002` : `\u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u4E0D\u53EF\u6258\u7BA1\u6267\u884C\uFF1A${readiness?.reason || capability?.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u6838\u9A8C\u3002"}`), command ? /* @__PURE__ */ import_react5.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, summary?.error ?? tool.unsupportedReason), (summary?.notices ?? []).map((notice) => /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail", key: notice }, notice)), probe?.detail && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error mr-tool-error", role: "alert" }, rowErrors[tool.id] || job.error), Array.isArray(job?.outputTail) && job.outputTail.length > 0 && /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react5.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-methods" }, /* @__PURE__ */ import_react5.default.createElement("label", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "\u5B89\u88C5\u65B9\u5F0F"), /* @__PURE__ */ import_react5.default.createElement(
13856
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, probe?.installed && probe.version ? ` \xB7 ${probe.version}` : "", healthById[tool.id]?.latestVersion ? ` \xB7 \u6700\u65B0 ${healthById[tool.id].latestVersion}` : ""), /* @__PURE__ */ import_react5.default.createElement(ToolLoginLine, { entry: healthById[tool.id] }), probe?.installed && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, tool.headlessAdapter ? "\u5DF2\u53EF\u7531 model_router_execute \u4EE5\u65E0\u754C\u9762\u65B9\u5F0F\u8C03\u7528\u3002\u547D\u4EE4\u7F3A\u5931\u6216\u5931\u8D25\u65F6\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002\u7B7E\u540D\u6C99\u7BB1\u5165\u53E3\u4E0D\u542F\u52A8\u6B64 CLI\u3002" : readiness?.ready ? `\u5B98\u65B9\u6267\u884C\u5165\u53E3\u5DF2\u6838\u9A8C\uFF0C\u53EF\u5728\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_tool_run\uFF1B${capability?.modes?.includes("read-only") ? "\u652F\u6301\u53EA\u8BFB\u548C\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u4EFB\u52A1" : "\u4EC5\u652F\u6301\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u9694\u79BB\u5DE5\u4F5C\u533A\u4EFB\u52A1"}\uFF0C\u8D26\u53F7\u53CA\u6A21\u578B\u4ECD\u9700\u5B9E\u6D4B\u3002` : `\u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u4E0D\u53EF\u6258\u7BA1\u6267\u884C\uFF1A${readiness?.reason || capability?.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u6838\u9A8C\u3002"}`), command ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, "\u5B89\u88C5\u65B9\u5F0F\uFF1A", methods.find((method) => method.id === summary?.methodId)?.label ?? tool.manager, " \xB7 \u5B9E\u9645\u5B89\u88C5\u76EE\u5F55\uFF1A", summary?.installDir || "\u5404\u5382\u5546\u9ED8\u8BA4\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("code", { className: "mr-tool-command", "aria-label": `${tool.label} \u5B89\u88C5\u547D\u4EE4` }, command), updateCommand !== command && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, "\u66F4\u65B0\u6CBF\u7528\u539F\u5B89\u88C5\u4F4D\u7F6E\uFF1A", summary?.updateInstallDir || "\u5404\u5382\u5546\u9ED8\u8BA4\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("code", { className: "mr-tool-command", "aria-label": `${tool.label} \u66F4\u65B0\u547D\u4EE4` }, updateCommand))) : /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, summary?.error ?? tool.unsupportedReason), (summary?.notices ?? []).map((notice) => /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail", key: notice }, notice)), probe?.detail && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error mr-tool-error", role: "alert" }, rowErrors[tool.id] || job.error), Array.isArray(job?.outputTail) && job.outputTail.length > 0 && /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react5.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), (command || methods.length > 0) && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-actions" }, methods.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-tool-methods" }, /* @__PURE__ */ import_react5.default.createElement("label", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "\u5B89\u88C5\u65B9\u5F0F"), /* @__PURE__ */ import_react5.default.createElement(
13792
13857
  "select",
13793
13858
  {
13794
13859
  className: "mr-mini-select",
13795
- value: summary?.savedMethodId ?? summary?.methodId ?? "",
13796
- disabled: running || methods.length === 0,
13860
+ value: summary?.savedMethodId ?? "",
13861
+ disabled: running || settingsBusy || !settingsWritable,
13797
13862
  onChange: (event) => {
13798
13863
  void setInstallMethod(tool.id, event.target.value);
13799
13864
  }
13800
13865
  },
13866
+ /* @__PURE__ */ import_react5.default.createElement("option", { value: "" }, "\u81EA\u52A8\u9009\u62E9"),
13801
13867
  methods.map((method) => /* @__PURE__ */ import_react5.default.createElement("option", { key: method.id, value: method.id, disabled: Boolean(method.blocked) }, method.label, method.blocked ? `\uFF08${method.blocked}\uFF09` : ""))
13802
- ))), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled, onClick: () => {
13868
+ ))), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled || settingsBusy || !command || Boolean(summary?.error), title: summary?.error || (settingsBusy ? "\u8BF7\u5148\u4FDD\u5B58\u6216\u653E\u5F03\u5B89\u88C5\u8BBE\u7F6E\u66F4\u6539\uFF0C\u5E76\u7B49\u5F85\u547D\u4EE4\u5237\u65B0\u3002" : void 0), onClick: () => {
13803
13869
  void install(tool.id);
13804
- } }, action.label), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: maintenance.repair.disabled, title: maintenance.repair.title, onClick: () => {
13870
+ } }, methodSaving === tool.id ? "\u4FDD\u5B58\u65B9\u5F0F\u4E2D\u2026" : action.label), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: maintenance.repair.disabled || settingsBusy, title: maintenance.repair.title, onClick: () => {
13805
13871
  void repair(tool.id);
13806
- } }, maintenance.repair.label), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: maintenance.uninstall.disabled, title: maintenance.uninstall.title, onClick: () => {
13872
+ } }, maintenance.repair.label), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: maintenance.uninstall.disabled || settingsBusy, title: maintenance.uninstall.title, onClick: () => {
13807
13873
  void uninstall(tool.id);
13808
13874
  } }, maintenance.uninstall.label), running && /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
13809
13875
  void cancel(tool.id);
13810
13876
  } }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : `\u53D6\u6D88${operation}`)));
13811
- })), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 12 } }, "\u5B89\u88C5\u7531 Host \u6309\u6CE8\u518C\u8868\u56FA\u5B9A\u6765\u6E90\u6267\u884C\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\uFF1B\u5B89\u88C5\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\u6765\u81EA\u4F60\u81EA\u5DF1\u586B\u5199\u7684\u8BBE\u7F6E\uFF0C\u547D\u4EE4\u4E0A\u65B9\u5B9E\u65F6\u663E\u793A\u5C06\u8981\u6267\u884C\u7684\u5185\u5BB9\u3002\u53EF\u70B9\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u5B9E\u9645\u7248\u672C\u3002\u4E00\u952E\u5378\u8F7D\u53EA\u5220\u9664\u7A0B\u5E8F\u672C\u8EAB\uFF08\u811A\u672C\u5B89\u88C5\u4F1A\u987A\u5E26\u6E05\u6389\u5B83\u5728 shell \u914D\u7F6E\u91CC\u5199\u7684 PATH \u8BB0\u5F55\uFF09\uFF0C\u914D\u7F6E\u3001\u767B\u5F55\u4FE1\u606F\u548C\u5386\u53F2\u8BB0\u5F55\u4FDD\u7559\u3002ZCode \u5B89\u88C5\u5668\u542F\u52A8\u540E\u4ECD\u9700\u5728\u539F\u5382\u7A97\u53E3\u9009\u62E9\u76EE\u5F55\u5E76\u5B8C\u6210\u5B89\u88C5\u3002Agent \u4E5F\u53EF\u8C03\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_install"), " / ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_repair"), " / ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_uninstall"), "\uFF0C\u6216\u5728\u4F1A\u8BDD\u4F7F\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "/tools"), "\u3002")));
13877
+ })), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 12 } }, "\u5B89\u88C5\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\u4F7F\u7528\u5DF2\u4FDD\u5B58\u8BBE\u7F6E\uFF0C\u547D\u4EE4\u4E0A\u65B9\u663E\u793A\u5B9E\u9645\u6267\u884C\u65B9\u5F0F\u3002\u53EF\u70B9\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u7248\u672C\u3002\u5378\u8F7D\u4FDD\u7559\u914D\u7F6E\u3001\u767B\u5F55\u4FE1\u606F\u548C\u5386\u53F2\u8BB0\u5F55\uFF1B\u811A\u672C\u65B9\u5F0F\u53EA\u79FB\u9664\u767B\u8BB0\u7684\u547D\u4EE4\u5165\u53E3\uFF0C\u53EF\u80FD\u4FDD\u7559\u5382\u5546\u7248\u672C\u76EE\u5F55\u3002\u4E0D\u652F\u6301\u5B89\u5168\u5378\u8F7D\u7684\u65B9\u5F0F\u4F1A\u8BF4\u660E\u539F\u56E0\u3002ZCode \u9700\u5728\u5B98\u65B9\u5B89\u88C5\u7A97\u53E3\u5B8C\u6210\u5B89\u88C5\u3002\u4F1A\u8BDD\u4E0E Agent \u4E5F\u53EF\u4F7F\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "/tools"), "\u3001", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_install"), "\u3001", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_repair"), " \u548C ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_tool_uninstall"), "\u3002")));
13812
13878
  }
13813
13879
  var HEADLESS_TOOLS = /* @__PURE__ */ new Set(["claude-code", "codex", "gemini"]);
13814
13880
  var WORKSPACE_VIEWS = [
@@ -14670,12 +14736,12 @@ function RouterSettingsCard(props) {
14670
14736
  ), state.quotaPatternsJson.invalid && /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.profileError, role: "alert" }, "\u9700\u8981 JSON \u5BF9\u8C61\uFF0C\u4E14\u6BCF\u6761\u89C4\u5219\u90FD\u662F\u6709\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F\u3002"), /* @__PURE__ */ import_react6.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react6.default.createElement("summary", null, "\u67E5\u770B\u89C4\u5219\u683C\u5F0F"), /* @__PURE__ */ import_react6.default.createElement("pre", null, `{
14671
14737
  "kimi-code": { "quota": ["\u989D\u5EA6\u5DF2\u7528\u5B8C"], "rateLimit": ["\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41"] },
14672
14738
  "my-glm-plan": { "quota": ["Usage limit reached for", "1308"] }
14673
- }`)), /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("quotaPatternsJson") }, "\u6062\u590D\u9ED8\u8BA4\u89C4\u5219")), /* @__PURE__ */ import_react6.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react6.default.createElement("span", { style: styles.profileLabel }, "\u5B98\u65B9\u5DE5\u5177\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E"), /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.noticeText }, "\u5BF9\u6240\u6709\u5B98\u65B9\u5DE5\u5177\u7684\u4E00\u952E\u5B89\u88C5\u3001\u4FEE\u590D\u548C\u5378\u8F7D\u751F\u6548\u3002\u5DE5\u4F5C\u53F0\u7684\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u9875\u4E5F\u80FD\u6539\u540C\u6837\u7684\u5B57\u6BB5\uFF0C\u5E76\u4F1A\u5B9E\u65F6\u663E\u793A\u6BCF\u4E2A\u5DE5\u5177\u5C06\u8981\u6267\u884C\u7684\u547D\u4EE4\u3002 \u5B89\u88C5\u547D\u4EE4\u59CB\u7EC8\u6765\u81EA\u56FA\u5B9A\u6CE8\u518C\u8868\uFF0C\u8FD9\u91CC\u53EA\u51B3\u5B9A\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\u6216\u4EFB\u610F\u5730\u5740\u3002"), /* @__PURE__ */ import_react6.default.createElement(
14739
+ }`)), /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("quotaPatternsJson") }, "\u6062\u590D\u9ED8\u8BA4\u89C4\u5219")), /* @__PURE__ */ import_react6.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react6.default.createElement("span", { style: styles.profileLabel }, "\u5B98\u65B9\u5DE5\u5177\u7EDF\u4E00\u5B89\u88C5\u8BBE\u7F6E"), /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.noticeText }, "\u7528\u4E8E\u5B98\u65B9\u5DE5\u5177\u7684\u5B89\u88C5\u4E0E\u66F4\u65B0\u3002\u5DE5\u4F5C\u53F0\u7684\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u9875\u4E5F\u80FD\u4FDD\u5B58\u540C\u6837\u7684\u5B57\u6BB5\uFF0C\u5E76\u4F1A\u663E\u793A\u6BCF\u4E2A\u5DE5\u5177\u7684\u5B9E\u9645\u5B89\u88C5\u65B9\u5F0F\u3001\u76EE\u5F55\u4E0E\u6267\u884C\u547D\u4EE4\u3002 \u5B89\u88C5\u547D\u4EE4\u59CB\u7EC8\u6765\u81EA\u56FA\u5B9A\u6CE8\u518C\u8868\uFF0C\u8FD9\u91CC\u53EA\u51B3\u5B9A\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\u6216\u4EFB\u610F\u5730\u5740\u3002"), /* @__PURE__ */ import_react6.default.createElement(
14674
14740
  import_dsh_client_ui_primitives.SettingsValueField,
14675
14741
  {
14676
14742
  id: "model-router-tool-install-dir",
14677
14743
  label: "\u7EDF\u4E00\u5B89\u88C5\u76EE\u5F55",
14678
- hint: "\u7559\u7A7A\u8868\u793A\u4F7F\u7528\u5404\u5382\u5546\u9ED8\u8BA4\u4F4D\u7F6E\u3002npm \u4E0E pnpm \u4F1A\u5E26\u4E0A --prefix\uFF1B\u652F\u6301\u76EE\u5F55\u7684\u5B98\u65B9\u811A\u672C\u4F1A\u628A\u8FD9\u4E2A\u8DEF\u5F84\u4F20\u7ED9\u5382\u5546\u81EA\u5DF1\u7684\u5B89\u88C5\u76EE\u5F55\u53D8\u91CF\uFF08\u4F8B\u5982 MCODE_INSTALL_DIR\uFF09\u3002\u5199\u6B7B\u8DEF\u5F84\u7684\u811A\u672C\u4F1A\u5728\u5DE5\u5177\u884C\u6807\u660E\u5B9E\u9645\u843D\u70B9\u3002\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002\u66F4\u65B0\u4F7F\u7528\u540C\u4E00\u8DEF\u5F84\u3002",
14744
+ hint: "\u586B\u5199\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u7559\u7A7A\u4F7F\u7528\u5382\u5546\u9ED8\u8BA4\u4F4D\u7F6E\u3002npm / pnpm \u4F7F\u7528\u8BE5\u76EE\u5F55\u5B89\u88C5\uFF1B\u5382\u5546\u811A\u672C\u4E0D\u652F\u6301\u81EA\u5B9A\u4E49\u76EE\u5F55\u65F6\uFF0C\u5B98\u65B9\u5DE5\u5177\u9875\u4F1A\u663E\u793A\u5B9E\u9645\u76EE\u5F55\u4E0E\u539F\u56E0\u3002\u66F4\u65B0\u4F7F\u7528\u540C\u4E00\u5B89\u88C5\u65B9\u5F0F\u3001\u76EE\u5F55\u4E0E\u4E0B\u8F7D\u6E90\u3002",
14679
14745
  disabled,
14680
14746
  ...state.toolInstallDir,
14681
14747
  overriddenLabel: FIELD_COPY.overridden,
@@ -14711,7 +14777,7 @@ function RouterSettingsCard(props) {
14711
14777
  {
14712
14778
  id: "model-router-tool-install-methods",
14713
14779
  label: "\u9ED8\u8BA4\u5B89\u88C5\u65B9\u5F0F\uFF08JSON\uFF0C\u53EF\u9009\uFF09",
14714
- hint: "\u6309\u5DE5\u5177 ID \u56FA\u5B9A\u4E00\u79CD\u5B89\u88C5\u65B9\u5F0F\uFF0C\u4F8B\u5982\u56FA\u5B9A Step Code \u5728 Windows \u4E0A\u7528 irm | iex\u3002\u7559\u7A7A\u8868\u793A\u4F7F\u7528\u8BE5\u5DE5\u5177\u7684\u9ED8\u8BA4\u65B9\u5F0F\u3002\u53EF\u9009\u503C\uFF1Anpm\u3001pnpm\u3001script-bash\uFF08curl | bash\uFF09\u3001script-powershell\uFF08irm | iex\uFF09\u3002",
14780
+ hint: "\u6309\u5DE5\u5177 ID \u56FA\u5B9A\u4E00\u79CD\u5B89\u88C5\u65B9\u5F0F\uFF0C\u4F8B\u5982\u56FA\u5B9A Step Code \u5728 Windows \u4E0A\u7528 irm | iex\u3002\u7559\u7A7A\u81EA\u52A8\u9009\u62E9\u9002\u5408\u672C\u673A\u7684\u65B9\u5F0F\u3002\u53EF\u9009\u503C\u4EE5\u5DE5\u5177\u4E0B\u62C9\u6846\u4E3A\u51C6\uFF1Anpm\u3001pnpm\u3001script-bash\uFF08curl | bash\uFF09\u3001script-sh\uFF08curl | sh\uFF09\u3001script-powershell\uFF08irm | iex\uFF09\u3002",
14715
14781
  disabled,
14716
14782
  ...state.toolInstallMethodsJson,
14717
14783
  overriddenLabel: FIELD_COPY.overridden,