@dsh-workbench/core 0.2.0 → 0.2.1
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/client.js +1427 -607
- package/dist/client/client.body.js +1427 -607
- package/dist/client/index.js +1428 -608
- package/dist/host/index.js +645 -154
- package/dist/types/app/client.d.ts +165 -81
- package/dist/types/app/layouts/__tests__/async-feedback.test.d.ts +1 -0
- package/dist/types/app/layouts/market/use-add-module.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
- package/dist/types/app/layouts/rightbar/{PanelPluginRows.d.ts → PanelGroupRows.d.ts} +3 -2
- package/dist/types/app/layouts/rightbar/PanelRowControls.d.ts +0 -1
- package/dist/types/app/layouts/rightbar/__tests__/market-catalog.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-dots.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-state-rules.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/panel-dismiss.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/panel-drag-target.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/panel-write.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/board-selection.d.ts +9 -0
- package/dist/types/app/layouts/rightbar/dsh-borrow.d.ts +15 -0
- package/dist/types/app/layouts/rightbar/dsh-borrow.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/dsh-surface.d.ts +11 -0
- package/dist/types/app/layouts/rightbar/dsh-surface.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/market-installed-mock.d.ts +9 -0
- package/dist/types/app/layouts/rightbar/market-mock-types.d.ts +58 -0
- package/dist/types/app/layouts/rightbar/market-mock.d.ts +2 -48
- package/dist/types/app/layouts/rightbar/market-row-types.d.ts +4 -2
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +9 -22
- package/dist/types/app/layouts/rightbar/market-section-icon.d.ts +1 -1
- package/dist/types/app/layouts/rightbar/market-shared-rows.d.ts +21 -0
- package/dist/types/app/layouts/rightbar/panel-dismiss.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/panel-mock.d.ts +1 -1
- package/dist/types/app/layouts/rightbar/panel-write.d.ts +15 -0
- package/dist/types/app/layouts/rightbar/use-drag-listeners.d.ts +6 -0
- package/dist/types/app/layouts/sidebar/__tests__/session-row-ui.test.d.ts +1 -0
- package/dist/types/app/units/registry.d.ts +4 -1
- package/dist/types/modules/panels/__tests__/diy-harness.d.ts +19 -0
- package/dist/types/modules/panels/__tests__/diy-report.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/diy-tools-health.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/diy-tools-panel.test.d.ts +1 -0
- package/dist/types/modules/panels/model/config.d.ts +4 -3
- package/dist/types/modules/panels/model/diy-report.d.ts +63 -0
- package/dist/types/shared/api/host/__tests__/approval-note-injection.test.d.ts +1 -0
- package/dist/types/shared/api/host/__tests__/panel-state-route.test.d.ts +1 -0
- package/dist/types/shared/api/host/installed-module.d.ts +2 -0
- package/dist/types/shared/api/host/mount-units.d.ts +1 -0
- package/dist/types/shared/api/host/unit-registry.d.ts +6 -0
- package/dist/types/shared/api/unit-manifests.d.ts +1 -0
- package/dist/types/shared/components/PanelFoot.d.ts +1 -1
- package/dist/types/shared/hooks/panel-state-report.d.ts +13 -0
- package/dist/types/shared/hooks/panel-state-report.test.d.ts +1 -0
- package/docs/HANDOFF.md +7 -0
- package/package.json +2 -2
- package/preset/diy.patch.yml +5 -26
- package/skills/workbench-module-diy/REVIEW.md +22 -0
- package/skills/workbench-module-diy/SKILL.md +3 -1
- package/skills/workbench-module-diy/TOOLING.md +2 -0
package/dist/host/index.js
CHANGED
|
@@ -11,9 +11,11 @@ var PREFS_PREFIX = `${API_PREFIX}/prefs`;
|
|
|
11
11
|
var EVENTS_PATH = "/workbench/events";
|
|
12
12
|
var ACTIONS_PREFIX = `${API_PREFIX}/actions`;
|
|
13
13
|
var PROVISION_PREFIX = `${API_PREFIX}/provision`;
|
|
14
|
+
var PANEL_STATE_PREFIX = `${API_PREFIX}/panel-state`;
|
|
14
15
|
var OPENCLI_PIN = "1.8.8";
|
|
15
16
|
var PANELS_SERVICE = "workbenchPanels";
|
|
16
17
|
var UNITS_SERVICE = "workbenchUnits";
|
|
18
|
+
var PANEL_STATE_SERVICE = "workbenchPanelState";
|
|
17
19
|
var OPENCLI_TIMEOUT_MS = 12e4;
|
|
18
20
|
var WARM_TIMEOUT_MS = 2e4;
|
|
19
21
|
var INSTALL_TIMEOUT_MS = 3e5;
|
|
@@ -50,6 +52,11 @@ function parseMarkers(text) {
|
|
|
50
52
|
}
|
|
51
53
|
return refs;
|
|
52
54
|
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region ../shared/dist/api/unit.js
|
|
57
|
+
function hasEnvSupply(manifest) {
|
|
58
|
+
return (manifest.env?.plugins ?? []).length > 0 || (manifest.env?.libs ?? []).length > 0;
|
|
59
|
+
}
|
|
53
60
|
var SUPPORTED_API_VERSIONS = [1];
|
|
54
61
|
var DECLARATION_KEYS = [
|
|
55
62
|
"apiVersion",
|
|
@@ -212,6 +219,202 @@ function parseJsonLoose(text) {
|
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
}
|
|
222
|
+
var PANEL_STATE_MAX_TEXT = 2e3;
|
|
223
|
+
var PHASES = [
|
|
224
|
+
"idle",
|
|
225
|
+
"loading",
|
|
226
|
+
"ready",
|
|
227
|
+
"error"
|
|
228
|
+
];
|
|
229
|
+
function asRecord$10(value) {
|
|
230
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
231
|
+
}
|
|
232
|
+
function textOf$6(value, max) {
|
|
233
|
+
if (typeof value !== "string") return "";
|
|
234
|
+
const trimmed = value.trim();
|
|
235
|
+
return trimmed.length > max ? trimmed.slice(0, max) : trimmed;
|
|
236
|
+
}
|
|
237
|
+
function atOf(value) {
|
|
238
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
239
|
+
if (value < 0) return null;
|
|
240
|
+
return Math.trunc(value);
|
|
241
|
+
}
|
|
242
|
+
function errorOf(raw) {
|
|
243
|
+
const record = asRecord$10(raw);
|
|
244
|
+
if (record === void 0) return void 0;
|
|
245
|
+
const code = textOf$6(record["code"], 120);
|
|
246
|
+
const message = textOf$6(record["message"], PANEL_STATE_MAX_TEXT);
|
|
247
|
+
if (code === "" && message === "") return void 0;
|
|
248
|
+
return {
|
|
249
|
+
code: code === "" ? "unknown" : code,
|
|
250
|
+
message
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function parseCardsReport(payload) {
|
|
254
|
+
const record = asRecord$10(payload);
|
|
255
|
+
if (record === void 0) return {
|
|
256
|
+
ok: false,
|
|
257
|
+
message: "请求体要是一个对象"
|
|
258
|
+
};
|
|
259
|
+
const workspace = textOf$6(record["workspace"], PANEL_STATE_MAX_TEXT);
|
|
260
|
+
if (workspace === "") return {
|
|
261
|
+
ok: false,
|
|
262
|
+
message: "workspace 不能是空的 —— 面板按工作区存"
|
|
263
|
+
};
|
|
264
|
+
const raw = record["cards"];
|
|
265
|
+
if (!Array.isArray(raw)) return {
|
|
266
|
+
ok: false,
|
|
267
|
+
message: "cards 要是一个数组"
|
|
268
|
+
};
|
|
269
|
+
const cards = [];
|
|
270
|
+
for (const item of raw.slice(0, 32)) {
|
|
271
|
+
const card = asRecord$10(item);
|
|
272
|
+
if (card === void 0) continue;
|
|
273
|
+
const unit = textOf$6(card["unit"], 200);
|
|
274
|
+
const route = textOf$6(card["route"], 400);
|
|
275
|
+
const phase = textOf$6(card["phase"], 20);
|
|
276
|
+
const at = atOf(card["at"]);
|
|
277
|
+
if (unit === "" || route === "" || at === null || !PHASES.includes(phase)) continue;
|
|
278
|
+
const error = errorOf(card["error"]);
|
|
279
|
+
cards.push({
|
|
280
|
+
unit,
|
|
281
|
+
route,
|
|
282
|
+
phase,
|
|
283
|
+
at,
|
|
284
|
+
...error === void 0 ? {} : { error }
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (cards.length === 0) return {
|
|
288
|
+
ok: false,
|
|
289
|
+
message: "cards 里没有一条能认的(unit / route / phase / at 都要有)"
|
|
290
|
+
};
|
|
291
|
+
return {
|
|
292
|
+
ok: true,
|
|
293
|
+
value: {
|
|
294
|
+
workspace,
|
|
295
|
+
cards
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function parseCrashReport(payload) {
|
|
300
|
+
const record = asRecord$10(payload);
|
|
301
|
+
if (record === void 0) return {
|
|
302
|
+
ok: false,
|
|
303
|
+
message: "请求体要是一个对象"
|
|
304
|
+
};
|
|
305
|
+
const workspace = textOf$6(record["workspace"], PANEL_STATE_MAX_TEXT);
|
|
306
|
+
const unit = textOf$6(record["unit"], 200);
|
|
307
|
+
const reason = textOf$6(record["reason"], PANEL_STATE_MAX_TEXT);
|
|
308
|
+
if (workspace === "") return {
|
|
309
|
+
ok: false,
|
|
310
|
+
message: "workspace 不能是空的"
|
|
311
|
+
};
|
|
312
|
+
if (unit === "") return {
|
|
313
|
+
ok: false,
|
|
314
|
+
message: "unit 不能是空的"
|
|
315
|
+
};
|
|
316
|
+
if (reason === "") return {
|
|
317
|
+
ok: false,
|
|
318
|
+
message: "reason 不能是空的(没有原因的崩溃没法查)"
|
|
319
|
+
};
|
|
320
|
+
return {
|
|
321
|
+
ok: true,
|
|
322
|
+
value: {
|
|
323
|
+
workspace,
|
|
324
|
+
unit,
|
|
325
|
+
reason,
|
|
326
|
+
at: atOf(record["at"]) ?? Date.now()
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function createPanelStateStore() {
|
|
331
|
+
const workspaces = /* @__PURE__ */ new Map();
|
|
332
|
+
function trim(entry, max) {
|
|
333
|
+
while (entry.size > max) {
|
|
334
|
+
const oldest = entry.keys().next();
|
|
335
|
+
if (oldest.done === true) return;
|
|
336
|
+
entry.delete(oldest.value);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function workspaceEntryOf(workspace) {
|
|
340
|
+
const existing = workspaces.get(workspace);
|
|
341
|
+
if (existing !== void 0) return existing;
|
|
342
|
+
const created = {
|
|
343
|
+
at: 0,
|
|
344
|
+
units: /* @__PURE__ */ new Map()
|
|
345
|
+
};
|
|
346
|
+
workspaces.set(workspace, created);
|
|
347
|
+
trim(workspaces, 64);
|
|
348
|
+
return created;
|
|
349
|
+
}
|
|
350
|
+
function unitEntryOf(entry, unit) {
|
|
351
|
+
const existing = entry.units.get(unit);
|
|
352
|
+
if (existing !== void 0) return existing;
|
|
353
|
+
const created = {
|
|
354
|
+
at: 0,
|
|
355
|
+
cards: /* @__PURE__ */ new Map()
|
|
356
|
+
};
|
|
357
|
+
entry.units.set(unit, created);
|
|
358
|
+
return created;
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
cards(report) {
|
|
362
|
+
const entry = workspaceEntryOf(report.workspace);
|
|
363
|
+
for (const card of report.cards) {
|
|
364
|
+
const unit = unitEntryOf(entry, card.unit);
|
|
365
|
+
unit.cards.delete(card.route);
|
|
366
|
+
unit.cards.set(card.route, card);
|
|
367
|
+
trim(unit.cards, 12);
|
|
368
|
+
if (card.at > unit.at) unit.at = card.at;
|
|
369
|
+
if (card.at > entry.at) entry.at = card.at;
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
crash(report) {
|
|
373
|
+
const entry = workspaceEntryOf(report.workspace);
|
|
374
|
+
const unit = unitEntryOf(entry, report.unit);
|
|
375
|
+
const previous = unit.crash;
|
|
376
|
+
const counted = previous !== void 0 && previous.reason === report.reason && report.at >= previous.at && report.at - previous.at <= 6e5;
|
|
377
|
+
unit.crash = {
|
|
378
|
+
unit: report.unit,
|
|
379
|
+
reason: report.reason,
|
|
380
|
+
at: report.at,
|
|
381
|
+
count: counted ? previous.count + 1 : 1
|
|
382
|
+
};
|
|
383
|
+
if (report.at > unit.at) unit.at = report.at;
|
|
384
|
+
if (report.at > entry.at) entry.at = report.at;
|
|
385
|
+
},
|
|
386
|
+
snapshot(workspace) {
|
|
387
|
+
if (workspace === null) return {
|
|
388
|
+
workspace: null,
|
|
389
|
+
at: 0,
|
|
390
|
+
units: []
|
|
391
|
+
};
|
|
392
|
+
const entry = workspaces.get(workspace);
|
|
393
|
+
if (entry === void 0) return {
|
|
394
|
+
workspace,
|
|
395
|
+
at: 0,
|
|
396
|
+
units: []
|
|
397
|
+
};
|
|
398
|
+
const units = [...entry.units.entries()].map(([unit, one]) => {
|
|
399
|
+
const cards = [...one.cards.values()].sort((a, b) => a.route.localeCompare(b.route));
|
|
400
|
+
const okAt = cards.reduce((best, card) => card.phase === "ready" && (best === null || card.at > best) ? card.at : best, null);
|
|
401
|
+
return {
|
|
402
|
+
unit,
|
|
403
|
+
at: one.at,
|
|
404
|
+
okAt,
|
|
405
|
+
cards,
|
|
406
|
+
...one.crash === void 0 ? {} : { crash: one.crash }
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
units.sort((a, b) => b.at - a.at || a.unit.localeCompare(b.unit));
|
|
410
|
+
return {
|
|
411
|
+
workspace,
|
|
412
|
+
at: entry.at,
|
|
413
|
+
units
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
215
418
|
//#endregion
|
|
216
419
|
//#region ../shared/dist/host/cli-error.js
|
|
217
420
|
function unquote(raw) {
|
|
@@ -1403,6 +1606,42 @@ function registerActions(ctx, resources, wrote) {
|
|
|
1403
1606
|
...wrote === void 0 ? {} : { wrote }
|
|
1404
1607
|
});
|
|
1405
1608
|
}
|
|
1609
|
+
function registerPanelState(ctx, store) {
|
|
1610
|
+
return registerPost(ctx, {
|
|
1611
|
+
prefix: PANEL_STATE_PREFIX,
|
|
1612
|
+
what: "面板运行态",
|
|
1613
|
+
refusedCode: "report-refused",
|
|
1614
|
+
resources: [{
|
|
1615
|
+
resource: "cards",
|
|
1616
|
+
handle(payload) {
|
|
1617
|
+
const parsed = parseCardsReport(payload);
|
|
1618
|
+
if (!parsed.ok) return {
|
|
1619
|
+
ok: false,
|
|
1620
|
+
text: parsed.message
|
|
1621
|
+
};
|
|
1622
|
+
store.cards(parsed.value);
|
|
1623
|
+
return {
|
|
1624
|
+
ok: true,
|
|
1625
|
+
at: Date.now()
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
}, {
|
|
1629
|
+
resource: "crash",
|
|
1630
|
+
handle(payload) {
|
|
1631
|
+
const parsed = parseCrashReport(payload);
|
|
1632
|
+
if (!parsed.ok) return {
|
|
1633
|
+
ok: false,
|
|
1634
|
+
text: parsed.message
|
|
1635
|
+
};
|
|
1636
|
+
store.crash(parsed.value);
|
|
1637
|
+
return {
|
|
1638
|
+
ok: true,
|
|
1639
|
+
at: Date.now()
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
}]
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1406
1645
|
//#endregion
|
|
1407
1646
|
//#region ../shared/dist/host/long-task.js
|
|
1408
1647
|
var LIVE = /* @__PURE__ */ new Set(["running", "needs-login"]);
|
|
@@ -1796,15 +2035,48 @@ function resolvePluginSource(source, env, options = {}) {
|
|
|
1796
2035
|
}
|
|
1797
2036
|
//#endregion
|
|
1798
2037
|
//#region ../shared/dist/host/tool-approval.js
|
|
2038
|
+
var APPROVAL_NOTE_ARG = "approval_note";
|
|
2039
|
+
var NOTE_SCHEMA = {
|
|
2040
|
+
type: "string",
|
|
2041
|
+
description: "一句话说明你这次想拿它干什么(要点头的那几通会显示在审批屏上)。"
|
|
2042
|
+
};
|
|
2043
|
+
function withApprovalNote(parameters, options = {}) {
|
|
2044
|
+
const properties = parameters["properties"];
|
|
2045
|
+
if (typeof properties !== "object" || properties === null) return parameters;
|
|
2046
|
+
const map = properties;
|
|
2047
|
+
if ("approval_note" in map) return parameters;
|
|
2048
|
+
const kept = (Array.isArray(parameters["required"]) ? parameters["required"] : []).filter((name) => name !== APPROVAL_NOTE_ARG);
|
|
2049
|
+
const required = options.required === true ? [...kept, APPROVAL_NOTE_ARG] : kept;
|
|
2050
|
+
return {
|
|
2051
|
+
...parameters,
|
|
2052
|
+
properties: {
|
|
2053
|
+
...map,
|
|
2054
|
+
[APPROVAL_NOTE_ARG]: NOTE_SCHEMA
|
|
2055
|
+
},
|
|
2056
|
+
...required.length === 0 ? {} : { required }
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function approvalNoteOf(call) {
|
|
2060
|
+
const raw = call.arguments;
|
|
2061
|
+
const value = typeof raw === "object" && raw !== null ? raw[APPROVAL_NOTE_ARG] : void 0;
|
|
2062
|
+
if (typeof value !== "string") return void 0;
|
|
2063
|
+
const line = value.replace(/\s+/g, " ").trim();
|
|
2064
|
+
if (line === "") return void 0;
|
|
2065
|
+
return line.length > 160 ? `${line.slice(0, 160)}…` : line;
|
|
2066
|
+
}
|
|
1799
2067
|
function messageOf$1(error) {
|
|
1800
2068
|
return error instanceof Error ? error.message : String(error);
|
|
1801
2069
|
}
|
|
1802
|
-
function compose(detail, verdict) {
|
|
1803
|
-
|
|
1804
|
-
return {
|
|
2070
|
+
function compose(detail, verdict, note) {
|
|
2071
|
+
const base = detail === void 0 || typeof detail.zh !== "string" || detail.zh.trim() === "" ? verdict.fallback : {
|
|
1805
2072
|
en: `${detail.en} — ${verdict.question.en}`,
|
|
1806
2073
|
zh: `${detail.zh} —— ${verdict.question.zh}`
|
|
1807
2074
|
};
|
|
2075
|
+
if (note === void 0) return base;
|
|
2076
|
+
return {
|
|
2077
|
+
en: `${base.en} — ${note}`,
|
|
2078
|
+
zh: `${base.zh} —— ${note}`
|
|
2079
|
+
};
|
|
1808
2080
|
}
|
|
1809
2081
|
async function approvalFor(call, context = {}) {
|
|
1810
2082
|
if (context.writes !== true) return void 0;
|
|
@@ -1838,7 +2110,7 @@ async function approvalFor(call, context = {}) {
|
|
|
1838
2110
|
return {
|
|
1839
2111
|
kind: "ask",
|
|
1840
2112
|
reason: verdict.reason,
|
|
1841
|
-
displayReason: compose(facts, verdict)
|
|
2113
|
+
displayReason: compose(facts, verdict, approvalNoteOf(call))
|
|
1842
2114
|
};
|
|
1843
2115
|
}
|
|
1844
2116
|
//#endregion
|
|
@@ -2207,6 +2479,22 @@ function profileModules(profileDir) {
|
|
|
2207
2479
|
}
|
|
2208
2480
|
return found;
|
|
2209
2481
|
}
|
|
2482
|
+
function createDeclarationLookup(profileDir) {
|
|
2483
|
+
let roster;
|
|
2484
|
+
const scan = () => new Map(profileModules(profileDir).map((row) => [row.unit, row.pkg]));
|
|
2485
|
+
return (unit) => {
|
|
2486
|
+
roster ??= scan();
|
|
2487
|
+
let pkg = roster.get(unit);
|
|
2488
|
+
if (pkg === void 0) {
|
|
2489
|
+
roster = scan();
|
|
2490
|
+
pkg = roster.get(unit);
|
|
2491
|
+
}
|
|
2492
|
+
return pkg === void 0 ? void 0 : {
|
|
2493
|
+
pkg,
|
|
2494
|
+
declaration: moduleDeclarationOf(profileDir, pkg)
|
|
2495
|
+
};
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2210
2498
|
function registryFromLockfile(profileDir, pkg) {
|
|
2211
2499
|
let text;
|
|
2212
2500
|
try {
|
|
@@ -2667,40 +2955,58 @@ function strings(raw) {
|
|
|
2667
2955
|
function parseBoard(raw) {
|
|
2668
2956
|
if (typeof raw !== "object" || raw === null) return EMPTY_BOARD;
|
|
2669
2957
|
const value = raw;
|
|
2958
|
+
const order = strings(value.order);
|
|
2670
2959
|
return {
|
|
2671
2960
|
shown: strings(value.shown),
|
|
2672
|
-
hidden: strings(value.hidden)
|
|
2961
|
+
hidden: strings(value.hidden),
|
|
2962
|
+
...order.length === 0 ? {} : { order }
|
|
2673
2963
|
};
|
|
2674
2964
|
}
|
|
2675
2965
|
function parseReleased(raw) {
|
|
2676
2966
|
if (typeof raw !== "object" || raw === null) return [];
|
|
2677
2967
|
return strings(raw.ids);
|
|
2678
2968
|
}
|
|
2679
|
-
function resolvePanels(manifests, boards) {
|
|
2680
|
-
const
|
|
2681
|
-
const
|
|
2682
|
-
|
|
2969
|
+
function resolvePanels(manifests, boards, owned = /* @__PURE__ */ new Set()) {
|
|
2970
|
+
const listed = manifests.filter((manifest) => manifest.card !== null || !owned.has(manifest.id));
|
|
2971
|
+
const carded = listed.filter((manifest) => manifest.card !== null);
|
|
2972
|
+
const declared = (manifest) => manifest.card?.order ?? 0;
|
|
2973
|
+
const byDeclared = (a, b) => declared(a) - declared(b) || a.id.localeCompare(b.id);
|
|
2974
|
+
const queued = [...new Set(boards.workspace?.order ?? [])].map((id) => carded.find((manifest) => manifest.id === id)).filter((manifest) => manifest !== void 0).filter((manifest) => resolveScope({
|
|
2975
|
+
id: manifest.id,
|
|
2976
|
+
boards
|
|
2977
|
+
}));
|
|
2978
|
+
const placed = new Set(queued.map((manifest) => manifest.id));
|
|
2979
|
+
const ordered = [...queued, ...carded.filter((manifest) => !placed.has(manifest.id) && resolveScope({
|
|
2980
|
+
id: manifest.id,
|
|
2981
|
+
boards
|
|
2982
|
+
})).sort(byDeclared)];
|
|
2983
|
+
const rank = new Map(ordered.map((manifest, index) => [manifest.id, index]));
|
|
2984
|
+
return listed.map((manifest) => {
|
|
2985
|
+
const hasCard = manifest.card !== null;
|
|
2683
2986
|
const on = resolveScope({
|
|
2684
2987
|
id: manifest.id,
|
|
2685
2988
|
boards
|
|
2686
2989
|
});
|
|
2687
|
-
const order = rank.get(manifest.id) ?? ordered.length + (manifest
|
|
2990
|
+
const order = hasCard ? rank.get(manifest.id) ?? ordered.length + declared(manifest) : 0;
|
|
2688
2991
|
return {
|
|
2689
2992
|
id: manifest.id,
|
|
2690
2993
|
titleKey: manifest.card?.titleKey ?? manifest.id,
|
|
2691
2994
|
order,
|
|
2692
2995
|
shown: on,
|
|
2693
2996
|
source: overrideOf(boards.workspace, manifest.id) === void 0 ? "default" : "user",
|
|
2694
|
-
hasCard
|
|
2997
|
+
hasCard
|
|
2695
2998
|
};
|
|
2696
|
-
}).sort((a, b) =>
|
|
2999
|
+
}).sort((a, b) => {
|
|
3000
|
+
if (a.hasCard !== b.hasCard) return a.hasCard ? -1 : 1;
|
|
3001
|
+
return a.order - b.order || a.id.localeCompare(b.id);
|
|
3002
|
+
});
|
|
2697
3003
|
}
|
|
2698
3004
|
var PLUGIN_PANEL_PREFIX = "plugin:";
|
|
2699
3005
|
function pluginPanelId(pkg) {
|
|
2700
3006
|
return `${PLUGIN_PANEL_PREFIX}${pkg}`;
|
|
2701
3007
|
}
|
|
2702
3008
|
function resolvePluginPanels(plugins, boards) {
|
|
2703
|
-
const ordered = [
|
|
3009
|
+
const ordered = [...new Set(boards.workspace?.order ?? [])];
|
|
2704
3010
|
const rank = new Map(ordered.map((id, index) => [id, index]));
|
|
2705
3011
|
const seen = /* @__PURE__ */ new Set();
|
|
2706
3012
|
return plugins.map((plugin) => {
|
|
@@ -2735,16 +3041,22 @@ function added(list, id) {
|
|
|
2735
3041
|
function dropped(list, id) {
|
|
2736
3042
|
return list.filter((item) => item !== id);
|
|
2737
3043
|
}
|
|
2738
|
-
function withShown(board, id) {
|
|
2739
|
-
|
|
3044
|
+
function withShown(board, id, visible) {
|
|
3045
|
+
const next = {
|
|
2740
3046
|
shown: added(board.shown, id),
|
|
2741
3047
|
hidden: dropped(board.hidden, id)
|
|
2742
3048
|
};
|
|
3049
|
+
return visible === void 0 ? next : {
|
|
3050
|
+
...next,
|
|
3051
|
+
order: added(board.order ?? visible, id)
|
|
3052
|
+
};
|
|
2743
3053
|
}
|
|
2744
3054
|
function withHidden(board, id) {
|
|
3055
|
+
const order = dropped(board.order ?? [], id);
|
|
2745
3056
|
return {
|
|
2746
3057
|
shown: dropped(board.shown, id),
|
|
2747
|
-
hidden: added(board.hidden, id)
|
|
3058
|
+
hidden: added(board.hidden, id),
|
|
3059
|
+
...order.length === 0 ? {} : { order }
|
|
2748
3060
|
};
|
|
2749
3061
|
}
|
|
2750
3062
|
function withoutPanel(board, id) {
|
|
@@ -2753,71 +3065,15 @@ function withoutPanel(board, id) {
|
|
|
2753
3065
|
hidden: dropped(board.hidden, id)
|
|
2754
3066
|
};
|
|
2755
3067
|
}
|
|
2756
|
-
function withMoved(board, id, index) {
|
|
2757
|
-
const current = [...
|
|
3068
|
+
function withMoved(board, id, index, visible) {
|
|
3069
|
+
const current = visible.includes(id) ? [...visible] : [...visible, id];
|
|
2758
3070
|
const from = current.indexOf(id);
|
|
2759
|
-
const
|
|
2760
|
-
|
|
2761
|
-
if (from === -1) current.splice(to, 0, id);
|
|
2762
|
-
else if (from !== to) current.splice(to, 0, ...current.splice(from, 1));
|
|
2763
|
-
return {
|
|
2764
|
-
shown: current,
|
|
2765
|
-
hidden: dropped(board.hidden, id)
|
|
2766
|
-
};
|
|
2767
|
-
}
|
|
2768
|
-
//#endregion
|
|
2769
|
-
//#region src/shared/api/host/diy-facts.ts
|
|
2770
|
-
function asExported(dir) {
|
|
2771
|
-
try {
|
|
2772
|
-
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
2773
|
-
return (typeof manifest.exports === "object" && manifest.exports !== null ? Object.values(manifest.exports) : []).filter((one) => typeof one === "string");
|
|
2774
|
-
} catch {
|
|
2775
|
-
return [];
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
function mtimeOf(file) {
|
|
2779
|
-
try {
|
|
2780
|
-
return statSync(file).mtimeMs;
|
|
2781
|
-
} catch {
|
|
2782
|
-
return;
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
function newestMtime(dir) {
|
|
2786
|
-
let newest;
|
|
2787
|
-
const walk = (at) => {
|
|
2788
|
-
let entries;
|
|
2789
|
-
try {
|
|
2790
|
-
entries = readdirSync(at, { withFileTypes: true });
|
|
2791
|
-
} catch {
|
|
2792
|
-
return;
|
|
2793
|
-
}
|
|
2794
|
-
for (const entry of entries) {
|
|
2795
|
-
const full = join(at, entry.name);
|
|
2796
|
-
if (entry.isDirectory()) walk(full);
|
|
2797
|
-
else if (entry.isFile()) {
|
|
2798
|
-
const stamp = mtimeOf(full);
|
|
2799
|
-
if (stamp !== void 0 && (newest === void 0 || stamp > newest)) newest = stamp;
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
};
|
|
2803
|
-
walk(dir);
|
|
2804
|
-
return newest;
|
|
2805
|
-
}
|
|
2806
|
-
function moduleBuildFacts(dir) {
|
|
2807
|
-
const artifacts = asExported(dir).map((rel) => ({
|
|
2808
|
-
path: rel,
|
|
2809
|
-
mtime: mtimeOf(join(dir, rel))
|
|
2810
|
-
}));
|
|
2811
|
-
const present = artifacts.filter((one) => one.mtime !== void 0);
|
|
2812
|
-
const oldestBuilt = present.reduce((min, one) => min === void 0 || one.mtime < min ? one.mtime : min, void 0);
|
|
2813
|
-
const newestSource = newestMtime(join(dir, "src"));
|
|
3071
|
+
const to = Math.max(0, Math.min(current.length - 1, Math.trunc(index)));
|
|
3072
|
+
if (from !== to) current.splice(to, 0, ...current.splice(from, 1));
|
|
2814
3073
|
return {
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
})),
|
|
2819
|
-
builtAll: present.length === artifacts.length && artifacts.length > 0,
|
|
2820
|
-
stale: newestSource !== void 0 && oldestBuilt !== void 0 && newestSource > oldestBuilt
|
|
3074
|
+
shown: added(board.shown, id),
|
|
3075
|
+
hidden: dropped(board.hidden, id),
|
|
3076
|
+
order: current
|
|
2821
3077
|
};
|
|
2822
3078
|
}
|
|
2823
3079
|
//#endregion
|
|
@@ -3040,10 +3296,169 @@ function installedPluginsOf(dir = vendorPluginsDir()) {
|
|
|
3040
3296
|
}));
|
|
3041
3297
|
}
|
|
3042
3298
|
//#endregion
|
|
3299
|
+
//#region src/shared/api/host/diy-facts.ts
|
|
3300
|
+
function asExported(dir) {
|
|
3301
|
+
try {
|
|
3302
|
+
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
3303
|
+
return (typeof manifest.exports === "object" && manifest.exports !== null ? Object.values(manifest.exports) : []).filter((one) => typeof one === "string");
|
|
3304
|
+
} catch {
|
|
3305
|
+
return [];
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
function mtimeOf(file) {
|
|
3309
|
+
try {
|
|
3310
|
+
return statSync(file).mtimeMs;
|
|
3311
|
+
} catch {
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
function newestMtime(dir) {
|
|
3316
|
+
let newest;
|
|
3317
|
+
const walk = (at) => {
|
|
3318
|
+
let entries;
|
|
3319
|
+
try {
|
|
3320
|
+
entries = readdirSync(at, { withFileTypes: true });
|
|
3321
|
+
} catch {
|
|
3322
|
+
return;
|
|
3323
|
+
}
|
|
3324
|
+
for (const entry of entries) {
|
|
3325
|
+
const full = join(at, entry.name);
|
|
3326
|
+
if (entry.isDirectory()) walk(full);
|
|
3327
|
+
else if (entry.isFile()) {
|
|
3328
|
+
const stamp = mtimeOf(full);
|
|
3329
|
+
if (stamp !== void 0 && (newest === void 0 || stamp > newest)) newest = stamp;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
};
|
|
3333
|
+
walk(dir);
|
|
3334
|
+
return newest;
|
|
3335
|
+
}
|
|
3336
|
+
function moduleBuildFacts(dir) {
|
|
3337
|
+
const artifacts = asExported(dir).map((rel) => ({
|
|
3338
|
+
path: rel,
|
|
3339
|
+
mtime: mtimeOf(join(dir, rel))
|
|
3340
|
+
}));
|
|
3341
|
+
const present = artifacts.filter((one) => one.mtime !== void 0);
|
|
3342
|
+
const oldestBuilt = present.reduce((min, one) => min === void 0 || one.mtime < min ? one.mtime : min, void 0);
|
|
3343
|
+
const newestSource = newestMtime(join(dir, "src"));
|
|
3344
|
+
return {
|
|
3345
|
+
artifacts: artifacts.map((one) => ({
|
|
3346
|
+
path: one.path,
|
|
3347
|
+
at: one.mtime === void 0 ? null : new Date(one.mtime).toISOString()
|
|
3348
|
+
})),
|
|
3349
|
+
builtAll: present.length === artifacts.length && artifacts.length > 0,
|
|
3350
|
+
stale: newestSource !== void 0 && oldestBuilt !== void 0 && newestSource > oldestBuilt
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
//#endregion
|
|
3354
|
+
//#region src/modules/panels/model/diy-report.ts
|
|
3355
|
+
function healthOf(options) {
|
|
3356
|
+
const { registry, unit, installed } = options;
|
|
3357
|
+
const refused = registry?.refusals().find((one) => one.unit === unit);
|
|
3358
|
+
if (refused !== void 0) return {
|
|
3359
|
+
state: "failed",
|
|
3360
|
+
reason: refused.reasons.join(";")
|
|
3361
|
+
};
|
|
3362
|
+
const failed = registry?.failureOf(unit);
|
|
3363
|
+
if (failed !== void 0) return {
|
|
3364
|
+
state: "failed",
|
|
3365
|
+
reason: failed
|
|
3366
|
+
};
|
|
3367
|
+
if (registry?.all().some((one) => one.unit === unit) === true) return { state: "ok" };
|
|
3368
|
+
return {
|
|
3369
|
+
state: "absent",
|
|
3370
|
+
reason: installed ? "工作台还没读到它的声明 —— 新装的模块要重启一次工作台" : "还没装"
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
function healthText(health) {
|
|
3374
|
+
if (health.state === "ok") return "是";
|
|
3375
|
+
if (health.state === "failed") return `不是 —— ${health.reason ?? "(没给原因)"}`;
|
|
3376
|
+
return `还没有(${health.reason ?? "还不知道"})`;
|
|
3377
|
+
}
|
|
3378
|
+
function ageText(at, now) {
|
|
3379
|
+
const seconds = Math.max(0, Math.round((now - at) / 1e3));
|
|
3380
|
+
if (seconds < 10) return "刚刚";
|
|
3381
|
+
if (seconds < 60) return `${String(seconds)} 秒前`;
|
|
3382
|
+
const minutes = Math.round(seconds / 60);
|
|
3383
|
+
if (minutes < 60) return `${String(minutes)} 分钟前`;
|
|
3384
|
+
return `${String(Math.round(minutes / 60))} 小时前`;
|
|
3385
|
+
}
|
|
3386
|
+
function stampOf(at) {
|
|
3387
|
+
return `${new Date(at).toISOString().slice(11, 19)}Z`;
|
|
3388
|
+
}
|
|
3389
|
+
function readText$1(read, now) {
|
|
3390
|
+
const when = `${stampOf(read.at)} · ${ageText(read.at, now)}`;
|
|
3391
|
+
if (read.phase === "ready") return `${read.route} 读到了(${when})`;
|
|
3392
|
+
if (read.phase === "loading") return `${read.route} 正在读(${when})`;
|
|
3393
|
+
if (read.phase === "idle") return `${read.route} 没在取数(${when})`;
|
|
3394
|
+
const code = read.error?.code ?? "unknown";
|
|
3395
|
+
const message = read.error?.message ?? "";
|
|
3396
|
+
return `${read.route} 读不到(${code})${message === "" ? "" : `:${message}`}(${when})`;
|
|
3397
|
+
}
|
|
3398
|
+
function renderPanel(options) {
|
|
3399
|
+
const { packagesDir, workspace, rows, board, at, now } = options;
|
|
3400
|
+
const head = `工坊:${packagesDir}\n这个工作区:${workspace ?? "(会话不在任何工作区上)"}`;
|
|
3401
|
+
const note = at === 0 ? "\n还没有一条面板运行态上报 —— 面板上一个取数 / 渲染都没发生过:要么这个工作区的面板上没开卡片,要么用户那一页还是刷新前的旧版本(上报要页面刷一次才带上)。" : `\n面板上有 ${String(board.length)} 张卡|运行态来自用户那一页(最后一次上报 ${stampOf(at)} · ${ageText(at, now)})`;
|
|
3402
|
+
const body = rows.map((row) => {
|
|
3403
|
+
const title = `—— ${row.unit}${row.name === "" ? "" : `(${row.name})`} · ${healthText(row.health)}`;
|
|
3404
|
+
const report = row.report;
|
|
3405
|
+
if (report === void 0) return [title, ` 没上报:卡片没渲染出来,或者用户那一页还没刷新到带这份补丁的版本`].join("\n");
|
|
3406
|
+
const reads = report.cards.slice(0, 6).map((one) => ` 数据 · ${readText$1(one, now)}`);
|
|
3407
|
+
const more = report.cards.length > 6 ? [` 数据 · …还有 ${String(report.cards.length - 6)} 条`] : [];
|
|
3408
|
+
const crash = report.crash === void 0 ? " 崩过:没有" : ` 崩过:是 —— ${report.crash.reason}(${stampOf(report.crash.at)} · 共 ${String(report.crash.count)} 次)`;
|
|
3409
|
+
return [
|
|
3410
|
+
title,
|
|
3411
|
+
...reads,
|
|
3412
|
+
...more,
|
|
3413
|
+
crash
|
|
3414
|
+
].join("\n");
|
|
3415
|
+
});
|
|
3416
|
+
return body.length === 0 ? `${head}${note}` : `${head}${note}\n${body.join("\n")}`;
|
|
3417
|
+
}
|
|
3418
|
+
function statusOf(options) {
|
|
3419
|
+
const { dir, unit, name, installed, known, open, health } = options;
|
|
3420
|
+
const facts = moduleBuildFacts(dir);
|
|
3421
|
+
const next = health.state === "failed" ? `它起不来:${health.reason ?? "(没给原因)"} —— 先把这一条修掉(改模块源码 → build;改了 package.json / cordis.patch.yml 要重启工作台),别去重装` : !installed ? "装:action=install(第一次,会让用户点头一次)" : !known ? "面板还不认得这个单元 —— 重启一次工作台(新装的模块要重启才注册进面板),重启后再 action=open" : !open ? "上板:action=open(只上板,不重装)" : !facts.builtAll ? "还没构建:pnpm exec dsh-wb-module build" : facts.stale ? "源码比产物新:pnpm exec dsh-wb-module build(宿主侧秒级热换;改了 client.js 刷新页面)" : "都在生效:改源码 → build(宿主侧秒级热换;改了 client.js 刷新页面),不要再装一遍";
|
|
3422
|
+
return {
|
|
3423
|
+
unit,
|
|
3424
|
+
name,
|
|
3425
|
+
dir,
|
|
3426
|
+
installed,
|
|
3427
|
+
known,
|
|
3428
|
+
open,
|
|
3429
|
+
artifacts: facts.artifacts,
|
|
3430
|
+
stale: facts.stale,
|
|
3431
|
+
health,
|
|
3432
|
+
next
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
function renderStatus(packagesDir, lines) {
|
|
3436
|
+
if (lines.length === 0) return renderList(packagesDir, []);
|
|
3437
|
+
return `工坊:${packagesDir}\n${lines.map((one) => {
|
|
3438
|
+
const head = `—— ${one.unit}(${one.name})`;
|
|
3439
|
+
const facts = ` 装:${one.installed ? "是" : "没有"} · 面板认得:${one.known ? "是" : "不是"} · 这个项目上开着:${one.open ? "是" : "没有"}`;
|
|
3440
|
+
const artifacts = one.artifacts.length === 0 ? " 产物:没有" : ` 产物:${one.artifacts.map((item) => `${item.path}${item.at === null ? "(缺)" : ` ${item.at.slice(11, 19)}Z`}`).join(" · ")} · 源码比产物新:${one.stale ? "是" : "没有"}`;
|
|
3441
|
+
return [
|
|
3442
|
+
head,
|
|
3443
|
+
facts,
|
|
3444
|
+
` 起得来:${healthText(one.health)}`,
|
|
3445
|
+
artifacts,
|
|
3446
|
+
` 下一步:${one.next}`
|
|
3447
|
+
].join("\n");
|
|
3448
|
+
}).join("\n")}`;
|
|
3449
|
+
}
|
|
3450
|
+
function renderList(packagesDir, modules) {
|
|
3451
|
+
if (modules.length === 0) return `工坊里还没有模块(${packagesDir})—— 在里面造一个:pnpm exec dsh-wb-module init <名字> --dir <工坊>/packages/module-<名字> --template diy`;
|
|
3452
|
+
const lines = modules.map((one) => `${one.unit} · ${one.installed ? "已装" : "没装"} · ${one.name}` + (one.description === "" ? "" : `\n${" ".repeat(one.unit.length)} ${one.description}`));
|
|
3453
|
+
const footer = modules.filter((one) => !one.installed).length === 0 ? "都已装过 —— **不要再装**:改源码 + dsh-wb-module build(宿主侧秒级热换;改了 client.js 刷新一下页面)。" : "装:workbench_diy(action=install, unit=<上面那个 unit>)";
|
|
3454
|
+
return `工坊:${packagesDir}\n${lines.join("\n")}\n\n${footer}`;
|
|
3455
|
+
}
|
|
3456
|
+
//#endregion
|
|
3043
3457
|
//#region src/modules/panels/model/diy-tools.ts
|
|
3044
3458
|
var ACTIONS$2 = [
|
|
3045
3459
|
"list",
|
|
3046
3460
|
"status",
|
|
3461
|
+
"panel",
|
|
3047
3462
|
"probe",
|
|
3048
3463
|
"recon",
|
|
3049
3464
|
"install",
|
|
@@ -3099,33 +3514,6 @@ function verdictOf$1(change) {
|
|
|
3099
3514
|
const tail = (change.packageResult?.output ?? "").trim().split("\n").slice(-3).join(" | ");
|
|
3100
3515
|
return `没装上(${change.errorCode ?? "failed"})${tail === "" ? "" : `:${tail}`}`;
|
|
3101
3516
|
}
|
|
3102
|
-
function statusOf(options) {
|
|
3103
|
-
const { dir, unit, name, installed, known, open } = options;
|
|
3104
|
-
const facts = moduleBuildFacts(dir);
|
|
3105
|
-
const next = !installed ? "装:action=install(第一次,会让用户点头一次)" : !known ? "面板还不认得这个单元 —— 重启一次工作台(新装的模块要重启才注册进面板),重启后再 action=open" : !open ? "上板:action=open(只上板,不重装)" : !facts.builtAll ? "还没构建:pnpm exec dsh-wb-module build" : facts.stale ? "源码比产物新:pnpm exec dsh-wb-module build(宿主侧秒级热换;改了 client.js 刷新页面)" : "都在生效:改源码 → build(宿主侧秒级热换;改了 client.js 刷新页面),不要再装一遍";
|
|
3106
|
-
return {
|
|
3107
|
-
unit,
|
|
3108
|
-
name,
|
|
3109
|
-
dir,
|
|
3110
|
-
installed,
|
|
3111
|
-
known,
|
|
3112
|
-
open,
|
|
3113
|
-
artifacts: facts.artifacts,
|
|
3114
|
-
stale: facts.stale,
|
|
3115
|
-
next
|
|
3116
|
-
};
|
|
3117
|
-
}
|
|
3118
|
-
function renderStatus(packagesDir, lines) {
|
|
3119
|
-
if (lines.length === 0) return renderList(packagesDir, []);
|
|
3120
|
-
return `工坊:${packagesDir}\n${lines.map((one) => {
|
|
3121
|
-
return [
|
|
3122
|
-
`—— ${one.unit}(${one.name})`,
|
|
3123
|
-
` 装:${one.installed ? "是" : "没有"} · 面板认得:${one.known ? "是" : "不是"} · 这个项目上开着:${one.open ? "是" : "没有"}`,
|
|
3124
|
-
one.artifacts.length === 0 ? " 产物:没有" : ` 产物:${one.artifacts.map((item) => `${item.path}${item.at === null ? "(缺)" : ` ${item.at.slice(11, 19)}Z`}`).join(" · ")} · 源码比产物新:${one.stale ? "是" : "没有"}`,
|
|
3125
|
-
` 下一步:${one.next}`
|
|
3126
|
-
].join("\n");
|
|
3127
|
-
}).join("\n")}`;
|
|
3128
|
-
}
|
|
3129
3517
|
function renderProbe(out) {
|
|
3130
3518
|
if (out.ok) return `读到了 —— 这就是那条读的返回值,把它固化成模块宿主半侧的那一条:\n${(JSON.stringify(out.value, null, 2) ?? "undefined").slice(0, 2e3)}`;
|
|
3131
3519
|
const lines = [`没读到(${out.code}):${out.message}`];
|
|
@@ -3146,12 +3534,6 @@ function renderFind(out) {
|
|
|
3146
3534
|
const more = out.entries.length > 20 ? `\n… 还有 ${String(out.entries.length - 20)} 个` : "";
|
|
3147
3535
|
return `找到了 ${String(out.count)} 个元素 —— 每行的单元格按换行拼在 text 里;把它固化成模块宿主半侧的那一条(host 的 browserFind + 切格 + 按列序取值):\n${head.join("\n")}${more}`;
|
|
3148
3536
|
}
|
|
3149
|
-
function renderList(packagesDir, modules) {
|
|
3150
|
-
if (modules.length === 0) return `工坊里还没有模块(${packagesDir})—— 在里面造一个:pnpm exec dsh-wb-module init <名字> --dir <工坊>/packages/module-<名字> --template diy`;
|
|
3151
|
-
const lines = modules.map((one) => `${one.unit} · ${one.installed ? "已装" : "没装"} · ${one.name}` + (one.description === "" ? "" : `\n${" ".repeat(one.unit.length)} ${one.description}`));
|
|
3152
|
-
const footer = modules.filter((one) => !one.installed).length === 0 ? "都已装过 —— **不要再装**:改源码 + dsh-wb-module build(宿主侧秒级热换;改了 client.js 刷新一下页面)。" : "装:workbench_diy(action=install, unit=<上面那个 unit>)";
|
|
3153
|
-
return `工坊:${packagesDir}\n${lines.join("\n")}\n\n${footer}`;
|
|
3154
|
-
}
|
|
3155
3537
|
function showOrReason(panels, unit, workspace) {
|
|
3156
3538
|
try {
|
|
3157
3539
|
panels.show(unit, workspace);
|
|
@@ -3171,18 +3553,18 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
3171
3553
|
}
|
|
3172
3554
|
const tool = {
|
|
3173
3555
|
name: "workbench_diy",
|
|
3174
|
-
description: "看 DIY 工坊里的模块现在走到哪一步,或者把它们装上 / 上板。只认工坊里的模块(不是市场 —— 装别的东西用 workbench_modules)。status
|
|
3175
|
-
parameters: {
|
|
3556
|
+
description: "看 DIY 工坊里的模块现在走到哪一步,或者把它们装上 / 上板。只认工坊里的模块(不是市场 —— 装别的东西用 workbench_modules)。status 会把六件事一次说清(装了没 / 面板认得没 / 这个项目上开着没 / **起得来没** / 产物齐不齐 / 源码是否比产物新)并给出下一步,所以别自己去拼这些状态、也别反复 install。panel 是**面板上现在究竟是什么样**:这个工作区开了哪几张卡、每张最后是读到了还是读不到(带宿主回的原文)、有没有崩过 —— 用户说\"卡片不对 / 一片空白 / 一直转圈\"就先问它,别靠猜、也别让他截图给你。recon 是**先看有没有现成的**:拿一个关键词(站点名 / 系统名 / 域名)去 opencli 自带的那份目录里找,回它有哪些命令、要不要用户的 Chrome 登录;都没有就老实说,并给出\"写 adapter\"或\"探一条读\"两条路。用户不知道 opencli 是什么 —— 这一步别问他,自己查。probe 是**探一条读**:用用户浏览器里现有的登录去读一次,把结果与失败码归一(两拍、重试、平台码映射都在里面),探明之后再把它固化成模块宿主半侧的那一条。两种读法二选一:script(页面里跑一段回 JSON 的脚本,读接口)或 css(给一个选择器,读 DOM 那一行 —— 页面里没有接口时用这个,别写 document.querySelectorAll)。装是这台机器上的持久变更,要用户点头;**一辈子只装一次**:之后改源码只需 dsh-wb-module build(宿主侧秒级热换;改了 client.js 刷新一下页面),不要再调它。",
|
|
3557
|
+
parameters: withApprovalNote({
|
|
3176
3558
|
type: "object",
|
|
3177
3559
|
properties: {
|
|
3178
3560
|
action: {
|
|
3179
3561
|
type: "string",
|
|
3180
|
-
description: "list=工坊里有哪些模块(默认);status=某个模块现在走到哪一步、下一步做什么;probe=在用户浏览器里探一条读;recon=先查 opencli 现成的能力(自带 adapter / 本机插件);open=只把它在这个项目上板(不装);install=装上并上板(第一次、会让用户点头)。",
|
|
3562
|
+
description: "list=工坊里有哪些模块(默认);status=某个模块现在走到哪一步、下一步做什么;panel=这个工作区的面板上现在是什么样(谁读到了 / 谁读不到 / 谁崩过);probe=在用户浏览器里探一条读;recon=先查 opencli 现成的能力(自带 adapter / 本机插件);open=只把它在这个项目上板(不装);install=装上并上板(第一次、会让用户点头)。",
|
|
3181
3563
|
enum: [...ACTIONS$2]
|
|
3182
3564
|
},
|
|
3183
3565
|
unit: {
|
|
3184
3566
|
type: "string",
|
|
3185
|
-
description: "status / open / install 填:工坊里那个模块的单元 id(list 会给)。"
|
|
3567
|
+
description: "status / panel / open / install 填:工坊里那个模块的单元 id(list 会给)。"
|
|
3186
3568
|
},
|
|
3187
3569
|
script: {
|
|
3188
3570
|
type: "string",
|
|
@@ -3223,7 +3605,7 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
3223
3605
|
},
|
|
3224
3606
|
required: ["action"],
|
|
3225
3607
|
additionalProperties: false
|
|
3226
|
-
},
|
|
3608
|
+
}, { required: true }),
|
|
3227
3609
|
output: {
|
|
3228
3610
|
schema: OUTPUT_SCHEMA$2,
|
|
3229
3611
|
render: (_args, value) => {
|
|
@@ -3243,7 +3625,10 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
3243
3625
|
const session = sessionWorkspace(exec);
|
|
3244
3626
|
if (!inWorkshopSession(ctx, session)) throw new Error(`这把工具只在 DIY 工坊里用:当前会话不在工坊里(这个会话的工作区 ${session ?? "(不知道)"})—— 普通项目里装模块用 workbench_modules,工坊的入口是左栏那一行「工作台DIY」`);
|
|
3245
3627
|
const workspace = session;
|
|
3246
|
-
const
|
|
3628
|
+
const installed = profileModules(profileDirOf(ctx)).map((row) => row.unit);
|
|
3629
|
+
const modules = workshopModules(packagesDir, installed);
|
|
3630
|
+
const registry = ctx.get(UNITS_SERVICE);
|
|
3631
|
+
const panelState = ctx.get(PANEL_STATE_SERVICE);
|
|
3247
3632
|
if (action === "list") return plainJson({
|
|
3248
3633
|
workshop: packagesDir,
|
|
3249
3634
|
modules,
|
|
@@ -3262,7 +3647,12 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
3262
3647
|
name: one.name,
|
|
3263
3648
|
installed: one.installed,
|
|
3264
3649
|
known: panelEntries.some((entry) => entry.id === one.unit),
|
|
3265
|
-
open: panels.isEnabled(one.unit, workspace)
|
|
3650
|
+
open: panels.isEnabled(one.unit, workspace),
|
|
3651
|
+
health: healthOf({
|
|
3652
|
+
registry,
|
|
3653
|
+
unit: one.unit,
|
|
3654
|
+
installed: one.installed
|
|
3655
|
+
})
|
|
3266
3656
|
});
|
|
3267
3657
|
if (action === "status") {
|
|
3268
3658
|
const wanted = asText$1(args.unit);
|
|
@@ -3275,6 +3665,49 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
3275
3665
|
text: renderStatus(packagesDir, rows)
|
|
3276
3666
|
});
|
|
3277
3667
|
}
|
|
3668
|
+
if (action === "panel") {
|
|
3669
|
+
const snapshot = panelState?.snapshot(workspace) ?? {
|
|
3670
|
+
workspace,
|
|
3671
|
+
at: 0,
|
|
3672
|
+
units: []
|
|
3673
|
+
};
|
|
3674
|
+
const wanted = asText$1(args.unit);
|
|
3675
|
+
const board = panelEntries.filter((entry) => entry.shown && entry.hasCard).map((entry) => entry.id);
|
|
3676
|
+
const nameOf = new Map(modules.map((one) => [one.unit, one.name]));
|
|
3677
|
+
const reported = new Map(snapshot.units.map((one) => [one.unit, one]));
|
|
3678
|
+
const rows = [.../* @__PURE__ */ new Set([
|
|
3679
|
+
...board,
|
|
3680
|
+
...reported.keys(),
|
|
3681
|
+
...nameOf.keys()
|
|
3682
|
+
])].filter((unit) => wanted === "" || unit === wanted).map((unit) => {
|
|
3683
|
+
const report = reported.get(unit);
|
|
3684
|
+
return {
|
|
3685
|
+
unit,
|
|
3686
|
+
name: nameOf.get(unit) ?? "",
|
|
3687
|
+
health: healthOf({
|
|
3688
|
+
registry,
|
|
3689
|
+
unit,
|
|
3690
|
+
installed: installed.includes(unit)
|
|
3691
|
+
}),
|
|
3692
|
+
...report === void 0 ? {} : { report }
|
|
3693
|
+
};
|
|
3694
|
+
});
|
|
3695
|
+
return plainJson({
|
|
3696
|
+
workshop: packagesDir,
|
|
3697
|
+
workspace,
|
|
3698
|
+
board,
|
|
3699
|
+
report: snapshot,
|
|
3700
|
+
panels: rows,
|
|
3701
|
+
text: renderPanel({
|
|
3702
|
+
packagesDir,
|
|
3703
|
+
workspace,
|
|
3704
|
+
rows,
|
|
3705
|
+
board,
|
|
3706
|
+
at: snapshot.at,
|
|
3707
|
+
now: Date.now()
|
|
3708
|
+
})
|
|
3709
|
+
});
|
|
3710
|
+
}
|
|
3278
3711
|
if (action === "recon") {
|
|
3279
3712
|
const keywords = keywordsOf(asText$1(args.keyword), asText$1(args.url));
|
|
3280
3713
|
if (keywords.length === 0) throw new Error("recon 要一个关键词(站点名 / 系统名 / 域名都行):先拿它去 opencli 自带的那份目录里找有没有现成的能力,别一上来就自己写 adapter;用户不知道 opencli 是什么,这一步别问他。");
|
|
@@ -4206,7 +4639,7 @@ async function retryAfterRegistryFix(deps, manager, pkg, change) {
|
|
|
4206
4639
|
function createMarketService(options) {
|
|
4207
4640
|
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, ledger, references } = options;
|
|
4208
4641
|
const command = options.command ?? "opencli";
|
|
4209
|
-
const catalog = () =>
|
|
4642
|
+
const catalog = () => manifestsNow?.() ?? manifests;
|
|
4210
4643
|
function requireEntry(unit) {
|
|
4211
4644
|
const manifest = catalog().find((item) => item.id === unit);
|
|
4212
4645
|
if (manifest === void 0) throw new Error(`市场里没有这个模块:${unit}(可安装:${catalog().map((item) => item.id).join(", ") || "(空)"})`);
|
|
@@ -4749,8 +5182,8 @@ function registerMarketTools(ctx, market) {
|
|
|
4749
5182
|
}
|
|
4750
5183
|
const tool = {
|
|
4751
5184
|
name: "workbench_modules",
|
|
4752
|
-
description: "看 / 装 / 升 / 卸工作台的面板模块,以及用户自己装的 opencli 插件。install-bundle 按 spec(包名,可带版本;git / tarball / 本地路径也行)把模块包装进 profile —— 装完会连带装它声明的 opencli 插件;其余 action 按模块 id 动它要的外部供给;update-
|
|
4753
|
-
parameters: {
|
|
5185
|
+
description: "看 / 装 / 升 / 卸工作台的面板模块,以及用户自己装的 opencli 插件。install-bundle 按 spec(包名,可带版本;git / tarball / 本地路径也行)把模块包装进 profile —— 装完会连带装它声明的 opencli 插件;其余 action 按模块 id 动它要的外部供给;update-workbench 把工作台自己更新到源上的新版(客户端与宿主一般会自己换上;真要重启时结果里会说,重启由用户来,别自己重启)。卸载只动工作台运行时,不影响终端那套;共用依赖只有升级。",
|
|
5186
|
+
parameters: withApprovalNote({
|
|
4754
5187
|
type: "object",
|
|
4755
5188
|
properties: {
|
|
4756
5189
|
action: {
|
|
@@ -4777,7 +5210,7 @@ function registerMarketTools(ctx, market) {
|
|
|
4777
5210
|
},
|
|
4778
5211
|
required: ["action"],
|
|
4779
5212
|
additionalProperties: false
|
|
4780
|
-
},
|
|
5213
|
+
}, { required: true }),
|
|
4781
5214
|
output: {
|
|
4782
5215
|
schema: OUTPUT_SCHEMA$1,
|
|
4783
5216
|
render: (_args, value) => {
|
|
@@ -4988,9 +5421,11 @@ function createPanelsService(options) {
|
|
|
4988
5421
|
return readBoard(folder) ?? EMPTY_BOARD;
|
|
4989
5422
|
}
|
|
4990
5423
|
function writeBoard(folder, next) {
|
|
5424
|
+
const order = next.order ?? [];
|
|
4991
5425
|
folder.write(PANELS_FILE, {
|
|
4992
5426
|
shown: next.shown,
|
|
4993
|
-
hidden: next.hidden
|
|
5427
|
+
hidden: next.hidden,
|
|
5428
|
+
...order.length === 0 ? {} : { order }
|
|
4994
5429
|
});
|
|
4995
5430
|
}
|
|
4996
5431
|
function boardsOf(workspace) {
|
|
@@ -5021,6 +5456,9 @@ function createPanelsService(options) {
|
|
|
5021
5456
|
if (next.length === 0) machine.remove(RELEASED_FILE);
|
|
5022
5457
|
else machine.write(RELEASED_FILE, { ids: next });
|
|
5023
5458
|
}
|
|
5459
|
+
function ownedSet() {
|
|
5460
|
+
return new Set(owned?.() ?? []);
|
|
5461
|
+
}
|
|
5024
5462
|
function notify() {
|
|
5025
5463
|
for (const listener of [...listeners]) try {
|
|
5026
5464
|
listener();
|
|
@@ -5031,7 +5469,7 @@ function createPanelsService(options) {
|
|
|
5031
5469
|
function withPersisted(folder, next, key) {
|
|
5032
5470
|
writeBoard(folder, next);
|
|
5033
5471
|
notify();
|
|
5034
|
-
return resolvePanels(manifests(), boardsOf(key));
|
|
5472
|
+
return resolvePanels(manifests(), boardsOf(key), ownedSet());
|
|
5035
5473
|
}
|
|
5036
5474
|
function takeBackReleased(id) {
|
|
5037
5475
|
const current = readReleased();
|
|
@@ -5051,16 +5489,19 @@ function createPanelsService(options) {
|
|
|
5051
5489
|
function requireSwitchable(id) {
|
|
5052
5490
|
if (pluginIds().has(id)) return;
|
|
5053
5491
|
requireKnown(id);
|
|
5054
|
-
if (
|
|
5492
|
+
if (manifests().find((item) => item.id === id)?.card === null && ownedSet().has(id)) throw new Error(`${id} 没有卡片,不能收起(它是把别的套件放回来的入口)`);
|
|
5055
5493
|
}
|
|
5056
5494
|
function requireWorkspace(workspace) {
|
|
5057
5495
|
const key = workspace?.trim() ?? "";
|
|
5058
5496
|
if (key === "") throw new Error("这个会话不属于任何工作区:面板按工作区存,先打开或新建一个工作区");
|
|
5059
5497
|
return key;
|
|
5060
5498
|
}
|
|
5499
|
+
function visibleOrder(workspace) {
|
|
5500
|
+
return resolvePanels(manifests(), boardsOf(workspace), ownedSet()).filter((entry) => entry.shown && entry.hasCard).map((entry) => entry.id);
|
|
5501
|
+
}
|
|
5061
5502
|
return {
|
|
5062
5503
|
list(workspace, target) {
|
|
5063
|
-
return resolvePanels(manifests(), target === "machine" ? boardsOf(null) : boardsOf(workspace));
|
|
5504
|
+
return resolvePanels(manifests(), target === "machine" ? boardsOf(null) : boardsOf(workspace), ownedSet());
|
|
5064
5505
|
},
|
|
5065
5506
|
listPlugins(workspace, target) {
|
|
5066
5507
|
return resolvePluginPanels(pluginRefs(), target === "machine" ? boardsOf(null) : boardsOf(workspace));
|
|
@@ -5105,7 +5546,7 @@ function createPanelsService(options) {
|
|
|
5105
5546
|
requireSwitchable(id);
|
|
5106
5547
|
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
5107
5548
|
takeBackReleased(id);
|
|
5108
|
-
return withPersisted(folder, withShown(boardIn(folder), id), key);
|
|
5549
|
+
return withPersisted(folder, withShown(boardIn(folder), id, key === null ? void 0 : visibleOrder(key)), key);
|
|
5109
5550
|
},
|
|
5110
5551
|
hide(id, workspace, target) {
|
|
5111
5552
|
requireSwitchable(id);
|
|
@@ -5114,10 +5555,12 @@ function createPanelsService(options) {
|
|
|
5114
5555
|
},
|
|
5115
5556
|
move(id, index, workspace, target) {
|
|
5116
5557
|
requireSwitchable(id);
|
|
5117
|
-
const
|
|
5558
|
+
const scope = target ?? "workspace";
|
|
5559
|
+
if (scope === "machine") throw new Error("机器级默认板没有顺序这一说:它只回答\"默认开哪几行\"。顺序是每个工作区自己的 —— 要排顺序就用默认作用域的那一次 move(或面板里拖动)。");
|
|
5560
|
+
const { folder, key } = targetOf(scope, workspace);
|
|
5118
5561
|
const current = boardIn(folder);
|
|
5119
5562
|
if (!current.shown.includes(id)) takeBackReleased(id);
|
|
5120
|
-
return withPersisted(folder, withMoved(current, id, index), key);
|
|
5563
|
+
return withPersisted(folder, withMoved(current, id, index, visibleOrder(key)), key);
|
|
5121
5564
|
},
|
|
5122
5565
|
reset(workspace, target) {
|
|
5123
5566
|
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
@@ -5137,7 +5580,7 @@ function createPanelsService(options) {
|
|
|
5137
5580
|
if (id === void 0) {
|
|
5138
5581
|
folder.remove(PANELS_FILE);
|
|
5139
5582
|
notify();
|
|
5140
|
-
return resolvePanels(manifests(), boardsOf(key));
|
|
5583
|
+
return resolvePanels(manifests(), boardsOf(key), ownedSet());
|
|
5141
5584
|
}
|
|
5142
5585
|
return withPersisted(folder, withoutPanel(boardIn(folder), id), key);
|
|
5143
5586
|
}
|
|
@@ -5318,13 +5761,13 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
5318
5761
|
}
|
|
5319
5762
|
const tool = {
|
|
5320
5763
|
name: "workbench_panels",
|
|
5321
|
-
description: "看或调右侧面板:启用哪些套件、什么顺序。启用/收起 = 卡片与 agent 工具一起上下线(别的项目不受影响,不删数据)。收起是\"这个工作区明确不要它\",连那些在机器级默认板上开着的套件也照样在这个工作区收起来,之后 show
|
|
5322
|
-
parameters: {
|
|
5764
|
+
description: "看或调右侧面板:启用哪些套件、什么顺序。启用/收起 = 卡片与 agent 工具一起上下线(别的项目不受影响,不删数据)。收起是\"这个工作区明确不要它\",连那些在机器级默认板上开着的套件也照样在这个工作区收起来,之后 show 能放回来。顺序只属于工作区:没排过的按各模块自己声明的次序(开着的在前、关着的跟在后面);show 或 move 过就以那次为准。机器级默认板只回答\"默认开哪几行\",没有顺序这一说。scope=machine 动的是\"机器级默认板\" —— 它只是还没配过的工作区的默认值,已经自己配过的工作区保持自己的选择,不会被它改。在那份板上 hide = 明确写下\"不要它\",这一笔比名单更管用:名单里还列着它也算不要,没提过它的工作区因此看不到它;show 会把这笔划掉、把它放回去。本工作区那份板只记**改过的那几行**(逐行覆盖):没提到的行跟着机器级默认板走,所以那份板以后改了,没改过的工作区会跟着变。",
|
|
5765
|
+
parameters: withApprovalNote({
|
|
5323
5766
|
type: "object",
|
|
5324
5767
|
properties: {
|
|
5325
5768
|
action: {
|
|
5326
5769
|
type: "string",
|
|
5327
|
-
description: "show=这个工作区要它(之前收起的也放回来);hide=这个工作区明确不要它(卡片与工具一起下线,默认在机器上也开着的套件同样收起,别的项目不受影响);move
|
|
5770
|
+
description: "show=这个工作区要它(之前收起的也放回来);hide=这个工作区明确不要它(卡片与工具一起下线,默认在机器上也开着的套件同样收起,别的项目不受影响);move=调这个工作区的顺序(新开一行时它排在最后);reset=清掉这个工作区改过的那些行(改完就全部回到跟着机器级默认板走);follow=把某一行放回跟随(带 id = 只放回那一行;不带 id = 全部放回)。",
|
|
5328
5771
|
enum: [
|
|
5329
5772
|
"list",
|
|
5330
5773
|
"show",
|
|
@@ -5340,17 +5783,17 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
5340
5783
|
},
|
|
5341
5784
|
index: {
|
|
5342
5785
|
type: "integer",
|
|
5343
|
-
description: "move 的位置,0
|
|
5786
|
+
description: "move 的位置,0 基(数的是当前开着的那几行)。"
|
|
5344
5787
|
},
|
|
5345
5788
|
scope: {
|
|
5346
5789
|
type: "string",
|
|
5347
|
-
description: "workspace(默认)=只动当前工作区;machine=动机器级默认板,那是还没配过的工作区的默认值 ——
|
|
5790
|
+
description: "workspace(默认)=只动当前工作区;machine=动机器级默认板,那是还没配过的工作区的默认值 —— 已经自己配过的工作区保持自己的选择,不受它影响。机器级板没有顺序这一说(move 会报错)。",
|
|
5348
5791
|
enum: ["workspace", "machine"]
|
|
5349
5792
|
}
|
|
5350
5793
|
},
|
|
5351
5794
|
required: ["action"],
|
|
5352
5795
|
additionalProperties: false
|
|
5353
|
-
},
|
|
5796
|
+
}, { required: true }),
|
|
5354
5797
|
output: {
|
|
5355
5798
|
schema: OUTPUT_SCHEMA,
|
|
5356
5799
|
render: (_args, value) => {
|
|
@@ -5430,7 +5873,7 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
5430
5873
|
var OWNED = [{
|
|
5431
5874
|
unit: panelsUnit.id,
|
|
5432
5875
|
manifest: panelsUnit,
|
|
5433
|
-
mount({ ctx, logger, manifests, ownedUnits, opencliCommand, probe, store, values }) {
|
|
5876
|
+
mount({ ctx, logger, manifests, ownedUnits, installable, opencliCommand, probe, store, values }) {
|
|
5434
5877
|
const ledger = createPluginLedger({
|
|
5435
5878
|
store,
|
|
5436
5879
|
home: isolatedHome()
|
|
@@ -5445,9 +5888,15 @@ var OWNED = [{
|
|
|
5445
5888
|
ctx.provide(PANELS_SERVICE, panels);
|
|
5446
5889
|
const profileDir = profileDirOf(ctx);
|
|
5447
5890
|
const shell = ctx.get("shell");
|
|
5891
|
+
const moduleManifests = () => {
|
|
5892
|
+
const list = manifests();
|
|
5893
|
+
if (installable === void 0) return list.filter(hasEnvSupply);
|
|
5894
|
+
const ids = new Set(installable());
|
|
5895
|
+
return list.filter((manifest) => ids.has(manifest.id));
|
|
5896
|
+
};
|
|
5448
5897
|
const market = createMarketService({
|
|
5449
|
-
manifests:
|
|
5450
|
-
manifestsNow:
|
|
5898
|
+
manifests: moduleManifests(),
|
|
5899
|
+
manifestsNow: moduleManifests,
|
|
5451
5900
|
...probe === void 0 ? {} : { probe },
|
|
5452
5901
|
shell,
|
|
5453
5902
|
...opencliCommand === void 0 ? {} : { command: opencliCommand },
|
|
@@ -6687,6 +7136,28 @@ function withRunsRoute(result, unit) {
|
|
|
6687
7136
|
}
|
|
6688
7137
|
};
|
|
6689
7138
|
}
|
|
7139
|
+
function notedRegistry(registry, writesOf) {
|
|
7140
|
+
return { register(definition) {
|
|
7141
|
+
if (!writesOf(definition.name)) return registry.register(definition);
|
|
7142
|
+
const noted = withApprovalNote(definition.parameters, { required: true });
|
|
7143
|
+
return registry.register(noted === definition.parameters ? definition : {
|
|
7144
|
+
...definition,
|
|
7145
|
+
parameters: noted
|
|
7146
|
+
});
|
|
7147
|
+
} };
|
|
7148
|
+
}
|
|
7149
|
+
function scopedMountCtx(ctx, registry, writesOf) {
|
|
7150
|
+
const tools = notedRegistry(registry, writesOf);
|
|
7151
|
+
const get = (name) => {
|
|
7152
|
+
const value = ctx.get(name);
|
|
7153
|
+
return name === "tools" && value !== void 0 ? tools : value;
|
|
7154
|
+
};
|
|
7155
|
+
return new Proxy(ctx, { get(target, key) {
|
|
7156
|
+
if (key === "get") return get;
|
|
7157
|
+
const value = Reflect.get(target, key, target);
|
|
7158
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
7159
|
+
} });
|
|
7160
|
+
}
|
|
6690
7161
|
function failureReasonOf(error) {
|
|
6691
7162
|
if (error instanceof Error) return error.message;
|
|
6692
7163
|
return typeof error === "string" ? error : String(error);
|
|
@@ -6830,6 +7301,9 @@ function mountUnits(units, deps) {
|
|
|
6830
7301
|
continue;
|
|
6831
7302
|
}
|
|
6832
7303
|
const unit = host.manifest.id;
|
|
7304
|
+
const tools = deps.ctx.get("tools");
|
|
7305
|
+
const writes = toolUnits(deps.manifests());
|
|
7306
|
+
const mountCtx = tools === void 0 ? deps.ctx : scopedMountCtx(deps.ctx, tools, (name) => writes.get(name)?.writes === true);
|
|
6833
7307
|
const longTasks = deps.longTasks === void 0 ? void 0 : createLongTaskHost({
|
|
6834
7308
|
scope: deps.longTasks.scope,
|
|
6835
7309
|
unit,
|
|
@@ -6839,10 +7313,11 @@ function mountUnits(units, deps) {
|
|
|
6839
7313
|
try {
|
|
6840
7314
|
result = host.face.mount({
|
|
6841
7315
|
manifest: host.manifest,
|
|
6842
|
-
ctx:
|
|
7316
|
+
ctx: mountCtx,
|
|
6843
7317
|
logger: deps.logger,
|
|
6844
7318
|
manifests: deps.manifests,
|
|
6845
7319
|
ownedUnits,
|
|
7320
|
+
...deps.installable === void 0 ? {} : { installable: deps.installable },
|
|
6846
7321
|
...deps.opencliCommand === void 0 ? {} : { opencliCommand: deps.opencliCommand },
|
|
6847
7322
|
...deps.probe === void 0 ? {} : { probe: deps.probe },
|
|
6848
7323
|
...deps.values === void 0 ? {} : { values: deps.values },
|
|
@@ -6907,6 +7382,9 @@ function mountUnits(units, deps) {
|
|
|
6907
7382
|
}
|
|
6908
7383
|
//#endregion
|
|
6909
7384
|
//#region src/shared/api/host/unit-registry.ts
|
|
7385
|
+
function installableUnitsOf(registry) {
|
|
7386
|
+
return registry.all().filter((host) => host.source.kind === "package").map((host) => host.unit);
|
|
7387
|
+
}
|
|
6910
7388
|
function sourceLabel(source) {
|
|
6911
7389
|
if (source.kind === "owned") return "外壳自己";
|
|
6912
7390
|
if (source.kind === "plugin") return "按已装 opencli 插件合成";
|
|
@@ -6916,6 +7394,7 @@ function createUnitRegistry(options) {
|
|
|
6916
7394
|
const refusals = [];
|
|
6917
7395
|
const hosts = [];
|
|
6918
7396
|
const byUnit = /* @__PURE__ */ new Map();
|
|
7397
|
+
const failures = /* @__PURE__ */ new Map();
|
|
6919
7398
|
const listeners = /* @__PURE__ */ new Set();
|
|
6920
7399
|
function notify() {
|
|
6921
7400
|
for (const listener of listeners) listener();
|
|
@@ -6985,7 +7464,14 @@ function createUnitRegistry(options) {
|
|
|
6985
7464
|
subscribe(listener) {
|
|
6986
7465
|
listeners.add(listener);
|
|
6987
7466
|
return () => listeners.delete(listener);
|
|
6988
|
-
}
|
|
7467
|
+
},
|
|
7468
|
+
fail(unit, reason) {
|
|
7469
|
+
failures.set(unit, reason);
|
|
7470
|
+
},
|
|
7471
|
+
recovered(unit) {
|
|
7472
|
+
failures.delete(unit);
|
|
7473
|
+
},
|
|
7474
|
+
failureOf: (unit) => failures.get(unit)
|
|
6989
7475
|
};
|
|
6990
7476
|
}
|
|
6991
7477
|
//#endregion
|
|
@@ -7270,12 +7756,13 @@ function pluginToolParameters(command) {
|
|
|
7270
7756
|
const properties = {};
|
|
7271
7757
|
for (const arg of command.args) properties[arg.name] = propertyOf(arg);
|
|
7272
7758
|
const required = command.args.filter((arg) => arg.required).map((arg) => arg.name);
|
|
7273
|
-
|
|
7759
|
+
const schema = {
|
|
7274
7760
|
type: "object",
|
|
7275
7761
|
properties,
|
|
7276
7762
|
...required.length === 0 ? {} : { required },
|
|
7277
7763
|
additionalProperties: false
|
|
7278
7764
|
};
|
|
7765
|
+
return pluginToolWrites(command) ? withApprovalNote(schema, { required: true }) : schema;
|
|
7279
7766
|
}
|
|
7280
7767
|
function pluginToolDescription(command) {
|
|
7281
7768
|
const described = command.description.trim();
|
|
@@ -7675,6 +8162,7 @@ function seedWorkbenchRuntime(ctx) {
|
|
|
7675
8162
|
function apply(ctx) {
|
|
7676
8163
|
const facts = createFactLog();
|
|
7677
8164
|
const values = createValueHandoff();
|
|
8165
|
+
const panelState = createPanelStateStore();
|
|
7678
8166
|
seedWorkbenchRuntime(ctx);
|
|
7679
8167
|
const opencli = opencliCommandOf(ctx);
|
|
7680
8168
|
const routes = {
|
|
@@ -7687,23 +8175,17 @@ function apply(ctx) {
|
|
|
7687
8175
|
};
|
|
7688
8176
|
const renderers = /* @__PURE__ */ new Map();
|
|
7689
8177
|
const shell = ctx.get("shell");
|
|
7690
|
-
let roster;
|
|
7691
8178
|
const profileDir = profileDirOf(ctx);
|
|
7692
8179
|
const units = createUnitRegistry({
|
|
7693
|
-
declarationOf: (
|
|
7694
|
-
if (roster === void 0) roster = new Map(profileModules(profileDir).map((row) => [row.unit, row.pkg]));
|
|
7695
|
-
const pkg = roster.get(unit);
|
|
7696
|
-
return pkg === void 0 ? void 0 : {
|
|
7697
|
-
pkg,
|
|
7698
|
-
declaration: moduleDeclarationOf(profileDir, pkg)
|
|
7699
|
-
};
|
|
7700
|
-
},
|
|
8180
|
+
declarationOf: createDeclarationLookup(profileDir),
|
|
7701
8181
|
logger: ctx.logger
|
|
7702
8182
|
});
|
|
7703
8183
|
ctx.provide(UNITS_SERVICE, units);
|
|
8184
|
+
ctx.provide(PANEL_STATE_SERVICE, panelState);
|
|
7704
8185
|
routes[UNIT_MANIFESTS_ROUTE] = () => ({
|
|
7705
8186
|
ok: true,
|
|
7706
|
-
manifests: units.manifests()
|
|
8187
|
+
manifests: units.manifests(),
|
|
8188
|
+
installable: installableUnitsOf(units)
|
|
7707
8189
|
});
|
|
7708
8190
|
assembleHostUnits(units);
|
|
7709
8191
|
ctx.inject(["workspaceRegistry"], (scoped) => {
|
|
@@ -7777,6 +8259,7 @@ function apply(ctx) {
|
|
|
7777
8259
|
if (item === void 0) return;
|
|
7778
8260
|
mounted.delete(id);
|
|
7779
8261
|
mountFailures.delete(id);
|
|
8262
|
+
units.recovered(id);
|
|
7780
8263
|
for (const dispose of item.toolDisposers) dispose?.();
|
|
7781
8264
|
item.gateOff?.();
|
|
7782
8265
|
item.plan.disposeLongTasks();
|
|
@@ -7795,6 +8278,7 @@ function apply(ctx) {
|
|
|
7795
8278
|
logger: ctx.logger,
|
|
7796
8279
|
manifests: liveManifests,
|
|
7797
8280
|
ownedUnits: () => units.owned(),
|
|
8281
|
+
installable: () => installableUnitsOf(units),
|
|
7798
8282
|
store,
|
|
7799
8283
|
opencliCommand: opencli.command,
|
|
7800
8284
|
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
@@ -7809,6 +8293,7 @@ function apply(ctx) {
|
|
|
7809
8293
|
reason: failureReasonOf(error),
|
|
7810
8294
|
host
|
|
7811
8295
|
});
|
|
8296
|
+
units.fail(id, failureReasonOf(error));
|
|
7812
8297
|
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(装配期):${failureReasonOf(error)}`);
|
|
7813
8298
|
return;
|
|
7814
8299
|
}
|
|
@@ -7841,6 +8326,7 @@ function apply(ctx) {
|
|
|
7841
8326
|
gateOff: one.gate?.subscribe(() => toolScope.reapply())
|
|
7842
8327
|
});
|
|
7843
8328
|
mountFailures.delete(id);
|
|
8329
|
+
units.recovered(id);
|
|
7844
8330
|
} catch (error) {
|
|
7845
8331
|
for (const route of added) delete routes[route];
|
|
7846
8332
|
for (const kind of one.contextRenderers.keys()) renderers.delete(kind);
|
|
@@ -7852,6 +8338,7 @@ function apply(ctx) {
|
|
|
7852
8338
|
reason: failureReasonOf(error),
|
|
7853
8339
|
host
|
|
7854
8340
|
});
|
|
8341
|
+
units.fail(id, failureReasonOf(error));
|
|
7855
8342
|
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(注册期):${failureReasonOf(error)}`);
|
|
7856
8343
|
}
|
|
7857
8344
|
}
|
|
@@ -7876,7 +8363,10 @@ function apply(ctx) {
|
|
|
7876
8363
|
function sync() {
|
|
7877
8364
|
const present = new Set(units.all().map((one) => one.unit));
|
|
7878
8365
|
for (const id of [...mounted.keys()]) if (!present.has(id)) unmountUnit(id);
|
|
7879
|
-
for (const id of [...mountFailures.keys()]) if (!present.has(id))
|
|
8366
|
+
for (const id of [...mountFailures.keys()]) if (!present.has(id)) {
|
|
8367
|
+
mountFailures.delete(id);
|
|
8368
|
+
units.recovered(id);
|
|
8369
|
+
}
|
|
7880
8370
|
for (const host of units.all()) {
|
|
7881
8371
|
if (mounted.has(host.unit)) continue;
|
|
7882
8372
|
const already = mountFailures.get(host.unit);
|
|
@@ -8023,6 +8513,7 @@ function apply(ctx) {
|
|
|
8023
8513
|
return () => off?.();
|
|
8024
8514
|
}, `${PLUGIN_ID}: 对话引用注入`);
|
|
8025
8515
|
ctx.effect(() => registerFactStream(ctx, facts), `${PLUGIN_ID}: 变更流`);
|
|
8516
|
+
ctx.effect(() => registerPanelState(ctx, panelState), `${PLUGIN_ID}: 面板运行态上报(浏览器半侧报自己的取数与渲染收尾)`);
|
|
8026
8517
|
ctx.effect(() => registerApi(ctx, routes), `${PLUGIN_ID}: HTTP 投影`);
|
|
8027
8518
|
}
|
|
8028
8519
|
//#endregion
|