@ljwei-stak/dsh-model-router 0.13.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 +3092 -0
- package/.dsh-plugin/index.mjs +1651 -0
- package/.dsh-plugin/official-tools-remote-service.mjs +104 -0
- package/.dsh-plugin/shared/harness-plan.mjs +179 -0
- package/.dsh-plugin/shared/livebench.mjs +264 -0
- package/.dsh-plugin/shared/model-profiles.mjs +142 -0
- package/.dsh-plugin/shared/official-team-runtime.mjs +411 -0
- package/.dsh-plugin/shared/official-tool-executor.mjs +801 -0
- package/.dsh-plugin/shared/official-tool-registry.mjs +138 -0
- package/.dsh-plugin/shared/official-tools-remote.mjs +173 -0
- package/.dsh-plugin/shared/official-tools-runtime.mjs +642 -0
- package/.dsh-plugin/shared/router-state.mjs +207 -0
- package/.dsh-plugin/shared/router.mjs +1134 -0
- package/.dsh-plugin/shared/routing-presets.mjs +49 -0
- package/.dsh-plugin/shared/run-ledger.mjs +348 -0
- package/.dsh-plugin/shared/security-boundaries.mjs +54 -0
- package/.dsh-plugin/shared/subscription-billing.mjs +340 -0
- package/.dsh-plugin/shared/task-executors.mjs +1154 -0
- package/.dsh-plugin/shared/tool-health.mjs +311 -0
- package/.dsh-plugin/shared/vendor-mimo-grok-adapter.mjs +308 -0
- package/.dsh-plugin/shared/vendor-minimax-adapter.mjs +247 -0
- package/.dsh-plugin/shared/zcode-bundle.mjs +208 -0
- package/.dsh-plugin/shared/zcode-installer.mjs +247 -0
- package/CHANGELOG.md +36 -0
- package/INSTALLATION_GUIDE.zh.md +134 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +53 -0
- package/README.i18n.yaml +3 -0
- package/README.md +424 -0
- package/README.zh.md +413 -0
- package/cordis.patch.yml +12 -0
- package/docs/assets/candidate-pruning.svg +80 -0
- package/docs/assets/desktop-official-tools-0.9.0.png +0 -0
- package/docs/assets/router-only-0.12.0.png +0 -0
- package/docs/assets/routing-workflow.svg +96 -0
- package/docs/assets/workbench-usage.svg +119 -0
- package/package.json +161 -0
|
@@ -0,0 +1,3092 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@ljwei-stak/dsh-model-router",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
var __create = Object.create;
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// .dsh-plugin/client/official-harness.jsx
|
|
35
|
+
var official_harness_exports = {};
|
|
36
|
+
__export(official_harness_exports, {
|
|
37
|
+
ROUTER_NAMESPACE: () => ROUTER_NAMESPACE,
|
|
38
|
+
ROUTER_PACKAGE: () => ROUTER_PACKAGE,
|
|
39
|
+
ROUTER_PANEL: () => ROUTER_PANEL,
|
|
40
|
+
RouterSettingsCard: () => RouterSettingsCard,
|
|
41
|
+
RouterSettingsCardController: () => RouterSettingsCardController,
|
|
42
|
+
apply: () => apply,
|
|
43
|
+
inject: () => inject
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(official_harness_exports);
|
|
46
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
47
|
+
|
|
48
|
+
// .dsh-plugin/shared/official-tool-registry.mjs
|
|
49
|
+
var OFFICIAL_TOOLS = Object.freeze([
|
|
50
|
+
Object.freeze({
|
|
51
|
+
id: "kimi-code",
|
|
52
|
+
label: "Kimi Code \xB7 Node",
|
|
53
|
+
vendor: "Moonshot AI",
|
|
54
|
+
purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
55
|
+
package: "@moonshot-ai/kimi-code",
|
|
56
|
+
version: "2.1.1",
|
|
57
|
+
manager: "npm",
|
|
58
|
+
installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
|
|
59
|
+
probeExecutables: ["kimi"],
|
|
60
|
+
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",
|
|
61
|
+
providerHints: ["moonshot", "kimi"]
|
|
62
|
+
}),
|
|
63
|
+
Object.freeze({
|
|
64
|
+
id: "claude-code",
|
|
65
|
+
label: "Claude Code",
|
|
66
|
+
vendor: "Anthropic",
|
|
67
|
+
purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
|
|
68
|
+
package: "@anthropic-ai/claude-code",
|
|
69
|
+
version: "2.1.283",
|
|
70
|
+
manager: "npm",
|
|
71
|
+
installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
|
|
72
|
+
probeExecutables: ["claude"],
|
|
73
|
+
providerHints: ["anthropic", "claude"]
|
|
74
|
+
}),
|
|
75
|
+
Object.freeze({
|
|
76
|
+
id: "codex",
|
|
77
|
+
label: "Codex CLI",
|
|
78
|
+
vendor: "OpenAI",
|
|
79
|
+
purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
|
|
80
|
+
package: "@openai/codex",
|
|
81
|
+
version: "0.157.1",
|
|
82
|
+
installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
|
|
83
|
+
manager: "npm",
|
|
84
|
+
probeExecutables: ["codex"],
|
|
85
|
+
probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
|
|
86
|
+
providerHints: ["openai", "gpt", "codex"]
|
|
87
|
+
}),
|
|
88
|
+
Object.freeze({
|
|
89
|
+
id: "minimax-code",
|
|
90
|
+
label: "MiniMax Code",
|
|
91
|
+
vendor: "MiniMax",
|
|
92
|
+
purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
|
|
93
|
+
package: "@minimax-ai/code",
|
|
94
|
+
version: "0.5.5",
|
|
95
|
+
manager: "npm",
|
|
96
|
+
installArgs: ["install", "-g", "@minimax-ai/code@0.5.5", "--registry=https://registry.npmjs.org/", "--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"],
|
|
97
|
+
probeExecutables: ["mcode"],
|
|
98
|
+
providerHints: ["minimax"]
|
|
99
|
+
}),
|
|
100
|
+
Object.freeze({
|
|
101
|
+
id: "mimo-code",
|
|
102
|
+
label: "MiMo Code",
|
|
103
|
+
vendor: "XiaoMi",
|
|
104
|
+
purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
|
|
105
|
+
package: "@mimo-ai/cli",
|
|
106
|
+
version: "0.1.15",
|
|
107
|
+
manager: "npm",
|
|
108
|
+
installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
|
|
109
|
+
probeExecutables: ["mimo"],
|
|
110
|
+
providerHints: ["mimo", "xiaomi"]
|
|
111
|
+
}),
|
|
112
|
+
Object.freeze({
|
|
113
|
+
id: "grok-build",
|
|
114
|
+
label: "Grok Build",
|
|
115
|
+
vendor: "xAI",
|
|
116
|
+
purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
117
|
+
package: "@xai-official/grok",
|
|
118
|
+
version: "1.0.41",
|
|
119
|
+
manager: "npm",
|
|
120
|
+
installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
|
|
121
|
+
probeExecutables: ["grok"],
|
|
122
|
+
providerHints: ["xai", "grok"]
|
|
123
|
+
}),
|
|
124
|
+
Object.freeze({
|
|
125
|
+
id: "gemini",
|
|
126
|
+
label: "Gemini CLI",
|
|
127
|
+
vendor: "Google",
|
|
128
|
+
purpose: "Google \u5B98\u65B9 Gemini CLI\uFF0C\u65E0\u754C\u9762\u6A21\u5F0F\u4F7F\u7528 gemini -p\u3002",
|
|
129
|
+
package: "@google/gemini-cli",
|
|
130
|
+
version: "0.62.0",
|
|
131
|
+
manager: "npm",
|
|
132
|
+
installArgs: ["install", "-g", "@google/gemini-cli@0.62.0", "--registry=https://registry.npmjs.org/"],
|
|
133
|
+
probeExecutables: ["gemini"],
|
|
134
|
+
providerHints: ["gemini", "google"],
|
|
135
|
+
// Headless runs go through the task adapter. The signed sandbox runner does
|
|
136
|
+
// not launch this CLI; a missing or failed process falls back to the API.
|
|
137
|
+
headlessAdapter: true
|
|
138
|
+
}),
|
|
139
|
+
Object.freeze({
|
|
140
|
+
id: "zcode",
|
|
141
|
+
label: "ZCode",
|
|
142
|
+
vendor: "Z.ai",
|
|
143
|
+
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",
|
|
144
|
+
version: "3.14.3",
|
|
145
|
+
manager: "signed-windows-installer",
|
|
146
|
+
installArgs: [],
|
|
147
|
+
probeExecutables: [],
|
|
148
|
+
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",
|
|
149
|
+
providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu", "bigmodel"]
|
|
150
|
+
})
|
|
151
|
+
]);
|
|
152
|
+
var TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
|
|
153
|
+
function getOfficialTool(id2) {
|
|
154
|
+
return TOOL_BY_ID.get(String(id2 ?? "").trim()) ?? null;
|
|
155
|
+
}
|
|
156
|
+
function toolForProvider(provider) {
|
|
157
|
+
const value = String(provider ?? "").trim().toLowerCase();
|
|
158
|
+
if (!value) return null;
|
|
159
|
+
for (const tool of OFFICIAL_TOOLS) {
|
|
160
|
+
if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function installCommandLine(tool) {
|
|
165
|
+
if (!tool || tool.unsupported) return null;
|
|
166
|
+
if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
|
|
167
|
+
return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// .dsh-plugin/shared/model-profiles.mjs
|
|
171
|
+
var MAX_TEXT = 32e3;
|
|
172
|
+
var MAX_PROFILES = 200;
|
|
173
|
+
var id = (value) => typeof value === "string" ? value.trim() : "";
|
|
174
|
+
var BILLING_VALUES = /* @__PURE__ */ new Set(["subscription-first", "api-only", "subscription-only"]);
|
|
175
|
+
var SUBSCRIPTION_VALUES = /* @__PURE__ */ new Set(["plan-key", "cli-login", "none"]);
|
|
176
|
+
function nonnegative(value, label) {
|
|
177
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1e6) {
|
|
178
|
+
throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 1000000 \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
function normalizeProfile(entry, index) {
|
|
183
|
+
const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
|
|
184
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
185
|
+
const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel", "execution", "billing", "subscription", "apiRoute"]);
|
|
186
|
+
const unknown = Object.keys(entry).find((key) => !allowed.has(key));
|
|
187
|
+
if (unknown) throw new Error(`${label} \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknown}\uFF1B\u4E0D\u8981\u5728\u8FD9\u91CC\u586B\u5199\u5BC6\u94A5\u6216\u547D\u4EE4`);
|
|
188
|
+
const provider = id(entry.provider);
|
|
189
|
+
const model = id(entry.model);
|
|
190
|
+
if (!provider || !model || provider.length > 160 || model.length > 240) {
|
|
191
|
+
throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
|
|
192
|
+
}
|
|
193
|
+
const profile = { provider, model };
|
|
194
|
+
if (entry.quality !== void 0) {
|
|
195
|
+
const value = nonnegative(entry.quality, `${label} \u7684 quality`);
|
|
196
|
+
if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
|
|
197
|
+
profile.quality = value / 100;
|
|
198
|
+
}
|
|
199
|
+
if (entry.pricing !== void 0) {
|
|
200
|
+
const prices = entry.pricing;
|
|
201
|
+
if (!prices || typeof prices !== "object" || Array.isArray(prices)) throw new Error(`${label} \u7684 pricing \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
202
|
+
const priceFields = /* @__PURE__ */ new Set(["input", "output", "cacheRead", "cacheWrite", "currency"]);
|
|
203
|
+
const unknownPrice = Object.keys(prices).find((key) => !priceFields.has(key));
|
|
204
|
+
if (unknownPrice) throw new Error(`${label} \u7684 pricing \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknownPrice}`);
|
|
205
|
+
if (prices.currency !== void 0 && prices.currency !== "USD") throw new Error(`${label} \u7684\u4EF7\u683C\u5E01\u79CD\u76EE\u524D\u4EC5\u652F\u6301 USD`);
|
|
206
|
+
profile.pricing = {
|
|
207
|
+
input: nonnegative(prices.input, `${label} \u7684\u8F93\u5165\u5355\u4EF7`),
|
|
208
|
+
output: nonnegative(prices.output, `${label} \u7684\u8F93\u51FA\u5355\u4EF7`),
|
|
209
|
+
currency: "USD"
|
|
210
|
+
};
|
|
211
|
+
if (prices.cacheRead !== void 0) profile.pricing.cacheRead = nonnegative(prices.cacheRead, `${label} \u7684\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7`);
|
|
212
|
+
if (prices.cacheWrite !== void 0) profile.pricing.cacheWrite = nonnegative(prices.cacheWrite, `${label} \u7684\u7F13\u5B58\u5199\u5165\u5355\u4EF7`);
|
|
213
|
+
}
|
|
214
|
+
if (entry.specialties !== void 0) {
|
|
215
|
+
if (!Array.isArray(entry.specialties) || entry.specialties.length > 16 || entry.specialties.some((value) => !/^[a-z][a-z0-9-]{0,39}$/.test(value))) {
|
|
216
|
+
throw new Error(`${label} \u7684 specialties \u5FC5\u987B\u662F\u81F3\u591A 16 \u4E2A\u82F1\u6587\u4EFB\u52A1\u6807\u7B7E`);
|
|
217
|
+
}
|
|
218
|
+
profile.specialties = [...new Set(entry.specialties)];
|
|
219
|
+
}
|
|
220
|
+
if (entry.cliModel !== void 0) {
|
|
221
|
+
const cliModel = id(entry.cliModel);
|
|
222
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error(`${label} \u7684 cliModel \u65E0\u6548`);
|
|
223
|
+
if (toolForProvider(provider)?.id === "zcode") throw new Error(`${label} \u5BF9\u5E94\u7684 ZCode CLI \u6682\u4E0D\u652F\u6301\u9010\u6B21\u5207\u6362\u6A21\u578B`);
|
|
224
|
+
profile.cliModel = cliModel;
|
|
225
|
+
}
|
|
226
|
+
if (entry.execution !== void 0) {
|
|
227
|
+
if (entry.execution !== "auto" && entry.execution !== "official" && entry.execution !== "api") {
|
|
228
|
+
throw new Error(`${label} \u7684 execution \u53EA\u80FD\u662F auto\u3001official \u6216 api`);
|
|
229
|
+
}
|
|
230
|
+
if (entry.execution !== "auto") profile.execution = entry.execution;
|
|
231
|
+
}
|
|
232
|
+
if (entry.billing !== void 0) {
|
|
233
|
+
if (!BILLING_VALUES.has(entry.billing)) throw new Error(`${label} \u7684 billing \u53EA\u80FD\u662F subscription-first\u3001api-only \u6216 subscription-only`);
|
|
234
|
+
profile.billing = entry.billing;
|
|
235
|
+
}
|
|
236
|
+
if (entry.subscription !== void 0) {
|
|
237
|
+
if (!SUBSCRIPTION_VALUES.has(entry.subscription)) throw new Error(`${label} \u7684 subscription \u53EA\u80FD\u662F plan-key\u3001cli-login \u6216 none`);
|
|
238
|
+
profile.subscription = entry.subscription;
|
|
239
|
+
}
|
|
240
|
+
if (entry.apiRoute !== void 0) {
|
|
241
|
+
const target = entry.apiRoute;
|
|
242
|
+
if (!target || typeof target !== "object" || Array.isArray(target) || Object.keys(target).some((key) => key !== "provider" && key !== "model")) {
|
|
243
|
+
throw new Error(`${label} \u7684 apiRoute \u5FC5\u987B\u662F { provider, model }`);
|
|
244
|
+
}
|
|
245
|
+
const apiProvider = id(target.provider);
|
|
246
|
+
const apiModel = id(target.model);
|
|
247
|
+
if (!apiProvider || !apiModel || apiProvider.length > 160 || apiModel.length > 240) throw new Error(`${label} \u7684 apiRoute \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
|
|
248
|
+
if (profile.subscription !== "plan-key") throw new Error(`${label} \u53EA\u6709 subscription \u4E3A plan-key \u65F6\u624D\u80FD\u8BBE\u7F6E apiRoute`);
|
|
249
|
+
if (apiProvider === provider && apiModel === model) throw new Error(`${label} \u7684 apiRoute \u4E0D\u80FD\u6307\u5411\u81EA\u5DF1`);
|
|
250
|
+
profile.apiRoute = { provider: apiProvider, model: apiModel };
|
|
251
|
+
}
|
|
252
|
+
if (Object.keys(profile).length === 2) throw new Error(`${label} \u81F3\u5C11\u63D0\u4F9B quality\u3001pricing\u3001specialties\u3001cliModel\u3001execution\u3001billing \u6216 subscription \u4E4B\u4E00`);
|
|
253
|
+
return profile;
|
|
254
|
+
}
|
|
255
|
+
function normalizeExecutionPreference(value) {
|
|
256
|
+
if (value === void 0 || value === null || value === "" || value === "auto") return "auto";
|
|
257
|
+
if (value === "official" || value === "api") return value;
|
|
258
|
+
throw new TypeError("execution must be auto, official, or api");
|
|
259
|
+
}
|
|
260
|
+
function parseModelProfilesJson(value) {
|
|
261
|
+
const source = value == null || value === "" ? "[]" : value;
|
|
262
|
+
if (typeof source !== "string" || source.length > MAX_TEXT) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u8D85\u8FC7 32000 \u5B57\u7B26\u4E0A\u9650");
|
|
263
|
+
let input;
|
|
264
|
+
try {
|
|
265
|
+
input = JSON.parse(source);
|
|
266
|
+
} catch {
|
|
267
|
+
throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u4E0D\u662F\u6709\u6548\u7684 JSON");
|
|
268
|
+
}
|
|
269
|
+
if (!Array.isArray(input) || input.length > MAX_PROFILES) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u5FC5\u987B\u662F\u6700\u591A 200 \u9879\u7684 JSON \u6570\u7EC4");
|
|
270
|
+
const profiles = input.map(normalizeProfile);
|
|
271
|
+
const seen = /* @__PURE__ */ new Set();
|
|
272
|
+
for (const profile of profiles) {
|
|
273
|
+
const key = `${profile.provider}\0${profile.model}`;
|
|
274
|
+
if (seen.has(key)) throw new Error(`\u6A21\u578B ${profile.provider}/${profile.model} \u91CD\u590D\u914D\u7F6E`);
|
|
275
|
+
seen.add(key);
|
|
276
|
+
}
|
|
277
|
+
return profiles;
|
|
278
|
+
}
|
|
279
|
+
function applyModelProfiles(routes, profiles) {
|
|
280
|
+
const input = Array.isArray(routes) ? routes : [];
|
|
281
|
+
const values = typeof profiles === "string" ? parseModelProfilesJson(profiles) : profiles;
|
|
282
|
+
if (!Array.isArray(values)) throw new Error("\u6A21\u578B\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
283
|
+
const byKey = new Map(values.map((profile) => [`${profile.provider}\0${profile.model}`, profile]));
|
|
284
|
+
return input.map((route) => {
|
|
285
|
+
const profile = byKey.get(`${route.provider}\0${route.model}`);
|
|
286
|
+
if (!profile) return route;
|
|
287
|
+
return {
|
|
288
|
+
...route,
|
|
289
|
+
...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
|
|
290
|
+
...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
|
|
291
|
+
...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
|
|
292
|
+
...profile.cliModel === void 0 ? {} : { cliModel: profile.cliModel },
|
|
293
|
+
...profile.execution === void 0 ? {} : { execution: profile.execution },
|
|
294
|
+
...profile.billing === void 0 ? {} : { billing: profile.billing },
|
|
295
|
+
...profile.subscription === void 0 ? {} : { subscription: profile.subscription },
|
|
296
|
+
...profile.apiRoute === void 0 ? {} : { apiRoute: { ...profile.apiRoute } }
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// .dsh-plugin/shared/subscription-billing.mjs
|
|
302
|
+
var BILLING_MODES = Object.freeze(["subscription-first", "api-only", "subscription-only"]);
|
|
303
|
+
var DEFAULT_BILLING_MODE = "subscription-first";
|
|
304
|
+
var MAX_COOLDOWN_MS = 31 * 24 * 60 * 6e4;
|
|
305
|
+
var BILLING_MODE_LABEL = Object.freeze({
|
|
306
|
+
"subscription-first": "\u8BA2\u9605\u4F18\u5148",
|
|
307
|
+
"api-only": "\u53EA\u7528 API Key",
|
|
308
|
+
"subscription-only": "\u53EA\u7528\u8BA2\u9605"
|
|
309
|
+
});
|
|
310
|
+
var VENDOR_QUOTA_PATTERNS = Object.freeze({
|
|
311
|
+
"claude-code": Object.freeze([
|
|
312
|
+
{ kind: "quota", source: "code.claude.com/docs/en/errors", re: /you['’]?ve hit your [a-z0-9 -]{0,24}limit/i },
|
|
313
|
+
{ kind: "quota", source: "\u65E7\u7248 CLI \u6587\u672C\uFF08\u672A\u6838\u9A8C\uFF09", re: /claude ai usage limit reached|usage limit reached\|\d{10}/i },
|
|
314
|
+
{ kind: "rate-limit", source: "code.claude.com/docs/en/errors", re: /server is temporarily limiting requests|request rejected \(429\)/i }
|
|
315
|
+
]),
|
|
316
|
+
codex: Object.freeze([
|
|
317
|
+
{ kind: "quota", source: "Codex usage_limit_reached\uFF08\u793E\u533A\u8BB0\u5F55\u7684 API \u8FD4\u56DE\uFF09", re: /usage_limit_reached|you['’]?ve hit your usage limit|usage limit has been reached/i },
|
|
318
|
+
{ kind: "rate-limit", source: "OpenAI 429\uFF08\u901A\u7528\uFF09", re: /rate_limit_exceeded|rate limit reached for/i }
|
|
319
|
+
]),
|
|
320
|
+
gemini: Object.freeze([
|
|
321
|
+
{ kind: "quota", source: "Google API RESOURCE_EXHAUSTED", re: /RESOURCE_EXHAUSTED|quota exceeded|exhausted your (?:daily )?quota|usage limit reached for all/i }
|
|
322
|
+
]),
|
|
323
|
+
"kimi-code": Object.freeze([
|
|
324
|
+
{ kind: "quota", source: "kimi.com/code/docs \u9519\u8BEF\u53C2\u8003", re: /you['’]?ve reached your (?:5-hour|weekly \(7-day\)|monthly) usage limit/i },
|
|
325
|
+
{ kind: "rate-limit", source: "kimi.com/code/docs \u9519\u8BEF\u53C2\u8003", re: /you['’]?ve reached your concurrent request limit|we['’]?re receiving too many requests|engine is currently overloaded/i }
|
|
326
|
+
]),
|
|
327
|
+
"minimax-code": Object.freeze([
|
|
328
|
+
{ kind: "quota", source: "platform.minimax.io \u9519\u8BEF\u7801 2056", re: /(?:\bcode\b["'\s:=]*|\[)2056\b|usage limit exceeded|token plan usage limit reached/i },
|
|
329
|
+
{ kind: "rate-limit", source: "platform.minimax.io \u9519\u8BEF\u7801 2045", re: /(?:\bcode\b["'\s:=]*|\[)2045\b|rate growth limit/i }
|
|
330
|
+
]),
|
|
331
|
+
zcode: Object.freeze([
|
|
332
|
+
{ kind: "quota", source: "docs.z.ai / docs.bigmodel.cn \u9519\u8BEF\u7801 1308\u20131321", re: /(?:\bcode\b["'\s:=]*|\[)13(?:08|09|10|1[6-9]|2[01])\b|usage limit reached for|weekly\/monthly limit exhausted|已达到.{0,20}使用上限|套餐已到期|每周\/每月使用上限/i },
|
|
333
|
+
{ kind: "rate-limit", source: "docs.z.ai \u9519\u8BEF\u7801 1302/1305", re: /(?:\bcode\b["'\s:=]*|\[)130[25]\b|rate limit reached for requests|速率限制|访问量过大/i }
|
|
334
|
+
]),
|
|
335
|
+
"*": Object.freeze([
|
|
336
|
+
{ kind: "rate-limit", source: "HTTP 429\uFF08\u901A\u7528\uFF09", re: /\b429\b|too many requests|rate[_ ]limit/i }
|
|
337
|
+
])
|
|
338
|
+
});
|
|
339
|
+
var clean = (value) => typeof value === "string" ? value.trim() : "";
|
|
340
|
+
function parseQuotaPatterns(value) {
|
|
341
|
+
const patterns = {};
|
|
342
|
+
const errors = [];
|
|
343
|
+
if (value === void 0 || value === null || clean(value) === "") return { patterns, errors };
|
|
344
|
+
let input;
|
|
345
|
+
try {
|
|
346
|
+
input = JSON.parse(value);
|
|
347
|
+
} catch {
|
|
348
|
+
return { patterns, errors: ["quotaPatternsJson \u4E0D\u662F\u6709\u6548\u7684 JSON"] };
|
|
349
|
+
}
|
|
350
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return { patterns, errors: ["quotaPatternsJson \u5FC5\u987B\u662F\u5BF9\u8C61"] };
|
|
351
|
+
for (const [key, entry] of Object.entries(input).slice(0, 40)) {
|
|
352
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
353
|
+
errors.push(`${key}: \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const list = [];
|
|
357
|
+
for (const [field2, kind] of [["quota", "quota"], ["rateLimit", "rate-limit"]]) {
|
|
358
|
+
const values = entry[field2];
|
|
359
|
+
if (values === void 0) continue;
|
|
360
|
+
if (!Array.isArray(values)) {
|
|
361
|
+
errors.push(`${key}.${field2}: \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
for (const source of values.slice(0, 20)) {
|
|
365
|
+
if (typeof source !== "string" || !source || source.length > 200) {
|
|
366
|
+
errors.push(`${key}.${field2}: \u6BCF\u9879\u5FC5\u987B\u662F 1\u2013200 \u5B57\u7B26\u7684\u6B63\u5219`);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
list.push({ kind, source: "\u7528\u6237\u914D\u7F6E", re: new RegExp(source, "i") });
|
|
371
|
+
} catch {
|
|
372
|
+
errors.push(`${key}.${field2}: \u65E0\u6548\u6B63\u5219 ${source.slice(0, 40)}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (list.length) patterns[key] = list;
|
|
377
|
+
}
|
|
378
|
+
return { patterns, errors };
|
|
379
|
+
}
|
|
380
|
+
var SUBSCRIPTION_STATE_LABEL = Object.freeze({
|
|
381
|
+
"logged-in": "\u5DF2\u767B\u5F55\u8BA2\u9605\u8D26\u53F7",
|
|
382
|
+
"plan-key": "\u7F16\u7A0B\u5957\u9910 Key \u8DEF\u7EBF",
|
|
383
|
+
"api-key-only": "\u4EC5 API Key \u767B\u5F55",
|
|
384
|
+
"logged-out": "\u672A\u767B\u5F55",
|
|
385
|
+
exhausted: "\u989D\u5EA6\u5DF2\u7528\u5C3D",
|
|
386
|
+
unknown: "\u767B\u5F55\u72B6\u6001\u672A\u77E5",
|
|
387
|
+
none: "\u65E0\u8BA2\u9605"
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// .dsh-plugin/shared/security-boundaries.mjs
|
|
391
|
+
var READ_ONLY_SCOPE = Object.freeze({
|
|
392
|
+
"claude-code": "\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A\uFF08\u4EC5 Read/Glob/Grep \u5DE5\u5177\uFF0CdontAsk \u6A21\u5F0F\u62D2\u7EDD\u672A\u6388\u6743\u7684\u5DE5\u4F5C\u533A\u5916\u8BFB\u53D6\uFF09",
|
|
393
|
+
codex: "\u5F53\u524D\u7528\u6237\u53EF\u8BFB\u7684\u5168\u90E8\u6587\u4EF6\uFF08Codex read-only \u6C99\u7BB1\u53EA\u7981\u6B62\u5199\u5165\u548C\u8054\u7F51\u547D\u4EE4\uFF09",
|
|
394
|
+
gemini: "\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A\uFF08Gemini CLI \u9ED8\u8BA4\u5DE5\u4F5C\u533A\u9650\u5236\uFF09"
|
|
395
|
+
});
|
|
396
|
+
function toolBoundary(toolId, { sandboxed = false, platform = "unknown" } = {}) {
|
|
397
|
+
const directOnly = toolId === "gemini";
|
|
398
|
+
const harnessSandbox = sandboxed && !directOnly;
|
|
399
|
+
return {
|
|
400
|
+
toolId,
|
|
401
|
+
readOnly: {
|
|
402
|
+
readable: READ_ONLY_SCOPE[toolId] ?? "\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A",
|
|
403
|
+
writable: "\u65E0\uFF08\u53EA\u8BFB\u8FD0\u884C\uFF0C\u4E0D\u5E94\u7528\u4EFB\u4F55\u6539\u52A8\uFF09",
|
|
404
|
+
sandbox: harnessSandbox ? platform === "win32" ? "Harness \u8FDB\u7A0B\u6C99\u7BB1\uFF08Windows ACL \u540E\u7AEF\u4E3A\u90E8\u5206\u5F3A\u5236\uFF09" : "Harness \u8FDB\u7A0B\u6C99\u7BB1" : "\u65E0 Harness \u6C99\u7BB1\uFF1A\u76F4\u63A5\u542F\u52A8 CLI\uFF0C\u53EA\u8BFB\u4EC5\u7531 CLI \u81EA\u8EAB\u53C2\u6570\u4FDD\u8BC1",
|
|
405
|
+
direct: !harnessSandbox
|
|
406
|
+
},
|
|
407
|
+
write: toolId === "gemini" ? null : {
|
|
408
|
+
readable: "\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\uFF08\u5F53\u524D\u4ED3\u5E93\u7684\u5E72\u51C0\u526F\u672C\uFF09",
|
|
409
|
+
writable: "\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\uFF1BCLI \u6210\u529F\u4E14\u539F\u5DE5\u4F5C\u533A\u672A\u53D8\u52A8\u65F6\uFF0C\u624D\u628A\u6E90\u4EE3\u7801\u8865\u4E01\u5E94\u7528\u56DE\u5F53\u524D\u5DE5\u4F5C\u533A",
|
|
410
|
+
sandbox: "Harness \u8FDB\u7A0B\u6C99\u7BB1\uFF0C\u7F3A\u5C11\u6C99\u7BB1\u65F6\u62D2\u7EDD\u542F\u52A8",
|
|
411
|
+
requiresApproval: true
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// .dsh-plugin/client/router-main.jsx
|
|
417
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
418
|
+
|
|
419
|
+
// .dsh-plugin/shared/routing-presets.mjs
|
|
420
|
+
var DEFAULT_ROUTING_PRESET = "balanced";
|
|
421
|
+
var ROUTING_PRESETS = Object.freeze({
|
|
422
|
+
economy: Object.freeze({
|
|
423
|
+
id: "economy",
|
|
424
|
+
label: "\u7701\u94B1\u4F18\u5148",
|
|
425
|
+
description: "\u66F4\u770B\u91CD\u5355\u4EF7\uFF0C\u5141\u8BB8\u8D28\u91CF\u7565\u4F4E\u7684\u6A21\u578B\u627F\u62C5\u7B80\u5355\u548C\u4E2D\u7B49\u4EFB\u52A1\u3002",
|
|
426
|
+
floorDelta: -0.04,
|
|
427
|
+
tilt: Object.freeze({ quality: 0.75, cost: 1.6, latency: 1.1 })
|
|
428
|
+
}),
|
|
429
|
+
balanced: Object.freeze({
|
|
430
|
+
id: "balanced",
|
|
431
|
+
label: "\u5747\u8861",
|
|
432
|
+
description: "\u9ED8\u8BA4\uFF1A\u6309\u4EFB\u52A1\u96BE\u5EA6\u5E73\u8861\u8D28\u91CF\u3001\u6210\u672C\u548C\u901F\u5EA6\u3002",
|
|
433
|
+
floorDelta: 0,
|
|
434
|
+
tilt: Object.freeze({})
|
|
435
|
+
}),
|
|
436
|
+
quality: Object.freeze({
|
|
437
|
+
id: "quality",
|
|
438
|
+
label: "\u6548\u679C\u4F18\u5148",
|
|
439
|
+
description: "\u66F4\u770B\u91CD\u8D28\u91CF\uFF0C\u63D0\u9AD8\u66FF\u4EE3\u6A21\u578B\u5FC5\u987B\u8FBE\u5230\u7684\u8D28\u91CF\u95E8\u69DB\u3002",
|
|
440
|
+
floorDelta: 0.04,
|
|
441
|
+
tilt: Object.freeze({ quality: 1.35, cost: 0.5, specialty: 1.2, reasoning: 1.2 })
|
|
442
|
+
})
|
|
443
|
+
});
|
|
444
|
+
function normalizeRoutingPreset(value) {
|
|
445
|
+
return Object.hasOwn(ROUTING_PRESETS, value) ? value : DEFAULT_ROUTING_PRESET;
|
|
446
|
+
}
|
|
447
|
+
function routingPreset(value) {
|
|
448
|
+
return ROUTING_PRESETS[normalizeRoutingPreset(value)];
|
|
449
|
+
}
|
|
450
|
+
function presetWeights(weights, preset) {
|
|
451
|
+
const { tilt } = routingPreset(preset);
|
|
452
|
+
if (!weights || Object.keys(tilt).length === 0) return weights;
|
|
453
|
+
const total = Object.values(weights).reduce((sum, value) => sum + value, 0);
|
|
454
|
+
const tilted = Object.fromEntries(Object.entries(weights).map(([key, value]) => [key, value * (tilt[key] ?? 1)]));
|
|
455
|
+
const tiltedTotal = Object.values(tilted).reduce((sum, value) => sum + value, 0);
|
|
456
|
+
return Object.freeze(Object.fromEntries(Object.entries(tilted).map(([key, value]) => [key, value * total / tiltedTotal])));
|
|
457
|
+
}
|
|
458
|
+
function presetFloor(floor, preset) {
|
|
459
|
+
const value = Number(floor) + routingPreset(preset).floorDelta;
|
|
460
|
+
return Math.max(0.5, Math.min(0.97, value));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// .dsh-plugin/shared/livebench.mjs
|
|
464
|
+
var TASK_ALIASES = Object.freeze({
|
|
465
|
+
reasoning: ["reasoning", "reasoning_score", "hard_reasoning"],
|
|
466
|
+
code: ["code", "coding", "coding_score"],
|
|
467
|
+
math: ["math", "mathematics", "math_score"],
|
|
468
|
+
research: ["research", "retrieval", "knowledge", "data_analysis"],
|
|
469
|
+
writing: ["writing", "creative_writing", "language"],
|
|
470
|
+
vision: ["vision", "multimodal", "visual"],
|
|
471
|
+
summarization: ["summarization", "summary", "if"],
|
|
472
|
+
classification: ["classification", "instruction_following"]
|
|
473
|
+
});
|
|
474
|
+
var CATEGORY_TO_TASK = Object.freeze({
|
|
475
|
+
reasoning: "reasoning",
|
|
476
|
+
coding: "code",
|
|
477
|
+
"agentic coding": "code",
|
|
478
|
+
mathematics: "math",
|
|
479
|
+
"data analysis": "research",
|
|
480
|
+
language: "writing",
|
|
481
|
+
if: "summarization",
|
|
482
|
+
vision: "vision",
|
|
483
|
+
multimodal: "vision"
|
|
484
|
+
});
|
|
485
|
+
function normalized(value) {
|
|
486
|
+
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
487
|
+
}
|
|
488
|
+
function liveBenchRow(snapshot, model) {
|
|
489
|
+
return snapshot?.models?.[normalized(model)] ?? null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// .dsh-plugin/shared/router.mjs
|
|
493
|
+
var OBJECTIVE_WEIGHTS = Object.freeze({
|
|
494
|
+
simple: Object.freeze({ quality: 0.28, cost: 0.45, latency: 0.14, specialty: 0.04, reasoning: 0.07, risk: 0.02 }),
|
|
495
|
+
balanced: Object.freeze({ quality: 0.4, cost: 0.26, latency: 0.1, specialty: 0.09, reasoning: 0.1, risk: 0.05 }),
|
|
496
|
+
complex: Object.freeze({ quality: 0.48, cost: 0.14, latency: 0.06, specialty: 0.14, reasoning: 0.11, risk: 0.07 })
|
|
497
|
+
});
|
|
498
|
+
var REASONING_EFFORT_ORDER = Object.freeze(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
499
|
+
var REASONING_EFFORT_MULTIPLIERS = Object.freeze({
|
|
500
|
+
off: Object.freeze({ output: 0.78, latency: 0.75 }),
|
|
501
|
+
minimal: Object.freeze({ output: 0.86, latency: 0.82 }),
|
|
502
|
+
low: Object.freeze({ output: 0.93, latency: 0.9 }),
|
|
503
|
+
medium: Object.freeze({ output: 1, latency: 1 }),
|
|
504
|
+
high: Object.freeze({ output: 1.16, latency: 1.15 }),
|
|
505
|
+
xhigh: Object.freeze({ output: 1.34, latency: 1.3 }),
|
|
506
|
+
max: Object.freeze({ output: 1.58, latency: 1.5 })
|
|
507
|
+
});
|
|
508
|
+
var QUALITY_FLOORS = Object.freeze({ simple: 0.75, balanced: 0.78, complex: 0.82 });
|
|
509
|
+
var DEFAULT_ROUTER_SETTINGS = Object.freeze({
|
|
510
|
+
pricing: Object.freeze({}),
|
|
511
|
+
// The official site publishes versioned table/categories assets and the
|
|
512
|
+
// adapter discovers the newest release from this root URL.
|
|
513
|
+
liveBenchEndpoint: "https://livebench.ai",
|
|
514
|
+
liveBenchTtlMs: 9e5,
|
|
515
|
+
budgetUsd: 0,
|
|
516
|
+
cacheReadRatio: 0,
|
|
517
|
+
cacheWriteRatio: 0
|
|
518
|
+
});
|
|
519
|
+
var MODEL_CATALOG = Object.freeze([
|
|
520
|
+
{ id: "claude-fable-5", aliases: ["claude-fable-5", "claude fable 5"], quality: 0.99, latency: 0.34, costIn: 10, costOut: 50, specialties: ["reasoning", "writing", "research"], risk: 0.06 },
|
|
521
|
+
{ id: "claude-opus-4-8", aliases: ["claude-opus-4-8", "claude opus 4.8"], quality: 0.97, latency: 0.39, costIn: 5, costOut: 25, specialties: ["reasoning", "writing", "code"], risk: 0.07 },
|
|
522
|
+
{ id: "gpt-5.6-sol", aliases: ["gpt-5.6-sol", "gpt 5.6 sol"], quality: 0.98, latency: 0.4, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math", "vision"], risk: 0.06 },
|
|
523
|
+
{ id: "gpt-5.5", aliases: ["gpt-5.5", "gpt 5.5"], quality: 0.95, latency: 0.44, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math"], risk: 0.08 },
|
|
524
|
+
{ id: "deepseek-v4-pro", aliases: ["deepseek-v4-pro", "deepseek v4 pro"], quality: 0.93, latency: 0.52, costIn: 1.74, costOut: 3.48, specialties: ["code", "math", "reasoning"], risk: 0.1 },
|
|
525
|
+
{ id: "deepseek-v4-flash", aliases: ["deepseek-v4-flash", "deepseek v4 flash"], quality: 0.82, latency: 0.82, costIn: 0.14, costOut: 0.28, specialties: ["code", "summarization", "classification"], risk: 0.14 },
|
|
526
|
+
{ id: "kimi-k3", aliases: ["kimi-k3", "kimi k3"], quality: 0.91, latency: 0.56, costIn: 3, costOut: 15, specialties: ["reasoning", "long-context", "code"], risk: 0.1 },
|
|
527
|
+
{ id: "qwen3.7-max", aliases: ["qwen3.7-max", "qwen 3.7 max"], quality: 0.94, latency: 0.5, costIn: 2.5, costOut: 7.5, specialties: ["reasoning", "math", "code"], risk: 0.08 },
|
|
528
|
+
{ id: "qwen3.7-plus", aliases: ["qwen3.7-plus", "qwen 3.7 plus"], quality: 0.87, latency: 0.72, costIn: 0.4, costOut: 1.6, specialties: ["code", "math", "writing"], risk: 0.12 },
|
|
529
|
+
{ id: "glm-5.2", aliases: ["glm-5.2", "glm 5.2"], quality: 0.89, latency: 0.64, costIn: 1.4, costOut: 4.4, specialties: ["reasoning", "writing", "math"], risk: 0.11 },
|
|
530
|
+
{ id: "gpt-5.6-luna", aliases: ["gpt-5.6-luna", "gpt 5.6 luna"], quality: 0.84, latency: 0.86, costIn: 0.2, costOut: 1.2, specialties: ["classification", "summarization", "code"], risk: 0.14 },
|
|
531
|
+
{ id: "gpt-5.6-terra", aliases: ["gpt-5.6-terra", "gpt 5.6 terra"], quality: 0.91, latency: 0.66, costIn: 2, costOut: 12, specialties: ["code", "writing", "reasoning"], risk: 0.1 },
|
|
532
|
+
{ id: "minimax-m3", aliases: ["minimax-m3", "minimax m3"], quality: 0.86, latency: 0.69, costIn: 0.3, costOut: 1.2, specialties: ["writing", "code", "summarization"], risk: 0.13 },
|
|
533
|
+
{ id: "gemini-3-flash", aliases: ["gemini 3 flash", "gemini-3-flash"], quality: 0.88, latency: 0.73, costIn: 0.5, costOut: 3, specialties: ["vision", "research", "summarization"], risk: 0.12 },
|
|
534
|
+
{ id: "big-pickle", aliases: ["big pickle"], quality: 0.7, latency: 0.88, costIn: 0, costOut: 0, specialties: ["classification", "summarization"], risk: 0.24 }
|
|
535
|
+
]);
|
|
536
|
+
var clamp = (value, min = 0, max = 1) => Math.max(min, Math.min(max, value));
|
|
537
|
+
var normalize = (value) => String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
538
|
+
var routeKey = (provider, model) => `${String(provider ?? "")}/${String(model ?? "")}`;
|
|
539
|
+
var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
|
|
540
|
+
"opencode",
|
|
541
|
+
"opencode-go",
|
|
542
|
+
// Some OpenCode-compatible configuration examples use the product name as
|
|
543
|
+
// the route id. Treat those aliases as catalog routes too; the pi-ai catalog
|
|
544
|
+
// still owns the actual model endpoints.
|
|
545
|
+
"opencode-zen",
|
|
546
|
+
"opencode-go-zen"
|
|
547
|
+
]);
|
|
548
|
+
function classifyTask(text4) {
|
|
549
|
+
const value = String(text4 ?? "");
|
|
550
|
+
if (value.length < 80 && /翻译|解释|translate|explain/i.test(value)) return "general";
|
|
551
|
+
return detectTaskTypes(value)[0] ?? "general";
|
|
552
|
+
}
|
|
553
|
+
var TASK_TYPE_RULES = Object.freeze([
|
|
554
|
+
["vision", /图片|图像|照片|视觉|image|vision|截图|识图/i],
|
|
555
|
+
["math", /数学|证明|定理|公式|方程|math|proof|theorem/i],
|
|
556
|
+
["code", /代码|编程|工程|项目|架构|接口|api|debug|实现|部署|测试|code/i],
|
|
557
|
+
["research", /研究|论文|文献|联网|检索|research|source|引用/i],
|
|
558
|
+
["summarization", /总结|摘要|提炼|提取|关键词|分类|翻译|summar|classif|extract/i],
|
|
559
|
+
["writing", /写作|润色|小说|文案|报告|writing|draft/i]
|
|
560
|
+
]);
|
|
561
|
+
var TASK_TYPE_LABELS = Object.freeze({
|
|
562
|
+
vision: "\u89C6\u89C9\u5904\u7406",
|
|
563
|
+
math: "\u6570\u5B66\u63A8\u5BFC",
|
|
564
|
+
code: "\u5DE5\u7A0B\u4E0E\u4EE3\u7801",
|
|
565
|
+
research: "\u7814\u7A76\u4E0E\u68C0\u7D22",
|
|
566
|
+
summarization: "\u6458\u8981\u4E0E\u6574\u7406",
|
|
567
|
+
writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
|
|
568
|
+
});
|
|
569
|
+
function detectTaskTypes(text4) {
|
|
570
|
+
const value = String(text4 ?? "");
|
|
571
|
+
const ranked = TASK_TYPE_RULES.map(([type, pattern]) => ({
|
|
572
|
+
type,
|
|
573
|
+
signals: value.match(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`))?.length ?? 0
|
|
574
|
+
})).filter((item) => item.signals > 0);
|
|
575
|
+
ranked.sort((left, right) => right.signals - left.signals || left.type.localeCompare(right.type));
|
|
576
|
+
return ranked.map((item) => item.type);
|
|
577
|
+
}
|
|
578
|
+
function assessComplexity(text4) {
|
|
579
|
+
const value = String(text4 ?? "");
|
|
580
|
+
const lengthScore = clamp(value.length / 2200);
|
|
581
|
+
const requirementScore = clamp((value.match(/(?:^|\n)\s*(?:[-*]|\d+[.)]|[一二三四五六七八九十]+[、.])/g) ?? []).length / 8);
|
|
582
|
+
const codeScore = /(代码|工程|架构|接口|实现|部署|测试|code|api|debug)/i.test(value) ? 0.22 : 0;
|
|
583
|
+
const highReasoningScore = /(数学|证明|定理|研究|论文|复杂|多步骤|约束|比较|评估|架构|模块|部署|math|proof|research)/i.test(value) ? 0.2 : 0;
|
|
584
|
+
const visionScore = /(图片|图像|照片|截图|视觉|image|vision)/i.test(value) ? 0.12 : 0;
|
|
585
|
+
const domainMarkers = (value.match(/代码|工程|架构|接口|实现|部署|测试|模块|拆分|约束|评估|证明|定理|研究|论文|图片|图像|照片|视觉|code|api|debug|proof|research|vision/gi) ?? []).length;
|
|
586
|
+
const domainComplexity = clamp(domainMarkers / 5) * 0.28;
|
|
587
|
+
const raw = clamp(0.1 + lengthScore * 0.3 + requirementScore * 0.18 + domainComplexity + codeScore + highReasoningScore + visionScore);
|
|
588
|
+
const band = isHardRequirement(value) ? "complex" : isSimpleRequirement(value) && value.length <= 180 ? "simple" : raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
|
|
589
|
+
return { value: raw, band };
|
|
590
|
+
}
|
|
591
|
+
function specialtyMatch(model, taskType, liveScores = {}) {
|
|
592
|
+
const benchmark = asScore(liveScores?.[taskType]);
|
|
593
|
+
if (benchmark !== void 0) return benchmark;
|
|
594
|
+
if (model.specialties.includes(taskType)) return 1;
|
|
595
|
+
if (taskType === "general") return 0.58;
|
|
596
|
+
if (taskType === "research" && model.specialties.includes("writing")) return 0.68;
|
|
597
|
+
if (taskType === "writing" && model.specialties.includes("reasoning")) return 0.62;
|
|
598
|
+
return 0.38;
|
|
599
|
+
}
|
|
600
|
+
function qualityForTask(row, taskType) {
|
|
601
|
+
const base = asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.quality ?? 0;
|
|
602
|
+
return row?.qualityBias ? clamp(base + row.qualityBias) : base;
|
|
603
|
+
}
|
|
604
|
+
function specialtyForTask(row, taskType) {
|
|
605
|
+
return specialtyMatch(row.metadata, taskType, row.liveScores);
|
|
606
|
+
}
|
|
607
|
+
function asScore(value) {
|
|
608
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
609
|
+
const number = Number(value);
|
|
610
|
+
if (!Number.isFinite(number)) return void 0;
|
|
611
|
+
return clamp(number > 1 ? number / 100 : number);
|
|
612
|
+
}
|
|
613
|
+
function normalizePricing(pricing) {
|
|
614
|
+
if (pricing === null || typeof pricing !== "object" || Array.isArray(pricing)) return {};
|
|
615
|
+
const normalized2 = {};
|
|
616
|
+
for (const [id2, raw] of Object.entries(pricing)) {
|
|
617
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
618
|
+
if ((raw.input ?? raw.costIn) === null || (raw.input ?? raw.costIn) === void 0) continue;
|
|
619
|
+
if ((raw.output ?? raw.costOut) === null || (raw.output ?? raw.costOut) === void 0) continue;
|
|
620
|
+
const input = Number(raw.input ?? raw.costIn);
|
|
621
|
+
const output = Number(raw.output ?? raw.costOut);
|
|
622
|
+
const cacheRead = Number(raw.cacheRead ?? input);
|
|
623
|
+
const cacheWrite = Number(raw.cacheWrite ?? input);
|
|
624
|
+
if (![input, output, cacheRead, cacheWrite].every((value) => Number.isFinite(value) && value >= 0)) continue;
|
|
625
|
+
if (String(raw.currency ?? "USD").toUpperCase() !== "USD") continue;
|
|
626
|
+
normalized2[normalize(id2)] = {
|
|
627
|
+
input,
|
|
628
|
+
output,
|
|
629
|
+
cacheRead,
|
|
630
|
+
cacheWrite,
|
|
631
|
+
currency: String(raw.currency ?? "USD").toUpperCase()
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return normalized2;
|
|
635
|
+
}
|
|
636
|
+
function pricingFor(model, pricing, provider = "") {
|
|
637
|
+
const normalizedPricing = normalizePricing(pricing);
|
|
638
|
+
const providerOverride = provider === "" ? void 0 : normalizedPricing[normalize(`${provider}/${model.id}`)];
|
|
639
|
+
const override = providerOverride ?? normalizedPricing[normalize(model.id)];
|
|
640
|
+
if (override) return override;
|
|
641
|
+
if (!Number.isFinite(Number(model.costIn)) || !Number.isFinite(Number(model.costOut)) || model.costIn === null || model.costIn === void 0 || model.costOut === null || model.costOut === void 0) return null;
|
|
642
|
+
return {
|
|
643
|
+
input: Number(model.costIn),
|
|
644
|
+
output: Number(model.costOut),
|
|
645
|
+
cacheRead: Number(model.cacheRead ?? model.costIn),
|
|
646
|
+
cacheWrite: Number(model.cacheWrite ?? model.costIn),
|
|
647
|
+
currency: "USD"
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function normalizedCacheRatios(cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
651
|
+
const read = Number.isFinite(Number(cacheReadRatio)) ? clamp(Number(cacheReadRatio)) : 0;
|
|
652
|
+
const write = Number.isFinite(Number(cacheWriteRatio)) ? Math.min(clamp(Number(cacheWriteRatio)), 1 - read) : 0;
|
|
653
|
+
return { read, write };
|
|
654
|
+
}
|
|
655
|
+
function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
656
|
+
const { read, write } = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
657
|
+
return {
|
|
658
|
+
input: (1 - read - write) * Number(pricing.input) + read * Number(pricing.cacheRead) + write * Number(pricing.cacheWrite),
|
|
659
|
+
output: Number(pricing.output)
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
663
|
+
if (pricing === null) return 0;
|
|
664
|
+
const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
|
|
665
|
+
const mean = (effective.input + effective.output) / 2;
|
|
666
|
+
if (maxCost <= 0) return mean === 0 ? 1 : 0;
|
|
667
|
+
return clamp(1 - mean / maxCost);
|
|
668
|
+
}
|
|
669
|
+
function estimateCost(model, text4, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
670
|
+
const pricing = pricingFor(model, pricingOverrides);
|
|
671
|
+
if (pricing === null) return null;
|
|
672
|
+
const inputTokens = Math.max(80, Math.ceil(String(text4 ?? "").length / 3.7));
|
|
673
|
+
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
674
|
+
const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
|
|
675
|
+
const cacheWriteTokens = Math.min(inputTokens - cacheReadTokens, Math.max(0, Math.round(inputTokens * ratios.write)));
|
|
676
|
+
const billableInputTokens = inputTokens - cacheReadTokens - cacheWriteTokens;
|
|
677
|
+
return (billableInputTokens * pricing.input + cacheReadTokens * pricing.cacheRead + cacheWriteTokens * pricing.cacheWrite + outputTokens * pricing.output) / 1e6;
|
|
678
|
+
}
|
|
679
|
+
function modelMetadata(name) {
|
|
680
|
+
const key = normalize(name);
|
|
681
|
+
if (!key) return null;
|
|
682
|
+
return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
|
|
683
|
+
}
|
|
684
|
+
function taskTokenBudget(text4, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
685
|
+
const inputTokens = Math.max(80, Math.ceil(String(text4 ?? "").length / 3.7));
|
|
686
|
+
const multipliers = {
|
|
687
|
+
analysis: { input: 0.9, output: 0.55 },
|
|
688
|
+
execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
|
|
689
|
+
verification: { input: 1.15, output: 0.7 },
|
|
690
|
+
synthesis: { input: 1.65, output: 1.3 }
|
|
691
|
+
};
|
|
692
|
+
const multiplier = multipliers[task.purpose] ?? { input: 1, output: 1 };
|
|
693
|
+
const effortMultiplier = reasoningEffortMultiplier(task.reasoningEffort);
|
|
694
|
+
const totalInputTokens = Math.max(80, Math.round(inputTokens * multiplier.input));
|
|
695
|
+
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
696
|
+
const cacheReadTokens = Math.min(totalInputTokens, Math.max(0, Math.round(totalInputTokens * ratios.read)));
|
|
697
|
+
const cacheWriteTokens = Math.min(totalInputTokens - cacheReadTokens, Math.max(0, Math.round(totalInputTokens * ratios.write)));
|
|
698
|
+
return {
|
|
699
|
+
inputTokens: totalInputTokens,
|
|
700
|
+
cacheReadTokens,
|
|
701
|
+
cacheWriteTokens,
|
|
702
|
+
outputTokens: Math.max(220, Math.round(900 * multiplier.output * effortMultiplier.output))
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function taskQualityFloor(band, task) {
|
|
706
|
+
const difficulty = task.difficulty ?? band;
|
|
707
|
+
const base = QUALITY_FLOORS[difficulty] ?? QUALITY_FLOORS.balanced;
|
|
708
|
+
if (task.purpose === "synthesis") return Math.max(base, band === "complex" ? 0.84 : base);
|
|
709
|
+
if (difficulty !== "complex") return base;
|
|
710
|
+
return clamp(base + Math.max(0, Number(task.criticality ?? 0.75) - 0.65) * 0.12);
|
|
711
|
+
}
|
|
712
|
+
var MAX_EXPLICIT_EXECUTION_PACKAGES = 6;
|
|
713
|
+
var REQUIREMENT_ACTION = /(?:实现|新增|添加|修复|更新|构建|设计|完成|编写|优化|接入|支持|配置|部署|测试|验证|检查|整理|创建|移除|替换|迁移|适配|开发|调研|安装|下载|上传|发布|生成|集成|改造|封装|显示|提供|允许|确保|处理|解决|分析|探测|检测|选择|分配|拆分|对接|加入|保留|记录|输出|审查|核对|对比|提取|摘要|总结|分类|翻译|格式化|润色|可以|能够|需要|进行|implement|add|fix|update|build|design|write|improve|support|configure|deploy|test|verify|check|create|remove|replace|migrate|install|publish|generate|integrate|review|extract|summarize|classify|translate|format|should|must|need)/i;
|
|
714
|
+
var HIGH_STAKES_WORK = /架构|安全|隐私|权限|并发|事务|迁移|生产|部署|发布|证明|定理|科研|论文|复杂|多步骤|跨系统|系统设计|architecture|security|migration|production|proof|theorem|research/i;
|
|
715
|
+
var SIMPLE_WORK = /翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread/i;
|
|
716
|
+
var HIGH_STAKES_ACTION = /设计|实现|修复|审计|测试|验证|证明|推导|部署|迁移|规划|研究|解决|推理|design|implement|fix|audit|verify|prove|derive|deploy|migrate|research|solve/i;
|
|
717
|
+
var SIMPLE_REQUEST_START = /^(?:请|帮我)?(?:翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread)/i;
|
|
718
|
+
function isSimpleRequirement(value) {
|
|
719
|
+
const item = String(value ?? "").trim();
|
|
720
|
+
return SIMPLE_REQUEST_START.test(item) && !/(?:并|然后|最后|同时|以及|and then).{0,20}(?:设计|实现|验证|部署|证明|审计|测试|design|implement|verify|deploy|prove|audit|test)/i.test(item);
|
|
721
|
+
}
|
|
722
|
+
function isHardRequirement(value) {
|
|
723
|
+
const item = String(value ?? "").trim();
|
|
724
|
+
if (isSimpleRequirement(item)) return false;
|
|
725
|
+
return HIGH_STAKES_WORK.test(item) && HIGH_STAKES_ACTION.test(item);
|
|
726
|
+
}
|
|
727
|
+
function requirementDifficulty(objective, type, fallback = "balanced") {
|
|
728
|
+
const value = String(objective ?? "").trim();
|
|
729
|
+
if (isHardRequirement(value)) return "complex";
|
|
730
|
+
if (value.length <= 180 && SIMPLE_WORK.test(value)) return "simple";
|
|
731
|
+
const assessed = assessComplexity(value);
|
|
732
|
+
if (type === "math" && /证明|推导|proof|derive/i.test(value)) return "complex";
|
|
733
|
+
if (assessed.band === "simple" && (type === "code" || type === "research")) return "balanced";
|
|
734
|
+
return assessed.band === "simple" ? "simple" : assessed.band === "complex" ? "complex" : fallback;
|
|
735
|
+
}
|
|
736
|
+
function shouldSplitRequirements(requirements) {
|
|
737
|
+
if (requirements.length >= 3) return true;
|
|
738
|
+
if (requirements.length !== 2) return false;
|
|
739
|
+
const types = requirements.map((item) => detectTaskTypes(item)[0] ?? "general");
|
|
740
|
+
return types[0] !== types[1] || /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(requirements[1]);
|
|
741
|
+
}
|
|
742
|
+
function requirementText(value) {
|
|
743
|
+
return String(value ?? "").trim().replace(/[。;;\s]+$/u, "").trim();
|
|
744
|
+
}
|
|
745
|
+
function looksLikeRequirement(value) {
|
|
746
|
+
const item = requirementText(value);
|
|
747
|
+
return item.length >= 3 && !/[::]$/u.test(item) && !/(?:以下|下列|如下)(?:的)?(?:任务|需求|工作|事项|要求)/u.test(item) && REQUIREMENT_ACTION.test(item.slice(0, 32));
|
|
748
|
+
}
|
|
749
|
+
function explicitRequirements(text4) {
|
|
750
|
+
const visibleLines = [];
|
|
751
|
+
let fence = null;
|
|
752
|
+
for (const line of String(text4 ?? "").split(/\r?\n/u)) {
|
|
753
|
+
const marker2 = /^\s*(`{3,}|~{3,})/u.exec(line);
|
|
754
|
+
if (fence) {
|
|
755
|
+
if (marker2 && marker2[1][0] === fence.char && marker2[1].length >= fence.length && !line.slice(marker2[0].length).trim()) fence = null;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
if (marker2) {
|
|
759
|
+
fence = { char: marker2[1][0], length: marker2[1].length };
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
visibleLines.push(line);
|
|
763
|
+
}
|
|
764
|
+
const value = visibleLines.join("\n");
|
|
765
|
+
const lines = visibleLines.map((line) => line.trim()).filter(Boolean);
|
|
766
|
+
if (/^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(lines[0] ?? "") && !/(?:执行|完成|实施|分配)/u.test(lines[0])) return [];
|
|
767
|
+
const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
|
|
768
|
+
const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
|
|
769
|
+
if (marked.length >= 2) return marked;
|
|
770
|
+
const plain = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
|
|
771
|
+
if (plain.length >= 2 && plain.every(looksLikeRequirement)) {
|
|
772
|
+
return plain.map(requirementText).filter((item) => item.length >= 3);
|
|
773
|
+
}
|
|
774
|
+
const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
|
|
775
|
+
if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
|
|
776
|
+
return clauses;
|
|
777
|
+
}
|
|
778
|
+
const sentences = value.split(/[。!?]/u).map(requirementText).filter(Boolean);
|
|
779
|
+
if (sentences.length >= 2 && sentences.every(looksLikeRequirement)) {
|
|
780
|
+
return sentences;
|
|
781
|
+
}
|
|
782
|
+
return [];
|
|
783
|
+
}
|
|
784
|
+
var CN_DIGITS = Object.freeze({ \u4E00: 1, \u4E8C: 2, \u4E24: 2, \u4E09: 3, \u56DB: 4, \u4E94: 5, \u516D: 6, \u4E03: 7, \u516B: 8, \u4E5D: 9, \u5341: 10 });
|
|
785
|
+
var stepNumber = (token) => {
|
|
786
|
+
const value = String(token ?? "").trim();
|
|
787
|
+
if (/^\d{1,2}$/u.test(value)) return Number(value);
|
|
788
|
+
if (/^十[一二三四五六七八九]?$/u.test(value)) return 10 + (CN_DIGITS[value[1]] ?? 0);
|
|
789
|
+
if (/^[一二两三四五六七八九]十?$/u.test(value)) return CN_DIGITS[value[0]] * (value.length === 2 ? 10 : 1);
|
|
790
|
+
return null;
|
|
791
|
+
};
|
|
792
|
+
var STEP_TOKEN = "(?:\\d{1,2}|[\u4E00\u4E8C\u4E24\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u5341]{1,2})";
|
|
793
|
+
var STEP_LIST = `${STEP_TOKEN}(?:\\s*(?:[\u3001,\uFF0C/]|\u548C|\u4E0E|\u53CA|\u4EE5\u53CA|and|&|-|~|\u5230|\u81F3)\\s*(?:\u7B2C\\s*)?${STEP_TOKEN})*`;
|
|
794
|
+
var STEP_REFERENCE_PATTERNS = Object.freeze([
|
|
795
|
+
// 依赖第 1 步 / 基于第 2、3 步 / 根据步骤 1 / 在第 2 步完成后 / 第 1 步之后
|
|
796
|
+
new RegExp(`(?:\u4F9D\u8D56|\u4F9D\u9760|\u53D6\u51B3\u4E8E|\u57FA\u4E8E|\u6839\u636E|\u627F\u63A5|\u4F7F\u7528|\u5229\u7528|\u9700\u8981|\u7B49\u5F85|\u5F85)(?:\u4E8E)?\\s*(?:\u7B2C\\s*(${STEP_LIST})\\s*(?:\u6B65|\u9879|\u4E2A?\u6B65\u9AA4|\u6761)|\u6B65\u9AA4\\s*(${STEP_LIST}))`, "giu"),
|
|
797
|
+
new RegExp(`(?:\u5728|\u7B49)?\\s*\u7B2C\\s*(${STEP_LIST})\\s*(?:\u6B65|\u9879|\u4E2A?\u6B65\u9AA4|\u6761)(?:\u5B8C\u6210|\u7ED3\u675F|\u505A\u5B8C)?(?:\u4E4B\u540E|\u4EE5\u540E|\u540E)`, "giu"),
|
|
798
|
+
new RegExp(`\u6B65\u9AA4\\s*(${STEP_LIST})\\s*(?:\u5B8C\u6210|\u7ED3\u675F)?(?:\u4E4B\u540E|\u4EE5\u540E|\u540E)`, "giu"),
|
|
799
|
+
// depends on step 1 / after steps 1 and 2 / based on step #2 / requires step 3
|
|
800
|
+
new RegExp(`(?:depends?\\s+on|depending\\s+on|after|based\\s+on|builds?\\s+on|requires?|using(?:\\s+the\\s+output\\s+of)?)\\s+(?:the\\s+(?:result|output)s?\\s+of\\s+)?(?:steps?|items?|#)\\s*#?(${STEP_LIST})`, "giu")
|
|
801
|
+
]);
|
|
802
|
+
function explicitStepReferences(objective) {
|
|
803
|
+
const found = /* @__PURE__ */ new Set();
|
|
804
|
+
const value = String(objective ?? "");
|
|
805
|
+
for (const pattern of STEP_REFERENCE_PATTERNS) {
|
|
806
|
+
pattern.lastIndex = 0;
|
|
807
|
+
for (const match of value.matchAll(pattern)) {
|
|
808
|
+
const list = match.slice(1).find(Boolean) ?? "";
|
|
809
|
+
const parts = list.split(/\s*(?:[、,,/]|和|与|及|以及|and|&)\s*/iu);
|
|
810
|
+
for (const part of parts) {
|
|
811
|
+
const range = /^(.+?)\s*(?:-|~|到|至)\s*(?:第\s*)?(.+)$/u.exec(part);
|
|
812
|
+
if (range) {
|
|
813
|
+
const from = stepNumber(range[1]);
|
|
814
|
+
const to = stepNumber(range[2]);
|
|
815
|
+
if (from && to && to >= from && to - from < 20) for (let step2 = from; step2 <= to; step2 += 1) found.add(step2);
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
const step = stepNumber(part.replace(/^第\s*/u, ""));
|
|
819
|
+
if (step) found.add(step);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return [...found].sort((a, b) => a - b);
|
|
824
|
+
}
|
|
825
|
+
function taskPackages(taskType, text4, band) {
|
|
826
|
+
if (band !== "complex") {
|
|
827
|
+
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" };
|
|
828
|
+
return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
|
|
829
|
+
}
|
|
830
|
+
const value = String(text4 ?? "");
|
|
831
|
+
const packages = [
|
|
832
|
+
{ id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
|
|
833
|
+
];
|
|
834
|
+
const requirements = explicitRequirements(text4);
|
|
835
|
+
if (requirements.length >= 2) {
|
|
836
|
+
const groups = requirements.length > MAX_EXPLICIT_EXECUTION_PACKAGES ? [
|
|
837
|
+
...requirements.slice(0, MAX_EXPLICIT_EXECUTION_PACKAGES - 1).map((item) => [item]),
|
|
838
|
+
requirements.slice(MAX_EXPLICIT_EXECUTION_PACKAGES - 1)
|
|
839
|
+
] : requirements.map((item) => [item]);
|
|
840
|
+
groups.forEach((group, index) => {
|
|
841
|
+
const objective = group.length === 1 ? group[0] : group.map((item, offset) => `${MAX_EXPLICIT_EXECUTION_PACKAGES + offset}. ${item}`).join("\n");
|
|
842
|
+
const type = detectTaskTypes(objective)[0] ?? "general";
|
|
843
|
+
const difficulty = group.length > 1 ? "complex" : requirementDifficulty(objective, type);
|
|
844
|
+
const previous = packages.at(-1);
|
|
845
|
+
const groupOf = (step) => requirements.length > MAX_EXPLICIT_EXECUTION_PACKAGES && step >= MAX_EXPLICIT_EXECUTION_PACKAGES ? MAX_EXPLICIT_EXECUTION_PACKAGES - 1 : step - 1;
|
|
846
|
+
const explicit = [...new Set(group.flatMap((item) => explicitStepReferences(item)).map(groupOf))].filter((target) => target >= 0 && target < index).map((target) => `execution-${target + 1}`);
|
|
847
|
+
const sequential = explicit.length === 0 && /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(objective);
|
|
848
|
+
packages.push({
|
|
849
|
+
id: `execution-${index + 1}`,
|
|
850
|
+
name: group.length === 1 ? `\u9700\u6C42 ${index + 1}\uFF1A${group[0].slice(0, 28)}` : `\u9700\u6C42 ${index + 1}\uFF1A\u5176\u4F59 ${group.length} \u9879`,
|
|
851
|
+
objective,
|
|
852
|
+
type,
|
|
853
|
+
purpose: "execution",
|
|
854
|
+
difficulty,
|
|
855
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
856
|
+
dependsOn: explicit.length ? ["analysis", ...explicit] : sequential && previous?.purpose === "execution" ? ["analysis", previous.id] : ["analysis"],
|
|
857
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
} else {
|
|
861
|
+
const domains = [...new Set([...detectTaskTypes(text4), taskType].filter((type) => type !== "general"))];
|
|
862
|
+
for (const type of domains.length > 0 ? domains : [taskType]) {
|
|
863
|
+
const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
|
|
864
|
+
const difficulty = requirementDifficulty(objective, type);
|
|
865
|
+
packages.push({
|
|
866
|
+
id: `execution-${type}`,
|
|
867
|
+
name: `${TASK_TYPE_LABELS[type] ?? type}\u65B9\u5411\u5904\u7406`,
|
|
868
|
+
objective,
|
|
869
|
+
type,
|
|
870
|
+
purpose: "execution",
|
|
871
|
+
difficulty,
|
|
872
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
873
|
+
dependsOn: ["analysis"],
|
|
874
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (/(测试|验证|评估|对比|benchmark|test|verify|audit)/i.test(value)) {
|
|
879
|
+
packages.push({
|
|
880
|
+
id: "verification",
|
|
881
|
+
name: "\u9A8C\u8BC1\u3001\u53CD\u4F8B\u4E0E\u98CE\u9669\u5BA1\u67E5",
|
|
882
|
+
type: "reasoning",
|
|
883
|
+
purpose: "verification",
|
|
884
|
+
difficulty: "complex",
|
|
885
|
+
criticality: 0.88,
|
|
886
|
+
dependsOn: packages.filter((task) => task.purpose === "execution").map((task) => task.id),
|
|
887
|
+
preferredReasoningEffort: "high"
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
packages.push({
|
|
891
|
+
id: "synthesis",
|
|
892
|
+
name: "\u7ED3\u679C\u6821\u9A8C\u4E0E\u6574\u5408",
|
|
893
|
+
type: "reasoning",
|
|
894
|
+
purpose: "synthesis",
|
|
895
|
+
difficulty: "complex",
|
|
896
|
+
criticality: 1,
|
|
897
|
+
dependsOn: packages.filter((task) => task.purpose !== "analysis").map((task) => task.id),
|
|
898
|
+
preferredReasoningEffort: "xhigh"
|
|
899
|
+
});
|
|
900
|
+
return packages.map((task) => ({ ...task, qualityFloor: taskQualityFloor(band, task) }));
|
|
901
|
+
}
|
|
902
|
+
var SYNTHESIS_WEIGHTS = Object.freeze({ quality: 0.58, cost: 0.08, latency: 0.04, specialty: 0.08, reasoning: 0.16, risk: 0.06 });
|
|
903
|
+
var ROUTING_BEAM_WIDTH = 256;
|
|
904
|
+
var ROUTING_CANDIDATE_LIMIT = 12;
|
|
905
|
+
function weightsForTask(weights, task) {
|
|
906
|
+
if (task.weights) return task.weights;
|
|
907
|
+
return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
|
|
908
|
+
}
|
|
909
|
+
function compareText(left, right) {
|
|
910
|
+
const a = String(left);
|
|
911
|
+
const b = String(right);
|
|
912
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
913
|
+
}
|
|
914
|
+
function compareRowsStable(left, right) {
|
|
915
|
+
return compareText(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
|
|
916
|
+
}
|
|
917
|
+
function reasoningEffortRank(effort) {
|
|
918
|
+
const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
919
|
+
const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
|
|
920
|
+
const canonical = aliases[normalized2] ?? normalized2;
|
|
921
|
+
const index = REASONING_EFFORT_ORDER.indexOf(canonical);
|
|
922
|
+
return index < 0 ? REASONING_EFFORT_ORDER.indexOf("medium") : index;
|
|
923
|
+
}
|
|
924
|
+
function reasoningEffortMultiplier(effort) {
|
|
925
|
+
const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
926
|
+
const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
|
|
927
|
+
return REASONING_EFFORT_MULTIPLIERS[aliases[normalized2] ?? normalized2] ?? REASONING_EFFORT_MULTIPLIERS.medium;
|
|
928
|
+
}
|
|
929
|
+
function selectReasoningEffort(efforts, preferred = "medium") {
|
|
930
|
+
const exact = Array.isArray(efforts) ? [...new Set(efforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean))] : [];
|
|
931
|
+
if (exact.length === 0) return void 0;
|
|
932
|
+
const preferredRank = reasoningEffortRank(preferred);
|
|
933
|
+
return exact.slice().sort((left, right) => {
|
|
934
|
+
const distance = Math.abs(reasoningEffortRank(left) - preferredRank) - Math.abs(reasoningEffortRank(right) - preferredRank);
|
|
935
|
+
if (distance !== 0) return distance;
|
|
936
|
+
return reasoningEffortRank(left) - reasoningEffortRank(right) || compareText(left, right);
|
|
937
|
+
})[0];
|
|
938
|
+
}
|
|
939
|
+
function reasoningDecision(row, task) {
|
|
940
|
+
const preferred = String(task.preferredReasoningEffort ?? "medium");
|
|
941
|
+
const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : [];
|
|
942
|
+
if (efforts.length === 0) {
|
|
943
|
+
const knownUnsupported = row.reasoningKnown === true;
|
|
944
|
+
const preferredRank2 = reasoningEffortRank(preferred);
|
|
945
|
+
return {
|
|
946
|
+
reasoningEffort: void 0,
|
|
947
|
+
reasoningFit: knownUnsupported ? clamp(0.78 - preferredRank2 * 0.08) : 0.55,
|
|
948
|
+
preferredReasoningEffort: preferred,
|
|
949
|
+
multiplier: REASONING_EFFORT_MULTIPLIERS.medium
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
const preferredRank = reasoningEffortRank(preferred);
|
|
953
|
+
const chosen = selectReasoningEffort(efforts, preferred);
|
|
954
|
+
const distance = Math.abs(reasoningEffortRank(chosen) - preferredRank);
|
|
955
|
+
return {
|
|
956
|
+
reasoningEffort: chosen,
|
|
957
|
+
reasoningFit: clamp(1 - distance / (REASONING_EFFORT_ORDER.length - 1)),
|
|
958
|
+
preferredReasoningEffort: preferred,
|
|
959
|
+
multiplier: reasoningEffortMultiplier(chosen)
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
963
|
+
const quality = qualityForTask(row, task.type);
|
|
964
|
+
const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
|
|
965
|
+
const qualityGap = Math.max(0, floor - quality);
|
|
966
|
+
const duplicatePenalty = 0;
|
|
967
|
+
const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
|
|
968
|
+
const reasoning = reasoningDecision(row, task);
|
|
969
|
+
const cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
|
|
970
|
+
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 - duplicatePenalty - qualityGap * (task.criticality ?? 0.75) + synthesisPreference;
|
|
971
|
+
return { score, floor, qualityGap, ...reasoning };
|
|
972
|
+
}
|
|
973
|
+
function taskCost(row, task, text4, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
974
|
+
if (row?.pricing === null) return 0;
|
|
975
|
+
const decision = row === null ? null : reasoningDecision(row, task);
|
|
976
|
+
const tokens = taskTokenBudget(text4, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
|
|
977
|
+
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;
|
|
978
|
+
}
|
|
979
|
+
function dominates(left, right, task, text4, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
980
|
+
if (left.pricing === null || right.pricing === null) return false;
|
|
981
|
+
const leftReasoning = reasoningDecision(left, task);
|
|
982
|
+
const rightReasoning = reasoningDecision(right, task);
|
|
983
|
+
const leftValues = {
|
|
984
|
+
quality: qualityForTask(left, task.type),
|
|
985
|
+
cost: taskCost(left, task, text4, complexity, cacheReadRatio, cacheWriteRatio),
|
|
986
|
+
latency: clamp(left.latency * leftReasoning.multiplier.latency),
|
|
987
|
+
specialty: specialtyForTask(left, task.type),
|
|
988
|
+
reasoning: leftReasoning.reasoningFit,
|
|
989
|
+
risk: clamp(left.risk)
|
|
990
|
+
};
|
|
991
|
+
const rightValues = {
|
|
992
|
+
quality: qualityForTask(right, task.type),
|
|
993
|
+
cost: taskCost(right, task, text4, complexity, cacheReadRatio, cacheWriteRatio),
|
|
994
|
+
latency: clamp(right.latency * rightReasoning.multiplier.latency),
|
|
995
|
+
specialty: specialtyForTask(right, task.type),
|
|
996
|
+
reasoning: rightReasoning.reasoningFit,
|
|
997
|
+
risk: clamp(right.risk)
|
|
998
|
+
};
|
|
999
|
+
const noWorse = leftValues.quality >= rightValues.quality && leftValues.cost <= rightValues.cost && leftValues.latency <= rightValues.latency && leftValues.specialty >= rightValues.specialty && leftValues.reasoning >= rightValues.reasoning && leftValues.risk <= rightValues.risk;
|
|
1000
|
+
const strictlyBetter = leftValues.quality > rightValues.quality || leftValues.cost < rightValues.cost || leftValues.latency < rightValues.latency || leftValues.specialty > rightValues.specialty || leftValues.reasoning > rightValues.reasoning || leftValues.risk < rightValues.risk;
|
|
1001
|
+
return noWorse && strictlyBetter;
|
|
1002
|
+
}
|
|
1003
|
+
function candidatePool(rows, task, weights, maxCost, text4, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
1004
|
+
const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
|
|
1005
|
+
const floor = Number(task.qualityFloor ?? 0);
|
|
1006
|
+
const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
|
|
1007
|
+
const source = feasible.length > 0 ? feasible : eligibleRows.slice().sort((left, right) => qualityForTask(right, task.type) - qualityForTask(left, task.type) || compareRowsStable(left, right)).slice(0, 3);
|
|
1008
|
+
const taskWeights = weightsForTask(weights, task);
|
|
1009
|
+
const scored = source.map((row) => ({
|
|
1010
|
+
row,
|
|
1011
|
+
decision: candidateUtility(row, task, taskWeights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio),
|
|
1012
|
+
cost: taskCost(row, task, text4, complexity, cacheReadRatio, cacheWriteRatio)
|
|
1013
|
+
}));
|
|
1014
|
+
const frontier = scored.filter((item) => !source.some((other) => other !== item.row && dominates(other, item.row, task, text4, complexity, cacheReadRatio, cacheWriteRatio)));
|
|
1015
|
+
const essential = [
|
|
1016
|
+
scored.slice().sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
|
|
1017
|
+
scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
|
|
1018
|
+
scored.slice().sort((left, right) => qualityForTask(right.row, task.type) - qualityForTask(left.row, task.type) || compareRowsStable(left.row, right.row))[0]
|
|
1019
|
+
].filter(Boolean);
|
|
1020
|
+
const ordered = [...frontier, ...essential].filter((item, index, all) => all.findIndex((candidate) => candidate.row === item.row) === index).sort((left, right) => right.decision.score - left.decision.score || left.cost - right.cost || compareRowsStable(left.row, right.row)).slice(0, ROUTING_CANDIDATE_LIMIT);
|
|
1021
|
+
return {
|
|
1022
|
+
options: ordered,
|
|
1023
|
+
relaxed: feasible.length === 0,
|
|
1024
|
+
pruned: Math.max(0, eligibleRows.length - ordered.length)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function stateSignature(state) {
|
|
1028
|
+
return state.assignments.map((assignment) => `${routeKey(assignment.row?.provider, assignment.row?.model)}@${assignment.decision?.reasoningEffort ?? "provider-default"}`).join("|");
|
|
1029
|
+
}
|
|
1030
|
+
function compareUtilityStates(left, right) {
|
|
1031
|
+
return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || right.score - left.score || left.cost - right.cost || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
|
|
1032
|
+
}
|
|
1033
|
+
function compareCostStates(left, right) {
|
|
1034
|
+
return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || left.cost - right.cost || right.score - left.score || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
|
|
1035
|
+
}
|
|
1036
|
+
function solveAssignments({ rows, tasks, weights, maxCost, text: text4, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
|
|
1037
|
+
const pools = tasks.map((task) => candidatePool(rows, task, weights, maxCost, text4, complexity, cacheReadRatio, cacheWriteRatio));
|
|
1038
|
+
if (pools.some((pool) => pool.options.length === 0)) return null;
|
|
1039
|
+
const suffixMinimum = Array(tasks.length + 1).fill(0);
|
|
1040
|
+
for (let index = tasks.length - 1; index >= 0; index -= 1) {
|
|
1041
|
+
const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option) => option.row.pricing !== null) : pools[index].options;
|
|
1042
|
+
if (costOptions.length === 0) return null;
|
|
1043
|
+
suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option) => option.cost));
|
|
1044
|
+
}
|
|
1045
|
+
if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
|
|
1046
|
+
let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
|
|
1047
|
+
for (let index = 0; index < tasks.length; index += 1) {
|
|
1048
|
+
const task = tasks[index];
|
|
1049
|
+
const pool = pools[index];
|
|
1050
|
+
const expanded = [];
|
|
1051
|
+
for (const state of states) {
|
|
1052
|
+
for (const option of pool.options) {
|
|
1053
|
+
if (Number.isFinite(budget) && option.row.pricing === null) continue;
|
|
1054
|
+
const nextCost = state.cost + option.cost;
|
|
1055
|
+
if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
|
|
1056
|
+
const taskWeights = weightsForTask(weights, task);
|
|
1057
|
+
const decision = candidateUtility(option.row, task, taskWeights, maxCost, state.usedRoutes, cacheReadRatio, cacheWriteRatio);
|
|
1058
|
+
const route = routeKey(option.row.provider, option.row.model);
|
|
1059
|
+
const dependencySwitches = (task.dependsOn ?? []).reduce((count, dependency) => {
|
|
1060
|
+
const dependencyRoute = state.routesByTask.get(dependency);
|
|
1061
|
+
return count + (dependencyRoute !== void 0 && dependencyRoute !== route ? 1 : 0);
|
|
1062
|
+
}, 0);
|
|
1063
|
+
const handoffPenalty = dependencySwitches * 0.015;
|
|
1064
|
+
const qualityShortfall = Math.max(0, decision.floor - qualityForTask(option.row, task.type));
|
|
1065
|
+
const usedRoutes = new Set(state.usedRoutes);
|
|
1066
|
+
usedRoutes.add(route);
|
|
1067
|
+
const routesByTask = new Map(state.routesByTask);
|
|
1068
|
+
routesByTask.set(task.id, route);
|
|
1069
|
+
expanded.push({
|
|
1070
|
+
assignments: [...state.assignments, { task, row: option.row, decision: { ...decision, relaxed: qualityShortfall > 0 }, estimatedCost: option.cost, handoffPenalty }],
|
|
1071
|
+
routesByTask,
|
|
1072
|
+
usedRoutes,
|
|
1073
|
+
score: state.score + decision.score - handoffPenalty,
|
|
1074
|
+
cost: nextCost,
|
|
1075
|
+
switches: state.switches + dependencySwitches,
|
|
1076
|
+
relaxedCount: state.relaxedCount + (qualityShortfall > 0 ? 1 : 0),
|
|
1077
|
+
qualityShortfall: state.qualityShortfall + qualityShortfall
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (expanded.length === 0) return null;
|
|
1082
|
+
expanded.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
1083
|
+
states = expanded.slice(0, ROUTING_BEAM_WIDTH);
|
|
1084
|
+
}
|
|
1085
|
+
states.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
1086
|
+
return {
|
|
1087
|
+
...states[0],
|
|
1088
|
+
candidatePools: pools,
|
|
1089
|
+
minimumFeasibleCost: suffixMinimum[0]
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
function buildPlan({ text: text4 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
|
|
1093
|
+
const presetId = normalizeRoutingPreset(preset);
|
|
1094
|
+
const firstLine = String(text4 ?? "").split(/\r?\n/u)[0].trim();
|
|
1095
|
+
const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
|
|
1096
|
+
const assessed = assessComplexity(transformOnly ? firstLine : text4);
|
|
1097
|
+
const requirements = explicitRequirements(text4);
|
|
1098
|
+
const compound = shouldSplitRequirements(requirements);
|
|
1099
|
+
const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
|
|
1100
|
+
const taskType = classifyTask(transformOnly ? firstLine : text4);
|
|
1101
|
+
const weights = presetWeights(OBJECTIVE_WEIGHTS[complexity.band], presetId);
|
|
1102
|
+
const discovered = Array.isArray(available) ? available.map((entry) => {
|
|
1103
|
+
const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
|
|
1104
|
+
const reasoningEfforts = rawEfforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean);
|
|
1105
|
+
return {
|
|
1106
|
+
provider: String(entry.provider ?? ""),
|
|
1107
|
+
model: String(entry.model ?? ""),
|
|
1108
|
+
reasoningEfforts: [...new Set(reasoningEfforts)],
|
|
1109
|
+
defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
|
|
1110
|
+
reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
|
|
1111
|
+
quality: asScore(entry.quality),
|
|
1112
|
+
qualityBias: Number.isFinite(entry.qualityBias) ? clamp(entry.qualityBias, -0.05, 0.05) : 0,
|
|
1113
|
+
qualitySource: entry.qualitySource === "user" ? "user" : "route",
|
|
1114
|
+
latency: asScore(entry.latency),
|
|
1115
|
+
risk: asScore(entry.risk),
|
|
1116
|
+
specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
|
|
1117
|
+
pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
|
|
1118
|
+
pricingSource: entry.pricingSource === "user" ? "user" : "route",
|
|
1119
|
+
inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
|
|
1120
|
+
};
|
|
1121
|
+
}) : [];
|
|
1122
|
+
const rows = [];
|
|
1123
|
+
const normalizedPrices = normalizePricing(pricing);
|
|
1124
|
+
for (const route of discovered) {
|
|
1125
|
+
if (!route.provider || !route.model) continue;
|
|
1126
|
+
const catalog = modelMetadata(route.model);
|
|
1127
|
+
const metadata = {
|
|
1128
|
+
...catalog ?? { id: route.model, aliases: [route.model], specialties: [] },
|
|
1129
|
+
...route.quality === void 0 ? {} : { quality: route.quality },
|
|
1130
|
+
...route.latency === void 0 ? {} : { latency: route.latency },
|
|
1131
|
+
...route.risk === void 0 ? {} : { risk: route.risk },
|
|
1132
|
+
...route.specialties === null ? {} : { specialties: route.specialties }
|
|
1133
|
+
};
|
|
1134
|
+
const live = liveBenchRow(liveBench, route.model);
|
|
1135
|
+
const liveScores = live?.scores ?? {};
|
|
1136
|
+
const liveOverall = asScore(live?.overall);
|
|
1137
|
+
const quality = clamp((asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0) + route.qualityBias);
|
|
1138
|
+
const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
|
|
1139
|
+
const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
|
|
1140
|
+
const pricingRow = userPrice ?? route.pricing ?? null;
|
|
1141
|
+
const pricingSource = userPrice ? "user" : route.pricing ? route.pricingSource : "unknown";
|
|
1142
|
+
const specialty = specialtyMatch(metadata, taskType, liveScores);
|
|
1143
|
+
rows.push({
|
|
1144
|
+
provider: route.provider,
|
|
1145
|
+
model: route.model,
|
|
1146
|
+
metadata,
|
|
1147
|
+
quality,
|
|
1148
|
+
qualityBias: route.qualityBias,
|
|
1149
|
+
qualitySource,
|
|
1150
|
+
pricingSource,
|
|
1151
|
+
liveScores,
|
|
1152
|
+
liveOverall,
|
|
1153
|
+
latency: metadata.latency ?? 0.5,
|
|
1154
|
+
risk: metadata.risk ?? 0.2,
|
|
1155
|
+
specialty,
|
|
1156
|
+
reasoningEfforts: route.reasoningEfforts,
|
|
1157
|
+
defaultReasoningEffort: route.defaultReasoningEffort,
|
|
1158
|
+
reasoningKnown: route.reasoningKnown,
|
|
1159
|
+
inputModalities: route.inputModalities,
|
|
1160
|
+
pricing: pricingRow,
|
|
1161
|
+
score: 0,
|
|
1162
|
+
estimatedCost: pricingRow === null ? null : estimateCost({
|
|
1163
|
+
id: route.model,
|
|
1164
|
+
costIn: pricingRow.input,
|
|
1165
|
+
costOut: pricingRow.output,
|
|
1166
|
+
cacheRead: pricingRow.cacheRead,
|
|
1167
|
+
cacheWrite: pricingRow.cacheWrite
|
|
1168
|
+
}, text4, 900, {}, cacheReadRatio, cacheWriteRatio)
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
|
|
1172
|
+
const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
|
|
1173
|
+
return effective.input + effective.output;
|
|
1174
|
+
}));
|
|
1175
|
+
const taskNodes = taskPackages(taskType, text4, complexity.band).map((task) => presetId === DEFAULT_ROUTING_PRESET ? task : {
|
|
1176
|
+
...task,
|
|
1177
|
+
qualityFloor: presetFloor(task.qualityFloor, presetId),
|
|
1178
|
+
weights: presetWeights(weightsForTask(weights, task), presetId)
|
|
1179
|
+
});
|
|
1180
|
+
const unassignableTasks = taskNodes.filter((task) => task.type === "vision" && !rows.some((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image"))).map((task) => task.id);
|
|
1181
|
+
const budget = Number(budgetUsd);
|
|
1182
|
+
const utilityPlan = solveAssignments({
|
|
1183
|
+
rows,
|
|
1184
|
+
tasks: taskNodes,
|
|
1185
|
+
weights,
|
|
1186
|
+
maxCost,
|
|
1187
|
+
text: text4,
|
|
1188
|
+
complexity: complexity.band,
|
|
1189
|
+
budget: Number.POSITIVE_INFINITY,
|
|
1190
|
+
cacheReadRatio,
|
|
1191
|
+
cacheWriteRatio
|
|
1192
|
+
});
|
|
1193
|
+
const budgetPlan = budget > 0 ? solveAssignments({
|
|
1194
|
+
rows,
|
|
1195
|
+
tasks: taskNodes,
|
|
1196
|
+
weights,
|
|
1197
|
+
maxCost,
|
|
1198
|
+
text: text4,
|
|
1199
|
+
complexity: complexity.band,
|
|
1200
|
+
budget,
|
|
1201
|
+
cacheReadRatio,
|
|
1202
|
+
cacheWriteRatio
|
|
1203
|
+
}) : null;
|
|
1204
|
+
const minimumCostPlan = budget > 0 && budgetPlan === null ? solveAssignments({
|
|
1205
|
+
rows,
|
|
1206
|
+
tasks: taskNodes,
|
|
1207
|
+
weights,
|
|
1208
|
+
maxCost,
|
|
1209
|
+
text: text4,
|
|
1210
|
+
complexity: complexity.band,
|
|
1211
|
+
budget: Number.POSITIVE_INFINITY,
|
|
1212
|
+
cacheReadRatio,
|
|
1213
|
+
cacheWriteRatio,
|
|
1214
|
+
minimizeCost: true
|
|
1215
|
+
}) : null;
|
|
1216
|
+
const optimized = budget > 0 ? budgetPlan ?? minimumCostPlan ?? utilityPlan : utilityPlan;
|
|
1217
|
+
const assignments = optimized?.assignments ?? [];
|
|
1218
|
+
const usedRoutes = optimized?.usedRoutes ?? /* @__PURE__ */ new Set();
|
|
1219
|
+
const constraintRelaxed = (optimized?.relaxedCount ?? 0) > 0;
|
|
1220
|
+
for (const row of rows) {
|
|
1221
|
+
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio).score;
|
|
1222
|
+
}
|
|
1223
|
+
rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
|
|
1224
|
+
const selectedAssignment = assignments[0];
|
|
1225
|
+
const selected = selectedAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows[0] ?? null);
|
|
1226
|
+
const synthesizerAssignment = assignments.at(-1);
|
|
1227
|
+
const synthesizer = synthesizerAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0]);
|
|
1228
|
+
const subtasks = assignments.map(({ task, row, decision }) => ({
|
|
1229
|
+
id: task.id,
|
|
1230
|
+
name: task.name,
|
|
1231
|
+
...task.objective ? { objective: task.objective } : {},
|
|
1232
|
+
type: task.type,
|
|
1233
|
+
difficulty: task.difficulty,
|
|
1234
|
+
recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
1235
|
+
recommendedProvider: row?.provider ?? "",
|
|
1236
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
1237
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
1238
|
+
recommendedReasoningEffort: decision?.reasoningEffort,
|
|
1239
|
+
preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
|
|
1240
|
+
reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
|
|
1241
|
+
purpose: task.purpose,
|
|
1242
|
+
criticality: task.criticality,
|
|
1243
|
+
qualityFloor: Number(task.qualityFloor.toFixed(3)),
|
|
1244
|
+
dependsOn: [...task.dependsOn ?? []]
|
|
1245
|
+
}));
|
|
1246
|
+
const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
|
|
1247
|
+
const tokens = taskTokenBudget(text4, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1248
|
+
const taskEstimate = estimatedCost ?? taskCost(row, task, text4, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1249
|
+
return {
|
|
1250
|
+
stage: index + 1,
|
|
1251
|
+
purpose: task.purpose,
|
|
1252
|
+
difficulty: task.difficulty,
|
|
1253
|
+
model: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
1254
|
+
provider: row?.provider ?? "",
|
|
1255
|
+
reasoningEffort: decision?.reasoningEffort,
|
|
1256
|
+
preferredReasoningEffort: decision?.preferredReasoningEffort,
|
|
1257
|
+
reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
|
|
1258
|
+
reasoningOutputMultiplier: Number(Number(decision?.multiplier?.output ?? 1).toFixed(2)),
|
|
1259
|
+
inputTokens: tokens.inputTokens,
|
|
1260
|
+
cacheReadTokens: tokens.cacheReadTokens,
|
|
1261
|
+
cacheWriteTokens: tokens.cacheWriteTokens,
|
|
1262
|
+
outputTokens: tokens.outputTokens,
|
|
1263
|
+
estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
|
|
1264
|
+
quality: row?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
|
|
1265
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
1266
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
1267
|
+
handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
|
|
1268
|
+
};
|
|
1269
|
+
});
|
|
1270
|
+
const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
|
|
1271
|
+
const totalEstimate = pricingComplete ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
|
|
1272
|
+
const baselineRows = assignments.map(({ task }) => {
|
|
1273
|
+
const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
1274
|
+
return { task, strongest };
|
|
1275
|
+
});
|
|
1276
|
+
const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text4, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
|
|
1277
|
+
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
|
|
1278
|
+
const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
|
|
1279
|
+
const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
|
|
1280
|
+
const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
|
|
1281
|
+
const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
|
|
1282
|
+
const reason = selected === null ? unassignableTasks.length > 0 ? `\u56FE\u50CF\u5DE5\u4F5C\u5305 ${unassignableTasks.join("\u3001")} \u6CA1\u6709\u53EF\u7528\u7684\u56FE\u50CF\u6A21\u578B\uFF0C\u65E0\u6CD5\u5F62\u6210\u5B8C\u6574\u5206\u914D\u8BA1\u5212\u3002` : "\u5C1A\u672A\u53D1\u73B0\u53EF\u7528\u6A21\u578B\uFF0C\u4FDD\u7559 Harness \u539F\u59CB\u6A21\u578B\u9009\u62E9\u3002" : `${complexity.band === "simple" ? "\u4F4E\u590D\u6742\u5EA6\u4F18\u5148\u6210\u672C\u3001\u54CD\u5E94\u901F\u5EA6\u4E0E\u8F83\u4F4E\u63A8\u7406\u5F00\u9500" : complexity.band === "balanced" ? "\u5728\u8D28\u91CF\u3001\u6210\u672C\u3001\u63A8\u7406\u7B49\u7EA7\u3001\u5EF6\u8FDF\u4E0E\u98CE\u9669\u4E4B\u95F4\u5E73\u8861" : "\u9AD8\u590D\u6742\u5EA6\u6267\u884C\u5305\u542B\u63A8\u7406\u7B49\u7EA7\u7684\u4F9D\u8D56\u611F\u77E5\u5168\u5C40\u7EA6\u675F\u5206\u914D"}\uFF1B\u4EFB\u52A1\u7C7B\u578B\u4E3A ${taskType}\uFF0C\u5DF2\u5BF9 ${String(subtasks.length)} \u4E2A\u5DE5\u4F5C\u5305\u8FDB\u884C Pareto \u526A\u679D\u548C\u6709\u754C\u7EC4\u5408\u641C\u7D22\u3002`;
|
|
1283
|
+
return {
|
|
1284
|
+
mode,
|
|
1285
|
+
preset: presetId,
|
|
1286
|
+
complexity: { value: Number(complexity.value.toFixed(3)), band: complexity.band },
|
|
1287
|
+
compound,
|
|
1288
|
+
unassignableTasks,
|
|
1289
|
+
taskType,
|
|
1290
|
+
taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
|
|
1291
|
+
objectiveWeights: weights,
|
|
1292
|
+
candidates: rows.slice(0, 8).map((row) => {
|
|
1293
|
+
const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio);
|
|
1294
|
+
return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), 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 };
|
|
1295
|
+
}),
|
|
1296
|
+
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: selected.qualitySource, pricingSource: selected.pricingSource },
|
|
1297
|
+
subtasks,
|
|
1298
|
+
synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
|
|
1299
|
+
estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
|
|
1300
|
+
costBreakdown,
|
|
1301
|
+
optimization: {
|
|
1302
|
+
solver: "pareto-pruned quality-constrained beam assignment",
|
|
1303
|
+
qualityFloor: QUALITY_FLOORS[complexity.band],
|
|
1304
|
+
budgetUsd: Number(Number(budgetUsd) > 0 ? Number(budgetUsd) : 0),
|
|
1305
|
+
cacheReadRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).read,
|
|
1306
|
+
cacheWriteRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).write,
|
|
1307
|
+
budgetExceeded,
|
|
1308
|
+
constraintRelaxed,
|
|
1309
|
+
pricingComplete,
|
|
1310
|
+
qualityEvidenceComplete,
|
|
1311
|
+
baselineAllStrongCost: baselineCost === null ? null : Number(baselineCost.toFixed(6)),
|
|
1312
|
+
estimatedSavings: savings === null ? null : Number(savings.toFixed(4)),
|
|
1313
|
+
distinctRoutes: usedRoutes.size,
|
|
1314
|
+
handoffCount: optimized?.switches ?? 0,
|
|
1315
|
+
paretoPruned,
|
|
1316
|
+
beamWidth: ROUTING_BEAM_WIDTH,
|
|
1317
|
+
budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
|
|
1318
|
+
minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
|
|
1319
|
+
liveBench: liveBench?.fetchedAt ? { source: liveBench.source ?? "livebench", fetchedAt: liveBench.fetchedAt, models: Object.keys(liveBench.models ?? {}).length, stale: String(liveBenchError).length > 0, error: String(liveBenchError || "") } : { source: "experimental-baseline", fetchedAt: null, models: 0, stale: false, error: String(liveBenchError || "") }
|
|
1320
|
+
},
|
|
1321
|
+
reason,
|
|
1322
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// .dsh-plugin/shared/harness-plan.mjs
|
|
1327
|
+
var clean2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1328
|
+
var HEADLESS_TOOL_IDS = /* @__PURE__ */ new Set(["claude-code", "codex", "gemini"]);
|
|
1329
|
+
function channelForProvider(provider, installedToolIds = [], runnableToolIds = [], route = null, loggedOutToolIds = []) {
|
|
1330
|
+
const preference = normalizeExecutionPreference(route?.execution);
|
|
1331
|
+
const tool = toolForProvider(provider);
|
|
1332
|
+
if (preference === "api") {
|
|
1333
|
+
return {
|
|
1334
|
+
kind: "harness-llm",
|
|
1335
|
+
preference,
|
|
1336
|
+
...tool ? { tool: tool.id, label: tool.label } : {},
|
|
1337
|
+
detail: "\u8BE5\u6A21\u578B\u914D\u7F6E\u4E3A\u53EA\u4F7F\u7528\u6A21\u578B\u76EE\u5F55 API\u3002"
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
if (!tool || tool.unsupported) {
|
|
1341
|
+
return { kind: "harness-llm", preference, detail: "\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API \u8C03\u7528\u3002" };
|
|
1342
|
+
}
|
|
1343
|
+
const installed = Array.isArray(installedToolIds) && installedToolIds.includes(tool.id);
|
|
1344
|
+
if (installed && Array.isArray(loggedOutToolIds) && loggedOutToolIds.includes(tool.id)) {
|
|
1345
|
+
return {
|
|
1346
|
+
kind: "harness-llm",
|
|
1347
|
+
preference,
|
|
1348
|
+
tool: tool.id,
|
|
1349
|
+
label: tool.label,
|
|
1350
|
+
loginRequired: true,
|
|
1351
|
+
detail: `${tool.label} \u5DF2\u5B89\u88C5\u4F46\u672A\u767B\u5F55\uFF08\u5F00\u7BB1\u4F53\u68C0\u7ED3\u679C\uFF09\uFF1B\u5B9E\u9645\u8C03\u7528\u76F4\u63A5\u4F7F\u7528\u6A21\u578B\u76EE\u5F55 API\uFF0C\u4E0D\u7B49\u5F85 CLI \u5931\u8D25\u3002`
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
const runnable = installed && Array.isArray(runnableToolIds) && runnableToolIds.includes(tool.id);
|
|
1355
|
+
const headless = installed && HEADLESS_TOOL_IDS.has(tool.id);
|
|
1356
|
+
if (runnable || headless) {
|
|
1357
|
+
return {
|
|
1358
|
+
kind: "official-cli",
|
|
1359
|
+
preference,
|
|
1360
|
+
tool: tool.id,
|
|
1361
|
+
label: tool.label,
|
|
1362
|
+
detail: tool.id === "zcode" ? "ZCode \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u8C03\u7528\u5176\u5B98\u65B9\u7F16\u7A0B\u4EE3\u7406\uFF1B3.14.3 \u7684 CLI \u4F7F\u7528\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\uFF0C\u4E0D\u80FD\u4FDD\u8BC1\u4E0E Harness \u5EFA\u8BAE\u6A21\u578B\u4E00\u81F4\u3002" : headless && !runnable ? `${tool.label} \u5DF2\u5B89\u88C5\u3002\u5206\u914D\u5230\u8BE5\u6A21\u578B\u7684\u4EFB\u52A1\u4F1A\u5148\u8D70\u5B98\u65B9\u65E0\u754C\u9762\u547D\u4EE4\uFF1B\u547D\u4EE4\u7F3A\u5931\u6216\u5931\u8D25\u65F6\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002` : `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u6258\u7BA1\u8C03\u7528\u5176\u5B98\u65B9 CLI\uFF1BHarness \u6A21\u578B\u76EE\u5F55\u4E0E\u5382\u5546 CLI \u540D\u79F0\u53EF\u80FD\u4E0D\u540C\uFF0C\u56E2\u961F\u65E0\u6CD5\u786E\u8BA4\u6620\u5C04\u65F6\u4F7F\u7528 CLI \u9ED8\u8BA4\u6A21\u578B\uFF0C\u5B9E\u9645\u6A21\u578B\u4ECD\u987B\u6838\u5BF9\u8FD0\u884C\u8BB0\u5F55\u3002`
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
return {
|
|
1366
|
+
kind: "harness-llm",
|
|
1367
|
+
preference,
|
|
1368
|
+
tool: tool.id,
|
|
1369
|
+
label: tool.label,
|
|
1370
|
+
detail: installed ? `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u5E73\u53F0\u7F3A\u5C11\u7ECF\u6838\u9A8C\u7684\u6258\u7BA1\u6267\u884C\u9002\u914D\u5668\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002` : `${tool.label} \u672A\u5B89\u88C5\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u4E00\u952E\u5B89\u88C5\uFF0C\u6216\u8FD0\u884C /tools install ${tool.id}\u3002`
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
function annotate(channel) {
|
|
1374
|
+
return {
|
|
1375
|
+
executionChannel: channel.kind,
|
|
1376
|
+
...channel.tool ? { channelTool: channel.tool } : {},
|
|
1377
|
+
...channel.label ? { channelLabel: channel.label } : {},
|
|
1378
|
+
...channel.preference ? { executionPreference: channel.preference } : {},
|
|
1379
|
+
...channel.loginRequired ? { loginRequired: true } : {},
|
|
1380
|
+
channelDetail: channel.detail
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
function createPlanFromRoutes(task, availableRoutes, {
|
|
1384
|
+
mode = "single",
|
|
1385
|
+
budgetUsd = 0,
|
|
1386
|
+
installedToolIds = [],
|
|
1387
|
+
runnableToolIds = [],
|
|
1388
|
+
pricing = {},
|
|
1389
|
+
liveBench = null,
|
|
1390
|
+
cacheReadRatio = 0,
|
|
1391
|
+
cacheWriteRatio = 0,
|
|
1392
|
+
directProvider = "",
|
|
1393
|
+
directModel = "",
|
|
1394
|
+
preset = "balanced",
|
|
1395
|
+
loggedOutToolIds = []
|
|
1396
|
+
} = {}) {
|
|
1397
|
+
const taskText = clean2(task);
|
|
1398
|
+
if (!taskText) throw new Error("task must contain text");
|
|
1399
|
+
const requestedDirect = mode === "direct" || clean2(directProvider) !== "" || clean2(directModel) !== "";
|
|
1400
|
+
const selectedMode = mode === "team" ? "team" : "single";
|
|
1401
|
+
let routes = Array.isArray(availableRoutes) ? availableRoutes : [];
|
|
1402
|
+
let directRoute = null;
|
|
1403
|
+
if (requestedDirect) {
|
|
1404
|
+
const provider = clean2(directProvider);
|
|
1405
|
+
const model = clean2(directModel);
|
|
1406
|
+
if (!provider || !model) throw new Error("\u6307\u5B9A\u5355\u4E00\u6A21\u578B\u9700\u8981\u540C\u65F6\u63D0\u4F9B provider \u548C model");
|
|
1407
|
+
const match = routes.filter((route) => route.provider === provider && route.model === model);
|
|
1408
|
+
if (match.length !== 1) throw new Error(`\u6307\u5B9A\u6A21\u578B ${provider}/${model} \u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D`);
|
|
1409
|
+
routes = match;
|
|
1410
|
+
directRoute = { provider, model };
|
|
1411
|
+
}
|
|
1412
|
+
const installedIds = Array.isArray(installedToolIds) ? installedToolIds.filter(Boolean) : [];
|
|
1413
|
+
const channelCache = /* @__PURE__ */ new Map();
|
|
1414
|
+
const channelOf = (provider, model) => {
|
|
1415
|
+
const route = routes.find((item) => item.provider === provider && item.model === model);
|
|
1416
|
+
const key = `${String(provider ?? "")}\0${String(model ?? "")}\0${route?.execution ?? ""}`;
|
|
1417
|
+
if (!channelCache.has(key)) channelCache.set(key, channelForProvider(provider, installedIds, runnableToolIds, route, loggedOutToolIds));
|
|
1418
|
+
return channelCache.get(key);
|
|
1419
|
+
};
|
|
1420
|
+
const needsImage = detectTaskTypes(taskText).includes("vision");
|
|
1421
|
+
const plan = buildPlan({
|
|
1422
|
+
text: taskText,
|
|
1423
|
+
available: routes,
|
|
1424
|
+
mode: directRoute ? "single" : selectedMode,
|
|
1425
|
+
budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
|
|
1426
|
+
pricing,
|
|
1427
|
+
liveBench,
|
|
1428
|
+
cacheReadRatio,
|
|
1429
|
+
cacheWriteRatio,
|
|
1430
|
+
preset
|
|
1431
|
+
});
|
|
1432
|
+
const selectedChannel = plan.selected ? channelOf(plan.selected.provider, plan.selected.model) : null;
|
|
1433
|
+
return {
|
|
1434
|
+
...plan,
|
|
1435
|
+
mode: directRoute ? "direct" : plan.mode,
|
|
1436
|
+
routingBypassed: directRoute !== null,
|
|
1437
|
+
directRoute,
|
|
1438
|
+
...directRoute ? { reason: `\u5DF2\u6307\u5B9A ${directRoute.provider}/${directRoute.model}\uFF0C\u4E0D\u4E0E\u5176\u4ED6\u5DF2\u914D\u7F6E\u6A21\u578B\u6BD4\u8F83\u3002\u590D\u6742\u5EA6\u4ECD\u6309\u4EFB\u52A1\u6587\u672C\u4F30\u8BA1\u3002` } : {},
|
|
1439
|
+
contractVersion: 2,
|
|
1440
|
+
availableRoutes: routes,
|
|
1441
|
+
...selectedChannel ? annotate(selectedChannel) : {},
|
|
1442
|
+
availabilityNotice: "\u6A21\u578B\u76EE\u5F55\u5217\u51FA\u7684\u8DEF\u7EBF\u5C1A\u672A\u9A8C\u8BC1\u5F53\u524D\u51ED\u636E\u548C\u7F51\u7EDC\uFF1B\u5B9E\u9645\u53EF\u7528\u6027\u4EE5\u5B98\u65B9\u9002\u914D\u5668\u8C03\u7528\u7ED3\u679C\u4E3A\u51C6\u3002",
|
|
1443
|
+
pricingNotice: plan.estimatedCost === null ? "\u90E8\u5206\u8DEF\u7EBF\u5C1A\u672A\u914D\u7F6E\u8BE5\u4F9B\u5E94\u5546\u7684\u7F8E\u5143\u8F93\u5165/\u8F93\u51FA\u5355\u4EF7\uFF0C\u65E0\u6CD5\u8BA1\u7B97\u53EF\u9760\u7684\u603B\u8D39\u7528\u4E0E\u8282\u7701\u6BD4\u4F8B\uFF1B\u8BF7\u5728\u6A21\u578B\u4EF7\u683C\u8BBE\u7F6E\u4E2D\u8865\u9F50\u3002" : "\u8D39\u7528\u6309\u5DF2\u63D0\u4F9B\u7684\u7F8E\u5143\u5355\u4EF7\u548C\u4F30\u8BA1 token \u6570\u8BA1\u7B97\uFF0C\u4E0D\u662F\u4F9B\u5E94\u5546\u8D26\u5355\uFF0C\u4E5F\u4E0D\u662F\u786C\u6027\u652F\u51FA\u4E0A\u9650\u3002",
|
|
1444
|
+
qualityNotice: plan.optimization.qualityEvidenceComplete ? "\u6A21\u578B\u8D28\u91CF\u4F7F\u7528\u5DF2\u63D0\u4F9B\u8BC4\u5206\u6216\u57FA\u51C6\u6570\u636E\u4F30\u8BA1\uFF0C\u4ECD\u9700\u5B9E\u9645\u4EFB\u52A1\u9A8C\u8BC1\u3002" : "\u90E8\u5206\u6A21\u578B\u8D28\u91CF\u7F3A\u5C11\u53EF\u6838\u9A8C\u8BC4\u5206\uFF1B\u76EE\u5F55\u542F\u53D1\u5F0F\u53EA\u4F9B\u9009\u62E9\u53C2\u8003\uFF0C\u8D28\u91CF\u95E8\u69DB\u548C\u8282\u7701\u6BD4\u4F8B\u65E0\u6CD5\u4FDD\u8BC1\u3002",
|
|
1445
|
+
modalityNotice: needsImage ? plan.unassignableTasks.length > 0 ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u6CA1\u6709\u53EF\u786E\u8BA4\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u8DEF\u7EBF\uFF0C\u5F53\u524D\u8BA1\u5212\u65E0\u6CD5\u5B8C\u6574\u5206\u914D\uFF1B\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u914D\u7F6E\u652F\u6301\u56FE\u50CF\u7684\u6A21\u578B\u3002" : routes.some((route) => !Array.isArray(route.inputModalities) || route.inputModalities.length === 0) ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u5DF2\u58F0\u660E\u56FE\u50CF\u80FD\u529B\u6216\u672A\u58F0\u660E\u8F93\u5165\u80FD\u529B\u7684\u6A21\u578B\uFF1B\u672A\u58F0\u660E\u80FD\u529B\u7684\u6A21\u578B\u4ECD\u9700\u5B9E\u9645\u9A8C\u8BC1\u3002\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u660E\u786E\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u6A21\u578B\uFF1B\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : null,
|
|
1446
|
+
toolNotice: "\u6267\u884C\u6E20\u9053\u6309\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868\u548C\u5DF2\u6838\u9A8C\u9002\u914D\u5668\u6807\u6CE8\uFF1Aofficial-cli \u8868\u793A\u8BE5\u5382\u5546\u5B98\u65B9 CLI \u5DF2\u5B89\u88C5\u4E14\u53EF\u6258\u7BA1\u6267\u884C\uFF1Bharness-llm \u8868\u793A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u8C03\u7528\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u67E5\u770B\u5B89\u88C5\u4E0E\u6267\u884C\u652F\u6301\u72B6\u6001\u3002",
|
|
1447
|
+
team: {
|
|
1448
|
+
requested: selectedMode === "team",
|
|
1449
|
+
recommended: selectedMode === "team" && plan.complexity.band === "complex" && plan.subtasks.length > 1,
|
|
1450
|
+
handoff: "\u53EF\u5728\u5B98\u65B9\u4F1A\u8BDD\u8C03\u7528 model_router_team_execute \u6258\u7BA1\u6267\u884C\u5F53\u524D\u5E73\u53F0\u652F\u6301\u7684\u5B98\u65B9 CLI \u5DE5\u4F5C\u5305\uFF1B\u5B98\u65B9 Agent Teams \u53EF\u534F\u4F5C\u7BA1\u7406\u4EFB\u52A1\uFF0C\u4F46\u6210\u5458\u6A21\u578B\u7531\u5BBF\u4E3B\u914D\u7F6E\uFF0C\u4E0D\u80FD\u76F4\u63A5\u6309\u672C\u8BA1\u5212\u9010\u4E2A\u5207\u6362\u3002",
|
|
1451
|
+
workPackages: plan.subtasks.map((item, index) => ({
|
|
1452
|
+
id: item.id,
|
|
1453
|
+
name: item.name,
|
|
1454
|
+
...item.objective ? { objective: item.objective } : {},
|
|
1455
|
+
type: item.type,
|
|
1456
|
+
purpose: item.purpose,
|
|
1457
|
+
difficulty: item.difficulty,
|
|
1458
|
+
qualitySource: item.qualitySource,
|
|
1459
|
+
pricingSource: item.pricingSource,
|
|
1460
|
+
dependsOn: item.dependsOn,
|
|
1461
|
+
recommendedProvider: item.recommendedProvider,
|
|
1462
|
+
recommendedModel: item.recommended,
|
|
1463
|
+
estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
|
|
1464
|
+
...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
|
|
1465
|
+
...annotate(channelOf(item.recommendedProvider, item.recommended)),
|
|
1466
|
+
verificationChecklist: item.purpose === "synthesis" ? ["\u6838\u5BF9\u5404\u5DE5\u4F5C\u5305\u4EA4\u4ED8\u7269\u4E0E\u4F9D\u8D56", "\u8BB0\u5F55\u51B2\u7A81\u3001\u672A\u89E3\u51B3\u4E8B\u9879\u548C\u6700\u7EC8\u9A8C\u6536\u7ED3\u679C"] : item.type === "code" ? ["\u8BF4\u660E\u6539\u52A8\u6587\u4EF6\u4E0E\u63A5\u53E3", "\u8FD0\u884C\u4E0E\u6539\u52A8\u76F8\u5173\u7684\u9A8C\u8BC1\u5E76\u8BB0\u5F55\u7ED3\u679C", "\u5217\u51FA\u5C1A\u672A\u5B8C\u6210\u7684\u8FB9\u754C\u60C5\u51B5"] : item.type === "research" ? ["\u5217\u51FA\u6765\u6E90\u3001\u65E5\u671F\u548C\u53EF\u6838\u5BF9\u7684\u7ED3\u8BBA", "\u6807\u51FA\u63A8\u65AD\u4E0E\u4E0D\u786E\u5B9A\u4E8B\u9879"] : ["\u5217\u51FA\u4EA4\u4ED8\u5185\u5BB9\u548C\u9A8C\u6536\u4F9D\u636E", "\u6807\u51FA\u672A\u5B8C\u6210\u4E8B\u9879"]
|
|
1467
|
+
}))
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// .dsh-plugin/shared/run-ledger.mjs
|
|
1473
|
+
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
1474
|
+
var routeKey2 = (provider, model) => `${String(provider ?? "")}\0${String(model ?? "")}`;
|
|
1475
|
+
function formatUsd(value) {
|
|
1476
|
+
return typeof value === "number" && Number.isFinite(value) ? `$${value.toFixed(4)}` : "\u2014";
|
|
1477
|
+
}
|
|
1478
|
+
function budgetCheck({ estimateUsd = null, spent = { today: 0, month: 0 }, dailyLimitUsd = 0, monthlyLimitUsd = 0 } = {}) {
|
|
1479
|
+
const limits = [];
|
|
1480
|
+
if (finite(dailyLimitUsd) && dailyLimitUsd > 0) limits.push({ period: "daily", label: "\u4ECA\u65E5", limit: dailyLimitUsd, spent: spent.today ?? 0 });
|
|
1481
|
+
if (finite(monthlyLimitUsd) && monthlyLimitUsd > 0) limits.push({ period: "monthly", label: "\u672C\u6708", limit: monthlyLimitUsd, spent: spent.month ?? 0 });
|
|
1482
|
+
const estimateKnown = finite(estimateUsd);
|
|
1483
|
+
const remaining = limits.length ? Math.max(0, Math.min(...limits.map((item) => item.limit - item.spent))) : null;
|
|
1484
|
+
const exceeded = limits.find((item) => item.spent >= item.limit || estimateKnown && item.spent + estimateUsd > item.limit) ?? null;
|
|
1485
|
+
return {
|
|
1486
|
+
limited: limits.length > 0,
|
|
1487
|
+
estimateKnown,
|
|
1488
|
+
estimateUsd: estimateKnown ? estimateUsd : null,
|
|
1489
|
+
remainingUsd: remaining,
|
|
1490
|
+
exceeded: exceeded ? exceeded.period : null,
|
|
1491
|
+
message: exceeded ? `${exceeded.label}\u9884\u7B97 ${formatUsd(exceeded.limit)}\uFF0C\u5DF2\u7528 ${formatUsd(exceeded.spent)}${estimateKnown ? `\uFF0C\u672C\u6B21\u9884\u4F30 ${formatUsd(estimateUsd)}` : ""}\uFF0C\u5C06\u8D85\u51FA\u4E0A\u9650\u3002` : limits.length && !estimateKnown ? "\u90E8\u5206\u8DEF\u7EBF\u7F3A\u5C11\u5355\u4EF7\uFF0C\u65E0\u6CD5\u9884\u4F30\u672C\u6B21\u8D39\u7528\uFF1B\u4EC5\u5728\u5DF2\u7528\u91D1\u989D\u8FBE\u5230\u4E0A\u9650\u65F6\u963B\u6B62\u3002" : ""
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function applyQualityBiases(routes, biases) {
|
|
1495
|
+
if (!biases || typeof biases !== "object") return routes;
|
|
1496
|
+
return (Array.isArray(routes) ? routes : []).map((route) => {
|
|
1497
|
+
const bias = biases[routeKey2(route.provider, route.model)];
|
|
1498
|
+
return finite(bias) && bias !== 0 ? { ...route, qualityBias: bias } : route;
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// .dsh-plugin/client/catalog.mjs
|
|
1503
|
+
var clean3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1504
|
+
function routesFromModelCatalog(catalog) {
|
|
1505
|
+
const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
|
|
1506
|
+
const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
|
|
1507
|
+
const routes = [];
|
|
1508
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1509
|
+
for (const group of groups) {
|
|
1510
|
+
const provider = clean3(group?.id);
|
|
1511
|
+
if (!provider || !routable.has(provider)) continue;
|
|
1512
|
+
for (const entry of Array.isArray(group.models) ? group.models : []) {
|
|
1513
|
+
const model = clean3(entry?.id);
|
|
1514
|
+
if (!model) continue;
|
|
1515
|
+
const key = `${provider}\0${model}`;
|
|
1516
|
+
if (seen.has(key)) continue;
|
|
1517
|
+
seen.add(key);
|
|
1518
|
+
const reasoning = entry?.reasoning;
|
|
1519
|
+
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean3(item?.id ?? item)).filter(Boolean))] : [];
|
|
1520
|
+
routes.push({
|
|
1521
|
+
provider,
|
|
1522
|
+
providerName: clean3(group.name) || provider,
|
|
1523
|
+
model,
|
|
1524
|
+
name: clean3(entry.name) || model,
|
|
1525
|
+
reasoningKnown: reasoning !== void 0 && reasoning !== null,
|
|
1526
|
+
reasoningEfforts: efforts,
|
|
1527
|
+
...clean3(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean3(reasoning.defaultEffort) } : {},
|
|
1528
|
+
// modelCatalog deliberately omits modalities; unknown means the Host
|
|
1529
|
+
// must still validate image capability before a real request.
|
|
1530
|
+
inputModalities: []
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
routes.sort((left, right) => `${left.provider}/${left.model}`.localeCompare(`${right.provider}/${right.model}`));
|
|
1535
|
+
return routes;
|
|
1536
|
+
}
|
|
1537
|
+
function createWorkspacePlan(task, catalog, options = {}) {
|
|
1538
|
+
const routes = applyQualityBiases(applyModelProfiles(
|
|
1539
|
+
routesFromModelCatalog(catalog),
|
|
1540
|
+
parseModelProfilesJson(options.modelProfilesJson ?? "[]")
|
|
1541
|
+
), options.qualityBiases ?? null);
|
|
1542
|
+
return createPlanFromRoutes(task, routes, options);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1546
|
+
var import_react = __toESM(require("react"), 1);
|
|
1547
|
+
|
|
1548
|
+
// .dsh-plugin/client/model-profile-editor-state.mjs
|
|
1549
|
+
var PROFILE_SPECIALTY_HINT = "code, math, research, summarization, writing, vision, reasoning";
|
|
1550
|
+
var profileRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
|
|
1551
|
+
var field = (value) => String(value ?? "").trim();
|
|
1552
|
+
function nonnegativeField(value, label, max = 1e6) {
|
|
1553
|
+
const raw = field(value);
|
|
1554
|
+
if (!raw) return null;
|
|
1555
|
+
const parsed = Number(raw);
|
|
1556
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u7684\u6570\u5B57\u3002`);
|
|
1557
|
+
return parsed;
|
|
1558
|
+
}
|
|
1559
|
+
function profileDraft(profile) {
|
|
1560
|
+
return {
|
|
1561
|
+
quality: profile?.quality === void 0 ? "" : String(Number((profile.quality * 100).toFixed(4))),
|
|
1562
|
+
input: profile?.pricing?.input === void 0 ? "" : String(profile.pricing.input),
|
|
1563
|
+
output: profile?.pricing?.output === void 0 ? "" : String(profile.pricing.output),
|
|
1564
|
+
cacheRead: profile?.pricing?.cacheRead === void 0 ? "" : String(profile.pricing.cacheRead),
|
|
1565
|
+
cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
|
|
1566
|
+
specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
|
|
1567
|
+
cliModel: profile?.cliModel ?? "",
|
|
1568
|
+
execution: profile?.execution === "official" || profile?.execution === "api" ? profile.execution : "auto",
|
|
1569
|
+
billing: BILLING_MODES.includes(profile?.billing) ? profile.billing : DEFAULT_BILLING_MODE,
|
|
1570
|
+
subscription: ["plan-key", "cli-login", "none"].includes(profile?.subscription) ? profile.subscription : "auto",
|
|
1571
|
+
apiRoute: profile?.apiRoute ? profileRouteKey(profile.apiRoute) : ""
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
function profileFromDraft(route, draft) {
|
|
1575
|
+
const provider = field(route?.provider);
|
|
1576
|
+
const model = field(route?.model);
|
|
1577
|
+
if (!provider || !model) throw new Error("\u8BF7\u5148\u4ECE\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u9009\u62E9\u4E00\u6761\u51C6\u786E\u7684\u6A21\u578B\u8DEF\u7EBF\u3002");
|
|
1578
|
+
const profile = { provider, model };
|
|
1579
|
+
const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
|
|
1580
|
+
if (quality !== null) profile.quality = quality;
|
|
1581
|
+
const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
|
|
1582
|
+
const output = nonnegativeField(draft?.output, "\u8F93\u51FA\u5355\u4EF7");
|
|
1583
|
+
const cacheRead = nonnegativeField(draft?.cacheRead, "\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7");
|
|
1584
|
+
const cacheWrite = nonnegativeField(draft?.cacheWrite, "\u7F13\u5B58\u5199\u5165\u5355\u4EF7");
|
|
1585
|
+
if (input === null !== (output === null)) throw new Error("\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u9700\u8981\u540C\u65F6\u586B\u5199\uFF1B\u7559\u7A7A\u8868\u793A\u4EF7\u683C\u672A\u77E5\u3002");
|
|
1586
|
+
if (input === null && (cacheRead !== null || cacheWrite !== null)) throw new Error("\u586B\u5199\u7F13\u5B58\u5355\u4EF7\u524D\uFF0C\u8BF7\u5148\u586B\u5199\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u3002");
|
|
1587
|
+
if (input !== null) {
|
|
1588
|
+
profile.pricing = { input, output, currency: "USD" };
|
|
1589
|
+
if (cacheRead !== null) profile.pricing.cacheRead = cacheRead;
|
|
1590
|
+
if (cacheWrite !== null) profile.pricing.cacheWrite = cacheWrite;
|
|
1591
|
+
}
|
|
1592
|
+
const rawSpecialties = field(draft?.specialties);
|
|
1593
|
+
if (rawSpecialties) {
|
|
1594
|
+
const specialties = [...new Set(rawSpecialties.split(/[,,\s]+/u).filter(Boolean))];
|
|
1595
|
+
if (specialties.length > 16 || specialties.some((item) => !/^[a-z][a-z0-9-]{0,39}$/.test(item))) {
|
|
1596
|
+
throw new Error("\u64C5\u957F\u65B9\u5411\u6700\u591A 16 \u9879\uFF0C\u7528\u82F1\u6587\u5C0F\u5199\u6807\u7B7E\u5E76\u4EE5\u9017\u53F7\u5206\u9694\uFF0C\u4F8B\u5982 code, research\u3002");
|
|
1597
|
+
}
|
|
1598
|
+
profile.specialties = specialties;
|
|
1599
|
+
}
|
|
1600
|
+
const cliModel = field(draft?.cliModel);
|
|
1601
|
+
if (cliModel) {
|
|
1602
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error("CLI \u6A21\u578B\u540D\u9700\u7531\u5B57\u6BCD\u6570\u5B57\u5F00\u5934\uFF0C\u4E14\u53EA\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u3001\u5192\u53F7\u3001\u659C\u6760\u6216\u8FDE\u5B57\u7B26\u3002");
|
|
1603
|
+
profile.cliModel = cliModel;
|
|
1604
|
+
}
|
|
1605
|
+
const execution = field(draft?.execution) || "auto";
|
|
1606
|
+
if (!["auto", "official", "api"].includes(execution)) throw new Error("\u6267\u884C\u65B9\u5F0F\u53EA\u80FD\u662F\u81EA\u52A8\u3001\u5B98\u65B9\u5DE5\u5177\u6216\u6A21\u578B\u76EE\u5F55 API\u3002");
|
|
1607
|
+
if (execution !== "auto") profile.execution = execution;
|
|
1608
|
+
const billing = field(draft?.billing) || DEFAULT_BILLING_MODE;
|
|
1609
|
+
if (!BILLING_MODES.includes(billing)) throw new Error("\u8BA1\u8D39\u65B9\u5F0F\u53EA\u80FD\u662F\u8BA2\u9605\u4F18\u5148\u3001\u53EA\u7528 API Key \u6216\u53EA\u7528\u8BA2\u9605\u3002");
|
|
1610
|
+
if (billing !== DEFAULT_BILLING_MODE) profile.billing = billing;
|
|
1611
|
+
const subscription = field(draft?.subscription) || "auto";
|
|
1612
|
+
if (!["auto", "plan-key", "cli-login", "none"].includes(subscription)) throw new Error("\u8BA2\u9605\u6765\u6E90\u53EA\u80FD\u662F\u81EA\u52A8\u3001\u7F16\u7A0B\u5957\u9910 Key\u3001CLI \u8D26\u53F7\u767B\u5F55\u6216\u65E0\u8BA2\u9605\u3002");
|
|
1613
|
+
if (subscription !== "auto") profile.subscription = subscription;
|
|
1614
|
+
const apiKey = String(draft?.apiRoute ?? "");
|
|
1615
|
+
if (apiKey) {
|
|
1616
|
+
if (subscription !== "plan-key") throw new Error("\u53EA\u6709\u201C\u7F16\u7A0B\u5957\u9910 Key\u201D\u8DEF\u7EBF\u9700\u8981\u9009\u62E9\u56DE\u9000\u7684 API \u8DEF\u7EBF\u3002");
|
|
1617
|
+
const [apiProvider, apiModel] = apiKey.split("\0");
|
|
1618
|
+
if (!field(apiProvider) || !field(apiModel)) throw new Error("\u56DE\u9000 API \u8DEF\u7EBF\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002");
|
|
1619
|
+
if (field(apiProvider) === provider && field(apiModel) === model) throw new Error("\u56DE\u9000 API \u8DEF\u7EBF\u4E0D\u80FD\u662F\u5957\u9910\u8DEF\u7EBF\u672C\u8EAB\u3002");
|
|
1620
|
+
profile.apiRoute = { provider: field(apiProvider), model: field(apiModel) };
|
|
1621
|
+
}
|
|
1622
|
+
return profile;
|
|
1623
|
+
}
|
|
1624
|
+
function updateProfileJson(rawJson, route, draft) {
|
|
1625
|
+
const profiles = parseModelProfilesJson(rawJson ?? "[]");
|
|
1626
|
+
const candidate = draft === null ? null : profileFromDraft(route, draft);
|
|
1627
|
+
const selected = profileRouteKey(route);
|
|
1628
|
+
const others = profiles.filter((profile) => profileRouteKey(profile) !== selected).map((profile) => ({
|
|
1629
|
+
...profile,
|
|
1630
|
+
...profile.quality === void 0 ? {} : { quality: Number((profile.quality * 100).toFixed(6)) }
|
|
1631
|
+
}));
|
|
1632
|
+
const next = candidate && Object.keys(candidate).length > 2 ? [...others, candidate] : others;
|
|
1633
|
+
const json = JSON.stringify(next, null, 2);
|
|
1634
|
+
parseModelProfilesJson(json);
|
|
1635
|
+
return json;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1639
|
+
var messageOf = (error) => typeof error?.message === "string" ? error.message : "\u6A21\u578B\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002";
|
|
1640
|
+
function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
1641
|
+
const [snapshot, setSnapshot] = import_react.default.useState(() => settingsScope.getSnapshot());
|
|
1642
|
+
const [selected, setSelected] = import_react.default.useState("");
|
|
1643
|
+
const [drafts, setDrafts] = import_react.default.useState({});
|
|
1644
|
+
const [saving, setSaving] = import_react.default.useState(false);
|
|
1645
|
+
const [notice, setNotice] = import_react.default.useState(null);
|
|
1646
|
+
import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1647
|
+
const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
|
|
1648
|
+
const routeKey3 = route ? profileRouteKey(route) : "";
|
|
1649
|
+
const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
|
|
1650
|
+
let profiles = [];
|
|
1651
|
+
let loadError = "";
|
|
1652
|
+
try {
|
|
1653
|
+
profiles = parseModelProfilesJson(rawJson);
|
|
1654
|
+
} catch (error) {
|
|
1655
|
+
loadError = messageOf(error);
|
|
1656
|
+
}
|
|
1657
|
+
const saved = profiles.find((item) => profileRouteKey(item) === routeKey3);
|
|
1658
|
+
const pending = drafts[routeKey3];
|
|
1659
|
+
const draft = pending?.fields ?? profileDraft(saved);
|
|
1660
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
|
|
1661
|
+
const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
|
|
1662
|
+
const edit = (name, value) => {
|
|
1663
|
+
if (!route || !writable) return;
|
|
1664
|
+
setDrafts((previous) => {
|
|
1665
|
+
const previousEntry = previous[routeKey3] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
|
|
1666
|
+
return { ...previous, [routeKey3]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
|
|
1667
|
+
});
|
|
1668
|
+
setNotice(null);
|
|
1669
|
+
};
|
|
1670
|
+
const write = async (remove) => {
|
|
1671
|
+
if (!route || !writable) return;
|
|
1672
|
+
const revision = pending?.baseRevision ?? snapshot.revision;
|
|
1673
|
+
if (revision !== snapshot.revision) {
|
|
1674
|
+
setNotice({ tone: "error", text: "\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u66F4\u65B0\u3002\u5F53\u524D\u8F93\u5165\u4ECD\u5728\uFF1B\u8BF7\u6838\u5BF9\u6700\u65B0\u5185\u5BB9\uFF0C\u518D\u9009\u62E9\u201C\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF\u201D\u540E\u7F16\u8F91\u3002" });
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
let nextJson;
|
|
1678
|
+
try {
|
|
1679
|
+
nextJson = updateProfileJson(rawJson, route, remove ? null : draft);
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
setSaving(true);
|
|
1685
|
+
setNotice(null);
|
|
1686
|
+
try {
|
|
1687
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: ["modelProfilesJson"], value: nextJson }], revision);
|
|
1688
|
+
if (!accepted) throw new Error("\u8BBE\u7F6E\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002\u8F93\u5165\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002");
|
|
1689
|
+
setDrafts((previous) => {
|
|
1690
|
+
const next = { ...previous };
|
|
1691
|
+
delete next[routeKey3];
|
|
1692
|
+
return next;
|
|
1693
|
+
});
|
|
1694
|
+
setNotice({ tone: "success", text: remove ? "\u5DF2\u5220\u9664\u8BE5\u6A21\u578B\u7684\u81EA\u62A5\u914D\u7F6E\u3002" : "\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u5373\u53EF\u4F7F\u7528\u3002" });
|
|
1695
|
+
onSaved?.();
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1698
|
+
} finally {
|
|
1699
|
+
setSaving(false);
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
const reload = () => {
|
|
1703
|
+
setDrafts((previous) => {
|
|
1704
|
+
const next = { ...previous };
|
|
1705
|
+
delete next[routeKey3];
|
|
1706
|
+
return next;
|
|
1707
|
+
});
|
|
1708
|
+
setNotice(null);
|
|
1709
|
+
};
|
|
1710
|
+
return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value: routeKey3, onChange: (event) => {
|
|
1711
|
+
setSelected(event.target.value);
|
|
1712
|
+
setNotice(null);
|
|
1713
|
+
} }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.execution || "auto", disabled: !writable, onChange: (event) => edit("execution", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u5DF2\u5B89\u88C5\u5219\u7528\u5B98\u65B9\u5DE5\u5177\uFF0C\u5931\u8D25\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "official" }, "\u5B98\u65B9\u5DE5\u5177\uFF1A\u5931\u8D25\u6216\u672A\u5B89\u88C5\u65F6\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api" }, "\u4EC5\u6A21\u578B\u76EE\u5F55 API"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.billing, disabled: !writable, onChange: (event) => edit("billing", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-first" }, "\u8BA2\u9605\u4F18\u5148\uFF1A\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api-only" }, "\u53EA\u7528 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-only" }, "\u53EA\u7528\u8BA2\u9605\uFF1A\u989D\u5EA6\u7528\u5C3D\u65F6\u4E0D\u5207\u6362"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA2\u9605\u6765\u6E90"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.subscription, disabled: !writable, onChange: (event) => edit("subscription", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u6709\u5B98\u65B9 CLI \u65F6\u7528 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "plan-key" }, "\u7F16\u7A0B\u5957\u9910 Key\uFF1A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u5957\u9910\u7AEF\u70B9"), /* @__PURE__ */ import_react.default.createElement("option", { value: "cli-login" }, "\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "none" }, "\u65E0\u8BA2\u9605\uFF1A\u59CB\u7EC8\u6309 API \u8BA1\u8D39"))), draft.subscription === "plan-key" && /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684 API \u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.apiRoute, disabled: !writable, onChange: (event) => edit("apiRoute", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "\u4E0D\u56DE\u9000\uFF08\u672A\u914D\u7F6E API \u8DEF\u7EBF\uFF09"), routes.filter((item) => profileRouteKey(item) !== routeKey3).map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))))), /* @__PURE__ */ import_react.default.createElement("details", { className: "mr-profile-advanced" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u7F13\u5B58\u5355\u4EF7\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u8BFB\u53D6\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheRead, disabled: !writable, onChange: (event) => edit("cacheRead", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u5199\u5165\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheWrite, disabled: !writable, onChange: (event) => edit("cacheWrite", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button", type: "button", disabled: !writable || !pending, onClick: () => {
|
|
1714
|
+
void write(false);
|
|
1715
|
+
} }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u6B64\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !pending || saving, onClick: reload }, "\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF"), saved && /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable, onClick: () => {
|
|
1716
|
+
void write(true);
|
|
1717
|
+
} }, "\u5220\u9664\u6B64\u6A21\u578B\u914D\u7F6E")), notice && /* @__PURE__ */ import_react.default.createElement("p", { className: notice.tone === "error" ? "mr-error" : "mr-profile-success", role: notice.tone === "error" ? "alert" : "status" }, notice.text), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8D28\u91CF\u8BC4\u5206\u548C\u4EF7\u683C\u90FD\u662F\u7528\u6237\u63D0\u4F9B\u7684\u4F30\u503C\u3002\u672A\u586B\u5199\u5355\u4EF7\u65F6\u663E\u793A\u201C\u4EF7\u683C\u5F85\u914D\u7F6E\u201D\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u672C\u5730\u89C4\u5212\uFF0C\u4E0D\u9650\u5236\u5B9E\u9645\u8D26\u5355\u3002CLI \u6A21\u578B\u540D\u987B\u4E0E\u5382\u5546\u5DE5\u5177\u6838\u5BF9\u3002\u6267\u884C\u65B9\u5F0F\u51B3\u5B9A\u8BE5\u6A21\u578B\u6536\u5230\u4EFB\u52A1\u65F6\u8D70\u5B98\u65B9\u65E0\u754C\u9762\u5DE5\u5177\u8FD8\u662F\u6A21\u578B\u76EE\u5F55 API\uFF1B\u5B98\u65B9\u5DE5\u5177\u5931\u8D25\u65F6\u4ECD\u4F1A\u56DE\u9000 API\u3002\u8BA1\u8D39\u65B9\u5F0F\u9ED8\u8BA4\u8BA2\u9605\u4F18\u5148\uFF1A\u5148\u7528 CLI \u8D26\u53F7\u767B\u5F55\u6216\u7F16\u7A0B\u5957\u9910 Key \u8DEF\u7EBF\uFF0C\u8BA2\u9605\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u540C\u4E00\u6B65\u9AA4\u81EA\u52A8\u6539\u7528 API Key\uFF0C\u5E76\u8BB0\u5165\u8FD0\u884C\u5386\u53F2\u3002"))));
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// .dsh-plugin/client/tool-install-state.mjs
|
|
1721
|
+
function stableVersionOrder(left, right) {
|
|
1722
|
+
const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
|
|
1723
|
+
const current = parse(left);
|
|
1724
|
+
const target = parse(right);
|
|
1725
|
+
if (!current || !target) return null;
|
|
1726
|
+
for (let index = 1; index <= 3; index += 1) {
|
|
1727
|
+
const delta = Number(current[index]) - Number(target[index]);
|
|
1728
|
+
if (delta) return Math.sign(delta);
|
|
1729
|
+
}
|
|
1730
|
+
return 0;
|
|
1731
|
+
}
|
|
1732
|
+
function toolInstallAction({ tool, probe, readiness, job, probeStatus }) {
|
|
1733
|
+
const running = job?.status === "running";
|
|
1734
|
+
const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
|
|
1735
|
+
const currentAndReady = versionOrder === 0 && readiness?.ready === true;
|
|
1736
|
+
const repairable = versionOrder === 0 && readiness?.ready === false;
|
|
1737
|
+
const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
|
|
1738
|
+
const label = running ? "\u5B89\u88C5\u4E2D\u2026" : currentAndReady ? "\u5DF2\u662F\u76EE\u6807\u7248\u672C" : repairable ? "\u4FEE\u590D\u5B98\u65B9\u6267\u884C\u5165\u53E3" : newerOrUncertain ? "\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u7248\u672C" : probe?.installed ? "\u66F4\u65B0\u5230\u76EE\u6807\u7248\u672C" : job?.status === "failed" ? "\u91CD\u8BD5\u5B89\u88C5" : tool.manager === "signed-windows-installer" ? "\u4E0B\u8F7D\u5B89\u88C5\u5668" : "\u4E00\u952E\u5B89\u88C5";
|
|
1739
|
+
return {
|
|
1740
|
+
label,
|
|
1741
|
+
disabled: probeStatus !== "ready" || running || currentAndReady || newerOrUncertain
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// .dsh-plugin/client/router-insights.jsx
|
|
1746
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
1747
|
+
|
|
1748
|
+
// .dsh-plugin/client/insights-state.mjs
|
|
1749
|
+
var PACKAGE_STATUS = Object.freeze({
|
|
1750
|
+
pending: Object.freeze({ label: "\u5F85\u6267\u884C", tone: "pending" }),
|
|
1751
|
+
succeeded: Object.freeze({ label: "\u5B98\u65B9 CLI \u5B8C\u6210", tone: "ok" }),
|
|
1752
|
+
fallback: Object.freeze({ label: "\u5DF2\u56DE\u9000 API \u5B8C\u6210", tone: "warn" }),
|
|
1753
|
+
failed: Object.freeze({ label: "\u5931\u8D25", tone: "error" }),
|
|
1754
|
+
blocked: Object.freeze({ label: "\u4F9D\u8D56\u672A\u5B8C\u6210", tone: "blocked" }),
|
|
1755
|
+
cancelled: Object.freeze({ label: "\u5DF2\u53D6\u6D88", tone: "blocked" }),
|
|
1756
|
+
paused: Object.freeze({ label: "\u7B49\u5F85\u786E\u8BA4", tone: "warn" }),
|
|
1757
|
+
waiting: Object.freeze({ label: "\u7B49\u5F85\u4E0A\u6E38\u786E\u8BA4", tone: "blocked" })
|
|
1758
|
+
});
|
|
1759
|
+
function packageStatus(item) {
|
|
1760
|
+
if (item?.status === "succeeded" && item.channel === "harness-llm") return { label: "API \u5B8C\u6210", tone: "ok" };
|
|
1761
|
+
return PACKAGE_STATUS[item?.status] ?? PACKAGE_STATUS.pending;
|
|
1762
|
+
}
|
|
1763
|
+
function dagLayers(packages) {
|
|
1764
|
+
const list = Array.isArray(packages) ? packages : [];
|
|
1765
|
+
const byId = new Map(list.map((item) => [item.id, item]));
|
|
1766
|
+
const depth = /* @__PURE__ */ new Map();
|
|
1767
|
+
const visit = (id2, trail = /* @__PURE__ */ new Set()) => {
|
|
1768
|
+
if (depth.has(id2)) return depth.get(id2);
|
|
1769
|
+
if (trail.has(id2) || !byId.has(id2)) return -1;
|
|
1770
|
+
trail.add(id2);
|
|
1771
|
+
const deps = (byId.get(id2).dependsOn ?? []).map((dep) => visit(dep, trail));
|
|
1772
|
+
const value = deps.length ? Math.max(...deps) + 1 : 0;
|
|
1773
|
+
depth.set(id2, Math.max(0, value));
|
|
1774
|
+
return depth.get(id2);
|
|
1775
|
+
};
|
|
1776
|
+
list.forEach((item) => visit(item.id));
|
|
1777
|
+
const layers = [];
|
|
1778
|
+
for (const item of list) {
|
|
1779
|
+
const level = depth.get(item.id) ?? 0;
|
|
1780
|
+
(layers[level] ?? (layers[level] = [])).push(item);
|
|
1781
|
+
}
|
|
1782
|
+
return layers.filter(Boolean);
|
|
1783
|
+
}
|
|
1784
|
+
var LOGIN_LABEL = Object.freeze({ "logged-in": "\u5DF2\u767B\u5F55", "logged-out": "\u672A\u767B\u5F55", unknown: "\u767B\u5F55\u72B6\u6001\u672A\u77E5" });
|
|
1785
|
+
var VERSION_LABEL = Object.freeze({ ok: "\u7248\u672C\u7B26\u5408", older: "\u7248\u672C\u4F4E\u4E8E\u76EE\u6807", unknown: "\u7248\u672C\u672A\u77E5" });
|
|
1786
|
+
function healthSummary(tools) {
|
|
1787
|
+
const list = Array.isArray(tools) ? tools : [];
|
|
1788
|
+
const installed = list.filter((item) => item.installed);
|
|
1789
|
+
return {
|
|
1790
|
+
total: list.length,
|
|
1791
|
+
installed: installed.length,
|
|
1792
|
+
ready: installed.filter((item) => item.login?.state === "logged-in").length,
|
|
1793
|
+
loggedOut: installed.filter((item) => item.login?.state === "logged-out").length,
|
|
1794
|
+
unknown: installed.filter((item) => item.login?.state === "unknown").length,
|
|
1795
|
+
older: installed.filter((item) => item.versionStatus === "older").length
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
var money = formatUsd;
|
|
1799
|
+
function budgetMeter(spent, limit) {
|
|
1800
|
+
if (!(limit > 0)) return { limited: false, share: 0, text: `${money(spent)}\uFF08\u672A\u8BBE\u4E0A\u9650\uFF09` };
|
|
1801
|
+
const share = Math.min(1, Math.max(0, spent / limit));
|
|
1802
|
+
return { limited: true, share, over: spent >= limit, text: `${money(spent)} / ${money(limit)}` };
|
|
1803
|
+
}
|
|
1804
|
+
function planBudget(ledger, estimateUsd) {
|
|
1805
|
+
if (!ledger?.budget) return null;
|
|
1806
|
+
return budgetCheck({
|
|
1807
|
+
estimateUsd,
|
|
1808
|
+
spent: ledger.spent ?? { today: 0, month: 0 },
|
|
1809
|
+
dailyLimitUsd: ledger.budget.dailyLimitUsd,
|
|
1810
|
+
monthlyLimitUsd: ledger.budget.monthlyLimitUsd
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
function unwrapRemote(response, fallback) {
|
|
1814
|
+
if (!response?.ok) throw new Error(String(response?.error?.message ?? "") || fallback);
|
|
1815
|
+
const inner = response.value;
|
|
1816
|
+
if (inner && typeof inner === "object" && "ok" in inner && !inner.ok) {
|
|
1817
|
+
const error = inner.error;
|
|
1818
|
+
throw new Error(String((error && typeof error === "object" ? error.message : error) ?? "") || fallback);
|
|
1819
|
+
}
|
|
1820
|
+
return inner && typeof inner === "object" && "ok" in inner ? inner.value : inner;
|
|
1821
|
+
}
|
|
1822
|
+
var RUN_KIND_LABEL = Object.freeze({ assign: "\u8DEF\u7531\u6267\u884C", team: "\u56E2\u961F\u6267\u884C", tool: "\u5355\u5DE5\u5177\u8C03\u7528" });
|
|
1823
|
+
var RUN_STATUS_LABEL = Object.freeze({
|
|
1824
|
+
completed: "\u5B8C\u6210",
|
|
1825
|
+
partial: "\u90E8\u5206\u5B8C\u6210",
|
|
1826
|
+
failed: "\u5931\u8D25",
|
|
1827
|
+
"cli-completed": "\u5B8C\u6210",
|
|
1828
|
+
incomplete: "\u672A\u5B8C\u6210",
|
|
1829
|
+
paused: "\u7B49\u5F85\u786E\u8BA4",
|
|
1830
|
+
"integration-pending": "\u5F85\u6574\u5408",
|
|
1831
|
+
blocked: "\u88AB\u963B\u6B62",
|
|
1832
|
+
cancelled: "\u5DF2\u53D6\u6D88",
|
|
1833
|
+
pending: "\u5F85\u6267\u884C"
|
|
1834
|
+
});
|
|
1835
|
+
function packageCost(item) {
|
|
1836
|
+
if (item?.billing === "subscription") {
|
|
1837
|
+
return {
|
|
1838
|
+
budget: "\u4E0D\u8BA1\u5165\u9884\u7B97",
|
|
1839
|
+
reference: item.referenceCostUsd !== null && item.referenceCostUsd !== void 0 ? `\u8BA2\u9605\u53C2\u8003\u8D39\u7528 ${money(item.referenceCostUsd)}\uFF08\u6309 API \u4EF7\u6298\u7B97\uFF09` : "\u8BA2\u9605\u767B\u5F55\uFF0C\u672A\u56DE\u62A5\u53EF\u6298\u7B97\u7684\u7528\u91CF"
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
if (item?.costUsd !== null && item?.costUsd !== void 0) {
|
|
1843
|
+
return { budget: `${money(item.costUsd)}${item.costSource === "cli-reported" ? "\uFF08CLI \u81EA\u62A5\uFF09" : ""}`, reference: null };
|
|
1844
|
+
}
|
|
1845
|
+
return { budget: item?.ran ? "\u8D39\u7528\u672A\u77E5" : "\u2014", reference: null };
|
|
1846
|
+
}
|
|
1847
|
+
function runTotals(run) {
|
|
1848
|
+
const items = [...run?.packages ?? [], ...run?.reviews ?? []];
|
|
1849
|
+
let budgetUsd = 0;
|
|
1850
|
+
let referenceUsd = 0;
|
|
1851
|
+
let subscription = false;
|
|
1852
|
+
for (const item of items) {
|
|
1853
|
+
if (item.billing === "subscription") {
|
|
1854
|
+
subscription = true;
|
|
1855
|
+
if (typeof item.referenceCostUsd === "number") referenceUsd += item.referenceCostUsd;
|
|
1856
|
+
} else if (typeof item.costUsd === "number") budgetUsd += item.costUsd;
|
|
1857
|
+
}
|
|
1858
|
+
return { budgetUsd, referenceUsd, subscription };
|
|
1859
|
+
}
|
|
1860
|
+
function rerunSupport(run) {
|
|
1861
|
+
if (run?.kind === "tool") {
|
|
1862
|
+
const editable = run.executionMode === "workspace-write";
|
|
1863
|
+
return {
|
|
1864
|
+
supported: true,
|
|
1865
|
+
writes: editable,
|
|
1866
|
+
reassign: false,
|
|
1867
|
+
reason: "",
|
|
1868
|
+
confirm: editable ? "\u91CD\u8DD1\u4F1A\u5728\u65B0\u7684\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u518D\u6B21\u6267\u884C\u8FD9\u6B21\u53EF\u7F16\u8F91\u8C03\u7528\uFF0C\u6210\u529F\u540E\u628A\u6539\u52A8\u6574\u5408\u56DE\u5DE5\u4F5C\u533A\uFF08\u5DE5\u4F5C\u533A\u987B\u4E3A\u5E72\u51C0\u7684 Git \u4ED3\u5E93\uFF09\u3002" : ""
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
if (run?.kind === "team" && run.executionMode === "workspace-write") {
|
|
1872
|
+
if (!["incomplete", "cancelled"].includes(run.status)) {
|
|
1873
|
+
return { supported: false, writes: true, reassign: false, reason: run.status === "integration-pending" ? "\u6539\u52A8\u5C1A\u5F85\u4EBA\u5DE5\u6574\u5408\uFF08\u72EC\u7ACB\u5DE5\u4F5C\u533A\u5DF2\u4FDD\u7559\uFF09\uFF0C\u4E0D\u80FD\u5355\u6B65\u91CD\u8DD1\uFF1B\u8BF7\u5148\u6838\u5BF9\u5E76\u6574\u5408\uFF0C\u518D\u5728\u4F1A\u8BDD\u4E2D\u91CD\u65B0\u6267\u884C\u56E2\u961F\u4EFB\u52A1\u3002" : "\u6539\u52A8\u5DF2\u6574\u5408\u5230\u5DE5\u4F5C\u533A\uFF0C\u5355\u6B65\u91CD\u8DD1\u4F1A\u91CD\u590D\u5957\u7528\u6539\u52A8\uFF1B\u5982\u9700\u91CD\u505A\u8BF7\u5728\u4F1A\u8BDD\u4E2D\u91CD\u65B0\u8C03\u7528 model_router_team_execute\u3002" };
|
|
1874
|
+
}
|
|
1875
|
+
if (!run.isolatedWorkspace || !run.baseCommit) {
|
|
1876
|
+
return { supported: false, writes: true, reassign: false, reason: "\u65E7\u7248\u672C\u8BB0\u5F55\u7F3A\u5C11\u72EC\u7ACB\u5DE5\u4F5C\u533A\u6216 Git \u57FA\u7EBF\uFF0C\u65E0\u6CD5\u5B89\u5168\u7EED\u8DD1\uFF1B\u8BF7\u5728\u4F1A\u8BDD\u4E2D\u91CD\u65B0\u8C03\u7528 model_router_team_execute\u3002" };
|
|
1877
|
+
}
|
|
1878
|
+
return {
|
|
1879
|
+
supported: true,
|
|
1880
|
+
writes: true,
|
|
1881
|
+
reassign: true,
|
|
1882
|
+
reason: "",
|
|
1883
|
+
confirm: "\u7EED\u8DD1\u4F1A\u5728\u65B0\u7684\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5148\u5957\u7528\u4E4B\u524D\u7684\u6539\u52A8\uFF0C\u518D\u91CD\u8DD1\u8BE5\u6B65\u9AA4\u53CA\u5176\u4E0B\u6E38\uFF1B\u6210\u529F\u540E\u628A\u5408\u5E76\u8865\u4E01\u6574\u5408\u56DE\u5DE5\u4F5C\u533A\uFF08\u5DE5\u4F5C\u533A\u987B\u5E72\u51C0\u4E14\u4ECD\u5728\u539F\u57FA\u7EBF\u63D0\u4EA4\uFF09\u3002"
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
return { supported: true, writes: false, reassign: true, reason: "", confirm: "" };
|
|
1887
|
+
}
|
|
1888
|
+
var pad2 = (value) => String(value).padStart(2, "0");
|
|
1889
|
+
function formatResetTime(at) {
|
|
1890
|
+
if (!Number.isFinite(at)) return "";
|
|
1891
|
+
const date = new Date(at);
|
|
1892
|
+
return `${date.getMonth() + 1}-${date.getDate()} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
|
|
1893
|
+
}
|
|
1894
|
+
var BILLING_CHANNEL_LABEL = Object.freeze({ subscription: "\u8BA2\u9605", api: "API Key" });
|
|
1895
|
+
function billingRows(billing) {
|
|
1896
|
+
return (billing?.providers ?? []).map((row) => {
|
|
1897
|
+
const sub = row.subscription ?? {};
|
|
1898
|
+
const subscription = sub.kind === "plan-key" ? `\u7F16\u7A0B\u5957\u9910 Key${sub.planRoute ? `\uFF08${sub.planRoute.provider}/${sub.planRoute.model}\uFF09` : ""}` : sub.kind === "cli-login" ? `\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55 \xB7 ${sub.toolId}` : "\u65E0\u8BA2\u9605";
|
|
1899
|
+
const state = sub.state === "exhausted" ? `${sub.exhaustedKind === "rate-limit" ? "\u9650\u6D41\u4E2D" : "\u989D\u5EA6\u5DF2\u7528\u5C3D"}\uFF0C\u9884\u8BA1 ${formatResetTime(sub.exhaustedUntil)} \u6062\u590D${sub.resetReported ? "" : "\uFF08\u5382\u5546\u672A\u7ED9\u51FA\u65F6\u95F4\uFF0C\u6309\u51B7\u5374\u65F6\u95F4\u4F30\u7B97\uFF09"}` : sub.stateLabel ?? "\u2014";
|
|
1900
|
+
const api = row.mode === "subscription-only" ? "\u4E0D\u56DE\u9000\uFF08\u53EA\u7528\u8BA2\u9605\uFF09" : row.api?.available ? `\u53EF\u7528${row.api.route ? `\uFF1A${row.api.route.provider}/${row.api.route.model}` : ""}${row.api.cliKeyEnv ? " \xB7 CLI \u73AF\u5883\u53D8\u91CF\u4E5F\u6709 Key" : ""}` : "\u672A\u914D\u7F6E\u56DE\u9000\u7684 API \u8DEF\u7EBF";
|
|
1901
|
+
return {
|
|
1902
|
+
key: `${row.provider}\0${sub.key ?? "none"}\0${row.mode}`,
|
|
1903
|
+
provider: row.provider,
|
|
1904
|
+
models: (row.models ?? []).join("\u3001"),
|
|
1905
|
+
mode: row.modeLabel ?? row.mode,
|
|
1906
|
+
subscription,
|
|
1907
|
+
state,
|
|
1908
|
+
exhausted: sub.state === "exhausted",
|
|
1909
|
+
api,
|
|
1910
|
+
apiAvailable: row.api?.available === true
|
|
1911
|
+
};
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
function billingSwitchText(item) {
|
|
1915
|
+
const note = item?.billingSwitch;
|
|
1916
|
+
if (!note?.reason) return null;
|
|
1917
|
+
return note.reason;
|
|
1918
|
+
}
|
|
1919
|
+
function launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace } = {}) {
|
|
1920
|
+
const budget = Number(budgetUsd);
|
|
1921
|
+
return {
|
|
1922
|
+
task: typeof task === "string" ? task : "",
|
|
1923
|
+
...mode === "direct" && directRoute ? { provider: directRoute.provider, model: directRoute.model } : { planMode: mode === "team" ? "team" : "single", preset },
|
|
1924
|
+
...Number.isFinite(budget) && budget >= 0 ? { budgetUsd: budget } : {},
|
|
1925
|
+
...typeof workspace === "string" && workspace.trim() ? { workspace: workspace.trim() } : {}
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
function rerunConfirmations({ run, item, override = null, choice = null, ledger = null, health = null, toolFor = () => null, headlessIds = /* @__PURE__ */ new Set() } = {}) {
|
|
1929
|
+
const reasons = [];
|
|
1930
|
+
const support = rerunSupport(run);
|
|
1931
|
+
if (support.writes) reasons.push({ code: "workspace-write", text: support.confirm });
|
|
1932
|
+
if (choice === "api") reasons.push({ code: "subscription-api", text: "\u6539\u7528 API Key \u91CD\u8BD5\uFF1A\u4F1A\u6309 API \u8BA1\u8D39\u5E76\u8BA1\u5165\u9884\u7B97\u3002" });
|
|
1933
|
+
const tool = toolFor(override?.provider ?? item?.provider);
|
|
1934
|
+
const entry = (health?.tools ?? []).find((candidate) => candidate.id === tool?.id);
|
|
1935
|
+
if (choice !== "api" && choice !== "cancel" && run?.kind !== "team" && run?.kind !== "tool" && ledger?.settings?.confirmUnsandboxedCli !== false && tool && headlessIds.has(tool.id) && entry?.installed && entry.login?.state !== "logged-out") {
|
|
1936
|
+
reasons.push({ code: "unsandboxed", text: `\u4F1A\u76F4\u63A5\u542F\u52A8 ${tool.label} \u7684\u65E0\u754C\u9762 CLI\uFF0C\u4E0D\u7ECF\u8FC7 Harness \u8FDB\u7A0B\u6C99\u7BB1\uFF0C\u53EA\u8BFB\u4EC5\u7531 CLI \u53C2\u6570\u4FDD\u8BC1\u3002` });
|
|
1937
|
+
}
|
|
1938
|
+
const estimate = override ? null : item?.estimatedCost;
|
|
1939
|
+
const check = ledger?.budget ? budgetCheck({
|
|
1940
|
+
estimateUsd: typeof estimate === "number" ? estimate : null,
|
|
1941
|
+
spent: ledger.spent ?? {},
|
|
1942
|
+
dailyLimitUsd: ledger.budget.dailyLimitUsd,
|
|
1943
|
+
monthlyLimitUsd: ledger.budget.monthlyLimitUsd
|
|
1944
|
+
}) : null;
|
|
1945
|
+
if (check?.exceeded) reasons.push({ code: "over-budget", text: `\u8D85\u51FA\u9884\u7B97\uFF1A${check.message}` });
|
|
1946
|
+
return reasons;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// .dsh-plugin/client/router-insights.jsx
|
|
1950
|
+
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
1951
|
+
var BAND = { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" };
|
|
1952
|
+
var SUBSCRIPTION_FAILURE_OPTIONS = [["ask", "\u6682\u505C\u5E76\u8BE2\u95EE"], ["api", "\u81EA\u52A8\u6539\u7528 API"], ["fail", "\u76F4\u63A5\u5931\u8D25"]];
|
|
1953
|
+
var SUBSCRIPTION_CHOICE_BUTTONS = [["api", "\u6539\u7528 API \u91CD\u8BD5", false], ["subscription", "\u91CD\u8BD5\u8BA2\u9605", true], ["cancel", "\u53D6\u6D88", true]];
|
|
1954
|
+
function OnboardingBanner({ health, onDone, onRefresh, refreshing, error }) {
|
|
1955
|
+
const summary = healthSummary(health?.tools);
|
|
1956
|
+
const problems = (health?.tools ?? []).filter((item) => item.installed && (item.login?.state === "logged-out" || item.versionStatus === "older"));
|
|
1957
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-onboarding", "aria-label": "\u5F00\u7BB1\u4F53\u68C0" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u5F00\u7BB1\u4F53\u68C0"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u9996\u6B21\u6253\u5F00\u65F6\u68C0\u6D4B\u6BCF\u4E2A\u5B98\u65B9\u5DE5\u5177\u662F\u5426\u5DF2\u5B89\u88C5\u3001\u7248\u672C\u662F\u5426\u7B26\u5408\u3001\u662F\u5426\u5DF2\u767B\u5F55\u3002\u672A\u767B\u5F55\u7684\u5DE5\u5177\u4F1A\u88AB\u8DEF\u7531\u76F4\u63A5\u8DF3\u8FC7\uFF0C\u6539\u8D70\u6A21\u578B\u76EE\u5F55 API\uFF0C\u4E0D\u5FC5\u7B49\u5F85 CLI \u5931\u8D25\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: refreshing, onClick: onRefresh }, refreshing ? "\u68C0\u6D4B\u4E2D\u2026" : "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error), !health && !error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u4F53\u68C0\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), health && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u5DF2\u5B89\u88C5"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, summary.installed, " / ", summary.total)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u5DF2\u767B\u5F55\u53EF\u7528"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, summary.ready)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u672A\u767B\u5F55"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, summary.loggedOut)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u767B\u5F55\u72B6\u6001\u672A\u77E5"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, summary.unknown))), problems.length > 0 ? /* @__PURE__ */ import_react2.default.createElement("ul", { className: "mr-checklist" }, problems.map((item) => /* @__PURE__ */ import_react2.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react2.default.createElement("strong", null, item.label), "\uFF1A", item.login?.state === "logged-out" ? `\u672A\u767B\u5F55\uFF0C\u53EF\u5728\u4E0B\u65B9\u70B9\u201C\u53BB\u767B\u5F55\u201D\u67E5\u770B\u767B\u5F55\u65B9\u6CD5\uFF08${item.login.command ?? "\u89C1\u8BF4\u660E"}\uFF09\u3002` : "", item.versionStatus === "older" ? `\u5F53\u524D ${item.version}\uFF0C\u4F4E\u4E8E\u76EE\u6807 ${item.pinnedVersion}\uFF0C\u53EF\u5728\u4E0B\u65B9\u66F4\u65B0\u3002` : ""))) : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5DF2\u5B89\u88C5\u7684\u5DE5\u5177\u6CA1\u6709\u53D1\u73B0\u672A\u767B\u5F55\u6216\u7248\u672C\u8FC7\u65E7\u7684\u95EE\u9898\u3002\u672A\u5B89\u88C5\u7684\u5DE5\u5177\u53EF\u5728\u4E0B\u65B9\u4E00\u952E\u5B89\u88C5\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", onClick: onDone }, "\u5B8C\u6210\u4F53\u68C0"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E4B\u540E\u53EF\u968F\u65F6\u5728\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u5361\u7247\u4E2D\u91CD\u65B0\u4F53\u68C0\u3002")))));
|
|
1958
|
+
}
|
|
1959
|
+
function ToolLoginLine({ entry }) {
|
|
1960
|
+
const [open, setOpen] = import_react2.default.useState(false);
|
|
1961
|
+
const [copied, setCopied] = import_react2.default.useState(false);
|
|
1962
|
+
if (!entry?.installed) return null;
|
|
1963
|
+
const state = entry.login?.state ?? "unknown";
|
|
1964
|
+
const copy = async () => {
|
|
1965
|
+
try {
|
|
1966
|
+
await navigator.clipboard.writeText(entry.login.command);
|
|
1967
|
+
setCopied(true);
|
|
1968
|
+
} catch {
|
|
1969
|
+
setCopied(false);
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-login" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-tool-dot ${state === "logged-in" ? "installed" : state === "logged-out" ? "missing-strong" : ""}` }), LOGIN_LABEL[state], " \xB7 ", VERSION_LABEL[entry.versionStatus] ?? "\u7248\u672C\u672A\u77E5", entry.pinnedVersion ? `\uFF08\u76EE\u6807 ${entry.pinnedVersion}\uFF09` : ""), entry.login?.detail && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, entry.login.detail), state === "logged-in" && entry.login?.billing === "subscription" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, "\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\uFF1A\u8D39\u7528\u53EA\u4F5C\u53C2\u8003\u663E\u793A\uFF0C\u4E0D\u8BA1\u5165\u9884\u7B97\u3002"), state === "logged-in" && entry.login?.billing === "api-key" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, "API Key \u8BA1\u8D39\uFF1A\u8D39\u7528\u8BA1\u5165\u6BCF\u65E5/\u6BCF\u6708\u9884\u7B97\u3002"), state !== "logged-in" && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button mr-login-button", type: "button", "aria-expanded": open, onClick: () => setOpen((value) => !value) }, "\u53BB\u767B\u5F55"), open && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-login-guide" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, entry.login.steps), entry.login.command && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-login-command" }, /* @__PURE__ */ import_react2.default.createElement("code", { className: "mr-tool-command" }, entry.login.command), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => {
|
|
1973
|
+
void copy();
|
|
1974
|
+
} }, copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u547D\u4EE4")), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u63D2\u4EF6\u4E0D\u4F1A\u66FF\u4F60\u542F\u52A8\u4EA4\u4E92\u5F0F\u767B\u5F55\u3002\u767B\u5F55\u5B8C\u6210\u540E\u70B9\u201C\u91CD\u65B0\u4F53\u68C0\u201D\u3002")));
|
|
1975
|
+
}
|
|
1976
|
+
function Meter({ label, meter }) {
|
|
1977
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-meter" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-meter-top" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, label), /* @__PURE__ */ import_react2.default.createElement("span", { className: meter.over ? "mr-meter-over" : "" }, meter.text)), meter.limited && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-meter-track", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(meter.share * 100) }, /* @__PURE__ */ import_react2.default.createElement("div", { className: `mr-meter-fill ${meter.over ? "over" : meter.share > 0.8 ? "near" : ""}`, style: { width: `${Math.round(meter.share * 100)}%` } })));
|
|
1978
|
+
}
|
|
1979
|
+
function useSettingsWriter(settingsScope) {
|
|
1980
|
+
const [snapshot, setSnapshot] = import_react2.default.useState(() => settingsScope.getSnapshot());
|
|
1981
|
+
const [notice, setNotice] = import_react2.default.useState("");
|
|
1982
|
+
import_react2.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1983
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true;
|
|
1984
|
+
const write = async (key, value) => {
|
|
1985
|
+
if (!writable) return false;
|
|
1986
|
+
setNotice("");
|
|
1987
|
+
try {
|
|
1988
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }], snapshot.revision);
|
|
1989
|
+
if (!accepted) setNotice("\u8BBE\u7F6E\u672A\u4FDD\u5B58\uFF0C\u53EF\u80FD\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
1990
|
+
return accepted;
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
setNotice(text(error?.message) || "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
return { value: snapshot.value ?? {}, writable, write, notice };
|
|
1997
|
+
}
|
|
1998
|
+
function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
1999
|
+
const settings = useSettingsWriter(settingsScope);
|
|
2000
|
+
const [dailyDraft, setDailyDraft] = import_react2.default.useState("");
|
|
2001
|
+
const [monthlyDraft, setMonthlyDraft] = import_react2.default.useState("");
|
|
2002
|
+
const value = settings.value;
|
|
2003
|
+
const preset = value.routingPreset ?? "balanced";
|
|
2004
|
+
const daily = Number(value.dailyBudgetUsd ?? 0);
|
|
2005
|
+
const monthly = Number(value.monthlyBudgetUsd ?? 0);
|
|
2006
|
+
const spent = ledger?.spent ?? { today: 0, month: 0, unknownToday: 0, unknownMonth: 0 };
|
|
2007
|
+
const set = async (key, next) => {
|
|
2008
|
+
if (await settings.write(key, next)) onChanged?.();
|
|
2009
|
+
};
|
|
2010
|
+
const saveLimit = async (key, draft, reset) => {
|
|
2011
|
+
const parsed = Number(draft);
|
|
2012
|
+
if (draft === "" || !Number.isFinite(parsed) || parsed < 0) return;
|
|
2013
|
+
if (await settings.write(key, parsed)) {
|
|
2014
|
+
reset("");
|
|
2015
|
+
onChanged?.();
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-cost", "aria-label": "\u6210\u672C\u63A7\u5236" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u6210\u672C\u63A7\u5236\u4E0E\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u9884\u7B97\u53EA\u8BA1\u7B97\u8D70 API Key \u6216\u6A21\u578B\u76EE\u5F55 API \u7684\u82B1\u8D39\uFF08CLI \u81EA\u62A5\u91D1\u989D\u6216 token \u7528\u91CF \xD7 \u4F60\u914D\u7F6E\u7684\u5355\u4EF7\uFF09\u3002\u5B98\u65B9 CLI \u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\u3001\u672A\u6CE8\u5165 API Key \u65F6\uFF0C\u53EA\u663E\u793A\u6309 API \u4EF7\u6298\u7B97\u7684\u201C\u8BA2\u9605\u53C2\u8003\u8D39\u7528\u201D\uFF0C\u4E0D\u8BA1\u5165\u9884\u7B97\u3002\u7F3A\u5C11\u5355\u4EF7\u7684\u8C03\u7528\u8BA1\u4E3A\u201C\u8D39\u7528\u672A\u77E5\u201D\u3002\u8FD9\u662F\u672C\u673A\u4F30\u7B97\uFF0C\u4E0D\u662F\u5382\u5546\u8D26\u5355\u3002"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-meters" }, /* @__PURE__ */ import_react2.default.createElement(Meter, { label: "\u4ECA\u65E5\u5DF2\u7528\uFF08\u8BA1\u5165\u9884\u7B97\uFF09", meter: budgetMeter(spent.today, daily) }), /* @__PURE__ */ import_react2.default.createElement(Meter, { label: "\u672C\u6708\u5DF2\u7528\uFF08\u8BA1\u5165\u9884\u7B97\uFF09", meter: budgetMeter(spent.month, monthly) })), (spent.subscriptionRunsMonth ?? 0) > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8BA2\u9605\u53C2\u8003\u8D39\u7528\uFF08\u6309 API \u4EF7\u6298\u7B97\uFF0C\u4E0D\u8BA1\u5165\u9884\u7B97\uFF09\uFF1A\u4ECA\u65E5 ", money(spent.subscriptionToday ?? 0), " \xB7 \u672C\u6708 ", money(spent.subscriptionMonth ?? 0), "\uFF0C\u672C\u6708\u5171 ", spent.subscriptionRunsMonth, " \u6B21\u8BA2\u9605\u767B\u5F55\u8C03\u7528\u3002"), (spent.unknownToday > 0 || spent.unknownMonth > 0) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u672C\u6708\u6709 ", spent.unknownMonth, " \u6B21\u8C03\u7528\u7F3A\u5C11\u5355\u4EF7\u6216\u7528\u91CF\uFF0C\u672A\u8BA1\u5165\u91D1\u989D\u3002\u8BF7\u5728\u201C\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u201D\u4E2D\u8865\u9F50\u5355\u4EF7\u3002"), ledger?.budget?.exceeded && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, ledger.budget.message, " ", ledger.budget.action === "pause" ? "\u65B0\u7684\u6267\u884C\u4F1A\u5148\u6682\u505C\u5E76\u8BF7\u6C42\u786E\u8BA4\u3002" : "\u65B0\u7684\u6267\u884C\u4F1A\u5C1D\u8BD5\u81EA\u52A8\u964D\u7EA7\u4E3A\u201C\u7701\u94B1\u4F18\u5148\u201D\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u8DEF\u7531\u65B9\u6848" }, Object.values(ROUTING_PRESETS).map((item) => /* @__PURE__ */ import_react2.default.createElement("button", { key: item.id, type: "button", title: item.description, "aria-pressed": preset === item.id, disabled: !settings.writable, onClick: () => {
|
|
2019
|
+
void set("routingPreset", item.id);
|
|
2020
|
+
} }, item.label))), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, ROUTING_PRESETS[preset]?.description)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-daily-budget" }, "\u6BCF\u65E5\u4E0A\u9650\uFF08USD\uFF0C0 \u4E3A\u4E0D\u9650\uFF09"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input mr-budget", id: "mr-daily-budget", type: "number", min: "0", step: "0.5", placeholder: String(daily), value: dailyDraft, onChange: (event) => setDailyDraft(event.target.value) }), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || dailyDraft === "", onClick: () => {
|
|
2021
|
+
void saveLimit("dailyBudgetUsd", dailyDraft, setDailyDraft);
|
|
2022
|
+
} }, "\u4FDD\u5B58"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-monthly-budget" }, "\u6BCF\u6708\u4E0A\u9650\uFF08USD\uFF0C0 \u4E3A\u4E0D\u9650\uFF09"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input mr-budget", id: "mr-monthly-budget", type: "number", min: "0", step: "1", placeholder: String(monthly), value: monthlyDraft, onChange: (event) => setMonthlyDraft(event.target.value) }), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || monthlyDraft === "", onClick: () => {
|
|
2023
|
+
void saveLimit("monthlyBudgetUsd", monthlyDraft, setMonthlyDraft);
|
|
2024
|
+
} }, "\u4FDD\u5B58"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u8D85\u51FA\u9884\u7B97\u65F6"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u8D85\u51FA\u9884\u7B97\u65F6" }, /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": (value.overBudgetAction ?? "downgrade") === "downgrade", disabled: !settings.writable, onClick: () => {
|
|
2025
|
+
void set("overBudgetAction", "downgrade");
|
|
2026
|
+
} }, "\u81EA\u52A8\u964D\u7EA7"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": value.overBudgetAction === "pause", disabled: !settings.writable, onClick: () => {
|
|
2027
|
+
void set("overBudgetAction", "pause");
|
|
2028
|
+
} }, "\u6682\u505C\u5E76\u8BE2\u95EE"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u8BA2\u9605\u8C03\u7528\u5931\u8D25\uFF08\u975E\u989D\u5EA6\u7528\u5C3D\uFF09\u65F6"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u8BA2\u9605\u8C03\u7528\u5931\u8D25\u65F6" }, SUBSCRIPTION_FAILURE_OPTIONS.map(([id2, label]) => /* @__PURE__ */ import_react2.default.createElement("button", { key: id2, type: "button", "aria-pressed": (value.onSubscriptionFailure ?? "ask") === id2, disabled: !settings.writable, onClick: () => {
|
|
2029
|
+
void set("onSubscriptionFailure", id2);
|
|
2030
|
+
} }, label))), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u8D85\u65F6\u3001\u5D29\u6E83\u3001\u89E3\u6790\u5931\u8D25\u3001\u975E\u96F6\u9000\u51FA\u3001\u767B\u5F55\u9519\u8BEF\u7B49\u3002\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u4ECD\u4F1A\u81EA\u52A8\u5207\u6362 API Key\u3002"))), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u8D28\u91CF\u56DE\u8DEF"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u5F3A\u6A21\u578B\u62BD\u67E5"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u5F3A\u6A21\u578B\u62BD\u67E5" }, [["off", "\u5173\u95ED"], ["sample", "\u6309\u6BD4\u4F8B\u62BD\u67E5"], ["always", "\u6BCF\u6B21\u5BA1\u9605"]].map(([id2, label]) => /* @__PURE__ */ import_react2.default.createElement("button", { key: id2, type: "button", "aria-pressed": (value.reviewMode ?? "off") === id2, disabled: !settings.writable, onClick: () => {
|
|
2031
|
+
void set("reviewMode", id2);
|
|
2032
|
+
} }, label))), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u62BD\u67E5\u7531\u76EE\u5F55\u4E2D\u8D28\u91CF\u6700\u9AD8\u7684\u5DF2\u914D\u7F6E\u6A21\u578B\u5B8C\u6210\uFF0C\u53EA\u5BA1\u9605\u66F4\u4FBF\u5B9C\u6A21\u578B\u7684\u7ED3\u679C\uFF1B\u5BA1\u9605\u672C\u8EAB\u4E5F\u4F1A\u8BA1\u8D39\u3002\u62BD\u67E5\u6BD4\u4F8B ", Math.round(Number(value.reviewSampleRate ?? 0.2) * 100), "%\uFF08\u53EF\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u8C03\u6574\uFF09\u3002")), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement("input", { type: "checkbox", checked: value.allowManualReassign !== false, disabled: !settings.writable, onChange: (event) => {
|
|
2033
|
+
void set("allowManualReassign", event.target.checked);
|
|
2034
|
+
} }), "\u5141\u8BB8\u624B\u52A8\u6539\u6D3E\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement("input", { type: "checkbox", checked: value.confirmUnsandboxedCli !== false, disabled: !settings.writable, onChange: (event) => {
|
|
2035
|
+
void set("confirmUnsandboxedCli", event.target.checked);
|
|
2036
|
+
} }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
|
|
2037
|
+
}
|
|
2038
|
+
function DagView({ packages, renderNode, label = "\u5B50\u4EFB\u52A1\u4F9D\u8D56\u56FE" }) {
|
|
2039
|
+
const layers = dagLayers(packages);
|
|
2040
|
+
if (layers.length === 0) return null;
|
|
2041
|
+
const names = new Map((packages ?? []).map((item) => [item.id, item.name]));
|
|
2042
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-dag", role: "list", "aria-label": label }, layers.map((layer, index) => /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-dag-column", key: index }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-dag-step" }, "\u7B2C ", index + 1, " \u6B65"), layer.map((item) => {
|
|
2043
|
+
const status = packageStatus(item);
|
|
2044
|
+
return /* @__PURE__ */ import_react2.default.createElement("article", { className: `mr-dag-node ${status.tone}`, role: "listitem", key: item.id }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-dag-node-top" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-package-name" }, item.name), /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-pill mr-status-${status.tone}` }, status.label)), (item.dependsOn ?? []).length > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u4F9D\u8D56\uFF1A", item.dependsOn.map((id2) => names.get(id2) ?? id2).join("\u3001")), renderNode?.(item));
|
|
2045
|
+
}))));
|
|
2046
|
+
}
|
|
2047
|
+
function ChannelText({ item }) {
|
|
2048
|
+
if (!item.channel) return /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-pill" }, "\u5F85\u6267\u884C");
|
|
2049
|
+
return /* @__PURE__ */ import_react2.default.createElement("span", { className: item.channel === "official-cli" ? "mr-pill mr-pill-channel-ok" : "mr-pill" }, item.channel === "official-cli" ? `\u5B98\u65B9 CLI \xB7 ${item.toolId ?? ""}` : "\u6A21\u578B\u76EE\u5F55 API");
|
|
2050
|
+
}
|
|
2051
|
+
function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
|
|
2052
|
+
if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
|
|
2053
|
+
const [target, setTarget] = import_react2.default.useState("");
|
|
2054
|
+
const cost = packageCost(item);
|
|
2055
|
+
const rerun = rerunSupport(run);
|
|
2056
|
+
const canRerun = rerun.supported && !item.ok && !busy;
|
|
2057
|
+
const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
|
|
2058
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(run.id, item.id, item.rating === 1 ? "clear" : "up") }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(run.id, item.id, item.rating === -1 ? "clear" : "down") }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
|
|
2059
|
+
const [provider, model] = target.split("\0");
|
|
2060
|
+
onRerun(run.id, item.id, { provider, model });
|
|
2061
|
+
} }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
|
|
2062
|
+
}
|
|
2063
|
+
function PausedNode({ run, item, busy, onRerun }) {
|
|
2064
|
+
const pause = item.pause ?? {};
|
|
2065
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-pill mr-pill-warn" }, "\u7B49\u5F85\u786E\u8BA4"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u8BA2\u9605\u8C03\u7528\u5931\u8D25\uFF0C\u672A\u81EA\u52A8\u6539\u7528 API Key")), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, pause.reason ?? item.error), pause.detail && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "\u771F\u5B9E\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, pause.detail)), pause.loginRequired && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u770B\u8D77\u6765\u662F\u767B\u5F55\u95EE\u9898\uFF1A\u8BF7\u5148\u5728\u7EC8\u7AEF\u91CD\u65B0\u767B\u5F55\u8BE5 CLI\uFF0C\u518D\u70B9\u201C\u91CD\u8BD5\u8BA2\u9605\u201D\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, SUBSCRIPTION_CHOICE_BUTTONS.map(([choice, label, secondary]) => /* @__PURE__ */ import_react2.default.createElement(
|
|
2066
|
+
"button",
|
|
2067
|
+
{
|
|
2068
|
+
key: choice,
|
|
2069
|
+
className: `mr-button mr-mini${secondary ? " mr-button-secondary" : ""}`,
|
|
2070
|
+
type: "button",
|
|
2071
|
+
disabled: busy,
|
|
2072
|
+
onClick: () => onRerun(run.id, item.id, null, choice)
|
|
2073
|
+
},
|
|
2074
|
+
label
|
|
2075
|
+
))));
|
|
2076
|
+
}
|
|
2077
|
+
function RunHistoryCard({ ledger, routes, onRefresh, onRate, onRerun, busy, error }) {
|
|
2078
|
+
const runs = ledger?.runs ?? [];
|
|
2079
|
+
const [openId, setOpenId] = import_react2.default.useState(null);
|
|
2080
|
+
const current = runs.find((run) => run.id === openId) ?? runs[0];
|
|
2081
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u6267\u884C\u8BB0\u5F55" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u6267\u884C\u8BB0\u5F55\u4E0E\u5B50\u4EFB\u52A1"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u6765\u81EA\u4F1A\u8BDD\u4E2D\u7684 model_router_execute\u3001model_router_team_execute \u548C model_router_tool_run\u3002\u6BCF\u4E2A\u5DE5\u4F5C\u5305\u663E\u793A\u5206\u914D\u7684\u6A21\u578B\u3001\u539F\u56E0\u3001\u6E20\u9053\u3001\u8D39\u7528\uFF1B\u56DE\u9000\u65F6\u663E\u793A CLI \u539F\u59CB\u9519\u8BEF\u3002\u5931\u8D25\u7684\u6B65\u9AA4\u53EF\u5355\u72EC\u91CD\u8DD1\uFF0C\u4E0D\u4F1A\u91CD\u505A\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4\uFF1B\u53EF\u7F16\u8F91\u56E2\u961F\u8FD0\u884C\u4F1A\u5728\u65B0\u7684\u72EC\u7ACB\u5DE5\u4F5C\u533A\u5148\u5957\u7528\u4E4B\u524D\u7684\u6539\u52A8\u518D\u7EED\u8DD1\uFF0C\u5931\u8D25\u7684\u5355\u6B21\u8C03\u7528\u4F1A\u4F5C\u4E3A\u65B0\u8FD0\u884C\u91CD\u65B0\u6267\u884C\uFF0C\u4E8C\u8005\u90FD\u9700\u5148\u786E\u8BA4\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: busy, onClick: onRefresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error), !ledger && !error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u8BFB\u53D6\u6267\u884C\u8BB0\u5F55\u2026"), ledger && runs.length === 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty" }, "\u8FD8\u6CA1\u6709\u6267\u884C\u8BB0\u5F55\u3002\u53EF\u5728\u4E0A\u65B9\u201C\u5728\u5DE5\u4F5C\u53F0\u6267\u884C\u201D\u4E2D\u9884\u89C8\u5E76\u6267\u884C\uFF0C\u6216\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_execute\u3001model_router_team_execute\u3001model_router_tool_run\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u51B3\u7B56\u548C\u7ED3\u679C\u3002"), runs.length > 0 && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-label", htmlFor: "mr-run-select" }, "\u9009\u62E9\u8BB0\u5F55"), /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input", id: "mr-run-select", value: current?.id ?? "", onChange: (event) => setOpenId(event.target.value) }, runs.map((run) => /* @__PURE__ */ import_react2.default.createElement("option", { key: run.id, value: run.id }, new Date(run.createdAt).toLocaleString(), " \xB7 ", RUN_KIND_LABEL[run.kind ?? "assign"] ?? run.kind, " \xB7 ", RUN_STATUS_LABEL[run.status] ?? run.status, " \xB7 ", run.task.slice(0, 40)))), current && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-run" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u65B9\u6848"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[current.preset]?.label ?? current.preset, current.budget?.downgraded ? "\uFF08\u8D85\u9884\u7B97\u81EA\u52A8\u964D\u7EA7\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u96BE\u5EA6"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, BAND[current.decision?.complexity?.band] ?? "\u2014", current.decision?.complexity?.value !== null && current.decision?.complexity?.value !== void 0 ? ` \xB7 ${current.decision.complexity.value}` : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u9884\u4F30 / \u5B9E\u9645\uFF08\u8BA1\u5165\u9884\u7B97\uFF09"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, money(current.decision?.estimatedCost), " / ", money(runTotals(current).budgetUsd))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u7C7B\u578B"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, RUN_KIND_LABEL[current.kind ?? "assign"] ?? current.kind, current.kind === "team" || current.kind === "tool" ? ` \xB7 ${current.executionMode === "workspace-write" ? "\u53EF\u7F16\u8F91" : "\u53EA\u8BFB"}` : ""))), runTotals(current).subscription && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8BA2\u9605\u53C2\u8003\u8D39\u7528\uFF08\u6309 API \u4EF7\u6298\u7B97\uFF0C\u4E0D\u8BA1\u5165\u9884\u7B97\uFF09\uFF1A", money(runTotals(current).referenceUsd)), current.isolatedWorkspace && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u72EC\u7ACB\u5DE5\u4F5C\u533A\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, current.isolatedWorkspace)), current.rerunOf && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u91CD\u65B0\u6267\u884C\u81EA\u8FD0\u884C ", current.rerunOf.slice(0, 8), "\u3002"), current.decision?.reason && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8DEF\u7531\u539F\u56E0\uFF1A", current.decision.reason), /* @__PURE__ */ import_react2.default.createElement(DagView, { packages: current.packages, renderNode: (item) => /* @__PURE__ */ import_react2.default.createElement(RunNode, { run: current, item, routes, allowReassign: ledger?.settings?.allowManualReassign !== false, busy, onRate, onRerun }) })))));
|
|
2082
|
+
}
|
|
2083
|
+
function BillingCard({ billing, error, onRefresh, refreshing }) {
|
|
2084
|
+
const rows = billingRows(billing);
|
|
2085
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8BA2\u9605\u4E0E API Key" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u8BA2\u9605\u4E0E API Key"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u9ED8\u8BA4\u8BA2\u9605\u4F18\u5148\uFF1A\u6709\u8BA2\u9605\uFF08\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55\u6216\u7F16\u7A0B\u5957\u9910 Key \u8DEF\u7EBF\uFF09\u5C31\u5148\u7528\u8BA2\u9605\uFF1B\u8BA2\u9605\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\uFF0C\u540C\u4E00\u6B65\u9AA4\u81EA\u52A8\u6539\u7528 API Key\uFF0C\u5E76\u5728\u6267\u884C\u8BB0\u5F55\u4E2D\u5199\u660E\u539F\u56E0\u3002\u8BA2\u9605\u8FD0\u884C\u53EA\u663E\u793A\u53C2\u8003\u8D39\u7528\uFF0C\u4E0D\u8BA1\u5165\u9884\u7B97\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: refreshing, onClick: onRefresh }, "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error), billing?.quotaPatternErrors?.length > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, "\u989D\u5EA6\u8BC6\u522B\u89C4\u5219\u6709\u8BEF\uFF0C\u5DF2\u5FFD\u7565\uFF1A", billing.quotaPatternErrors.join("\uFF1B")), rows.length === 0 && !error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: billing ? void 0 : "status" }, billing ? "\u6A21\u578B\u76EE\u5F55\u4E2D\u6CA1\u6709\u8DEF\u7EBF\uFF1B\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u70B9\u201C\u5237\u65B0\u201D\u3002" : "\u6B63\u5728\u8BFB\u53D6\u8BA1\u8D39\u72B6\u6001\u2026"), rows.length > 0 && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-table-wrap" }, /* @__PURE__ */ import_react2.default.createElement("table", { className: "mr-table" }, /* @__PURE__ */ import_react2.default.createElement("thead", null, /* @__PURE__ */ import_react2.default.createElement("tr", null, /* @__PURE__ */ import_react2.default.createElement("th", null, "\u4F9B\u5E94\u5546"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8BA2\u9605"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8BA2\u9605\u72B6\u6001 / \u6062\u590D\u65F6\u95F4"), /* @__PURE__ */ import_react2.default.createElement("th", null, "API Key \u56DE\u9000"))), /* @__PURE__ */ import_react2.default.createElement("tbody", null, rows.map((row) => /* @__PURE__ */ import_react2.default.createElement("tr", { key: row.key }, /* @__PURE__ */ import_react2.default.createElement("td", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, row.provider), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, row.models)), /* @__PURE__ */ import_react2.default.createElement("td", null, row.mode), /* @__PURE__ */ import_react2.default.createElement("td", null, row.subscription), /* @__PURE__ */ import_react2.default.createElement("td", { className: row.exhausted ? "mr-warn-text" : "" }, row.state), /* @__PURE__ */ import_react2.default.createElement("td", { className: row.apiAvailable ? "" : "mr-warn-text" }, row.api)))))), billing && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5382\u5546\u6CA1\u6709\u7ED9\u51FA\u6062\u590D\u65F6\u95F4\u65F6\uFF0C\u6309\u8BBE\u7F6E\u4E2D\u7684\u51B7\u5374\u65F6\u95F4\uFF08", billing.cooldownMinutes ?? 60, " \u5206\u949F\uFF09\u6682\u505C\u4F7F\u7528\u8BE5\u8BA2\u9605\u3002\u56E2\u961F\u6267\u884C\u548C\u5355\u5DE5\u5177\u8C03\u7528\u53EA\u80FD\u901A\u8FC7\u5B98\u65B9 CLI \u4FEE\u6539\u6587\u4EF6\uFF0C\u989D\u5EA6\u7528\u5C3D\u65F6\u4F1A\u8BB0\u5F55\u5E76\u63D0\u793A\uFF0C\u4F46\u4E0D\u4F1A\u81EA\u52A8\u6539\u7528 API Key\u3002")));
|
|
2086
|
+
}
|
|
2087
|
+
function SecurityCard({ data, error, onRefresh }) {
|
|
2088
|
+
const rows = data?.boundaries ?? [];
|
|
2089
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u5B89\u5168\u8FB9\u754C" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u5B89\u5168\u8FB9\u754C"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u6BCF\u6761\u5DF2\u914D\u7F6E\u8DEF\u7EBF\u53EF\u8BFB\u3001\u53EF\u5199\u7684\u8303\u56F4\u548C\u662F\u5426\u7ECF\u8FC7 Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u53EF\u7F16\u8F91\u8FD0\u884C\u603B\u662F\u9700\u8981\u4F60\u5728\u5B98\u65B9\u5BA1\u6279\u4E2D\u786E\u8BA4\uFF1B\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u9ED8\u8BA4\u4E5F\u4F1A\u8BE2\u95EE\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: onRefresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, error), data && !data.sandboxAvailable && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error" }, "\u5F53\u524D Host \u6CA1\u6709\u63D0\u4F9B Harness \u8FDB\u7A0B\u6C99\u7BB1\uFF1A\u53EF\u7F16\u8F91\u8FD0\u884C\u4F1A\u88AB\u62D2\u7EDD\uFF0C\u53EA\u8BFB\u7684\u65E0\u754C\u9762 CLI \u5C06\u76F4\u63A5\u542F\u52A8\u3002"), rows.length === 0 && !error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: data ? void 0 : "status" }, data ? "\u6A21\u578B\u76EE\u5F55\u4E2D\u6CA1\u6709\u8DEF\u7EBF\uFF1B\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u70B9\u201C\u5237\u65B0\u201D\u3002" : "\u6B63\u5728\u8BFB\u53D6\u5B89\u5168\u8FB9\u754C\u2026"), rows.length > 0 && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-table-wrap" }, /* @__PURE__ */ import_react2.default.createElement("table", { className: "mr-table" }, /* @__PURE__ */ import_react2.default.createElement("thead", null, /* @__PURE__ */ import_react2.default.createElement("tr", null, /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8DEF\u7EBF"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u53EA\u8BFB\u8FD0\u884C\u53EF\u8BFB"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u53EA\u8BFB\u8FD0\u884C\u6C99\u7BB1"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u53EF\u7F16\u8F91\u8FD0\u884C"))), /* @__PURE__ */ import_react2.default.createElement("tbody", null, rows.map((row) => /* @__PURE__ */ import_react2.default.createElement("tr", { key: `${row.provider}/${row.model}` }, /* @__PURE__ */ import_react2.default.createElement("td", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, row.provider, "/", row.model), row.toolLabel ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, row.toolLabel) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, "\u4EC5 API")), /* @__PURE__ */ import_react2.default.createElement("td", null, row.readOnly.readable), /* @__PURE__ */ import_react2.default.createElement("td", { className: row.readOnly.direct ? "mr-warn-text" : "" }, row.readOnly.sandbox), /* @__PURE__ */ import_react2.default.createElement("td", null, row.write ? `${row.write.writable}\uFF1B\u9700\u5BA1\u6279` : "\u4E0D\u652F\u6301"))))))));
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
// .dsh-plugin/client/run-launcher.jsx
|
|
2093
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
2094
|
+
var text2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
2095
|
+
var STATUS_TEXT = Object.freeze({
|
|
2096
|
+
succeeded: "\u6267\u884C\u5B8C\u6210",
|
|
2097
|
+
completed: "\u6267\u884C\u5B8C\u6210",
|
|
2098
|
+
partial: "\u90E8\u5206\u6B65\u9AA4\u5931\u8D25",
|
|
2099
|
+
failed: "\u6267\u884C\u5931\u8D25",
|
|
2100
|
+
cancelled: "\u5DF2\u53D6\u6D88",
|
|
2101
|
+
"paused-budget": "\u5DF2\u56E0\u9884\u7B97\u6682\u505C\uFF0C\u672A\u542F\u52A8\u6A21\u578B",
|
|
2102
|
+
"paused-subscription-failure": "\u6709\u6B65\u9AA4\u56E0\u8BA2\u9605\u8C03\u7528\u5931\u8D25\u800C\u6682\u505C\uFF0C\u8BF7\u5728\u4E0B\u65B9\u6267\u884C\u8BB0\u5F55\u4E2D\u9009\u62E9\u5982\u4F55\u7EE7\u7EED"
|
|
2103
|
+
});
|
|
2104
|
+
function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger, previewRun, startRun, onStarted, disabledReason }) {
|
|
2105
|
+
const [preset, setPreset] = import_react3.default.useState(defaultPreset || "balanced");
|
|
2106
|
+
const lastWorkspace = [...ledger?.runs ?? []].map((run) => run.workspace).find(Boolean) ?? "";
|
|
2107
|
+
const [workspace, setWorkspace] = import_react3.default.useState("");
|
|
2108
|
+
const [phase, setPhase] = import_react3.default.useState({ kind: "idle" });
|
|
2109
|
+
const mounted = import_react3.default.useRef(true);
|
|
2110
|
+
const confirmRef = import_react3.default.useRef(null);
|
|
2111
|
+
import_react3.default.useEffect(() => () => {
|
|
2112
|
+
mounted.current = false;
|
|
2113
|
+
}, []);
|
|
2114
|
+
import_react3.default.useEffect(() => {
|
|
2115
|
+
setPreset(defaultPreset || "balanced");
|
|
2116
|
+
}, [defaultPreset]);
|
|
2117
|
+
import_react3.default.useEffect(() => {
|
|
2118
|
+
setPhase((previous) => previous.kind === "preview" || previous.kind === "error" ? { kind: "idle" } : previous);
|
|
2119
|
+
}, [task, mode, directRoute?.provider, directRoute?.model, budgetUsd, preset, workspace]);
|
|
2120
|
+
import_react3.default.useEffect(() => {
|
|
2121
|
+
if (phase.kind === "preview") confirmRef.current?.focus();
|
|
2122
|
+
}, [phase.kind]);
|
|
2123
|
+
const request = () => launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace: text2(workspace) || lastWorkspace });
|
|
2124
|
+
const unavailable = typeof previewRun !== "function" || typeof startRun !== "function";
|
|
2125
|
+
const blocked = disabledReason || (unavailable ? "\u5DE5\u4F5C\u53F0\u6267\u884C\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002" : !text2(task) ? "\u5148\u5728\u4E0A\u65B9\u201C\u4EFB\u52A1\u89C4\u5212\u201D\u4E2D\u63CF\u8FF0\u4EFB\u52A1\u3002" : "");
|
|
2126
|
+
const busy = phase.kind === "previewing" || phase.kind === "running";
|
|
2127
|
+
const preview = async () => {
|
|
2128
|
+
setPhase({ kind: "previewing" });
|
|
2129
|
+
try {
|
|
2130
|
+
const value = unwrapRemote(await previewRun(request()), "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002");
|
|
2131
|
+
if (mounted.current) setPhase({ kind: "preview", value });
|
|
2132
|
+
} catch (error) {
|
|
2133
|
+
if (mounted.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
const confirm = async () => {
|
|
2137
|
+
const shown = phase.value;
|
|
2138
|
+
setPhase({ kind: "running", value: shown });
|
|
2139
|
+
try {
|
|
2140
|
+
const value = unwrapRemote(await startRun({ ...request(), confirmedReasons: shown.reasons.map((item) => item.code) }), "\u6267\u884C\u5931\u8D25\u3002");
|
|
2141
|
+
if (!mounted.current) return;
|
|
2142
|
+
if (value.status === "needs-confirmation") {
|
|
2143
|
+
setPhase({ kind: "preview", value, changed: true });
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
setPhase({ kind: "done", value });
|
|
2147
|
+
onStarted?.(value.runId);
|
|
2148
|
+
} catch (error) {
|
|
2149
|
+
if (mounted.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u5931\u8D25\u3002" });
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
return /* @__PURE__ */ import_react3.default.createElement("section", { className: "mr-card", "aria-label": "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement("h2", { className: "mr-card-title" }, "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-card-copy" }, "\u4F7F\u7528\u4E0A\u65B9\u7684\u4EFB\u52A1\u63CF\u8FF0\u4E0E\u89C4\u5212\u6A21\u5F0F\uFF0C\u5148\u9884\u89C8\u6267\u884C\u8BA1\u5212\u4E0E\u9884\u4F30\u8D39\u7528\uFF0C\u786E\u8BA4\u540E\u624D\u4F1A\u8C03\u7528\u6A21\u578B\u3002\u6267\u884C\u4E3A\u53EA\u8BFB\uFF1A\u5B98\u65B9 CLI \u4EE5\u65E0\u754C\u9762\u53EA\u8BFB\u65B9\u5F0F\u8FD0\u884C\uFF0C\u4E0D\u4FEE\u6539\u6587\u4EF6\uFF1B\u53EF\u7F16\u8F91\u4EFB\u52A1\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u4F7F\u7528 model_router_tool_run \u6216 model_router_team_execute\u3002"))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-preset" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-preset" }, Object.values(ROUTING_PRESETS).map((item) => /* @__PURE__ */ import_react3.default.createElement("button", { key: item.id, type: "button", title: item.description, "aria-pressed": preset === item.id, disabled: busy || mode === "direct", onClick: () => setPreset(item.id) }, item.label)))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group mr-launch-workspace" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-launch-workspace" }, "\u5DE5\u4F5C\u533A\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"), /* @__PURE__ */ import_react3.default.createElement("input", { className: "mr-input", id: "mr-launch-workspace", value: workspace, disabled: busy, placeholder: lastWorkspace ? `\u7559\u7A7A\u6CBF\u7528\u4E0A\u6B21\u8FD0\u884C\uFF1A${lastWorkspace}` : "\u4F8B\u5982 D:\\projects\\demo \u6216 /home/me/demo", onChange: (event) => setWorkspace(event.target.value) }))), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, mode === "direct" ? `\u6307\u5B9A\u6A21\u578B\uFF1A${directRoute ? `${directRoute.provider}/${directRoute.model}` : "\u672A\u9009\u62E9"}\uFF08\u8DF3\u8FC7\u8DEF\u7531\uFF0C\u65B9\u6848\u4E0D\u751F\u6548\uFF09` : mode === "team" ? "\u56E2\u961F\u5206\u5DE5\uFF1A\u6309\u65B9\u6848\u62C6\u5206\u5DE5\u4F5C\u5305\uFF0C\u9010\u4E2A\u5206\u914D\u6A21\u578B\u3002" : "\u5355\u4EFB\u52A1\uFF1A\u6309\u65B9\u6848\u9009\u62E9\u4E00\u6761\u8DEF\u7EBF\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", disabled: Boolean(blocked) || busy, onClick: () => {
|
|
2153
|
+
void preview();
|
|
2154
|
+
} }, phase.kind === "previewing" ? "\u6B63\u5728\u751F\u6210\u9884\u89C8\u2026" : "\u9884\u89C8\u6267\u884C\u8BA1\u5212"), blocked && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, blocked)), phase.kind === "previewing" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u6309\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u3001\u767B\u5F55\u72B6\u6001\u548C\u9884\u7B97\u751F\u6210\u6267\u884C\u8BA1\u5212\u2026"), phase.kind === "error" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, phase.message), (phase.kind === "preview" || phase.kind === "running") && /* @__PURE__ */ import_react3.default.createElement(
|
|
2155
|
+
LaunchPreview,
|
|
2156
|
+
{
|
|
2157
|
+
value: phase.value,
|
|
2158
|
+
changed: phase.changed,
|
|
2159
|
+
running: phase.kind === "running",
|
|
2160
|
+
confirmRef,
|
|
2161
|
+
onConfirm: () => {
|
|
2162
|
+
void confirm();
|
|
2163
|
+
},
|
|
2164
|
+
onCancel: () => setPhase({ kind: "idle" })
|
|
2165
|
+
}
|
|
2166
|
+
), phase.kind === "done" && /* @__PURE__ */ import_react3.default.createElement("div", { className: phase.value.status === "failed" || phase.value.status?.startsWith("paused") ? "mr-error" : "mr-empty", role: "status" }, STATUS_TEXT[phase.value.status] ?? `\u72B6\u6001\uFF1A${phase.value.status}`, phase.value.runId ? `\uFF1B\u7ED3\u679C\u5DF2\u8BB0\u5F55\uFF08\u8FD0\u884C ${phase.value.runId.slice(0, 8)}\uFF09\uFF0C\u89C1\u4E0B\u65B9\u201C\u6267\u884C\u8BB0\u5F55\u4E0E\u5B50\u4EFB\u52A1\u201D\u3002` : "\u3002", phase.value.budget?.message ? ` ${phase.value.budget.exceeded && phase.value.status !== "paused-budget" ? "\u5DF2\u6309\u4F60\u7684\u786E\u8BA4\u8D85\u9884\u7B97\u6267\u884C\uFF1A" : ""}${phase.value.budget.message}` : "")));
|
|
2167
|
+
}
|
|
2168
|
+
function LaunchPreview({ value, changed, running, confirmRef, onConfirm, onCancel }) {
|
|
2169
|
+
const packages = value.decision?.packages ?? [];
|
|
2170
|
+
const reasons = value.reasons ?? [];
|
|
2171
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-launch-preview", role: "region", "aria-label": "\u6267\u884C\u9884\u89C8" }, changed && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, "\u786E\u8BA4\u671F\u95F4\u60C5\u51B5\u6709\u53D8\u5316\uFF08\u4F8B\u5982\u9884\u7B97\u6216\u767B\u5F55\u72B6\u6001\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9\u4E0B\u9762\u7684\u786E\u8BA4\u9879\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u8DEF\u7EBF"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, value.selected ? `${value.selected.provider}/${value.selected.model}` : "\u6682\u65E0\u8DEF\u7EBF", value.routingBypassed ? "\uFF08\u6307\u5B9A\u6A21\u578B\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u9884\u4F30\u8D39\u7528"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, value.estimatedCost === null || value.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(value.estimatedCost))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[value.decision?.preset]?.label ?? "\u2014", value.budget?.downgraded ? "\uFF08\u8D85\u9884\u7B97\u81EA\u52A8\u964D\u7EA7\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u5DE5\u4F5C\u533A"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value mr-break" }, /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace)))), value.budget?.message && /* @__PURE__ */ import_react3.default.createElement("p", { className: value.budget.exceeded ? "mr-error" : "mr-caption" }, "\u9884\u7B97\u68C0\u67E5\uFF1A", value.budget.message), packages.length > 0 && /* @__PURE__ */ import_react3.default.createElement("ol", { className: "mr-launch-packages" }, packages.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.name), " \u2192 ", item.route, " ", /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\uFF08\u9884\u4F30 ", money(item.estimatedCost), item.channel === "official-cli" ? " \xB7 \u5B98\u65B9 CLI" : item.channel ? " \xB7 \u6A21\u578B\u76EE\u5F55 API" : "", "\uFF09")))), reasons.length > 0 ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-confirm-box" }, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-section-title" }, "\u6267\u884C\u524D\u9700\u8981\u786E\u8BA4\u4EE5\u4E0B ", reasons.length, " \u9879\uFF1A"), /* @__PURE__ */ import_react3.default.createElement("ol", null, reasons.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.code }, item.zh)))) : /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u65E0\u9700\u989D\u5916\u786E\u8BA4\uFF1A\u672A\u8D85\u9884\u7B97\uFF0C\u4E5F\u4E0D\u4F1A\u4E0D\u7ECF\u6C99\u7BB1\u542F\u52A8 CLI\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { ref: confirmRef, className: "mr-button", type: "button", disabled: running, onClick: onConfirm }, running ? "\u6B63\u5728\u6267\u884C\u2026" : reasons.length ? "\u5168\u90E8\u786E\u8BA4\u5E76\u6267\u884C" : "\u786E\u8BA4\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: running, onClick: onCancel }, "\u53D6\u6D88"), running && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption", role: "status", "aria-live": "polite" }, "\u6A21\u578B\u8C03\u7528\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF0C\u8BF7\u52FF\u5173\u95ED\u5DE5\u4F5C\u53F0\u3002")));
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// .dsh-plugin/client/router-main.css
|
|
2175
|
+
var router_main_default = ".mr-workspace {\n --mr-card: rgba(255, 255, 255, .94);\n --mr-ink: #182232;\n --mr-muted: #526174;\n --mr-line: rgba(38, 55, 75, .13);\n --mr-accent: #315cc8;\n --mr-soft: #edf3ff;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n background: rgba(246, 249, 253, .9);\n backdrop-filter: blur(18px);\n color: var(--mr-ink);\n font-family: inherit;\n}\n.mr-workspace *, .mr-workspace *::before, .mr-workspace *::after { box-sizing: border-box; }\n.mr-shell { width: min(1160px, 100%); margin: 0 auto; padding: 36px 32px 64px; }\n.mr-header { display: flex; flex-wrap: wrap; gap: 20px; align-items: end; justify-content: space-between; margin-bottom: 24px; }\n.mr-eyebrow { margin: 0 0 8px; color: var(--mr-accent); font-size: 11px; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; }\n.mr-title { margin: 0; font-size: clamp(25px, 3vw, 34px); letter-spacing: -.035em; line-height: 1.16; }\n.mr-subtitle { margin: 10px 0 0; color: var(--mr-muted); font-size: 14px; line-height: 1.65; }\n.mr-status { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--mr-line); border-radius: 999px; background: var(--mr-card); color: var(--mr-muted); font-size: 12px; white-space: nowrap; }\n.mr-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #2cba83; }\n.mr-status-dot.loading { background: #e9a640; }\n.mr-status-dot.error { background: #d95360; }\n.mr-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, .85fr); gap: 18px; align-items: start; }\n.mr-card { min-width: 0; border: 1px solid var(--mr-line); border-radius: 18px; background: var(--mr-card); box-shadow: 0 12px 42px rgba(25, 45, 76, .08); }\n.mr-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 0; }\n.mr-card-title { margin: 0; font-size: 16px; font-weight: 700; }\n.mr-card-copy { margin: 5px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.55; }\n.mr-card-body { padding: 18px 22px 22px; }\n.mr-label { display: block; margin: 0 0 8px; font-size: 12px; font-weight: 700; }\n.mr-textarea, .mr-input { width: 100%; border: 1px solid var(--mr-line); border-radius: 11px; background: #fff; color: var(--mr-ink); font: inherit; outline: none; }\n.mr-textarea { min-height: 154px; padding: 13px 14px; resize: vertical; line-height: 1.6; font-size: 14px; }\n.mr-input { min-height: 38px; padding: 8px 11px; font-size: 13px; }\n.mr-textarea:focus, .mr-input:focus { border-color: var(--mr-accent); box-shadow: 0 0 0 3px rgba(49, 92, 200, .13); }\n.mr-controls { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 14px; margin-top: 17px; }\n.mr-control-group { display: flex; flex-direction: column; gap: 7px; }\n.mr-control-label { color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-segment { display: inline-flex; padding: 3px; border-radius: 10px; background: #edf1f7; }\n.mr-segment button { border: 0; border-radius: 8px; padding: 8px 12px; background: transparent; color: var(--mr-muted); font: inherit; font-size: 12px; cursor: pointer; }\n.mr-segment button[aria-pressed='true'] { background: #fff; color: var(--mr-ink); box-shadow: 0 2px 8px rgba(20, 35, 56, .1); font-weight: 700; }\n.mr-budget { width: 140px; }\n.mr-direct { min-width: min(100%, 280px); }\n.mr-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 18px; }\n.mr-button { border: 1px solid transparent; border-radius: 10px; min-height: 38px; padding: 8px 14px; background: var(--mr-accent); color: #fff; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }\n.mr-button:hover:not(:disabled) { filter: brightness(.94); }\n.mr-button:disabled { cursor: not-allowed; opacity: .5; }\n.mr-button-secondary { border-color: var(--mr-line); background: #fff; color: var(--mr-ink); }\n.mr-caption { color: var(--mr-muted); font-size: 11px; line-height: 1.5; }\n.mr-search { margin-top: 14px; }\n.mr-list { display: grid; gap: 8px; margin-top: 13px; max-height: 395px; overflow-y: auto; }\n.mr-route { display: flex; justify-content: space-between; gap: 10px; padding: 10px 11px; border: 1px solid var(--mr-line); border-radius: 10px; background: #f9fbfe; }\n.mr-route-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; }\n.mr-route-provider { margin-top: 2px; color: var(--mr-muted); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.mr-pill { align-self: start; flex: none; padding: 3px 7px; border-radius: 7px; background: var(--mr-soft); color: var(--mr-accent); font-size: 10px; font-weight: 700; }\n.mr-empty, .mr-error { padding: 14px; border-radius: 10px; background: #f4f6fa; color: var(--mr-muted); font-size: 12px; line-height: 1.6; }\n.mr-error { background: #fff0f1; color: #a52d3c; }\n.mr-results { margin-top: 18px; }\n.mr-result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }\n.mr-metric { padding: 13px; border-radius: 12px; background: #f5f8fd; }\n.mr-metric-label { color: var(--mr-muted); font-size: 10px; }\n.mr-metric-value { margin-top: 5px; font-size: 15px; font-weight: 750; word-break: break-word; }\n.mr-section-title { margin: 20px 0 10px; font-size: 13px; font-weight: 750; }\n.mr-package { padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: #fafcff; }\n.mr-package + .mr-package { margin-top: 8px; }\n.mr-package-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; }\n.mr-package-name { font-size: 12px; font-weight: 750; }\n.mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }\n.mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 11px; line-height: 1.6; }\n.mr-notice { margin-top: 18px; padding: 14px 16px; border: 1px solid rgba(49, 92, 200, .16); border-radius: 12px; background: #eff4ff; color: #354d7a; font-size: 11px; line-height: 1.65; }\n.mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }\n@media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }\n@media (prefers-color-scheme: dark) {\n .mr-workspace { --mr-card: rgba(30, 38, 51, .94); --mr-ink: #ecf2ff; --mr-muted: #aebbd0; --mr-line: rgba(210, 224, 245, .15); --mr-accent: #91adff; --mr-soft: rgba(97, 132, 222, .18); background: rgba(18, 23, 32, .91); }\n .mr-textarea, .mr-input, .mr-segment button[aria-pressed='true'], .mr-button-secondary { background: #273245; color: var(--mr-ink); }\n .mr-segment, .mr-empty, .mr-metric { background: #222d3e; }\n .mr-route, .mr-package { background: #222d3e; }\n .mr-notice { background: #243454; color: #c9d6fa; }\n .mr-error { background: #4a2730; color: #ffd4da; }\n}\n\n/* \u5B98\u65B9\u5DE5\u5177\u5361\u7247 */\n.mr-tools { display: grid; gap: 9px; }\n.mr-tool { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }\n.mr-tool-info { min-width: 0; flex: 1; }\n.mr-tool-status { display: flex; align-items: center; gap: 6px; margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #8d9aaa; }\n.mr-tool-dot.installed { background: #2cba83; }\n.mr-tool-dot.running { background: #e9a640; }\n.mr-tool-command { display: block; margin-top: 6px; padding: 4px 8px; font-size: 12px; background: var(--dsw-alias-markdown-code-block); border-radius: 6px; overflow-wrap: anywhere; }\n.mr-tool-button { flex-shrink: 0; }\n.mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }\n.mr-tool-detail { margin: 7px 0 0; }\n.mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }\n.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-log summary { cursor: pointer; }\n.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; }\n.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }\n.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }\n.mr-pill-warn { border-color: var(--dsw-alias-warning, #c27c0e); color: var(--dsw-alias-warning, #c27c0e); }\n.mr-profile-card { margin-top: 18px; }\n.mr-profile-state { display: block; margin: 8px 0 0; }\n.mr-profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; margin-top: 15px; }\n.mr-profile-field { display: grid; align-content: start; gap: 6px; color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-profile-field .mr-input { color: var(--mr-ink); }\n.mr-profile-advanced { margin-top: 14px; color: var(--mr-muted); font-size: 11px; }\n.mr-profile-advanced summary { cursor: pointer; }\n.mr-profile-success { padding: 10px 12px; border-radius: 10px; background: #e5f5eb; color: #175f3a; font-size: 12px; }\n@media (prefers-color-scheme: dark) { .mr-profile-success { background: #193b2b; color: #b4f1cc; } }\n@media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }\n\n/* \u5F00\u7BB1\u4F53\u68C0\u3001\u6210\u672C\u63A7\u5236\u3001\u6267\u884C\u8BB0\u5F55\u3001\u5B89\u5168\u8FB9\u754C */\n.mr-onboarding, .mr-cost { margin-bottom: 18px; }\n.mr-onboarding { border-color: rgba(49, 92, 200, .35); }\n.mr-checklist { margin: 4px 0 0; padding-left: 18px; color: var(--mr-ink); font-size: 12px; line-height: 1.8; }\n.mr-login { margin-top: 4px; }\n.mr-login-button { margin-top: 8px; min-height: 30px; padding: 4px 10px; font-size: 12px; }\n.mr-login-guide { margin-top: 8px; padding: 10px 12px; border: 1px dashed var(--mr-line); border-radius: 10px; }\n.mr-login-command { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.mr-login-command .mr-tool-command { margin: 0; }\n.mr-tool-dot.missing-strong { background: #d95360; }\n.mr-meters { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }\n.mr-meter { padding: 12px 13px; border-radius: 12px; background: #f5f8fd; }\n.mr-meter-top { display: flex; justify-content: space-between; gap: 8px; font-size: 15px; font-weight: 750; }\n.mr-meter-over { color: #a52d3c; }\n.mr-meter-track { height: 7px; margin-top: 9px; border-radius: 999px; background: rgba(38, 55, 75, .1); overflow: hidden; }\n.mr-meter-fill { height: 100%; border-radius: inherit; background: #2cba83; }\n.mr-meter-fill.near { background: #e9a640; }\n.mr-meter-fill.over { background: #d95360; }\n.mr-inline { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.mr-check { display: inline-flex; align-items: center; gap: 7px; color: var(--mr-ink); font-size: 12px; }\n.mr-dag { display: flex; gap: 12px; margin: 12px 0; overflow-x: auto; padding-bottom: 6px; }\n.mr-dag-column { display: grid; align-content: start; gap: 8px; min-width: 240px; flex: 1 0 240px; }\n.mr-dag-step { color: var(--mr-muted); font-size: 10px; font-weight: 750; letter-spacing: .08em; }\n.mr-dag-node { padding: 11px 12px; border: 1px solid var(--mr-line); border-left-width: 4px; border-radius: 11px; background: #fafcff; }\n.mr-dag-node.ok { border-left-color: #2cba83; }\n.mr-dag-node.warn { border-left-color: #e9a640; }\n.mr-dag-node.error { border-left-color: #d95360; }\n.mr-dag-node.blocked { border-left-color: #8d9aaa; }\n.mr-dag-node.pending { border-left-color: var(--mr-accent); }\n.mr-dag-node-top { display: flex; justify-content: space-between; gap: 8px; align-items: center; }\n.mr-status-error { background: #fff0f1; color: #a52d3c; }\n.mr-status-warn { background: #fff6e6; color: #8a5a08; }\n.mr-status-ok { background: #e5f5eb; color: #175f3a; }\n.mr-fallback-error code { font-size: 10px; overflow-wrap: anywhere; }\n.mr-node-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }\n.mr-mini { min-height: 28px; padding: 3px 9px; font-size: 11px; }\n.mr-mini-select { min-height: 28px; width: auto; max-width: 180px; padding: 3px 7px; font-size: 11px; }\n.mr-run { margin-top: 14px; }\n.mr-table-wrap { overflow-x: auto; }\n.mr-table { width: 100%; border-collapse: collapse; font-size: 11px; line-height: 1.55; }\n.mr-table th, .mr-table td { padding: 8px 9px; border-bottom: 1px solid var(--mr-line); text-align: left; vertical-align: top; }\n.mr-table th { color: var(--mr-muted); font-weight: 700; }\n.mr-warn-text { color: #8a5a08; }\n@media (prefers-color-scheme: dark) {\n .mr-meter, .mr-dag-node { background: #222d3e; }\n .mr-status-error { background: #4a2730; color: #ffd4da; }\n .mr-status-warn { background: #4a3a1c; color: #ffe2a8; }\n .mr-status-ok { background: #193b2b; color: #b4f1cc; }\n .mr-warn-text { color: #ffd38a; }\n}\n/* Keyboard focus: every interactive control shows a visible ring. */\n.mr-workspace button:focus-visible, .mr-workspace select:focus-visible, .mr-workspace input:focus-visible, .mr-workspace textarea:focus-visible, .mr-workspace summary:focus-visible, .mr-workspace a:focus-visible { outline: 2px solid var(--mr-accent); outline-offset: 2px; }\n.mr-segment button:disabled { cursor: not-allowed; opacity: .55; }\n.mr-launch-workspace { flex: 1 1 320px; min-width: min(100%, 260px); }\n.mr-launch-preview { margin-top: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 12px; background: #f9fbfe; }\n.mr-launch-packages { margin: 12px 0 0; padding-left: 20px; font-size: 12px; line-height: 1.7; }\n.mr-confirm-box { margin-top: 12px; padding: 10px 14px; border: 1px solid rgba(217, 140, 30, .35); border-radius: 10px; background: #fff8ec; color: #6d4a12; font-size: 12px; line-height: 1.6; }\n.mr-confirm-box .mr-section-title { margin: 0 0 6px; }\n.mr-confirm-box ol { margin: 0; padding-left: 20px; }\n.mr-break { overflow-wrap: anywhere; }\n@media (prefers-color-scheme: dark) {\n .mr-launch-preview { background: rgba(255, 255, 255, .04); }\n .mr-confirm-box { background: rgba(217, 140, 30, .12); color: #f2d39b; }\n}\n";
|
|
2176
|
+
|
|
2177
|
+
// .dsh-plugin/client/router-main.jsx
|
|
2178
|
+
var money2 = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
|
|
2179
|
+
var text3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
2180
|
+
function RouterPanelIcon({ size = 20, active = false }) {
|
|
2181
|
+
return /* @__PURE__ */ import_react4.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react4.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react4.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react4.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react4.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
|
|
2182
|
+
}
|
|
2183
|
+
function RouteList({ routes, query }) {
|
|
2184
|
+
const filtered = routes.filter((route) => {
|
|
2185
|
+
const haystack = `${route.providerName} ${route.provider} ${route.name} ${route.model}`.toLowerCase();
|
|
2186
|
+
return haystack.includes(query.toLowerCase());
|
|
2187
|
+
});
|
|
2188
|
+
if (filtered.length === 0) return /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-empty" }, routes.length === 0 ? "\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u5C1A\u65E0\u53EF\u89C4\u5212\u7684\u8DEF\u7EBF\u3002\u8BF7\u5148\u5728\u201C\u6A21\u578B\u201D\u9875\u5B8C\u6210\u914D\u7F6E\u3002" : "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B\u3002");
|
|
2189
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-list", role: "list", "aria-label": "\u5B98\u65B9\u5DF2\u767B\u8BB0\u6A21\u578B\u8DEF\u7EBF" }, filtered.map((route) => /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-route", role: "listitem", key: `${route.provider}/${route.model}` }, /* @__PURE__ */ import_react4.default.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-route-name", title: route.name }, route.name), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-route-provider", title: `${route.provider}/${route.model}` }, route.provider, "/", route.model)), route.reasoningKnown && /* @__PURE__ */ import_react4.default.createElement("span", { className: "mr-pill" }, "\u63A8\u7406\u7B49\u7EA7"))));
|
|
2190
|
+
}
|
|
2191
|
+
function ChannelBadge({ item }) {
|
|
2192
|
+
if (!item?.executionChannel) return null;
|
|
2193
|
+
const official = item.executionChannel === "official-cli";
|
|
2194
|
+
return /* @__PURE__ */ import_react4.default.createElement("span", { className: official ? "mr-pill mr-pill-channel-ok" : "mr-pill", title: item.channelDetail ?? "" }, official ? `\u5B98\u65B9 CLI \xB7 ${item.channelLabel ?? item.channelTool}` : "\u6A21\u578B\u76EE\u5F55 API");
|
|
2195
|
+
}
|
|
2196
|
+
function PlanResults({ plan, ledger }) {
|
|
2197
|
+
const selected = plan.selected;
|
|
2198
|
+
const budget = planBudget(ledger, plan.estimatedCost);
|
|
2199
|
+
return /* @__PURE__ */ import_react4.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("h2", { className: "mr-card-title" }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-label" }, "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6 \xB7 \u96BE\u5EA6\u5206"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band, " \xB7 ", plan.complexity.value)), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-label" }, "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-value" }, money2(plan.estimatedCost))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-label" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[plan.preset]?.label ?? "\u5747\u8861"))), budget?.exceeded && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-error", role: "alert" }, "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A", budget.message, " \u5B9E\u9645\u6267\u884C\u65F6\u5C06\u6309\u8BBE\u7F6E\u81EA\u52A8\u964D\u7EA7\u6216\u6682\u505C\u8BE2\u95EE\u3002"), budget?.limited && !budget.exceeded && budget.estimateKnown && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A\u672C\u6B21\u9884\u4F30 ", money2(plan.estimatedCost), "\uFF0C\u5269\u4F59\u989D\u5EA6 ", money2(budget.remainingUsd), "\u3002"), plan.loginRequired && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, "\u63A8\u8350\u6A21\u578B\u7684\u5B98\u65B9 CLI \u672A\u767B\u5F55\uFF0C\u6267\u884C\u65F6\u76F4\u63A5\u8D70\u6A21\u578B\u76EE\u5F55 API\uFF1B\u53EF\u5728\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u5361\u7247\u70B9\u201C\u53BB\u767B\u5F55\u201D\u3002"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "mr-control-label" }, "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react4.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, plan.reason), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-error" }, "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement("h3", { className: "mr-section-title" }, "\u56E2\u961F\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, "\u4E0B\u65B9\u6A21\u578B\u662F\u89C4\u5212\u5EFA\u8BAE\uFF1B\u6258\u7BA1\u6267\u884C\u4F1A\u6309\u5382\u5546 CLI \u7684\u6A21\u578B\u540D\u89C4\u5219\u9009\u7528\uFF0C\u672A\u6838\u9A8C\u6620\u5C04\u65F6\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B\u3002"), plan.team.workPackages.length > 1 && /* @__PURE__ */ import_react4.default.createElement(DagView, { packages: plan.team.workPackages, label: "\u5DE5\u4F5C\u5305\u4F9D\u8D56\u56FE", renderNode: (item) => /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel) }), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react4.default.createElement("article", { className: "mr-package", key: item.id }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-package-top" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel)), item.objective && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-copy" }, item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.join("\u3001")}` : ""), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-copy" }, "\u96BE\u5EA6\uFF1A", { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" }[item.difficulty] || item.difficulty || "\u5F85\u8BC4\u4F30", " \xB7 \u8D39\u7528\uFF1A", money2(item.estimatedCost)), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react4.default.createElement(ChannelBadge, { item }), item.loginRequired && /* @__PURE__ */ import_react4.default.createElement("span", { className: "mr-pill" }, "CLI \u672A\u767B\u5F55")), item.channelDetail && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-package-copy" }, "\u6E20\u9053\u8BF4\u660E\uFF1A", item.channelDetail)))), plan.routingBypassed && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, "\u5DF2\u6307\u5B9A\u5355\u4E00\u6A21\u578B\uFF0C\u672A\u4E0E\u5176\u4ED6\u8DEF\u7EBF\u6BD4\u8F83\u3002\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_execute"), " \u5E76\u4F20\u5165\u8BE5 provider \u4E0E model \u5373\u53EF\u76F4\u63A5\u6267\u884C\uFF1B\u82E5\u8BE5\u6A21\u578B\u5141\u8BB8\u5B98\u65B9\u5DE5\u5177\uFF0C\u4F1A\u4F18\u5148\u4F7F\u7528\u5BF9\u5E94 CLI\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, plan.team.handoff), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-notice" }, plan.pricingNotice, " ", plan.qualityNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")));
|
|
2200
|
+
}
|
|
2201
|
+
var remoteError = (response, fallback) => text3(response?.error?.message) || text3(response?.value?.error) || fallback;
|
|
2202
|
+
function probeLabel(probe) {
|
|
2203
|
+
if (!probe) return "\u5C1A\u672A\u68C0\u6D4B";
|
|
2204
|
+
if (probe.installed) return `\u5DF2\u5B89\u88C5${probe.version ? ` \xB7 ${probe.version}` : ""}`;
|
|
2205
|
+
if (probe.status === "not-installed") return "\u672A\u5B89\u88C5";
|
|
2206
|
+
if (probe.status === "probe-timeout") return "\u68C0\u6D4B\u8D85\u65F6";
|
|
2207
|
+
if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
|
|
2208
|
+
return probe.detail || "\u672A\u5B89\u88C5";
|
|
2209
|
+
}
|
|
2210
|
+
function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes, health, onRefreshHealth }) {
|
|
2211
|
+
const healthById = Object.fromEntries((health?.tools ?? []).map((item) => [item.id, item]));
|
|
2212
|
+
const [probeState, setProbeState] = import_react4.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
|
|
2213
|
+
const [jobs, setJobs] = import_react4.default.useState({});
|
|
2214
|
+
const [rowErrors, setRowErrors] = import_react4.default.useState({});
|
|
2215
|
+
const mounted = import_react4.default.useRef(false);
|
|
2216
|
+
const request = import_react4.default.useRef(0);
|
|
2217
|
+
const submitting = import_react4.default.useRef(/* @__PURE__ */ new Set());
|
|
2218
|
+
const polling = import_react4.default.useRef(/* @__PURE__ */ new Set());
|
|
2219
|
+
const refresh = async () => {
|
|
2220
|
+
onRefreshHealth?.();
|
|
2221
|
+
const current = ++request.current;
|
|
2222
|
+
setProbeState((previous) => ({ ...previous, status: "loading", error: "" }));
|
|
2223
|
+
try {
|
|
2224
|
+
if (typeof listOfficialTools !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
2225
|
+
const response = await listOfficialTools();
|
|
2226
|
+
if (!mounted.current || current !== request.current) return;
|
|
2227
|
+
if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002"));
|
|
2228
|
+
const probes = Array.isArray(response.value?.tools) ? response.value.tools : [];
|
|
2229
|
+
const capabilities = Array.isArray(response.value?.executionCapabilities) ? response.value.executionCapabilities : [];
|
|
2230
|
+
const readiness = Array.isArray(response.value?.executionReadiness) ? response.value.executionReadiness : [];
|
|
2231
|
+
setProbeState({ status: "ready", probes, capabilities, readiness, error: "" });
|
|
2232
|
+
onProbes({ probes, capabilities, readiness });
|
|
2233
|
+
} catch (error) {
|
|
2234
|
+
if (!mounted.current || current !== request.current) return;
|
|
2235
|
+
setProbeState({ status: "error", probes: [], capabilities: [], readiness: [], error: text3(error?.message) || "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002" });
|
|
2236
|
+
onProbes({ probes: [], capabilities: [], readiness: [] });
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
import_react4.default.useEffect(() => {
|
|
2240
|
+
mounted.current = true;
|
|
2241
|
+
void refresh();
|
|
2242
|
+
if (typeof officialToolInstallStatus === "function") {
|
|
2243
|
+
void Promise.all(OFFICIAL_TOOLS.map(async (tool) => {
|
|
2244
|
+
try {
|
|
2245
|
+
const response = await officialToolInstallStatus(tool.id);
|
|
2246
|
+
return response?.ok && response.value?.job ? [tool.id, response.value.job] : null;
|
|
2247
|
+
} catch {
|
|
2248
|
+
return null;
|
|
2249
|
+
}
|
|
2250
|
+
})).then((entries) => {
|
|
2251
|
+
if (mounted.current) setJobs((previous) => ({ ...Object.fromEntries(entries.filter(Boolean)), ...previous }));
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
return () => {
|
|
2255
|
+
mounted.current = false;
|
|
2256
|
+
request.current += 1;
|
|
2257
|
+
};
|
|
2258
|
+
}, []);
|
|
2259
|
+
import_react4.default.useEffect(() => {
|
|
2260
|
+
const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
|
|
2261
|
+
if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
|
|
2262
|
+
let listening = true;
|
|
2263
|
+
const poll = async (id2) => {
|
|
2264
|
+
if (polling.current.has(id2)) return;
|
|
2265
|
+
polling.current.add(id2);
|
|
2266
|
+
try {
|
|
2267
|
+
const response = await officialToolInstallStatus(id2);
|
|
2268
|
+
if (!listening || !mounted.current) return;
|
|
2269
|
+
if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u83B7\u53D6\u5B89\u88C5\u8FDB\u5EA6\u3002"));
|
|
2270
|
+
const job = response.value?.job;
|
|
2271
|
+
if (!job) throw new Error("\u5B89\u88C5\u4EFB\u52A1\u72B6\u6001\u6682\u4E0D\u53EF\u7528\u3002");
|
|
2272
|
+
setJobs((previous) => ({ ...previous, [id2]: job }));
|
|
2273
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
2274
|
+
if (job.status !== "running") void refresh();
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
if (listening && mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text3(error?.message) || "\u5B89\u88C5\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u91CD\u8BD5\u3002" }));
|
|
2277
|
+
} finally {
|
|
2278
|
+
polling.current.delete(id2);
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
const timer = setInterval(() => active.forEach((id2) => {
|
|
2282
|
+
void poll(id2);
|
|
2283
|
+
}), 1500);
|
|
2284
|
+
return () => {
|
|
2285
|
+
listening = false;
|
|
2286
|
+
clearInterval(timer);
|
|
2287
|
+
};
|
|
2288
|
+
}, [jobs, officialToolInstallStatus]);
|
|
2289
|
+
const install = async (id2) => {
|
|
2290
|
+
const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
|
|
2291
|
+
if (!tool || tool.unsupported || submitting.current.has(id2) || jobs[id2]?.status === "running") return;
|
|
2292
|
+
submitting.current.add(id2);
|
|
2293
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
2294
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "running", outputTail: [] } }));
|
|
2295
|
+
try {
|
|
2296
|
+
if (typeof installOfficialTool !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
2297
|
+
const response = await installOfficialTool(id2);
|
|
2298
|
+
if (!mounted.current) return;
|
|
2299
|
+
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u5B89\u88C5\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
2300
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
2301
|
+
} catch (error) {
|
|
2302
|
+
if (mounted.current) {
|
|
2303
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "failed", error: text3(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
|
|
2304
|
+
setRowErrors((previous) => ({ ...previous, [id2]: text3(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
|
|
2305
|
+
}
|
|
2306
|
+
} finally {
|
|
2307
|
+
submitting.current.delete(id2);
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
const cancel = async (id2) => {
|
|
2311
|
+
if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
|
|
2312
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
2313
|
+
try {
|
|
2314
|
+
const response = await cancelOfficialToolInstall(id2);
|
|
2315
|
+
if (!mounted.current) return;
|
|
2316
|
+
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u53D6\u6D88\u8BF7\u6C42\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
2317
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
2318
|
+
if (response.value.job.status !== "running") void refresh();
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
if (mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text3(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
|
|
2321
|
+
}
|
|
2322
|
+
};
|
|
2323
|
+
const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
|
|
2324
|
+
const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
|
|
2325
|
+
const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
|
|
2326
|
+
return /* @__PURE__ */ import_react4.default.createElement("section", { className: "mr-card", "aria-label": "\u5B98\u65B9\u5DE5\u5177" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("h2", { className: "mr-card-title" }, "\u5B98\u65B9\u5DE5\u5177 \xB7 \u4F53\u68C0"), /* @__PURE__ */ import_react4.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\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_react4.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: probeState.status === "loading", onClick: () => {
|
|
2327
|
+
void refresh();
|
|
2328
|
+
} }, "\u91CD\u65B0\u4F53\u68C0")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-body" }, probeState.status === "loading" && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.status === "error" && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
|
|
2329
|
+
const command = installCommandLine(tool);
|
|
2330
|
+
const probe = byId[tool.id];
|
|
2331
|
+
const capability = capabilitiesById[tool.id];
|
|
2332
|
+
const readiness = readinessById[tool.id];
|
|
2333
|
+
const job = jobs[tool.id];
|
|
2334
|
+
const running = job?.status === "running";
|
|
2335
|
+
const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status });
|
|
2336
|
+
const verified = job?.status === "succeeded" && job.postInstallProbe?.installed === true;
|
|
2337
|
+
const status = running ? job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u5B89\u88C5\u2026" : "\u5B89\u88C5\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" ? "\u5B89\u88C5\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B" : verified ? "\u5B89\u88C5\u6210\u529F\u5E76\u9A8C\u8BC1" : probeLabel(probe);
|
|
2338
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, tool.version ? ` \xB7 \u76EE\u6807 ${tool.version}` : ""), /* @__PURE__ */ import_react4.default.createElement(ToolLoginLine, { entry: healthById[tool.id] }), probe?.installed && /* @__PURE__ */ import_react4.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_react4.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, tool.unsupportedReason), probe?.detail && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react4.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_react4.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react4.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react4.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react4.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled, onClick: () => {
|
|
2339
|
+
void install(tool.id);
|
|
2340
|
+
} }, action.label), running && /* @__PURE__ */ import_react4.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
|
|
2341
|
+
void cancel(tool.id);
|
|
2342
|
+
} }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u5B89\u88C5")));
|
|
2343
|
+
})), /* @__PURE__ */ import_react4.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\u5B89\u88C5\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u5B9E\u9645\u7248\u672C\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_react4.default.createElement("code", null, "model_router_tool_install"), "\uFF0C\u6216\u5728\u4F1A\u8BDD\u4F7F\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "/tools"), "\u3002")));
|
|
2344
|
+
}
|
|
2345
|
+
var HEADLESS_TOOLS = /* @__PURE__ */ new Set(["claude-code", "codex", "gemini"]);
|
|
2346
|
+
function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries }) {
|
|
2347
|
+
const [health, setHealth] = import_react4.default.useState({ report: null, error: "", refreshing: false });
|
|
2348
|
+
const [ledger, setLedger] = import_react4.default.useState({ value: null, error: "" });
|
|
2349
|
+
const [boundaries, setBoundaries] = import_react4.default.useState({ value: null, error: "" });
|
|
2350
|
+
const [busy, setBusy] = import_react4.default.useState(false);
|
|
2351
|
+
const mounted = import_react4.default.useRef(true);
|
|
2352
|
+
import_react4.default.useEffect(() => () => {
|
|
2353
|
+
mounted.current = false;
|
|
2354
|
+
}, []);
|
|
2355
|
+
const call = async (operation, fallback) => {
|
|
2356
|
+
if (typeof operation !== "function") throw new Error("\u5DE5\u4F5C\u53F0\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002");
|
|
2357
|
+
return unwrapRemote(await operation(), fallback);
|
|
2358
|
+
};
|
|
2359
|
+
const refreshHealth = async (fresh) => {
|
|
2360
|
+
setHealth((previous) => ({ ...previous, refreshing: true, error: "" }));
|
|
2361
|
+
try {
|
|
2362
|
+
const report = await call(() => toolHealth(fresh === true), "\u4F53\u68C0\u5931\u8D25\u3002");
|
|
2363
|
+
if (mounted.current) setHealth({ report, error: "", refreshing: false });
|
|
2364
|
+
} catch (error) {
|
|
2365
|
+
if (mounted.current) setHealth((previous) => ({ ...previous, refreshing: false, error: text3(error?.message) || "\u4F53\u68C0\u5931\u8D25\u3002" }));
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
const refreshLedger = async () => {
|
|
2369
|
+
try {
|
|
2370
|
+
const value = await call(loadLedger, "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002");
|
|
2371
|
+
if (mounted.current) setLedger({ value, error: "" });
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
if (mounted.current) setLedger((previous) => ({ ...previous, error: text3(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002" }));
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
const refreshBoundaries = async () => {
|
|
2377
|
+
try {
|
|
2378
|
+
const value = await call(loadBoundaries, "\u5B89\u5168\u8FB9\u754C\u8BFB\u53D6\u5931\u8D25\u3002");
|
|
2379
|
+
if (mounted.current) setBoundaries({ value, error: "" });
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
if (mounted.current) setBoundaries({ value: null, error: text3(error?.message) || "\u5B89\u5168\u8FB9\u754C\u8BFB\u53D6\u5931\u8D25\u3002" });
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
const finishOnboarding = async () => {
|
|
2385
|
+
try {
|
|
2386
|
+
const onboarding = await call(completeOnboarding, "\u65E0\u6CD5\u4FDD\u5B58\u4F53\u68C0\u72B6\u6001\u3002");
|
|
2387
|
+
if (mounted.current) setHealth((previous) => ({ ...previous, report: { ...previous.report, onboarding } }));
|
|
2388
|
+
} catch (error) {
|
|
2389
|
+
if (mounted.current) setHealth((previous) => ({ ...previous, error: text3(error?.message) || "\u65E0\u6CD5\u4FDD\u5B58\u4F53\u68C0\u72B6\u6001\u3002" }));
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
const rate = async (runId, packageId, rating) => {
|
|
2393
|
+
setBusy(true);
|
|
2394
|
+
try {
|
|
2395
|
+
await call(() => rateResult({ runId, packageId, rating }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
2396
|
+
await refreshLedger();
|
|
2397
|
+
} catch (error) {
|
|
2398
|
+
if (mounted.current) setLedger((previous) => ({ ...previous, error: text3(error?.message) || "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002" }));
|
|
2399
|
+
} finally {
|
|
2400
|
+
if (mounted.current) setBusy(false);
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
const rerun = async (runId, packageId, override, choice = null) => {
|
|
2404
|
+
const run = ledger.value?.runs?.find((item2) => item2.id === runId);
|
|
2405
|
+
const item = run?.packages?.find((entry) => entry.id === packageId);
|
|
2406
|
+
const reasons = rerunConfirmations({ run, item, override, choice, ledger: ledger.value, health: health.report, toolFor: toolForProvider, headlessIds: HEADLESS_TOOLS });
|
|
2407
|
+
if (reasons.length && !window.confirm(reasons.length === 1 ? `${reasons[0].text}
|
|
2408
|
+
\u7EE7\u7EED\u5417\uFF1F` : `\u91CD\u8DD1\u524D\u9700\u8981\u786E\u8BA4\u4EE5\u4E0B ${reasons.length} \u9879\uFF1A
|
|
2409
|
+
${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
2410
|
+
\u5168\u90E8\u786E\u8BA4\u5E76\u7EE7\u7EED\u5417\uFF1F`)) return;
|
|
2411
|
+
const codes = new Set(reasons.map((entry) => entry.code));
|
|
2412
|
+
setBusy(true);
|
|
2413
|
+
try {
|
|
2414
|
+
const request = {
|
|
2415
|
+
runId,
|
|
2416
|
+
packageId,
|
|
2417
|
+
...override ? { provider: override.provider, model: override.model } : {},
|
|
2418
|
+
...choice ? { subscriptionChoice: choice } : {},
|
|
2419
|
+
...codes.has("over-budget") ? { confirmOverBudget: true } : {},
|
|
2420
|
+
...codes.has("workspace-write") ? { confirmWrite: true } : {}
|
|
2421
|
+
};
|
|
2422
|
+
let result = await call(() => rerunStep(request), "\u91CD\u8DD1\u5931\u8D25\u3002");
|
|
2423
|
+
if (result?.paused && result.budget?.exceeded && !request.confirmOverBudget) {
|
|
2424
|
+
if (!window.confirm(`${result.budget?.message ?? "\u672C\u6B21\u91CD\u8DD1\u4F1A\u8D85\u51FA\u9884\u7B97\u3002"}
|
|
2425
|
+
\u4ECD\u8981\u7EE7\u7EED\u5417\uFF1F`)) return;
|
|
2426
|
+
result = await call(() => rerunStep({ ...request, confirmOverBudget: true }), "\u91CD\u8DD1\u5931\u8D25\u3002");
|
|
2427
|
+
}
|
|
2428
|
+
await refreshLedger();
|
|
2429
|
+
} catch (error) {
|
|
2430
|
+
if (mounted.current) setLedger((previous) => ({ ...previous, error: `${text3(error?.message) || "\u91CD\u8DD1\u5931\u8D25\u3002"} \u53EF\u4EE5\u4FEE\u6B63\u540E\u518D\u70B9\u201C\u91CD\u8DD1\u6B64\u6B65\u201D\uFF0C\u6216\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_rerun_step\u3002` }));
|
|
2431
|
+
} finally {
|
|
2432
|
+
if (mounted.current) setBusy(false);
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
import_react4.default.useEffect(() => {
|
|
2436
|
+
void refreshHealth(false);
|
|
2437
|
+
void refreshLedger();
|
|
2438
|
+
void refreshBoundaries();
|
|
2439
|
+
}, []);
|
|
2440
|
+
return { health, ledger, boundaries, busy, refreshHealth, refreshLedger, refreshBoundaries, finishOnboarding, rate, rerun };
|
|
2441
|
+
}
|
|
2442
|
+
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries, previewRun, startRun }) {
|
|
2443
|
+
const workbench = useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries });
|
|
2444
|
+
const [catalogState, setCatalogState] = import_react4.default.useState({ status: "loading", catalog: null, error: "" });
|
|
2445
|
+
const [task, setTask] = import_react4.default.useState("");
|
|
2446
|
+
const [mode, setMode] = import_react4.default.useState("single");
|
|
2447
|
+
const [directKey, setDirectKey] = import_react4.default.useState("");
|
|
2448
|
+
const [budget, setBudget] = import_react4.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
|
|
2449
|
+
const [query, setQuery] = import_react4.default.useState("");
|
|
2450
|
+
const [plan, setPlan] = import_react4.default.useState(null);
|
|
2451
|
+
const [planError, setPlanError] = import_react4.default.useState("");
|
|
2452
|
+
const [toolProbes, setToolProbes] = import_react4.default.useState(null);
|
|
2453
|
+
const budgetEdited = import_react4.default.useRef(false);
|
|
2454
|
+
const budgetValue = import_react4.default.useRef(budget);
|
|
2455
|
+
const mounted = import_react4.default.useRef(false);
|
|
2456
|
+
const catalogRequest = import_react4.default.useRef(0);
|
|
2457
|
+
import_react4.default.useEffect(() => {
|
|
2458
|
+
const syncBudget = () => {
|
|
2459
|
+
if (budgetEdited.current) return;
|
|
2460
|
+
const next = String(settingsScope.getSnapshot().value?.budgetUsd ?? 0);
|
|
2461
|
+
if (next !== budgetValue.current) {
|
|
2462
|
+
budgetValue.current = next;
|
|
2463
|
+
setBudget(next);
|
|
2464
|
+
setPlan(null);
|
|
2465
|
+
setPlanError("");
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
syncBudget();
|
|
2469
|
+
return settingsScope.subscribe(syncBudget);
|
|
2470
|
+
}, [settingsScope]);
|
|
2471
|
+
import_react4.default.useEffect(() => {
|
|
2472
|
+
mounted.current = true;
|
|
2473
|
+
const request = ++catalogRequest.current;
|
|
2474
|
+
Promise.resolve().then(loadCatalog).then((response) => {
|
|
2475
|
+
if (!mounted.current || request !== catalogRequest.current) return;
|
|
2476
|
+
if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
|
|
2477
|
+
else setCatalogState({ status: "error", catalog: null, error: text3(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
2478
|
+
}).catch((error) => {
|
|
2479
|
+
if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text3(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
2480
|
+
});
|
|
2481
|
+
return () => {
|
|
2482
|
+
mounted.current = false;
|
|
2483
|
+
catalogRequest.current += 1;
|
|
2484
|
+
};
|
|
2485
|
+
}, []);
|
|
2486
|
+
const routes = routesFromModelCatalog(catalogState.catalog);
|
|
2487
|
+
const providerCount = new Set(routes.map((route) => route.provider)).size;
|
|
2488
|
+
const refresh = async () => {
|
|
2489
|
+
const request = ++catalogRequest.current;
|
|
2490
|
+
setCatalogState({ status: "loading", catalog: null, error: "" });
|
|
2491
|
+
setPlan(null);
|
|
2492
|
+
try {
|
|
2493
|
+
const response = await loadCatalog();
|
|
2494
|
+
if (!mounted.current || request !== catalogRequest.current) return;
|
|
2495
|
+
if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
|
|
2496
|
+
else setCatalogState({ status: "error", catalog: null, error: text3(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text3(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
const invalidatePlan = () => {
|
|
2502
|
+
setPlan(null);
|
|
2503
|
+
setPlanError("");
|
|
2504
|
+
};
|
|
2505
|
+
const handleToolProbes = import_react4.default.useCallback((snapshot) => {
|
|
2506
|
+
setToolProbes(snapshot);
|
|
2507
|
+
setPlan(null);
|
|
2508
|
+
setPlanError("");
|
|
2509
|
+
}, []);
|
|
2510
|
+
const generate = () => {
|
|
2511
|
+
setPlanError("");
|
|
2512
|
+
try {
|
|
2513
|
+
if (!text3(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
|
|
2514
|
+
if (routes.length === 0) throw new Error("\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002");
|
|
2515
|
+
const parsedBudget = Number(budget);
|
|
2516
|
+
if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
|
|
2517
|
+
const direct = mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] : null;
|
|
2518
|
+
if (mode === "direct" && !direct) throw new Error("\u8BF7\u9009\u62E9\u8981\u76F4\u63A5\u4F7F\u7528\u7684\u6A21\u578B\u3002");
|
|
2519
|
+
setPlan(createWorkspacePlan(task, catalogState.catalog, {
|
|
2520
|
+
mode,
|
|
2521
|
+
...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
|
|
2522
|
+
budgetUsd: parsedBudget,
|
|
2523
|
+
modelProfilesJson: settingsScope.getSnapshot().value?.modelProfilesJson ?? "[]",
|
|
2524
|
+
installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
|
|
2525
|
+
runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id),
|
|
2526
|
+
preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
2527
|
+
qualityBiases: workbench.ledger.value?.biases ?? null,
|
|
2528
|
+
loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
|
|
2529
|
+
}));
|
|
2530
|
+
} catch (error) {
|
|
2531
|
+
setPlan(null);
|
|
2532
|
+
setPlanError(text3(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
|
|
2533
|
+
}
|
|
2534
|
+
};
|
|
2535
|
+
return /* @__PURE__ */ import_react4.default.createElement("main", { className: "mr-workspace" }, /* @__PURE__ */ import_react4.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react4.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-eyebrow" }, "Model Router \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react4.default.createElement("h1", { className: "mr-title" }, "\u6A21\u578B\u8DEF\u7531\u5DE5\u4F5C\u53F0"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-subtitle" }, "\u67E5\u770B\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E3A\u4EFB\u52A1\u751F\u6210\u8DEF\u7EBF\u5EFA\u8BAE\u4E0E\u56E2\u961F\u5DE5\u4F5C\u5305\u3002\u4E3B\u4F1A\u8BDD\u6A21\u578B\u4ECD\u7531\u5B98\u65B9\u9009\u62E9\u5668\u7BA1\u7406\u3002")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: `mr-status-dot ${catalogState.status === "loading" ? "loading" : catalogState.status === "error" ? "error" : ""}` }), catalogState.status === "ready" ? `${providerCount} \u4E2A\u4F9B\u5E94\u5546 \xB7 ${routes.length} \u6761\u8DEF\u7EBF` : catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55" : "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25")), (workbench.health.report?.notices ?? []).map((notice) => /* @__PURE__ */ import_react4.default.createElement("div", { key: `${notice.kind}-${notice.at}`, className: "mr-error", role: "alert" }, notice.message)), workbench.health.report && !workbench.health.report.onboarding?.completedAt && /* @__PURE__ */ import_react4.default.createElement(
|
|
2536
|
+
OnboardingBanner,
|
|
2537
|
+
{
|
|
2538
|
+
health: workbench.health.report,
|
|
2539
|
+
error: workbench.health.error,
|
|
2540
|
+
refreshing: workbench.health.refreshing,
|
|
2541
|
+
onRefresh: () => {
|
|
2542
|
+
void workbench.refreshHealth(true);
|
|
2543
|
+
},
|
|
2544
|
+
onDone: () => {
|
|
2545
|
+
void workbench.finishOnboarding();
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
2549
|
+
CostControlCard,
|
|
2550
|
+
{
|
|
2551
|
+
ledger: workbench.ledger.value,
|
|
2552
|
+
error: workbench.ledger.error && !workbench.ledger.value ? workbench.ledger.error : "",
|
|
2553
|
+
settingsScope,
|
|
2554
|
+
onChanged: () => {
|
|
2555
|
+
invalidatePlan();
|
|
2556
|
+
void workbench.refreshLedger();
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-grid" }, /* @__PURE__ */ import_react4.default.createElement("section", { className: "mr-card", "aria-label": "\u4EFB\u52A1\u89C4\u5212" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("h2", { className: "mr-card-title" }, "\u4EFB\u52A1\u89C4\u5212"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-card-copy" }, "\u201C\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u201D\u5728\u672C\u673A\u5B8C\u6210\uFF0C\u4E0D\u4F1A\u542F\u52A8\u6A21\u578B\uFF1B\u8981\u5B9E\u9645\u6267\u884C\uFF0C\u8BF7\u5728\u4E0B\u65B9\u201C\u5728\u5DE5\u4F5C\u53F0\u6267\u884C\u201D\u4E2D\u9884\u89C8\u5E76\u786E\u8BA4\u3002"))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react4.default.createElement("label", { className: "mr-label", htmlFor: "mr-task" }, "\u4EFB\u52A1\u63CF\u8FF0"), /* @__PURE__ */ import_react4.default.createElement("textarea", { className: "mr-textarea", id: "mr-task", value: task, onChange: (event) => {
|
|
2560
|
+
setTask(event.target.value);
|
|
2561
|
+
invalidatePlan();
|
|
2562
|
+
}, placeholder: "\u4F8B\u5982\uFF1A\u5206\u6790\u9879\u76EE\u67B6\u6784\uFF0C\u5206\u5DE5\u4FEE\u590D\u5173\u952E\u95EE\u9898\uFF0C\u5E76\u7ED9\u51FA\u9A8C\u6536\u6E05\u5355" }), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "mr-control-label" }, "\u89C4\u5212\u6A21\u5F0F"), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u89C4\u5212\u6A21\u5F0F" }, /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", "aria-pressed": mode === "single", onClick: () => {
|
|
2563
|
+
setMode("single");
|
|
2564
|
+
invalidatePlan();
|
|
2565
|
+
} }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
|
|
2566
|
+
setMode("team");
|
|
2567
|
+
invalidatePlan();
|
|
2568
|
+
} }, "\u56E2\u961F\u5206\u5DE5"), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", "aria-pressed": mode === "direct", onClick: () => {
|
|
2569
|
+
setMode("direct");
|
|
2570
|
+
invalidatePlan();
|
|
2571
|
+
} }, "\u6307\u5B9A\u6A21\u578B"))), mode === "direct" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-control-group mr-direct" }, /* @__PURE__ */ import_react4.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-direct-model" }, "\u76F4\u63A5\u4F7F\u7528"), /* @__PURE__ */ import_react4.default.createElement("select", { className: "mr-input", id: "mr-direct-model", value: directKey || (routes[0] ? `${routes[0].provider}/${routes[0].model}` : ""), onChange: (event) => {
|
|
2572
|
+
setDirectKey(event.target.value);
|
|
2573
|
+
invalidatePlan();
|
|
2574
|
+
} }, routes.map((route) => /* @__PURE__ */ import_react4.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}/${route.model}` }, route.provider, "/", route.model)))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-control-group mr-budget" }, /* @__PURE__ */ import_react4.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-budget" }, "\u672C\u6B21\u4F30\u7B97\u9884\u7B97\uFF08USD\uFF09"), /* @__PURE__ */ import_react4.default.createElement("input", { className: "mr-input", id: "mr-budget", type: "number", min: "0", step: "0.01", value: budget, onChange: (event) => {
|
|
2575
|
+
budgetEdited.current = true;
|
|
2576
|
+
budgetValue.current = event.target.value;
|
|
2577
|
+
setBudget(event.target.value);
|
|
2578
|
+
invalidatePlan();
|
|
2579
|
+
} }))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react4.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react4.default.createElement("span", { className: "mr-caption" }, toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "0 \u8868\u793A\u4E0D\u9650\u5236\u672C\u6B21\u5EFA\u8BAE\uFF1B\u4E0D\u4F1A\u8BBE\u7F6E\u771F\u5B9E\u652F\u51FA\u4E0A\u9650\u3002")), planError && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react4.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-card-copy" }, "\u53EA\u663E\u793A\u5B98\u65B9\u5DF2\u767B\u8BB0\u7684 provider/model\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react4.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react4.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react4.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react4.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002")))), /* @__PURE__ */ import_react4.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), plan && /* @__PURE__ */ import_react4.default.createElement(PlanResults, { plan, ledger: workbench.ledger.value }), /* @__PURE__ */ import_react4.default.createElement(
|
|
2580
|
+
RunLauncher,
|
|
2581
|
+
{
|
|
2582
|
+
task,
|
|
2583
|
+
mode,
|
|
2584
|
+
budgetUsd: budget,
|
|
2585
|
+
ledger: workbench.ledger.value,
|
|
2586
|
+
previewRun,
|
|
2587
|
+
startRun,
|
|
2588
|
+
directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
|
|
2589
|
+
defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
2590
|
+
disabledReason: catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u76EE\u5F55\u201D\u5361\u7247\u70B9\u201C\u5237\u65B0\u201D\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002" : "",
|
|
2591
|
+
onStarted: () => {
|
|
2592
|
+
void workbench.refreshLedger();
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
2596
|
+
RunHistoryCard,
|
|
2597
|
+
{
|
|
2598
|
+
ledger: workbench.ledger.value,
|
|
2599
|
+
routes,
|
|
2600
|
+
busy: workbench.busy,
|
|
2601
|
+
error: workbench.ledger.error,
|
|
2602
|
+
onRefresh: () => {
|
|
2603
|
+
void workbench.refreshLedger();
|
|
2604
|
+
},
|
|
2605
|
+
onRate: (runId, packageId, rating) => {
|
|
2606
|
+
void workbench.rate(runId, packageId, rating);
|
|
2607
|
+
},
|
|
2608
|
+
onRerun: (runId, packageId, override, choice) => {
|
|
2609
|
+
void workbench.rerun(runId, packageId, override, choice);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
2613
|
+
OfficialToolsCard,
|
|
2614
|
+
{
|
|
2615
|
+
listOfficialTools,
|
|
2616
|
+
installOfficialTool,
|
|
2617
|
+
cancelOfficialToolInstall,
|
|
2618
|
+
officialToolInstallStatus,
|
|
2619
|
+
onProbes: handleToolProbes,
|
|
2620
|
+
health: workbench.health.report,
|
|
2621
|
+
onRefreshHealth: () => {
|
|
2622
|
+
void workbench.refreshHealth(true);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
2626
|
+
BillingCard,
|
|
2627
|
+
{
|
|
2628
|
+
billing: workbench.health.report?.billing ?? null,
|
|
2629
|
+
error: workbench.health.report ? "" : workbench.health.error,
|
|
2630
|
+
refreshing: workbench.health.refreshing,
|
|
2631
|
+
onRefresh: () => {
|
|
2632
|
+
void workbench.refreshHealth(true);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
), /* @__PURE__ */ import_react4.default.createElement(SecurityCard, { data: workbench.boundaries.value, error: workbench.boundaries.error, onRefresh: () => {
|
|
2636
|
+
void workbench.refreshBoundaries();
|
|
2637
|
+
} }), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mr-notice" }, "\u53EA\u8BFB\u4EFB\u52A1\u53EF\u5728\u4E0A\u65B9\u201C\u5728\u5DE5\u4F5C\u53F0\u6267\u884C\u201D\u4E2D\u76F4\u63A5\u9884\u89C8\u5E76\u6267\u884C\uFF1B\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u53EF\u4F7F\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_execute"), "\uFF08\u6309\u8DEF\u7531\u6216\u6307\u5B9A\u6A21\u578B\u6267\u884C\uFF0C\u5B98\u65B9 CLI \u5931\u8D25\u5219\u56DE\u9000 API\uFF09\u3001", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_consult"), "\u3001", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_tool_run"), " \u6216 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_team_execute"), "\u3002\u6307\u5B9A\u6A21\u578B\u4F1A\u8DF3\u8FC7\u8DEF\u7EBF\u6BD4\u8F83\u3002\u6258\u7BA1\u6267\u884C\u80FD\u529B\u548C\u5C31\u7EEA\u72B6\u6001\u89C1\u4E0A\u65B9\u5404\u5DE5\u5177\u5361\u7247\uFF1B\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u578B\u4EE5\u5382\u5546\u8BB0\u5F55\u4E3A\u51C6\u3002ZCode 3.14.3 \u4F7F\u7528\u5176\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\u3002\u53EF\u7F16\u8F91\u56E2\u961F\u4EFB\u52A1\u8981\u6C42\u5E72\u51C0\u7684 Git \u4ED3\u5E93\uFF0C\u5E76\u7ECF\u5B98\u65B9\u5DE5\u5177\u5BA1\u6279\u3002\u8BBE\u7F6E\u4F4D\u4E8E\u201C\u63D2\u4EF6 \u2192 \u5DF2\u5B89\u88C5 \u2192 @ljwei-stak/model-router-galgame\u201D\u3002")));
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// .dsh-plugin/shared/official-tools-remote.mjs
|
|
2641
|
+
var OFFICIAL_TOOLS_REMOTE_PACKAGE = "@ljwei-stak/dsh-model-router";
|
|
2642
|
+
var OFFICIAL_TOOLS_REMOTE_NAMESPACE = "modelRouterOfficialTools";
|
|
2643
|
+
function strictCodec(typeSymbol, parse) {
|
|
2644
|
+
return Object.freeze({ mode: "strict", typeSymbol, create: () => ({ parse }) });
|
|
2645
|
+
}
|
|
2646
|
+
function plainObject(value, subject) {
|
|
2647
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2648
|
+
throw new TypeError(`${subject} must be an object`);
|
|
2649
|
+
}
|
|
2650
|
+
return value;
|
|
2651
|
+
}
|
|
2652
|
+
var toolIdCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolId`, (value) => {
|
|
2653
|
+
if (typeof value !== "string" || !getOfficialTool(value)) {
|
|
2654
|
+
throw new TypeError("toolId must name a fixed official tool");
|
|
2655
|
+
}
|
|
2656
|
+
return value;
|
|
2657
|
+
});
|
|
2658
|
+
var listResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolList`, (value) => {
|
|
2659
|
+
const result = plainObject(value, "tool list result");
|
|
2660
|
+
if (!Array.isArray(result.tools)) throw new TypeError("tool list result needs a tools array");
|
|
2661
|
+
return result;
|
|
2662
|
+
});
|
|
2663
|
+
var installResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolInstall`, (value) => {
|
|
2664
|
+
const result = plainObject(value, "install result");
|
|
2665
|
+
if (typeof result.accepted !== "boolean") throw new TypeError("install result needs accepted");
|
|
2666
|
+
if (result.accepted && !result.job) throw new TypeError("accepted install needs a job");
|
|
2667
|
+
if (!result.accepted && typeof result.error !== "string") throw new TypeError("refused install needs an error");
|
|
2668
|
+
return result;
|
|
2669
|
+
});
|
|
2670
|
+
var statusResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolStatus`, (value) => {
|
|
2671
|
+
const result = plainObject(value, "install status result");
|
|
2672
|
+
if (result.job !== null && (typeof result.job !== "object" || Array.isArray(result.job))) {
|
|
2673
|
+
throw new TypeError("install status result needs a job or null");
|
|
2674
|
+
}
|
|
2675
|
+
return result;
|
|
2676
|
+
});
|
|
2677
|
+
var anyObjectCodec = (name) => strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#${name}`, (value) => plainObject(value, name));
|
|
2678
|
+
var freshCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#HealthFresh`, (value) => {
|
|
2679
|
+
if (typeof value !== "boolean") throw new TypeError("fresh must be a boolean");
|
|
2680
|
+
return value;
|
|
2681
|
+
});
|
|
2682
|
+
var idText = (value, subject) => {
|
|
2683
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9._:-]{1,80}$/.test(value)) throw new TypeError(`${subject} must be a short id`);
|
|
2684
|
+
return value;
|
|
2685
|
+
};
|
|
2686
|
+
var optionalRouteText = (value, subject) => {
|
|
2687
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
2688
|
+
if (typeof value !== "string" || value.length > 240 || value.includes("\0")) throw new TypeError(`${subject} is invalid`);
|
|
2689
|
+
return value;
|
|
2690
|
+
};
|
|
2691
|
+
var rateRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RateRequest`, (value) => {
|
|
2692
|
+
const request = plainObject(value, "rate request");
|
|
2693
|
+
if (!["up", "down", "clear"].includes(request.rating)) throw new TypeError("rating must be up, down or clear");
|
|
2694
|
+
return { runId: idText(request.runId, "runId"), packageId: idText(request.packageId, "packageId"), rating: request.rating };
|
|
2695
|
+
});
|
|
2696
|
+
var rerunRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RerunRequest`, (value) => {
|
|
2697
|
+
const request = plainObject(value, "rerun request");
|
|
2698
|
+
const provider = optionalRouteText(request.provider, "provider");
|
|
2699
|
+
const model = optionalRouteText(request.model, "model");
|
|
2700
|
+
if (Boolean(provider) !== Boolean(model)) throw new TypeError("provider and model must be supplied together");
|
|
2701
|
+
return {
|
|
2702
|
+
runId: idText(request.runId, "runId"),
|
|
2703
|
+
packageId: idText(request.packageId, "packageId"),
|
|
2704
|
+
...provider ? { provider, model } : {},
|
|
2705
|
+
confirmOverBudget: request.confirmOverBudget === true,
|
|
2706
|
+
confirmWrite: request.confirmWrite === true,
|
|
2707
|
+
...["api", "subscription", "cancel"].includes(request.subscriptionChoice) ? { subscriptionChoice: request.subscriptionChoice } : {}
|
|
2708
|
+
};
|
|
2709
|
+
});
|
|
2710
|
+
var RUN_CONFIRMATION_CODES = Object.freeze(["workspace-write", "rerun-write", "subscription-api", "over-budget", "unsandboxed"]);
|
|
2711
|
+
var MAX_RUN_TASK_CHARS = 2e5;
|
|
2712
|
+
function parseRunRequest(value) {
|
|
2713
|
+
const request = plainObject(value, "run request");
|
|
2714
|
+
if (typeof request.task !== "string" || !request.task.trim() || request.task.length > MAX_RUN_TASK_CHARS) {
|
|
2715
|
+
throw new TypeError("task must be a non-empty string");
|
|
2716
|
+
}
|
|
2717
|
+
const provider = optionalRouteText(request.provider, "provider");
|
|
2718
|
+
const model = optionalRouteText(request.model, "model");
|
|
2719
|
+
if (Boolean(provider) !== Boolean(model)) throw new TypeError("provider and model must be supplied together");
|
|
2720
|
+
const workspace = request.workspace === void 0 || request.workspace === null || request.workspace === "" ? void 0 : request.workspace;
|
|
2721
|
+
if (workspace !== void 0 && (typeof workspace !== "string" || workspace.length > 4096 || workspace.includes("\0"))) throw new TypeError("workspace is invalid");
|
|
2722
|
+
if (request.budgetUsd !== void 0 && (typeof request.budgetUsd !== "number" || !Number.isFinite(request.budgetUsd) || request.budgetUsd < 0)) {
|
|
2723
|
+
throw new TypeError("budgetUsd must be a non-negative number");
|
|
2724
|
+
}
|
|
2725
|
+
const confirmed = Array.isArray(request.confirmedReasons) ? request.confirmedReasons : [];
|
|
2726
|
+
if (confirmed.length > RUN_CONFIRMATION_CODES.length || confirmed.some((code) => !RUN_CONFIRMATION_CODES.includes(code))) {
|
|
2727
|
+
throw new TypeError("confirmedReasons contains an unknown reason");
|
|
2728
|
+
}
|
|
2729
|
+
return {
|
|
2730
|
+
task: request.task,
|
|
2731
|
+
...provider ? { provider, model } : {},
|
|
2732
|
+
...["single", "team"].includes(request.planMode) ? { planMode: request.planMode } : {},
|
|
2733
|
+
...["economy", "balanced", "quality"].includes(request.preset) ? { preset: request.preset } : {},
|
|
2734
|
+
...request.budgetUsd !== void 0 ? { budgetUsd: request.budgetUsd } : {},
|
|
2735
|
+
...workspace ? { workspace } : {},
|
|
2736
|
+
confirmedReasons: [...new Set(confirmed)]
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
var runRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RunRequest`, parseRunRequest);
|
|
2740
|
+
function descriptor(method, parameters, result) {
|
|
2741
|
+
return Object.freeze({
|
|
2742
|
+
id: `${OFFICIAL_TOOLS_REMOTE_PACKAGE}#${OFFICIAL_TOOLS_REMOTE_NAMESPACE}/${method}`,
|
|
2743
|
+
service: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
|
|
2744
|
+
namespace: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
|
|
2745
|
+
method,
|
|
2746
|
+
invocation: { kind: "direct" },
|
|
2747
|
+
parameters,
|
|
2748
|
+
result
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
var toolIdParameter = Object.freeze({
|
|
2752
|
+
name: "toolId",
|
|
2753
|
+
wire: "toolId",
|
|
2754
|
+
source: "json",
|
|
2755
|
+
codec: toolIdCodec
|
|
2756
|
+
});
|
|
2757
|
+
var jsonParameter = (name, codec) => Object.freeze({ name, wire: name, source: "json", codec });
|
|
2758
|
+
var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
|
|
2759
|
+
descriptor("list", [], listResultCodec),
|
|
2760
|
+
descriptor("installTool", [toolIdParameter], installResultCodec),
|
|
2761
|
+
descriptor("cancel", [toolIdParameter], installResultCodec),
|
|
2762
|
+
descriptor("status", [toolIdParameter], statusResultCodec),
|
|
2763
|
+
// Workbench: onboarding health check, run ledger, ratings, step retry, security boundaries.
|
|
2764
|
+
descriptor("health", [jsonParameter("fresh", freshCodec)], anyObjectCodec("HealthReport")),
|
|
2765
|
+
descriptor("completeOnboarding", [], anyObjectCodec("OnboardingState")),
|
|
2766
|
+
descriptor("ledger", [], anyObjectCodec("RunLedger")),
|
|
2767
|
+
descriptor("rateResult", [jsonParameter("request", rateRequestCodec)], anyObjectCodec("RateResult")),
|
|
2768
|
+
descriptor("rerunStep", [jsonParameter("request", rerunRequestCodec)], anyObjectCodec("RerunResult")),
|
|
2769
|
+
descriptor("boundaries", [], anyObjectCodec("SecurityBoundaries")),
|
|
2770
|
+
// Workbench "start a run": plan preview with cost estimate and confirmation reasons, then execute.
|
|
2771
|
+
descriptor("previewRun", [jsonParameter("request", runRequestCodec)], anyObjectCodec("RunPreview")),
|
|
2772
|
+
descriptor("startRun", [jsonParameter("request", runRequestCodec)], anyObjectCodec("RunStarted"))
|
|
2773
|
+
]);
|
|
2774
|
+
var OFFICIAL_TOOLS_CLIENT_REMOTE = Object.freeze({
|
|
2775
|
+
package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
|
|
2776
|
+
descriptors: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS
|
|
2777
|
+
});
|
|
2778
|
+
var OFFICIAL_TOOLS_HOST_TYPERT = Object.freeze({
|
|
2779
|
+
package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
|
|
2780
|
+
face: "host",
|
|
2781
|
+
schemas: [],
|
|
2782
|
+
invocations: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS,
|
|
2783
|
+
model: { services: [], events: [], objects: [] }
|
|
2784
|
+
});
|
|
2785
|
+
|
|
2786
|
+
// .dsh-plugin/client/official-harness.jsx
|
|
2787
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
2788
|
+
var ROUTER_NAMESPACE = "model-router-galgame";
|
|
2789
|
+
var ROUTER_PACKAGE = "@ljwei-stak/dsh-model-router";
|
|
2790
|
+
var ROUTER_PANEL = "model-router-galgame";
|
|
2791
|
+
var inject = ["remote"];
|
|
2792
|
+
var UI_INJECT = [
|
|
2793
|
+
"slots",
|
|
2794
|
+
"configForms",
|
|
2795
|
+
"remote",
|
|
2796
|
+
"remote.session",
|
|
2797
|
+
`remote.${OFFICIAL_TOOLS_REMOTE_NAMESPACE}`,
|
|
2798
|
+
"layout"
|
|
2799
|
+
];
|
|
2800
|
+
var FORM_LABELS = Object.freeze({
|
|
2801
|
+
unavailable: "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",
|
|
2802
|
+
readOnly: "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002",
|
|
2803
|
+
saveFailed: "\u8BBE\u7F6E\u6CA1\u6709\u88AB\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u8F93\u5165\u540E\u91CD\u8BD5\u3002",
|
|
2804
|
+
save: "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
2805
|
+
saving: "\u4FDD\u5B58\u4E2D\u2026"
|
|
2806
|
+
});
|
|
2807
|
+
var FIELD_COPY = Object.freeze({
|
|
2808
|
+
overridden: "\u5DF2\u8986\u76D6",
|
|
2809
|
+
reset: "\u6062\u590D\u9ED8\u8BA4",
|
|
2810
|
+
invalidNumber: "\u8BF7\u8F93\u5165\u5141\u8BB8\u8303\u56F4\u5185\u7684\u6570\u5B57\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002"
|
|
2811
|
+
});
|
|
2812
|
+
function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
|
|
2813
|
+
const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
|
|
2814
|
+
return {
|
|
2815
|
+
...numeric,
|
|
2816
|
+
parse: (text4) => {
|
|
2817
|
+
const write = numeric.parse(text4);
|
|
2818
|
+
if (write?.kind !== "set") return write;
|
|
2819
|
+
const value = write.value;
|
|
2820
|
+
if (typeof value !== "number" || value < minimum || value > maximum) return void 0;
|
|
2821
|
+
if (integer && (!Number.isSafeInteger(value) || Object.is(value, -0))) return void 0;
|
|
2822
|
+
return write;
|
|
2823
|
+
}
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
function modelProfilesField() {
|
|
2827
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
|
|
2828
|
+
return {
|
|
2829
|
+
...field2,
|
|
2830
|
+
parse: (text4) => {
|
|
2831
|
+
try {
|
|
2832
|
+
parseModelProfilesJson(text4);
|
|
2833
|
+
return field2.parse(text4);
|
|
2834
|
+
} catch {
|
|
2835
|
+
return void 0;
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
function quotaPatternsField() {
|
|
2841
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("quotaPatternsJson");
|
|
2842
|
+
return {
|
|
2843
|
+
...field2,
|
|
2844
|
+
parse: (text4) => {
|
|
2845
|
+
const raw = String(text4 ?? "").trim() || "{}";
|
|
2846
|
+
try {
|
|
2847
|
+
const value = JSON.parse(raw);
|
|
2848
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2849
|
+
} catch {
|
|
2850
|
+
return void 0;
|
|
2851
|
+
}
|
|
2852
|
+
return parseQuotaPatterns(raw).errors.length ? void 0 : field2.parse(text4);
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
var RouterSettingsCardController = class {
|
|
2857
|
+
constructor(scope) {
|
|
2858
|
+
this.form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
|
|
2859
|
+
boundedNumberField("budgetUsd", { minimum: 0 }),
|
|
2860
|
+
boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true }),
|
|
2861
|
+
boundedNumberField("dailyBudgetUsd", { minimum: 0, maximum: 1e6 }),
|
|
2862
|
+
boundedNumberField("monthlyBudgetUsd", { minimum: 0, maximum: 1e6 }),
|
|
2863
|
+
boundedNumberField("reviewSampleRate", { minimum: 0, maximum: 1 }),
|
|
2864
|
+
boundedNumberField("subscriptionCooldownMinutes", { minimum: 1, maximum: 10080, integer: true }),
|
|
2865
|
+
modelProfilesField(),
|
|
2866
|
+
quotaPatternsField()
|
|
2867
|
+
]);
|
|
2868
|
+
this.store = this.form.bind(() => ({
|
|
2869
|
+
...this.form.shell(),
|
|
2870
|
+
budgetUsd: this.form.field("budgetUsd"),
|
|
2871
|
+
maxConsultOutputChars: this.form.field("maxConsultOutputChars"),
|
|
2872
|
+
dailyBudgetUsd: this.form.field("dailyBudgetUsd"),
|
|
2873
|
+
monthlyBudgetUsd: this.form.field("monthlyBudgetUsd"),
|
|
2874
|
+
reviewSampleRate: this.form.field("reviewSampleRate"),
|
|
2875
|
+
subscriptionCooldownMinutes: this.form.field("subscriptionCooldownMinutes"),
|
|
2876
|
+
modelProfilesJson: this.form.field("modelProfilesJson"),
|
|
2877
|
+
quotaPatternsJson: this.form.field("quotaPatternsJson")
|
|
2878
|
+
}));
|
|
2879
|
+
}
|
|
2880
|
+
/** Supply the snapshot hook and staged form actions to the Plugins slot. */
|
|
2881
|
+
inject() {
|
|
2882
|
+
return { hooks: { modelRouterSettings: this.store }, ...this.form.actions() };
|
|
2883
|
+
}
|
|
2884
|
+
/** Release the live Host-form subscription when the client plugin unloads. */
|
|
2885
|
+
dispose() {
|
|
2886
|
+
this.form.dispose();
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
function RouterSettingsCard(props) {
|
|
2890
|
+
const state = props.useModelRouterSettings((snapshot) => snapshot);
|
|
2891
|
+
if (props.view === "summary") {
|
|
2892
|
+
return "\u4E3A\u5B98\u65B9\u5DF2\u914D\u7F6E\u7684\u6A21\u578B\u751F\u6210\u8DEF\u7531\u8BA1\u5212\uFF0C\u5E76\u63D0\u4F9B\u8DE8\u6A21\u578B\u54A8\u8BE2\u5DE5\u5177\u3002";
|
|
2893
|
+
}
|
|
2894
|
+
const disabled = !state.writable || state.saving;
|
|
2895
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_dsh_client_ui_primitives.SettingsForm, { labels: FORM_LABELS, state, onSave: props.save, onDiscard: props.discard }, /* @__PURE__ */ import_react5.default.createElement(
|
|
2896
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
2897
|
+
{
|
|
2898
|
+
id: "model-router-budget-usd",
|
|
2899
|
+
label: "\u5355\u6B21\u8BA1\u5212\u9884\u7B97\u4E0A\u9650\uFF08USD\uFF09",
|
|
2900
|
+
hint: "0 \u8868\u793A\u4E0D\u8BBE\u9884\u7B97\u4E0A\u9650\u3002\u53EA\u6709\u586B\u5199\u4E0B\u65B9\u5B9E\u9645\u4F7F\u7528\u7684\u5355\u4EF7\u540E\uFF0C\u624D\u80FD\u4F30\u7B97\u8D39\u7528\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\u3002",
|
|
2901
|
+
help: {
|
|
2902
|
+
label: "\u9884\u7B97\u8BF4\u660E",
|
|
2903
|
+
content: "\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u9650\u5236\u670D\u52A1\u5546\u6263\u8D39\uFF0C\u4E5F\u4E0D\u4F1A\u8BFB\u53D6\u6216\u4FDD\u5B58 API Key\u3002"
|
|
2904
|
+
},
|
|
2905
|
+
disabled,
|
|
2906
|
+
...state.budgetUsd,
|
|
2907
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
2908
|
+
resetLabel: FIELD_COPY.reset,
|
|
2909
|
+
invalidLabel: FIELD_COPY.invalidNumber,
|
|
2910
|
+
onEdit: (text4) => {
|
|
2911
|
+
props.edit("budgetUsd", text4);
|
|
2912
|
+
},
|
|
2913
|
+
onReset: () => {
|
|
2914
|
+
props.resetField("budgetUsd");
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2918
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
2919
|
+
{
|
|
2920
|
+
id: "model-router-consult-output",
|
|
2921
|
+
label: "\u8DE8\u6A21\u578B\u54A8\u8BE2\u6700\u5927\u8F93\u51FA\u5B57\u7B26\u6570",
|
|
2922
|
+
hint: "\u9650\u5236 model_router_consult \u8FD4\u56DE\u7684\u5B57\u7B26\u6570\uFF0C\u9632\u6B62\u5355\u6B21\u54A8\u8BE2\u5360\u7528\u8FC7\u591A\u4E0A\u4E0B\u6587\u3002",
|
|
2923
|
+
numeric: true,
|
|
2924
|
+
disabled,
|
|
2925
|
+
...state.maxConsultOutputChars,
|
|
2926
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
2927
|
+
resetLabel: FIELD_COPY.reset,
|
|
2928
|
+
invalidLabel: FIELD_COPY.invalidNumber,
|
|
2929
|
+
onEdit: (text4) => {
|
|
2930
|
+
props.edit("maxConsultOutputChars", text4);
|
|
2931
|
+
},
|
|
2932
|
+
onReset: () => {
|
|
2933
|
+
props.resetField("maxConsultOutputChars");
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
), [
|
|
2937
|
+
["dailyBudgetUsd", "model-router-daily-budget", "\u6BCF\u65E5\u6267\u884C\u9884\u7B97\uFF08USD\uFF09", "0 \u8868\u793A\u4E0D\u9650\u3002\u6309\u672C\u673A\u6267\u884C\u8BB0\u5F55\u7684\u5B9E\u9645\u8D39\u7528\u7D2F\u8BA1\uFF0C\u8D85\u51FA\u540E\u6309\u5DE5\u4F5C\u53F0\u8BBE\u7F6E\u81EA\u52A8\u964D\u7EA7\u6216\u6682\u505C\u8BE2\u95EE\u3002"],
|
|
2938
|
+
["monthlyBudgetUsd", "model-router-monthly-budget", "\u6BCF\u6708\u6267\u884C\u9884\u7B97\uFF08USD\uFF09", "0 \u8868\u793A\u4E0D\u9650\u3002\u6309\u672C\u5730\u65F6\u533A\u7684\u81EA\u7136\u6708\u7D2F\u8BA1\u3002"],
|
|
2939
|
+
["reviewSampleRate", "model-router-review-rate", "\u5F3A\u6A21\u578B\u62BD\u68C0\u6BD4\u4F8B\uFF080\u20131\uFF09", "\u8D28\u91CF\u56DE\u8DEF\u8BBE\u4E3A\u201C\u62BD\u68C0\u201D\u65F6\uFF0C\u6309\u6B64\u6BD4\u4F8B\u8BA9\u66F4\u5F3A\u7684\u6A21\u578B\u590D\u6838\u4FBF\u5B9C\u6A21\u578B\u7684\u7ED3\u679C\u3002"],
|
|
2940
|
+
["subscriptionCooldownMinutes", "model-router-subscription-cooldown", "\u8BA2\u9605\u989D\u5EA6\u51B7\u5374\u65F6\u95F4\uFF08\u5206\u949F\uFF09", "\u8BA2\u9605\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u3001\u4F46\u5382\u5546\u6CA1\u6709\u7ED9\u51FA\u6062\u590D\u65F6\u95F4\u65F6\uFF0C\u6682\u505C\u4F7F\u7528\u8BE5\u8BA2\u9605\u7684\u65F6\u957F\uFF1B\u671F\u95F4\u540C\u4E00\u8DEF\u7EBF\u76F4\u63A5\u4F7F\u7528 API Key\u3002"]
|
|
2941
|
+
].map(([key, id2, label, hint]) => /* @__PURE__ */ import_react5.default.createElement(
|
|
2942
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
2943
|
+
{
|
|
2944
|
+
key,
|
|
2945
|
+
id: id2,
|
|
2946
|
+
label,
|
|
2947
|
+
hint,
|
|
2948
|
+
disabled,
|
|
2949
|
+
...state[key],
|
|
2950
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
2951
|
+
resetLabel: FIELD_COPY.reset,
|
|
2952
|
+
invalidLabel: FIELD_COPY.invalidNumber,
|
|
2953
|
+
onEdit: (text4) => {
|
|
2954
|
+
props.edit(key, text4);
|
|
2955
|
+
},
|
|
2956
|
+
onReset: () => {
|
|
2957
|
+
props.resetField(key);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
)), /* @__PURE__ */ import_react5.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react5.default.createElement("span", { style: styles.profileLabel }, "\u5B98\u65B9 CLI \u8BFB\u5199\u8FB9\u754C"), /* @__PURE__ */ import_react5.default.createElement("p", { style: styles.noticeText }, "\u53EA\u8BFB\u6267\u884C\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6\uFF1B\u4FEE\u6539\u6587\u4EF6\u7684\u6267\u884C\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u8FDB\u884C\uFF0C\u9700\u8981\u4F60\u786E\u8BA4\u540E\u624D\u4F1A\u628A\u8865\u4E01\u5E94\u7528\u56DE\u5F53\u524D\u5DE5\u4F5C\u533A\u3002\u5DE5\u4F5C\u53F0\u201C\u5B89\u5168\u8FB9\u754C\u201D\u5361\u7247\u6309\u5DF2\u914D\u7F6E\u6A21\u578B\u9010\u6761\u5217\u51FA\u3002"), /* @__PURE__ */ import_react5.default.createElement("table", { style: styles.boundaryTable }, /* @__PURE__ */ import_react5.default.createElement("thead", null, /* @__PURE__ */ import_react5.default.createElement("tr", null, /* @__PURE__ */ import_react5.default.createElement("th", { style: styles.boundaryCell }, "\u5DE5\u5177"), /* @__PURE__ */ import_react5.default.createElement("th", { style: styles.boundaryCell }, "\u53EA\u8BFB\u6267\u884C\u53EF\u8BFB"), /* @__PURE__ */ import_react5.default.createElement("th", { style: styles.boundaryCell }, "\u4FEE\u6539\u6267\u884C\u53EF\u5199"))), /* @__PURE__ */ import_react5.default.createElement("tbody", null, [["claude-code", "Claude Code"], ["codex", "Codex"], ["gemini", "Gemini CLI"]].map(([toolId, label]) => {
|
|
2961
|
+
const boundary = toolBoundary(toolId);
|
|
2962
|
+
return /* @__PURE__ */ import_react5.default.createElement("tr", { key: toolId }, /* @__PURE__ */ import_react5.default.createElement("td", { style: styles.boundaryCell }, label), /* @__PURE__ */ import_react5.default.createElement("td", { style: styles.boundaryCell }, boundary.readOnly.readable), /* @__PURE__ */ import_react5.default.createElement("td", { style: styles.boundaryCell }, boundary.write?.writable ?? "\u4E0D\u652F\u6301\u4FEE\u6539\u6267\u884C"));
|
|
2963
|
+
})))), /* @__PURE__ */ import_react5.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react5.default.createElement("label", { htmlFor: "model-router-profiles-json", style: styles.profileLabel }, "\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\uFF08JSON\uFF09"), /* @__PURE__ */ import_react5.default.createElement("p", { style: styles.noticeText }, "\u6309\u201C\u6A21\u578B\u76EE\u5F55\u201D\u4E2D\u7684\u51C6\u786E provider/model \u586B\u5199\u3002quality \u4E3A\u81EA\u5B9A\u7684 0\u2013100 \u5206\uFF1Binput/output \u662F\u7F8E\u5143\u6BCF\u767E\u4E07 token\u3002\u7F3A\u5C11\u4EF7\u683C\u65F6\u53EA\u7ED9\u8DEF\u7EBF\u5EFA\u8BAE\uFF0C\u4E0D\u663E\u793A\u865A\u6784\u8D39\u7528\u3002"), /* @__PURE__ */ import_react5.default.createElement(
|
|
2964
|
+
"textarea",
|
|
2965
|
+
{
|
|
2966
|
+
id: "model-router-profiles-json",
|
|
2967
|
+
value: state.modelProfilesJson.text,
|
|
2968
|
+
disabled,
|
|
2969
|
+
"aria-invalid": state.modelProfilesJson.invalid,
|
|
2970
|
+
onChange: (event) => props.edit("modelProfilesJson", event.target.value),
|
|
2971
|
+
spellCheck: false,
|
|
2972
|
+
style: styles.profileTextarea
|
|
2973
|
+
}
|
|
2974
|
+
), state.modelProfilesJson.invalid && /* @__PURE__ */ import_react5.default.createElement("p", { style: styles.profileError, role: "alert" }, "JSON \u683C\u5F0F\u6216\u67D0\u9879\u914D\u7F6E\u65E0\u6548\u3002\u6BCF\u9879\u9700\u63D0\u4F9B\u51C6\u786E\u7684 provider/model\uFF0C\u5355\u4EF7\u4E3A\u975E\u8D1F USD \u6570\u5B57\uFF0C\u8D28\u91CF\u4E3A 0\u2013100\u3002"), /* @__PURE__ */ import_react5.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u67E5\u770B\u914D\u7F6E\u683C\u5F0F"), /* @__PURE__ */ import_react5.default.createElement("pre", null, `[
|
|
2975
|
+
{
|
|
2976
|
+
"provider": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u4F9B\u5E94\u5546 ID",
|
|
2977
|
+
"model": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u6A21\u578B ID",
|
|
2978
|
+
"quality": 80,
|
|
2979
|
+
"pricing": { "input": 0.2, "output": 0.8 },
|
|
2980
|
+
"specialties": ["code"],
|
|
2981
|
+
"cliModel": "\u5382\u5546 CLI \u4F7F\u7528\u7684\u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09",
|
|
2982
|
+
"execution": "auto",
|
|
2983
|
+
"billing": "subscription-first",
|
|
2984
|
+
"subscription": "plan-key",
|
|
2985
|
+
"apiRoute": { "provider": "\u6309\u91CF\u4ED8\u8D39\u4F9B\u5E94\u5546 ID", "model": "\u6A21\u578B ID" }
|
|
2986
|
+
}
|
|
2987
|
+
]`), /* @__PURE__ */ import_react5.default.createElement("p", { style: styles.noticeText }, "execution \u53EF\u7701\u7565\u3002auto \u6216 official \u8868\u793A\u4F18\u5148\u5B98\u65B9\u65E0\u754C\u9762\u5DE5\u5177\uFF0C\u5931\u8D25\u540E\u56DE\u9000 API\uFF1Bapi \u8868\u793A\u59CB\u7EC8\u8D70\u6A21\u578B\u76EE\u5F55\u3002billing \u9ED8\u8BA4 subscription-first\uFF08\u8BA2\u9605\u4F18\u5148\uFF0C\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key\uFF09\uFF0C\u4E5F\u53EF\u8BBE\u4E3A api-only \u6216 subscription-only\u3002subscription \u4E3A plan-key \u8868\u793A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u7F16\u7A0B\u5957\u9910\u7AEF\u70B9\uFF08\u5982 GLM Coding Plan\u3001Kimi Code\u3001MiniMax Token Plan\uFF09\uFF0CapiRoute \u6307\u5B9A\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684\u6309\u91CF\u4ED8\u8D39\u8DEF\u7EBF\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("modelProfilesJson") }, "\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E")), /* @__PURE__ */ import_react5.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react5.default.createElement("label", { htmlFor: "model-router-quota-patterns", style: styles.profileLabel }, "\u8BA2\u9605\u989D\u5EA6\u8BC6\u522B\u89C4\u5219\uFF08JSON\uFF0C\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react5.default.createElement("p", { style: styles.noticeText }, "\u5185\u7F6E\u89C4\u5219\u5DF2\u8986\u76D6 Claude\u3001Codex\u3001Gemini\u3001Kimi\u3001MiniMax\u3001GLM \u7684\u6587\u6863\u5316\u62A5\u9519\u3002\u5382\u5546\u6539\u4E86\u62A5\u9519\u6587\u6848\u65F6\uFF0C\u53EF\u6309\u5DE5\u5177 ID\u3001\u4F9B\u5E94\u5546 ID \u6216 * \u8FFD\u52A0\u6B63\u5219\uFF1Aquota \u8868\u793A\u989D\u5EA6\u7528\u5C3D\uFF0CrateLimit \u8868\u793A\u9650\u6D41\u3002"), /* @__PURE__ */ import_react5.default.createElement(
|
|
2988
|
+
"textarea",
|
|
2989
|
+
{
|
|
2990
|
+
id: "model-router-quota-patterns",
|
|
2991
|
+
value: state.quotaPatternsJson.text,
|
|
2992
|
+
disabled,
|
|
2993
|
+
"aria-invalid": state.quotaPatternsJson.invalid,
|
|
2994
|
+
onChange: (event) => props.edit("quotaPatternsJson", event.target.value),
|
|
2995
|
+
spellCheck: false,
|
|
2996
|
+
style: styles.profileTextarea
|
|
2997
|
+
}
|
|
2998
|
+
), state.quotaPatternsJson.invalid && /* @__PURE__ */ import_react5.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_react5.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u67E5\u770B\u89C4\u5219\u683C\u5F0F"), /* @__PURE__ */ import_react5.default.createElement("pre", null, `{
|
|
2999
|
+
"kimi-code": { "quota": ["\u989D\u5EA6\u5DF2\u7528\u5B8C"], "rateLimit": ["\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41"] },
|
|
3000
|
+
"my-glm-plan": { "quota": ["Usage limit reached for", "1308"] }
|
|
3001
|
+
}`)), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("quotaPatternsJson") }, "\u6062\u590D\u9ED8\u8BA4\u89C4\u5219")), /* @__PURE__ */ import_react5.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react5.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react5.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react5.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_react5.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react5.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")));
|
|
3002
|
+
}
|
|
3003
|
+
function OpenRouterWorkspace({ subject, openPanel }) {
|
|
3004
|
+
if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
|
|
3005
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", type: "button", onClick: openPanel }, "\u6253\u5F00\u5DE5\u4F5C\u53F0");
|
|
3006
|
+
}
|
|
3007
|
+
function registerUi(ctx) {
|
|
3008
|
+
const officialToolsRemote = ctx.remote[OFFICIAL_TOOLS_REMOTE_NAMESPACE];
|
|
3009
|
+
const settingsScope = ctx.configForms.get(ROUTER_NAMESPACE);
|
|
3010
|
+
const card = new RouterSettingsCardController(settingsScope);
|
|
3011
|
+
ctx.effect(() => () => {
|
|
3012
|
+
card.dispose();
|
|
3013
|
+
}, "model-router-galgame: settings form subscription");
|
|
3014
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
3015
|
+
name: "plugins.bundle.config",
|
|
3016
|
+
key: ROUTER_PACKAGE,
|
|
3017
|
+
inject: () => card.inject()
|
|
3018
|
+
}, RouterSettingsCard))), "model-router-galgame: installed bundle settings");
|
|
3019
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("main", () => ctx.slots.register({
|
|
3020
|
+
name: "main",
|
|
3021
|
+
key: ROUTER_PANEL,
|
|
3022
|
+
inject: () => ({
|
|
3023
|
+
loadCatalog: () => ctx.remote.session.modelCatalog(),
|
|
3024
|
+
settingsScope,
|
|
3025
|
+
listOfficialTools: () => officialToolsRemote.list(),
|
|
3026
|
+
installOfficialTool: (toolId) => officialToolsRemote.installTool(toolId),
|
|
3027
|
+
cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
|
|
3028
|
+
officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId),
|
|
3029
|
+
toolHealth: (fresh) => officialToolsRemote.health(fresh),
|
|
3030
|
+
completeOnboarding: () => officialToolsRemote.completeOnboarding(),
|
|
3031
|
+
loadLedger: () => officialToolsRemote.ledger(),
|
|
3032
|
+
rateResult: (request) => officialToolsRemote.rateResult(request),
|
|
3033
|
+
rerunStep: (request) => officialToolsRemote.rerunStep(request),
|
|
3034
|
+
loadBoundaries: () => officialToolsRemote.boundaries(),
|
|
3035
|
+
previewRun: (request) => officialToolsRemote.previewRun(request),
|
|
3036
|
+
startRun: (request) => officialToolsRemote.startRun(request)
|
|
3037
|
+
})
|
|
3038
|
+
}, RouterMainPage))), "model-router-galgame: main workspace");
|
|
3039
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
3040
|
+
name: "sidebar.panellist",
|
|
3041
|
+
id: ROUTER_PANEL,
|
|
3042
|
+
order: 30,
|
|
3043
|
+
label: "\u6A21\u578B\u8DEF\u7531"
|
|
3044
|
+
}, RouterPanelIcon))), "model-router-galgame: sidebar entry");
|
|
3045
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.detail.actions", () => ctx.slots.register({
|
|
3046
|
+
name: "plugins.detail.actions",
|
|
3047
|
+
id: "model-router-open-workspace",
|
|
3048
|
+
order: 30,
|
|
3049
|
+
inject: () => ({ openPanel: () => ctx.layout.selectPanel(ROUTER_PANEL) })
|
|
3050
|
+
}, OpenRouterWorkspace))), "model-router-galgame: bundle open action");
|
|
3051
|
+
}
|
|
3052
|
+
async function apply(ctx) {
|
|
3053
|
+
const disposeRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
|
|
3054
|
+
const ui = ctx.inject(UI_INJECT, registerUi);
|
|
3055
|
+
try {
|
|
3056
|
+
await ui;
|
|
3057
|
+
} catch (error) {
|
|
3058
|
+
await ui.dispose();
|
|
3059
|
+
await disposeRemote();
|
|
3060
|
+
throw error;
|
|
3061
|
+
}
|
|
3062
|
+
return async () => {
|
|
3063
|
+
await ui.dispose();
|
|
3064
|
+
await disposeRemote();
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
3067
|
+
var styles = Object.freeze({
|
|
3068
|
+
titleLine: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: "8px" },
|
|
3069
|
+
notice: {
|
|
3070
|
+
marginTop: "4px",
|
|
3071
|
+
padding: "12px",
|
|
3072
|
+
border: "1px solid var(--dsw-alias-border-l1)",
|
|
3073
|
+
borderRadius: "8px",
|
|
3074
|
+
background: "var(--dsw-alias-markdown-code-block)"
|
|
3075
|
+
},
|
|
3076
|
+
boundaryTable: { width: "100%", borderCollapse: "collapse", fontSize: 12, lineHeight: 1.5 },
|
|
3077
|
+
boundaryCell: { padding: "6px 8px", borderBottom: "1px solid rgba(127, 127, 127, .25)", textAlign: "left", verticalAlign: "top" },
|
|
3078
|
+
noticeText: {
|
|
3079
|
+
margin: "7px 0 0",
|
|
3080
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
3081
|
+
fontSize: "13px",
|
|
3082
|
+
lineHeight: "20px"
|
|
3083
|
+
},
|
|
3084
|
+
profileEditor: { display: "grid", gap: "8px", marginTop: "14px" },
|
|
3085
|
+
profileLabel: { fontSize: "13px", fontWeight: 600 },
|
|
3086
|
+
profileTextarea: { width: "100%", minHeight: "150px", padding: "10px", fontFamily: "monospace", fontSize: "12px", borderRadius: "7px", border: "1px solid var(--dsw-alias-border-l1)", boxSizing: "border-box" },
|
|
3087
|
+
profileError: { color: "var(--dsw-alias-label-danger)", fontSize: "12px", margin: 0 },
|
|
3088
|
+
profileExample: { fontSize: "12px", whiteSpace: "pre-wrap" }
|
|
3089
|
+
});
|
|
3090
|
+
return module.exports;
|
|
3091
|
+
},
|
|
3092
|
+
});
|