@ran-sh/dsh-crew 0.4.2 → 0.5.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/README.md +5 -3
- package/README.zh.md +5 -3
- package/docs/ui-surfaces.md +47 -0
- package/lib/client.js +978 -498
- package/official-web-bridge/lib/client.js +978 -498
- package/package.json +5 -4
- package/src/client/collapsible-sections.mjs +4 -3
- package/src/client/entry.tsx +9 -236
- package/src/client/host-readiness.mjs +45 -0
- package/src/client/index.tsx +368 -123
- package/src/client/surface-detection.mjs +30 -0
- package/src/client/task-telemetry.mjs +72 -0
- package/src/install/install-legacy.mjs +113 -14
- package/src/official-web-bridge.mjs +6 -1
- package/src/runtime-identity.mjs +6 -4
|
@@ -35,6 +35,7 @@ const SETTINGS_SECTION_IDS = Object.freeze([
|
|
|
35
35
|
"flash",
|
|
36
36
|
"pro",
|
|
37
37
|
"dispatch",
|
|
38
|
+
"adaptive",
|
|
38
39
|
"runtime",
|
|
39
40
|
"multimodal",
|
|
40
41
|
"providers",
|
|
@@ -70,17 +71,276 @@ function openSections(state, ids) {
|
|
|
70
71
|
return next;
|
|
71
72
|
}
|
|
72
73
|
//#endregion
|
|
74
|
+
//#region src/client/activation-summary.tsx
|
|
75
|
+
const ORDER = [
|
|
76
|
+
"live",
|
|
77
|
+
"next-workflow",
|
|
78
|
+
"next-session",
|
|
79
|
+
"restart-required"
|
|
80
|
+
];
|
|
81
|
+
const LABELS = {
|
|
82
|
+
zh: {
|
|
83
|
+
title: "配置生效边界",
|
|
84
|
+
hint: "这里显示全局 Settings 保存后的实际生效时机;会话内 dsh_worker_config 覆盖可能更早生效。",
|
|
85
|
+
boundary: {
|
|
86
|
+
live: "Live · 当前运行时",
|
|
87
|
+
"next-workflow": "Next workflow · 下一个任务",
|
|
88
|
+
"next-session": "Next session · 新 CC / Codex 会话",
|
|
89
|
+
"restart-required": "Restart required · 重启 DSH / MCP"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
en: {
|
|
93
|
+
title: "Configuration activation boundaries",
|
|
94
|
+
hint: "Shows when persisted Settings changes actually take effect. Session-level dsh_worker_config overrides may activate earlier.",
|
|
95
|
+
boundary: {
|
|
96
|
+
live: "Live · current runtime",
|
|
97
|
+
"next-workflow": "Next workflow",
|
|
98
|
+
"next-session": "Next session · new CC / Codex session",
|
|
99
|
+
"restart-required": "Restart required · restart DSH / MCP"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function groupActivationBoundaries(activation = {}) {
|
|
104
|
+
const grouped = Object.fromEntries(ORDER.map((boundary) => [boundary, []]));
|
|
105
|
+
for (const [key, entry] of Object.entries(activation)) if (entry?.global && grouped[entry.global]) grouped[entry.global].push(key);
|
|
106
|
+
for (const boundary of ORDER) grouped[boundary].sort();
|
|
107
|
+
return grouped;
|
|
108
|
+
}
|
|
109
|
+
function ActivationSummary({ activation, locale }) {
|
|
110
|
+
if (!activation || Object.keys(activation).length === 0) return null;
|
|
111
|
+
const copy = LABELS[locale === "zh" ? "zh" : "en"];
|
|
112
|
+
const grouped = groupActivationBoundaries(activation);
|
|
113
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
114
|
+
style: {
|
|
115
|
+
border: "1px solid rgba(128,128,128,0.22)",
|
|
116
|
+
borderRadius: 8,
|
|
117
|
+
padding: "9px 12px",
|
|
118
|
+
display: "flex",
|
|
119
|
+
flexDirection: "column",
|
|
120
|
+
gap: 5
|
|
121
|
+
},
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
124
|
+
style: {
|
|
125
|
+
fontWeight: 600,
|
|
126
|
+
fontSize: 12.5
|
|
127
|
+
},
|
|
128
|
+
children: copy.title
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
131
|
+
style: {
|
|
132
|
+
fontSize: 11,
|
|
133
|
+
opacity: .6
|
|
134
|
+
},
|
|
135
|
+
children: copy.hint
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
138
|
+
style: {
|
|
139
|
+
display: "grid",
|
|
140
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
141
|
+
gap: "5px 12px"
|
|
142
|
+
},
|
|
143
|
+
children: ORDER.map((boundary) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
144
|
+
style: { minWidth: 0 },
|
|
145
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
146
|
+
style: {
|
|
147
|
+
fontSize: 10.5,
|
|
148
|
+
opacity: .7,
|
|
149
|
+
fontWeight: 600
|
|
150
|
+
},
|
|
151
|
+
children: copy.boundary[boundary]
|
|
152
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
153
|
+
style: {
|
|
154
|
+
fontSize: 10.5,
|
|
155
|
+
opacity: .55,
|
|
156
|
+
wordBreak: "break-word"
|
|
157
|
+
},
|
|
158
|
+
children: grouped[boundary].length ? grouped[boundary].join(" · ") : "—"
|
|
159
|
+
})]
|
|
160
|
+
}, boundary))
|
|
161
|
+
})
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/client/host-readiness.mjs
|
|
167
|
+
const READINESS_STATES = Object.freeze({
|
|
168
|
+
READY: "READY",
|
|
169
|
+
DEGRADED: "DEGRADED",
|
|
170
|
+
UNAVAILABLE: "UNAVAILABLE",
|
|
171
|
+
UNKNOWN: "UNKNOWN"
|
|
172
|
+
});
|
|
173
|
+
function componentState(status, keys, { aligned = false } = {}) {
|
|
174
|
+
if (status === void 0 || status === null || typeof status !== "object") return READINESS_STATES.UNKNOWN;
|
|
175
|
+
if (status.installed === false) return READINESS_STATES.UNAVAILABLE;
|
|
176
|
+
if (status.installed !== true || !status.components || typeof status.components !== "object") return READINESS_STATES.UNKNOWN;
|
|
177
|
+
const evidence = keys.map((key) => status.components[key]);
|
|
178
|
+
if (evidence.some((value) => typeof value !== "boolean")) return READINESS_STATES.UNKNOWN;
|
|
179
|
+
return evidence.every(Boolean) && (!aligned || status.components.target_alignment === true) ? READINESS_STATES.READY : READINESS_STATES.DEGRADED;
|
|
180
|
+
}
|
|
181
|
+
function runtimeState(runtime) {
|
|
182
|
+
if (runtime === void 0) return READINESS_STATES.UNKNOWN;
|
|
183
|
+
if (runtime === null) return READINESS_STATES.UNAVAILABLE;
|
|
184
|
+
return runtime?.ok === true && runtime.service === "dsh-crew-hub" ? READINESS_STATES.READY : READINESS_STATES.DEGRADED;
|
|
185
|
+
}
|
|
186
|
+
function bridgeState(surface) {
|
|
187
|
+
if (surface === "official-bridge") return READINESS_STATES.READY;
|
|
188
|
+
if (surface === "native-crew-harness") return READINESS_STATES.UNAVAILABLE;
|
|
189
|
+
return READINESS_STATES.UNKNOWN;
|
|
190
|
+
}
|
|
191
|
+
/** Project only structured, non-secret installer/runtime evidence. */
|
|
192
|
+
function projectHostReadiness({ installStatus, runtime, surface } = {}) {
|
|
193
|
+
const codex = installStatus?.codex;
|
|
194
|
+
const claude = installStatus?.claude;
|
|
195
|
+
return [
|
|
196
|
+
{
|
|
197
|
+
id: "codex_mcp",
|
|
198
|
+
state: componentState(codex, ["mcp"], { aligned: true })
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "ds_worker",
|
|
202
|
+
state: componentState(codex, ["worker_role"], { aligned: true })
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "ds_reviewer",
|
|
206
|
+
state: componentState(codex, ["reviewer_role"], { aligned: true })
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "claude_plugin",
|
|
210
|
+
state: componentState(claude, [
|
|
211
|
+
"enabled",
|
|
212
|
+
"marketplace",
|
|
213
|
+
"snapshot",
|
|
214
|
+
"permissions"
|
|
215
|
+
])
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "crew_harness",
|
|
219
|
+
state: runtimeState(runtime),
|
|
220
|
+
detail: runtime?.runtime_version ?? null
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: "official_bridge",
|
|
224
|
+
state: bridgeState(surface)
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/client/surface-detection.mjs
|
|
230
|
+
const CREW_UI_SURFACES = Object.freeze({
|
|
231
|
+
OFFICIAL: "official-bridge",
|
|
232
|
+
NATIVE: "native-crew-harness",
|
|
233
|
+
UNKNOWN: "unknown"
|
|
234
|
+
});
|
|
235
|
+
/**
|
|
236
|
+
* Classify the current same-origin UI from explicit backend contracts. The
|
|
237
|
+
* official bridge signal wins because its proxied runtime response correctly
|
|
238
|
+
* describes the 3210 backend, not the browser-facing 3080 surface.
|
|
239
|
+
*/
|
|
240
|
+
function classifyCrewSurface({ bridgeStatus, runtime } = {}) {
|
|
241
|
+
if (bridgeStatus?.ok === true && (bridgeStatus.surface === CREW_UI_SURFACES.OFFICIAL || bridgeStatus.mode === "official-3080-isolated-3210")) return CREW_UI_SURFACES.OFFICIAL;
|
|
242
|
+
if (runtime?.ok === true && runtime.service === "dsh-crew-hub" && (runtime.surface === CREW_UI_SURFACES.NATIVE || runtime.surface === void 0)) return CREW_UI_SURFACES.NATIVE;
|
|
243
|
+
return CREW_UI_SURFACES.UNKNOWN;
|
|
244
|
+
}
|
|
245
|
+
function surfaceResponsibilities(surface) {
|
|
246
|
+
const fullControlPlane = surface === CREW_UI_SURFACES.OFFICIAL;
|
|
247
|
+
return {
|
|
248
|
+
fullControlPlane,
|
|
249
|
+
minimalDiagnostics: !fullControlPlane
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/client/task-telemetry.mjs
|
|
254
|
+
function clean(value, fallback) {
|
|
255
|
+
return (typeof value === "string" ? value.trim() : "") || fallback;
|
|
256
|
+
}
|
|
257
|
+
function validTimestamp(value) {
|
|
258
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) return null;
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
function sorted(values) {
|
|
262
|
+
return [...values].sort((left, right) => left.localeCompare(right));
|
|
263
|
+
}
|
|
264
|
+
function hasInvocationEvidence(job) {
|
|
265
|
+
const tokenCount = Number(job?.tokens?.input ?? 0) + Number(job?.tokens?.output ?? 0);
|
|
266
|
+
return Number(job?.turn ?? 0) > 0 || Number(job?.toolCalls ?? 0) > 0 || tokenCount > 0;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Build a bounded, presentation-only model activity summary from the jobs the
|
|
270
|
+
* Hub already exposes. No prompts, results, credentials, or new persistence
|
|
271
|
+
* are introduced here.
|
|
272
|
+
*/
|
|
273
|
+
function aggregateModelInvocations(jobs = []) {
|
|
274
|
+
const groups = /* @__PURE__ */ new Map();
|
|
275
|
+
const recentJobs = Array.isArray(jobs) ? jobs.slice(-500) : [];
|
|
276
|
+
for (const job of recentJobs) {
|
|
277
|
+
const provider = clean(job?.provider, "");
|
|
278
|
+
const model = clean(job?.model, "");
|
|
279
|
+
if (!provider || !model || !hasInvocationEvidence(job)) continue;
|
|
280
|
+
const key = `${provider}\0${model}`;
|
|
281
|
+
const current = groups.get(key) ?? {
|
|
282
|
+
provider,
|
|
283
|
+
model,
|
|
284
|
+
count: 0,
|
|
285
|
+
taskSources: /* @__PURE__ */ new Set(),
|
|
286
|
+
selectionSources: /* @__PURE__ */ new Set(),
|
|
287
|
+
roles: /* @__PURE__ */ new Set(),
|
|
288
|
+
lastCalledAt: null
|
|
289
|
+
};
|
|
290
|
+
current.count += 1;
|
|
291
|
+
current.taskSources.add(clean(job?.source, "api"));
|
|
292
|
+
current.selectionSources.add(clean(job?.selection_source, "unknown"));
|
|
293
|
+
current.roles.add(job?.role === "reviewer" ? "reviewer" : "worker");
|
|
294
|
+
const calledAt = validTimestamp(job?.startedAt);
|
|
295
|
+
if (calledAt && (!current.lastCalledAt || Date.parse(calledAt) > Date.parse(current.lastCalledAt))) current.lastCalledAt = calledAt;
|
|
296
|
+
groups.set(key, current);
|
|
297
|
+
}
|
|
298
|
+
return [...groups.values()].sort((left, right) => {
|
|
299
|
+
return (right.lastCalledAt ? Date.parse(right.lastCalledAt) : -1) - (left.lastCalledAt ? Date.parse(left.lastCalledAt) : -1) || `${left.provider}/${left.model}`.localeCompare(`${right.provider}/${right.model}`);
|
|
300
|
+
}).slice(0, 50).map((entry) => ({
|
|
301
|
+
provider: entry.provider,
|
|
302
|
+
model: entry.model,
|
|
303
|
+
count: entry.count,
|
|
304
|
+
task_sources: sorted(entry.taskSources),
|
|
305
|
+
selection_sources: sorted(entry.selectionSources),
|
|
306
|
+
roles: sorted(entry.roles),
|
|
307
|
+
last_called_at: entry.lastCalledAt
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
73
311
|
//#region src/client/index.tsx
|
|
74
312
|
const inject$1 = ["slots", "locale"];
|
|
75
|
-
const API
|
|
313
|
+
const API = "/_dsh/dsh-crew";
|
|
314
|
+
const CREW_HARNESS_URL = "http://127.0.0.1:3210/";
|
|
315
|
+
const CREW_CONTROL_PLANE_URL = "http://127.0.0.1:3080/";
|
|
316
|
+
const DEFAULT_ADAPTIVE = {
|
|
317
|
+
enabled: false,
|
|
318
|
+
window_size: 8,
|
|
319
|
+
min_samples: 2
|
|
320
|
+
};
|
|
321
|
+
function clampInt(value, fallback, min, max) {
|
|
322
|
+
const parsed = Number(value);
|
|
323
|
+
if (!Number.isInteger(parsed)) return fallback;
|
|
324
|
+
return Math.max(min, Math.min(max, parsed));
|
|
325
|
+
}
|
|
326
|
+
function normalizeAdaptive(value) {
|
|
327
|
+
const windowSize = clampInt(value?.window_size, DEFAULT_ADAPTIVE.window_size, 1, 32);
|
|
328
|
+
return {
|
|
329
|
+
enabled: value?.enabled === true,
|
|
330
|
+
window_size: windowSize,
|
|
331
|
+
min_samples: clampInt(value?.min_samples, DEFAULT_ADAPTIVE.min_samples, 1, windowSize)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
76
334
|
const COPY = {
|
|
77
335
|
zh: {
|
|
78
336
|
label: "DSH Crew",
|
|
79
337
|
title: "DSH Crew",
|
|
80
|
-
intro: "
|
|
338
|
+
intro: "日常 Crew 控制台:在一个入口里配置编排、角色、模型路由与宿主集成,并跟踪真实 Worker / Reviewer 任务。",
|
|
81
339
|
integrations: "集成",
|
|
82
340
|
installed: "已安装",
|
|
83
341
|
notInstalled: "未安装",
|
|
342
|
+
ready: "可调用",
|
|
343
|
+
notReady: "未就绪",
|
|
84
344
|
hud: "HUD 段",
|
|
85
345
|
install: "安装",
|
|
86
346
|
update: "更新",
|
|
@@ -94,15 +354,34 @@ const COPY = {
|
|
|
94
354
|
jobCount: (count) => `${count} 个任务`,
|
|
95
355
|
runningCount: (count) => `${count} 个运行中`,
|
|
96
356
|
sectionNames: {
|
|
97
|
-
integrations: "
|
|
98
|
-
workflow: "
|
|
99
|
-
flash: "Flash",
|
|
100
|
-
pro: "Pro",
|
|
101
|
-
dispatch: "
|
|
102
|
-
|
|
357
|
+
integrations: "Codex / Claude 集成状态",
|
|
358
|
+
workflow: "Crew 工作流设置",
|
|
359
|
+
flash: "Worker / Flash",
|
|
360
|
+
pro: "Reviewer / Pro",
|
|
361
|
+
dispatch: "模型优先级与派发",
|
|
362
|
+
adaptive: "自适应路由",
|
|
363
|
+
runtime: "运行 / 生效边界",
|
|
103
364
|
multimodal: "视觉与生图",
|
|
104
365
|
providers: "自定义 Provider",
|
|
105
|
-
jobs: "
|
|
366
|
+
jobs: "任务状态"
|
|
367
|
+
},
|
|
368
|
+
openHarness: "打开 3210 Crew Harness →",
|
|
369
|
+
harnessHint: "底层 Provider、Harness Models 与运行时配置",
|
|
370
|
+
hostReadiness: "宿主集成就绪度",
|
|
371
|
+
hostReadinessHint: "只使用结构化安装与运行时证据;缺少证据不会显示 READY。",
|
|
372
|
+
readinessLabels: {
|
|
373
|
+
codex_mcp: "Codex MCP",
|
|
374
|
+
ds_worker: "ds-worker",
|
|
375
|
+
ds_reviewer: "ds-reviewer",
|
|
376
|
+
claude_plugin: "Claude plugin",
|
|
377
|
+
crew_harness: "Crew Harness",
|
|
378
|
+
official_bridge: "Official bridge"
|
|
379
|
+
},
|
|
380
|
+
readinessStates: {
|
|
381
|
+
READY: "READY",
|
|
382
|
+
DEGRADED: "DEGRADED",
|
|
383
|
+
UNAVAILABLE: "UNAVAILABLE",
|
|
384
|
+
UNKNOWN: "UNKNOWN"
|
|
106
385
|
},
|
|
107
386
|
globalHint: "修改即时保存到 ~/.config/dsh-crew/config.json;CC / Codex 的新会话自动读取为默认值(会话内可用 /dsh-crew:config 临时覆盖)。",
|
|
108
387
|
orchestration: "Agent 编排",
|
|
@@ -284,10 +563,28 @@ const COPY = {
|
|
|
284
563
|
save: "保存",
|
|
285
564
|
saved: "已保存",
|
|
286
565
|
jobs: "Worker 任务",
|
|
287
|
-
empty: "当前没有
|
|
566
|
+
empty: "当前没有 Worker / Reviewer 任务。",
|
|
567
|
+
adaptiveTitle: "自适应模型路由(实验)",
|
|
568
|
+
adaptiveHint: "默认关闭;只重排系统自动候选,手动模型优先级始终保持原序。健康信号仅来自本进程已观察到的成功、失败、超时与粗粒度延迟,重启后清空。",
|
|
569
|
+
adaptiveEnabled: "启用自适应路由",
|
|
570
|
+
adaptiveWindow: "健康窗口",
|
|
571
|
+
adaptiveSamples: "最少样本",
|
|
572
|
+
adaptiveBoundary: "下一个工作流生效",
|
|
573
|
+
modelActivity: "模型调用概览",
|
|
574
|
+
modelActivityHint: "聚合本 Hub 内存中最近 500 个任务的真实调用证据,最多显示 50 个模型;不保存提示词、结果或凭据。",
|
|
575
|
+
noModelActivity: "还没有真实模型调用。",
|
|
576
|
+
calls: "调用次数",
|
|
577
|
+
invocationSource: "任务来源",
|
|
578
|
+
routingSource: "路由来源",
|
|
579
|
+
lastCalled: "最近调用",
|
|
580
|
+
role: "角色",
|
|
581
|
+
model: "模型",
|
|
582
|
+
never: "—",
|
|
288
583
|
col: {
|
|
289
584
|
id: "任务",
|
|
585
|
+
role: "角色",
|
|
290
586
|
source: "来源",
|
|
587
|
+
model: "模型",
|
|
291
588
|
tier: "档位",
|
|
292
589
|
status: "状态",
|
|
293
590
|
progress: "进度",
|
|
@@ -311,10 +608,12 @@ const COPY = {
|
|
|
311
608
|
en: {
|
|
312
609
|
label: "DSH Crew",
|
|
313
610
|
title: "DSH Crew",
|
|
314
|
-
intro: "
|
|
611
|
+
intro: "The daily Crew console: configure orchestration, roles, model routing, and host integrations in one place while tracking real Worker / Reviewer jobs.",
|
|
315
612
|
integrations: "Integrations",
|
|
316
613
|
installed: "installed",
|
|
317
614
|
notInstalled: "not installed",
|
|
615
|
+
ready: "ready",
|
|
616
|
+
notReady: "not ready",
|
|
318
617
|
hud: "HUD segment",
|
|
319
618
|
install: "Install",
|
|
320
619
|
update: "Update",
|
|
@@ -328,15 +627,34 @@ const COPY = {
|
|
|
328
627
|
jobCount: (count) => `${count} jobs`,
|
|
329
628
|
runningCount: (count) => `${count} running`,
|
|
330
629
|
sectionNames: {
|
|
331
|
-
integrations: "
|
|
332
|
-
workflow: "
|
|
333
|
-
flash: "Flash",
|
|
334
|
-
pro: "Pro",
|
|
335
|
-
dispatch: "
|
|
336
|
-
|
|
630
|
+
integrations: "Codex / Claude integration status",
|
|
631
|
+
workflow: "Crew workflow settings",
|
|
632
|
+
flash: "Worker / Flash",
|
|
633
|
+
pro: "Reviewer / Pro",
|
|
634
|
+
dispatch: "Model priority & dispatch",
|
|
635
|
+
adaptive: "Adaptive routing",
|
|
636
|
+
runtime: "Runtime / activation boundaries",
|
|
337
637
|
multimodal: "Vision & image generation",
|
|
338
638
|
providers: "Custom providers",
|
|
339
|
-
jobs: "
|
|
639
|
+
jobs: "Task status"
|
|
640
|
+
},
|
|
641
|
+
openHarness: "Open 3210 Crew Harness →",
|
|
642
|
+
harnessHint: "Low-level providers, Harness Models, and runtime configuration",
|
|
643
|
+
hostReadiness: "Host integration readiness",
|
|
644
|
+
hostReadinessHint: "Uses structured installer and runtime evidence only; missing evidence is never READY.",
|
|
645
|
+
readinessLabels: {
|
|
646
|
+
codex_mcp: "Codex MCP",
|
|
647
|
+
ds_worker: "ds-worker",
|
|
648
|
+
ds_reviewer: "ds-reviewer",
|
|
649
|
+
claude_plugin: "Claude plugin",
|
|
650
|
+
crew_harness: "Crew Harness",
|
|
651
|
+
official_bridge: "Official bridge"
|
|
652
|
+
},
|
|
653
|
+
readinessStates: {
|
|
654
|
+
READY: "READY",
|
|
655
|
+
DEGRADED: "DEGRADED",
|
|
656
|
+
UNAVAILABLE: "UNAVAILABLE",
|
|
657
|
+
UNKNOWN: "UNKNOWN"
|
|
340
658
|
},
|
|
341
659
|
globalHint: "Changes save instantly to ~/.config/dsh-crew/config.json; new CC / Codex sessions pick them up as defaults (override per session with /dsh-crew:config).",
|
|
342
660
|
orchestration: "Agent orchestration",
|
|
@@ -518,10 +836,28 @@ const COPY = {
|
|
|
518
836
|
save: "Save",
|
|
519
837
|
saved: "Saved",
|
|
520
838
|
jobs: "Worker jobs",
|
|
521
|
-
empty: "No
|
|
839
|
+
empty: "No Worker / Reviewer jobs yet.",
|
|
840
|
+
adaptiveTitle: "Adaptive model routing (experimental)",
|
|
841
|
+
adaptiveHint: "Off by default. Only system-derived candidates may be reordered; explicit model priorities always keep their order. Health uses only process-local success, failure, timeout, and coarse latency observations and resets on restart.",
|
|
842
|
+
adaptiveEnabled: "Enable adaptive routing",
|
|
843
|
+
adaptiveWindow: "Health window",
|
|
844
|
+
adaptiveSamples: "Minimum samples",
|
|
845
|
+
adaptiveBoundary: "Effective for the next workflow",
|
|
846
|
+
modelActivity: "Model invocation overview",
|
|
847
|
+
modelActivityHint: "Aggregates real invocation evidence from the latest 500 in-memory Hub jobs and shows at most 50 models; prompts, results, and credentials are never stored here.",
|
|
848
|
+
noModelActivity: "No real model invocations yet.",
|
|
849
|
+
calls: "Calls",
|
|
850
|
+
invocationSource: "Task source",
|
|
851
|
+
routingSource: "Routing source",
|
|
852
|
+
lastCalled: "Last called",
|
|
853
|
+
role: "Role",
|
|
854
|
+
model: "Model",
|
|
855
|
+
never: "—",
|
|
522
856
|
col: {
|
|
523
857
|
id: "job",
|
|
858
|
+
role: "role",
|
|
524
859
|
source: "source",
|
|
860
|
+
model: "model",
|
|
525
861
|
tier: "tier",
|
|
526
862
|
status: "status",
|
|
527
863
|
progress: "progress",
|
|
@@ -873,6 +1209,95 @@ function elapsed(startedAt, endedAt) {
|
|
|
873
1209
|
function ktok(n) {
|
|
874
1210
|
return n >= 1e3 ? `${Math.round(n / 100) / 10}k` : `${n}`;
|
|
875
1211
|
}
|
|
1212
|
+
function formatTimestamp(value, locale) {
|
|
1213
|
+
if (!value || !Number.isFinite(Date.parse(value))) return "—";
|
|
1214
|
+
return new Intl.DateTimeFormat(locale === "zh" ? "zh-CN" : "en", {
|
|
1215
|
+
month: "2-digit",
|
|
1216
|
+
day: "2-digit",
|
|
1217
|
+
hour: "2-digit",
|
|
1218
|
+
minute: "2-digit",
|
|
1219
|
+
second: "2-digit"
|
|
1220
|
+
}).format(new Date(value));
|
|
1221
|
+
}
|
|
1222
|
+
function readinessChip(state) {
|
|
1223
|
+
const color = state === READINESS_STATES.READY ? "#3fb950" : state === READINESS_STATES.DEGRADED ? "#c98735" : state === READINESS_STATES.UNAVAILABLE ? "#f85149" : "inherit";
|
|
1224
|
+
return {
|
|
1225
|
+
fontSize: 10.5,
|
|
1226
|
+
fontWeight: 650,
|
|
1227
|
+
padding: "1px 7px",
|
|
1228
|
+
borderRadius: 99,
|
|
1229
|
+
border: `1px solid ${state === READINESS_STATES.UNKNOWN ? "rgba(128,128,128,0.35)" : color}`,
|
|
1230
|
+
color,
|
|
1231
|
+
opacity: state === READINESS_STATES.UNKNOWN ? .58 : 1
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function MinimalCrewPanel({ locale, surface, runtime }) {
|
|
1235
|
+
const zh = locale === "zh";
|
|
1236
|
+
const native = surface === CREW_UI_SURFACES.NATIVE;
|
|
1237
|
+
const title = native ? zh ? "DSH Crew Runtime" : "DSH Crew Runtime" : zh ? "DSH Crew Surface 未确认" : "DSH Crew surface not verified";
|
|
1238
|
+
const hint = native ? zh ? "这里是隔离的 3210 Crew Harness。请使用 Harness 原生菜单管理 Provider、Harness Models、Agent 预设与底层配置;Crew 编排和宿主集成统一在 3080 管理。" : "This is the isolated 3210 Crew Harness. Use the native Harness menus for Providers, Harness Models, Agent presets, and low-level settings; manage Crew orchestration and host integrations on 3080." : zh ? "无法用结构化后端证据确认当前界面,因此已保守隐藏 Crew 编排、任务与宿主集成功能。" : "Structured backend evidence could not identify this surface, so Crew orchestration, jobs, and host integrations are conservatively hidden.";
|
|
1239
|
+
const runtimeReady = runtime?.ok === true && runtime?.service === "dsh-crew-hub";
|
|
1240
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1241
|
+
style: {
|
|
1242
|
+
display: "flex",
|
|
1243
|
+
flexDirection: "column",
|
|
1244
|
+
gap: 10,
|
|
1245
|
+
fontSize: 13,
|
|
1246
|
+
lineHeight: 1.55
|
|
1247
|
+
},
|
|
1248
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1249
|
+
style: {
|
|
1250
|
+
...S.block,
|
|
1251
|
+
background: "linear-gradient(135deg, rgba(74,158,255,0.08), rgba(128,128,128,0.02))"
|
|
1252
|
+
},
|
|
1253
|
+
children: [
|
|
1254
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1255
|
+
style: {
|
|
1256
|
+
fontSize: 19,
|
|
1257
|
+
fontWeight: 680
|
|
1258
|
+
},
|
|
1259
|
+
children: title
|
|
1260
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1261
|
+
style: {
|
|
1262
|
+
fontSize: 12,
|
|
1263
|
+
opacity: .68,
|
|
1264
|
+
marginTop: 3
|
|
1265
|
+
},
|
|
1266
|
+
children: hint
|
|
1267
|
+
})] }),
|
|
1268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1269
|
+
style: {
|
|
1270
|
+
display: "flex",
|
|
1271
|
+
gap: 7,
|
|
1272
|
+
flexWrap: "wrap"
|
|
1273
|
+
},
|
|
1274
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1275
|
+
style: readinessChip(runtimeReady ? READINESS_STATES.READY : READINESS_STATES.UNKNOWN),
|
|
1276
|
+
children: runtimeReady ? `Runtime ${runtime.runtime_version ?? "READY"}` : "Runtime UNKNOWN"
|
|
1277
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1278
|
+
style: readinessChip(native ? READINESS_STATES.READY : READINESS_STATES.UNKNOWN),
|
|
1279
|
+
children: native ? "3210 · isolated" : "surface · unknown"
|
|
1280
|
+
})]
|
|
1281
|
+
}),
|
|
1282
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1283
|
+
href: CREW_CONTROL_PLANE_URL,
|
|
1284
|
+
target: "_blank",
|
|
1285
|
+
rel: "noopener noreferrer",
|
|
1286
|
+
style: {
|
|
1287
|
+
...S.btn,
|
|
1288
|
+
alignSelf: "flex-start",
|
|
1289
|
+
textDecoration: "none",
|
|
1290
|
+
fontWeight: 650,
|
|
1291
|
+
padding: "7px 12px",
|
|
1292
|
+
borderColor: "rgba(74,158,255,0.55)",
|
|
1293
|
+
background: "rgba(74,158,255,0.08)"
|
|
1294
|
+
},
|
|
1295
|
+
children: zh ? "打开 3080 DSH Crew 控制台 →" : "Open the 3080 DSH Crew console →"
|
|
1296
|
+
})
|
|
1297
|
+
]
|
|
1298
|
+
})
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
876
1301
|
function WorkersPanel({ ctx }) {
|
|
877
1302
|
const locale = (0, react.useSyncExternalStore)((notify) => ctx.on("locale/change", notify), () => ctx.locale.getLocale().active, () => ctx.locale.getLocale().active);
|
|
878
1303
|
const copy = COPY[locale === "zh" ? "zh" : "en"];
|
|
@@ -893,6 +1318,8 @@ function WorkersPanel({ ctx }) {
|
|
|
893
1318
|
const [modelQuery, setModelQuery] = (0, react.useState)("");
|
|
894
1319
|
const [testResult, setTestResult] = (0, react.useState)(null);
|
|
895
1320
|
const [expandedSections, setExpandedSections] = (0, react.useState)(() => readSectionState(sectionStorage()));
|
|
1321
|
+
const [surface, setSurface] = (0, react.useState)("detecting");
|
|
1322
|
+
const [runtimeInfo, setRuntimeInfo] = (0, react.useState)(void 0);
|
|
896
1323
|
const toggleSection = (sectionId) => {
|
|
897
1324
|
setExpandedSections((current) => ({
|
|
898
1325
|
...current,
|
|
@@ -933,7 +1360,7 @@ function WorkersPanel({ ctx }) {
|
|
|
933
1360
|
}, [copy]);
|
|
934
1361
|
/** Every request carries the active locale: the panel is the authority on it
|
|
935
1362
|
* (DSH's setting may be unset), and the hub localizes its own strings from it. */
|
|
936
|
-
const withLang = (0, react.useCallback)((path) => `${API
|
|
1363
|
+
const withLang = (0, react.useCallback)((path) => `${API}${path}${path.includes("?") ? "&" : "?"}lang=${locale === "zh" ? "zh" : "en"}`, [locale]);
|
|
937
1364
|
const get = (0, react.useCallback)(async (path) => readJson(await fetch(withLang(path), { cache: "no-store" }), path), [readJson, withLang]);
|
|
938
1365
|
const post = (0, react.useCallback)(async (path, body) => readJson(await fetch(withLang(path), {
|
|
939
1366
|
method: "POST",
|
|
@@ -947,6 +1374,27 @@ function WorkersPanel({ ctx }) {
|
|
|
947
1374
|
withLang,
|
|
948
1375
|
locale
|
|
949
1376
|
]);
|
|
1377
|
+
const detectSurface = (0, react.useCallback)(async () => {
|
|
1378
|
+
const readOptional = async (path) => {
|
|
1379
|
+
try {
|
|
1380
|
+
const response = await fetch(`${API}${path}`, { cache: "no-store" });
|
|
1381
|
+
if (!response.ok) return null;
|
|
1382
|
+
const body = await response.json();
|
|
1383
|
+
return body && typeof body === "object" ? body : null;
|
|
1384
|
+
} catch {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
const [bridgeStatus, runtime] = await Promise.all([readOptional("/bridge-status"), readOptional("/runtime")]);
|
|
1389
|
+
setRuntimeInfo(runtime);
|
|
1390
|
+
setSurface(classifyCrewSurface({
|
|
1391
|
+
bridgeStatus,
|
|
1392
|
+
runtime
|
|
1393
|
+
}));
|
|
1394
|
+
}, []);
|
|
1395
|
+
(0, react.useEffect)(() => {
|
|
1396
|
+
detectSurface();
|
|
1397
|
+
}, [detectSurface]);
|
|
950
1398
|
const refreshAll = (0, react.useCallback)(async () => {
|
|
951
1399
|
try {
|
|
952
1400
|
const [j, s, c, pr] = await Promise.all([
|
|
@@ -968,6 +1416,7 @@ function WorkersPanel({ ctx }) {
|
|
|
968
1416
|
} catch {}
|
|
969
1417
|
}, [get]);
|
|
970
1418
|
(0, react.useEffect)(() => {
|
|
1419
|
+
if (surface !== CREW_UI_SURFACES.OFFICIAL) return void 0;
|
|
971
1420
|
refreshAll();
|
|
972
1421
|
const timer = setInterval(() => {
|
|
973
1422
|
get("/jobs").then((j) => {
|
|
@@ -975,7 +1424,11 @@ function WorkersPanel({ ctx }) {
|
|
|
975
1424
|
}).catch(() => {});
|
|
976
1425
|
}, 3e3);
|
|
977
1426
|
return () => clearInterval(timer);
|
|
978
|
-
}, [
|
|
1427
|
+
}, [
|
|
1428
|
+
surface,
|
|
1429
|
+
refreshAll,
|
|
1430
|
+
get
|
|
1431
|
+
]);
|
|
979
1432
|
const refreshHarnessModels = (0, react.useCallback)(async () => {
|
|
980
1433
|
setModelCatalogBusy(true);
|
|
981
1434
|
setModelCatalogError("");
|
|
@@ -990,8 +1443,8 @@ function WorkersPanel({ ctx }) {
|
|
|
990
1443
|
}
|
|
991
1444
|
}, [get, copy]);
|
|
992
1445
|
(0, react.useEffect)(() => {
|
|
993
|
-
refreshHarnessModels();
|
|
994
|
-
}, [refreshHarnessModels]);
|
|
1446
|
+
if (surface === CREW_UI_SURFACES.OFFICIAL) refreshHarnessModels();
|
|
1447
|
+
}, [surface, refreshHarnessModels]);
|
|
995
1448
|
const act = (0, react.useCallback)(async (target, confirmName) => {
|
|
996
1449
|
if (confirmName && !window.confirm(copy.confirmRestore(confirmName))) return;
|
|
997
1450
|
setBusy(true);
|
|
@@ -1012,15 +1465,27 @@ function WorkersPanel({ ctx }) {
|
|
|
1012
1465
|
get,
|
|
1013
1466
|
copy
|
|
1014
1467
|
]);
|
|
1015
|
-
const applyPatch = (0, react.useCallback)((patch) => {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1468
|
+
const applyPatch = (0, react.useCallback)(async (patch) => {
|
|
1469
|
+
try {
|
|
1470
|
+
const result = await post("/config", patch);
|
|
1471
|
+
if (!result.ok) throw new Error(result.error ?? "Configuration save failed");
|
|
1472
|
+
setConfig(result.config);
|
|
1473
|
+
setNotice(copy.saved);
|
|
1474
|
+
setTimeout(() => setNotice(""), 1500);
|
|
1475
|
+
return true;
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
setNotice(String(error?.message ?? error));
|
|
1478
|
+
try {
|
|
1479
|
+
const authoritative = await get("/config");
|
|
1480
|
+
if (authoritative.ok) setConfig(authoritative.config);
|
|
1481
|
+
} catch {}
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
}, [
|
|
1485
|
+
post,
|
|
1486
|
+
get,
|
|
1487
|
+
copy
|
|
1488
|
+
]);
|
|
1024
1489
|
/** Selects & checkboxes: apply immediately. */
|
|
1025
1490
|
const field = (key, value) => {
|
|
1026
1491
|
setConfig((c) => ({
|
|
@@ -1272,7 +1737,7 @@ function WorkersPanel({ ctx }) {
|
|
|
1272
1737
|
children: fields
|
|
1273
1738
|
}) : fields]
|
|
1274
1739
|
});
|
|
1275
|
-
const integrationRow = (name, installed, extra, installTarget, uninstallTarget, tips) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1740
|
+
const integrationRow = (name, installed, ready, extra, installTarget, uninstallTarget, tips) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1276
1741
|
style: S.card,
|
|
1277
1742
|
children: [
|
|
1278
1743
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1287,6 +1752,10 @@ function WorkersPanel({ ctx }) {
|
|
|
1287
1752
|
style: S.chip(installed),
|
|
1288
1753
|
children: installed ? `● ${copy.installed}` : `○ ${copy.notInstalled}`
|
|
1289
1754
|
}),
|
|
1755
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1756
|
+
style: S.chip(ready),
|
|
1757
|
+
children: ready ? `● ${copy.ready}` : `○ ${copy.notReady}`
|
|
1758
|
+
}),
|
|
1290
1759
|
extra,
|
|
1291
1760
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
|
|
1292
1761
|
!installed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -1321,6 +1790,37 @@ function WorkersPanel({ ctx }) {
|
|
|
1321
1790
|
})
|
|
1322
1791
|
]
|
|
1323
1792
|
});
|
|
1793
|
+
const adaptive = normalizeAdaptive(config?.worker?.model_policy?.adaptive);
|
|
1794
|
+
const setAdaptiveLocal = (candidate) => {
|
|
1795
|
+
const next = normalizeAdaptive(candidate);
|
|
1796
|
+
setConfig((current) => ({
|
|
1797
|
+
...current,
|
|
1798
|
+
worker: {
|
|
1799
|
+
...current.worker,
|
|
1800
|
+
model_policy: {
|
|
1801
|
+
...current.worker?.model_policy,
|
|
1802
|
+
adaptive: next
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}));
|
|
1806
|
+
return next;
|
|
1807
|
+
};
|
|
1808
|
+
const saveAdaptive = (candidate) => {
|
|
1809
|
+
const next = setAdaptiveLocal(candidate);
|
|
1810
|
+
applyPatch({ worker: { model_policy: { adaptive: next } } });
|
|
1811
|
+
};
|
|
1812
|
+
const modelActivity = aggregateModelInvocations(jobs);
|
|
1813
|
+
const currentSurfaceResponsibilities = surfaceResponsibilities(surface);
|
|
1814
|
+
const hostReadiness = projectHostReadiness({
|
|
1815
|
+
installStatus: status,
|
|
1816
|
+
runtime: runtimeInfo,
|
|
1817
|
+
surface
|
|
1818
|
+
});
|
|
1819
|
+
if (!currentSurfaceResponsibilities.fullControlPlane) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MinimalCrewPanel, {
|
|
1820
|
+
locale,
|
|
1821
|
+
surface,
|
|
1822
|
+
runtime: runtimeInfo
|
|
1823
|
+
});
|
|
1324
1824
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1325
1825
|
style: {
|
|
1326
1826
|
display: "flex",
|
|
@@ -1330,17 +1830,90 @@ function WorkersPanel({ ctx }) {
|
|
|
1330
1830
|
lineHeight: 1.55
|
|
1331
1831
|
},
|
|
1332
1832
|
children: [
|
|
1333
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1833
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1334
1834
|
style: {
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1835
|
+
border: "1px solid rgba(128,128,128,0.24)",
|
|
1836
|
+
borderRadius: 12,
|
|
1837
|
+
padding: "14px 15px",
|
|
1838
|
+
display: "flex",
|
|
1839
|
+
gap: 14,
|
|
1840
|
+
alignItems: "center",
|
|
1841
|
+
flexWrap: "wrap",
|
|
1842
|
+
background: "linear-gradient(135deg, rgba(74,158,255,0.10), rgba(128,128,128,0.025) 56%)"
|
|
1338
1843
|
},
|
|
1339
|
-
children:
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1844
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1845
|
+
style: {
|
|
1846
|
+
minWidth: 240,
|
|
1847
|
+
flex: 1
|
|
1848
|
+
},
|
|
1849
|
+
children: [
|
|
1850
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1851
|
+
style: {
|
|
1852
|
+
fontSize: 20,
|
|
1853
|
+
fontWeight: 680,
|
|
1854
|
+
letterSpacing: "-0.01em"
|
|
1855
|
+
},
|
|
1856
|
+
children: copy.title
|
|
1857
|
+
}),
|
|
1858
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1859
|
+
style: {
|
|
1860
|
+
opacity: .7,
|
|
1861
|
+
fontSize: 12.5,
|
|
1862
|
+
marginTop: 2
|
|
1863
|
+
},
|
|
1864
|
+
children: copy.intro
|
|
1865
|
+
}),
|
|
1866
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1867
|
+
style: {
|
|
1868
|
+
display: "flex",
|
|
1869
|
+
gap: 6,
|
|
1870
|
+
marginTop: 8,
|
|
1871
|
+
flexWrap: "wrap"
|
|
1872
|
+
},
|
|
1873
|
+
children: [
|
|
1874
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1875
|
+
style: S.chip(!!status?.codex?.ready),
|
|
1876
|
+
children: ["Codex ", status?.codex?.ready ? "READY" : "CHECK"]
|
|
1877
|
+
}),
|
|
1878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1879
|
+
style: S.chip(!!status?.claude?.ready),
|
|
1880
|
+
children: ["Claude ", status?.claude?.ready ? "READY" : "CHECK"]
|
|
1881
|
+
}),
|
|
1882
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1883
|
+
style: S.chip(jobs.some((job) => job.status === "running")),
|
|
1884
|
+
children: [jobs.filter((job) => job.status === "running").length, " running"]
|
|
1885
|
+
})
|
|
1886
|
+
]
|
|
1887
|
+
})
|
|
1888
|
+
]
|
|
1889
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1890
|
+
style: {
|
|
1891
|
+
display: "flex",
|
|
1892
|
+
flexDirection: "column",
|
|
1893
|
+
alignItems: "flex-end",
|
|
1894
|
+
gap: 3
|
|
1895
|
+
},
|
|
1896
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1897
|
+
href: CREW_HARNESS_URL,
|
|
1898
|
+
target: "_blank",
|
|
1899
|
+
rel: "noopener noreferrer",
|
|
1900
|
+
style: {
|
|
1901
|
+
...S.btn,
|
|
1902
|
+
textDecoration: "none",
|
|
1903
|
+
fontWeight: 650,
|
|
1904
|
+
padding: "7px 12px",
|
|
1905
|
+
borderColor: "rgba(74,158,255,0.55)",
|
|
1906
|
+
background: "rgba(74,158,255,0.10)"
|
|
1907
|
+
},
|
|
1908
|
+
children: copy.openHarness
|
|
1909
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1910
|
+
style: {
|
|
1911
|
+
fontSize: 10.5,
|
|
1912
|
+
opacity: .52
|
|
1913
|
+
},
|
|
1914
|
+
children: copy.harnessHint
|
|
1915
|
+
})]
|
|
1916
|
+
})]
|
|
1344
1917
|
}),
|
|
1345
1918
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1346
1919
|
style: {
|
|
@@ -1381,7 +1954,7 @@ function WorkersPanel({ ctx }) {
|
|
|
1381
1954
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
1382
1955
|
sectionId: "integrations",
|
|
1383
1956
|
title: copy.sectionNames.integrations,
|
|
1384
|
-
summary: sectionSummary(status?.claude?.
|
|
1957
|
+
summary: sectionSummary(status?.claude?.ready ? "Claude READY" : "Claude CHECK", status?.codex?.ready ? "Codex READY" : "Codex CHECK"),
|
|
1385
1958
|
expanded: !!expandedSections.integrations,
|
|
1386
1959
|
onToggle: () => toggleSection("integrations"),
|
|
1387
1960
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1390,17 +1963,75 @@ function WorkersPanel({ ctx }) {
|
|
|
1390
1963
|
flexDirection: "column",
|
|
1391
1964
|
gap: 8
|
|
1392
1965
|
},
|
|
1393
|
-
children: [
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1966
|
+
children: [
|
|
1967
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1968
|
+
style: S.block,
|
|
1969
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1970
|
+
style: S.settingTitle,
|
|
1971
|
+
children: copy.hostReadiness
|
|
1972
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1973
|
+
style: S.settingDesc,
|
|
1974
|
+
children: copy.hostReadinessHint
|
|
1975
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1976
|
+
style: {
|
|
1977
|
+
display: "grid",
|
|
1978
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
1979
|
+
gap: "6px 10px"
|
|
1980
|
+
},
|
|
1981
|
+
children: hostReadiness.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1982
|
+
style: {
|
|
1983
|
+
display: "flex",
|
|
1984
|
+
gap: 8,
|
|
1985
|
+
alignItems: "center",
|
|
1986
|
+
minWidth: 0,
|
|
1987
|
+
padding: "4px 7px",
|
|
1988
|
+
border: "1px solid rgba(128,128,128,0.16)",
|
|
1989
|
+
borderRadius: 7
|
|
1990
|
+
},
|
|
1991
|
+
children: [
|
|
1992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1993
|
+
style: {
|
|
1994
|
+
flex: 1,
|
|
1995
|
+
minWidth: 0,
|
|
1996
|
+
fontSize: 11.5
|
|
1997
|
+
},
|
|
1998
|
+
children: copy.readinessLabels[row.id]
|
|
1999
|
+
}),
|
|
2000
|
+
row.detail && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2001
|
+
style: {
|
|
2002
|
+
...S.mono,
|
|
2003
|
+
opacity: .55
|
|
2004
|
+
},
|
|
2005
|
+
children: row.detail
|
|
2006
|
+
}),
|
|
2007
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2008
|
+
style: readinessChip(row.state),
|
|
2009
|
+
children: copy.readinessStates[row.state]
|
|
2010
|
+
})
|
|
2011
|
+
]
|
|
2012
|
+
}, row.id))
|
|
2013
|
+
})]
|
|
2014
|
+
}),
|
|
2015
|
+
integrationRow("Claude Code", !!status?.claude?.installed, !!status?.claude?.ready, status?.claude?.installed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2016
|
+
style: S.chip(!!status?.claude?.hud),
|
|
2017
|
+
children: copy.hud
|
|
2018
|
+
}) : null, "claude", "claude-uninstall", copy.tips.claude),
|
|
2019
|
+
integrationRow("Codex", !!status?.codex?.installed, !!status?.codex?.ready, status?.codex?.ready ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2020
|
+
title: (status?.codex?.missing ?? []).join(", "),
|
|
2021
|
+
style: {
|
|
2022
|
+
fontSize: 11,
|
|
2023
|
+
opacity: .6
|
|
2024
|
+
},
|
|
2025
|
+
children: (status?.codex?.missing ?? []).join(" · ")
|
|
2026
|
+
}), "codex", "codex-uninstall", copy.tips.codex)
|
|
2027
|
+
]
|
|
2028
|
+
})
|
|
2029
|
+
}),
|
|
2030
|
+
config && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2031
|
+
(() => {
|
|
2032
|
+
const clampNum = (v) => {
|
|
2033
|
+
const n = Number(v);
|
|
2034
|
+
if (Number.isNaN(n)) return 3;
|
|
1404
2035
|
return Math.max(1, Math.min(16, n));
|
|
1405
2036
|
};
|
|
1406
2037
|
const mode = config.collaboration_mode ?? "flash-only";
|
|
@@ -2188,12 +2819,90 @@ function WorkersPanel({ ctx }) {
|
|
|
2188
2819
|
] }))
|
|
2189
2820
|
}),
|
|
2190
2821
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
2822
|
+
sectionId: "adaptive",
|
|
2823
|
+
title: copy.sectionNames.adaptive,
|
|
2824
|
+
summary: sectionSummary(adaptive.enabled ? copy.adaptiveEnabled : "off", `${adaptive.min_samples}/${adaptive.window_size}`, copy.adaptiveBoundary),
|
|
2825
|
+
expanded: !!expandedSections.adaptive,
|
|
2826
|
+
onToggle: () => toggleSection("adaptive"),
|
|
2827
|
+
children: block({
|
|
2828
|
+
t: copy.adaptiveTitle,
|
|
2829
|
+
d: copy.adaptiveHint
|
|
2830
|
+
}, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2832
|
+
style: {
|
|
2833
|
+
...S.field,
|
|
2834
|
+
flexDirection: "row",
|
|
2835
|
+
alignItems: "center",
|
|
2836
|
+
gap: 7,
|
|
2837
|
+
gridColumn: "1 / -1"
|
|
2838
|
+
},
|
|
2839
|
+
children: [
|
|
2840
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2841
|
+
type: "checkbox",
|
|
2842
|
+
checked: adaptive.enabled,
|
|
2843
|
+
onChange: (event) => saveAdaptive({
|
|
2844
|
+
...adaptive,
|
|
2845
|
+
enabled: event.target.checked
|
|
2846
|
+
})
|
|
2847
|
+
}),
|
|
2848
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2849
|
+
style: { fontSize: 12.5 },
|
|
2850
|
+
children: copy.adaptiveEnabled
|
|
2851
|
+
}),
|
|
2852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2853
|
+
style: {
|
|
2854
|
+
fontSize: 11,
|
|
2855
|
+
opacity: .55
|
|
2856
|
+
},
|
|
2857
|
+
children: ["· ", copy.adaptiveBoundary]
|
|
2858
|
+
})
|
|
2859
|
+
]
|
|
2860
|
+
}),
|
|
2861
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2862
|
+
style: S.field,
|
|
2863
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2864
|
+
style: S.fieldLabel,
|
|
2865
|
+
children: copy.adaptiveWindow
|
|
2866
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2867
|
+
type: "number",
|
|
2868
|
+
min: 1,
|
|
2869
|
+
max: 32,
|
|
2870
|
+
value: adaptive.window_size,
|
|
2871
|
+
style: S.input,
|
|
2872
|
+
onChange: (event) => setAdaptiveLocal({
|
|
2873
|
+
...adaptive,
|
|
2874
|
+
window_size: clampInt(event.target.value, adaptive.window_size, 1, 32)
|
|
2875
|
+
}),
|
|
2876
|
+
onBlur: () => saveAdaptive(adaptive)
|
|
2877
|
+
})]
|
|
2878
|
+
}),
|
|
2879
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2880
|
+
style: S.field,
|
|
2881
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2882
|
+
style: S.fieldLabel,
|
|
2883
|
+
children: copy.adaptiveSamples
|
|
2884
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2885
|
+
type: "number",
|
|
2886
|
+
min: 1,
|
|
2887
|
+
max: adaptive.window_size,
|
|
2888
|
+
value: adaptive.min_samples,
|
|
2889
|
+
style: S.input,
|
|
2890
|
+
onChange: (event) => setAdaptiveLocal({
|
|
2891
|
+
...adaptive,
|
|
2892
|
+
min_samples: clampInt(event.target.value, adaptive.min_samples, 1, adaptive.window_size)
|
|
2893
|
+
}),
|
|
2894
|
+
onBlur: () => saveAdaptive(adaptive)
|
|
2895
|
+
})]
|
|
2896
|
+
})
|
|
2897
|
+
] }))
|
|
2898
|
+
}),
|
|
2899
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
2191
2900
|
sectionId: "runtime",
|
|
2192
2901
|
title: copy.sectionNames.runtime,
|
|
2193
2902
|
summary: sectionSummary(config.mode, `${config.default_timeout_seconds}s`, config.hub_url),
|
|
2194
2903
|
expanded: !!expandedSections.runtime,
|
|
2195
2904
|
onToggle: () => toggleSection("runtime"),
|
|
2196
|
-
children: block(copy.cardRuntime, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2905
|
+
children: [block(copy.cardRuntime, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2197
2906
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2198
2907
|
style: S.field,
|
|
2199
2908
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2228,7 +2937,9 @@ function WorkersPanel({ ctx }) {
|
|
|
2228
2937
|
max: 7200,
|
|
2229
2938
|
value: config.default_timeout_seconds,
|
|
2230
2939
|
onChange: (e) => fieldLocal("default_timeout_seconds", Number(e.target.value)),
|
|
2231
|
-
onBlur: () =>
|
|
2940
|
+
onBlur: () => {
|
|
2941
|
+
applyPatch({ default_timeout_seconds: config.default_timeout_seconds });
|
|
2942
|
+
}
|
|
2232
2943
|
})]
|
|
2233
2944
|
}),
|
|
2234
2945
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -2244,13 +2955,18 @@ function WorkersPanel({ ctx }) {
|
|
|
2244
2955
|
},
|
|
2245
2956
|
value: config.hub_url,
|
|
2246
2957
|
onChange: (e) => fieldLocal("hub_url", e.target.value),
|
|
2247
|
-
onBlur: () =>
|
|
2958
|
+
onBlur: () => {
|
|
2959
|
+
applyPatch({ hub_url: config.hub_url });
|
|
2960
|
+
},
|
|
2248
2961
|
onKeyDown: (e) => {
|
|
2249
2962
|
if (e.key === "Enter") e.currentTarget.blur();
|
|
2250
2963
|
}
|
|
2251
2964
|
})]
|
|
2252
2965
|
})
|
|
2253
|
-
] }))
|
|
2966
|
+
] })), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivationSummary, {
|
|
2967
|
+
activation: config.config_activation,
|
|
2968
|
+
locale
|
|
2969
|
+
})]
|
|
2254
2970
|
}),
|
|
2255
2971
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, {
|
|
2256
2972
|
sectionId: "multimodal",
|
|
@@ -2813,62 +3529,79 @@ function WorkersPanel({ ctx }) {
|
|
|
2813
3529
|
},
|
|
2814
3530
|
children: notice
|
|
2815
3531
|
}),
|
|
2816
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3532
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, {
|
|
2817
3533
|
sectionId: "jobs",
|
|
2818
3534
|
title: copy.sectionNames.jobs,
|
|
2819
3535
|
summary: sectionSummary(copy.jobCount(jobs.length), jobs.some((job) => job.status === "running") && copy.runningCount(jobs.filter((job) => job.status === "running").length)),
|
|
2820
3536
|
expanded: !!expandedSections.jobs,
|
|
2821
3537
|
onToggle: () => toggleSection("jobs"),
|
|
2822
|
-
children: jobs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3538
|
+
children: [jobs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2823
3539
|
style: { opacity: .55 },
|
|
2824
3540
|
children: copy.empty
|
|
2825
3541
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2826
|
-
style: {
|
|
3542
|
+
style: {
|
|
3543
|
+
overflowX: "auto",
|
|
3544
|
+
border: "1px solid rgba(128,128,128,0.20)",
|
|
3545
|
+
borderRadius: 8,
|
|
3546
|
+
padding: "0 9px"
|
|
3547
|
+
},
|
|
2827
3548
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
2828
3549
|
style: {
|
|
2829
3550
|
borderCollapse: "collapse",
|
|
2830
3551
|
width: "100%",
|
|
2831
3552
|
tableLayout: "fixed",
|
|
2832
|
-
minWidth:
|
|
3553
|
+
minWidth: 1020
|
|
2833
3554
|
},
|
|
2834
3555
|
children: [
|
|
2835
3556
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("colgroup", { children: [
|
|
2836
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width:
|
|
2837
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 58 } }),
|
|
3557
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 68 } }),
|
|
2838
3558
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 72 } }),
|
|
2839
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width:
|
|
2840
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width:
|
|
3559
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 58 } }),
|
|
3560
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 190 } }),
|
|
3561
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 70 } }),
|
|
3562
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 70 } }),
|
|
3563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 105 } }),
|
|
2841
3564
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 84 } }),
|
|
2842
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width:
|
|
3565
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("col", { style: { width: 300 } })
|
|
2843
3566
|
] }),
|
|
2844
3567
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: [
|
|
2845
3568
|
copy.col.id,
|
|
3569
|
+
copy.col.role,
|
|
2846
3570
|
copy.col.source,
|
|
3571
|
+
copy.col.model,
|
|
2847
3572
|
copy.col.tier,
|
|
2848
3573
|
copy.col.status,
|
|
2849
3574
|
copy.col.progress,
|
|
2850
3575
|
copy.col.tokens,
|
|
2851
3576
|
copy.col.task
|
|
2852
|
-
].map((
|
|
3577
|
+
].map((heading) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
2853
3578
|
style: {
|
|
2854
3579
|
...S.tight,
|
|
2855
|
-
fontSize:
|
|
3580
|
+
fontSize: 10.5,
|
|
2856
3581
|
opacity: .55,
|
|
2857
3582
|
fontWeight: 500
|
|
2858
3583
|
},
|
|
2859
|
-
children:
|
|
2860
|
-
},
|
|
3584
|
+
children: heading
|
|
3585
|
+
}, heading)) }) }),
|
|
2861
3586
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: jobs.map((job) => {
|
|
2862
3587
|
const color = job.status === "running" ? "#4a9eff" : job.status === "done" ? "#3fb950" : "#f85149";
|
|
2863
3588
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
2864
3589
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
2865
3590
|
style: {
|
|
2866
3591
|
...S.tight,
|
|
2867
|
-
...S.mono
|
|
3592
|
+
...S.mono,
|
|
3593
|
+
fontWeight: 600
|
|
2868
3594
|
},
|
|
2869
3595
|
title: job.id,
|
|
2870
3596
|
children: String(job.id).replace(/-[a-z0-9]+$/, "")
|
|
2871
3597
|
}),
|
|
3598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
3599
|
+
style: S.tight,
|
|
3600
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3601
|
+
style: S.chip(job.role === "reviewer"),
|
|
3602
|
+
children: job.role === "reviewer" ? "Reviewer" : "Worker"
|
|
3603
|
+
})
|
|
3604
|
+
}),
|
|
2872
3605
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
2873
3606
|
style: S.tight,
|
|
2874
3607
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2876,6 +3609,18 @@ function WorkersPanel({ ctx }) {
|
|
|
2876
3609
|
children: sourceLabel(job.source)
|
|
2877
3610
|
})
|
|
2878
3611
|
}),
|
|
3612
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
3613
|
+
style: {
|
|
3614
|
+
...S.tight,
|
|
3615
|
+
...S.mono
|
|
3616
|
+
},
|
|
3617
|
+
title: `${job.provider ?? "—"} / ${job.model ?? "—"}`,
|
|
3618
|
+
children: [
|
|
3619
|
+
job.provider ?? "—",
|
|
3620
|
+
" / ",
|
|
3621
|
+
job.model ?? "—"
|
|
3622
|
+
]
|
|
3623
|
+
}),
|
|
2879
3624
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
2880
3625
|
style: S.tight,
|
|
2881
3626
|
children: [
|
|
@@ -2884,19 +3629,24 @@ function WorkersPanel({ ctx }) {
|
|
|
2884
3629
|
job.effort
|
|
2885
3630
|
]
|
|
2886
3631
|
}),
|
|
2887
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2888
|
-
style:
|
|
2889
|
-
...S.tight,
|
|
2890
|
-
textAlign: "center"
|
|
2891
|
-
},
|
|
3632
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
3633
|
+
style: S.tight,
|
|
2892
3634
|
title: `${job.status}${job.currentTool ? ` · ${job.currentTool}` : ""}`,
|
|
2893
|
-
children:
|
|
2894
|
-
|
|
2895
|
-
color,
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
3635
|
+
children: [
|
|
3636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3637
|
+
style: { color },
|
|
3638
|
+
"aria-hidden": "true",
|
|
3639
|
+
children: "●"
|
|
3640
|
+
}),
|
|
3641
|
+
" ",
|
|
3642
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3643
|
+
style: {
|
|
3644
|
+
color,
|
|
3645
|
+
fontSize: 10.5
|
|
3646
|
+
},
|
|
3647
|
+
children: job.status
|
|
3648
|
+
})
|
|
3649
|
+
]
|
|
2900
3650
|
}),
|
|
2901
3651
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
2902
3652
|
style: {
|
|
@@ -2906,11 +3656,11 @@ function WorkersPanel({ ctx }) {
|
|
|
2906
3656
|
title: copy.progressTip(elapsed(job.startedAt, job.endedAt), job.turn, job.step, job.toolCalls ?? "-"),
|
|
2907
3657
|
children: [
|
|
2908
3658
|
elapsed(job.startedAt, job.endedAt),
|
|
2909
|
-
" ",
|
|
3659
|
+
" · ",
|
|
2910
3660
|
job.turn,
|
|
2911
3661
|
".",
|
|
2912
3662
|
job.step,
|
|
2913
|
-
" ",
|
|
3663
|
+
" · ",
|
|
2914
3664
|
job.toolCalls ?? "-",
|
|
2915
3665
|
"t"
|
|
2916
3666
|
]
|
|
@@ -2920,15 +3670,15 @@ function WorkersPanel({ ctx }) {
|
|
|
2920
3670
|
...S.tight,
|
|
2921
3671
|
...S.mono
|
|
2922
3672
|
},
|
|
2923
|
-
children: job.tokens ? `${ktok(job.tokens.input)}/${ktok(job.tokens.output)}` : "
|
|
3673
|
+
children: job.tokens ? `${ktok(job.tokens.input)}/${ktok(job.tokens.output)}` : "—"
|
|
2924
3674
|
}),
|
|
2925
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3675
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
2926
3676
|
style: {
|
|
2927
3677
|
...S.cell,
|
|
2928
3678
|
position: "relative"
|
|
2929
3679
|
},
|
|
2930
|
-
onMouseEnter: (
|
|
2931
|
-
const rect =
|
|
3680
|
+
onMouseEnter: (event) => {
|
|
3681
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
2932
3682
|
setHoverTask({
|
|
2933
3683
|
id: job.id,
|
|
2934
3684
|
text: job.task,
|
|
@@ -2939,22 +3689,149 @@ function WorkersPanel({ ctx }) {
|
|
|
2939
3689
|
});
|
|
2940
3690
|
},
|
|
2941
3691
|
onMouseLeave: () => setHoverTask(null),
|
|
2942
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3692
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2943
3693
|
style: {
|
|
2944
3694
|
whiteSpace: "nowrap",
|
|
2945
3695
|
overflow: "hidden",
|
|
2946
3696
|
textOverflow: "ellipsis",
|
|
2947
3697
|
lineHeight: 1.45,
|
|
2948
|
-
fontSize: 12
|
|
3698
|
+
fontSize: 12
|
|
2949
3699
|
},
|
|
2950
3700
|
children: job.task
|
|
2951
|
-
})
|
|
3701
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3702
|
+
style: {
|
|
3703
|
+
fontSize: 10,
|
|
3704
|
+
opacity: .5
|
|
3705
|
+
},
|
|
3706
|
+
children: job.selection_source ?? "unknown"
|
|
3707
|
+
})]
|
|
2952
3708
|
})
|
|
2953
3709
|
] }, job.id);
|
|
2954
3710
|
}) })
|
|
2955
3711
|
]
|
|
2956
3712
|
})
|
|
2957
|
-
})
|
|
3713
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3714
|
+
style: {
|
|
3715
|
+
...S.block,
|
|
3716
|
+
marginTop: 2
|
|
3717
|
+
},
|
|
3718
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3719
|
+
style: S.settingTitle,
|
|
3720
|
+
children: copy.modelActivity
|
|
3721
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3722
|
+
style: S.settingDesc,
|
|
3723
|
+
children: copy.modelActivityHint
|
|
3724
|
+
})] }), modelActivity.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3725
|
+
style: { opacity: .55 },
|
|
3726
|
+
children: copy.noModelActivity
|
|
3727
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3728
|
+
style: { overflowX: "auto" },
|
|
3729
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3730
|
+
role: "table",
|
|
3731
|
+
style: { minWidth: 620 },
|
|
3732
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3733
|
+
role: "row",
|
|
3734
|
+
style: {
|
|
3735
|
+
display: "grid",
|
|
3736
|
+
gridTemplateColumns: "minmax(190px, 1.35fr) 72px minmax(170px, 1fr) 125px",
|
|
3737
|
+
gap: 10,
|
|
3738
|
+
padding: "0 6px 5px",
|
|
3739
|
+
fontSize: 10.5,
|
|
3740
|
+
opacity: .55
|
|
3741
|
+
},
|
|
3742
|
+
children: [
|
|
3743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3744
|
+
role: "columnheader",
|
|
3745
|
+
children: copy.model
|
|
3746
|
+
}),
|
|
3747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3748
|
+
role: "columnheader",
|
|
3749
|
+
children: copy.calls
|
|
3750
|
+
}),
|
|
3751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3752
|
+
role: "columnheader",
|
|
3753
|
+
children: [
|
|
3754
|
+
copy.invocationSource,
|
|
3755
|
+
" / ",
|
|
3756
|
+
copy.routingSource
|
|
3757
|
+
]
|
|
3758
|
+
}),
|
|
3759
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3760
|
+
role: "columnheader",
|
|
3761
|
+
children: copy.lastCalled
|
|
3762
|
+
})
|
|
3763
|
+
]
|
|
3764
|
+
}), modelActivity.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3765
|
+
role: "row",
|
|
3766
|
+
style: {
|
|
3767
|
+
display: "grid",
|
|
3768
|
+
gridTemplateColumns: "minmax(190px, 1.35fr) 72px minmax(170px, 1fr) 125px",
|
|
3769
|
+
gap: 10,
|
|
3770
|
+
alignItems: "center",
|
|
3771
|
+
padding: "7px 6px",
|
|
3772
|
+
borderTop: "1px solid rgba(128,128,128,0.16)"
|
|
3773
|
+
},
|
|
3774
|
+
children: [
|
|
3775
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3776
|
+
role: "cell",
|
|
3777
|
+
style: { minWidth: 0 },
|
|
3778
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
3779
|
+
style: {
|
|
3780
|
+
display: "block",
|
|
3781
|
+
fontSize: 12
|
|
3782
|
+
},
|
|
3783
|
+
children: entry.model
|
|
3784
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3785
|
+
style: {
|
|
3786
|
+
...S.mono,
|
|
3787
|
+
opacity: .55
|
|
3788
|
+
},
|
|
3789
|
+
children: entry.provider
|
|
3790
|
+
})]
|
|
3791
|
+
}),
|
|
3792
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3793
|
+
role: "cell",
|
|
3794
|
+
style: {
|
|
3795
|
+
...S.mono,
|
|
3796
|
+
fontSize: 15,
|
|
3797
|
+
fontWeight: 650
|
|
3798
|
+
},
|
|
3799
|
+
children: entry.count
|
|
3800
|
+
}),
|
|
3801
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3802
|
+
role: "cell",
|
|
3803
|
+
style: {
|
|
3804
|
+
minWidth: 0,
|
|
3805
|
+
fontSize: 11.5
|
|
3806
|
+
},
|
|
3807
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3808
|
+
style: { display: "block" },
|
|
3809
|
+
children: entry.task_sources.map(sourceLabel).join(" · ")
|
|
3810
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3811
|
+
style: {
|
|
3812
|
+
display: "block",
|
|
3813
|
+
opacity: .55
|
|
3814
|
+
},
|
|
3815
|
+
children: [
|
|
3816
|
+
entry.selection_sources.join(" · "),
|
|
3817
|
+
" · ",
|
|
3818
|
+
entry.roles.join(" / ")
|
|
3819
|
+
]
|
|
3820
|
+
})]
|
|
3821
|
+
}),
|
|
3822
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3823
|
+
role: "cell",
|
|
3824
|
+
style: {
|
|
3825
|
+
...S.mono,
|
|
3826
|
+
fontSize: 10.5
|
|
3827
|
+
},
|
|
3828
|
+
children: formatTimestamp(entry.last_called_at, locale)
|
|
3829
|
+
})
|
|
3830
|
+
]
|
|
3831
|
+
}, `${entry.provider}/${entry.model}`))]
|
|
3832
|
+
})
|
|
3833
|
+
})]
|
|
3834
|
+
})]
|
|
2958
3835
|
}),
|
|
2959
3836
|
hoverTask && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2960
3837
|
style: {
|
|
@@ -2983,6 +3860,7 @@ function WorkersPanel({ ctx }) {
|
|
|
2983
3860
|
}
|
|
2984
3861
|
function apply$1(ctx) {
|
|
2985
3862
|
let runningCount = 0;
|
|
3863
|
+
let officialSurface = null;
|
|
2986
3864
|
ctx.slots.inject("settings.section", () => {
|
|
2987
3865
|
let dispose = register();
|
|
2988
3866
|
function register() {
|
|
@@ -3011,7 +3889,12 @@ function apply$1(ctx) {
|
|
|
3011
3889
|
const poll = async () => {
|
|
3012
3890
|
if (document.visibilityState !== "visible") return;
|
|
3013
3891
|
try {
|
|
3014
|
-
|
|
3892
|
+
if (officialSurface === null) {
|
|
3893
|
+
const response = await fetch(`${API}/bridge-status`, { cache: "no-store" });
|
|
3894
|
+
officialSurface = classifyCrewSurface({ bridgeStatus: response.ok ? await response.json() : null }) === CREW_UI_SURFACES.OFFICIAL;
|
|
3895
|
+
}
|
|
3896
|
+
if (!officialSurface) return;
|
|
3897
|
+
const r = await (await fetch(`${API}/jobs`, { cache: "no-store" })).json();
|
|
3015
3898
|
const n = r.ok ? (r.jobs ?? []).filter((j) => j.status === "running").length : 0;
|
|
3016
3899
|
if (n !== runningCount) {
|
|
3017
3900
|
runningCount = n;
|
|
@@ -3031,413 +3914,10 @@ function apply$1(ctx) {
|
|
|
3031
3914
|
});
|
|
3032
3915
|
}
|
|
3033
3916
|
//#endregion
|
|
3034
|
-
//#region src/client/activation-summary.tsx
|
|
3035
|
-
const ORDER = [
|
|
3036
|
-
"live",
|
|
3037
|
-
"next-workflow",
|
|
3038
|
-
"next-session",
|
|
3039
|
-
"restart-required"
|
|
3040
|
-
];
|
|
3041
|
-
const LABELS = {
|
|
3042
|
-
zh: {
|
|
3043
|
-
title: "配置生效边界",
|
|
3044
|
-
hint: "这里显示全局 Settings 保存后的实际生效时机;会话内 dsh_worker_config 覆盖可能更早生效。",
|
|
3045
|
-
boundary: {
|
|
3046
|
-
live: "Live · 当前运行时",
|
|
3047
|
-
"next-workflow": "Next workflow · 下一个任务",
|
|
3048
|
-
"next-session": "Next session · 新 CC / Codex 会话",
|
|
3049
|
-
"restart-required": "Restart required · 重启 DSH / MCP"
|
|
3050
|
-
}
|
|
3051
|
-
},
|
|
3052
|
-
en: {
|
|
3053
|
-
title: "Configuration activation boundaries",
|
|
3054
|
-
hint: "Shows when persisted Settings changes actually take effect. Session-level dsh_worker_config overrides may activate earlier.",
|
|
3055
|
-
boundary: {
|
|
3056
|
-
live: "Live · current runtime",
|
|
3057
|
-
"next-workflow": "Next workflow",
|
|
3058
|
-
"next-session": "Next session · new CC / Codex session",
|
|
3059
|
-
"restart-required": "Restart required · restart DSH / MCP"
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
};
|
|
3063
|
-
function groupActivationBoundaries(activation = {}) {
|
|
3064
|
-
const grouped = Object.fromEntries(ORDER.map((boundary) => [boundary, []]));
|
|
3065
|
-
for (const [key, entry] of Object.entries(activation)) if (entry?.global && grouped[entry.global]) grouped[entry.global].push(key);
|
|
3066
|
-
for (const boundary of ORDER) grouped[boundary].sort();
|
|
3067
|
-
return grouped;
|
|
3068
|
-
}
|
|
3069
|
-
function ActivationSummary({ activation, locale }) {
|
|
3070
|
-
if (!activation || Object.keys(activation).length === 0) return null;
|
|
3071
|
-
const copy = LABELS[locale === "zh" ? "zh" : "en"];
|
|
3072
|
-
const grouped = groupActivationBoundaries(activation);
|
|
3073
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3074
|
-
style: {
|
|
3075
|
-
border: "1px solid rgba(128,128,128,0.22)",
|
|
3076
|
-
borderRadius: 8,
|
|
3077
|
-
padding: "9px 12px",
|
|
3078
|
-
display: "flex",
|
|
3079
|
-
flexDirection: "column",
|
|
3080
|
-
gap: 5
|
|
3081
|
-
},
|
|
3082
|
-
children: [
|
|
3083
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3084
|
-
style: {
|
|
3085
|
-
fontWeight: 600,
|
|
3086
|
-
fontSize: 12.5
|
|
3087
|
-
},
|
|
3088
|
-
children: copy.title
|
|
3089
|
-
}),
|
|
3090
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3091
|
-
style: {
|
|
3092
|
-
fontSize: 11,
|
|
3093
|
-
opacity: .6
|
|
3094
|
-
},
|
|
3095
|
-
children: copy.hint
|
|
3096
|
-
}),
|
|
3097
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3098
|
-
style: {
|
|
3099
|
-
display: "grid",
|
|
3100
|
-
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
3101
|
-
gap: "5px 12px"
|
|
3102
|
-
},
|
|
3103
|
-
children: ORDER.map((boundary) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3104
|
-
style: { minWidth: 0 },
|
|
3105
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3106
|
-
style: {
|
|
3107
|
-
fontSize: 10.5,
|
|
3108
|
-
opacity: .7,
|
|
3109
|
-
fontWeight: 600
|
|
3110
|
-
},
|
|
3111
|
-
children: copy.boundary[boundary]
|
|
3112
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3113
|
-
style: {
|
|
3114
|
-
fontSize: 10.5,
|
|
3115
|
-
opacity: .55,
|
|
3116
|
-
wordBreak: "break-word"
|
|
3117
|
-
},
|
|
3118
|
-
children: grouped[boundary].length ? grouped[boundary].join(" · ") : "—"
|
|
3119
|
-
})]
|
|
3120
|
-
}, boundary))
|
|
3121
|
-
})
|
|
3122
|
-
]
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
//#endregion
|
|
3126
3917
|
//#region src/client/entry.tsx
|
|
3127
3918
|
const inject = inject$1;
|
|
3128
|
-
const API = "/_dsh/dsh-crew";
|
|
3129
|
-
const DEFAULT_ADAPTIVE = {
|
|
3130
|
-
enabled: false,
|
|
3131
|
-
window_size: 8,
|
|
3132
|
-
min_samples: 2
|
|
3133
|
-
};
|
|
3134
|
-
function clampInt(value, fallback, min, max) {
|
|
3135
|
-
const parsed = Number(value);
|
|
3136
|
-
if (!Number.isInteger(parsed)) return fallback;
|
|
3137
|
-
return Math.max(min, Math.min(max, parsed));
|
|
3138
|
-
}
|
|
3139
|
-
function normalizeAdaptive(value) {
|
|
3140
|
-
const windowSize = clampInt(value?.window_size, DEFAULT_ADAPTIVE.window_size, 1, 32);
|
|
3141
|
-
return {
|
|
3142
|
-
enabled: value?.enabled === true,
|
|
3143
|
-
window_size: windowSize,
|
|
3144
|
-
min_samples: clampInt(value?.min_samples, DEFAULT_ADAPTIVE.min_samples, 1, windowSize)
|
|
3145
|
-
};
|
|
3146
|
-
}
|
|
3147
|
-
function useLocale(ctx) {
|
|
3148
|
-
return (0, react.useSyncExternalStore)((notify) => ctx.on("locale/change", notify), () => ctx.locale.getLocale().active, () => ctx.locale.getLocale().active);
|
|
3149
|
-
}
|
|
3150
|
-
function AdaptiveRoutingPanel({ ctx }) {
|
|
3151
|
-
const zh = useLocale(ctx) === "zh";
|
|
3152
|
-
const [adaptive, setAdaptive] = (0, react.useState)(DEFAULT_ADAPTIVE);
|
|
3153
|
-
const [loaded, setLoaded] = (0, react.useState)(false);
|
|
3154
|
-
const [saving, setSaving] = (0, react.useState)(false);
|
|
3155
|
-
const [message, setMessage] = (0, react.useState)("");
|
|
3156
|
-
const copy = zh ? {
|
|
3157
|
-
title: "自适应模型路由(实验)",
|
|
3158
|
-
hint: "默认关闭。仅对系统自动产生的候选做健康排序;显式 Provider / Model 优先级永远保持原序。信号只来自本进程内 Crew 已观察到的成功、失败、超时与粗粒度延迟,不读取额度、价格或凭据。重启 Hub 会清空健康历史。",
|
|
3159
|
-
enabled: "启用自适应路由",
|
|
3160
|
-
window: "健康窗口",
|
|
3161
|
-
minSamples: "最少样本",
|
|
3162
|
-
windowHint: "每个 role/provider/model 最多参考最近 1–32 次结果。",
|
|
3163
|
-
minHint: "达到该样本数后才允许健康分数影响自动候选顺序。",
|
|
3164
|
-
boundary: "生效边界:下一工作流",
|
|
3165
|
-
saved: "已保存",
|
|
3166
|
-
loading: "加载中…"
|
|
3167
|
-
} : {
|
|
3168
|
-
title: "Adaptive Model Routing (experimental)",
|
|
3169
|
-
hint: "Off by default. Health ordering applies only to automatically derived candidates; explicit Provider / Model priorities always keep their order. Signals are limited to Crew-observed success, failure, timeout, and coarse latency in this process—never quota, pricing, or credentials. Restarting the Hub clears the history.",
|
|
3170
|
-
enabled: "Enable adaptive routing",
|
|
3171
|
-
window: "Health window",
|
|
3172
|
-
minSamples: "Minimum samples",
|
|
3173
|
-
windowHint: "Use at most the most recent 1–32 outcomes per role/provider/model.",
|
|
3174
|
-
minHint: "Health may affect automatic candidate order only after this many samples.",
|
|
3175
|
-
boundary: "Activation boundary: next workflow",
|
|
3176
|
-
saved: "Saved",
|
|
3177
|
-
loading: "Loading…"
|
|
3178
|
-
};
|
|
3179
|
-
(0, react.useEffect)(() => {
|
|
3180
|
-
let cancelled = false;
|
|
3181
|
-
const load = async () => {
|
|
3182
|
-
try {
|
|
3183
|
-
const res = await fetch(`${API}/config?lang=${zh ? "zh" : "en"}`, { cache: "no-store" });
|
|
3184
|
-
const body = await res.json();
|
|
3185
|
-
if (!res.ok || body?.ok === false) throw new Error(body?.error ?? `HTTP ${res.status}`);
|
|
3186
|
-
if (!cancelled) {
|
|
3187
|
-
setAdaptive(normalizeAdaptive(body?.config?.worker?.model_policy?.adaptive));
|
|
3188
|
-
setMessage("");
|
|
3189
|
-
setLoaded(true);
|
|
3190
|
-
}
|
|
3191
|
-
} catch (err) {
|
|
3192
|
-
if (!cancelled) {
|
|
3193
|
-
setMessage(err?.message ?? String(err));
|
|
3194
|
-
setLoaded(true);
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
};
|
|
3198
|
-
load();
|
|
3199
|
-
return () => {
|
|
3200
|
-
cancelled = true;
|
|
3201
|
-
};
|
|
3202
|
-
}, [zh]);
|
|
3203
|
-
const save = async (candidate) => {
|
|
3204
|
-
const next = normalizeAdaptive(candidate);
|
|
3205
|
-
setAdaptive(next);
|
|
3206
|
-
setSaving(true);
|
|
3207
|
-
try {
|
|
3208
|
-
const res = await fetch(`${API}/config?lang=${zh ? "zh" : "en"}`, {
|
|
3209
|
-
method: "POST",
|
|
3210
|
-
headers: { "content-type": "application/json" },
|
|
3211
|
-
body: JSON.stringify({ worker: { model_policy: { adaptive: next } } })
|
|
3212
|
-
});
|
|
3213
|
-
const body = await res.json();
|
|
3214
|
-
if (!res.ok || body?.ok === false) throw new Error(body?.error ?? `HTTP ${res.status}`);
|
|
3215
|
-
setAdaptive(normalizeAdaptive(body?.config?.worker?.model_policy?.adaptive));
|
|
3216
|
-
setMessage(copy.saved);
|
|
3217
|
-
setTimeout(() => setMessage(""), 1500);
|
|
3218
|
-
} catch (err) {
|
|
3219
|
-
setMessage(err?.message ?? String(err));
|
|
3220
|
-
} finally {
|
|
3221
|
-
setSaving(false);
|
|
3222
|
-
}
|
|
3223
|
-
};
|
|
3224
|
-
if (!loaded) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3225
|
-
style: {
|
|
3226
|
-
fontSize: 12,
|
|
3227
|
-
opacity: .6
|
|
3228
|
-
},
|
|
3229
|
-
children: copy.loading
|
|
3230
|
-
});
|
|
3231
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3232
|
-
style: {
|
|
3233
|
-
display: "flex",
|
|
3234
|
-
flexDirection: "column",
|
|
3235
|
-
gap: 10,
|
|
3236
|
-
fontSize: 13,
|
|
3237
|
-
lineHeight: 1.55
|
|
3238
|
-
},
|
|
3239
|
-
children: [
|
|
3240
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3241
|
-
style: {
|
|
3242
|
-
fontWeight: 600,
|
|
3243
|
-
fontSize: 14
|
|
3244
|
-
},
|
|
3245
|
-
children: copy.title
|
|
3246
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3247
|
-
style: {
|
|
3248
|
-
opacity: .68,
|
|
3249
|
-
fontSize: 12,
|
|
3250
|
-
marginTop: 2
|
|
3251
|
-
},
|
|
3252
|
-
children: copy.hint
|
|
3253
|
-
})] }),
|
|
3254
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3255
|
-
style: {
|
|
3256
|
-
display: "flex",
|
|
3257
|
-
alignItems: "center",
|
|
3258
|
-
gap: 7
|
|
3259
|
-
},
|
|
3260
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3261
|
-
type: "checkbox",
|
|
3262
|
-
checked: adaptive.enabled,
|
|
3263
|
-
disabled: saving,
|
|
3264
|
-
onChange: (event) => {
|
|
3265
|
-
save({
|
|
3266
|
-
...adaptive,
|
|
3267
|
-
enabled: event.target.checked
|
|
3268
|
-
});
|
|
3269
|
-
}
|
|
3270
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copy.enabled })]
|
|
3271
|
-
}),
|
|
3272
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3273
|
-
style: {
|
|
3274
|
-
display: "grid",
|
|
3275
|
-
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
3276
|
-
gap: 10
|
|
3277
|
-
},
|
|
3278
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3279
|
-
style: {
|
|
3280
|
-
display: "flex",
|
|
3281
|
-
flexDirection: "column",
|
|
3282
|
-
gap: 4
|
|
3283
|
-
},
|
|
3284
|
-
children: [
|
|
3285
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3286
|
-
style: {
|
|
3287
|
-
fontSize: 12,
|
|
3288
|
-
fontWeight: 500
|
|
3289
|
-
},
|
|
3290
|
-
children: copy.window
|
|
3291
|
-
}),
|
|
3292
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3293
|
-
type: "number",
|
|
3294
|
-
min: 1,
|
|
3295
|
-
max: 32,
|
|
3296
|
-
value: adaptive.window_size,
|
|
3297
|
-
disabled: saving,
|
|
3298
|
-
onChange: (event) => {
|
|
3299
|
-
const windowSize = clampInt(event.target.value, adaptive.window_size, 1, 32);
|
|
3300
|
-
setAdaptive((current) => ({
|
|
3301
|
-
...current,
|
|
3302
|
-
window_size: windowSize,
|
|
3303
|
-
min_samples: Math.min(current.min_samples, windowSize)
|
|
3304
|
-
}));
|
|
3305
|
-
},
|
|
3306
|
-
onBlur: () => {
|
|
3307
|
-
save(adaptive);
|
|
3308
|
-
},
|
|
3309
|
-
style: {
|
|
3310
|
-
padding: "5px 7px",
|
|
3311
|
-
borderRadius: 5,
|
|
3312
|
-
border: "1px solid rgba(128,128,128,0.35)",
|
|
3313
|
-
background: "transparent",
|
|
3314
|
-
color: "inherit"
|
|
3315
|
-
}
|
|
3316
|
-
}),
|
|
3317
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3318
|
-
style: {
|
|
3319
|
-
fontSize: 10.5,
|
|
3320
|
-
opacity: .55
|
|
3321
|
-
},
|
|
3322
|
-
children: copy.windowHint
|
|
3323
|
-
})
|
|
3324
|
-
]
|
|
3325
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3326
|
-
style: {
|
|
3327
|
-
display: "flex",
|
|
3328
|
-
flexDirection: "column",
|
|
3329
|
-
gap: 4
|
|
3330
|
-
},
|
|
3331
|
-
children: [
|
|
3332
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3333
|
-
style: {
|
|
3334
|
-
fontSize: 12,
|
|
3335
|
-
fontWeight: 500
|
|
3336
|
-
},
|
|
3337
|
-
children: copy.minSamples
|
|
3338
|
-
}),
|
|
3339
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3340
|
-
type: "number",
|
|
3341
|
-
min: 1,
|
|
3342
|
-
max: adaptive.window_size,
|
|
3343
|
-
value: adaptive.min_samples,
|
|
3344
|
-
disabled: saving,
|
|
3345
|
-
onChange: (event) => setAdaptive((current) => ({
|
|
3346
|
-
...current,
|
|
3347
|
-
min_samples: clampInt(event.target.value, current.min_samples, 1, current.window_size)
|
|
3348
|
-
})),
|
|
3349
|
-
onBlur: () => {
|
|
3350
|
-
save(adaptive);
|
|
3351
|
-
},
|
|
3352
|
-
style: {
|
|
3353
|
-
padding: "5px 7px",
|
|
3354
|
-
borderRadius: 5,
|
|
3355
|
-
border: "1px solid rgba(128,128,128,0.35)",
|
|
3356
|
-
background: "transparent",
|
|
3357
|
-
color: "inherit"
|
|
3358
|
-
}
|
|
3359
|
-
}),
|
|
3360
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3361
|
-
style: {
|
|
3362
|
-
fontSize: 10.5,
|
|
3363
|
-
opacity: .55
|
|
3364
|
-
},
|
|
3365
|
-
children: copy.minHint
|
|
3366
|
-
})
|
|
3367
|
-
]
|
|
3368
|
-
})]
|
|
3369
|
-
}),
|
|
3370
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3371
|
-
style: {
|
|
3372
|
-
display: "flex",
|
|
3373
|
-
gap: 8,
|
|
3374
|
-
alignItems: "center",
|
|
3375
|
-
fontSize: 11.5,
|
|
3376
|
-
opacity: .62
|
|
3377
|
-
},
|
|
3378
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copy.boundary }), message && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["· ", message] })]
|
|
3379
|
-
})
|
|
3380
|
-
]
|
|
3381
|
-
});
|
|
3382
|
-
}
|
|
3383
|
-
function ActivationBoundaryPanel({ ctx }) {
|
|
3384
|
-
const locale = useLocale(ctx);
|
|
3385
|
-
const [activation, setActivation] = (0, react.useState)(null);
|
|
3386
|
-
const [error, setError] = (0, react.useState)("");
|
|
3387
|
-
(0, react.useEffect)(() => {
|
|
3388
|
-
let cancelled = false;
|
|
3389
|
-
const load = async () => {
|
|
3390
|
-
try {
|
|
3391
|
-
const res = await fetch(`${API}/config?lang=${locale === "zh" ? "zh" : "en"}`, { cache: "no-store" });
|
|
3392
|
-
const body = await res.json();
|
|
3393
|
-
if (!cancelled) {
|
|
3394
|
-
if (!res.ok || body?.ok === false) throw new Error(body?.error ?? `HTTP ${res.status}`);
|
|
3395
|
-
setActivation(body?.config?.config_activation ?? null);
|
|
3396
|
-
setError("");
|
|
3397
|
-
}
|
|
3398
|
-
} catch (err) {
|
|
3399
|
-
if (!cancelled) setError(err?.message ?? String(err));
|
|
3400
|
-
}
|
|
3401
|
-
};
|
|
3402
|
-
load();
|
|
3403
|
-
return () => {
|
|
3404
|
-
cancelled = true;
|
|
3405
|
-
};
|
|
3406
|
-
}, [locale]);
|
|
3407
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3408
|
-
style: {
|
|
3409
|
-
display: "flex",
|
|
3410
|
-
flexDirection: "column",
|
|
3411
|
-
gap: 8,
|
|
3412
|
-
fontSize: 13,
|
|
3413
|
-
lineHeight: 1.55
|
|
3414
|
-
},
|
|
3415
|
-
children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3416
|
-
style: {
|
|
3417
|
-
fontSize: 11.5,
|
|
3418
|
-
opacity: .6
|
|
3419
|
-
},
|
|
3420
|
-
children: error
|
|
3421
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivationSummary, {
|
|
3422
|
-
activation: activation ?? void 0,
|
|
3423
|
-
locale
|
|
3424
|
-
})]
|
|
3425
|
-
});
|
|
3426
|
-
}
|
|
3427
3919
|
function apply(ctx) {
|
|
3428
3920
|
apply$1(ctx);
|
|
3429
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
3430
|
-
name: "settings.section",
|
|
3431
|
-
id: "dsh-crew-adaptive-routing",
|
|
3432
|
-
order: 65,
|
|
3433
|
-
label: () => ctx.locale.getLocale().active === "zh" ? "DSH Crew · 自适应路由" : "DSH Crew · Adaptive Routing"
|
|
3434
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AdaptiveRoutingPanel, { ctx })));
|
|
3435
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
3436
|
-
name: "settings.section",
|
|
3437
|
-
id: "dsh-crew-runtime-controls",
|
|
3438
|
-
order: 66,
|
|
3439
|
-
label: () => ctx.locale.getLocale().active === "zh" ? "DSH Crew · 生效边界" : "DSH Crew · Activation"
|
|
3440
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivationBoundaryPanel, { ctx })));
|
|
3441
3921
|
}
|
|
3442
3922
|
//#endregion
|
|
3443
3923
|
exports.apply = apply;
|