@ljwei-stak/dsh-model-router 0.22.1 → 0.23.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 +305 -10
- package/.dsh-plugin/index.mjs +72 -16
- package/.dsh-plugin/official-tools-remote-service.mjs +2 -0
- package/.dsh-plugin/shared/official-team-runtime.mjs +27 -4
- package/.dsh-plugin/shared/official-tools-remote.mjs +11 -0
- package/.dsh-plugin/shared/router-state.mjs +6 -1
- package/.dsh-plugin/shared/run-jobs.mjs +169 -0
- package/.dsh-plugin/shared/task-executors.mjs +28 -6
- package/CHANGELOG.md +14 -0
- package/README.i18n.yaml +2 -2
- package/README.md +59 -17
- package/README.zh.md +59 -17
- package/docs/INSTALLATION_GUIDE.zh.md +12 -12
- package/docs/WORKBENCH_USER_GUIDE.zh.md +5 -1
- package/docs/assets/workbench-run-progress.png +0 -0
- package/docs/releases/0.23.0-validation.md +30 -0
- package/package.json +6 -3
package/.dsh-plugin/client.js
CHANGED
|
@@ -4017,7 +4017,51 @@ var STATUS_TEXT = Object.freeze({
|
|
|
4017
4017
|
"paused-budget": "\u5DF2\u56E0\u9884\u7B97\u6682\u505C\uFF0C\u672A\u542F\u52A8\u6A21\u578B",
|
|
4018
4018
|
"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"
|
|
4019
4019
|
});
|
|
4020
|
-
|
|
4020
|
+
var LIVE_STATUSES = /* @__PURE__ */ new Set(["running", "cancelling"]);
|
|
4021
|
+
var JOB_STATUSES = /* @__PURE__ */ new Set([...LIVE_STATUSES, "completed", "cancelled", "failed"]);
|
|
4022
|
+
var PACKAGE_STATUS_TEXT = Object.freeze({ pending: "\u7B49\u5F85\u6267\u884C", running: "\u6B63\u5728\u6267\u884C", succeeded: "\u5DF2\u5B8C\u6210", failed: "\u6267\u884C\u5931\u8D25", cancelled: "\u5DF2\u53D6\u6D88", blocked: "\u4F9D\u8D56\u672A\u5B8C\u6210", skipped: "\u5DF2\u8DF3\u8FC7" });
|
|
4023
|
+
var STAGE_TEXT = Object.freeze({
|
|
4024
|
+
starting: "\u6B63\u5728\u542F\u52A8\u4EFB\u52A1\u2026",
|
|
4025
|
+
preparing: "\u6B63\u5728\u51C6\u5907\u6267\u884C\u8BA1\u5212\u2026",
|
|
4026
|
+
executing: "\u6B63\u5728\u5B89\u6392\u5DE5\u4F5C\u5305\u2026",
|
|
4027
|
+
integrating: "\u6B63\u5728\u6574\u5408\u5DE5\u4F5C\u5305\u7ED3\u679C\u2026",
|
|
4028
|
+
reviewing: "\u6B63\u5728\u68C0\u67E5\u6267\u884C\u7ED3\u679C\u2026",
|
|
4029
|
+
saving: "\u6B63\u5728\u4FDD\u5B58\u6267\u884C\u8BB0\u5F55\u2026"
|
|
4030
|
+
});
|
|
4031
|
+
var ACTIVE_JOB_KEY = "dsh-model-router:active-run-job";
|
|
4032
|
+
var isJobSnapshot = (value) => Boolean(text2(value?.jobId) && JOB_STATUSES.has(value.status));
|
|
4033
|
+
function savedJob() {
|
|
4034
|
+
try {
|
|
4035
|
+
return typeof sessionStorage === "undefined" ? "" : text2(sessionStorage.getItem(ACTIVE_JOB_KEY));
|
|
4036
|
+
} catch {
|
|
4037
|
+
return "";
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
function rememberJob(jobId) {
|
|
4041
|
+
try {
|
|
4042
|
+
if (typeof sessionStorage !== "undefined") sessionStorage.setItem(ACTIVE_JOB_KEY, jobId);
|
|
4043
|
+
} catch {
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
function forgetJob(jobId) {
|
|
4047
|
+
try {
|
|
4048
|
+
if (typeof sessionStorage !== "undefined" && savedJob() === jobId) sessionStorage.removeItem(ACTIVE_JOB_KEY);
|
|
4049
|
+
} catch {
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
var unknownJob = (error) => /unknown run job id\.?/i.test(text2(error?.message));
|
|
4053
|
+
var pendingLaunch = null;
|
|
4054
|
+
var launchListeners = /* @__PURE__ */ new Set();
|
|
4055
|
+
function notifyLaunch(event) {
|
|
4056
|
+
for (const listener of launchListeners) listener(event);
|
|
4057
|
+
}
|
|
4058
|
+
function settleLaunch(identity, value, error) {
|
|
4059
|
+
if (pendingLaunch !== identity) return;
|
|
4060
|
+
pendingLaunch = null;
|
|
4061
|
+
if (isJobSnapshot(value) && LIVE_STATUSES.has(value.status)) rememberJob(value.jobId);
|
|
4062
|
+
notifyLaunch({ kind: "settled", identity, value, error });
|
|
4063
|
+
}
|
|
4064
|
+
function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger, previewRun, startRun, runStatus, cancelRun, onStarted, disabledReason, planningRevision, assignmentMode = "algorithm", packageAssignments = {}, packagePlanKey: packagePlanKey2 }) {
|
|
4021
4065
|
const [preset, setPreset] = import_react3.default.useState(defaultPreset || "balanced");
|
|
4022
4066
|
const [executor, setExecutor] = import_react3.default.useState("routed");
|
|
4023
4067
|
const [executionMode, setExecutionMode] = import_react3.default.useState("read-only");
|
|
@@ -4030,17 +4074,75 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4030
4074
|
const lastWorkspace = [...ledger?.runs ?? []].map((run) => run.workspace).find(Boolean) ?? "";
|
|
4031
4075
|
const [workspace, setWorkspace] = import_react3.default.useState("");
|
|
4032
4076
|
const [phase, setPhase] = import_react3.default.useState({ kind: "idle" });
|
|
4077
|
+
const currentPhase = import_react3.default.useRef(phase);
|
|
4078
|
+
currentPhase.current = phase;
|
|
4079
|
+
const [now, setNow] = import_react3.default.useState(Date.now);
|
|
4033
4080
|
const mounted = import_react3.default.useRef(true);
|
|
4034
4081
|
const confirmRef = import_react3.default.useRef(null);
|
|
4035
4082
|
const pendingRequest = import_react3.default.useRef(0);
|
|
4083
|
+
const activeRun = import_react3.default.useRef(null);
|
|
4084
|
+
const launchSequence = import_react3.default.useRef(0);
|
|
4085
|
+
const launching = import_react3.default.useRef(false);
|
|
4086
|
+
const awaitingLaunch = import_react3.default.useRef(null);
|
|
4087
|
+
const onFinished = import_react3.default.useRef(onStarted);
|
|
4088
|
+
onFinished.current = onStarted;
|
|
4036
4089
|
const inputSignature = JSON.stringify([task, mode, directRoute?.provider, directRoute?.model, budgetUsd, runPreset, workspace, lastWorkspace, planningRevision, disabledReason, runExecutor, runMode, runEnvironment, assignmentMode, packagePlanKey2, packageAssignments]);
|
|
4037
4090
|
const latestSignature = import_react3.default.useRef(inputSignature);
|
|
4038
4091
|
latestSignature.current = inputSignature;
|
|
4039
4092
|
import_react3.default.useEffect(() => {
|
|
4040
4093
|
mounted.current = true;
|
|
4094
|
+
const resume = (jobId, snapshot) => {
|
|
4095
|
+
activeRun.current = {
|
|
4096
|
+
token: ++launchSequence.current,
|
|
4097
|
+
jobId,
|
|
4098
|
+
terminal: false,
|
|
4099
|
+
cancelRequested: false,
|
|
4100
|
+
cancelPending: false,
|
|
4101
|
+
requestSequence: 0,
|
|
4102
|
+
appliedSequence: 0,
|
|
4103
|
+
restoring: true
|
|
4104
|
+
};
|
|
4105
|
+
launching.current = true;
|
|
4106
|
+
setPhase({ kind: "running", job: snapshot ?? { jobId, status: "running" }, restoring: !snapshot });
|
|
4107
|
+
};
|
|
4108
|
+
const receiveLaunch = (event) => {
|
|
4109
|
+
if (!mounted.current) return;
|
|
4110
|
+
if (event.kind === "pending") {
|
|
4111
|
+
if (activeRun.current?.launchIdentity === event.identity || activeRun.current?.jobId && !activeRun.current.terminal) return;
|
|
4112
|
+
awaitingLaunch.current = event.identity;
|
|
4113
|
+
pendingRequest.current += 1;
|
|
4114
|
+
launching.current = true;
|
|
4115
|
+
setPhase({ kind: "starting" });
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
4118
|
+
if (awaitingLaunch.current !== event.identity) return;
|
|
4119
|
+
awaitingLaunch.current = null;
|
|
4120
|
+
const value = event.value;
|
|
4121
|
+
if (isJobSnapshot(value) && LIVE_STATUSES.has(value.status)) {
|
|
4122
|
+
resume(value.jobId, value);
|
|
4123
|
+
return;
|
|
4124
|
+
}
|
|
4125
|
+
launching.current = false;
|
|
4126
|
+
if (event.error || value?.status === "needs-confirmation") {
|
|
4127
|
+
setPhase({ kind: "error", message: event.error || "\u542F\u52A8\u524D\u7684\u786E\u8BA4\u9879\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u9884\u89C8\u5E76\u786E\u8BA4\u6267\u884C\u3002" });
|
|
4128
|
+
return;
|
|
4129
|
+
}
|
|
4130
|
+
const result = isJobSnapshot(value) ? value.result ?? { status: value.status } : value;
|
|
4131
|
+
if (isJobSnapshot(value)) forgetJob(value.jobId);
|
|
4132
|
+
setPhase({ kind: "done", value: result });
|
|
4133
|
+
onFinished.current?.(result.runId);
|
|
4134
|
+
};
|
|
4135
|
+
launchListeners.add(receiveLaunch);
|
|
4136
|
+
if (pendingLaunch) receiveLaunch({ kind: "pending", identity: pendingLaunch });
|
|
4137
|
+
else {
|
|
4138
|
+
const jobId = savedJob();
|
|
4139
|
+
if (jobId && !activeRun.current) resume(jobId);
|
|
4140
|
+
}
|
|
4041
4141
|
return () => {
|
|
4042
4142
|
mounted.current = false;
|
|
4143
|
+
launchListeners.delete(receiveLaunch);
|
|
4043
4144
|
pendingRequest.current += 1;
|
|
4145
|
+
activeRun.current = null;
|
|
4044
4146
|
};
|
|
4045
4147
|
}, []);
|
|
4046
4148
|
import_react3.default.useEffect(() => {
|
|
@@ -4060,6 +4162,93 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4060
4162
|
import_react3.default.useEffect(() => {
|
|
4061
4163
|
if (phase.kind === "preview") confirmRef.current?.focus();
|
|
4062
4164
|
}, [phase.kind]);
|
|
4165
|
+
const acceptSnapshot = import_react3.default.useCallback((value, token, sequence, cancelAcknowledged = false) => {
|
|
4166
|
+
const active = activeRun.current;
|
|
4167
|
+
if (!mounted.current || !active || active.token !== token || active.terminal || value.jobId !== active.jobId) return false;
|
|
4168
|
+
if (!isJobSnapshot(value)) throw new Error("\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u6267\u884C\u8FDB\u5EA6\u3002");
|
|
4169
|
+
const terminal = !LIVE_STATUSES.has(value.status);
|
|
4170
|
+
if (!terminal && sequence < active.appliedSequence) {
|
|
4171
|
+
if (cancelAcknowledged) {
|
|
4172
|
+
active.cancelRequested = true;
|
|
4173
|
+
setPhase((previous) => previous.kind === "running" ? {
|
|
4174
|
+
...previous,
|
|
4175
|
+
job: { ...previous.job, status: "cancelling", cancelRequested: true },
|
|
4176
|
+
cancelPending: false,
|
|
4177
|
+
cancelError: ""
|
|
4178
|
+
} : previous);
|
|
4179
|
+
return true;
|
|
4180
|
+
}
|
|
4181
|
+
return false;
|
|
4182
|
+
}
|
|
4183
|
+
active.appliedSequence = Math.max(active.appliedSequence, sequence);
|
|
4184
|
+
active.cancelRequested || (active.cancelRequested = cancelAcknowledged || value.cancelRequested || value.status === "cancelling");
|
|
4185
|
+
const job = {
|
|
4186
|
+
...value,
|
|
4187
|
+
cancelRequested: Boolean(active.cancelRequested),
|
|
4188
|
+
status: !terminal && active.cancelRequested ? "cancelling" : value.status
|
|
4189
|
+
};
|
|
4190
|
+
if (terminal) {
|
|
4191
|
+
active.terminal = true;
|
|
4192
|
+
launching.current = false;
|
|
4193
|
+
forgetJob(value.jobId);
|
|
4194
|
+
const result = value.result ?? { status: value.status };
|
|
4195
|
+
setPhase({ kind: "done", value: result, job });
|
|
4196
|
+
onFinished.current?.(result.runId);
|
|
4197
|
+
} else {
|
|
4198
|
+
rememberJob(value.jobId);
|
|
4199
|
+
setPhase((previous) => previous.kind === "running" ? {
|
|
4200
|
+
...previous,
|
|
4201
|
+
job,
|
|
4202
|
+
restoring: false,
|
|
4203
|
+
pollError: "",
|
|
4204
|
+
cancelError: cancelAcknowledged ? "" : previous.cancelError,
|
|
4205
|
+
cancelPending: cancelAcknowledged ? false : previous.cancelPending
|
|
4206
|
+
} : previous);
|
|
4207
|
+
}
|
|
4208
|
+
return true;
|
|
4209
|
+
}, []);
|
|
4210
|
+
const runningJobId = phase.kind === "running" ? phase.job?.jobId : null;
|
|
4211
|
+
import_react3.default.useEffect(() => {
|
|
4212
|
+
if (!runningJobId || typeof runStatus !== "function") return;
|
|
4213
|
+
const active = activeRun.current;
|
|
4214
|
+
if (!active || active.jobId !== runningJobId) return;
|
|
4215
|
+
let disposed = false, timer;
|
|
4216
|
+
const poll = async () => {
|
|
4217
|
+
if (disposed || activeRun.current !== active || active.terminal) return;
|
|
4218
|
+
const sequence = ++active.requestSequence;
|
|
4219
|
+
try {
|
|
4220
|
+
const value = unwrapRemote(await runStatus({ jobId: runningJobId }), "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u6267\u884C\u8FDB\u5EA6\u3002");
|
|
4221
|
+
if (disposed) return;
|
|
4222
|
+
if (!isJobSnapshot(value) || value.jobId !== runningJobId) throw new Error("\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u6267\u884C\u8FDB\u5EA6\u3002");
|
|
4223
|
+
acceptSnapshot(value, active.token, sequence);
|
|
4224
|
+
} catch (error) {
|
|
4225
|
+
if (!disposed && mounted.current && activeRun.current === active && !active.terminal) {
|
|
4226
|
+
if (unknownJob(error)) {
|
|
4227
|
+
active.terminal = true;
|
|
4228
|
+
launching.current = false;
|
|
4229
|
+
forgetJob(runningJobId);
|
|
4230
|
+
setPhase({ kind: "error", message: "\u65E0\u6CD5\u627E\u5230\u6B64\u524D\u7684\u6267\u884C\uFF0C\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u5DE5\u4F5C\u53F0\u5DF2\u91CD\u65B0\u542F\u52A8\u3002\u8BF7\u5237\u65B0\u4E0B\u65B9\u6267\u884C\u8BB0\u5F55\u6838\u5BF9\u7ED3\u679C\u3002" });
|
|
4231
|
+
} else {
|
|
4232
|
+
setPhase((previous) => previous.kind === "running" && previous.job?.jobId === runningJobId ? { ...previous, restoring: false, pollError: "\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u6267\u884C\u8FDB\u5EA6\uFF0C\u5C06\u81EA\u52A8\u91CD\u8BD5\uFF1B\u4EFB\u52A1\u53EF\u80FD\u4ECD\u5728\u6267\u884C\u3002" } : previous);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
} finally {
|
|
4236
|
+
if (!disposed && activeRun.current === active && !active.terminal) timer = setTimeout(poll, 1e3);
|
|
4237
|
+
}
|
|
4238
|
+
};
|
|
4239
|
+
timer = setTimeout(poll, active.restoring ? 0 : 1e3);
|
|
4240
|
+
active.restoring = false;
|
|
4241
|
+
return () => {
|
|
4242
|
+
disposed = true;
|
|
4243
|
+
clearTimeout(timer);
|
|
4244
|
+
};
|
|
4245
|
+
}, [runningJobId, runStatus, acceptSnapshot]);
|
|
4246
|
+
import_react3.default.useEffect(() => {
|
|
4247
|
+
if (!runningJobId) return;
|
|
4248
|
+
setNow(Date.now());
|
|
4249
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
4250
|
+
return () => clearInterval(timer);
|
|
4251
|
+
}, [runningJobId]);
|
|
4063
4252
|
const request = () => launchRequest({
|
|
4064
4253
|
task,
|
|
4065
4254
|
mode,
|
|
@@ -4076,11 +4265,11 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4076
4265
|
});
|
|
4077
4266
|
const unavailable = typeof previewRun !== "function" || typeof startRun !== "function";
|
|
4078
4267
|
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" : manual && !packagePlanKey2 ? "\u8BF7\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u5305\u540E\u518D\u624B\u52A8\u5206\u914D\u3002" : runExecutor === "official-team" && mode !== "team" ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u9700\u8981\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002" : "");
|
|
4079
|
-
const busy = phase.kind === "previewing" || phase.kind === "running";
|
|
4268
|
+
const busy = phase.kind === "previewing" || phase.kind === "running" || phase.kind === "starting";
|
|
4080
4269
|
const recordedRun = phase.kind === "done" && phase.value.runId ? (ledger?.runs ?? []).find((run) => run.id === phase.value.runId) : null;
|
|
4081
4270
|
const resultStatus = recordedRun?.status ?? phase.value?.status;
|
|
4082
4271
|
const preview = async () => {
|
|
4083
|
-
if (blocked || busy) return;
|
|
4272
|
+
if (blocked || busy || launching.current || pendingLaunch) return;
|
|
4084
4273
|
const current = ++pendingRequest.current;
|
|
4085
4274
|
let snapshot;
|
|
4086
4275
|
try {
|
|
@@ -4099,14 +4288,38 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4099
4288
|
}
|
|
4100
4289
|
};
|
|
4101
4290
|
const confirm2 = async () => {
|
|
4102
|
-
if (blocked || phase.kind !== "preview" || phase.signature !== latestSignature.current || phase.value.status === "blocked") return;
|
|
4291
|
+
if (launching.current || pendingLaunch || currentPhase.current !== phase || blocked || phase.kind !== "preview" || phase.signature !== latestSignature.current || phase.value.status === "blocked") return;
|
|
4103
4292
|
const shown = phase.value;
|
|
4104
4293
|
const snapshot = phase.request;
|
|
4105
4294
|
const signature2 = phase.signature;
|
|
4295
|
+
launching.current = true;
|
|
4296
|
+
const identity = /* @__PURE__ */ Symbol("launch");
|
|
4297
|
+
const active = {
|
|
4298
|
+
token: ++launchSequence.current,
|
|
4299
|
+
launchIdentity: identity,
|
|
4300
|
+
jobId: null,
|
|
4301
|
+
terminal: false,
|
|
4302
|
+
cancelRequested: false,
|
|
4303
|
+
cancelPending: false,
|
|
4304
|
+
requestSequence: 0,
|
|
4305
|
+
appliedSequence: 0
|
|
4306
|
+
};
|
|
4307
|
+
activeRun.current = active;
|
|
4308
|
+
pendingLaunch = identity;
|
|
4309
|
+
notifyLaunch({ kind: "pending", identity });
|
|
4106
4310
|
setPhase({ kind: "running", value: shown, request: snapshot, signature: signature2 });
|
|
4107
4311
|
try {
|
|
4108
4312
|
const value = unwrapRemote(await startRun({ ...snapshot, confirmedReasons: (shown.reasons ?? []).map((item) => item.code) }), "\u6267\u884C\u5931\u8D25\u3002");
|
|
4313
|
+
settleLaunch(identity, value);
|
|
4109
4314
|
if (!mounted.current) return;
|
|
4315
|
+
if (activeRun.current !== active) return;
|
|
4316
|
+
if (isJobSnapshot(value)) {
|
|
4317
|
+
active.jobId = value.jobId;
|
|
4318
|
+
acceptSnapshot(value, active.token, ++active.requestSequence);
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
active.terminal = true;
|
|
4322
|
+
launching.current = false;
|
|
4110
4323
|
if (value.status === "needs-confirmation") {
|
|
4111
4324
|
setPhase(signature2 === latestSignature.current ? { kind: "preview", value, request: snapshot, signature: signature2, changed: true } : { kind: "idle" });
|
|
4112
4325
|
return;
|
|
@@ -4114,7 +4327,34 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4114
4327
|
setPhase({ kind: "done", value });
|
|
4115
4328
|
onStarted?.(value.runId);
|
|
4116
4329
|
} catch (error) {
|
|
4117
|
-
|
|
4330
|
+
settleLaunch(identity, null, text2(error?.message) || "\u6267\u884C\u5931\u8D25\u3002");
|
|
4331
|
+
if (mounted.current && activeRun.current === active) {
|
|
4332
|
+
active.terminal = true;
|
|
4333
|
+
launching.current = false;
|
|
4334
|
+
setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u5931\u8D25\u3002" });
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
};
|
|
4338
|
+
const cancel = async () => {
|
|
4339
|
+
const active = activeRun.current;
|
|
4340
|
+
if (!active?.jobId || active.terminal || active.cancelPending || active.cancelRequested || typeof cancelRun !== "function") return;
|
|
4341
|
+
active.cancelPending = true;
|
|
4342
|
+
const sequence = ++active.requestSequence;
|
|
4343
|
+
setPhase((previous) => previous.kind === "running" ? { ...previous, cancelPending: true, cancelError: "" } : previous);
|
|
4344
|
+
try {
|
|
4345
|
+
const value = unwrapRemote(await cancelRun({ jobId: active.jobId }), "\u53D6\u6D88\u8BF7\u6C42\u672A\u786E\u8BA4\u3002");
|
|
4346
|
+
if (!isJobSnapshot(value) || value.jobId !== active.jobId) throw new Error("\u53D6\u6D88\u8BF7\u6C42\u672A\u786E\u8BA4\u3002");
|
|
4347
|
+
acceptSnapshot(value, active.token, sequence, true);
|
|
4348
|
+
} catch {
|
|
4349
|
+
if (mounted.current && activeRun.current === active && !active.terminal) {
|
|
4350
|
+
setPhase((previous) => previous.kind === "running" ? {
|
|
4351
|
+
...previous,
|
|
4352
|
+
cancelPending: false,
|
|
4353
|
+
cancelError: "\u53D6\u6D88\u8BF7\u6C42\u672A\u786E\u8BA4\uFF0C\u4EFB\u52A1\u53EF\u80FD\u4ECD\u5728\u6267\u884C\u3002\u8BF7\u91CD\u8BD5\u53D6\u6D88\u3002"
|
|
4354
|
+
} : previous);
|
|
4355
|
+
}
|
|
4356
|
+
} finally {
|
|
4357
|
+
active.cancelPending = false;
|
|
4118
4358
|
}
|
|
4119
4359
|
};
|
|
4120
4360
|
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\u3001\u6743\u9650\u4E0E\u9884\u4F30\u8D39\u7528\uFF0C\u786E\u8BA4\u540E\u624D\u4F1A\u542F\u52A8\u6267\u884C\u3002\u6A21\u578B\u8DEF\u7531\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF1B\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u53EF\u9009\u62E9\u53EA\u8BFB\u68C0\u67E5\uFF0C\u6216\u5728\u9694\u79BB\u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u7684\u6B63\u5E38\u5DE5\u5177\u7F16\u8F91\u6587\u4EF6\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-executor" }, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-executor" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": runExecutor === "routed", disabled: busy, onClick: () => {
|
|
@@ -4128,7 +4368,7 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4128
4368
|
setExecutionEnvironment("harness");
|
|
4129
4369
|
} }, "\u53EA\u8BFB\u68C0\u67E5"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionMode === "workspace-write", disabled: busy, onClick: () => setExecutionMode("workspace-write") }, "\u7F16\u8F91\u6587\u4EF6"))), runExecutor === "official-team" && executionMode === "workspace-write" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-environment" }, "\u6267\u884C\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-environment" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "harness", disabled: busy, onClick: () => setExecutionEnvironment("harness") }, "Harness \u53D7\u9650\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "cli-native", disabled: busy, onClick: () => setExecutionEnvironment("cli-native") }, "\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883"))), /* @__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: manual ? "\u624B\u52A8\u5206\u914D\u6CBF\u7528\u5F53\u524D\u5EFA\u8BAE\u7684\u65B9\u6848\uFF1B\u66F4\u6539\u65B9\u6848\u540E\u8BF7\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u3002" : item.description, "aria-pressed": runPreset === item.id, disabled: busy || mode === "direct" || manual, 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" }, manual ? "\u624B\u52A8\u5206\u914D\u6309\u5305\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF0C\u53EF\u6DF7\u5408\u6A21\u578B API \u4E0E\u5B98\u65B9\u5DE5\u5177\uFF1B\u5DE5\u5177\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B\u3002\u6240\u9009\u76EE\u6807\u3001\u5C31\u7EEA\u72B6\u6001\u548C\u8D39\u7528\u4EE5\u6267\u884C\u9884\u89C8\u4E3A\u51C6\u3002" : executor === "official-team" ? `${executionMode === "workspace-write" ? "\u7F16\u8F91\u6587\u4EF6\uFF1A\u5404\u5DE5\u4F5C\u5305\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u6B63\u5E38\u5DE5\u5177\uFF0C\u5B8C\u6210\u540E\u6574\u5408\u56DE\u6240\u9009\u5DE5\u4F5C\u533A\u3002" : "\u53EA\u8BFB\u68C0\u67E5\uFF1A\u5404\u5DE5\u4F5C\u5305\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002"}\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002\u5DF2\u767B\u5F55\u4E0D\u4EE3\u8868\u5F53\u524D\u4EFB\u52A1\u6743\u9650\u5DF2\u5C31\u7EEA\uFF0C\u9884\u89C8\u4F1A\u68C0\u67E5\u53EF\u7528\u5DE5\u5177\u3002` : 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\u4F9D\u8D56\u6267\u884C\u53EA\u8BFB\u5DE5\u4F5C\u5305\uFF0C\u6BCF\u6B65\u4F7F\u7528\u53EF\u7528\u7684\u5B98\u65B9 CLI \u6216\u6A21\u578B\u76EE\u5F55 API\u3002" : "\u5355\u4EFB\u52A1\uFF1A\u6309\u65B9\u6848\u9009\u62E9\u4E00\u6761\u8DEF\u7EBF\u3002"), mode !== "team" && !manual && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4F7F\u7528\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u524D\uFF0C\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002"), runEnvironment === "cli-native" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5382\u5546 CLI \u4F7F\u7528\u539F\u767B\u5F55\u3001\u914D\u7F6E\u3001\u6280\u80FD\u53CA MCP\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF\u8FC7 Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u7528\u4E8E\u6536\u96C6\u6539\u52A8\uFF0C\u4E0D\u662F\u64CD\u4F5C\u7CFB\u7EDF\u8BFB\u5199\u8FB9\u754C\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5207\u6362\u5230\u6B64\u73AF\u5883\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: () => {
|
|
4130
4370
|
void preview();
|
|
4131
|
-
} }, 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.signature === inputSignature || phase.kind === "running") && /* @__PURE__ */ import_react3.default.createElement(
|
|
4371
|
+
} }, 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 === "starting" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u7B49\u5F85\u5DF2\u786E\u8BA4\u4EFB\u52A1\u7684\u542F\u52A8\u7ED3\u679C\uFF0C\u8BF7\u7A0D\u5019\u2026"), phase.kind === "error" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, phase.message), (phase.kind === "preview" && phase.signature === inputSignature || phase.kind === "running" && !phase.job) && /* @__PURE__ */ import_react3.default.createElement(
|
|
4132
4372
|
LaunchPreview,
|
|
4133
4373
|
{
|
|
4134
4374
|
value: phase.value,
|
|
@@ -4139,7 +4379,23 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
4139
4379
|
onConfirm: () => {
|
|
4140
4380
|
void confirm2();
|
|
4141
4381
|
},
|
|
4142
|
-
onCancel: () =>
|
|
4382
|
+
onCancel: () => {
|
|
4383
|
+
if (!launching.current) setPhase({ kind: "idle" });
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
), phase.kind === "running" && phase.job && /* @__PURE__ */ import_react3.default.createElement(
|
|
4387
|
+
RunProgress,
|
|
4388
|
+
{
|
|
4389
|
+
job: phase.job,
|
|
4390
|
+
now,
|
|
4391
|
+
restoring: phase.restoring,
|
|
4392
|
+
cancelPending: phase.cancelPending,
|
|
4393
|
+
pollError: phase.pollError || (typeof runStatus !== "function" ? "\u5F53\u524D\u65E0\u6CD5\u66F4\u65B0\u6267\u884C\u8FDB\u5EA6\uFF0C\u4EFB\u52A1\u53EF\u80FD\u4ECD\u5728\u6267\u884C\u3002\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u67E5\u770B\u6267\u884C\u8BB0\u5F55\u3002" : ""),
|
|
4394
|
+
cancelError: phase.cancelError,
|
|
4395
|
+
canCancel: typeof cancelRun === "function",
|
|
4396
|
+
onCancel: () => {
|
|
4397
|
+
void cancel();
|
|
4398
|
+
}
|
|
4143
4399
|
}
|
|
4144
4400
|
), phase.kind === "done" && /* @__PURE__ */ import_react3.default.createElement("div", { className: ["failed", "partial", "blocked", "incomplete"].includes(resultStatus) || resultStatus?.startsWith("paused") ? "mr-error" : "mr-empty", role: "status" }, STATUS_TEXT[resultStatus] ?? `\u72B6\u6001\uFF1A${resultStatus}`, 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 && resultStatus !== "paused-budget" ? "\u5DF2\u6309\u4F60\u7684\u786E\u8BA4\u8D85\u9884\u7B97\u6267\u884C\uFF1A" : ""}${phase.value.budget.message}` : "")));
|
|
4145
4401
|
}
|
|
@@ -4156,7 +4412,25 @@ function LaunchPreview({ value, request, changed, running, confirmRef, onConfirm
|
|
|
4156
4412
|
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"), blocked && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-error", role: "alert" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, "\u5F53\u524D\u65E0\u6CD5\u542F\u52A8\u6267\u884C\u3002"), blocking.length ? /* @__PURE__ */ import_react3.default.createElement("ul", null, blocking.map((message) => /* @__PURE__ */ import_react3.default.createElement("li", { key: message }, message))) : /* @__PURE__ */ import_react3.default.createElement("p", null, "\u6267\u884C\u6761\u4EF6\u5C1A\u672A\u6EE1\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6240\u9009\u6743\u9650\u3001\u5DE5\u4F5C\u533A\u548C\u5B98\u65B9\u5DE5\u5177\u72B6\u6001\u540E\u91CD\u65B0\u9884\u89C8\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" }, officialTeam || manual ? "\u6267\u884C\u65B9\u5F0F" : "\u8DEF\u7EBF"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, manual ? "\u624B\u52A8\u5206\u914D \xB7 \u6309\u5305\u53EA\u8BFB\u6267\u884C" : officialTeam ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C" : 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)))), officialTeam && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4EFB\u52A1\u6743\u9650\uFF1A", readOnly ? "\u53EA\u8BFB\u68C0\u67E5\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\u3002\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002" : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, "\u7F16\u8F91\u6587\u4EF6\u3002\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\u3002\u5B98\u65B9 CLI \u4F7F\u7528\u81EA\u5DF1\u7684\u6B63\u5E38\u5DE5\u5177\uFF0C\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002"), "\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002"), native && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u6267\u884C\u73AF\u5883\uFF1A\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u5DE5\u4F5C\u6811\u4E0D\u9650\u5236\u5DE5\u5177\u8BBF\u95EE\u5DE5\u4F5C\u6811\u5916\u8D44\u6E90\u3002"), !officialTeam && !readOnly && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\uFF1B\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002\u4E0D\u4F1A\u81EA\u52A8\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002"), 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) => {
|
|
4157
4413
|
const tool = item.manualAssignment?.kind === "tool";
|
|
4158
4414
|
return /* @__PURE__ */ import_react3.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.name), (manual || item.selectionSource === "manual") && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-pill" }, "\u624B\u52A8"), " \u2192 ", tool ? `${item.toolLabel || item.toolId || item.manualAssignment.toolId} \xB7 \u9ED8\u8BA4\u6A21\u578B` : item.route || item.toolLabel || item.toolId || item.tool || "\u5F85\u5206\u914D\u8DEF\u7EBF", " ", /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\uFF08\u9884\u4F30 ", tool ? "\u8D39\u7528\u672A\u77E5" : item.estimatedCost === null || item.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(item.estimatedCost), item.channel === "official-cli" ? " \xB7 \u5B98\u65B9 CLI" : item.channel ? " \xB7 \u6A21\u578B\u76EE\u5F55 API" : "", "\uFF09"), item.channelDetail && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, item.channelDetail));
|
|
4159
|
-
})), 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 }, reasonText(item))))) : /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5F53\u524D\u6CA1\u6709\u989D\u5916\u786E\u8BA4\u9879\u3002", value.estimatedCost === null || value.estimatedCost === void 0 ? "\u8D39\u7528\u672A\u77E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u8D39\u7528\u662F\u5426\u5728\u9884\u7B97\u5185\u3002" : value.budget?.limited && value.budget?.estimateKnown && !value.budget?.exceeded ? "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u672C\u6B21\u672A\u8D85\u51FA\u9884\u7B97\uFF1B\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002" : "\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\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 || blocked, 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" }, "\
|
|
4415
|
+
})), 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 }, reasonText(item))))) : /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5F53\u524D\u6CA1\u6709\u989D\u5916\u786E\u8BA4\u9879\u3002", value.estimatedCost === null || value.estimatedCost === void 0 ? "\u8D39\u7528\u672A\u77E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u8D39\u7528\u662F\u5426\u5728\u9884\u7B97\u5185\u3002" : value.budget?.limited && value.budget?.estimateKnown && !value.budget?.exceeded ? "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u672C\u6B21\u672A\u8D85\u51FA\u9884\u7B97\uFF1B\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002" : "\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\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 || blocked, 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" }, "\u6B63\u5728\u542F\u52A8\u6267\u884C\uFF0C\u8BF7\u7A0D\u5019\u2026")));
|
|
4416
|
+
}
|
|
4417
|
+
function RunProgress({ job, now, restoring, pollError, cancelError, cancelPending, canCancel, onCancel }) {
|
|
4418
|
+
const progress = job.progress ?? {};
|
|
4419
|
+
const packages = Array.isArray(progress.packages) ? progress.packages : [];
|
|
4420
|
+
const startedAt = typeof job.startedAt === "number" ? job.startedAt : Date.parse(job.startedAt);
|
|
4421
|
+
const seconds = Number.isFinite(startedAt) ? Math.max(0, Math.floor((now - startedAt) / 1e3)) : 0;
|
|
4422
|
+
const elapsed = Number.isFinite(startedAt) ? `${Math.floor(seconds / 60)} \u5206 ${String(seconds % 60).padStart(2, "0")} \u79D2` : "\u5F85\u66F4\u65B0";
|
|
4423
|
+
const cancelling = job.cancelRequested || job.status === "cancelling";
|
|
4424
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-launch-preview mr-run-progress", role: "region", "aria-label": "\u6267\u884C\u8FDB\u5EA6" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-run-progress-head" }, /* @__PURE__ */ import_react3.default.createElement("strong", { role: "status" }, cancelling ? "\u6B63\u5728\u53D6\u6D88\u2026" : restoring ? "\u6B63\u5728\u6062\u590D\u6267\u884C\u8FDB\u5EA6\u2026" : "\u6B63\u5728\u6267\u884C\u2026"), /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u5DF2\u8017\u65F6 ", elapsed)), job.progress && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5DF2\u5904\u7406 ", progress.completed ?? 0, " / ", progress.total ?? packages.length, " \u4E2A\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react3.default.createElement("progress", { className: "mr-run-meter", "aria-label": "\u5DE5\u4F5C\u5305\u5B8C\u6210\u8FDB\u5EA6", value: progress.completed ?? 0, max: Math.max(1, progress.total ?? packages.length) })), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption", "aria-live": "polite" }, progress.current ? `\u5F53\u524D\u5DE5\u4F5C\u5305\uFF1A${progress.current.name || progress.current.id}` : cancelling ? "\u6B63\u5728\u7ED3\u675F\u5F53\u524D\u6267\u884C\u5E76\u4FDD\u7559\u5DF2\u5B8C\u6210\u7ED3\u679C\u3002" : STAGE_TEXT[progress.stage] ?? "\u6B63\u5728\u8BFB\u53D6\u6267\u884C\u8FDB\u5EA6\u2026"), packages.length > 0 && /* @__PURE__ */ import_react3.default.createElement("ol", { className: "mr-run-packages" }, packages.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react3.default.createElement("span", null, item.name || item.id), /* @__PURE__ */ import_react3.default.createElement("span", { className: `mr-pill mr-run-package-${item.status}` }, PACKAGE_STATUS_TEXT[item.status] ?? "\u7B49\u5F85\u66F4\u65B0")))), pollError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "status" }, pollError), cancelError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, cancelError), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement(
|
|
4425
|
+
"button",
|
|
4426
|
+
{
|
|
4427
|
+
className: "mr-button mr-button-secondary",
|
|
4428
|
+
type: "button",
|
|
4429
|
+
disabled: !canCancel || cancelPending || cancelling,
|
|
4430
|
+
onClick: onCancel
|
|
4431
|
+
},
|
|
4432
|
+
cancelPending ? "\u6B63\u5728\u8BF7\u6C42\u53D6\u6D88\u2026" : cancelling ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u6267\u884C"
|
|
4433
|
+
), cancelling && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u53D6\u6D88\u5DF2\u8BF7\u6C42\uFF0C\u7B49\u5F85\u6267\u884C\u7ED3\u675F\uFF1B\u5DF2\u5B8C\u6210\u7ED3\u679C\u4F1A\u4FDD\u7559\u5728\u6267\u884C\u8BB0\u5F55\u4E2D\u3002"), !canCancel && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u5F53\u524D\u5DE5\u4F5C\u53F0\u6682\u4E0D\u652F\u6301\u53D6\u6D88\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u3002")));
|
|
4160
4434
|
}
|
|
4161
4435
|
|
|
4162
4436
|
// .dsh-plugin/client/cli-terminal.jsx
|
|
@@ -13589,7 +13863,7 @@ var xterm_default = `/**
|
|
|
13589
13863
|
`;
|
|
13590
13864
|
|
|
13591
13865
|
// .dsh-plugin/client/host-version.mjs
|
|
13592
|
-
var ROUTER_CLIENT_VERSION = true ? "0.
|
|
13866
|
+
var ROUTER_CLIENT_VERSION = true ? "0.23.0" : "";
|
|
13593
13867
|
var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
|
|
13594
13868
|
function isMissingRemoteMethod(message) {
|
|
13595
13869
|
const value = String(message ?? "");
|
|
@@ -14315,12 +14589,21 @@ var router_main_default = `.mr-workspace {
|
|
|
14315
14589
|
.mr-launch-workspace { flex: 1 1 320px; min-width: min(100%, 260px); }
|
|
14316
14590
|
.mr-launch-preview { margin-top: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 12px; background: #f9fbfe; }
|
|
14317
14591
|
.mr-launch-packages { margin: 12px 0 0; padding-left: 20px; font-size: 12px; line-height: 1.7; }
|
|
14592
|
+
.mr-run-progress-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
|
|
14593
|
+
.mr-run-meter { display: block; width: 100%; height: 8px; accent-color: var(--mr-accent); }
|
|
14594
|
+
.mr-run-packages { display: grid; gap: 7px; margin: 12px 0; padding-left: 20px; font-size: 12px; line-height: 1.6; }
|
|
14595
|
+
.mr-run-packages li > span:first-child { margin-right: 8px; }
|
|
14596
|
+
.mr-run-package-running { color: var(--mr-accent); }
|
|
14597
|
+
.mr-run-package-failed, .mr-run-package-blocked { color: #b42c39; }
|
|
14598
|
+
.mr-run-package-succeeded { color: #187042; }
|
|
14318
14599
|
.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; }
|
|
14319
14600
|
.mr-confirm-box .mr-section-title { margin: 0 0 6px; }
|
|
14320
14601
|
.mr-confirm-box ol { margin: 0; padding-left: 20px; }
|
|
14321
14602
|
.mr-break { overflow-wrap: anywhere; }
|
|
14322
14603
|
@media (prefers-color-scheme: dark) {
|
|
14323
14604
|
.mr-launch-preview { background: rgba(255, 255, 255, .04); }
|
|
14605
|
+
.mr-run-package-failed, .mr-run-package-blocked { color: #ffd4da; }
|
|
14606
|
+
.mr-run-package-succeeded { color: #b4f1cc; }
|
|
14324
14607
|
.mr-confirm-box { background: rgba(217, 140, 30, .12); color: #f2d39b; }
|
|
14325
14608
|
}
|
|
14326
14609
|
|
|
@@ -15114,7 +15397,7 @@ ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
|
15114
15397
|
}, []);
|
|
15115
15398
|
return { health, ledger, boundaries, busy, refreshHealth, refreshLedger, refreshBoundaries, finishOnboarding, rate, rerun };
|
|
15116
15399
|
}
|
|
15117
|
-
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOfficialInstallSettings, installOfficialTool, uninstallOfficialTool, repairOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries, previewRun, startRun, terminalApi }) {
|
|
15400
|
+
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOfficialInstallSettings, installOfficialTool, uninstallOfficialTool, repairOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries, previewRun, startRun, runStatus, cancelRun, terminalApi }) {
|
|
15118
15401
|
const workbench = useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries });
|
|
15119
15402
|
const [catalogState, setCatalogState] = import_react5.default.useState({ status: "loading", catalog: null, error: "" });
|
|
15120
15403
|
const [task, setTask] = import_react5.default.useState("");
|
|
@@ -15336,6 +15619,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
|
|
|
15336
15619
|
ledger: workbench.ledger.value,
|
|
15337
15620
|
previewRun,
|
|
15338
15621
|
startRun,
|
|
15622
|
+
runStatus,
|
|
15623
|
+
cancelRun,
|
|
15339
15624
|
planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}/${evidenceRevision}`,
|
|
15340
15625
|
assignmentMode: effectiveAssignmentMode,
|
|
15341
15626
|
packageAssignments: effectiveAssignmentMode === "manual" ? packageAssignments : {},
|
|
@@ -15494,6 +15779,12 @@ var idText = (value, subject) => {
|
|
|
15494
15779
|
if (typeof value !== "string" || !/^[A-Za-z0-9._:-]{1,80}$/.test(value)) throw new TypeError(`${subject} must be a short id`);
|
|
15495
15780
|
return value;
|
|
15496
15781
|
};
|
|
15782
|
+
function parseRunJobRequest(value) {
|
|
15783
|
+
const request = plainObject2(value, "run job request");
|
|
15784
|
+
if (Object.keys(request).some((key) => key !== "jobId")) throw new TypeError("run job request only accepts jobId");
|
|
15785
|
+
return { jobId: idText(request.jobId, "jobId") };
|
|
15786
|
+
}
|
|
15787
|
+
var runJobRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RunJobRequest`, parseRunJobRequest);
|
|
15497
15788
|
var optionalRouteText = (value, subject) => {
|
|
15498
15789
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
15499
15790
|
if (typeof value !== "string" || value.length > 240 || value.includes("\0")) throw new TypeError(`${subject} is invalid`);
|
|
@@ -15619,6 +15910,8 @@ var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
|
|
|
15619
15910
|
// Workbench "start a run": plan preview with cost estimate and confirmation reasons, then execute.
|
|
15620
15911
|
descriptor("previewRun", [jsonParameter("request", runRequestCodec)], anyObjectCodec("RunPreview")),
|
|
15621
15912
|
descriptor("startRun", [jsonParameter("request", runRequestCodec)], anyObjectCodec("RunStarted")),
|
|
15913
|
+
descriptor("runStatus", [jsonParameter("request", runJobRequestCodec)], anyObjectCodec("RunJobStatus")),
|
|
15914
|
+
descriptor("cancelRun", [jsonParameter("request", runJobRequestCodec)], anyObjectCodec("RunJobStatus")),
|
|
15622
15915
|
// Workbench "官方工具终端": interactive shell / fixed official CLI sessions.
|
|
15623
15916
|
// Output streams through long-poll reads; input and resize are unary calls.
|
|
15624
15917
|
descriptor("terminalInfo", [], anyObjectCodec("TerminalInfo")),
|
|
@@ -16072,6 +16365,8 @@ function registerUi(ctx) {
|
|
|
16072
16365
|
loadBoundaries: () => officialToolsRemote.boundaries(),
|
|
16073
16366
|
previewRun: (request) => officialToolsRemote.previewRun(request),
|
|
16074
16367
|
startRun: (request) => officialToolsRemote.startRun(request),
|
|
16368
|
+
runStatus: (request) => officialToolsRemote.runStatus(request),
|
|
16369
|
+
cancelRun: (request) => officialToolsRemote.cancelRun(request),
|
|
16075
16370
|
terminalApi: Object.freeze({
|
|
16076
16371
|
terminalInfo: () => officialToolsRemote.terminalInfo(),
|
|
16077
16372
|
terminalStart: (request) => officialToolsRemote.terminalStart(request),
|