@ljwei-stak/dsh-model-router 0.16.3 → 0.17.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.
- package/.dsh-plugin/client.js +737 -99
- package/.dsh-plugin/index.mjs +133 -27
- package/.dsh-plugin/official-tools-remote-service.mjs +82 -10
- package/.dsh-plugin/shared/latest-versions.mjs +19 -0
- package/.dsh-plugin/shared/official-tool-executor.mjs +8 -0
- package/.dsh-plugin/shared/official-tool-registry.mjs +223 -12
- package/.dsh-plugin/shared/official-tools-remote.mjs +38 -2
- package/.dsh-plugin/shared/official-tools-runtime.mjs +467 -42
- package/.dsh-plugin/shared/tool-health.mjs +7 -0
- package/.dsh-plugin/shared/tool-install-preferences.mjs +294 -0
- package/CHANGELOG.md +23 -0
- package/INSTALLATION_GUIDE.zh.md +12 -12
- package/README.md +18 -6
- package/README.zh.md +18 -6
- package/docs/WORKBENCH_USER_GUIDE.zh.md +35 -4
- package/package.json +3 -2
package/.dsh-plugin/client.js
CHANGED
|
@@ -46,6 +46,40 @@ module.exports = __toCommonJS(official_harness_exports);
|
|
|
46
46
|
var import_react6 = __toESM(require("react"), 1);
|
|
47
47
|
|
|
48
48
|
// .dsh-plugin/shared/official-tool-registry.mjs
|
|
49
|
+
var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
50
|
+
function packageManagerMethod(id2, label, manager, spec, { registryFlag = "--registry=" } = {}) {
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
id: id2,
|
|
53
|
+
label,
|
|
54
|
+
kind: "package-manager",
|
|
55
|
+
manager,
|
|
56
|
+
spec,
|
|
57
|
+
registryFlag,
|
|
58
|
+
installArgs: Object.freeze(["install", "-g", spec]),
|
|
59
|
+
uninstallArgs: Object.freeze(["uninstall", "-g", spec.replace(/@latest$/, "")]),
|
|
60
|
+
/** npm/pnpm both accept --prefix to place a global install off the default. */
|
|
61
|
+
supportsInstallDir: true,
|
|
62
|
+
supportsRegistry: true
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function scriptMethod(id2, label, { shell, scriptUrl, verify, installArg = [], env = [], platforms, supportsInstallDir = false }) {
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
id: id2,
|
|
68
|
+
label,
|
|
69
|
+
kind: "script",
|
|
70
|
+
shell,
|
|
71
|
+
scriptUrl,
|
|
72
|
+
verify,
|
|
73
|
+
/** Passed after the staged script path; empty means the script takes no argument. */
|
|
74
|
+
installArgs: Object.freeze(installArg),
|
|
75
|
+
/** Environment the vendor script itself documents for this install. */
|
|
76
|
+
env: Object.freeze(env),
|
|
77
|
+
platforms: Object.freeze(platforms),
|
|
78
|
+
/** Whether the vendor script honours a caller-chosen directory at all. */
|
|
79
|
+
supportsInstallDir,
|
|
80
|
+
supportsRegistry: false
|
|
81
|
+
});
|
|
82
|
+
}
|
|
49
83
|
var OFFICIAL_TOOLS = Object.freeze([
|
|
50
84
|
Object.freeze({
|
|
51
85
|
id: "kimi-code",
|
|
@@ -55,9 +89,14 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
55
89
|
package: "@moonshot-ai/kimi-code",
|
|
56
90
|
manager: "npm",
|
|
57
91
|
installArgs: ["install", "-g", "@moonshot-ai/kimi-code@latest", "--registry=https://registry.npmjs.org/"],
|
|
92
|
+
uninstallArgs: ["uninstall", "-g", "@moonshot-ai/kimi-code"],
|
|
58
93
|
probeExecutables: ["kimi"],
|
|
59
94
|
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",
|
|
60
|
-
providerHints: ["moonshot", "kimi"]
|
|
95
|
+
providerHints: ["moonshot", "kimi"],
|
|
96
|
+
installMethods: Object.freeze([
|
|
97
|
+
packageManagerMethod("npm", "npm", "npm", "@moonshot-ai/kimi-code@latest"),
|
|
98
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@moonshot-ai/kimi-code@latest")
|
|
99
|
+
])
|
|
61
100
|
}),
|
|
62
101
|
Object.freeze({
|
|
63
102
|
id: "claude-code",
|
|
@@ -67,8 +106,13 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
67
106
|
package: "@anthropic-ai/claude-code",
|
|
68
107
|
manager: "npm",
|
|
69
108
|
installArgs: ["install", "-g", "@anthropic-ai/claude-code@latest", "--registry=https://registry.npmjs.org/"],
|
|
109
|
+
uninstallArgs: ["uninstall", "-g", "@anthropic-ai/claude-code"],
|
|
70
110
|
probeExecutables: ["claude"],
|
|
71
|
-
providerHints: ["anthropic", "claude"]
|
|
111
|
+
providerHints: ["anthropic", "claude"],
|
|
112
|
+
installMethods: Object.freeze([
|
|
113
|
+
packageManagerMethod("npm", "npm", "npm", "@anthropic-ai/claude-code@latest"),
|
|
114
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@anthropic-ai/claude-code@latest")
|
|
115
|
+
])
|
|
72
116
|
}),
|
|
73
117
|
Object.freeze({
|
|
74
118
|
id: "codex",
|
|
@@ -76,11 +120,16 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
76
120
|
vendor: "OpenAI",
|
|
77
121
|
purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
|
|
78
122
|
package: "@openai/codex",
|
|
79
|
-
installArgs: ["install", "-g", "@openai/codex@latest", "--registry=https://registry.npmjs.org/"],
|
|
80
123
|
manager: "npm",
|
|
124
|
+
installArgs: ["install", "-g", "@openai/codex@latest", "--registry=https://registry.npmjs.org/"],
|
|
125
|
+
uninstallArgs: ["uninstall", "-g", "@openai/codex"],
|
|
81
126
|
probeExecutables: ["codex"],
|
|
82
127
|
probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u53D6 npm \u6700\u65B0\u7248\u3002",
|
|
83
|
-
providerHints: ["openai", "gpt", "codex"]
|
|
128
|
+
providerHints: ["openai", "gpt", "codex"],
|
|
129
|
+
installMethods: Object.freeze([
|
|
130
|
+
packageManagerMethod("npm", "npm", "npm", "@openai/codex@latest"),
|
|
131
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@openai/codex@latest")
|
|
132
|
+
])
|
|
84
133
|
}),
|
|
85
134
|
Object.freeze({
|
|
86
135
|
id: "minimax-code",
|
|
@@ -90,8 +139,13 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
90
139
|
package: "@minimax-ai/code",
|
|
91
140
|
manager: "npm",
|
|
92
141
|
installArgs: ["install", "-g", "@minimax-ai/code@latest", "--registry=https://registry.npmjs.org/", "--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"],
|
|
142
|
+
uninstallArgs: ["uninstall", "-g", "@minimax-ai/code"],
|
|
93
143
|
probeExecutables: ["mcode"],
|
|
94
|
-
providerHints: ["minimax"]
|
|
144
|
+
providerHints: ["minimax"],
|
|
145
|
+
installMethods: Object.freeze([
|
|
146
|
+
packageManagerMethod("npm", "npm", "npm", "@minimax-ai/code@latest"),
|
|
147
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@minimax-ai/code@latest")
|
|
148
|
+
])
|
|
95
149
|
}),
|
|
96
150
|
Object.freeze({
|
|
97
151
|
id: "mimo-code",
|
|
@@ -101,8 +155,13 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
101
155
|
package: "@mimo-ai/cli",
|
|
102
156
|
manager: "npm",
|
|
103
157
|
installArgs: ["install", "-g", "@mimo-ai/cli@latest", "--registry=https://registry.npmjs.org/"],
|
|
158
|
+
uninstallArgs: ["uninstall", "-g", "@mimo-ai/cli"],
|
|
104
159
|
probeExecutables: ["mimo"],
|
|
105
|
-
providerHints: ["mimo", "xiaomi"]
|
|
160
|
+
providerHints: ["mimo", "xiaomi"],
|
|
161
|
+
installMethods: Object.freeze([
|
|
162
|
+
packageManagerMethod("npm", "npm", "npm", "@mimo-ai/cli@latest"),
|
|
163
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@mimo-ai/cli@latest")
|
|
164
|
+
])
|
|
106
165
|
}),
|
|
107
166
|
Object.freeze({
|
|
108
167
|
id: "grok-build",
|
|
@@ -112,8 +171,13 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
112
171
|
package: "@xai-official/grok",
|
|
113
172
|
manager: "npm",
|
|
114
173
|
installArgs: ["install", "-g", "@xai-official/grok@latest", "--registry=https://registry.npmjs.org/"],
|
|
174
|
+
uninstallArgs: ["uninstall", "-g", "@xai-official/grok"],
|
|
115
175
|
probeExecutables: ["grok"],
|
|
116
|
-
providerHints: ["xai", "grok"]
|
|
176
|
+
providerHints: ["xai", "grok"],
|
|
177
|
+
installMethods: Object.freeze([
|
|
178
|
+
packageManagerMethod("npm", "npm", "npm", "@xai-official/grok@latest"),
|
|
179
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@xai-official/grok@latest")
|
|
180
|
+
])
|
|
117
181
|
}),
|
|
118
182
|
Object.freeze({
|
|
119
183
|
id: "gemini",
|
|
@@ -123,11 +187,93 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
123
187
|
package: "@google/gemini-cli",
|
|
124
188
|
manager: "npm",
|
|
125
189
|
installArgs: ["install", "-g", "@google/gemini-cli@latest", "--registry=https://registry.npmjs.org/"],
|
|
190
|
+
uninstallArgs: ["uninstall", "-g", "@google/gemini-cli"],
|
|
126
191
|
probeExecutables: ["gemini"],
|
|
127
192
|
providerHints: ["gemini", "google"],
|
|
128
193
|
// Headless runs go through the task adapter. The signed sandbox runner does
|
|
129
194
|
// not launch this CLI; a missing or failed process falls back to the API.
|
|
130
|
-
headlessAdapter: true
|
|
195
|
+
headlessAdapter: true,
|
|
196
|
+
installMethods: Object.freeze([
|
|
197
|
+
packageManagerMethod("npm", "npm", "npm", "@google/gemini-cli@latest"),
|
|
198
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "@google/gemini-cli@latest")
|
|
199
|
+
])
|
|
200
|
+
}),
|
|
201
|
+
Object.freeze({
|
|
202
|
+
id: "opencode",
|
|
203
|
+
label: "OpenCode",
|
|
204
|
+
vendor: "anomalyco / SST",
|
|
205
|
+
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",
|
|
206
|
+
package: "opencode-ai",
|
|
207
|
+
manager: "npm",
|
|
208
|
+
installArgs: ["install", "-g", "opencode-ai@latest", "--registry=https://registry.npmjs.org/"],
|
|
209
|
+
uninstallArgs: ["uninstall", "-g", "opencode-ai"],
|
|
210
|
+
probeExecutables: ["opencode"],
|
|
211
|
+
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",
|
|
212
|
+
providerHints: ["opencode", "anomalyco"],
|
|
213
|
+
/** File an uninstall may delete, when the copy came from a script install. */
|
|
214
|
+
scriptBinaryNames: Object.freeze(["opencode", "opencode.exe"]),
|
|
215
|
+
/** Where the vendor script puts the binary when the user sets no directory. */
|
|
216
|
+
defaultScriptInstallDir: ".opencode/bin",
|
|
217
|
+
installMethods: Object.freeze([
|
|
218
|
+
packageManagerMethod("npm", "npm", "npm", "opencode-ai@latest"),
|
|
219
|
+
packageManagerMethod("pnpm", "pnpm", "pnpm", "opencode-ai@latest"),
|
|
220
|
+
// The published script hardcodes $HOME/.opencode/bin, so it cannot honour
|
|
221
|
+
// a custom install directory; the npm/pnpm methods above can.
|
|
222
|
+
scriptMethod("script-bash", "curl | bash", {
|
|
223
|
+
shell: "bash",
|
|
224
|
+
scriptUrl: "https://opencode.ai/install",
|
|
225
|
+
platforms: ["linux", "darwin"],
|
|
226
|
+
// The published script hardcodes $HOME/.opencode/bin, so it cannot honour
|
|
227
|
+
// a custom install directory; the npm/pnpm methods above can.
|
|
228
|
+
supportsInstallDir: false,
|
|
229
|
+
verify: Object.freeze({
|
|
230
|
+
mustInclude: ["anomalyco/opencode", "opencode.ai"],
|
|
231
|
+
mustNotInclude: ["static-openapi.stepfun.com"]
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
])
|
|
235
|
+
}),
|
|
236
|
+
Object.freeze({
|
|
237
|
+
id: "stepcode",
|
|
238
|
+
label: "Step Code",
|
|
239
|
+
vendor: "StepFun \u9636\u8DC3\u661F\u8FB0",
|
|
240
|
+
purpose: "\u9636\u8DC3\u661F\u8FB0\u5F00\u6E90\u7EC8\u7AEF\u7F16\u7A0B\u4EE3\u7406\uFF0C\u63D0\u4F9B step \u547D\u4EE4\uFF1B\u5B98\u65B9\u53EA\u53D1\u5E03\u5B89\u88C5\u811A\u672C\uFF0C\u6CA1\u6709 npm \u5305\u3002",
|
|
241
|
+
manager: "script-installer",
|
|
242
|
+
installArgs: [],
|
|
243
|
+
uninstallArgs: [],
|
|
244
|
+
probeExecutables: ["step"],
|
|
245
|
+
probeNote: "step \u547D\u4EE4\u6765\u81EA\u9636\u8DC3\u5B98\u65B9\u5B89\u88C5\u811A\u672C\uFF0C\u6821\u9A8C\u548C\u5199\u5165 ~/.stepcode\uFF1B\u914D\u7F6E\u4E0E\u51ED\u636E\u540C\u5728\u8BE5\u76EE\u5F55\uFF0C\u5378\u8F7D\u53EA\u5220\u9664\u53EF\u6267\u884C\u6587\u4EF6\u3002",
|
|
246
|
+
providerHints: ["stepfun", "stepcode"],
|
|
247
|
+
scriptBinaryNames: Object.freeze(["step", "step.exe"]),
|
|
248
|
+
defaultScriptInstallDir: ".stepcode/bin",
|
|
249
|
+
/** The vendor installer writes this marked block into the shell profile. */
|
|
250
|
+
pathProfileMarkers: Object.freeze(["# stepcode"]),
|
|
251
|
+
installMethods: Object.freeze([
|
|
252
|
+
scriptMethod("script-bash", "curl | bash", {
|
|
253
|
+
shell: "bash",
|
|
254
|
+
scriptUrl: "https://static-openapi.stepfun.com/stepcode/install.sh",
|
|
255
|
+
platforms: ["linux", "darwin"],
|
|
256
|
+
installArg: ["--install-dir"],
|
|
257
|
+
env: ["STEP_INSTALL_DIR", "STEP_RELEASE_BASE_URL"],
|
|
258
|
+
supportsInstallDir: true,
|
|
259
|
+
verify: Object.freeze({
|
|
260
|
+
mustInclude: ["static-openapi.stepfun.com", "stepcode installer"],
|
|
261
|
+
mustNotInclude: ["anomalyco/opencode"]
|
|
262
|
+
})
|
|
263
|
+
}),
|
|
264
|
+
scriptMethod("script-powershell", "irm | iex", {
|
|
265
|
+
shell: "powershell",
|
|
266
|
+
scriptUrl: "https://static-openapi.stepfun.com/stepcode/install.ps1",
|
|
267
|
+
platforms: ["win32"],
|
|
268
|
+
installArg: ["-InstallDir"],
|
|
269
|
+
env: ["STEP_INSTALL_DIR", "STEP_RELEASE_BASE_URL"],
|
|
270
|
+
supportsInstallDir: true,
|
|
271
|
+
verify: Object.freeze({
|
|
272
|
+
mustInclude: ["static-openapi.stepfun.com", "stepcode"],
|
|
273
|
+
mustNotInclude: ["anomalyco/opencode"]
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
])
|
|
131
277
|
}),
|
|
132
278
|
Object.freeze({
|
|
133
279
|
id: "zcode",
|
|
@@ -136,6 +282,7 @@ var OFFICIAL_TOOLS = Object.freeze([
|
|
|
136
282
|
purpose: "\u667A\u8C31\u5B98\u65B9 ZCode \u684C\u9762\u7248\uFF0C\u5185\u542B GLM \u7F16\u7A0B\u4EE3\u7406\u3002Windows \u5B89\u88C5\u5668\u53EF\u9009\u62E9 D \u76D8\u76EE\u5F55\u3002",
|
|
137
283
|
manager: "signed-windows-installer",
|
|
138
284
|
installArgs: [],
|
|
285
|
+
uninstallArgs: [],
|
|
139
286
|
probeExecutables: [],
|
|
140
287
|
probeNote: "\u68C0\u6D4B\u7ECF\u8FC7\u6709\u6548\u7B7E\u540D\u7684 ZCode.exe \u548C\u540C\u76EE\u5F55 GLM \u8D44\u6E90\uFF1B\u684C\u9762\u5B89\u88C5\u5668\u9700\u4EBA\u5DE5\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E\u3002",
|
|
141
288
|
providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu", "bigmodel"]
|
|
@@ -153,10 +300,30 @@ function toolForProvider(provider) {
|
|
|
153
300
|
}
|
|
154
301
|
return null;
|
|
155
302
|
}
|
|
303
|
+
function installMethodsFor(tool) {
|
|
304
|
+
return Array.isArray(tool?.installMethods) ? tool.installMethods : [];
|
|
305
|
+
}
|
|
306
|
+
function installMethodFor(tool, methodId) {
|
|
307
|
+
const wanted = String(methodId ?? "").trim();
|
|
308
|
+
if (!wanted) return null;
|
|
309
|
+
return installMethodsFor(tool).find((method) => method.id === wanted) ?? null;
|
|
310
|
+
}
|
|
311
|
+
function defaultInstallMethod(tool) {
|
|
312
|
+
return installMethodsFor(tool)[0] ?? null;
|
|
313
|
+
}
|
|
156
314
|
function installCommandLine(tool) {
|
|
157
315
|
if (!tool || tool.unsupported) return null;
|
|
158
316
|
if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
|
|
159
|
-
return tool
|
|
317
|
+
return installCommandLineFor(tool, defaultInstallMethod(tool));
|
|
318
|
+
}
|
|
319
|
+
function installCommandLineFor(tool, method) {
|
|
320
|
+
if (!tool || tool.unsupported) return null;
|
|
321
|
+
if (!method) return null;
|
|
322
|
+
if (method.kind === "script") {
|
|
323
|
+
const piped = method.shell === "powershell" ? `irm ${method.scriptUrl} | iex` : `curl -fsSL ${method.scriptUrl} | bash`;
|
|
324
|
+
return method.installArgs.length ? `${piped} ${method.installArgs[0]} <\u5B89\u88C5\u76EE\u5F55>` : piped;
|
|
325
|
+
}
|
|
326
|
+
return `${method.manager} ${method.installArgs.join(" ")}`;
|
|
160
327
|
}
|
|
161
328
|
|
|
162
329
|
// .dsh-plugin/shared/model-profiles.mjs
|
|
@@ -411,6 +578,112 @@ function toolBoundary(toolId, { sandboxed = false, platform = "unknown" } = {})
|
|
|
411
578
|
};
|
|
412
579
|
}
|
|
413
580
|
|
|
581
|
+
// .dsh-plugin/shared/tool-install-preferences.mjs
|
|
582
|
+
var MAX_INSTALL_DIR_CHARS = 4096;
|
|
583
|
+
var MAX_SOURCE_URL_CHARS = 2048;
|
|
584
|
+
var MAX_OVERRIDES = 64;
|
|
585
|
+
function isAbsolutePath(value) {
|
|
586
|
+
const raw = String(value ?? "");
|
|
587
|
+
if (!raw) return false;
|
|
588
|
+
if (raw.startsWith("/")) return true;
|
|
589
|
+
if (/^[A-Za-z]:[\\/]/.test(raw)) return true;
|
|
590
|
+
return /^\\\\[^\\]+\\[^\\]+/.test(raw);
|
|
591
|
+
}
|
|
592
|
+
function joinPath(base, ...segments) {
|
|
593
|
+
const separator = String(base ?? "").includes("\\") && !String(base ?? "").includes("/") ? "\\" : "/";
|
|
594
|
+
const parts = [String(base ?? "").replace(/[\\/]+$/, ""), ...segments.map((item) => String(item ?? "").replace(/^[\\/]+|[\\/]+$/g, ""))];
|
|
595
|
+
return parts.filter(Boolean).join(separator) || separator;
|
|
596
|
+
}
|
|
597
|
+
function expandInstallDir(value, { home = "", env = {} } = {}) {
|
|
598
|
+
const raw = String(value ?? "").trim();
|
|
599
|
+
if (!raw) return "";
|
|
600
|
+
if (raw.includes("\0")) throw new Error("\u5B89\u88C5\u76EE\u5F55\u5305\u542B\u65E0\u6548\u5B57\u7B26\u3002");
|
|
601
|
+
let expanded = raw;
|
|
602
|
+
const userHome = String(home ?? "");
|
|
603
|
+
if (expanded === "~") expanded = userHome;
|
|
604
|
+
else if (expanded.startsWith("~/") || expanded.startsWith("~\\")) expanded = joinPath(userHome, expanded.slice(2));
|
|
605
|
+
expanded = expanded.replace(/%([A-Za-z_][A-Za-z0-9_]*)%/g, (match, name) => env[name] ?? match).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, name) => env[name] ?? match);
|
|
606
|
+
if (expanded.length > MAX_INSTALL_DIR_CHARS) throw new Error("\u5B89\u88C5\u76EE\u5F55\u8FC7\u957F\u3002");
|
|
607
|
+
if (!isAbsolutePath(expanded)) throw new Error("\u5B89\u88C5\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
608
|
+
const withoutTrailing = expanded.replace(/[\\/]+$/, "");
|
|
609
|
+
if (!withoutTrailing) throw new Error("\u5B89\u88C5\u76EE\u5F55\u4E0D\u80FD\u662F\u6587\u4EF6\u7CFB\u7EDF\u6839\u76EE\u5F55\u3002");
|
|
610
|
+
if (/^[A-Za-z]:$/.test(withoutTrailing)) throw new Error("\u5B89\u88C5\u76EE\u5F55\u4E0D\u80FD\u662F\u6587\u4EF6\u7CFB\u7EDF\u6839\u76EE\u5F55\u3002");
|
|
611
|
+
if (/^\\\\[^\\]+\\[^\\]+$/.test(withoutTrailing)) throw new Error("\u5B89\u88C5\u76EE\u5F55\u4E0D\u80FD\u662F\u7F51\u7EDC\u5171\u4EAB\u6839\u76EE\u5F55\u3002");
|
|
612
|
+
return expanded;
|
|
613
|
+
}
|
|
614
|
+
function normalizeSourceUrl(value, subject) {
|
|
615
|
+
const raw = String(value ?? "").trim();
|
|
616
|
+
if (!raw) return "";
|
|
617
|
+
if (raw.length > MAX_SOURCE_URL_CHARS) throw new Error(`${subject}\u5730\u5740\u8FC7\u957F\u3002`);
|
|
618
|
+
let url;
|
|
619
|
+
try {
|
|
620
|
+
url = new URL(raw);
|
|
621
|
+
} catch {
|
|
622
|
+
throw new Error(`${subject}\u4E0D\u662F\u6709\u6548\u7F51\u5740\u3002`);
|
|
623
|
+
}
|
|
624
|
+
if (url.protocol !== "https:") throw new Error(`${subject}\u5FC5\u987B\u4F7F\u7528 https\u3002`);
|
|
625
|
+
if (url.username || url.password) throw new Error(`${subject}\u4E0D\u80FD\u5305\u542B\u8D26\u53F7\u5BC6\u7801\u3002`);
|
|
626
|
+
return url.toString();
|
|
627
|
+
}
|
|
628
|
+
function parseJsonObject(value, subject) {
|
|
629
|
+
const raw = String(value ?? "").trim();
|
|
630
|
+
if (!raw) return {};
|
|
631
|
+
let parsed;
|
|
632
|
+
try {
|
|
633
|
+
parsed = JSON.parse(raw);
|
|
634
|
+
} catch {
|
|
635
|
+
throw new Error(`${subject}\u4E0D\u662F\u6709\u6548 JSON\u3002`);
|
|
636
|
+
}
|
|
637
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
638
|
+
throw new Error(`${subject}\u5FC5\u987B\u662F JSON \u5BF9\u8C61\u3002`);
|
|
639
|
+
}
|
|
640
|
+
const entries = Object.entries(parsed);
|
|
641
|
+
if (entries.length > MAX_OVERRIDES) throw new Error(`${subject}\u6761\u76EE\u8FC7\u591A\u3002`);
|
|
642
|
+
return Object.fromEntries(entries.filter(([, item]) => typeof item === "string" && item.trim()));
|
|
643
|
+
}
|
|
644
|
+
function valueOf(config, key, fallback) {
|
|
645
|
+
const value = config?.[key];
|
|
646
|
+
return value !== void 0 && typeof value?.get === "function" ? value.get() : value ?? fallback;
|
|
647
|
+
}
|
|
648
|
+
function keepKnownToolIds(raw, known, subject) {
|
|
649
|
+
for (const key of Object.keys(raw)) {
|
|
650
|
+
if (!known.has(key)) throw new Error(`${subject}\u4E2D\u7684 ${key} \u4E0D\u662F\u5B98\u65B9\u5DE5\u5177 ID\u3002`);
|
|
651
|
+
}
|
|
652
|
+
return raw;
|
|
653
|
+
}
|
|
654
|
+
function parseInstallPreferences(config = {}, { tools = [] } = {}) {
|
|
655
|
+
const known = new Set(tools.map((tool) => tool.id));
|
|
656
|
+
const installDir = expandInstallDir(valueOf(config, "toolInstallDir", ""));
|
|
657
|
+
const registry = normalizeSourceUrl(valueOf(config, "toolNpmRegistry", DEFAULT_NPM_REGISTRY), "npm \u6E90") || DEFAULT_NPM_REGISTRY;
|
|
658
|
+
const methodRaw = keepKnownToolIds(
|
|
659
|
+
parseJsonObject(valueOf(config, "toolInstallMethodsJson", "{}"), "\u5B89\u88C5\u65B9\u5F0F\u8986\u76D6"),
|
|
660
|
+
known,
|
|
661
|
+
"\u5B89\u88C5\u65B9\u5F0F\u8986\u76D6"
|
|
662
|
+
);
|
|
663
|
+
const scriptRaw = keepKnownToolIds(
|
|
664
|
+
parseJsonObject(valueOf(config, "toolScriptUrlsJson", "{}"), "\u5B89\u88C5\u811A\u672C\u6E90\u8986\u76D6"),
|
|
665
|
+
known,
|
|
666
|
+
"\u5B89\u88C5\u811A\u672C\u6E90\u8986\u76D6"
|
|
667
|
+
);
|
|
668
|
+
const methods = {};
|
|
669
|
+
const scriptUrls = {};
|
|
670
|
+
for (const [id2, methodId] of Object.entries(methodRaw)) methods[id2] = methodId.trim();
|
|
671
|
+
for (const [id2, url] of Object.entries(scriptRaw)) scriptUrls[id2] = normalizeSourceUrl(url, `${id2} \u5B89\u88C5\u811A\u672C\u6E90`);
|
|
672
|
+
for (const [id2, methodId] of Object.entries(methods)) {
|
|
673
|
+
if (!installMethodFor({ id: id2, installMethods: tools.find((tool) => tool.id === id2)?.installMethods }, methodId)) {
|
|
674
|
+
throw new Error(`${id2} \u4E0D\u652F\u6301\u5B89\u88C5\u65B9\u5F0F ${methodId}\u3002`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
installDir,
|
|
679
|
+
registry,
|
|
680
|
+
methods,
|
|
681
|
+
scriptUrls,
|
|
682
|
+
/** Script execution stays a deliberate act; a config file alone must not run one. */
|
|
683
|
+
allowScriptInstall: valueOf(config, "toolAllowScriptInstall", true) !== false
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
414
687
|
// .dsh-plugin/client/router-main.jsx
|
|
415
688
|
var import_react5 = __toESM(require("react"), 1);
|
|
416
689
|
|
|
@@ -621,8 +894,8 @@ var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
|
|
|
621
894
|
"opencode-zen",
|
|
622
895
|
"opencode-go-zen"
|
|
623
896
|
]);
|
|
624
|
-
function classifyTask(
|
|
625
|
-
const value = String(
|
|
897
|
+
function classifyTask(text7) {
|
|
898
|
+
const value = String(text7 ?? "");
|
|
626
899
|
if (value.length < 80 && /翻译|解释|translate|explain/i.test(value)) return "general";
|
|
627
900
|
return detectTaskTypes(value)[0] ?? "general";
|
|
628
901
|
}
|
|
@@ -642,8 +915,8 @@ var TASK_TYPE_LABELS = Object.freeze({
|
|
|
642
915
|
summarization: "\u6458\u8981\u4E0E\u6574\u7406",
|
|
643
916
|
writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
|
|
644
917
|
});
|
|
645
|
-
function detectTaskTypes(
|
|
646
|
-
const value = String(
|
|
918
|
+
function detectTaskTypes(text7) {
|
|
919
|
+
const value = String(text7 ?? "");
|
|
647
920
|
const ranked = TASK_TYPE_RULES.map(([type, pattern]) => ({
|
|
648
921
|
type,
|
|
649
922
|
signals: value.match(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`))?.length ?? 0
|
|
@@ -651,8 +924,8 @@ function detectTaskTypes(text6) {
|
|
|
651
924
|
ranked.sort((left, right) => right.signals - left.signals || left.type.localeCompare(right.type));
|
|
652
925
|
return ranked.map((item) => item.type);
|
|
653
926
|
}
|
|
654
|
-
function assessComplexity(
|
|
655
|
-
const value = String(
|
|
927
|
+
function assessComplexity(text7) {
|
|
928
|
+
const value = String(text7 ?? "");
|
|
656
929
|
const lengthScore = clamp(value.length / 2200);
|
|
657
930
|
const requirementScore = clamp((value.match(/(?:^|\n)\s*(?:[-*]|\d+[.)]|[一二三四五六七八九十]+[、.])/g) ?? []).length / 8);
|
|
658
931
|
const codeScore = /(代码|工程|架构|接口|实现|部署|测试|code|api|debug)/i.test(value) ? 0.22 : 0;
|
|
@@ -732,10 +1005,10 @@ function normalizedCacheRatios(cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
|
732
1005
|
return { read, write };
|
|
733
1006
|
}
|
|
734
1007
|
var COST_REFERENCE_PRICING = Object.freeze({ input: 1, output: 5, cacheRead: 1, cacheWrite: 1, currency: "USD" });
|
|
735
|
-
function estimateCost(model,
|
|
1008
|
+
function estimateCost(model, text7, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
736
1009
|
const pricing = pricingFor(model, pricingOverrides);
|
|
737
1010
|
if (pricing === null) return null;
|
|
738
|
-
const inputTokens = Math.max(80, Math.ceil(String(
|
|
1011
|
+
const inputTokens = Math.max(80, Math.ceil(String(text7 ?? "").length / 3.7));
|
|
739
1012
|
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
740
1013
|
const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
|
|
741
1014
|
const cacheWriteTokens = Math.min(inputTokens - cacheReadTokens, Math.max(0, Math.round(inputTokens * ratios.write)));
|
|
@@ -747,8 +1020,8 @@ function modelMetadata(name) {
|
|
|
747
1020
|
if (!key) return null;
|
|
748
1021
|
return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
|
|
749
1022
|
}
|
|
750
|
-
function taskTokenBudget(
|
|
751
|
-
const inputTokens = Math.max(80, Math.ceil(String(
|
|
1023
|
+
function taskTokenBudget(text7, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
1024
|
+
const inputTokens = Math.max(80, Math.ceil(String(text7 ?? "").length / 3.7));
|
|
752
1025
|
const multipliers = {
|
|
753
1026
|
analysis: { input: 0.9, output: 0.55 },
|
|
754
1027
|
execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
|
|
@@ -812,10 +1085,10 @@ function looksLikeRequirement(value) {
|
|
|
812
1085
|
const item = requirementText(value);
|
|
813
1086
|
return item.length >= 3 && !/[::]$/u.test(item) && !/(?:以下|下列|如下)(?:的)?(?:任务|需求|工作|事项|要求)/u.test(item) && REQUIREMENT_ACTION.test(item.slice(0, 32));
|
|
814
1087
|
}
|
|
815
|
-
function explicitRequirements(
|
|
1088
|
+
function explicitRequirements(text7) {
|
|
816
1089
|
const visibleLines = [];
|
|
817
1090
|
let fence = null;
|
|
818
|
-
for (const line of String(
|
|
1091
|
+
for (const line of String(text7 ?? "").split(/\r?\n/u)) {
|
|
819
1092
|
const marker2 = /^\s*(`{3,}|~{3,})/u.exec(line);
|
|
820
1093
|
if (fence) {
|
|
821
1094
|
if (marker2 && marker2[1][0] === fence.char && marker2[1].length >= fence.length && !line.slice(marker2[0].length).trim()) fence = null;
|
|
@@ -888,16 +1161,16 @@ function explicitStepReferences(objective) {
|
|
|
888
1161
|
}
|
|
889
1162
|
return [...found].sort((a, b2) => a - b2);
|
|
890
1163
|
}
|
|
891
|
-
function taskPackages(taskType,
|
|
1164
|
+
function taskPackages(taskType, text7, band) {
|
|
892
1165
|
if (band !== "complex") {
|
|
893
1166
|
const task = { id: "execution", name: "\u76F4\u63A5\u56DE\u7B54\u4E0E\u5FC5\u8981\u6821\u9A8C", type: taskType, purpose: "execution", difficulty: band, criticality: 0.65, dependsOn: [], preferredReasoningEffort: band === "simple" ? "low" : "medium" };
|
|
894
1167
|
return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
|
|
895
1168
|
}
|
|
896
|
-
const value = String(
|
|
1169
|
+
const value = String(text7 ?? "");
|
|
897
1170
|
const packages = [
|
|
898
1171
|
{ id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
|
|
899
1172
|
];
|
|
900
|
-
const requirements = explicitRequirements(
|
|
1173
|
+
const requirements = explicitRequirements(text7);
|
|
901
1174
|
if (requirements.length >= 2) {
|
|
902
1175
|
const groups = requirements.length > MAX_EXPLICIT_EXECUTION_PACKAGES ? [
|
|
903
1176
|
...requirements.slice(0, MAX_EXPLICIT_EXECUTION_PACKAGES - 1).map((item) => [item]),
|
|
@@ -924,7 +1197,7 @@ function taskPackages(taskType, text6, band) {
|
|
|
924
1197
|
});
|
|
925
1198
|
});
|
|
926
1199
|
} else {
|
|
927
|
-
const domains = [...new Set([...detectTaskTypes(
|
|
1200
|
+
const domains = [...new Set([...detectTaskTypes(text7), taskType].filter((type) => type !== "general"))];
|
|
928
1201
|
for (const type of domains.length > 0 ? domains : [taskType]) {
|
|
929
1202
|
const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
|
|
930
1203
|
const difficulty = requirementDifficulty(objective, type);
|
|
@@ -1025,31 +1298,31 @@ function reasoningDecision(row, task) {
|
|
|
1025
1298
|
multiplier: reasoningEffortMultiplier(chosen)
|
|
1026
1299
|
};
|
|
1027
1300
|
}
|
|
1028
|
-
function candidateUtility(row, task, weights, cacheReadRatio = 0, cacheWriteRatio = 0,
|
|
1301
|
+
function candidateUtility(row, task, weights, cacheReadRatio = 0, cacheWriteRatio = 0, text7 = "", complexity = "balanced") {
|
|
1029
1302
|
const quality = qualityForTask(row, task.type);
|
|
1030
1303
|
const rawPreference = row.preferenceAdjustments?.[task.type];
|
|
1031
1304
|
const preferenceAdjustment = Number.isFinite(rawPreference) ? clamp(rawPreference, -0.1, 0.1) : 0;
|
|
1032
1305
|
const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
|
|
1033
1306
|
const qualityGap = Math.max(0, floor - quality);
|
|
1034
1307
|
const reasoning = reasoningDecision(row, task);
|
|
1035
|
-
const reference = taskCost({ pricing: COST_REFERENCE_PRICING, reasoningKnown: true, reasoningEfforts: [] }, task,
|
|
1036
|
-
const cost = row.pricing === null ? 0 : 1 / (1 + taskCost(row, task,
|
|
1308
|
+
const reference = taskCost({ pricing: COST_REFERENCE_PRICING, reasoningKnown: true, reasoningEfforts: [] }, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
|
|
1309
|
+
const cost = row.pricing === null ? 0 : 1 / (1 + taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio) / Math.max(reference, 1e-12));
|
|
1037
1310
|
const score = weights.quality * quality + weights.cost * cost + weights.latency * (1 - clamp(row.latency * reasoning.multiplier.latency)) + weights.specialty * specialtyForTask(row, task.type) + (weights.reasoning ?? 0) * reasoning.reasoningFit - weights.risk * row.risk - qualityGap * (task.criticality ?? 0.75) + preferenceAdjustment;
|
|
1038
1311
|
return { score, floor, qualityGap, preferenceAdjustment, ...reasoning };
|
|
1039
1312
|
}
|
|
1040
|
-
function taskCost(row, task,
|
|
1313
|
+
function taskCost(row, task, text7, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
1041
1314
|
if (row?.pricing === null) return 0;
|
|
1042
1315
|
const decision = row === null ? null : reasoningDecision(row, task);
|
|
1043
|
-
const tokens = taskTokenBudget(
|
|
1316
|
+
const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
|
|
1044
1317
|
return row === null ? 0 : ((tokens.inputTokens - tokens.cacheReadTokens - tokens.cacheWriteTokens) * row.pricing.input + tokens.cacheReadTokens * row.pricing.cacheRead + tokens.cacheWriteTokens * row.pricing.cacheWrite + tokens.outputTokens * row.pricing.output) / 1e6;
|
|
1045
1318
|
}
|
|
1046
|
-
function dominates(left, right, task,
|
|
1319
|
+
function dominates(left, right, task, text7, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
1047
1320
|
if (left.pricing === null || right.pricing === null) return false;
|
|
1048
1321
|
const leftReasoning = reasoningDecision(left, task);
|
|
1049
1322
|
const rightReasoning = reasoningDecision(right, task);
|
|
1050
1323
|
const leftValues = {
|
|
1051
1324
|
quality: qualityForTask(left, task.type),
|
|
1052
|
-
cost: taskCost(left, task,
|
|
1325
|
+
cost: taskCost(left, task, text7, complexity, cacheReadRatio, cacheWriteRatio),
|
|
1053
1326
|
latency: clamp(left.latency * leftReasoning.multiplier.latency),
|
|
1054
1327
|
specialty: specialtyForTask(left, task.type),
|
|
1055
1328
|
reasoning: leftReasoning.reasoningFit,
|
|
@@ -1057,7 +1330,7 @@ function dominates(left, right, task, text6, complexity, cacheReadRatio, cacheWr
|
|
|
1057
1330
|
};
|
|
1058
1331
|
const rightValues = {
|
|
1059
1332
|
quality: qualityForTask(right, task.type),
|
|
1060
|
-
cost: taskCost(right, task,
|
|
1333
|
+
cost: taskCost(right, task, text7, complexity, cacheReadRatio, cacheWriteRatio),
|
|
1061
1334
|
latency: clamp(right.latency * rightReasoning.multiplier.latency),
|
|
1062
1335
|
specialty: specialtyForTask(right, task.type),
|
|
1063
1336
|
reasoning: rightReasoning.reasoningFit,
|
|
@@ -1070,7 +1343,7 @@ function dominates(left, right, task, text6, complexity, cacheReadRatio, cacheWr
|
|
|
1070
1343
|
function eligibleRowsForTask(rows, task) {
|
|
1071
1344
|
return task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
|
|
1072
1345
|
}
|
|
1073
|
-
function candidatePool(rows, task, weights,
|
|
1346
|
+
function candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive = false) {
|
|
1074
1347
|
const eligibleRows = eligibleRowsForTask(rows, task);
|
|
1075
1348
|
const floor = Number(task.qualityFloor ?? 0);
|
|
1076
1349
|
const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
|
|
@@ -1078,10 +1351,10 @@ function candidatePool(rows, task, weights, text6, complexity, cacheReadRatio, c
|
|
|
1078
1351
|
const taskWeights = weightsForTask(weights, task);
|
|
1079
1352
|
const scored = source.map((row) => ({
|
|
1080
1353
|
row,
|
|
1081
|
-
decision: candidateUtility(row, task, taskWeights, cacheReadRatio, cacheWriteRatio,
|
|
1082
|
-
cost: taskCost(row, task,
|
|
1354
|
+
decision: candidateUtility(row, task, taskWeights, cacheReadRatio, cacheWriteRatio, text7, complexity),
|
|
1355
|
+
cost: taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)
|
|
1083
1356
|
}));
|
|
1084
|
-
const frontier = dependencySensitive ? scored : scored.filter((item) => !scored.some((other) => other !== item && other.decision.score >= item.decision.score && dominates(other.row, item.row, task,
|
|
1357
|
+
const frontier = dependencySensitive ? scored : scored.filter((item) => !scored.some((other) => other !== item && other.decision.score >= item.decision.score && dominates(other.row, item.row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)));
|
|
1085
1358
|
const essential = [
|
|
1086
1359
|
scored.filter((item) => item.row.pricing !== null).sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
|
|
1087
1360
|
scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
|
|
@@ -1099,9 +1372,9 @@ function candidatePool(rows, task, weights, text6, complexity, cacheReadRatio, c
|
|
|
1099
1372
|
dependencySensitive
|
|
1100
1373
|
};
|
|
1101
1374
|
}
|
|
1102
|
-
function solveAssignments({ rows, tasks, weights, text:
|
|
1375
|
+
function solveAssignments({ rows, tasks, weights, text: text7, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
|
|
1103
1376
|
const dependencySensitive = tasks.some((task) => (task.dependsOn ?? []).length > 0);
|
|
1104
|
-
const pools = tasks.map((task) => candidatePool(rows, task, weights,
|
|
1377
|
+
const pools = tasks.map((task) => candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive));
|
|
1105
1378
|
const result = solveCandidateAssignments({ tasks, pools: pools.map((pool, index) => pool.options.map((option) => ({
|
|
1106
1379
|
route: routeKey(option.row.provider, option.row.model),
|
|
1107
1380
|
score: option.decision.score,
|
|
@@ -1125,15 +1398,15 @@ function solveAssignments({ rows, tasks, weights, text: text6, complexity, budge
|
|
|
1125
1398
|
minimumFeasibleCost: result.minimumCostLowerBound
|
|
1126
1399
|
};
|
|
1127
1400
|
}
|
|
1128
|
-
function buildPlan({ text:
|
|
1401
|
+
function buildPlan({ text: text7 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", dataVersions = null, learning = null, budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
|
|
1129
1402
|
const presetId = normalizeRoutingPreset(preset);
|
|
1130
|
-
const firstLine = String(
|
|
1403
|
+
const firstLine = String(text7 ?? "").split(/\r?\n/u)[0].trim();
|
|
1131
1404
|
const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
|
|
1132
|
-
const assessed = assessComplexity(transformOnly ? firstLine :
|
|
1133
|
-
const requirements = explicitRequirements(
|
|
1405
|
+
const assessed = assessComplexity(transformOnly ? firstLine : text7);
|
|
1406
|
+
const requirements = explicitRequirements(text7);
|
|
1134
1407
|
const compound = shouldSplitRequirements(requirements);
|
|
1135
1408
|
const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
|
|
1136
|
-
const taskType = classifyTask(transformOnly ? firstLine :
|
|
1409
|
+
const taskType = classifyTask(transformOnly ? firstLine : text7);
|
|
1137
1410
|
const weights = presetWeights(OBJECTIVE_WEIGHTS[complexity.band], presetId);
|
|
1138
1411
|
const discovered = Array.isArray(available) ? available.map((entry) => {
|
|
1139
1412
|
const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
|
|
@@ -1212,10 +1485,10 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1212
1485
|
costOut: pricingRow.output,
|
|
1213
1486
|
cacheRead: pricingRow.cacheRead,
|
|
1214
1487
|
cacheWrite: pricingRow.cacheWrite
|
|
1215
|
-
},
|
|
1488
|
+
}, text7, 900, {}, cacheReadRatio, cacheWriteRatio)
|
|
1216
1489
|
});
|
|
1217
1490
|
}
|
|
1218
|
-
const taskNodes = taskPackages(taskType,
|
|
1491
|
+
const taskNodes = taskPackages(taskType, text7, complexity.band).map((task) => presetId === DEFAULT_ROUTING_PRESET ? task : {
|
|
1219
1492
|
...task,
|
|
1220
1493
|
qualityFloor: presetFloor(task.qualityFloor, presetId),
|
|
1221
1494
|
weights: presetWeights(weightsForTask(weights, task), presetId)
|
|
@@ -1226,7 +1499,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1226
1499
|
rows,
|
|
1227
1500
|
tasks: taskNodes,
|
|
1228
1501
|
weights,
|
|
1229
|
-
text:
|
|
1502
|
+
text: text7,
|
|
1230
1503
|
complexity: complexity.band,
|
|
1231
1504
|
budget: Number.POSITIVE_INFINITY,
|
|
1232
1505
|
cacheReadRatio,
|
|
@@ -1236,7 +1509,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1236
1509
|
rows,
|
|
1237
1510
|
tasks: taskNodes,
|
|
1238
1511
|
weights,
|
|
1239
|
-
text:
|
|
1512
|
+
text: text7,
|
|
1240
1513
|
complexity: complexity.band,
|
|
1241
1514
|
budget,
|
|
1242
1515
|
cacheReadRatio,
|
|
@@ -1246,7 +1519,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1246
1519
|
rows,
|
|
1247
1520
|
tasks: taskNodes,
|
|
1248
1521
|
weights,
|
|
1249
|
-
text:
|
|
1522
|
+
text: text7,
|
|
1250
1523
|
complexity: complexity.band,
|
|
1251
1524
|
budget: Number.POSITIVE_INFINITY,
|
|
1252
1525
|
cacheReadRatio,
|
|
@@ -1258,8 +1531,8 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1258
1531
|
const usedRoutes = optimized?.usedRoutes ?? /* @__PURE__ */ new Set();
|
|
1259
1532
|
const constraintRelaxed = (optimized?.relaxedCount ?? 0) > 0;
|
|
1260
1533
|
for (const row of rows) {
|
|
1261
|
-
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, cacheReadRatio, cacheWriteRatio,
|
|
1262
|
-
if (row.pricing !== null && taskNodes[0]) row.estimatedCost = taskCost(row, taskNodes[0],
|
|
1534
|
+
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, cacheReadRatio, cacheWriteRatio, text7, complexity.band).score;
|
|
1535
|
+
if (row.pricing !== null && taskNodes[0]) row.estimatedCost = taskCost(row, taskNodes[0], text7, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1263
1536
|
}
|
|
1264
1537
|
rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
|
|
1265
1538
|
const selectedAssignment = assignments[0];
|
|
@@ -1285,8 +1558,8 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1285
1558
|
dependsOn: [...task.dependsOn ?? []]
|
|
1286
1559
|
}));
|
|
1287
1560
|
const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
|
|
1288
|
-
const tokens = taskTokenBudget(
|
|
1289
|
-
const taskEstimate = estimatedCost ?? taskCost(row, task,
|
|
1561
|
+
const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1562
|
+
const taskEstimate = estimatedCost ?? taskCost(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1290
1563
|
return {
|
|
1291
1564
|
stage: index + 1,
|
|
1292
1565
|
purpose: task.purpose,
|
|
@@ -1309,12 +1582,12 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1309
1582
|
};
|
|
1310
1583
|
});
|
|
1311
1584
|
const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
|
|
1312
|
-
const totalEstimate = pricingComplete ? assignments.reduce((sum, { task, row, estimatedCost }) => sum + (estimatedCost ?? taskCost(row, task,
|
|
1585
|
+
const totalEstimate = pricingComplete ? assignments.reduce((sum, { task, row, estimatedCost }) => sum + (estimatedCost ?? taskCost(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio)), 0) : null;
|
|
1313
1586
|
const baselineRows = assignments.map(({ task }) => {
|
|
1314
1587
|
const strongest = eligibleRowsForTask(rows, task).reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
1315
1588
|
return { task, strongest };
|
|
1316
1589
|
});
|
|
1317
|
-
const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task,
|
|
1590
|
+
const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
|
|
1318
1591
|
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(row, task.type))) && baselineRows.every(({ strongest, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(strongest, task.type)));
|
|
1319
1592
|
const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
|
|
1320
1593
|
const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
|
|
@@ -1331,7 +1604,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1331
1604
|
taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
|
|
1332
1605
|
objectiveWeights: weights,
|
|
1333
1606
|
candidates: rows.slice(0, 8).map((row) => {
|
|
1334
|
-
const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, cacheReadRatio, cacheWriteRatio,
|
|
1607
|
+
const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, cacheReadRatio, cacheWriteRatio, text7, complexity.band);
|
|
1335
1608
|
return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), preferenceAdjustment: decision.preferenceAdjustment, quality: row.qualitySource === "unknown" ? null : Number(row.quality.toFixed(3)), qualitySource: row.qualitySource, specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: row.estimatedCost === null ? null : Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing?.input ?? null, outputPrice: row.pricing?.output ?? null, pricingSource: row.pricingSource, pricingVersion: row.pricingVersion, pricingAsOf: row.snapshotAsOf };
|
|
1336
1609
|
}),
|
|
1337
1610
|
selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: selected.estimatedCost === null ? null : Number(selected.estimatedCost.toFixed(6)), qualitySource: qualitySourceForTask(selected, selectedAssignment?.task.type ?? taskType), pricingSource: selected.pricingSource },
|
|
@@ -1909,10 +2182,34 @@ function toolInstallAction({ tool, probe, readiness, job, probeStatus, latestVer
|
|
|
1909
2182
|
disabled: !interactive || running || installed && (order === 1 || current)
|
|
1910
2183
|
};
|
|
1911
2184
|
}
|
|
1912
|
-
function
|
|
2185
|
+
function toolMaintenanceActions({ tool, probe, job, probeStatus, summary }) {
|
|
2186
|
+
const running = job?.status === "running";
|
|
2187
|
+
const installed = probe?.installed === true;
|
|
2188
|
+
const interactive = probeStatus === "ready" || probeStatus === "refreshing";
|
|
2189
|
+
const desktop = tool.manager === "signed-windows-installer";
|
|
2190
|
+
const busy = running || !interactive;
|
|
2191
|
+
return {
|
|
2192
|
+
repair: {
|
|
2193
|
+
label: "\u4E00\u952E\u4FEE\u590D",
|
|
2194
|
+
title: "\u7528\u540C\u4E00\u6761\u56FA\u5B9A\u5B98\u65B9\u5B89\u88C5\u547D\u4EE4\u91CD\u65B0\u5B89\u88C5\u4E00\u904D\uFF0C\u4FEE\u590D\u635F\u574F\u6216\u4E0D\u5B8C\u6574\u7684\u5B89\u88C5",
|
|
2195
|
+
disabled: busy || desktop || summary?.installable === false
|
|
2196
|
+
},
|
|
2197
|
+
uninstall: {
|
|
2198
|
+
label: "\u4E00\u952E\u5378\u8F7D",
|
|
2199
|
+
title: "\u53EA\u5220\u9664\u7A0B\u5E8F\u672C\u8EAB\uFF1B\u914D\u7F6E\u3001\u767B\u5F55\u4FE1\u606F\u548C\u5386\u53F2\u8BB0\u5F55\u4FDD\u7559",
|
|
2200
|
+
disabled: busy || desktop || !installed || summary?.removable === false
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
function installClickRefusal({ tool, submitting = false, running = false, operation = "install" } = {}) {
|
|
1913
2205
|
if (!tool) return "\u672A\u77E5\u5B98\u65B9\u5DE5\u5177\uFF0C\u65E0\u6CD5\u5F00\u59CB\u5B89\u88C5\u3002";
|
|
1914
2206
|
if (tool.unsupported) return String(tool.unsupportedReason ?? "").trim() || "\u6B64\u5DE5\u5177\u6682\u4E0D\u652F\u6301\u4E00\u952E\u5B89\u88C5\u3002";
|
|
1915
|
-
if (
|
|
2207
|
+
if (tool.manager === "signed-windows-installer" && operation !== "install") {
|
|
2208
|
+
return `${tool.label} \u7531\u5B98\u65B9\u7B7E\u540D\u684C\u9762\u5B89\u88C5\u5668\u5B89\u88C5\uFF0C\u8BF7\u5728\u7CFB\u7EDF\u201C\u5E94\u7528\u201D\u4E2D\u5378\u8F7D\u5B83\u3002`;
|
|
2209
|
+
}
|
|
2210
|
+
if (submitting || running) {
|
|
2211
|
+
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";
|
|
2212
|
+
}
|
|
1916
2213
|
return "";
|
|
1917
2214
|
}
|
|
1918
2215
|
function payloadError(payload, fallback) {
|
|
@@ -12154,7 +12451,7 @@ var xterm_default = `/**
|
|
|
12154
12451
|
`;
|
|
12155
12452
|
|
|
12156
12453
|
// .dsh-plugin/client/host-version.mjs
|
|
12157
|
-
var ROUTER_CLIENT_VERSION = true ? "0.
|
|
12454
|
+
var ROUTER_CLIENT_VERSION = true ? "0.17.0" : "";
|
|
12158
12455
|
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";
|
|
12159
12456
|
function isMissingRemoteMethod(message) {
|
|
12160
12457
|
const value = String(message ?? "");
|
|
@@ -12752,6 +13049,16 @@ var router_main_default = `.mr-workspace {
|
|
|
12752
13049
|
.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }
|
|
12753
13050
|
.mr-tool-log summary { cursor: pointer; }
|
|
12754
13051
|
.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; }
|
|
13052
|
+
.mr-tool-methods { display: flex; align-items: center; gap: 6px; margin-right: 2px; }
|
|
13053
|
+
.mr-tool-methods label { display: flex; align-items: center; gap: 6px; color: var(--mr-muted); font-size: 11px; }
|
|
13054
|
+
.mr-install-settings { margin-bottom: 14px; padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }
|
|
13055
|
+
.mr-install-settings-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
|
|
13056
|
+
.mr-install-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
|
13057
|
+
.mr-install-field { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--mr-muted); }
|
|
13058
|
+
.mr-install-field > span { font-weight: 600; color: var(--mr-ink); }
|
|
13059
|
+
.mr-install-field small { font-size: 11px; line-height: 1.6; }
|
|
13060
|
+
.mr-install-error { color: #d95360; }
|
|
13061
|
+
.mr-install-toggle-row { display: flex; align-items: center; gap: 7px; }
|
|
12755
13062
|
.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
|
|
12756
13063
|
.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }
|
|
12757
13064
|
.mr-pill-warn { border-color: var(--dsw-alias-warning, #c27c0e); color: var(--dsw-alias-warning, #c27c0e); }
|
|
@@ -12972,9 +13279,115 @@ function probeLabel(probe) {
|
|
|
12972
13279
|
if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
|
|
12973
13280
|
return probe.detail || "\u672A\u5B89\u88C5";
|
|
12974
13281
|
}
|
|
12975
|
-
|
|
13282
|
+
var INSTALL_SETTING_FIELDS = [
|
|
13283
|
+
{
|
|
13284
|
+
key: "toolInstallDir",
|
|
13285
|
+
label: "\u7EDF\u4E00\u5B89\u88C5\u76EE\u5F55",
|
|
13286
|
+
fallback: "",
|
|
13287
|
+
placeholder: "\u7559\u7A7A = \u5404\u5382\u5546\u9ED8\u8BA4\u76EE\u5F55",
|
|
13288
|
+
hint: "npm / pnpm \u65B9\u5F0F\u4F1A\u52A0 --prefix\uFF1B\u811A\u672C\u65B9\u5F0F\u770B\u5382\u5546\u662F\u5426\u652F\u6301\u76EE\u5F55\u53C2\u6570\u3002",
|
|
13289
|
+
validate: () => null
|
|
13290
|
+
},
|
|
13291
|
+
{
|
|
13292
|
+
key: "toolNpmRegistry",
|
|
13293
|
+
label: "npm \u6E90\u5730\u5740",
|
|
13294
|
+
fallback: "https://registry.npmjs.org/",
|
|
13295
|
+
placeholder: "https://registry.npmjs.org/",
|
|
13296
|
+
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",
|
|
13297
|
+
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/"
|
|
13298
|
+
},
|
|
13299
|
+
{
|
|
13300
|
+
key: "toolScriptUrlsJson",
|
|
13301
|
+
label: "\u5B89\u88C5\u811A\u672C\u6E90\u8986\u76D6\uFF08JSON\uFF0C\u53EF\u9009\uFF09",
|
|
13302
|
+
fallback: "{}",
|
|
13303
|
+
placeholder: '{ "stepcode": "https://mirror/stepcode/install.ps1" }',
|
|
13304
|
+
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",
|
|
13305
|
+
validate: (raw) => {
|
|
13306
|
+
if (!raw.trim()) return null;
|
|
13307
|
+
let parsed;
|
|
13308
|
+
try {
|
|
13309
|
+
parsed = JSON.parse(raw);
|
|
13310
|
+
} catch {
|
|
13311
|
+
return "JSON \u683C\u5F0F\u65E0\u6548\u3002";
|
|
13312
|
+
}
|
|
13313
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return "\u9700\u8981 JSON \u5BF9\u8C61\u3002";
|
|
13314
|
+
for (const [id2, url] of Object.entries(parsed)) {
|
|
13315
|
+
if (!OFFICIAL_TOOLS.some((tool) => tool.id === id2)) return `${id2} \u4E0D\u662F\u5B98\u65B9\u5DE5\u5177 ID\u3002`;
|
|
13316
|
+
if (typeof url !== "string" || !/^https:\/\/\S+$/i.test(url.trim())) return `${id2} \u7684\u5730\u5740\u5FC5\u987B\u662F https \u94FE\u63A5\u3002`;
|
|
13317
|
+
}
|
|
13318
|
+
return null;
|
|
13319
|
+
}
|
|
13320
|
+
}
|
|
13321
|
+
];
|
|
13322
|
+
function InstallSettings({ settingsScope, install }) {
|
|
13323
|
+
const [snapshot, setSnapshot] = import_react5.default.useState(() => settingsScope?.getSnapshot?.() ?? { value: {}, writable: false });
|
|
13324
|
+
const [drafts, setDrafts] = import_react5.default.useState({});
|
|
13325
|
+
const [saving, setSaving] = import_react5.default.useState(false);
|
|
13326
|
+
const [notice, setNotice] = import_react5.default.useState("");
|
|
13327
|
+
import_react5.default.useEffect(() => {
|
|
13328
|
+
if (typeof settingsScope?.subscribe !== "function") return void 0;
|
|
13329
|
+
const unsubscribe = settingsScope.subscribe(() => {
|
|
13330
|
+
setSnapshot(settingsScope.getSnapshot());
|
|
13331
|
+
setDrafts({});
|
|
13332
|
+
});
|
|
13333
|
+
setSnapshot(settingsScope.getSnapshot());
|
|
13334
|
+
return unsubscribe;
|
|
13335
|
+
}, [settingsScope]);
|
|
13336
|
+
if (!settingsScope) return null;
|
|
13337
|
+
const writable = snapshot.writable === true && !saving;
|
|
13338
|
+
const shown = (field2) => drafts[field2.key] ?? String(snapshot.value?.[field2.key] ?? field2.fallback);
|
|
13339
|
+
const errors = Object.fromEntries(INSTALL_SETTING_FIELDS.map((field2) => [field2.key, field2.validate(shown(field2))]));
|
|
13340
|
+
const apply2 = async (entries, successText) => {
|
|
13341
|
+
setSaving(true);
|
|
13342
|
+
setNotice("");
|
|
13343
|
+
try {
|
|
13344
|
+
const accepted = await settingsScope.mutate(entries, snapshot.revision);
|
|
13345
|
+
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");
|
|
13346
|
+
setNotice(successText);
|
|
13347
|
+
} catch (error) {
|
|
13348
|
+
setNotice(text5(error?.message) || "\u5B89\u88C5\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
13349
|
+
} finally {
|
|
13350
|
+
setSaving(false);
|
|
13351
|
+
}
|
|
13352
|
+
};
|
|
13353
|
+
const commit = (field2) => {
|
|
13354
|
+
const saved = String(snapshot.value?.[field2.key] ?? field2.fallback);
|
|
13355
|
+
const raw = shown(field2);
|
|
13356
|
+
if (raw === saved || errors[field2.key]) return;
|
|
13357
|
+
return apply2([{ op: "set", path: [field2.key], value: raw }], `\u5DF2\u4FDD\u5B58${field2.label}\uFF0C\u4E0B\u4E00\u6B21\u5B89\u88C5\u8D77\u751F\u6548\u3002`);
|
|
13358
|
+
};
|
|
13359
|
+
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(
|
|
13360
|
+
"input",
|
|
13361
|
+
{
|
|
13362
|
+
className: "mr-input",
|
|
13363
|
+
value: shown(field2),
|
|
13364
|
+
placeholder: field2.placeholder,
|
|
13365
|
+
disabled: !writable,
|
|
13366
|
+
"aria-invalid": Boolean(errors[field2.key]),
|
|
13367
|
+
onChange: (event) => setDrafts((previous) => ({ ...previous, [field2.key]: event.target.value })),
|
|
13368
|
+
onBlur: () => {
|
|
13369
|
+
void commit(field2);
|
|
13370
|
+
}
|
|
13371
|
+
}
|
|
13372
|
+
), 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(
|
|
13373
|
+
"input",
|
|
13374
|
+
{
|
|
13375
|
+
type: "checkbox",
|
|
13376
|
+
checked: snapshot.value?.toolAllowScriptInstall !== false,
|
|
13377
|
+
disabled: !writable,
|
|
13378
|
+
onChange: (event) => {
|
|
13379
|
+
const allowed = event.target.checked;
|
|
13380
|
+
void apply2(
|
|
13381
|
+
[{ op: "set", path: ["toolAllowScriptInstall"], value: allowed }],
|
|
13382
|
+
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"
|
|
13383
|
+
);
|
|
13384
|
+
}
|
|
13385
|
+
}
|
|
13386
|
+
), /* @__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}` : ""));
|
|
13387
|
+
}
|
|
13388
|
+
function OfficialToolsCard({ listOfficialTools, installOfficialTool, uninstallOfficialTool, repairOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes, health, onRefreshHealth, settingsScope }) {
|
|
12976
13389
|
const healthById = Object.fromEntries((health?.tools ?? []).map((item) => [item.id, item]));
|
|
12977
|
-
const [probeState, setProbeState] = import_react5.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
|
|
13390
|
+
const [probeState, setProbeState] = import_react5.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], install: null, error: "" });
|
|
12978
13391
|
const [jobs, setJobs] = import_react5.default.useState({});
|
|
12979
13392
|
const [rowErrors, setRowErrors] = import_react5.default.useState({});
|
|
12980
13393
|
const mounted = import_react5.default.useRef(false);
|
|
@@ -13005,7 +13418,7 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
13005
13418
|
const probes = Array.isArray(response.value?.tools) ? response.value.tools : [];
|
|
13006
13419
|
const capabilities = Array.isArray(response.value?.executionCapabilities) ? response.value.executionCapabilities : [];
|
|
13007
13420
|
const readiness = Array.isArray(response.value?.executionReadiness) ? response.value.executionReadiness : [];
|
|
13008
|
-
setProbeState({ status: "ready", probes, capabilities, readiness, error: "" });
|
|
13421
|
+
setProbeState({ status: "ready", probes, capabilities, readiness, install: response.value?.install ?? null, error: "" });
|
|
13009
13422
|
onProbes({ probes, capabilities, readiness, hostVersion: typeof response.value?.hostVersion === "string" ? response.value.hostVersion : null });
|
|
13010
13423
|
} catch (error) {
|
|
13011
13424
|
if (!mounted.current || current !== request.current) return;
|
|
@@ -13016,7 +13429,7 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
13016
13429
|
kept = true;
|
|
13017
13430
|
return { ...previous, status: "ready", error: message };
|
|
13018
13431
|
}
|
|
13019
|
-
return { status: "error", probes: [], capabilities: [], readiness: [], error: message };
|
|
13432
|
+
return { status: "error", probes: [], capabilities: [], readiness: [], install: null, error: message };
|
|
13020
13433
|
});
|
|
13021
13434
|
if (!kept) onProbes({ probes: [], capabilities: [], readiness: [] });
|
|
13022
13435
|
}
|
|
@@ -13088,34 +13501,36 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
13088
13501
|
clearInterval(timer);
|
|
13089
13502
|
};
|
|
13090
13503
|
}, [jobs, officialToolInstallStatus]);
|
|
13091
|
-
const
|
|
13504
|
+
const run = async (id2, operation) => {
|
|
13092
13505
|
const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
|
|
13506
|
+
const bridge = { install: installOfficialTool, repair: repairOfficialTool, uninstall: uninstallOfficialTool }[operation];
|
|
13093
13507
|
const refusal = installClickRefusal({
|
|
13094
13508
|
tool,
|
|
13095
13509
|
submitting: submitting.current.has(id2),
|
|
13096
|
-
running: jobsRef.current[id2]?.status === "running"
|
|
13510
|
+
running: jobsRef.current[id2]?.status === "running",
|
|
13511
|
+
operation
|
|
13097
13512
|
});
|
|
13098
13513
|
if (refusal) {
|
|
13099
13514
|
setRowErrors((previous) => ({ ...previous, [id2]: refusal }));
|
|
13100
13515
|
return;
|
|
13101
13516
|
}
|
|
13517
|
+
if (typeof bridge !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
13102
13518
|
submitting.current.add(id2);
|
|
13103
13519
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13104
13520
|
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
13105
13521
|
rememberJobs((previous) => ({
|
|
13106
13522
|
...previous,
|
|
13107
|
-
[id2]: { tool: id2, status: "running", outputTail: [], startedAt, error: null }
|
|
13523
|
+
[id2]: { tool: id2, operation, status: "running", outputTail: [], startedAt, error: null }
|
|
13108
13524
|
}));
|
|
13109
13525
|
try {
|
|
13110
|
-
|
|
13111
|
-
const response = await installOfficialTool(id2);
|
|
13526
|
+
const response = await bridge({ tool: id2 });
|
|
13112
13527
|
if (!mounted.current) return;
|
|
13113
13528
|
const accepted = acceptedInstallJob(response);
|
|
13114
|
-
if (!accepted.job) throw new Error(remoteErrorText(accepted.error, "\
|
|
13529
|
+
if (!accepted.job) throw new Error(remoteErrorText(accepted.error, "\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
13115
13530
|
rememberJobs((previous) => shouldApplyInstallStatus(previous[id2], accepted.job) ? { ...previous, [id2]: accepted.job } : previous);
|
|
13116
13531
|
} catch (error) {
|
|
13117
13532
|
if (!mounted.current) return;
|
|
13118
|
-
const message = remoteErrorText(text5(error?.message), "\
|
|
13533
|
+
const message = remoteErrorText(text5(error?.message), "\u542F\u52A8\u5931\u8D25\u3002");
|
|
13119
13534
|
rememberJobs((previous) => ({
|
|
13120
13535
|
...previous,
|
|
13121
13536
|
[id2]: {
|
|
@@ -13131,6 +13546,29 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
13131
13546
|
submitting.current.delete(id2);
|
|
13132
13547
|
}
|
|
13133
13548
|
};
|
|
13549
|
+
const install = (id2) => run(id2, "install");
|
|
13550
|
+
const repair = (id2) => run(id2, "repair");
|
|
13551
|
+
const uninstall = (id2) => run(id2, "uninstall");
|
|
13552
|
+
const setInstallMethod = async (toolId, methodId) => {
|
|
13553
|
+
const snapshot = settingsScope?.getSnapshot?.();
|
|
13554
|
+
if (!snapshot) return;
|
|
13555
|
+
let map = {};
|
|
13556
|
+
try {
|
|
13557
|
+
map = JSON.parse(String(snapshot.value?.toolInstallMethodsJson ?? "{}")) || {};
|
|
13558
|
+
} catch {
|
|
13559
|
+
map = {};
|
|
13560
|
+
}
|
|
13561
|
+
if (typeof map !== "object" || Array.isArray(map)) map = {};
|
|
13562
|
+
if (methodId) map[toolId] = methodId;
|
|
13563
|
+
else delete map[toolId];
|
|
13564
|
+
try {
|
|
13565
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: ["toolInstallMethodsJson"], value: JSON.stringify(map) }], snapshot.revision);
|
|
13566
|
+
if (!accepted) throw new Error("\u5B89\u88C5\u65B9\u5F0F\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002");
|
|
13567
|
+
void refresh();
|
|
13568
|
+
} catch (error) {
|
|
13569
|
+
if (mounted.current) setRowErrors((previous) => ({ ...previous, [toolId]: text5(error?.message) || "\u5B89\u88C5\u65B9\u5F0F\u4FDD\u5B58\u5931\u8D25\u3002" }));
|
|
13570
|
+
}
|
|
13571
|
+
};
|
|
13134
13572
|
const cancel = async (id2) => {
|
|
13135
13573
|
if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
|
|
13136
13574
|
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
@@ -13147,24 +13585,50 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
13147
13585
|
const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
|
|
13148
13586
|
const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
|
|
13149
13587
|
const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
|
|
13150
|
-
|
|
13588
|
+
const installById = Object.fromEntries((probeState.install?.tools ?? []).map((item) => [item.id, item]));
|
|
13589
|
+
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: () => {
|
|
13151
13590
|
void refresh();
|
|
13152
|
-
} }, "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, 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) => {
|
|
13153
|
-
const
|
|
13591
|
+
} }, "\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) => {
|
|
13592
|
+
const summary = installById[tool.id];
|
|
13593
|
+
const command = summary?.command ?? installCommandLine(tool);
|
|
13594
|
+
const methods = summary?.methods ?? installMethodsFor(tool).map((method) => ({ id: method.id, label: method.label }));
|
|
13154
13595
|
const probe = byId[tool.id];
|
|
13155
13596
|
const capability = capabilitiesById[tool.id];
|
|
13156
13597
|
const readiness = readinessById[tool.id];
|
|
13157
13598
|
const job = jobs[tool.id];
|
|
13158
13599
|
const running = job?.status === "running";
|
|
13159
13600
|
const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status, latestVersion: healthById[tool.id]?.latestVersion ?? null });
|
|
13160
|
-
const
|
|
13161
|
-
|
|
13162
|
-
|
|
13601
|
+
const maintenance = toolMaintenanceActions({
|
|
13602
|
+
tool,
|
|
13603
|
+
probe,
|
|
13604
|
+
job,
|
|
13605
|
+
probeStatus: probeState.status,
|
|
13606
|
+
summary: { installable: !summary?.error, removable: !summary?.error }
|
|
13607
|
+
});
|
|
13608
|
+
const verified = job?.status === "succeeded" && (job.operation === "uninstall" ? job.postInstallProbe?.installed === false : job.postInstallProbe?.installed === true);
|
|
13609
|
+
const operation = job?.operation === "uninstall" ? "\u5378\u8F7D" : job?.operation === "repair" ? "\u4FEE\u590D" : "\u5B89\u88C5";
|
|
13610
|
+
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);
|
|
13611
|
+
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(
|
|
13612
|
+
"select",
|
|
13613
|
+
{
|
|
13614
|
+
className: "mr-mini-select",
|
|
13615
|
+
value: summary?.savedMethodId ?? summary?.methodId ?? "",
|
|
13616
|
+
disabled: running || methods.length === 0,
|
|
13617
|
+
onChange: (event) => {
|
|
13618
|
+
void setInstallMethod(tool.id, event.target.value);
|
|
13619
|
+
}
|
|
13620
|
+
},
|
|
13621
|
+
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` : ""))
|
|
13622
|
+
))), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled, onClick: () => {
|
|
13163
13623
|
void install(tool.id);
|
|
13164
|
-
} }, action.label),
|
|
13624
|
+
} }, 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: () => {
|
|
13625
|
+
void repair(tool.id);
|
|
13626
|
+
} }, 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: () => {
|
|
13627
|
+
void uninstall(tool.id);
|
|
13628
|
+
} }, 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: () => {
|
|
13165
13629
|
void cancel(tool.id);
|
|
13166
|
-
} }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" :
|
|
13167
|
-
})), /* @__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\u53EF\u70B9\u201C\u53D6\u6D88\
|
|
13630
|
+
} }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : `\u53D6\u6D88${operation}`)));
|
|
13631
|
+
})), /* @__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")));
|
|
13168
13632
|
}
|
|
13169
13633
|
var HEADLESS_TOOLS = /* @__PURE__ */ new Set(["claude-code", "codex", "gemini"]);
|
|
13170
13634
|
var WORKSPACE_VIEWS = [
|
|
@@ -13291,7 +13755,7 @@ ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
|
13291
13755
|
}, []);
|
|
13292
13756
|
return { health, ledger, boundaries, busy, refreshHealth, refreshLedger, refreshBoundaries, finishOnboarding, rate, rerun };
|
|
13293
13757
|
}
|
|
13294
|
-
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries, previewRun, startRun, terminalApi }) {
|
|
13758
|
+
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, uninstallOfficialTool, repairOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries, previewRun, startRun, terminalApi }) {
|
|
13295
13759
|
const workbench = useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries });
|
|
13296
13760
|
const [catalogState, setCatalogState] = import_react5.default.useState({ status: "loading", catalog: null, error: "" });
|
|
13297
13761
|
const [task, setTask] = import_react5.default.useState("");
|
|
@@ -13514,9 +13978,12 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
13514
13978
|
{
|
|
13515
13979
|
listOfficialTools,
|
|
13516
13980
|
installOfficialTool,
|
|
13981
|
+
uninstallOfficialTool,
|
|
13982
|
+
repairOfficialTool,
|
|
13517
13983
|
cancelOfficialToolInstall,
|
|
13518
13984
|
officialToolInstallStatus,
|
|
13519
13985
|
onProbes: handleToolProbes,
|
|
13986
|
+
settingsScope,
|
|
13520
13987
|
health: workbench.health.report,
|
|
13521
13988
|
onRefreshHealth: () => {
|
|
13522
13989
|
void workbench.refreshHealth(true);
|
|
@@ -13567,6 +14034,26 @@ var toolIdCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolId`,
|
|
|
13567
14034
|
}
|
|
13568
14035
|
return value;
|
|
13569
14036
|
});
|
|
14037
|
+
var MAX_METHOD_ID_CHARS = 40;
|
|
14038
|
+
var text6 = (value) => typeof value === "string" ? value.trim() : "";
|
|
14039
|
+
var toolActionCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolAction`, (value) => {
|
|
14040
|
+
const request = plainObject2(value, "tool action request");
|
|
14041
|
+
for (const key of Object.keys(request)) {
|
|
14042
|
+
if (key !== "tool" && key !== "method") throw new TypeError(`unknown field ${key} in tool action request`);
|
|
14043
|
+
}
|
|
14044
|
+
const tool = getOfficialTool(text6(request.tool));
|
|
14045
|
+
if (!tool) throw new TypeError("tool must name a fixed official tool");
|
|
14046
|
+
const method = request.method === void 0 || request.method === null || request.method === "" ? null : text6(request.method);
|
|
14047
|
+
if (method !== null) {
|
|
14048
|
+
if (method.length > MAX_METHOD_ID_CHARS || !/^[A-Za-z0-9-]+$/.test(method)) {
|
|
14049
|
+
throw new TypeError("method must be one of the tool's declared install methods");
|
|
14050
|
+
}
|
|
14051
|
+
if (!installMethodsFor(tool).some((item) => item.id === method)) {
|
|
14052
|
+
throw new TypeError(`${tool.label} does not offer the install method ${method}`);
|
|
14053
|
+
}
|
|
14054
|
+
}
|
|
14055
|
+
return method === null ? { tool: tool.id } : { tool: tool.id, method };
|
|
14056
|
+
});
|
|
13570
14057
|
var listResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolList`, (value) => {
|
|
13571
14058
|
const result = plainObject2(value, "tool list result");
|
|
13572
14059
|
if (!Array.isArray(result.tools)) throw new TypeError("tool list result needs a tools array");
|
|
@@ -13680,7 +14167,9 @@ var toolIdParameter = Object.freeze({
|
|
|
13680
14167
|
var jsonParameter = (name, codec) => Object.freeze({ name, wire: name, source: "json", codec });
|
|
13681
14168
|
var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
|
|
13682
14169
|
descriptor("list", [], listResultCodec),
|
|
13683
|
-
descriptor("installTool", [
|
|
14170
|
+
descriptor("installTool", [jsonParameter("request", toolActionCodec)], installResultCodec),
|
|
14171
|
+
descriptor("uninstallTool", [jsonParameter("request", toolActionCodec)], installResultCodec),
|
|
14172
|
+
descriptor("repairTool", [jsonParameter("request", toolActionCodec)], installResultCodec),
|
|
13684
14173
|
descriptor("cancel", [toolIdParameter], installResultCodec),
|
|
13685
14174
|
descriptor("status", [toolIdParameter], statusResultCodec),
|
|
13686
14175
|
// Workbench: onboarding health check, run ledger, ratings, step retry, security boundaries.
|
|
@@ -13744,8 +14233,8 @@ function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INT
|
|
|
13744
14233
|
const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
|
|
13745
14234
|
return {
|
|
13746
14235
|
...numeric,
|
|
13747
|
-
parse: (
|
|
13748
|
-
const write = numeric.parse(
|
|
14236
|
+
parse: (text7) => {
|
|
14237
|
+
const write = numeric.parse(text7);
|
|
13749
14238
|
if (write?.kind !== "set") return write;
|
|
13750
14239
|
const value = write.value;
|
|
13751
14240
|
if (typeof value !== "number" || value < minimum || value > maximum) return void 0;
|
|
@@ -13758,10 +14247,10 @@ function modelProfilesField() {
|
|
|
13758
14247
|
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
|
|
13759
14248
|
return {
|
|
13760
14249
|
...field2,
|
|
13761
|
-
parse: (
|
|
14250
|
+
parse: (text7) => {
|
|
13762
14251
|
try {
|
|
13763
|
-
parseModelProfilesJson(
|
|
13764
|
-
return field2.parse(
|
|
14252
|
+
parseModelProfilesJson(text7);
|
|
14253
|
+
return field2.parse(text7);
|
|
13765
14254
|
} catch {
|
|
13766
14255
|
return void 0;
|
|
13767
14256
|
}
|
|
@@ -13772,15 +14261,79 @@ function quotaPatternsField() {
|
|
|
13772
14261
|
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("quotaPatternsJson");
|
|
13773
14262
|
return {
|
|
13774
14263
|
...field2,
|
|
13775
|
-
parse: (
|
|
13776
|
-
const raw = String(
|
|
14264
|
+
parse: (text7) => {
|
|
14265
|
+
const raw = String(text7 ?? "").trim() || "{}";
|
|
13777
14266
|
try {
|
|
13778
14267
|
const value = JSON.parse(raw);
|
|
13779
14268
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
13780
14269
|
} catch {
|
|
13781
14270
|
return void 0;
|
|
13782
14271
|
}
|
|
13783
|
-
return parseQuotaPatterns(raw).errors.length ? void 0 : field2.parse(
|
|
14272
|
+
return parseQuotaPatterns(raw).errors.length ? void 0 : field2.parse(text7);
|
|
14273
|
+
}
|
|
14274
|
+
};
|
|
14275
|
+
}
|
|
14276
|
+
function installDirField() {
|
|
14277
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("toolInstallDir");
|
|
14278
|
+
return {
|
|
14279
|
+
...field2,
|
|
14280
|
+
parse: (text7) => {
|
|
14281
|
+
if (!String(text7 ?? "").trim()) return field2.parse(text7);
|
|
14282
|
+
try {
|
|
14283
|
+
expandInstallDir(text7);
|
|
14284
|
+
} catch {
|
|
14285
|
+
return void 0;
|
|
14286
|
+
}
|
|
14287
|
+
return field2.parse(text7);
|
|
14288
|
+
}
|
|
14289
|
+
};
|
|
14290
|
+
}
|
|
14291
|
+
function npmRegistryField() {
|
|
14292
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("toolNpmRegistry");
|
|
14293
|
+
return {
|
|
14294
|
+
...field2,
|
|
14295
|
+
parse: (text7) => {
|
|
14296
|
+
const raw = String(text7 ?? "").trim() || DEFAULT_NPM_REGISTRY;
|
|
14297
|
+
try {
|
|
14298
|
+
normalizeSourceUrl(raw, "npm \u6E90");
|
|
14299
|
+
} catch {
|
|
14300
|
+
return void 0;
|
|
14301
|
+
}
|
|
14302
|
+
return field2.parse(text7);
|
|
14303
|
+
}
|
|
14304
|
+
};
|
|
14305
|
+
}
|
|
14306
|
+
function scriptUrlsField() {
|
|
14307
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("toolScriptUrlsJson");
|
|
14308
|
+
return {
|
|
14309
|
+
...field2,
|
|
14310
|
+
parse: (text7) => {
|
|
14311
|
+
const raw = String(text7 ?? "").trim() || "{}";
|
|
14312
|
+
try {
|
|
14313
|
+
const parsed = JSON.parse(raw);
|
|
14314
|
+
parseInstallPreferences({ toolScriptUrlsJson: raw }, { tools: OFFICIAL_TOOLS });
|
|
14315
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
14316
|
+
} catch {
|
|
14317
|
+
return void 0;
|
|
14318
|
+
}
|
|
14319
|
+
return field2.parse(text7);
|
|
14320
|
+
}
|
|
14321
|
+
};
|
|
14322
|
+
}
|
|
14323
|
+
function installMethodsField() {
|
|
14324
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("toolInstallMethodsJson");
|
|
14325
|
+
return {
|
|
14326
|
+
...field2,
|
|
14327
|
+
parse: (text7) => {
|
|
14328
|
+
const raw = String(text7 ?? "").trim() || "{}";
|
|
14329
|
+
try {
|
|
14330
|
+
const parsed = JSON.parse(raw);
|
|
14331
|
+
parseInstallPreferences({ toolInstallMethodsJson: raw }, { tools: OFFICIAL_TOOLS });
|
|
14332
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
14333
|
+
} catch {
|
|
14334
|
+
return void 0;
|
|
14335
|
+
}
|
|
14336
|
+
return field2.parse(text7);
|
|
13784
14337
|
}
|
|
13785
14338
|
};
|
|
13786
14339
|
}
|
|
@@ -13794,7 +14347,11 @@ var RouterSettingsCardController = class {
|
|
|
13794
14347
|
boundedNumberField("reviewSampleRate", { minimum: 0, maximum: 1 }),
|
|
13795
14348
|
boundedNumberField("subscriptionCooldownMinutes", { minimum: 1, maximum: 10080, integer: true }),
|
|
13796
14349
|
modelProfilesField(),
|
|
13797
|
-
quotaPatternsField()
|
|
14350
|
+
quotaPatternsField(),
|
|
14351
|
+
installDirField(),
|
|
14352
|
+
npmRegistryField(),
|
|
14353
|
+
installMethodsField(),
|
|
14354
|
+
scriptUrlsField()
|
|
13798
14355
|
]);
|
|
13799
14356
|
this.store = this.form.bind(() => ({
|
|
13800
14357
|
...this.form.shell(),
|
|
@@ -13805,7 +14362,11 @@ var RouterSettingsCardController = class {
|
|
|
13805
14362
|
reviewSampleRate: this.form.field("reviewSampleRate"),
|
|
13806
14363
|
subscriptionCooldownMinutes: this.form.field("subscriptionCooldownMinutes"),
|
|
13807
14364
|
modelProfilesJson: this.form.field("modelProfilesJson"),
|
|
13808
|
-
quotaPatternsJson: this.form.field("quotaPatternsJson")
|
|
14365
|
+
quotaPatternsJson: this.form.field("quotaPatternsJson"),
|
|
14366
|
+
toolInstallDir: this.form.field("toolInstallDir"),
|
|
14367
|
+
toolNpmRegistry: this.form.field("toolNpmRegistry"),
|
|
14368
|
+
toolInstallMethodsJson: this.form.field("toolInstallMethodsJson"),
|
|
14369
|
+
toolScriptUrlsJson: this.form.field("toolScriptUrlsJson")
|
|
13809
14370
|
}));
|
|
13810
14371
|
}
|
|
13811
14372
|
/** Supply the snapshot hook and staged form actions to the Plugins slot. */
|
|
@@ -13838,8 +14399,8 @@ function RouterSettingsCard(props) {
|
|
|
13838
14399
|
overriddenLabel: FIELD_COPY.overridden,
|
|
13839
14400
|
resetLabel: FIELD_COPY.reset,
|
|
13840
14401
|
invalidLabel: FIELD_COPY.invalidNumber,
|
|
13841
|
-
onEdit: (
|
|
13842
|
-
props.edit("budgetUsd",
|
|
14402
|
+
onEdit: (text7) => {
|
|
14403
|
+
props.edit("budgetUsd", text7);
|
|
13843
14404
|
},
|
|
13844
14405
|
onReset: () => {
|
|
13845
14406
|
props.resetField("budgetUsd");
|
|
@@ -13857,8 +14418,8 @@ function RouterSettingsCard(props) {
|
|
|
13857
14418
|
overriddenLabel: FIELD_COPY.overridden,
|
|
13858
14419
|
resetLabel: FIELD_COPY.reset,
|
|
13859
14420
|
invalidLabel: FIELD_COPY.invalidNumber,
|
|
13860
|
-
onEdit: (
|
|
13861
|
-
props.edit("maxConsultOutputChars",
|
|
14421
|
+
onEdit: (text7) => {
|
|
14422
|
+
props.edit("maxConsultOutputChars", text7);
|
|
13862
14423
|
},
|
|
13863
14424
|
onReset: () => {
|
|
13864
14425
|
props.resetField("maxConsultOutputChars");
|
|
@@ -13881,8 +14442,8 @@ function RouterSettingsCard(props) {
|
|
|
13881
14442
|
overriddenLabel: FIELD_COPY.overridden,
|
|
13882
14443
|
resetLabel: FIELD_COPY.reset,
|
|
13883
14444
|
invalidLabel: FIELD_COPY.invalidNumber,
|
|
13884
|
-
onEdit: (
|
|
13885
|
-
props.edit(key,
|
|
14445
|
+
onEdit: (text7) => {
|
|
14446
|
+
props.edit(key, text7);
|
|
13886
14447
|
},
|
|
13887
14448
|
onReset: () => {
|
|
13888
14449
|
props.resetField(key);
|
|
@@ -13929,7 +14490,82 @@ function RouterSettingsCard(props) {
|
|
|
13929
14490
|
), 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, `{
|
|
13930
14491
|
"kimi-code": { "quota": ["\u989D\u5EA6\u5DF2\u7528\u5B8C"], "rateLimit": ["\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41"] },
|
|
13931
14492
|
"my-glm-plan": { "quota": ["Usage limit reached for", "1308"] }
|
|
13932
|
-
}`)), /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("quotaPatternsJson") }, "\u6062\u590D\u9ED8\u8BA4\u89C4\u5219")), /* @__PURE__ */ import_react6.default.createElement("
|
|
14493
|
+
}`)), /* @__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(
|
|
14494
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
14495
|
+
{
|
|
14496
|
+
id: "model-router-tool-install-dir",
|
|
14497
|
+
label: "\u7EDF\u4E00\u5B89\u88C5\u76EE\u5F55",
|
|
14498
|
+
hint: "\u7559\u7A7A\u8868\u793A\u4F7F\u7528\u5404\u5382\u5546\u9ED8\u8BA4\u4F4D\u7F6E\uFF08npm \u5168\u5C40\u76EE\u5F55 / \u5382\u5546\u811A\u672C\u76EE\u5F55\uFF09\u3002npm \u4E0E pnpm \u65B9\u5F0F\u4F1A\u5E26\u4E0A --prefix\uFF1B\u5382\u5546\u5B89\u88C5\u811A\u672C\u662F\u5426\u652F\u6301\u76EE\u5F55\u53C2\u6570\u53D6\u51B3\u4E8E\u8BE5\u5382\u5546\u3002\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002",
|
|
14499
|
+
disabled,
|
|
14500
|
+
...state.toolInstallDir,
|
|
14501
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
14502
|
+
resetLabel: FIELD_COPY.reset,
|
|
14503
|
+
invalidLabel: "\u8BF7\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002",
|
|
14504
|
+
onEdit: (value) => {
|
|
14505
|
+
props.edit("toolInstallDir", value);
|
|
14506
|
+
},
|
|
14507
|
+
onReset: () => {
|
|
14508
|
+
props.resetField("toolInstallDir");
|
|
14509
|
+
}
|
|
14510
|
+
}
|
|
14511
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
14512
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
14513
|
+
{
|
|
14514
|
+
id: "model-router-tool-npm-registry",
|
|
14515
|
+
label: "npm \u6E90\u5730\u5740",
|
|
14516
|
+
hint: "\u6240\u6709\u5305\u7BA1\u7406\u5668\u5B89\u88C5\u65B9\u5F0F\u4ECE\u8BE5\u5730\u5740\u4E0B\u8F7D\uFF0C\u5FC5\u987B\u662F https \u94FE\u63A5\u3002\u56FD\u5185\u7F51\u7EDC\u53EF\u586B\u955C\u50CF\u6E90\uFF0C\u4F8B\u5982 https://registry.npmmirror.com/\u3002",
|
|
14517
|
+
disabled,
|
|
14518
|
+
...state.toolNpmRegistry,
|
|
14519
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
14520
|
+
resetLabel: FIELD_COPY.reset,
|
|
14521
|
+
invalidLabel: "\u9700\u8981 https \u5F00\u5934\u7684\u5B8C\u6574\u5730\u5740\u3002",
|
|
14522
|
+
onEdit: (value) => {
|
|
14523
|
+
props.edit("toolNpmRegistry", value);
|
|
14524
|
+
},
|
|
14525
|
+
onReset: () => {
|
|
14526
|
+
props.resetField("toolNpmRegistry");
|
|
14527
|
+
}
|
|
14528
|
+
}
|
|
14529
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
14530
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
14531
|
+
{
|
|
14532
|
+
id: "model-router-tool-install-methods",
|
|
14533
|
+
label: "\u9ED8\u8BA4\u5B89\u88C5\u65B9\u5F0F\uFF08JSON\uFF0C\u53EF\u9009\uFF09",
|
|
14534
|
+
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",
|
|
14535
|
+
disabled,
|
|
14536
|
+
...state.toolInstallMethodsJson,
|
|
14537
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
14538
|
+
resetLabel: FIELD_COPY.reset,
|
|
14539
|
+
invalidLabel: "\u9700\u8981 JSON \u5BF9\u8C61\uFF0C\u4E14\u6BCF\u4E2A\u952E\u90FD\u662F\u5B98\u65B9\u5DE5\u5177 ID\u3001\u6BCF\u4E2A\u503C\u90FD\u662F\u8BE5\u5DE5\u5177\u652F\u6301\u7684\u5B89\u88C5\u65B9\u5F0F\u3002",
|
|
14540
|
+
onEdit: (value) => {
|
|
14541
|
+
props.edit("toolInstallMethodsJson", value);
|
|
14542
|
+
},
|
|
14543
|
+
onReset: () => {
|
|
14544
|
+
props.resetField("toolInstallMethodsJson");
|
|
14545
|
+
}
|
|
14546
|
+
}
|
|
14547
|
+
), /* @__PURE__ */ import_react6.default.createElement(
|
|
14548
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
14549
|
+
{
|
|
14550
|
+
id: "model-router-tool-script-urls",
|
|
14551
|
+
label: "\u5B89\u88C5\u811A\u672C\u6E90\u8986\u76D6\uFF08JSON\uFF0C\u53EF\u9009\uFF09",
|
|
14552
|
+
hint: "\u6309\u5DE5\u5177 ID \u6307\u5B9A\u5382\u5546\u5B89\u88C5\u811A\u672C\u7684\u955C\u50CF\u5730\u5740\uFF08\u5FC5\u987B\u662F https\uFF09\u3002\u63D2\u4EF6\u4F1A\u5148\u4E0B\u8F7D\u811A\u672C\u3001\u6838\u5BF9\u5B83\u4ECD\u7136\u662F\u8BE5\u5382\u5546\u7684\u5B98\u65B9\u5B89\u88C5\u811A\u672C\uFF0C\u518D\u6267\u884C\u672C\u5730\u526F\u672C\uFF1B\u955C\u50CF\u4E0D\u80FD\u66FF\u6362\u6210\u522B\u7684\u7A0B\u5E8F\u3002",
|
|
14553
|
+
disabled,
|
|
14554
|
+
...state.toolScriptUrlsJson,
|
|
14555
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
14556
|
+
resetLabel: FIELD_COPY.reset,
|
|
14557
|
+
invalidLabel: "\u9700\u8981 JSON \u5BF9\u8C61\uFF0C\u952E\u662F\u5B98\u65B9\u5DE5\u5177 ID\uFF0C\u503C\u662F https \u94FE\u63A5\u3002",
|
|
14558
|
+
onEdit: (value) => {
|
|
14559
|
+
props.edit("toolScriptUrlsJson", value);
|
|
14560
|
+
},
|
|
14561
|
+
onReset: () => {
|
|
14562
|
+
props.resetField("toolScriptUrlsJson");
|
|
14563
|
+
}
|
|
14564
|
+
}
|
|
14565
|
+
), /* @__PURE__ */ import_react6.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react6.default.createElement("summary", null, "\u67E5\u770B\u683C\u5F0F"), /* @__PURE__ */ import_react6.default.createElement("pre", null, `{
|
|
14566
|
+
"stepcode": "https://mirror.example.com/stepcode/install.ps1",
|
|
14567
|
+
"opencode": "https://mirror.example.com/opencode/install"
|
|
14568
|
+
}`), /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.noticeText }, "\u5B89\u88C5\u811A\u672C\u65B9\u5F0F\uFF08curl / irm\uFF09\u53EF\u4EE5\u5728\u5DE5\u4F5C\u53F0\u7684\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u9875\u5173\u95ED\uFF1B\u5173\u95ED\u540E\u6240\u6709\u5DE5\u5177\u53EA\u80FD\u901A\u8FC7 npm \u6216 pnpm \u5B89\u88C5\u3002"))), /* @__PURE__ */ import_react6.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react6.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react6.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.noticeText }, "\u8BF7\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u9762\u914D\u7F6E DeepSeek\u3001OpenAI \u517C\u5BB9\u6216 Anthropic \u517C\u5BB9\u670D\u52A1\u3002\u6B64\u63D2\u4EF6\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E0D\u4FDD\u5B58 API Key\uFF1B\u76EE\u5F55\u4E2D\u7684\u8DEF\u7EBF\u4ECD\u9700\u901A\u8FC7\u5B9E\u9645\u8C03\u7528\u9A8C\u8BC1\u8D26\u53F7\u548C\u7F51\u7EDC\u53EF\u7528\u6027\u3002"), /* @__PURE__ */ import_react6.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react6.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react6.default.createElement("code", null, "model_router_consult"), " \u54A8\u8BE2\u4E00\u4E2A\u5DF2\u914D\u7F6E\u6A21\u578B\u3002\u5EFA\u8BAE\u4E0D\u4F1A\u6539\u5199\u4E3B\u4F1A\u8BDD\u6A21\u578B\uFF1B\u591A\u4EBA\u5206\u5DE5\u7531\u5B98\u65B9 Agent Teams \u5DE5\u5177\u6267\u884C\u3002")));
|
|
13933
14569
|
}
|
|
13934
14570
|
function OpenRouterWorkspace({ subject, openPanel }) {
|
|
13935
14571
|
if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
|
|
@@ -13954,7 +14590,9 @@ function registerUi(ctx) {
|
|
|
13954
14590
|
loadCatalog: () => ctx.remote.session.modelCatalog(),
|
|
13955
14591
|
settingsScope,
|
|
13956
14592
|
listOfficialTools: () => officialToolsRemote.list(),
|
|
13957
|
-
installOfficialTool: (
|
|
14593
|
+
installOfficialTool: (request) => officialToolsRemote.installTool(request),
|
|
14594
|
+
uninstallOfficialTool: (request) => officialToolsRemote.uninstallTool(request),
|
|
14595
|
+
repairOfficialTool: (request) => officialToolsRemote.repairTool(request),
|
|
13958
14596
|
cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
|
|
13959
14597
|
officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId),
|
|
13960
14598
|
toolHealth: (fresh) => officialToolsRemote.health(fresh),
|