@dsh-workbench/core 0.1.16 → 0.1.18
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 +665 -127
- package/dist/board.css +31 -0
- package/dist/client/client.body.js +665 -127
- package/dist/client/index.js +665 -127
- package/dist/host/index.js +1510 -263
- package/dist/types/app/client.d.ts +124 -27
- package/dist/types/app/layouts/market/add-module-text.d.ts +2 -0
- package/dist/types/app/layouts/market/use-add-module.d.ts +4 -3
- package/dist/types/app/layouts/market/use-module-versions.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
- package/dist/types/app/layouts/rightbar/__tests__/market-rows-plugins.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/market-freshness.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/market-mock.d.ts +9 -0
- package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +8 -0
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +14 -3
- package/dist/types/app/layouts/rightbar/registry.d.ts +0 -1
- package/dist/types/app/units/__tests__/manifests.test.d.ts +1 -0
- package/dist/types/app/units/fixture-unit-mock.d.ts +3 -3
- package/dist/types/app/units/host.d.ts +2 -2
- package/dist/types/app/units/manifests.d.ts +5 -0
- package/dist/types/app/units/registry.d.ts +10 -2
- package/dist/types/modules/panels/__tests__/market-env.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/market-plugin.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/market-route.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/plugin-actions.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/plugin-ledger.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/plugin-section.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/scope.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/service-release.test.d.ts +1 -0
- package/dist/types/modules/panels/index.d.ts +2 -2
- package/dist/types/modules/panels/model/config.d.ts +27 -9
- package/dist/types/modules/panels/model/market-dispatch.d.ts +9 -0
- package/dist/types/modules/panels/model/market-env.d.ts +20 -0
- package/dist/types/modules/panels/model/market-route.d.ts +26 -0
- package/dist/types/modules/panels/model/market-types.d.ts +71 -0
- package/dist/types/modules/panels/model/market.d.ts +7 -66
- package/dist/types/modules/panels/model/plugin-actions.d.ts +50 -0
- package/dist/types/modules/panels/model/plugin-ledger.d.ts +22 -0
- package/dist/types/modules/panels/model/plugin-name.d.ts +8 -0
- package/dist/types/modules/panels/model/plugin-name.test.d.ts +1 -0
- package/dist/types/modules/panels/model/plugin-section.d.ts +13 -0
- package/dist/types/modules/panels/model/service.d.ts +8 -6
- package/dist/types/shared/api/bundle-install.d.ts +8 -1
- package/dist/types/shared/api/host/__tests__/registry-declaration.test.d.ts +1 -0
- package/dist/types/shared/api/host/__tests__/unit-roster-facts.test.d.ts +1 -0
- package/dist/types/shared/api/host/bundle-routes.d.ts +1 -0
- package/dist/types/shared/api/host/installed-module.d.ts +7 -0
- package/dist/types/shared/api/host/mount-units.d.ts +5 -3
- package/dist/types/shared/api/host/package-content.d.ts +6 -0
- package/dist/types/shared/api/host/package-content.test.d.ts +1 -0
- package/dist/types/shared/api/host/package-kind.d.ts +21 -0
- package/dist/types/shared/api/host/package-kind.test.d.ts +1 -0
- package/dist/types/shared/api/host/package-route.d.ts +23 -0
- package/dist/types/shared/api/host/package-route.test.d.ts +1 -0
- package/dist/types/shared/api/host/registry-declaration.d.ts +23 -0
- package/dist/types/shared/api/host/unit-registry.d.ts +33 -4
- package/dist/types/shared/api/host/unit-roster-facts.d.ts +5 -0
- package/dist/types/shared/api/installed-plugins.d.ts +12 -0
- package/dist/types/shared/api/unit-manifests.d.ts +5 -0
- package/dist/types/shared/api/unit-manifests.test.d.ts +1 -0
- package/dist/types/shared/hooks/bundle-queries.d.ts +2 -0
- package/dist/types/shared/hooks/use-facts.d.ts +1 -0
- package/package.json +2 -2
- package/skills/workbench-module-diy/BUILD.md +5 -5
- package/skills/workbench-module-diy/DESIGN.md +7 -1
- package/skills/workbench-module-diy/REVIEW.md +4 -3
- package/skills/workbench-module-diy/TOOLING.md +2 -2
- package/skills/workbench-module-diy/TOOLS.md +13 -3
package/dist/host/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import fs, { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import path, { delimiter, dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import { spawn } from "node:child_process";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
//#region ../shared/dist/config/constants.js
|
|
@@ -49,6 +50,133 @@ function parseMarkers(text) {
|
|
|
49
50
|
}
|
|
50
51
|
return refs;
|
|
51
52
|
}
|
|
53
|
+
var SUPPORTED_API_VERSIONS = [1];
|
|
54
|
+
var DECLARATION_KEYS = [
|
|
55
|
+
"apiVersion",
|
|
56
|
+
"unit",
|
|
57
|
+
"card",
|
|
58
|
+
"tools",
|
|
59
|
+
"host",
|
|
60
|
+
"env"
|
|
61
|
+
];
|
|
62
|
+
var DECLARATION_REQUIRED_KEYS = [
|
|
63
|
+
"apiVersion",
|
|
64
|
+
"unit",
|
|
65
|
+
"card",
|
|
66
|
+
"tools",
|
|
67
|
+
"host"
|
|
68
|
+
];
|
|
69
|
+
var CARD_KEYS = ["titleKey", "order"];
|
|
70
|
+
var TOOL_KEYS = [
|
|
71
|
+
"name",
|
|
72
|
+
"writes",
|
|
73
|
+
"key"
|
|
74
|
+
];
|
|
75
|
+
var TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
|
|
76
|
+
var HOST_KEYS = ["inject", "optional"];
|
|
77
|
+
function text$1(value) {
|
|
78
|
+
return typeof value === "string" ? value.trim() : "";
|
|
79
|
+
}
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function outsideKeys(value, allowed) {
|
|
84
|
+
return Object.keys(value).filter((key) => !allowed.includes(key));
|
|
85
|
+
}
|
|
86
|
+
function listOf(keys) {
|
|
87
|
+
return keys.map((key) => JSON.stringify(key)).join(" / ");
|
|
88
|
+
}
|
|
89
|
+
function stringArrayProblems(at, value) {
|
|
90
|
+
if (!Array.isArray(value)) return [`${at} 要是一串字符串,现在是 ${JSON.stringify(value)}`];
|
|
91
|
+
return value.flatMap((item, index) => text$1(item) === "" ? [`${at}[${index}] 不是非空字符串:${JSON.stringify(item)}`] : []);
|
|
92
|
+
}
|
|
93
|
+
function declarationProblemsOf(subject, raw) {
|
|
94
|
+
if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
|
|
95
|
+
const version = raw.apiVersion;
|
|
96
|
+
if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v1 起,写 \`"apiVersion": 1\`。缺它 = 外壳不认这个模块(ADR-0038 §2.3)`];
|
|
97
|
+
if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)`];
|
|
98
|
+
const problems = [];
|
|
99
|
+
const unknown = outsideKeys(raw, DECLARATION_KEYS);
|
|
100
|
+
if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v1 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(拼错的键会被外壳**静默忽略**:那句声明等于没写)`);
|
|
101
|
+
for (const key of DECLARATION_REQUIRED_KEYS) if (raw[key] === void 0) problems.push(`${subject}: dsh.workbench 缺 ${key}` + (key === "card" ? "(这个模块没有卡片就写 `\"card\": null`)" : ""));
|
|
102
|
+
const card = raw.card;
|
|
103
|
+
if (card !== void 0 && card !== null) {
|
|
104
|
+
if (!isRecord(card)) problems.push(`${subject}: dsh.workbench.card 要么是对象、要么是 null,现在是 ${JSON.stringify(card)}`);
|
|
105
|
+
else {
|
|
106
|
+
const extra = outsideKeys(card, CARD_KEYS);
|
|
107
|
+
if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
|
|
108
|
+
if (text$1(card.titleKey) === "") problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
|
|
109
|
+
if (typeof card.order !== "number" || !Number.isFinite(card.order)) problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const tools = raw.tools;
|
|
113
|
+
if (tools !== void 0) {
|
|
114
|
+
if (!Array.isArray(tools)) problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
|
|
115
|
+
else {
|
|
116
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
118
|
+
tools.forEach((tool, index) => {
|
|
119
|
+
const at = `dsh.workbench.tools[${index}]`;
|
|
120
|
+
if (!isRecord(tool)) {
|
|
121
|
+
problems.push(`${subject}: ${at} 要是对象,现在是 ${JSON.stringify(tool)}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const extra = outsideKeys(tool, TOOL_KEYS);
|
|
125
|
+
if (extra.length > 0) problems.push(`${subject}: ${at} 里有不认的键 ${listOf(extra)} —— tools 只有 ${listOf(TOOL_KEYS)}`);
|
|
126
|
+
const name = text$1(tool.name);
|
|
127
|
+
if (name === "") problems.push(`${subject}: ${at}.name 要是非空字符串`);
|
|
128
|
+
else if (seen.has(name)) problems.push(`${subject}: ${at}.name「${name}」与前面那条撞了 —— 工具名会进 dsh 的工具表,不许重`);
|
|
129
|
+
else seen.add(name);
|
|
130
|
+
if (typeof tool.writes !== "boolean") problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
|
|
131
|
+
if (tool.key !== void 0) {
|
|
132
|
+
const key = text$1(tool.key);
|
|
133
|
+
if (!TOOL_KEY_SHAPE.test(key)) problems.push(`${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`);
|
|
134
|
+
else if (seenKeys.has(key)) problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
|
|
135
|
+
else seenKeys.add(key);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const host = raw.host;
|
|
141
|
+
if (host !== void 0) {
|
|
142
|
+
if (!isRecord(host)) problems.push(`${subject}: dsh.workbench.host 要是对象,现在是 ${JSON.stringify(host)}`);
|
|
143
|
+
else {
|
|
144
|
+
const extra = outsideKeys(host, HOST_KEYS);
|
|
145
|
+
if (extra.length > 0) problems.push(`${subject}: dsh.workbench.host 里有不认的键 ${listOf(extra)} —— host 只有 ${listOf(HOST_KEYS)}`);
|
|
146
|
+
if (host.inject === void 0) problems.push(`${subject}: dsh.workbench.host.inject 没写 —— 它声明这个模块要外壳的哪几个服务,外壳靠它判"能不能挂"。一个都不要也要写 \`"inject": []\``);
|
|
147
|
+
else problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.inject`, host.inject));
|
|
148
|
+
if (host.optional !== void 0) problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.optional`, host.optional));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (raw.env !== void 0 && !isRecord(raw.env)) problems.push(`${subject}: dsh.workbench.env 要是对象(command / plugins / libs)—— 语义由门禁的 env 判据查,这里只查形状`);
|
|
152
|
+
return problems;
|
|
153
|
+
}
|
|
154
|
+
function readDeclaration(subject, raw) {
|
|
155
|
+
const problems = declarationProblemsOf(subject, raw);
|
|
156
|
+
if (problems.length > 0) return { problems };
|
|
157
|
+
const declaration = raw;
|
|
158
|
+
const card = declaration.card;
|
|
159
|
+
return {
|
|
160
|
+
declaration,
|
|
161
|
+
problems: [],
|
|
162
|
+
manifest: {
|
|
163
|
+
id: text$1(declaration.unit),
|
|
164
|
+
card: card === null ? null : {
|
|
165
|
+
titleKey: text$1(card.titleKey),
|
|
166
|
+
order: card.order
|
|
167
|
+
},
|
|
168
|
+
tools: declaration.tools.map((tool) => ({
|
|
169
|
+
name: text$1(tool.name),
|
|
170
|
+
writes: tool.writes
|
|
171
|
+
})),
|
|
172
|
+
host: {
|
|
173
|
+
inject: [...declaration.host.inject ?? []],
|
|
174
|
+
...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] }
|
|
175
|
+
},
|
|
176
|
+
...declaration.env === void 0 ? {} : { env: declaration.env }
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
52
180
|
function unitFactScope(unit) {
|
|
53
181
|
return `${unit}*`;
|
|
54
182
|
}
|
|
@@ -151,27 +279,27 @@ var PERMISSION_CODES = /* @__PURE__ */ new Set([
|
|
|
151
279
|
"NO_PERMISSION",
|
|
152
280
|
"EACCES"
|
|
153
281
|
]);
|
|
154
|
-
function asRecord$
|
|
282
|
+
function asRecord$9(value) {
|
|
155
283
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
156
284
|
}
|
|
157
|
-
function textOf$
|
|
285
|
+
function textOf$4(value) {
|
|
158
286
|
return typeof value === "string" ? value.trim() : "";
|
|
159
287
|
}
|
|
160
288
|
function codeOf(page) {
|
|
161
|
-
const record = asRecord$
|
|
162
|
-
return record === void 0 ? "" : textOf$
|
|
289
|
+
const record = asRecord$9(page);
|
|
290
|
+
return record === void 0 ? "" : textOf$4(record["code"]);
|
|
163
291
|
}
|
|
164
292
|
function messageOf$1(page) {
|
|
165
|
-
const record = asRecord$
|
|
166
|
-
return record === void 0 ? "" : textOf$
|
|
293
|
+
const record = asRecord$9(page);
|
|
294
|
+
return record === void 0 ? "" : textOf$4(record["message"]);
|
|
167
295
|
}
|
|
168
296
|
function hintOf$1(page) {
|
|
169
|
-
const record = asRecord$
|
|
170
|
-
const hint = record === void 0 ? "" : textOf$
|
|
297
|
+
const record = asRecord$9(page);
|
|
298
|
+
const hint = record === void 0 ? "" : textOf$4(record["hint"]);
|
|
171
299
|
return hint === "" ? void 0 : hint;
|
|
172
300
|
}
|
|
173
301
|
function pageFailed(page) {
|
|
174
|
-
const record = asRecord$
|
|
302
|
+
const record = asRecord$9(page);
|
|
175
303
|
return record !== void 0 && record["ok"] === false;
|
|
176
304
|
}
|
|
177
305
|
function blockerFor(code) {
|
|
@@ -395,41 +523,41 @@ var DEFAULT_SETTLE_MS = 2500;
|
|
|
395
523
|
var URL_TIMEOUT_MS = 8e3;
|
|
396
524
|
var DEFAULT_LIMIT = 50;
|
|
397
525
|
var DEFAULT_TEXT_MAX = 200;
|
|
398
|
-
function asRecord$
|
|
526
|
+
function asRecord$8(value) {
|
|
399
527
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
400
528
|
}
|
|
401
|
-
function textOf$
|
|
529
|
+
function textOf$3(value) {
|
|
402
530
|
return typeof value === "string" ? value.trim() : "";
|
|
403
531
|
}
|
|
404
532
|
function entriesOf(value) {
|
|
405
|
-
const record = asRecord$
|
|
533
|
+
const record = asRecord$8(value);
|
|
406
534
|
const raw = record === void 0 ? void 0 : record["entries"];
|
|
407
535
|
if (!Array.isArray(raw)) return [];
|
|
408
536
|
const entries = [];
|
|
409
537
|
for (const one of raw) {
|
|
410
|
-
const item = asRecord$
|
|
538
|
+
const item = asRecord$8(one);
|
|
411
539
|
if (item === void 0) continue;
|
|
412
540
|
const nth = typeof item["nth"] === "number" ? item["nth"] : entries.length;
|
|
413
541
|
entries.push({
|
|
414
|
-
text: textOf$
|
|
415
|
-
tag: textOf$
|
|
542
|
+
text: textOf$3(item["text"]),
|
|
543
|
+
tag: textOf$3(item["tag"]),
|
|
416
544
|
nth
|
|
417
545
|
});
|
|
418
546
|
}
|
|
419
547
|
return entries;
|
|
420
548
|
}
|
|
421
549
|
function reasonOf(stdout, stderr) {
|
|
422
|
-
const parsed = asRecord$
|
|
423
|
-
const error = parsed === void 0 ? void 0 : asRecord$
|
|
424
|
-
const message = error === void 0 ? "" : textOf$
|
|
550
|
+
const parsed = asRecord$8(parseJsonLoose(stdout));
|
|
551
|
+
const error = parsed === void 0 ? void 0 : asRecord$8(parsed["error"]);
|
|
552
|
+
const message = error === void 0 ? "" : textOf$3(error["message"]);
|
|
425
553
|
if (message !== "") return message;
|
|
426
554
|
const lines = (stderr.trim() === "" ? stdout : stderr).split("\n").map((one) => one.trim()).filter((one) => one !== "");
|
|
427
555
|
return (lines[lines.length - 1] ?? "").slice(-200);
|
|
428
556
|
}
|
|
429
557
|
function hintOf(stdout) {
|
|
430
|
-
const parsed = asRecord$
|
|
431
|
-
const error = parsed === void 0 ? void 0 : asRecord$
|
|
432
|
-
const hint = error === void 0 ? "" : textOf$
|
|
558
|
+
const parsed = asRecord$8(parseJsonLoose(stdout));
|
|
559
|
+
const error = parsed === void 0 ? void 0 : asRecord$8(parsed["error"]);
|
|
560
|
+
const hint = error === void 0 ? "" : textOf$3(error["hint"]);
|
|
433
561
|
return hint === "" ? void 0 : hint;
|
|
434
562
|
}
|
|
435
563
|
function sameOrigin(left, right) {
|
|
@@ -442,8 +570,8 @@ function sameOrigin(left, right) {
|
|
|
442
570
|
function urlOf(stdout) {
|
|
443
571
|
const bare = (stdout.split("\n").map((one) => one.trim()).filter((one) => one !== "")[0] ?? "").replace(/^url:\s*/i, "").trim().replace(/^["']+|["']+$/g, "");
|
|
444
572
|
if (bare.startsWith("{")) {
|
|
445
|
-
const parsed = asRecord$
|
|
446
|
-
const url = parsed === void 0 ? "" : textOf$
|
|
573
|
+
const parsed = asRecord$8(parseJsonLoose(bare));
|
|
574
|
+
const url = parsed === void 0 ? "" : textOf$3(parsed["url"]);
|
|
447
575
|
if (url !== "") return url;
|
|
448
576
|
}
|
|
449
577
|
return bare;
|
|
@@ -666,7 +794,9 @@ var PROVISION_ACTIONS = [
|
|
|
666
794
|
"update-bundle",
|
|
667
795
|
"remove-bundle",
|
|
668
796
|
"cancel-install",
|
|
669
|
-
"update-dependency"
|
|
797
|
+
"update-dependency",
|
|
798
|
+
"update-plugin",
|
|
799
|
+
"uninstall-plugin"
|
|
670
800
|
];
|
|
671
801
|
var DEPENDENCY_ACTIONS = ["update-dependency"];
|
|
672
802
|
function isDependencyAction(action) {
|
|
@@ -676,6 +806,7 @@ function provisionTargetOf(action) {
|
|
|
676
806
|
if (action === "update-dependency") return "id";
|
|
677
807
|
if (action === "install-bundle") return "spec";
|
|
678
808
|
if (action === "remove-bundle") return "name";
|
|
809
|
+
if (action === "update-plugin" || action === "uninstall-plugin") return "name";
|
|
679
810
|
if (action === "cancel-install") return "requestId";
|
|
680
811
|
return "unit";
|
|
681
812
|
}
|
|
@@ -686,7 +817,7 @@ var TARGET_CODE = {
|
|
|
686
817
|
name: "workbench/no-name",
|
|
687
818
|
requestId: "workbench/no-request-id"
|
|
688
819
|
};
|
|
689
|
-
function asRecord$
|
|
820
|
+
function asRecord$7(value) {
|
|
690
821
|
return typeof value === "object" && value !== null ? value : {};
|
|
691
822
|
}
|
|
692
823
|
function stringsOf$2(value) {
|
|
@@ -707,6 +838,10 @@ function requestOf$1(action, value) {
|
|
|
707
838
|
action,
|
|
708
839
|
name: value
|
|
709
840
|
};
|
|
841
|
+
if (action === "update-plugin" || action === "uninstall-plugin") return {
|
|
842
|
+
action,
|
|
843
|
+
name: value
|
|
844
|
+
};
|
|
710
845
|
if (action === "cancel-install") return {
|
|
711
846
|
action,
|
|
712
847
|
requestId: value
|
|
@@ -717,7 +852,7 @@ function requestOf$1(action, value) {
|
|
|
717
852
|
};
|
|
718
853
|
}
|
|
719
854
|
function parseProvisionBody(body) {
|
|
720
|
-
const raw = asRecord$
|
|
855
|
+
const raw = asRecord$7(body);
|
|
721
856
|
const action = raw.action;
|
|
722
857
|
if (typeof action !== "string" || !PROVISION_ACTIONS.includes(action)) return {
|
|
723
858
|
ok: false,
|
|
@@ -1259,6 +1394,9 @@ function createLongTaskHost(options) {
|
|
|
1259
1394
|
function isolatedRoot() {
|
|
1260
1395
|
return `${dshHome()}/workbench/opencli`;
|
|
1261
1396
|
}
|
|
1397
|
+
function isolatedHome() {
|
|
1398
|
+
return `${isolatedRoot()}/home`;
|
|
1399
|
+
}
|
|
1262
1400
|
var OPENCLI_NPM_PACKAGE = "@jackwener/opencli";
|
|
1263
1401
|
function toolchainDir() {
|
|
1264
1402
|
return `${isolatedRoot()}/toolchain`;
|
|
@@ -1304,6 +1442,105 @@ function isolatedInstallCommand() {
|
|
|
1304
1442
|
return `npm i --prefix '${toolchainDir()}' ${OPENCLI_NPM_PACKAGE}@${OPENCLI_PIN} --cache '${npmCacheDir()}'`;
|
|
1305
1443
|
}
|
|
1306
1444
|
//#endregion
|
|
1445
|
+
//#region ../shared/dist/host/plugin-ledger.js
|
|
1446
|
+
var PLUGIN_LEDGER_FILE = "plugins.json";
|
|
1447
|
+
function ledgerKeyOf(entry) {
|
|
1448
|
+
return `${entry.opencliHome}\u0000${entry.package}`;
|
|
1449
|
+
}
|
|
1450
|
+
function textOf$2(value) {
|
|
1451
|
+
if (typeof value !== "string") return void 0;
|
|
1452
|
+
const trimmed = value.trim();
|
|
1453
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
1454
|
+
}
|
|
1455
|
+
function entryOf(value) {
|
|
1456
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1457
|
+
const box = value;
|
|
1458
|
+
const opencliHome = textOf$2(box["opencliHome"]);
|
|
1459
|
+
const pkg = textOf$2(box["package"]);
|
|
1460
|
+
const installedAt = textOf$2(box["installedAt"]);
|
|
1461
|
+
if (opencliHome === void 0 || pkg === void 0 || installedAt === void 0) return void 0;
|
|
1462
|
+
const name = textOf$2(box["name"]);
|
|
1463
|
+
const registry = textOf$2(box["registry"]);
|
|
1464
|
+
const releasedAt = textOf$2(box["releasedAt"]);
|
|
1465
|
+
return {
|
|
1466
|
+
opencliHome,
|
|
1467
|
+
package: pkg,
|
|
1468
|
+
installedAt,
|
|
1469
|
+
...name === void 0 ? {} : { name },
|
|
1470
|
+
...registry === void 0 ? {} : { registry },
|
|
1471
|
+
...releasedAt === void 0 ? {} : { releasedAt },
|
|
1472
|
+
...box["local"] === true ? { local: true } : {}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function parsePluginLedger(raw) {
|
|
1476
|
+
if (typeof raw !== "object" || raw === null) return [];
|
|
1477
|
+
const list = raw["entries"];
|
|
1478
|
+
if (!Array.isArray(list)) return [];
|
|
1479
|
+
const entries = [];
|
|
1480
|
+
for (const item of list) {
|
|
1481
|
+
const entry = entryOf(item);
|
|
1482
|
+
if (entry === void 0) continue;
|
|
1483
|
+
if (entries.some((known) => ledgerKeyOf(known) === ledgerKeyOf(entry))) continue;
|
|
1484
|
+
entries.push(entry);
|
|
1485
|
+
}
|
|
1486
|
+
return entries;
|
|
1487
|
+
}
|
|
1488
|
+
function readPluginLedger(store) {
|
|
1489
|
+
return parsePluginLedger(store.read(PLUGIN_LEDGER_FILE));
|
|
1490
|
+
}
|
|
1491
|
+
function writePluginLedger(store, entries) {
|
|
1492
|
+
if (entries.length === 0) {
|
|
1493
|
+
store.remove(PLUGIN_LEDGER_FILE);
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
store.write(PLUGIN_LEDGER_FILE, { entries });
|
|
1497
|
+
}
|
|
1498
|
+
function pluginEntriesOfHome(entries, opencliHome) {
|
|
1499
|
+
return entries.filter((entry) => entry.opencliHome === opencliHome);
|
|
1500
|
+
}
|
|
1501
|
+
function isHeldEntry(entry) {
|
|
1502
|
+
return entry.releasedAt === void 0;
|
|
1503
|
+
}
|
|
1504
|
+
function heldEntriesOfHome(entries, opencliHome) {
|
|
1505
|
+
return pluginEntriesOfHome(entries, opencliHome).filter(isHeldEntry);
|
|
1506
|
+
}
|
|
1507
|
+
function heldEntryOfPlugin(entries, opencliHome, plugin) {
|
|
1508
|
+
const key = plugin.trim();
|
|
1509
|
+
if (key === "") return void 0;
|
|
1510
|
+
return heldEntriesOfHome(entries, opencliHome).find((entry) => entry.name === key || entry.package === key);
|
|
1511
|
+
}
|
|
1512
|
+
function withReleasedEntry(entries, opencliHome, pkg, releasedAt) {
|
|
1513
|
+
const entry = pluginEntryOf(entries, opencliHome, pkg);
|
|
1514
|
+
if (entry === void 0 || entry.releasedAt !== void 0) return [...entries];
|
|
1515
|
+
return withPluginEntry(entries, {
|
|
1516
|
+
...entry,
|
|
1517
|
+
releasedAt
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
function pluginRetentionOf(options) {
|
|
1521
|
+
if (options.held) return "keep-held";
|
|
1522
|
+
if (!options.trustworthy) return "keep-unsure";
|
|
1523
|
+
return options.claimedBy.length > 0 ? "keep-claimed" : "remove";
|
|
1524
|
+
}
|
|
1525
|
+
function pluginEntryOf(entries, opencliHome, pkg) {
|
|
1526
|
+
const key = ledgerKeyOf({
|
|
1527
|
+
opencliHome,
|
|
1528
|
+
package: pkg
|
|
1529
|
+
});
|
|
1530
|
+
return entries.find((entry) => ledgerKeyOf(entry) === key);
|
|
1531
|
+
}
|
|
1532
|
+
function withPluginEntry(entries, entry) {
|
|
1533
|
+
const key = ledgerKeyOf(entry);
|
|
1534
|
+
return [...entries.filter((item) => ledgerKeyOf(item) !== key), entry];
|
|
1535
|
+
}
|
|
1536
|
+
function withoutPluginEntry(entries, opencliHome, pkg) {
|
|
1537
|
+
const key = ledgerKeyOf({
|
|
1538
|
+
opencliHome,
|
|
1539
|
+
package: pkg
|
|
1540
|
+
});
|
|
1541
|
+
return entries.filter((entry) => ledgerKeyOf(entry) !== key);
|
|
1542
|
+
}
|
|
1543
|
+
//#endregion
|
|
1307
1544
|
//#region ../shared/dist/host/plugin-source.js
|
|
1308
1545
|
function mergeLines(commands) {
|
|
1309
1546
|
return commands.map((item) => item.optional === true ? `(${item.line} || true)` : item.line).join(" && ");
|
|
@@ -1591,6 +1828,7 @@ var PROBLEMS = [
|
|
|
1591
1828
|
"not-found",
|
|
1592
1829
|
"not-a-package",
|
|
1593
1830
|
"not-a-bundle",
|
|
1831
|
+
"not-a-workbench-module",
|
|
1594
1832
|
"network",
|
|
1595
1833
|
"unknown"
|
|
1596
1834
|
];
|
|
@@ -1617,7 +1855,7 @@ var CANCELLATIONS = [
|
|
|
1617
1855
|
"too-late",
|
|
1618
1856
|
"not-running"
|
|
1619
1857
|
];
|
|
1620
|
-
function asRecord$
|
|
1858
|
+
function asRecord$6(value) {
|
|
1621
1859
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
1622
1860
|
}
|
|
1623
1861
|
function asText$4(value) {
|
|
@@ -1634,7 +1872,7 @@ function asTextArray(value) {
|
|
|
1634
1872
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1635
1873
|
}
|
|
1636
1874
|
function asRegistries(raw) {
|
|
1637
|
-
const record = asRecord$
|
|
1875
|
+
const record = asRecord$6(raw);
|
|
1638
1876
|
if (record === void 0) return null;
|
|
1639
1877
|
const registry = asRegistry(record.registry);
|
|
1640
1878
|
if (registry === void 0) return null;
|
|
@@ -1645,7 +1883,7 @@ function asRegistries(raw) {
|
|
|
1645
1883
|
};
|
|
1646
1884
|
}
|
|
1647
1885
|
function asPackageResult(raw) {
|
|
1648
|
-
const record = asRecord$
|
|
1886
|
+
const record = asRecord$6(raw);
|
|
1649
1887
|
if (record === void 0) return void 0;
|
|
1650
1888
|
const kind = pick(FAILURE_KINDS, record.kind);
|
|
1651
1889
|
return {
|
|
@@ -1657,12 +1895,12 @@ function asPackageResult(raw) {
|
|
|
1657
1895
|
};
|
|
1658
1896
|
}
|
|
1659
1897
|
function asChange(raw) {
|
|
1660
|
-
const record = asRecord$
|
|
1898
|
+
const record = asRecord$6(raw);
|
|
1661
1899
|
if (record === void 0) return null;
|
|
1662
1900
|
const application = pick(APPLICATIONS, record.application);
|
|
1663
1901
|
const stage = pick(STAGES, record.stage);
|
|
1664
1902
|
if (application === void 0 || stage === void 0) return null;
|
|
1665
|
-
const error = asRecord$
|
|
1903
|
+
const error = asRecord$6(record.error);
|
|
1666
1904
|
const packageResult = asPackageResult(record.packageResult);
|
|
1667
1905
|
const pendingBuilds = asTextArray(record.pendingBuilds);
|
|
1668
1906
|
const failedAt = pick(["registry", "spec-host"], record.failedAt);
|
|
@@ -1683,7 +1921,7 @@ function asChange(raw) {
|
|
|
1683
1921
|
};
|
|
1684
1922
|
}
|
|
1685
1923
|
function asInspection(raw) {
|
|
1686
|
-
const record = asRecord$
|
|
1924
|
+
const record = asRecord$6(raw);
|
|
1687
1925
|
if (record === void 0) return null;
|
|
1688
1926
|
const status = asText$4(record.status);
|
|
1689
1927
|
if (status === "refused") {
|
|
@@ -1741,14 +1979,14 @@ function readBundleManager(ctx) {
|
|
|
1741
1979
|
},
|
|
1742
1980
|
async cancel(requestId) {
|
|
1743
1981
|
if (typeof face.cancelInstall !== "function") return null;
|
|
1744
|
-
return pick(CANCELLATIONS, (asRecord$
|
|
1982
|
+
return pick(CANCELLATIONS, (asRecord$6(await face.cancelInstall(requestId)) ?? {}).status) ?? null;
|
|
1745
1983
|
}
|
|
1746
1984
|
};
|
|
1747
1985
|
}
|
|
1748
1986
|
//#endregion
|
|
1749
1987
|
//#region src/shared/api/host/installed-module.ts
|
|
1750
1988
|
var PACKAGE_NAME$1 = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
1751
|
-
function asRecord$
|
|
1989
|
+
function asRecord$5(value) {
|
|
1752
1990
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
1753
1991
|
}
|
|
1754
1992
|
function asText$3(value) {
|
|
@@ -1765,18 +2003,19 @@ function profileDirOf(ctx) {
|
|
|
1765
2003
|
function stringsOf$1(value) {
|
|
1766
2004
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1767
2005
|
}
|
|
1768
|
-
function
|
|
1769
|
-
if (profileDir === null) return
|
|
1770
|
-
let manifest;
|
|
2006
|
+
function profileManifestOf(profileDir) {
|
|
2007
|
+
if (profileDir === null) return void 0;
|
|
1771
2008
|
try {
|
|
1772
|
-
|
|
2009
|
+
return asRecord$5(JSON.parse(fs.readFileSync(path.join(profileDir, "package.json"), "utf8")));
|
|
1773
2010
|
} catch {
|
|
1774
|
-
return
|
|
2011
|
+
return;
|
|
1775
2012
|
}
|
|
1776
|
-
|
|
2013
|
+
}
|
|
2014
|
+
function profileModules(profileDir) {
|
|
2015
|
+
const record = profileManifestOf(profileDir);
|
|
1777
2016
|
if (record === void 0) return [];
|
|
1778
|
-
const bundles = stringsOf$1(asRecord$
|
|
1779
|
-
const dependencies = asRecord$
|
|
2017
|
+
const bundles = stringsOf$1(asRecord$5(asRecord$5(record.dsh)?.profile)?.bundles);
|
|
2018
|
+
const dependencies = asRecord$5(record.dependencies) ?? {};
|
|
1780
2019
|
const names = bundles.length > 0 ? bundles : Object.keys(dependencies);
|
|
1781
2020
|
const found = [];
|
|
1782
2021
|
for (const pkg of names) {
|
|
@@ -1810,6 +2049,27 @@ function registryFromLockfile(profileDir, pkg) {
|
|
|
1810
2049
|
if (cut <= 8) return void 0;
|
|
1811
2050
|
return tarball.slice(0, cut);
|
|
1812
2051
|
}
|
|
2052
|
+
function moduleDeclarationOf(profileDir, pkg) {
|
|
2053
|
+
if (profileDir === null || !isPackageName(pkg)) return void 0;
|
|
2054
|
+
try {
|
|
2055
|
+
const text = fs.readFileSync(path.join(profileDir, "node_modules", pkg, "package.json"), "utf8");
|
|
2056
|
+
return asRecord$5(asRecord$5(asRecord$5(JSON.parse(text))?.dsh)?.workbench);
|
|
2057
|
+
} catch {
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
function declarationGaps(profileDir) {
|
|
2062
|
+
if (profileManifestOf(profileDir) === void 0) return void 0;
|
|
2063
|
+
const gaps = [];
|
|
2064
|
+
for (const module of profileModules(profileDir)) {
|
|
2065
|
+
const problems = declarationProblemsOf(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
2066
|
+
if (problems.length > 0) gaps.push({
|
|
2067
|
+
pkg: module.pkg,
|
|
2068
|
+
problems
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
return gaps;
|
|
2072
|
+
}
|
|
1813
2073
|
function readInstalledModule(profileDir, pkg) {
|
|
1814
2074
|
if (profileDir === null || !isPackageName(pkg)) return {
|
|
1815
2075
|
kind: "unknown",
|
|
@@ -1824,18 +2084,18 @@ function readInstalledModule(profileDir, pkg) {
|
|
|
1824
2084
|
pkg
|
|
1825
2085
|
};
|
|
1826
2086
|
}
|
|
1827
|
-
const record = asRecord$
|
|
2087
|
+
const record = asRecord$5(manifest);
|
|
1828
2088
|
if (record === void 0) return {
|
|
1829
2089
|
kind: "unknown",
|
|
1830
2090
|
pkg
|
|
1831
2091
|
};
|
|
1832
|
-
const declared = asRecord$
|
|
2092
|
+
const declared = asRecord$5(asRecord$5(record.dsh)?.workbench);
|
|
1833
2093
|
const unit = declared === void 0 ? "" : asText$3(declared.unit);
|
|
1834
2094
|
if (unit === "") return {
|
|
1835
2095
|
kind: "not-module",
|
|
1836
2096
|
pkg
|
|
1837
2097
|
};
|
|
1838
|
-
const declaredRegistry = asText$3(asRecord$
|
|
2098
|
+
const declaredRegistry = asText$3(asRecord$5(record.publishConfig)?.registry);
|
|
1839
2099
|
const registry = declaredRegistry === "" ? registryFromLockfile(profileDir, pkg) : declaredRegistry;
|
|
1840
2100
|
return {
|
|
1841
2101
|
kind: "module",
|
|
@@ -1971,7 +2231,7 @@ function workshopPathOf(ctx) {
|
|
|
1971
2231
|
}
|
|
1972
2232
|
//#endregion
|
|
1973
2233
|
//#region src/shared/api/host/diy-workshop.ts
|
|
1974
|
-
function asRecord$
|
|
2234
|
+
function asRecord$4(value) {
|
|
1975
2235
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
1976
2236
|
}
|
|
1977
2237
|
function asText$2(value) {
|
|
@@ -1997,8 +2257,8 @@ function workshopModules(packagesDir, installed = []) {
|
|
|
1997
2257
|
} catch {
|
|
1998
2258
|
return [];
|
|
1999
2259
|
}
|
|
2000
|
-
const record = asRecord$
|
|
2001
|
-
const declared = record === void 0 ? void 0 : asRecord$
|
|
2260
|
+
const record = asRecord$4(manifest);
|
|
2261
|
+
const declared = record === void 0 ? void 0 : asRecord$4(asRecord$4(record.dsh)?.workbench);
|
|
2002
2262
|
const unit = declared === void 0 ? "" : asText$2(declared.unit);
|
|
2003
2263
|
if (unit === "") return [];
|
|
2004
2264
|
return [{
|
|
@@ -2021,7 +2281,6 @@ var MODULES_TOOL = "workbench_modules";
|
|
|
2021
2281
|
var DIY_TOOL = "workbench_diy";
|
|
2022
2282
|
var panelsUnit = {
|
|
2023
2283
|
id: "panels",
|
|
2024
|
-
kind: "ui",
|
|
2025
2284
|
card: null,
|
|
2026
2285
|
tools: [
|
|
2027
2286
|
{
|
|
@@ -2056,9 +2315,10 @@ function actionOf$1(call) {
|
|
|
2056
2315
|
const raw = call.arguments;
|
|
2057
2316
|
if (typeof raw !== "object" || raw === null) return {};
|
|
2058
2317
|
const value = raw;
|
|
2318
|
+
const target = typeof value.plugin === "string" ? value.plugin : typeof value.name === "string" ? value.name : typeof value.id === "string" ? value.id : typeof value.unit === "string" ? value.unit : void 0;
|
|
2059
2319
|
return {
|
|
2060
2320
|
...typeof value.action === "string" ? { action: value.action } : {},
|
|
2061
|
-
...
|
|
2321
|
+
...target === void 0 ? {} : { id: target }
|
|
2062
2322
|
};
|
|
2063
2323
|
}
|
|
2064
2324
|
function whatOf(id) {
|
|
@@ -2079,6 +2339,17 @@ var panelsApproval = (call) => {
|
|
|
2079
2339
|
const what = whatOf(id);
|
|
2080
2340
|
if (call.name === "workbench_modules") {
|
|
2081
2341
|
if (readAction) return READ;
|
|
2342
|
+
if (action === "update-plugin" || action === "uninstall-plugin") {
|
|
2343
|
+
const verb = action === "update-plugin" ? "更新" : "卸载";
|
|
2344
|
+
const verbEn = action === "update-plugin" ? "update" : "remove";
|
|
2345
|
+
return askOf(`workbench_modules ${action} ${id ?? ""} changes an opencli plugin on this machine`, {
|
|
2346
|
+
en: `it ${verbEn}s that opencli plugin on this machine. Allow?`,
|
|
2347
|
+
zh: `会${verb}这个 opencli 插件。允许吗?`
|
|
2348
|
+
}, {
|
|
2349
|
+
en: `The agent wants to ${verbEn} the opencli plugin ${what} (it changes what is installed on this machine). Allow?`,
|
|
2350
|
+
zh: `agent 想${verb} opencli 插件 ${what}(会改这台机器上装的东西)。允许吗?`
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2082
2353
|
if (action === "update-dependency") return askOf(`workbench_modules update-dependency ${id ?? ""} upgrades a shared dependency on this machine`, {
|
|
2083
2354
|
en: "it upgrades that shared dependency on this machine. Allow?",
|
|
2084
2355
|
zh: `会升级共用依赖 ${what}。允许吗?`
|
|
@@ -2145,13 +2416,102 @@ var panelsApproval = (call) => {
|
|
|
2145
2416
|
});
|
|
2146
2417
|
};
|
|
2147
2418
|
//#endregion
|
|
2419
|
+
//#region src/modules/panels/model/config.ts
|
|
2420
|
+
var EMPTY_BOARD = {
|
|
2421
|
+
shown: [],
|
|
2422
|
+
hidden: []
|
|
2423
|
+
};
|
|
2424
|
+
var BOARD_TARGETS = ["workspace", "machine"];
|
|
2425
|
+
var DEFAULT_BOARD_TARGET = "workspace";
|
|
2426
|
+
function resolveScope(query) {
|
|
2427
|
+
const { id, boards } = query;
|
|
2428
|
+
const board = boards.workspace ?? boards.machine;
|
|
2429
|
+
if (board === null) return false;
|
|
2430
|
+
if (board.hidden.includes(id)) return false;
|
|
2431
|
+
return board.shown.includes(id);
|
|
2432
|
+
}
|
|
2433
|
+
function strings(raw) {
|
|
2434
|
+
return Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
|
|
2435
|
+
}
|
|
2436
|
+
function parseBoard(raw) {
|
|
2437
|
+
if (typeof raw !== "object" || raw === null) return EMPTY_BOARD;
|
|
2438
|
+
const value = raw;
|
|
2439
|
+
return {
|
|
2440
|
+
shown: strings(value.shown),
|
|
2441
|
+
hidden: strings(value.hidden)
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
function parseReleased(raw) {
|
|
2445
|
+
if (typeof raw !== "object" || raw === null) return [];
|
|
2446
|
+
return strings(raw.ids);
|
|
2447
|
+
}
|
|
2448
|
+
function resolvePanels(manifests, boards) {
|
|
2449
|
+
const ordered = boards.workspace?.shown ?? boards.machine?.shown ?? [];
|
|
2450
|
+
const rank = new Map(ordered.map((id, index) => [id, index]));
|
|
2451
|
+
return manifests.filter((manifest) => manifest.card !== null).map((manifest) => {
|
|
2452
|
+
const on = resolveScope({
|
|
2453
|
+
id: manifest.id,
|
|
2454
|
+
boards
|
|
2455
|
+
});
|
|
2456
|
+
const order = rank.get(manifest.id) ?? ordered.length + (manifest.card?.order ?? 0);
|
|
2457
|
+
return {
|
|
2458
|
+
id: manifest.id,
|
|
2459
|
+
titleKey: manifest.card?.titleKey ?? manifest.id,
|
|
2460
|
+
order,
|
|
2461
|
+
shown: on,
|
|
2462
|
+
source: on && (boards.workspace?.shown.includes(manifest.id) ?? false) ? "user" : "default",
|
|
2463
|
+
hasCard: true
|
|
2464
|
+
};
|
|
2465
|
+
}).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
2466
|
+
}
|
|
2467
|
+
function withoutReleased(released, id) {
|
|
2468
|
+
return released.filter((item) => item !== id);
|
|
2469
|
+
}
|
|
2470
|
+
function withReleased(released, id) {
|
|
2471
|
+
return [.../* @__PURE__ */ new Set([...released, id])];
|
|
2472
|
+
}
|
|
2473
|
+
function added(list, id) {
|
|
2474
|
+
return list.includes(id) ? [...list] : [...list, id];
|
|
2475
|
+
}
|
|
2476
|
+
function dropped(list, id) {
|
|
2477
|
+
return list.filter((item) => item !== id);
|
|
2478
|
+
}
|
|
2479
|
+
function withShown(board, id) {
|
|
2480
|
+
return {
|
|
2481
|
+
shown: added(board.shown, id),
|
|
2482
|
+
hidden: dropped(board.hidden, id)
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
function withHidden(board, id) {
|
|
2486
|
+
return {
|
|
2487
|
+
shown: dropped(board.shown, id),
|
|
2488
|
+
hidden: added(board.hidden, id)
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
function withoutPanel(board, id) {
|
|
2492
|
+
return {
|
|
2493
|
+
shown: dropped(board.shown, id),
|
|
2494
|
+
hidden: dropped(board.hidden, id)
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
function withMoved(board, id, index) {
|
|
2498
|
+
const current = [...board.shown];
|
|
2499
|
+
const from = current.indexOf(id);
|
|
2500
|
+
const max = from === -1 ? current.length : current.length - 1;
|
|
2501
|
+
const to = Math.max(0, Math.min(max, Math.trunc(index)));
|
|
2502
|
+
if (from === -1) current.splice(to, 0, id);
|
|
2503
|
+
else if (from !== to) current.splice(to, 0, ...current.splice(from, 1));
|
|
2504
|
+
return {
|
|
2505
|
+
shown: current,
|
|
2506
|
+
hidden: dropped(board.hidden, id)
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
//#endregion
|
|
2148
2510
|
//#region src/shared/api/host/diy-facts.ts
|
|
2149
2511
|
function asExported(dir) {
|
|
2150
2512
|
try {
|
|
2151
2513
|
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
2152
|
-
|
|
2153
|
-
if (built.length > 0) return built;
|
|
2154
|
-
return Object.values(manifest.dsh?.workbench?.faces ?? {}).filter((one) => typeof one === "string");
|
|
2514
|
+
return (typeof manifest.exports === "object" && manifest.exports !== null ? Object.values(manifest.exports) : []).filter((one) => typeof one === "string");
|
|
2155
2515
|
} catch {
|
|
2156
2516
|
return [];
|
|
2157
2517
|
}
|
|
@@ -2456,7 +2816,7 @@ var OUTPUT_SCHEMA$2 = {
|
|
|
2456
2816
|
},
|
|
2457
2817
|
additionalProperties: true
|
|
2458
2818
|
};
|
|
2459
|
-
function asRecord$
|
|
2819
|
+
function asRecord$3(value) {
|
|
2460
2820
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
2461
2821
|
}
|
|
2462
2822
|
function asNumber(value) {
|
|
@@ -2472,7 +2832,7 @@ function asText$1(value) {
|
|
|
2472
2832
|
function plainJson(value) {
|
|
2473
2833
|
return JSON.parse(JSON.stringify(value));
|
|
2474
2834
|
}
|
|
2475
|
-
function verdictOf(change) {
|
|
2835
|
+
function verdictOf$1(change) {
|
|
2476
2836
|
if (change.application === "applied") return "装好了,已生效";
|
|
2477
2837
|
if (change.application === "restart-required") return "装好了,要重启一次才生效(装新模块、同名重装、替换已有包都走这一支)";
|
|
2478
2838
|
if (change.application === "overridden") return "被更高层的声明盖住了,这次没生效";
|
|
@@ -2616,7 +2976,7 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
2616
2976
|
}
|
|
2617
2977
|
},
|
|
2618
2978
|
async execute(raw, exec) {
|
|
2619
|
-
const args = asRecord$
|
|
2979
|
+
const args = asRecord$3(raw) ?? {};
|
|
2620
2980
|
const action = asText$1(args.action) || "list";
|
|
2621
2981
|
if (!ACTIONS$1.includes(action)) throw new Error(`不认识的 action:${action}(可用:${ACTIONS$1.join(" / ")})`);
|
|
2622
2982
|
const packagesDir = workshopPackagesDir(ctx);
|
|
@@ -2741,7 +3101,7 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
2741
3101
|
});
|
|
2742
3102
|
}
|
|
2743
3103
|
const outcome = await market.installBundle(found.dir);
|
|
2744
|
-
const verdict = verdictOf(outcome.change);
|
|
3104
|
+
const verdict = verdictOf$1(outcome.change);
|
|
2745
3105
|
const restartRequired = outcome.change.application === "restart-required";
|
|
2746
3106
|
const panel = outcome.change.application === "applied" ? showOrReason(panels, unit, workspace) : {
|
|
2747
3107
|
ok: false,
|
|
@@ -2986,14 +3346,570 @@ function sourceProblemText(problem, env) {
|
|
|
2986
3346
|
return "这份安装没声明安装方式 —— 没声明就不猜地址,只能自己动手。";
|
|
2987
3347
|
}
|
|
2988
3348
|
//#endregion
|
|
2989
|
-
//#region src/
|
|
3349
|
+
//#region src/shared/api/host/registry-declaration.ts
|
|
3350
|
+
var cache$1 = /* @__PURE__ */ new Map();
|
|
3351
|
+
function asRecord$2(value) {
|
|
3352
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
3353
|
+
}
|
|
3354
|
+
function localDirOf(spec) {
|
|
3355
|
+
const dir = spec.trim().replace(/^(?:file|link):/, "");
|
|
3356
|
+
return path.isAbsolute(dir) ? dir : void 0;
|
|
3357
|
+
}
|
|
3358
|
+
function packageManifestOf(dir) {
|
|
3359
|
+
try {
|
|
3360
|
+
return asRecord$2(JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8")));
|
|
3361
|
+
} catch {
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
function dshRecordOf(dir) {
|
|
3366
|
+
return packageManifestOf(dir)?.dsh;
|
|
3367
|
+
}
|
|
3368
|
+
function packageNameOf(dir) {
|
|
3369
|
+
const name = packageManifestOf(dir)?.name;
|
|
3370
|
+
return typeof name === "string" && name.trim() !== "" ? name.trim() : void 0;
|
|
3371
|
+
}
|
|
3372
|
+
function pathDeclarationOf(spec) {
|
|
3373
|
+
const dir = localDirOf(spec);
|
|
3374
|
+
return dir === void 0 ? void 0 : asRecord$2(dshRecordOf(dir))?.workbench;
|
|
3375
|
+
}
|
|
3376
|
+
async function registryDshOf(options) {
|
|
3377
|
+
const { shell, name, registry, force = false } = options;
|
|
3378
|
+
const key = `${registry ?? ""}|${name}`;
|
|
3379
|
+
const hit = cache$1.get(key);
|
|
3380
|
+
if (!force && hit !== void 0 && Date.now() - hit.at < 6e5) return {
|
|
3381
|
+
readable: true,
|
|
3382
|
+
raw: hit.raw
|
|
3383
|
+
};
|
|
3384
|
+
const at = registry === void 0 ? "" : ` --registry ${quoteShellArg(registry)}`;
|
|
3385
|
+
const result = await runShell(shell, `npm view ${quoteShellArg(name)} dsh --json${at} --cache ${quoteShellArg(npmCacheDir())}`, { timeoutMs: VERSION_CHECK_TIMEOUT_MS }).catch(() => void 0);
|
|
3386
|
+
const parsed = result?.exitCode === 0 ? parseJsonLoose(result.stdout) : void 0;
|
|
3387
|
+
if (parsed === void 0) return {
|
|
3388
|
+
readable: false,
|
|
3389
|
+
raw: void 0
|
|
3390
|
+
};
|
|
3391
|
+
cache$1.set(key, {
|
|
3392
|
+
at: Date.now(),
|
|
3393
|
+
raw: parsed
|
|
3394
|
+
});
|
|
3395
|
+
return {
|
|
3396
|
+
readable: true,
|
|
3397
|
+
raw: parsed
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
async function registryDeclarationOf(options) {
|
|
3401
|
+
const found = await registryDshOf(options);
|
|
3402
|
+
return found.readable ? asRecord$2(found.raw)?.workbench : void 0;
|
|
3403
|
+
}
|
|
3404
|
+
function declarationRefusalOf(name, raw) {
|
|
3405
|
+
if (raw === void 0) return [];
|
|
3406
|
+
return declarationProblemsOf(name, raw);
|
|
3407
|
+
}
|
|
3408
|
+
//#endregion
|
|
3409
|
+
//#region src/shared/api/host/package-kind.ts
|
|
3410
|
+
var OPENCLI_MANIFEST_FILE = "opencli-plugin.json";
|
|
3411
|
+
var VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]*$/;
|
|
3412
|
+
function splitNpmSpec(spec) {
|
|
3413
|
+
const value = spec.trim();
|
|
3414
|
+
const at = value.lastIndexOf("@");
|
|
3415
|
+
const name = at > 0 ? value.slice(0, at) : value;
|
|
3416
|
+
const version = at > 0 ? value.slice(at + 1) : "";
|
|
3417
|
+
if (!isPackageName(name)) return void 0;
|
|
3418
|
+
if (version !== "" && !VERSION.test(version)) return void 0;
|
|
3419
|
+
return {
|
|
3420
|
+
name,
|
|
3421
|
+
...version === "" ? {} : { version }
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
function asRecord$1(value) {
|
|
3425
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
3426
|
+
}
|
|
3427
|
+
function isCommandFile(name) {
|
|
3428
|
+
if (name.endsWith(".d.ts") || name.endsWith(".test.ts")) return false;
|
|
3429
|
+
if (name.endsWith(".d.js")) return false;
|
|
3430
|
+
return name.endsWith(".ts") || name.endsWith(".js");
|
|
3431
|
+
}
|
|
3432
|
+
function pluginManifestOf(dir) {
|
|
3433
|
+
try {
|
|
3434
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, OPENCLI_MANIFEST_FILE), "utf8"));
|
|
3435
|
+
if (Array.isArray(parsed)) return void 0;
|
|
3436
|
+
return asRecord$1(parsed);
|
|
3437
|
+
} catch {
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
function hasCommandFile(dir) {
|
|
3442
|
+
try {
|
|
3443
|
+
return fs.readdirSync(dir).some(isCommandFile);
|
|
3444
|
+
} catch {
|
|
3445
|
+
return false;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
function pluginAt(dir) {
|
|
3449
|
+
const manifest = pluginManifestOf(dir);
|
|
3450
|
+
if (manifest === void 0 || !hasCommandFile(dir)) return void 0;
|
|
3451
|
+
const name = manifest.name;
|
|
3452
|
+
return typeof name === "string" && name.trim() !== "" ? { name: name.trim() } : {};
|
|
3453
|
+
}
|
|
3454
|
+
function dshMarkersOf(raw) {
|
|
3455
|
+
const record = asRecord$1(raw);
|
|
3456
|
+
return record !== void 0 && ("bundle" in record || "workbench" in record);
|
|
3457
|
+
}
|
|
3458
|
+
function verdictOf(dir) {
|
|
3459
|
+
const plugin = pluginAt(dir);
|
|
3460
|
+
if (packageManifestOf(dir) === void 0 && plugin === void 0) return void 0;
|
|
3461
|
+
const dsh = dshMarkersOf(dshRecordOf(dir));
|
|
3462
|
+
const kind = plugin !== void 0 && dsh ? "both" : plugin !== void 0 ? "opencli-plugin" : dsh ? "dsh-bundle" : "neither";
|
|
3463
|
+
const pkg = packageNameOf(dir);
|
|
3464
|
+
return {
|
|
3465
|
+
kind,
|
|
3466
|
+
...pkg === void 0 ? {} : { pkg },
|
|
3467
|
+
...plugin?.name === void 0 ? {} : { pluginName: plugin.name }
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
function refusalReason() {
|
|
3471
|
+
return "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上";
|
|
3472
|
+
}
|
|
3473
|
+
//#endregion
|
|
3474
|
+
//#region src/shared/api/host/package-content.ts
|
|
3475
|
+
function packedFileFrom(stdout, root) {
|
|
3476
|
+
const parsed = parseJsonLoose(stdout);
|
|
3477
|
+
if (Array.isArray(parsed)) {
|
|
3478
|
+
const first = parsed[0];
|
|
3479
|
+
const filename = typeof first === "object" && first !== null ? first.filename : void 0;
|
|
3480
|
+
if (typeof filename === "string" && filename.trim() !== "") {
|
|
3481
|
+
const at = path.join(root, path.basename(filename.trim()));
|
|
3482
|
+
if (fs.existsSync(at)) return at;
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
try {
|
|
3486
|
+
const found = fs.readdirSync(root).filter((name) => name.endsWith(".tgz"));
|
|
3487
|
+
return found.length === 1 ? path.join(root, found[0] ?? "") : void 0;
|
|
3488
|
+
} catch {
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
async function withPackageDir(options, visit) {
|
|
3493
|
+
const local = localDirOf(options.spec);
|
|
3494
|
+
if (local !== void 0) return visit(local);
|
|
3495
|
+
if (splitNpmSpec(options.spec) === void 0) return void 0;
|
|
3496
|
+
const shell = options.shell;
|
|
3497
|
+
if (shell === void 0) return void 0;
|
|
3498
|
+
let root;
|
|
3499
|
+
try {
|
|
3500
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), "wb-package-"));
|
|
3501
|
+
} catch {
|
|
3502
|
+
return;
|
|
3503
|
+
}
|
|
3504
|
+
try {
|
|
3505
|
+
const at = options.registry === void 0 ? "" : ` --registry ${quoteShellArg(options.registry)}`;
|
|
3506
|
+
const packed = await runShell(shell, `npm pack ${quoteShellArg(options.spec)} --ignore-scripts --json --pack-destination ${quoteShellArg(root)} --cache ${quoteShellArg(npmCacheDir())}${at}`, { timeoutMs: INSTALL_TIMEOUT_MS }).catch(() => void 0);
|
|
3507
|
+
if (packed === void 0 || packed.exitCode !== 0) return void 0;
|
|
3508
|
+
const file = packedFileFrom(packed.stdout, root);
|
|
3509
|
+
if (file === void 0) return void 0;
|
|
3510
|
+
const unpacked = await runShell(shell, `tar -xzf ${quoteShellArg(file)} -C ${quoteShellArg(root)}`, { timeoutMs: INSTALL_TIMEOUT_MS }).catch(() => void 0);
|
|
3511
|
+
if (unpacked === void 0 || unpacked.exitCode !== 0) return void 0;
|
|
3512
|
+
const dir = path.join(root, "package");
|
|
3513
|
+
if (!fs.existsSync(dir)) return void 0;
|
|
3514
|
+
return visit(dir);
|
|
3515
|
+
} finally {
|
|
3516
|
+
fs.rmSync(root, {
|
|
3517
|
+
recursive: true,
|
|
3518
|
+
force: true
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
//#endregion
|
|
3523
|
+
//#region src/shared/api/host/package-route.ts
|
|
3524
|
+
var LOCAL_PLUGIN_ENV = "DSH_WB_OPENCLI_PLUGIN_PATH";
|
|
3525
|
+
var PLUGIN_REGISTRY_ENV = "DSH_WB_OPENCLI_PLUGIN_REGISTRY";
|
|
3526
|
+
var cache = /* @__PURE__ */ new Map();
|
|
3527
|
+
function routeOfVerdict(verdict, fallback) {
|
|
3528
|
+
const pkg = verdict.pkg ?? fallback;
|
|
3529
|
+
if (verdict.kind === "neither") return {
|
|
3530
|
+
kind: "neither",
|
|
3531
|
+
...pkg === void 0 ? {} : { pkg }
|
|
3532
|
+
};
|
|
3533
|
+
if (verdict.kind === "dsh-bundle" || verdict.kind === "both") return { kind: "dsh-bundle" };
|
|
3534
|
+
return {
|
|
3535
|
+
kind: "opencli-plugin",
|
|
3536
|
+
...pkg === void 0 ? {} : { pkg },
|
|
3537
|
+
...verdict.pluginName === void 0 ? {} : { pluginName: verdict.pluginName }
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
async function packageRouteOf(options) {
|
|
3541
|
+
const local = localDirOf(options.spec);
|
|
3542
|
+
if (local !== void 0) {
|
|
3543
|
+
const verdict = verdictOf(local);
|
|
3544
|
+
return verdict === void 0 ? void 0 : routeOfVerdict(verdict, splitNpmSpec(options.spec)?.name);
|
|
3545
|
+
}
|
|
3546
|
+
const ref = splitNpmSpec(options.spec);
|
|
3547
|
+
if (ref === void 0) return void 0;
|
|
3548
|
+
const shell = options.shell;
|
|
3549
|
+
const registry = options.registry;
|
|
3550
|
+
if (shell === void 0 || registry === void 0) return void 0;
|
|
3551
|
+
const key = `${registry}|${options.spec}`;
|
|
3552
|
+
const hit = cache.get(key);
|
|
3553
|
+
if (options.force !== true && hit !== void 0 && Date.now() - hit.at < 6e5) return hit.route ?? void 0;
|
|
3554
|
+
const found = await registryDshOf({
|
|
3555
|
+
shell,
|
|
3556
|
+
name: options.spec,
|
|
3557
|
+
registry
|
|
3558
|
+
});
|
|
3559
|
+
let route;
|
|
3560
|
+
if (found.readable && dshMarkersOf(found.raw)) route = { kind: "dsh-bundle" };
|
|
3561
|
+
else {
|
|
3562
|
+
const verdict = await withPackageDir({
|
|
3563
|
+
shell,
|
|
3564
|
+
spec: options.spec,
|
|
3565
|
+
registry
|
|
3566
|
+
}, (dir) => verdictOf(dir));
|
|
3567
|
+
route = verdict === void 0 ? void 0 : routeOfVerdict(verdict, ref.name);
|
|
3568
|
+
}
|
|
3569
|
+
cache.set(key, {
|
|
3570
|
+
at: Date.now(),
|
|
3571
|
+
route: route ?? null
|
|
3572
|
+
});
|
|
3573
|
+
return route;
|
|
3574
|
+
}
|
|
3575
|
+
function pluginInstallPlan(options) {
|
|
3576
|
+
const local = localDirOf(options.spec);
|
|
3577
|
+
if (local !== void 0) return resolvePluginSource({
|
|
3578
|
+
kind: "local",
|
|
3579
|
+
env: LOCAL_PLUGIN_ENV
|
|
3580
|
+
}, { [LOCAL_PLUGIN_ENV]: local }, { command: options.command });
|
|
3581
|
+
const ref = splitNpmSpec(options.spec);
|
|
3582
|
+
if (ref === void 0) return {
|
|
3583
|
+
ok: false,
|
|
3584
|
+
problem: "malformed"
|
|
3585
|
+
};
|
|
3586
|
+
return resolvePluginSource({
|
|
3587
|
+
kind: "npm",
|
|
3588
|
+
package: ref.name,
|
|
3589
|
+
registryEnv: PLUGIN_REGISTRY_ENV,
|
|
3590
|
+
...options.registry === void 0 ? {} : { registryDefault: options.registry }
|
|
3591
|
+
}, { [PLUGIN_REGISTRY_ENV]: options.registry }, {
|
|
3592
|
+
command: options.command,
|
|
3593
|
+
...options.pluginName === void 0 ? {} : { plugin: options.pluginName },
|
|
3594
|
+
...ref.version === void 0 ? {} : { version: ref.version },
|
|
3595
|
+
mode: "install"
|
|
3596
|
+
});
|
|
3597
|
+
}
|
|
3598
|
+
//#endregion
|
|
3599
|
+
//#region src/modules/panels/model/plugin-name.ts
|
|
3600
|
+
function pluginDirOf(entry) {
|
|
3601
|
+
return entry.local === true ? entry.package : `${vendorPluginsDir()}/${entry.package}`;
|
|
3602
|
+
}
|
|
3603
|
+
function pluginOnDisk(entry) {
|
|
3604
|
+
return pluginAt(pluginDirOf(entry)) !== void 0;
|
|
3605
|
+
}
|
|
3606
|
+
function pluginNameOf(entry) {
|
|
3607
|
+
if (entry.name !== void 0) return entry.name;
|
|
3608
|
+
const name = pluginAt(pluginDirOf(entry))?.name?.trim() ?? "";
|
|
3609
|
+
return name === "" ? void 0 : name;
|
|
3610
|
+
}
|
|
3611
|
+
//#endregion
|
|
3612
|
+
//#region src/modules/panels/model/market-route.ts
|
|
2990
3613
|
function bundleNameOf(change, spec) {
|
|
2991
3614
|
if (change.bundle !== void 0 && change.bundle !== "") return change.bundle;
|
|
2992
3615
|
const at = spec.lastIndexOf("@");
|
|
2993
3616
|
return at > 0 ? spec.slice(0, at) : spec;
|
|
2994
3617
|
}
|
|
3618
|
+
function installedPluginName(spec, pkg) {
|
|
3619
|
+
const local = localDirOf(spec);
|
|
3620
|
+
return pluginNameOf(local === void 0 ? { package: pkg } : {
|
|
3621
|
+
package: local,
|
|
3622
|
+
local: true
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
function outcomeOf(deps, pkg, change, plugin) {
|
|
3626
|
+
return {
|
|
3627
|
+
change,
|
|
3628
|
+
module: deps.installed?.(pkg) ?? {
|
|
3629
|
+
kind: "unknown",
|
|
3630
|
+
pkg
|
|
3631
|
+
},
|
|
3632
|
+
supply: { state: "no-needs" },
|
|
3633
|
+
...plugin === void 0 ? {} : { plugin }
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
async function installDshBundle(deps, spec, options) {
|
|
3637
|
+
const change = await deps.manager.install({
|
|
3638
|
+
spec,
|
|
3639
|
+
...options?.registry === void 0 ? {} : { registry: options.registry },
|
|
3640
|
+
...options?.requestId === void 0 ? {} : { requestId: options.requestId },
|
|
3641
|
+
...options?.approvedBuilds === void 0 ? {} : { approvedBuilds: options.approvedBuilds }
|
|
3642
|
+
});
|
|
3643
|
+
if (change === null) throw new Error("插件管理器回的安装结果看不懂。");
|
|
3644
|
+
const pkg = bundleNameOf(change, spec);
|
|
3645
|
+
const module = deps.installed?.(pkg) ?? {
|
|
3646
|
+
kind: "unknown",
|
|
3647
|
+
pkg
|
|
3648
|
+
};
|
|
3649
|
+
await deps.rememberSource(deps.manager, pkg, options?.registry);
|
|
3650
|
+
return {
|
|
3651
|
+
change,
|
|
3652
|
+
module,
|
|
3653
|
+
supply: await deps.supplyAfter(module, change)
|
|
3654
|
+
};
|
|
3655
|
+
}
|
|
3656
|
+
async function installRouted(deps) {
|
|
3657
|
+
const { spec, options, registry, shell } = deps;
|
|
3658
|
+
const route = await packageRouteOf({
|
|
3659
|
+
spec,
|
|
3660
|
+
...shell === void 0 ? {} : { shell },
|
|
3661
|
+
...registry === void 0 ? {} : { registry }
|
|
3662
|
+
});
|
|
3663
|
+
if (route === void 0 || route.kind === "dsh-bundle") return installDshBundle(deps, spec, options);
|
|
3664
|
+
const pkg = route.pkg ?? splitNpmSpec(spec)?.name ?? spec.trim();
|
|
3665
|
+
if (route.kind === "neither") {
|
|
3666
|
+
deps.logger?.warn?.(`[dsh-workbench] 不装 ${pkg}:${refusalReason()}`);
|
|
3667
|
+
return outcomeOf(deps, pkg, {
|
|
3668
|
+
application: "failed",
|
|
3669
|
+
stage: "install",
|
|
3670
|
+
changed: false,
|
|
3671
|
+
target: pkg,
|
|
3672
|
+
errorCode: "not-a-module-or-plugin",
|
|
3673
|
+
errorDetail: refusalReason()
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
3676
|
+
const plan = pluginInstallPlan({
|
|
3677
|
+
spec,
|
|
3678
|
+
command: deps.command,
|
|
3679
|
+
...registry === void 0 ? {} : { registry },
|
|
3680
|
+
...route.pluginName === void 0 ? {} : { pluginName: route.pluginName }
|
|
3681
|
+
});
|
|
3682
|
+
if (!plan.ok) throw new Error(sourceProblemText(plan.problem, plan.env));
|
|
3683
|
+
deps.ledger?.intend({
|
|
3684
|
+
package: pkg,
|
|
3685
|
+
...registry === void 0 ? {} : { registry },
|
|
3686
|
+
...localDirOf(spec) === void 0 ? {} : { local: true }
|
|
3687
|
+
});
|
|
3688
|
+
try {
|
|
3689
|
+
await deps.runAll(plan.commands);
|
|
3690
|
+
} catch (error) {
|
|
3691
|
+
deps.ledger?.drop(pkg);
|
|
3692
|
+
throw error;
|
|
3693
|
+
}
|
|
3694
|
+
deps.ledger?.complete(pkg, installedPluginName(spec, pkg));
|
|
3695
|
+
deps.logger?.info?.(`[dsh-workbench] 把 ${pkg} 装进了 opencli 的插件区`);
|
|
3696
|
+
return outcomeOf(deps, pkg, {
|
|
3697
|
+
application: "applied",
|
|
3698
|
+
stage: "install",
|
|
3699
|
+
changed: true,
|
|
3700
|
+
target: pkg
|
|
3701
|
+
}, {
|
|
3702
|
+
kind: "opencli-plugin",
|
|
3703
|
+
pkg,
|
|
3704
|
+
...route.pluginName === void 0 ? {} : { name: route.pluginName }
|
|
3705
|
+
});
|
|
3706
|
+
}
|
|
3707
|
+
//#endregion
|
|
3708
|
+
//#region src/modules/panels/model/plugin-actions.ts
|
|
3709
|
+
function pluginKeptOf(retention, detail) {
|
|
3710
|
+
if (retention === "keep-held") return { kind: "held" };
|
|
3711
|
+
if (retention === "keep-unsure") return {
|
|
3712
|
+
kind: "unsure",
|
|
3713
|
+
unreadable: [...detail.unreadable]
|
|
3714
|
+
};
|
|
3715
|
+
return {
|
|
3716
|
+
kind: "claimed",
|
|
3717
|
+
by: [...detail.by]
|
|
3718
|
+
};
|
|
3719
|
+
}
|
|
3720
|
+
function pluginKeptText(kept) {
|
|
3721
|
+
if (kept.kind === "held") return "文件留着:你自己装的那一份,模块放手不动它。";
|
|
3722
|
+
if (kept.kind === "unnamed") return "文件先留着:看不出这是哪个插件,说不准谁在用它。";
|
|
3723
|
+
if (kept.kind === "unsure") return kept.unreadable.length === 0 ? "文件先留着:说不准还有谁在用它。" : `文件先留着:${kept.unreadable.join("、")} 读不出来用了什么,说不准它们在用它。`;
|
|
3724
|
+
return `文件留着:${kept.by.join("、")} 还要用它。`;
|
|
3725
|
+
}
|
|
3726
|
+
function pluginRemovedText(pkg) {
|
|
3727
|
+
return `「${pkg}」卸载干净了:没人再用它,文件也删了。`;
|
|
3728
|
+
}
|
|
3729
|
+
function pluginSweptText(pkg) {
|
|
3730
|
+
return `清掉了 ${pkg}:先前要它的都放手了,也没别人在用,文件跟着删了。`;
|
|
3731
|
+
}
|
|
3732
|
+
function createPluginActions(options) {
|
|
3733
|
+
const { ledger, shell, command, runAll, logger } = options;
|
|
3734
|
+
function requireLedger() {
|
|
3735
|
+
if (ledger === void 0) throw new Error("这个市场没有台账(插件记录),用户装的插件动不了。");
|
|
3736
|
+
return ledger;
|
|
3737
|
+
}
|
|
3738
|
+
function requireHeld(pkg) {
|
|
3739
|
+
const held = requireLedger();
|
|
3740
|
+
const direct = held.read(pkg);
|
|
3741
|
+
if (direct !== void 0) return {
|
|
3742
|
+
entry: direct,
|
|
3743
|
+
key: direct.package
|
|
3744
|
+
};
|
|
3745
|
+
const named = held.heldAsPlugin(pkg);
|
|
3746
|
+
if (named !== void 0) return {
|
|
3747
|
+
entry: named,
|
|
3748
|
+
key: named.package
|
|
3749
|
+
};
|
|
3750
|
+
const known = held.list().map((entry) => entry.name ?? entry.package);
|
|
3751
|
+
throw new Error(`台账里没有「${pkg}」这一笔(包名与插件名都认)—— 你装的插件有:${known.join("、") || "(空)"}`);
|
|
3752
|
+
}
|
|
3753
|
+
function referencesOf() {
|
|
3754
|
+
return options.references?.() ?? {
|
|
3755
|
+
trustworthy: false,
|
|
3756
|
+
unreadable: []
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
function decide(entry, held) {
|
|
3760
|
+
const plugin = pluginNameOf(entry);
|
|
3761
|
+
if (plugin === void 0) return {
|
|
3762
|
+
act: "keep",
|
|
3763
|
+
kept: { kind: "unnamed" }
|
|
3764
|
+
};
|
|
3765
|
+
const refs = referencesOf();
|
|
3766
|
+
const by = options.claimedBy?.(plugin) ?? [];
|
|
3767
|
+
const retention = pluginRetentionOf({
|
|
3768
|
+
held,
|
|
3769
|
+
claimedBy: by,
|
|
3770
|
+
trustworthy: refs.trustworthy
|
|
3771
|
+
});
|
|
3772
|
+
if (retention === "remove") return {
|
|
3773
|
+
act: "remove",
|
|
3774
|
+
plugin
|
|
3775
|
+
};
|
|
3776
|
+
return {
|
|
3777
|
+
act: "keep",
|
|
3778
|
+
kept: pluginKeptOf(retention, {
|
|
3779
|
+
by,
|
|
3780
|
+
unreadable: refs.unreadable
|
|
3781
|
+
}),
|
|
3782
|
+
plugin
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
async function removeFiles(plugin) {
|
|
3786
|
+
if (shell === void 0) throw new Error("没有执行缝(shell 服务不可用),删不掉插件文件。");
|
|
3787
|
+
await runAll([{ line: `${quoteShellArg(command)} plugin uninstall ${quoteShellArg(plugin)}` }]);
|
|
3788
|
+
}
|
|
3789
|
+
return {
|
|
3790
|
+
async update(pkg) {
|
|
3791
|
+
const { entry } = requireHeld(pkg);
|
|
3792
|
+
if (entry.local === true || entry.registry === void 0) throw new Error(`「${entry.package}」是从本地装的,没有地方查新版。`);
|
|
3793
|
+
if (shell === void 0) throw new Error("没有执行缝(shell 服务不可用),更新做不了。");
|
|
3794
|
+
const latest = await latestVersion({
|
|
3795
|
+
shell,
|
|
3796
|
+
name: entry.package,
|
|
3797
|
+
registry: entry.registry,
|
|
3798
|
+
force: true
|
|
3799
|
+
});
|
|
3800
|
+
if (latest === void 0) throw new Error(`问不到「${entry.package}」的新版本。`);
|
|
3801
|
+
const plan = pluginInstallPlan({
|
|
3802
|
+
spec: `${entry.package}@${latest}`,
|
|
3803
|
+
command,
|
|
3804
|
+
registry: entry.registry,
|
|
3805
|
+
...entry.name === void 0 ? {} : { pluginName: entry.name }
|
|
3806
|
+
});
|
|
3807
|
+
if (!plan.ok) throw new Error(sourceProblemText(plan.problem, plan.env));
|
|
3808
|
+
await runAll(plan.commands);
|
|
3809
|
+
logger?.info?.(`[dsh-workbench] 把 ${entry.package} 更新到 ${latest}`);
|
|
3810
|
+
return { name: pluginNameOf(entry) ?? entry.name ?? entry.package };
|
|
3811
|
+
},
|
|
3812
|
+
async uninstall(pkg) {
|
|
3813
|
+
const ledger = requireLedger();
|
|
3814
|
+
const { entry, key } = requireHeld(pkg);
|
|
3815
|
+
ledger.release(key);
|
|
3816
|
+
const decision = decide(entry, false);
|
|
3817
|
+
if (decision.act === "keep") {
|
|
3818
|
+
logger?.info?.(`[dsh-workbench] ${pluginKeptText(decision.kept)}`);
|
|
3819
|
+
return {
|
|
3820
|
+
removed: false,
|
|
3821
|
+
...decision.plugin === void 0 ? {} : { name: decision.plugin },
|
|
3822
|
+
kept: decision.kept
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
await removeFiles(decision.plugin);
|
|
3826
|
+
ledger.drop(key);
|
|
3827
|
+
logger?.info?.(`[dsh-workbench] ${pluginRemovedText(key)}`);
|
|
3828
|
+
return {
|
|
3829
|
+
removed: true,
|
|
3830
|
+
name: decision.plugin
|
|
3831
|
+
};
|
|
3832
|
+
},
|
|
3833
|
+
async sweep() {
|
|
3834
|
+
if (ledger === void 0) return [];
|
|
3835
|
+
const removed = [];
|
|
3836
|
+
for (const entry of ledger.all()) {
|
|
3837
|
+
if (entry.releasedAt === void 0) continue;
|
|
3838
|
+
const decision = decide(entry, false);
|
|
3839
|
+
if (decision.act === "keep") continue;
|
|
3840
|
+
try {
|
|
3841
|
+
await removeFiles(decision.plugin);
|
|
3842
|
+
} catch (error) {
|
|
3843
|
+
logger?.warn?.(`[dsh-workbench] 清 ${entry.package} 没成:${String(error)}`);
|
|
3844
|
+
continue;
|
|
3845
|
+
}
|
|
3846
|
+
ledger.drop(entry.package);
|
|
3847
|
+
removed.push(entry.package);
|
|
3848
|
+
logger?.info?.(`[dsh-workbench] ${pluginSweptText(entry.package)}`);
|
|
3849
|
+
}
|
|
3850
|
+
return removed;
|
|
3851
|
+
}
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
//#endregion
|
|
3855
|
+
//#region src/modules/panels/model/market-env.ts
|
|
3856
|
+
function createEnvDropper(options) {
|
|
3857
|
+
const { catalog, isReleased, ledger, command, run, logger } = options;
|
|
3858
|
+
function referencesOf() {
|
|
3859
|
+
return options.references?.() ?? {
|
|
3860
|
+
trustworthy: false,
|
|
3861
|
+
unreadable: []
|
|
3862
|
+
};
|
|
3863
|
+
}
|
|
3864
|
+
function usersOf(plugin, except) {
|
|
3865
|
+
return catalog().filter((item) => item.id !== except && !(isReleased?.(item.id) ?? false) && (item.env?.plugins ?? []).some((need) => need.name === plugin)).map((item) => item.id);
|
|
3866
|
+
}
|
|
3867
|
+
function libUsersOf(need, unit) {
|
|
3868
|
+
return catalog().filter((item) => item.id !== unit && !(isReleased?.(item.id) ?? false) && (item.env?.libs ?? []).some((other) => other.name === need.name)).map((item) => item.id);
|
|
3869
|
+
}
|
|
3870
|
+
return {
|
|
3871
|
+
usersOf,
|
|
3872
|
+
async drop(unit, env, before) {
|
|
3873
|
+
const kept = /* @__PURE__ */ new Set();
|
|
3874
|
+
const onMachine = new Set((before.plugins ?? []).filter((state) => state.status !== "missing").map((state) => state.name));
|
|
3875
|
+
const unknown = before.plugins === void 0;
|
|
3876
|
+
for (const need of env?.plugins ?? []) {
|
|
3877
|
+
const users = usersOf(need.name, unit);
|
|
3878
|
+
const retention = pluginRetentionOf({
|
|
3879
|
+
held: ledger?.heldAsPlugin(need.name) !== void 0,
|
|
3880
|
+
claimedBy: users,
|
|
3881
|
+
trustworthy: referencesOf().trustworthy
|
|
3882
|
+
});
|
|
3883
|
+
if (retention !== "remove") {
|
|
3884
|
+
for (const id of users) kept.add(id);
|
|
3885
|
+
const reason = pluginKeptOf(retention, {
|
|
3886
|
+
by: users,
|
|
3887
|
+
unreadable: referencesOf().unreadable
|
|
3888
|
+
});
|
|
3889
|
+
logger?.info?.(`[dsh-workbench] ${pluginKeptText(reason)}`);
|
|
3890
|
+
continue;
|
|
3891
|
+
}
|
|
3892
|
+
if (!unknown && !onMachine.has(need.name)) continue;
|
|
3893
|
+
await run(`${quoteShellArg(command)} plugin uninstall ${quoteShellArg(need.name)}`);
|
|
3894
|
+
}
|
|
3895
|
+
for (const need of env?.libs ?? []) {
|
|
3896
|
+
const keepers = libUsersOf(need, unit);
|
|
3897
|
+
if (keepers.length > 0) {
|
|
3898
|
+
for (const id of keepers) kept.add(id);
|
|
3899
|
+
continue;
|
|
3900
|
+
}
|
|
3901
|
+
const present = (before.libs ?? []).some((state) => state.name === need.name && state.status !== "missing");
|
|
3902
|
+
if (before.libs !== void 0 && !present) continue;
|
|
3903
|
+
await run(`npm uninstall --prefix ${quoteShellArg(libsPrefix())} ${quoteShellArg(need.name)}`, true);
|
|
3904
|
+
}
|
|
3905
|
+
return [...kept];
|
|
3906
|
+
}
|
|
3907
|
+
};
|
|
3908
|
+
}
|
|
3909
|
+
//#endregion
|
|
3910
|
+
//#region src/modules/panels/model/market.ts
|
|
2995
3911
|
function createMarketService(options) {
|
|
2996
|
-
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, registries } = options;
|
|
3912
|
+
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, registries, ledger, references } = options;
|
|
2997
3913
|
const command = options.command ?? "opencli";
|
|
2998
3914
|
const catalog = () => (manifestsNow?.() ?? manifests).filter((manifest) => (manifest.env?.plugins ?? []).length > 0 || (manifest.env?.libs ?? []).length > 0);
|
|
2999
3915
|
function requireEntry(unit) {
|
|
@@ -3098,30 +4014,28 @@ function createMarketService(options) {
|
|
|
3098
4014
|
if (bundles === void 0) throw new Error("这个 profile 里没有插件管理器,装不了模块包。");
|
|
3099
4015
|
return bundles;
|
|
3100
4016
|
}
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
await run(`npm uninstall --prefix ${quoteShellArg(libsPrefix())} ${quoteShellArg(need.name)}`, true);
|
|
4017
|
+
const envDropper = createEnvDropper({
|
|
4018
|
+
catalog,
|
|
4019
|
+
...isReleased === void 0 ? {} : { isReleased },
|
|
4020
|
+
...ledger === void 0 ? {} : { ledger },
|
|
4021
|
+
...references === void 0 ? {} : { references },
|
|
4022
|
+
command,
|
|
4023
|
+
run,
|
|
4024
|
+
...logger === void 0 ? {} : { logger }
|
|
4025
|
+
});
|
|
4026
|
+
function pluginUsersOf(plugin, except) {
|
|
4027
|
+
return envDropper.usersOf(plugin, except);
|
|
4028
|
+
}
|
|
4029
|
+
async function reprobePlugin(pkg, name) {
|
|
4030
|
+
const keys = name === void 0 || name === pkg ? [pkg] : [name, pkg];
|
|
4031
|
+
for (const key of keys) {
|
|
4032
|
+
const id = `plugin:${key}`;
|
|
4033
|
+
if (findDependency(catalog(), id, isReleased) === void 0) continue;
|
|
4034
|
+
await probeSharing({
|
|
4035
|
+
action: "update-dependency",
|
|
4036
|
+
id
|
|
4037
|
+
});
|
|
3123
4038
|
}
|
|
3124
|
-
return [...kept];
|
|
3125
4039
|
}
|
|
3126
4040
|
async function supplyAfter(module, change) {
|
|
3127
4041
|
if (module.kind !== "module") return { state: "not-a-module" };
|
|
@@ -3172,6 +4086,38 @@ function createMarketService(options) {
|
|
|
3172
4086
|
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
3173
4087
|
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
3174
4088
|
}
|
|
4089
|
+
function routeDeps(manager) {
|
|
4090
|
+
return {
|
|
4091
|
+
manager,
|
|
4092
|
+
command,
|
|
4093
|
+
runAll,
|
|
4094
|
+
rememberSource: rememberInstalledSource,
|
|
4095
|
+
supplyAfter,
|
|
4096
|
+
...shell === void 0 ? {} : { shell },
|
|
4097
|
+
...installed === void 0 ? {} : { installed },
|
|
4098
|
+
...ledger === void 0 ? {} : { ledger },
|
|
4099
|
+
...logger === void 0 ? {} : { logger }
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
const plugins = createPluginActions({
|
|
4103
|
+
...ledger === void 0 ? {} : { ledger },
|
|
4104
|
+
...shell === void 0 ? {} : { shell },
|
|
4105
|
+
command,
|
|
4106
|
+
runAll,
|
|
4107
|
+
claimedBy: (plugin) => pluginUsersOf(plugin),
|
|
4108
|
+
...references === void 0 ? {} : { references },
|
|
4109
|
+
...logger === void 0 ? {} : { logger }
|
|
4110
|
+
});
|
|
4111
|
+
async function installByRoute(spec, options) {
|
|
4112
|
+
const manager = requireBundles();
|
|
4113
|
+
const registry = options?.registry ?? (await manager.registries().catch(() => null))?.resolved ?? void 0;
|
|
4114
|
+
return installRouted({
|
|
4115
|
+
...routeDeps(manager),
|
|
4116
|
+
spec,
|
|
4117
|
+
...options === void 0 ? {} : { options },
|
|
4118
|
+
...registry === void 0 ? {} : { registry }
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
3175
4121
|
const service = {
|
|
3176
4122
|
async list() {
|
|
3177
4123
|
return snapshot();
|
|
@@ -3258,7 +4204,8 @@ function createMarketService(options) {
|
|
|
3258
4204
|
};
|
|
3259
4205
|
}
|
|
3260
4206
|
const before = await capabilityOf(unit, true);
|
|
3261
|
-
const keptFor = await
|
|
4207
|
+
const keptFor = await envDropper.drop(unit, requireEntry(unit).env, before);
|
|
4208
|
+
await plugins.sweep();
|
|
3262
4209
|
await probeSharing({
|
|
3263
4210
|
action: "uninstall",
|
|
3264
4211
|
unit
|
|
@@ -3277,25 +4224,7 @@ function createMarketService(options) {
|
|
|
3277
4224
|
};
|
|
3278
4225
|
},
|
|
3279
4226
|
async installBundle(spec, options) {
|
|
3280
|
-
|
|
3281
|
-
const change = await manager.install({
|
|
3282
|
-
spec,
|
|
3283
|
-
...options?.registry === void 0 ? {} : { registry: options.registry },
|
|
3284
|
-
...options?.requestId === void 0 ? {} : { requestId: options.requestId },
|
|
3285
|
-
...options?.approvedBuilds === void 0 ? {} : { approvedBuilds: options.approvedBuilds }
|
|
3286
|
-
});
|
|
3287
|
-
if (change === null) throw new Error("插件管理器回的安装结果看不懂。");
|
|
3288
|
-
const pkg = bundleNameOf(change, spec);
|
|
3289
|
-
const module = installed?.(pkg) ?? {
|
|
3290
|
-
kind: "unknown",
|
|
3291
|
-
pkg
|
|
3292
|
-
};
|
|
3293
|
-
await rememberInstalledSource(manager, pkg, options?.registry);
|
|
3294
|
-
return {
|
|
3295
|
-
change,
|
|
3296
|
-
module,
|
|
3297
|
-
supply: await supplyAfter(module, change)
|
|
3298
|
-
};
|
|
4227
|
+
return installByRoute(spec, options);
|
|
3299
4228
|
},
|
|
3300
4229
|
async removeBundle(pkg) {
|
|
3301
4230
|
const manager = requireBundles();
|
|
@@ -3313,7 +4242,8 @@ function createMarketService(options) {
|
|
|
3313
4242
|
change,
|
|
3314
4243
|
keptFor: []
|
|
3315
4244
|
};
|
|
3316
|
-
const keptFor = manifest === void 0 || before === void 0 ? [] : await
|
|
4245
|
+
const keptFor = manifest === void 0 || before === void 0 ? [] : await envDropper.drop(unit, manifest.env, before);
|
|
4246
|
+
await plugins.sweep();
|
|
3317
4247
|
onUninstalled?.(unit);
|
|
3318
4248
|
return {
|
|
3319
4249
|
change,
|
|
@@ -3338,10 +4268,21 @@ function createMarketService(options) {
|
|
|
3338
4268
|
force: true
|
|
3339
4269
|
});
|
|
3340
4270
|
const spec = latest === void 0 ? pkg : `${pkg}@${latest}`;
|
|
3341
|
-
const outcome = await
|
|
4271
|
+
const outcome = await installDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
|
|
4272
|
+
await plugins.sweep();
|
|
3342
4273
|
await service.update(unit).catch(() => void 0);
|
|
3343
4274
|
return outcome;
|
|
3344
4275
|
},
|
|
4276
|
+
async updatePlugin(pkg) {
|
|
4277
|
+
const outcome = await plugins.update(pkg);
|
|
4278
|
+
await reprobePlugin(pkg, outcome.name);
|
|
4279
|
+
return outcome;
|
|
4280
|
+
},
|
|
4281
|
+
async uninstallPlugin(pkg) {
|
|
4282
|
+
const outcome = await plugins.uninstall(pkg);
|
|
4283
|
+
await reprobePlugin(pkg, outcome.name);
|
|
4284
|
+
return outcome;
|
|
4285
|
+
},
|
|
3345
4286
|
updateDependency: updateDependencyRow
|
|
3346
4287
|
};
|
|
3347
4288
|
return service;
|
|
@@ -3396,6 +4337,22 @@ async function dispatchMarket(market, request) {
|
|
|
3396
4337
|
action: "cancel-install",
|
|
3397
4338
|
cancellation: await market.cancelInstall(request.requestId ?? "")
|
|
3398
4339
|
};
|
|
4340
|
+
if (request.action === "update-plugin") {
|
|
4341
|
+
await market.updatePlugin(request.name ?? "");
|
|
4342
|
+
return {
|
|
4343
|
+
ok: true,
|
|
4344
|
+
action: "update-plugin"
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
if (request.action === "uninstall-plugin") {
|
|
4348
|
+
const outcome = await market.uninstallPlugin(request.name ?? "");
|
|
4349
|
+
return {
|
|
4350
|
+
ok: true,
|
|
4351
|
+
action: "uninstall-plugin",
|
|
4352
|
+
removed: outcome.removed,
|
|
4353
|
+
...outcome.kept === void 0 ? {} : { kept: outcome.kept }
|
|
4354
|
+
};
|
|
4355
|
+
}
|
|
3399
4356
|
const unit = request.unit ?? "";
|
|
3400
4357
|
if (request.action === "uninstall") {
|
|
3401
4358
|
const { entry, keptFor, change } = await market.uninstall(unit);
|
|
@@ -3470,13 +4427,24 @@ var OUTPUT_SCHEMA$1 = {
|
|
|
3470
4427
|
},
|
|
3471
4428
|
additionalProperties: true
|
|
3472
4429
|
};
|
|
4430
|
+
function describePluginUninstall(pkg, outcome) {
|
|
4431
|
+
if (outcome.removed) return `已卸载 opencli 插件 ${pkg}:没人再用它,文件也删了。`;
|
|
4432
|
+
return `已从「你装的插件」里去掉 ${pkg}。${outcome.kept === void 0 ? "" : pluginKeptText(outcome.kept)}`;
|
|
4433
|
+
}
|
|
4434
|
+
function usersOf(shared, names) {
|
|
4435
|
+
const hit = shared.find((item) => names.some((name) => name !== void 0 && name !== "" && item.id === `plugin:${name}`));
|
|
4436
|
+
if (hit === void 0 || hit.usedBy.length === 0) return "";
|
|
4437
|
+
return `它被 ${hit.usedBy.join("、")} 用着(动的是它们的供给,它们的状态跟着重探了一遍)`;
|
|
4438
|
+
}
|
|
3473
4439
|
var ACTIONS = [
|
|
3474
4440
|
"list",
|
|
3475
4441
|
"install",
|
|
3476
4442
|
"update",
|
|
3477
4443
|
"uninstall",
|
|
3478
4444
|
"install-bundle",
|
|
3479
|
-
"update-dependency"
|
|
4445
|
+
"update-dependency",
|
|
4446
|
+
"update-plugin",
|
|
4447
|
+
"uninstall-plugin"
|
|
3480
4448
|
];
|
|
3481
4449
|
function registerMarketTools(ctx, market) {
|
|
3482
4450
|
const tools = ctx.get("tools");
|
|
@@ -3486,13 +4454,13 @@ function registerMarketTools(ctx, market) {
|
|
|
3486
4454
|
}
|
|
3487
4455
|
const tool = {
|
|
3488
4456
|
name: "workbench_modules",
|
|
3489
|
-
description: "看 / 装 / 升 /
|
|
4457
|
+
description: "看 / 装 / 升 / 卸工作台的面板模块,以及用户自己装的 opencli 插件。install-bundle 按 spec(包名,可带版本;git / tarball / 本地路径也行)把模块包装进 profile —— 装完会连带装它声明的 opencli 插件;其余 action 按模块 id 动它要的外部供给;update-plugin / uninstall-plugin 按包名或插件名动用户装的那份插件。卸载只动工作台运行时,不影响终端那套;共用依赖只有升级。",
|
|
3490
4458
|
parameters: {
|
|
3491
4459
|
type: "object",
|
|
3492
4460
|
properties: {
|
|
3493
4461
|
action: {
|
|
3494
4462
|
type: "string",
|
|
3495
|
-
description: "install-bundle=按 spec 装模块包(要 spec),update-dependency
|
|
4463
|
+
description: "install-bundle=按 spec 装模块包(要 spec),update-dependency=升级共用依赖(要 id),update-plugin / uninstall-plugin=动用户自己装的 opencli 插件(要 plugin),其余=对模块(要 id)。",
|
|
3496
4464
|
enum: [...ACTIONS]
|
|
3497
4465
|
},
|
|
3498
4466
|
id: {
|
|
@@ -3506,6 +4474,10 @@ function registerMarketTools(ctx, market) {
|
|
|
3506
4474
|
registry: {
|
|
3507
4475
|
type: "string",
|
|
3508
4476
|
description: "install-bundle 用:先问哪个源(不填就用这个 profile 自己配的源)。"
|
|
4477
|
+
},
|
|
4478
|
+
plugin: {
|
|
4479
|
+
type: "string",
|
|
4480
|
+
description: "update-plugin / uninstall-plugin 用:opencli 插件的包名或插件名(就是市场「已安装 opencli 插件」那一段里的名字,如 cmdb 或 opencli-plugin-cmdb)。"
|
|
3509
4481
|
}
|
|
3510
4482
|
},
|
|
3511
4483
|
required: ["action"],
|
|
@@ -3544,6 +4516,23 @@ function registerMarketTools(ctx, market) {
|
|
|
3544
4516
|
text: describeInstall(spec, outcome.module, outcome.supply, outcome.change)
|
|
3545
4517
|
};
|
|
3546
4518
|
}
|
|
4519
|
+
if (action === "update-plugin" || action === "uninstall-plugin") {
|
|
4520
|
+
const plugin = args?.plugin?.trim() ?? "";
|
|
4521
|
+
if (plugin === "") throw new Error(`${action} 需要一个 plugin:opencli 插件的包名或插件名 —— 就是市场「已安装 opencli 插件」那一段里的名字。`);
|
|
4522
|
+
if (action === "update-plugin") {
|
|
4523
|
+
const updated = await market.updatePlugin(plugin);
|
|
4524
|
+
const users = usersOf((await market.list()).shared, [updated.name, plugin]);
|
|
4525
|
+
return { text: `已更新 opencli 插件 ${plugin}${users === "" ? "。" : `;${users}。`}` };
|
|
4526
|
+
}
|
|
4527
|
+
const outcome = await market.uninstallPlugin(plugin);
|
|
4528
|
+
const users = usersOf((await market.list()).shared, [outcome.name, plugin]);
|
|
4529
|
+
return {
|
|
4530
|
+
removed: outcome.removed,
|
|
4531
|
+
...outcome.name === void 0 ? {} : { name: outcome.name },
|
|
4532
|
+
...outcome.kept === void 0 ? {} : { kept: outcome.kept },
|
|
4533
|
+
text: `${describePluginUninstall(plugin, outcome)}${users === "" ? "" : `${users}。`}`
|
|
4534
|
+
};
|
|
4535
|
+
}
|
|
3547
4536
|
const id = args?.id;
|
|
3548
4537
|
if (id === void 0 || id.trim() === "") {
|
|
3549
4538
|
const { modules, shared } = await market.list();
|
|
@@ -3594,7 +4583,7 @@ function applyPanelsPref(panels, payload) {
|
|
|
3594
4583
|
const path = action.path ?? null;
|
|
3595
4584
|
if (action.action === "reset") return {
|
|
3596
4585
|
ok: true,
|
|
3597
|
-
units: panels.
|
|
4586
|
+
units: panels.reset(path)
|
|
3598
4587
|
};
|
|
3599
4588
|
if (action.id === void 0) throw new Error(`${action.action} 需要一个 id(当前套件:${panels.list(path).map((entry) => entry.id).join(", ")})`);
|
|
3600
4589
|
if (action.action === "show") return {
|
|
@@ -3655,66 +4644,12 @@ function panelsFactsOf(call, manifests) {
|
|
|
3655
4644
|
];
|
|
3656
4645
|
}
|
|
3657
4646
|
//#endregion
|
|
3658
|
-
//#region src/modules/panels/model/config.ts
|
|
3659
|
-
function strings(raw) {
|
|
3660
|
-
return Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
|
|
3661
|
-
}
|
|
3662
|
-
function parseShown(raw) {
|
|
3663
|
-
if (typeof raw !== "object" || raw === null) return [];
|
|
3664
|
-
return strings(raw.shown);
|
|
3665
|
-
}
|
|
3666
|
-
function parseReleased(raw) {
|
|
3667
|
-
if (typeof raw !== "object" || raw === null) return [];
|
|
3668
|
-
return strings(raw.ids);
|
|
3669
|
-
}
|
|
3670
|
-
function resolvePanels(manifests, panels) {
|
|
3671
|
-
const shown = panels ?? [];
|
|
3672
|
-
const rank = new Map(shown.map((id, index) => [id, index]));
|
|
3673
|
-
return manifests.filter((manifest) => manifest.card !== null).map((manifest) => {
|
|
3674
|
-
const isShown = rank.has(manifest.id);
|
|
3675
|
-
const order = rank.get(manifest.id) ?? shown.length + (manifest.card?.order ?? 0);
|
|
3676
|
-
return {
|
|
3677
|
-
id: manifest.id,
|
|
3678
|
-
titleKey: manifest.card?.titleKey ?? manifest.id,
|
|
3679
|
-
order,
|
|
3680
|
-
shown: isShown,
|
|
3681
|
-
source: isShown ? "user" : "default",
|
|
3682
|
-
hasCard: true
|
|
3683
|
-
};
|
|
3684
|
-
}).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
3685
|
-
}
|
|
3686
|
-
function withoutReleased(released, id) {
|
|
3687
|
-
return released.filter((item) => item !== id);
|
|
3688
|
-
}
|
|
3689
|
-
function withReleased(released, id) {
|
|
3690
|
-
return [.../* @__PURE__ */ new Set([...released, id])];
|
|
3691
|
-
}
|
|
3692
|
-
function withShown(panels, id) {
|
|
3693
|
-
return panels.includes(id) ? [...panels] : [...panels, id];
|
|
3694
|
-
}
|
|
3695
|
-
function withHidden(panels, id) {
|
|
3696
|
-
return panels.filter((item) => item !== id);
|
|
3697
|
-
}
|
|
3698
|
-
function withMoved(panels, id, index) {
|
|
3699
|
-
const current = [...panels];
|
|
3700
|
-
const from = current.indexOf(id);
|
|
3701
|
-
const max = from === -1 ? current.length : current.length - 1;
|
|
3702
|
-
const to = Math.max(0, Math.min(max, Math.trunc(index)));
|
|
3703
|
-
if (from === -1) {
|
|
3704
|
-
current.splice(to, 0, id);
|
|
3705
|
-
return current;
|
|
3706
|
-
}
|
|
3707
|
-
if (from === to) return current;
|
|
3708
|
-
current.splice(to, 0, ...current.splice(from, 1));
|
|
3709
|
-
return current;
|
|
3710
|
-
}
|
|
3711
|
-
//#endregion
|
|
3712
4647
|
//#region src/modules/panels/model/service.ts
|
|
3713
4648
|
var PANELS_MODULE_ID = "panels";
|
|
3714
4649
|
var PANELS_FILE = "config.json";
|
|
3715
4650
|
var RELEASED_FILE = "released.json";
|
|
3716
4651
|
function createPanelsService(options) {
|
|
3717
|
-
const { manifests, store, logger } = options;
|
|
4652
|
+
const { manifests, store, owned, logger } = options;
|
|
3718
4653
|
const listeners = /* @__PURE__ */ new Set();
|
|
3719
4654
|
const machine = store.machine(PANELS_MODULE_ID);
|
|
3720
4655
|
const folders = /* @__PURE__ */ new Map();
|
|
@@ -3725,12 +4660,36 @@ function createPanelsService(options) {
|
|
|
3725
4660
|
folders.set(workspace, folder);
|
|
3726
4661
|
return folder;
|
|
3727
4662
|
}
|
|
3728
|
-
function
|
|
3729
|
-
|
|
4663
|
+
function readBoard(folder) {
|
|
4664
|
+
const raw = folder.read(PANELS_FILE);
|
|
4665
|
+
return raw === null ? null : parseBoard(raw);
|
|
4666
|
+
}
|
|
4667
|
+
function boardIn(folder) {
|
|
4668
|
+
return readBoard(folder) ?? EMPTY_BOARD;
|
|
4669
|
+
}
|
|
4670
|
+
function writeBoard(folder, next) {
|
|
4671
|
+
folder.write(PANELS_FILE, {
|
|
4672
|
+
shown: next.shown,
|
|
4673
|
+
hidden: next.hidden
|
|
4674
|
+
});
|
|
3730
4675
|
}
|
|
3731
|
-
function
|
|
3732
|
-
|
|
3733
|
-
|
|
4676
|
+
function boardsOf(workspace) {
|
|
4677
|
+
const key = workspace?.trim() ?? "";
|
|
4678
|
+
return {
|
|
4679
|
+
workspace: key === "" ? null : readBoard(folderOf(key)),
|
|
4680
|
+
machine: readBoard(machine)
|
|
4681
|
+
};
|
|
4682
|
+
}
|
|
4683
|
+
function targetOf(target, workspace) {
|
|
4684
|
+
if (target === "machine") return {
|
|
4685
|
+
folder: machine,
|
|
4686
|
+
key: null
|
|
4687
|
+
};
|
|
4688
|
+
const key = requireWorkspace(workspace);
|
|
4689
|
+
return {
|
|
4690
|
+
folder: folderOf(key),
|
|
4691
|
+
key
|
|
4692
|
+
};
|
|
3734
4693
|
}
|
|
3735
4694
|
let released;
|
|
3736
4695
|
function readReleased() {
|
|
@@ -3749,10 +4708,10 @@ function createPanelsService(options) {
|
|
|
3749
4708
|
logger?.warn?.(`[dsh-workbench] 面板配置订阅者报错:${String(error)}`);
|
|
3750
4709
|
}
|
|
3751
4710
|
}
|
|
3752
|
-
function withPersisted(
|
|
3753
|
-
|
|
4711
|
+
function withPersisted(folder, next, key) {
|
|
4712
|
+
writeBoard(folder, next);
|
|
3754
4713
|
notify();
|
|
3755
|
-
return resolvePanels(manifests(),
|
|
4714
|
+
return resolvePanels(manifests(), boardsOf(key));
|
|
3756
4715
|
}
|
|
3757
4716
|
function takeBackReleased(id) {
|
|
3758
4717
|
const current = readReleased();
|
|
@@ -3773,16 +4732,19 @@ function createPanelsService(options) {
|
|
|
3773
4732
|
return key;
|
|
3774
4733
|
}
|
|
3775
4734
|
return {
|
|
3776
|
-
list(workspace) {
|
|
3777
|
-
|
|
3778
|
-
|
|
4735
|
+
list(workspace, target) {
|
|
4736
|
+
return resolvePanels(manifests(), target === "machine" ? boardsOf(null) : boardsOf(workspace));
|
|
4737
|
+
},
|
|
4738
|
+
boards(workspace) {
|
|
4739
|
+
return boardsOf(workspace);
|
|
3779
4740
|
},
|
|
3780
4741
|
isEnabled(id, workspace) {
|
|
3781
|
-
|
|
3782
|
-
if (
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
4742
|
+
if (!manifests().some((item) => item.id === id)) return false;
|
|
4743
|
+
if (owned?.().includes(id) === true) return true;
|
|
4744
|
+
return resolveScope({
|
|
4745
|
+
id,
|
|
4746
|
+
boards: boardsOf(workspace)
|
|
4747
|
+
});
|
|
3786
4748
|
},
|
|
3787
4749
|
subscribe(listener) {
|
|
3788
4750
|
listeners.add(listener);
|
|
@@ -3796,36 +4758,40 @@ function createPanelsService(options) {
|
|
|
3796
4758
|
const seen = /* @__PURE__ */ new Set();
|
|
3797
4759
|
const drop = (folder) => {
|
|
3798
4760
|
seen.add(folder.dir);
|
|
3799
|
-
const current =
|
|
3800
|
-
if (current
|
|
4761
|
+
const current = readBoard(folder);
|
|
4762
|
+
if (current === null) return;
|
|
4763
|
+
if (!current.shown.includes(id) && !current.hidden.includes(id)) return;
|
|
4764
|
+
writeBoard(folder, withoutPanel(current, id));
|
|
3801
4765
|
};
|
|
3802
4766
|
for (const folder of store.scan(PANELS_MODULE_ID)) drop(folder);
|
|
3803
4767
|
for (const folder of folders.values()) if (!seen.has(folder.dir)) drop(folder);
|
|
4768
|
+
drop(machine);
|
|
3804
4769
|
notify();
|
|
3805
4770
|
},
|
|
3806
4771
|
accept(id) {
|
|
3807
4772
|
takeBackReleased(id);
|
|
3808
4773
|
},
|
|
3809
|
-
show(id, workspace) {
|
|
4774
|
+
show(id, workspace, target) {
|
|
3810
4775
|
requireSwitchable(id);
|
|
3811
|
-
const key =
|
|
4776
|
+
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
3812
4777
|
takeBackReleased(id);
|
|
3813
|
-
return withPersisted(
|
|
4778
|
+
return withPersisted(folder, withShown(boardIn(folder), id), key);
|
|
3814
4779
|
},
|
|
3815
|
-
hide(id, workspace) {
|
|
4780
|
+
hide(id, workspace, target) {
|
|
3816
4781
|
requireSwitchable(id);
|
|
3817
|
-
const key =
|
|
3818
|
-
return withPersisted(
|
|
4782
|
+
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
4783
|
+
return withPersisted(folder, withHidden(boardIn(folder), id), key);
|
|
3819
4784
|
},
|
|
3820
|
-
move(id, index, workspace) {
|
|
4785
|
+
move(id, index, workspace, target) {
|
|
3821
4786
|
requireSwitchable(id);
|
|
3822
|
-
const key =
|
|
3823
|
-
const current =
|
|
3824
|
-
if (!current.includes(id)) takeBackReleased(id);
|
|
3825
|
-
return withPersisted(
|
|
4787
|
+
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
4788
|
+
const current = boardIn(folder);
|
|
4789
|
+
if (!current.shown.includes(id)) takeBackReleased(id);
|
|
4790
|
+
return withPersisted(folder, withMoved(current, id, index), key);
|
|
3826
4791
|
},
|
|
3827
|
-
|
|
3828
|
-
|
|
4792
|
+
reset(workspace, target) {
|
|
4793
|
+
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
4794
|
+
return withPersisted(folder, EMPTY_BOARD, key);
|
|
3829
4795
|
}
|
|
3830
4796
|
};
|
|
3831
4797
|
}
|
|
@@ -3847,11 +4813,119 @@ function panelRoutes(panels, values) {
|
|
|
3847
4813
|
} };
|
|
3848
4814
|
}
|
|
3849
4815
|
//#endregion
|
|
4816
|
+
//#region src/modules/panels/model/plugin-ledger.ts
|
|
4817
|
+
function createPluginLedger(options) {
|
|
4818
|
+
const folder = options.store.machine(PANELS_MODULE_ID);
|
|
4819
|
+
const home = options.home;
|
|
4820
|
+
const all = () => readPluginLedger(folder);
|
|
4821
|
+
const save = (entries) => writePluginLedger(folder, entries);
|
|
4822
|
+
return {
|
|
4823
|
+
home,
|
|
4824
|
+
list: () => heldEntriesOfHome(all(), home),
|
|
4825
|
+
all: () => pluginEntriesOfHome(all(), home),
|
|
4826
|
+
read: (pkg) => {
|
|
4827
|
+
const entry = pluginEntryOf(all(), home, pkg);
|
|
4828
|
+
return entry !== void 0 && entry.releasedAt === void 0 ? entry : void 0;
|
|
4829
|
+
},
|
|
4830
|
+
held: (pkg) => {
|
|
4831
|
+
const entry = pluginEntryOf(all(), home, pkg);
|
|
4832
|
+
return entry !== void 0 && entry.releasedAt === void 0;
|
|
4833
|
+
},
|
|
4834
|
+
heldAsPlugin: (plugin) => {
|
|
4835
|
+
const key = plugin.trim();
|
|
4836
|
+
if (key === "") return void 0;
|
|
4837
|
+
const known = heldEntryOfPlugin(all(), home, key);
|
|
4838
|
+
if (known !== void 0) return known;
|
|
4839
|
+
return heldEntriesOfHome(all(), home).find((entry) => pluginNameOf(entry) === key);
|
|
4840
|
+
},
|
|
4841
|
+
intend(intent) {
|
|
4842
|
+
const registry = intent.registry?.trim() ?? "";
|
|
4843
|
+
save(withPluginEntry(all(), {
|
|
4844
|
+
opencliHome: home,
|
|
4845
|
+
package: intent.package,
|
|
4846
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4847
|
+
...registry === "" ? {} : { registry },
|
|
4848
|
+
...intent.local === true ? { local: true } : {}
|
|
4849
|
+
}));
|
|
4850
|
+
},
|
|
4851
|
+
complete(pkg, name) {
|
|
4852
|
+
const trimmed = name?.trim() ?? "";
|
|
4853
|
+
if (trimmed === "") return;
|
|
4854
|
+
const current = pluginEntryOf(all(), home, pkg);
|
|
4855
|
+
if (current === void 0 || current.releasedAt !== void 0) return;
|
|
4856
|
+
save(withPluginEntry(all(), {
|
|
4857
|
+
...current,
|
|
4858
|
+
name: trimmed
|
|
4859
|
+
}));
|
|
4860
|
+
},
|
|
4861
|
+
release(pkg) {
|
|
4862
|
+
save(withReleasedEntry(all(), home, pkg, (/* @__PURE__ */ new Date()).toISOString()));
|
|
4863
|
+
},
|
|
4864
|
+
drop(pkg) {
|
|
4865
|
+
save(withoutPluginEntry(all(), home, pkg));
|
|
4866
|
+
}
|
|
4867
|
+
};
|
|
4868
|
+
}
|
|
4869
|
+
//#endregion
|
|
4870
|
+
//#region src/shared/api/installed-plugins.ts
|
|
4871
|
+
var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
|
|
4872
|
+
//#endregion
|
|
4873
|
+
//#region src/modules/panels/model/plugin-section.ts
|
|
4874
|
+
async function installedPlugins(options) {
|
|
4875
|
+
const { ledger, shell, force = false } = options;
|
|
4876
|
+
const rows = [];
|
|
4877
|
+
for (const entry of ledger.list()) {
|
|
4878
|
+
const release = shell === void 0 || entry.local === true || entry.registry === void 0 ? void 0 : await probeRelease({
|
|
4879
|
+
shell,
|
|
4880
|
+
package: entry.package,
|
|
4881
|
+
registry: entry.registry,
|
|
4882
|
+
force
|
|
4883
|
+
});
|
|
4884
|
+
rows.push({
|
|
4885
|
+
pkg: entry.package,
|
|
4886
|
+
...entry.name === void 0 ? {} : { name: entry.name },
|
|
4887
|
+
...release === void 0 ? {} : {
|
|
4888
|
+
installed: release.installed,
|
|
4889
|
+
...release.latest === void 0 ? {} : { latest: release.latest }
|
|
4890
|
+
},
|
|
4891
|
+
...entry.local === true ? { local: true } : {},
|
|
4892
|
+
...pluginOnDisk(entry) ? {} : { broken: true }
|
|
4893
|
+
});
|
|
4894
|
+
}
|
|
4895
|
+
return rows;
|
|
4896
|
+
}
|
|
4897
|
+
function pluginRoutes(options) {
|
|
4898
|
+
return { [INSTALLED_PLUGINS_ROUTE]: async (search) => {
|
|
4899
|
+
return {
|
|
4900
|
+
ok: true,
|
|
4901
|
+
plugins: await installedPlugins({
|
|
4902
|
+
ledger: options.ledger,
|
|
4903
|
+
...options.shell === void 0 ? {} : { shell: options.shell },
|
|
4904
|
+
force: param(search, "force") === "1"
|
|
4905
|
+
})
|
|
4906
|
+
};
|
|
4907
|
+
} };
|
|
4908
|
+
}
|
|
4909
|
+
//#endregion
|
|
3850
4910
|
//#region src/modules/panels/model/tools.ts
|
|
3851
|
-
|
|
4911
|
+
var MACHINE_LABEL = "机器级默认板 —— 还没配过的工作区默认开这些:";
|
|
4912
|
+
var MACHINE_CAVEAT = "机器级默认板只是还没配过的工作区的默认值:已经自己配过的工作区保持自己的选择,不会被它改。";
|
|
4913
|
+
function listed(entries) {
|
|
4914
|
+
return entries.map((entry, index) => `${index + 1}. ${entry.id} · ${entry.shown ? "已启用" : "未启用"}${entry.hasCard ? "" : "(无卡片)"}${entry.source === "user" ? " · 用户改过" : ""}`).join("\n");
|
|
4915
|
+
}
|
|
4916
|
+
function render(entries, workspace, scope) {
|
|
4917
|
+
if (scope === "machine") {
|
|
4918
|
+
if (entries.length === 0) return "机器级默认板是空的(还没配过的工作区默认什么卡片都不开)——用 show 放一个上去。";
|
|
4919
|
+
return `${MACHINE_LABEL}\n${listed(entries)}`;
|
|
4920
|
+
}
|
|
3852
4921
|
const where = workspace === null ? "(不知道当前会话在哪个工作区)" : workspace;
|
|
3853
4922
|
if (entries.length === 0) return `这个工作区的面板是空的(${where})——用 show 放一个上去。`;
|
|
3854
|
-
return `当前工作区:${where}\n${entries
|
|
4923
|
+
return `当前工作区:${where}\n${listed(entries)}`;
|
|
4924
|
+
}
|
|
4925
|
+
function parseScope(raw) {
|
|
4926
|
+
if (raw === void 0) return DEFAULT_BOARD_TARGET;
|
|
4927
|
+
if (typeof raw === "string" && BOARD_TARGETS.includes(raw)) return raw;
|
|
4928
|
+
throw new Error(`不认识的 scope:${String(raw)}(可用:workspace / machine)`);
|
|
3855
4929
|
}
|
|
3856
4930
|
var OUTPUT_SCHEMA = {
|
|
3857
4931
|
type: "object",
|
|
@@ -3882,13 +4956,13 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
3882
4956
|
}
|
|
3883
4957
|
const tool = {
|
|
3884
4958
|
name: "workbench_panels",
|
|
3885
|
-
description: "
|
|
4959
|
+
description: "看或调右侧面板:启用哪些套件、什么顺序。启用/收起 = 卡片与 agent 工具一起上下线(别的项目不受影响,不删数据)。收起是\"这个工作区明确不要它\",连那些在机器级默认板上开着的套件也照样在这个工作区收起来,之后 show 能放回来。默认只动当前工作区(scope=workspace);scope=machine 动的是\"机器级默认板\" —— 机器级默认板只是还没配过的工作区的默认值,已经自己配过的工作区保持自己的选择,不会被它改。在那份板上 hide = 明确写下\"不要它\",这一笔比名单更管用:名单里还列着它也算不要,没配过的工作区因此看不到它;show 会把这笔划掉、把它放回去。",
|
|
3886
4960
|
parameters: {
|
|
3887
4961
|
type: "object",
|
|
3888
4962
|
properties: {
|
|
3889
4963
|
action: {
|
|
3890
4964
|
type: "string",
|
|
3891
|
-
description: "show
|
|
4965
|
+
description: "show=这个工作区要它(之前收起的也放回来);hide=这个工作区明确不要它(卡片与工具一起下线,默认在机器上也开着的套件同样收起,别的项目不受影响);move=调顺序;reset=这个工作区不再有自己的选择(之前收起、启用的都忘掉;它从此明确\"什么都不要\",机器级默认板也管不到它,要哪一块用 show 放回来)。",
|
|
3892
4966
|
enum: [
|
|
3893
4967
|
"list",
|
|
3894
4968
|
"show",
|
|
@@ -3904,6 +4978,11 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
3904
4978
|
index: {
|
|
3905
4979
|
type: "integer",
|
|
3906
4980
|
description: "move 的位置,0 基(0=最上面)。"
|
|
4981
|
+
},
|
|
4982
|
+
scope: {
|
|
4983
|
+
type: "string",
|
|
4984
|
+
description: "workspace(默认)=只动当前工作区;machine=动机器级默认板,那是还没配过的工作区的默认值 —— 已经自己配过的工作区保持自己的选择,不受它影响。",
|
|
4985
|
+
enum: ["workspace", "machine"]
|
|
3907
4986
|
}
|
|
3908
4987
|
},
|
|
3909
4988
|
required: ["action"],
|
|
@@ -3922,8 +5001,9 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
3922
5001
|
async execute(args, exec) {
|
|
3923
5002
|
const action = args?.action ?? "list";
|
|
3924
5003
|
const id = args?.id;
|
|
5004
|
+
const scope = parseScope(args?.scope);
|
|
3925
5005
|
const workspace = sessionWorkspace(exec);
|
|
3926
|
-
const list = () => panels.list(workspace);
|
|
5006
|
+
const list = () => panels.list(workspace, scope);
|
|
3927
5007
|
if (![
|
|
3928
5008
|
"list",
|
|
3929
5009
|
"show",
|
|
@@ -3933,39 +5013,41 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
3933
5013
|
].includes(action)) throw new Error(`不认识的 action:${action}(可用:list / show / hide / move / reset)`);
|
|
3934
5014
|
if (action === "list") {
|
|
3935
5015
|
const units = list();
|
|
3936
|
-
|
|
3937
|
-
|
|
5016
|
+
if (scope === "workspace") {
|
|
5017
|
+
const entry = panelsHandoffEntry(workspace === null ? {} : { path: workspace });
|
|
5018
|
+
if (entry !== void 0) values?.hand(entry, units);
|
|
5019
|
+
}
|
|
3938
5020
|
return {
|
|
3939
5021
|
units,
|
|
3940
|
-
text: render(units, workspace)
|
|
5022
|
+
text: render(units, workspace, scope)
|
|
3941
5023
|
};
|
|
3942
5024
|
}
|
|
3943
5025
|
if (action === "reset") {
|
|
3944
|
-
const units = panels.
|
|
5026
|
+
const units = panels.reset(workspace, scope);
|
|
3945
5027
|
return {
|
|
3946
5028
|
units,
|
|
3947
|
-
text:
|
|
5029
|
+
text: scope === "machine" ? `机器级默认板已清空(是"配成空",不是"没配过")——还没配过的工作区从此默认什么都不开。${MACHINE_CAVEAT}\n${render(units, workspace, scope)}` : `这个工作区不再有自己的面板选择(之前收起、启用的都忘掉了;它从此明确"什么都不要"—— 机器级默认板也管不到它,要哪一块用 show 放回来)。\n${render(units, workspace, scope)}`
|
|
3948
5030
|
};
|
|
3949
5031
|
}
|
|
3950
5032
|
if (id === void 0) throw new Error(`${action} 需要一个 id(例如 coding-issues)。当前套件:${list().map((entry) => entry.id).join(", ")}`);
|
|
3951
5033
|
if (action === "show") {
|
|
3952
|
-
const units = panels.show(id, workspace);
|
|
5034
|
+
const units = panels.show(id, workspace, scope);
|
|
3953
5035
|
return {
|
|
3954
5036
|
units,
|
|
3955
|
-
text: `已启用 ${id} ——
|
|
5037
|
+
text: scope === "machine" ? `已把 ${id} 放进机器级默认板 —— 还没配过的工作区会带上它(卡片与工具一起上线);那份板上要是给它写过"不要",这一下也划掉了。${MACHINE_CAVEAT}\n${render(units, workspace, scope)}` : `已启用 ${id} —— 这个工作区要它:之前收起的也放回来了,卡片与工具一起上线。\n${render(units, workspace, scope)}`
|
|
3956
5038
|
};
|
|
3957
5039
|
}
|
|
3958
5040
|
if (action === "hide") {
|
|
3959
|
-
const units = panels.hide(id, workspace);
|
|
5041
|
+
const units = panels.hide(id, workspace, scope);
|
|
3960
5042
|
return {
|
|
3961
5043
|
units,
|
|
3962
|
-
text: `已收起 ${id}
|
|
5044
|
+
text: scope === "machine" ? `已把 ${id} 从机器级默认板划掉,并在那份板上明确写下"不要它" —— 还没配过的工作区从此看不到它(卡片与工具一起下线);名单里还列着它也一样不算数,要放回来用 show。${MACHINE_CAVEAT}\n${render(units, workspace, scope)}` : `已收起 ${id} —— 这个工作区明确不要它:卡片与工具一起下线(默认在机器上也开着的套件同样收起),别的项目不受影响;要放回来用 show。\n${render(units, workspace, scope)}`
|
|
3963
5045
|
};
|
|
3964
5046
|
}
|
|
3965
|
-
const units = panels.move(id, args?.index ?? 0, workspace);
|
|
5047
|
+
const units = panels.move(id, args?.index ?? 0, workspace, scope);
|
|
3966
5048
|
return {
|
|
3967
5049
|
units,
|
|
3968
|
-
text: `已把 ${id} 放到第 ${(args?.index ?? 0) + 1} 位。\n${render(units, workspace)}`
|
|
5050
|
+
text: scope === "machine" ? `已把 ${id} 放到机器级默认板第 ${(args?.index ?? 0) + 1} 位 —— 只影响还没配过的工作区。${MACHINE_CAVEAT}\n${render(units, workspace, scope)}` : `已把 ${id} 放到第 ${(args?.index ?? 0) + 1} 位。\n${render(units, workspace, scope)}`
|
|
3969
5051
|
};
|
|
3970
5052
|
}
|
|
3971
5053
|
};
|
|
@@ -3973,26 +5055,44 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
3973
5055
|
}
|
|
3974
5056
|
//#endregion
|
|
3975
5057
|
//#region src/app/units/host.ts
|
|
3976
|
-
var
|
|
5058
|
+
var OWNED = [{
|
|
5059
|
+
unit: panelsUnit.id,
|
|
3977
5060
|
manifest: panelsUnit,
|
|
3978
|
-
mount({ ctx, logger, manifests, opencliCommand, probe, store, values }) {
|
|
5061
|
+
mount({ ctx, logger, manifests, ownedUnits, opencliCommand, probe, store, values }) {
|
|
3979
5062
|
const panels = createPanelsService({
|
|
3980
5063
|
manifests,
|
|
3981
5064
|
store,
|
|
5065
|
+
owned: ownedUnits,
|
|
3982
5066
|
logger
|
|
3983
5067
|
});
|
|
3984
5068
|
ctx.provide(PANELS_SERVICE, panels);
|
|
3985
5069
|
const profileDir = profileDirOf(ctx);
|
|
5070
|
+
const shell = ctx.get("shell");
|
|
5071
|
+
const ledger = createPluginLedger({
|
|
5072
|
+
store,
|
|
5073
|
+
home: isolatedHome()
|
|
5074
|
+
});
|
|
3986
5075
|
const market = createMarketService({
|
|
3987
5076
|
manifests: manifests(),
|
|
3988
5077
|
manifestsNow: manifests,
|
|
3989
5078
|
...probe === void 0 ? {} : { probe },
|
|
3990
|
-
shell
|
|
5079
|
+
shell,
|
|
3991
5080
|
...opencliCommand === void 0 ? {} : { command: opencliCommand },
|
|
3992
5081
|
bundles: readBundleManager(ctx),
|
|
3993
5082
|
installed: (pkg) => readInstalledModule(profileDir, pkg),
|
|
3994
5083
|
modules: () => profileModules(profileDir),
|
|
3995
5084
|
registries: createRegistryNotebook(profileDir),
|
|
5085
|
+
ledger,
|
|
5086
|
+
references: () => {
|
|
5087
|
+
const gaps = declarationGaps(profileDir);
|
|
5088
|
+
return gaps === void 0 ? {
|
|
5089
|
+
trustworthy: false,
|
|
5090
|
+
unreadable: []
|
|
5091
|
+
} : {
|
|
5092
|
+
trustworthy: gaps.length === 0,
|
|
5093
|
+
unreadable: gaps.map((gap) => gap.pkg)
|
|
5094
|
+
};
|
|
5095
|
+
},
|
|
3996
5096
|
onUninstalled: (unit) => {
|
|
3997
5097
|
panels.release(unit);
|
|
3998
5098
|
},
|
|
@@ -4003,7 +5103,13 @@ var HOST_UNITS = [{
|
|
|
4003
5103
|
logger
|
|
4004
5104
|
});
|
|
4005
5105
|
return {
|
|
4006
|
-
routes:
|
|
5106
|
+
routes: {
|
|
5107
|
+
...panelRoutes(panels, values),
|
|
5108
|
+
...pluginRoutes({
|
|
5109
|
+
ledger,
|
|
5110
|
+
...shell === void 0 ? {} : { shell }
|
|
5111
|
+
})
|
|
5112
|
+
},
|
|
4007
5113
|
registerTools: () => [
|
|
4008
5114
|
...registerPanelTools(ctx, panels, values),
|
|
4009
5115
|
...registerMarketTools(ctx, market),
|
|
@@ -4012,7 +5118,14 @@ var HOST_UNITS = [{
|
|
|
4012
5118
|
factsOf: (call) => panelsFactsOf(call, manifests()),
|
|
4013
5119
|
needsApproval: panelsApproval,
|
|
4014
5120
|
gate: {
|
|
4015
|
-
deniedTools: (workspace) =>
|
|
5121
|
+
deniedTools: (workspace) => {
|
|
5122
|
+
const boards = panels.boards(workspace);
|
|
5123
|
+
const owned = new Set(ownedUnits());
|
|
5124
|
+
return [...manifests().flatMap((manifest) => owned.has(manifest.id) || resolveScope({
|
|
5125
|
+
id: manifest.id,
|
|
5126
|
+
boards
|
|
5127
|
+
}) ? [] : manifest.tools.map((tool) => tool.name)), ...inWorkshopSession(ctx, workspace) ? [] : [DIY_TOOL]];
|
|
5128
|
+
},
|
|
4016
5129
|
subscribe: (listener) => panels.subscribe(listener)
|
|
4017
5130
|
},
|
|
4018
5131
|
prefs: {
|
|
@@ -4027,7 +5140,7 @@ var HOST_UNITS = [{
|
|
|
4027
5140
|
}
|
|
4028
5141
|
}];
|
|
4029
5142
|
function assembleHostUnits(registry) {
|
|
4030
|
-
for (const face of
|
|
5143
|
+
for (const face of OWNED) registry.registerOwned(face);
|
|
4031
5144
|
}
|
|
4032
5145
|
//#endregion
|
|
4033
5146
|
//#region src/shared/api/host/runtime-seed.ts
|
|
@@ -4346,6 +5459,7 @@ function bundleRegistriesHandler(ctx) {
|
|
|
4346
5459
|
};
|
|
4347
5460
|
};
|
|
4348
5461
|
}
|
|
5462
|
+
var refusalLog = /* @__PURE__ */ new Map();
|
|
4349
5463
|
function moduleVersionsHandler(ctx, shell) {
|
|
4350
5464
|
return async (search) => {
|
|
4351
5465
|
const manager = readBundleManager(ctx);
|
|
@@ -4353,7 +5467,9 @@ function moduleVersionsHandler(ctx, shell) {
|
|
|
4353
5467
|
ok: true,
|
|
4354
5468
|
modules: []
|
|
4355
5469
|
};
|
|
4356
|
-
const
|
|
5470
|
+
const dir = profileDirOf(ctx);
|
|
5471
|
+
const found = profileModules(dir);
|
|
5472
|
+
const refusals = new Map((ctx.get("workbenchUnits")?.refusals() ?? []).filter((one) => one.unit !== "").map((one) => [one.unit, one.reasons]));
|
|
4357
5473
|
const force = param(search, "force") === "1";
|
|
4358
5474
|
const fallback = (await manager.registries())?.resolved ?? void 0;
|
|
4359
5475
|
return {
|
|
@@ -4366,17 +5482,66 @@ function moduleVersionsHandler(ctx, shell) {
|
|
|
4366
5482
|
registry,
|
|
4367
5483
|
force
|
|
4368
5484
|
});
|
|
5485
|
+
const raw = moduleDeclarationOf(dir, module.pkg);
|
|
5486
|
+
const declared = raw === void 0 ? [] : declarationProblemsOf(module.pkg, raw);
|
|
5487
|
+
const refused = refusals.get(module.unit) ?? (declared.length === 0 ? void 0 : declared);
|
|
5488
|
+
if (refused === void 0) refusalLog.delete(module.pkg);
|
|
5489
|
+
else {
|
|
5490
|
+
const line = refused.join(";");
|
|
5491
|
+
if (refusalLog.get(module.pkg) !== line) {
|
|
5492
|
+
refusalLog.set(module.pkg, line);
|
|
5493
|
+
ctx.logger?.warn?.(`[dsh-workbench] ${module.pkg} 装上了但用不了:${line}`);
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
4369
5496
|
return {
|
|
4370
5497
|
unit: module.unit,
|
|
4371
5498
|
pkg: module.pkg,
|
|
4372
5499
|
installed: module.version,
|
|
4373
5500
|
...latest === void 0 ? {} : { latest },
|
|
4374
|
-
...module.local === true ? { local: true } : {}
|
|
5501
|
+
...module.local === true ? { local: true } : {},
|
|
5502
|
+
...refused === void 0 ? {} : { refused }
|
|
4375
5503
|
};
|
|
4376
5504
|
}))
|
|
4377
5505
|
};
|
|
4378
5506
|
};
|
|
4379
5507
|
}
|
|
5508
|
+
function refusedInspection(problems) {
|
|
5509
|
+
if (problems.length === 0) return void 0;
|
|
5510
|
+
return {
|
|
5511
|
+
status: "refused",
|
|
5512
|
+
problem: "not-a-workbench-module",
|
|
5513
|
+
reason: problems.join(";")
|
|
5514
|
+
};
|
|
5515
|
+
}
|
|
5516
|
+
async function declarationRefusal(spec, inspection, ctx, manager) {
|
|
5517
|
+
if (inspection.status !== "accepted" || inspection.name === void 0) return void 0;
|
|
5518
|
+
if (inspection.kind === "path") return refusedInspection(declarationRefusalOf(inspection.name, pathDeclarationOf(spec)));
|
|
5519
|
+
if (inspection.kind !== "registry") return void 0;
|
|
5520
|
+
const shell = ctx.get("shell");
|
|
5521
|
+
if (shell === void 0) return void 0;
|
|
5522
|
+
const registry = typeof inspection.registry === "string" ? inspection.registry : (await manager.registries())?.resolved ?? void 0;
|
|
5523
|
+
if (registry === void 0) return void 0;
|
|
5524
|
+
const raw = await registryDeclarationOf({
|
|
5525
|
+
shell,
|
|
5526
|
+
name: inspection.name,
|
|
5527
|
+
registry
|
|
5528
|
+
});
|
|
5529
|
+
return refusedInspection(declarationRefusalOf(inspection.name, raw));
|
|
5530
|
+
}
|
|
5531
|
+
async function packageRefusal(spec, inspection, ctx, manager) {
|
|
5532
|
+
if (inspection.status !== "accepted") return void 0;
|
|
5533
|
+
const shell = ctx.get("shell");
|
|
5534
|
+
const registry = typeof inspection.registry === "string" ? inspection.registry : (await manager.registries())?.resolved ?? void 0;
|
|
5535
|
+
return (await packageRouteOf({
|
|
5536
|
+
spec,
|
|
5537
|
+
...shell === void 0 ? {} : { shell },
|
|
5538
|
+
...registry === void 0 ? {} : { registry }
|
|
5539
|
+
}))?.kind === "neither" ? {
|
|
5540
|
+
status: "refused",
|
|
5541
|
+
problem: "not-a-module-or-plugin",
|
|
5542
|
+
reason: refusalReason()
|
|
5543
|
+
} : void 0;
|
|
5544
|
+
}
|
|
4380
5545
|
function bundleInspectHandler(ctx) {
|
|
4381
5546
|
return async (search) => {
|
|
4382
5547
|
const spec = param(search, "spec");
|
|
@@ -4405,7 +5570,7 @@ function bundleInspectHandler(ctx) {
|
|
|
4405
5570
|
};
|
|
4406
5571
|
return {
|
|
4407
5572
|
ok: true,
|
|
4408
|
-
inspection
|
|
5573
|
+
inspection: await packageRefusal(spec, inspection, ctx, manager) ?? await declarationRefusal(spec, inspection, ctx, manager) ?? inspection
|
|
4409
5574
|
};
|
|
4410
5575
|
};
|
|
4411
5576
|
}
|
|
@@ -4416,6 +5581,9 @@ var WORKSHOP_TITLE = "工作台DIY";
|
|
|
4416
5581
|
//#region src/shared/api/dsh-plugins.ts
|
|
4417
5582
|
var DSH_PLUGINS_ROUTE = "dsh-plugins";
|
|
4418
5583
|
//#endregion
|
|
5584
|
+
//#region src/shared/api/unit-manifests.ts
|
|
5585
|
+
var UNIT_MANIFESTS_ROUTE = "unit-manifests";
|
|
5586
|
+
//#endregion
|
|
4419
5587
|
//#region src/shared/api/host/context-inject.ts
|
|
4420
5588
|
var CONTEXT_SOURCE_KIND = "workbench";
|
|
4421
5589
|
function collectContextRefs(messages) {
|
|
@@ -5114,7 +6282,7 @@ function failureReasonOf(error) {
|
|
|
5114
6282
|
function fail(ledger, item, reason) {
|
|
5115
6283
|
ledger.failed.add(item);
|
|
5116
6284
|
ledger.failures.push({
|
|
5117
|
-
id: item.
|
|
6285
|
+
id: item.host.manifest.id,
|
|
5118
6286
|
reason
|
|
5119
6287
|
});
|
|
5120
6288
|
}
|
|
@@ -5124,7 +6292,7 @@ function collectRoutes(mounted, ledger) {
|
|
|
5124
6292
|
const routesOfUnit = /* @__PURE__ */ new Map();
|
|
5125
6293
|
for (const item of mounted) {
|
|
5126
6294
|
if (ledger.failed.has(item)) continue;
|
|
5127
|
-
const unit = item.
|
|
6295
|
+
const unit = item.host.manifest.id;
|
|
5128
6296
|
const accepted = [];
|
|
5129
6297
|
let problem;
|
|
5130
6298
|
for (const [key, handler] of Object.entries(item.result.routes)) {
|
|
@@ -5162,7 +6330,7 @@ function collectNamed(mounted, ledger, pickMany, label) {
|
|
|
5162
6330
|
const out = [];
|
|
5163
6331
|
for (const item of mounted) {
|
|
5164
6332
|
if (ledger.failed.has(item)) continue;
|
|
5165
|
-
const unit = item.
|
|
6333
|
+
const unit = item.host.manifest.id;
|
|
5166
6334
|
const entries = pickMany(item);
|
|
5167
6335
|
if (entries.length === 0) continue;
|
|
5168
6336
|
const clash = entries.find((entry) => owner.has(entry.resource));
|
|
@@ -5185,7 +6353,7 @@ function collectContextRenderers(mounted, ledger) {
|
|
|
5185
6353
|
const owner = /* @__PURE__ */ new Map();
|
|
5186
6354
|
for (const item of mounted) {
|
|
5187
6355
|
if (ledger.failed.has(item)) continue;
|
|
5188
|
-
const unit = item.
|
|
6356
|
+
const unit = item.host.manifest.id;
|
|
5189
6357
|
const context = item.result.context;
|
|
5190
6358
|
if (context === void 0) continue;
|
|
5191
6359
|
const clash = context.kinds.find((kind) => owner.has(kind));
|
|
@@ -5209,10 +6377,10 @@ function pickProvider(mounted, ledger, pick, label) {
|
|
|
5209
6377
|
if (value === void 0) continue;
|
|
5210
6378
|
if (chosen === void 0) {
|
|
5211
6379
|
chosen = value;
|
|
5212
|
-
chosenBy = item.
|
|
6380
|
+
chosenBy = item.host.manifest.id;
|
|
5213
6381
|
continue;
|
|
5214
6382
|
}
|
|
5215
|
-
fail(ledger, item, `${label}只能有一个提供者,现在是:${chosenBy}、${item.
|
|
6383
|
+
fail(ledger, item, `${label}只能有一个提供者,现在是:${chosenBy}、${item.host.manifest.id}`);
|
|
5216
6384
|
}
|
|
5217
6385
|
return chosen;
|
|
5218
6386
|
}
|
|
@@ -5230,8 +6398,9 @@ function collectWarmReads(mounted) {
|
|
|
5230
6398
|
return reads;
|
|
5231
6399
|
};
|
|
5232
6400
|
}
|
|
5233
|
-
function mountUnits(
|
|
6401
|
+
function mountUnits(units, deps) {
|
|
5234
6402
|
const store = deps.store ?? createWorkspaceStore({ root: null });
|
|
6403
|
+
const ownedUnits = deps.ownedUnits ?? (() => []);
|
|
5235
6404
|
const mounted = [];
|
|
5236
6405
|
const skipped = [];
|
|
5237
6406
|
const longTaskDisposers = /* @__PURE__ */ new Map();
|
|
@@ -5239,16 +6408,16 @@ function mountUnits(faces, deps) {
|
|
|
5239
6408
|
failed: /* @__PURE__ */ new Set(),
|
|
5240
6409
|
failures: []
|
|
5241
6410
|
};
|
|
5242
|
-
for (const
|
|
5243
|
-
const readiness = unitReadiness(
|
|
6411
|
+
for (const host of units) {
|
|
6412
|
+
const readiness = unitReadiness(host.manifest, (dep) => deps.ctx.get(dep));
|
|
5244
6413
|
if (!readiness.canMount) {
|
|
5245
6414
|
skipped.push({
|
|
5246
|
-
id:
|
|
6415
|
+
id: host.manifest.id,
|
|
5247
6416
|
missing: readiness.missingHard
|
|
5248
6417
|
});
|
|
5249
6418
|
continue;
|
|
5250
6419
|
}
|
|
5251
|
-
const unit =
|
|
6420
|
+
const unit = host.manifest.id;
|
|
5252
6421
|
const longTasks = deps.longTasks === void 0 ? void 0 : createLongTaskHost({
|
|
5253
6422
|
scope: deps.longTasks.scope,
|
|
5254
6423
|
unit,
|
|
@@ -5256,10 +6425,12 @@ function mountUnits(faces, deps) {
|
|
|
5256
6425
|
});
|
|
5257
6426
|
let result;
|
|
5258
6427
|
try {
|
|
5259
|
-
result = face.mount({
|
|
6428
|
+
result = host.face.mount({
|
|
6429
|
+
manifest: host.manifest,
|
|
5260
6430
|
ctx: deps.ctx,
|
|
5261
6431
|
logger: deps.logger,
|
|
5262
6432
|
manifests: deps.manifests,
|
|
6433
|
+
ownedUnits,
|
|
5263
6434
|
...deps.opencliCommand === void 0 ? {} : { opencliCommand: deps.opencliCommand },
|
|
5264
6435
|
...deps.probe === void 0 ? {} : { probe: deps.probe },
|
|
5265
6436
|
...deps.values === void 0 ? {} : { values: deps.values },
|
|
@@ -5275,7 +6446,7 @@ function mountUnits(faces, deps) {
|
|
|
5275
6446
|
continue;
|
|
5276
6447
|
}
|
|
5277
6448
|
const item = {
|
|
5278
|
-
|
|
6449
|
+
host,
|
|
5279
6450
|
readiness,
|
|
5280
6451
|
result: withRunsRoute(result, unit)
|
|
5281
6452
|
};
|
|
@@ -5291,7 +6462,7 @@ function mountUnits(faces, deps) {
|
|
|
5291
6462
|
for (const item of mounted) {
|
|
5292
6463
|
if (ledger.failed.has(item)) continue;
|
|
5293
6464
|
const declare = item.result.factsOf;
|
|
5294
|
-
if (declare !== void 0) factsOf.set(item.
|
|
6465
|
+
if (declare !== void 0) factsOf.set(item.host.manifest.id, declare);
|
|
5295
6466
|
}
|
|
5296
6467
|
const gate = pickProvider(mounted, ledger, (item) => item.result.gate, "启停闸门");
|
|
5297
6468
|
const provision = pickProvider(mounted, ledger, (item) => item.result.provision, "机器供给");
|
|
@@ -5324,30 +6495,71 @@ function mountUnits(faces, deps) {
|
|
|
5324
6495
|
}
|
|
5325
6496
|
//#endregion
|
|
5326
6497
|
//#region src/shared/api/host/unit-registry.ts
|
|
5327
|
-
function
|
|
5328
|
-
|
|
5329
|
-
|
|
6498
|
+
function sourceLabel(source) {
|
|
6499
|
+
return source.kind === "owned" ? "外壳自己" : source.pkg;
|
|
6500
|
+
}
|
|
6501
|
+
function createUnitRegistry(options) {
|
|
6502
|
+
const refusals = [];
|
|
6503
|
+
const hosts = [];
|
|
6504
|
+
const byUnit = /* @__PURE__ */ new Map();
|
|
5330
6505
|
const listeners = /* @__PURE__ */ new Set();
|
|
5331
6506
|
function notify() {
|
|
5332
6507
|
for (const listener of listeners) listener();
|
|
5333
6508
|
}
|
|
6509
|
+
function add(host) {
|
|
6510
|
+
const taken = byUnit.get(host.unit);
|
|
6511
|
+
if (taken !== void 0) throw new Error(`套件 id 重复:${host.unit}(id 会进持久化配置,不能撞)—— 已被 ${sourceLabel(taken.source)} 占着`);
|
|
6512
|
+
byUnit.set(host.unit, host);
|
|
6513
|
+
hosts.push(host);
|
|
6514
|
+
notify();
|
|
6515
|
+
return () => {
|
|
6516
|
+
if (byUnit.get(host.unit) !== host) return;
|
|
6517
|
+
byUnit.delete(host.unit);
|
|
6518
|
+
const index = hosts.indexOf(host);
|
|
6519
|
+
if (index >= 0) hosts.splice(index, 1);
|
|
6520
|
+
notify();
|
|
6521
|
+
};
|
|
6522
|
+
}
|
|
5334
6523
|
return {
|
|
5335
6524
|
register(face) {
|
|
5336
|
-
const
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
faces.splice(index, 1);
|
|
5345
|
-
ids.delete(id);
|
|
6525
|
+
const unit = typeof face?.unit === "string" ? face.unit.trim() : "";
|
|
6526
|
+
const refuse = (pkg, reasons) => {
|
|
6527
|
+
refusals.push({
|
|
6528
|
+
unit,
|
|
6529
|
+
...pkg === void 0 ? {} : { pkg },
|
|
6530
|
+
reasons
|
|
6531
|
+
});
|
|
6532
|
+
options.logger?.warn?.(`[dsh-workbench] ${pkg ?? "这个模块"}没挂上(unit ${unit === "" ? "没写" : unit}):${reasons.join(";")}`);
|
|
5346
6533
|
notify();
|
|
6534
|
+
return () => {};
|
|
5347
6535
|
};
|
|
6536
|
+
if (unit === "") return refuse(void 0, ["register({ unit, mount }) 里的 unit 是空的 —— U1b 之前的旧形状是 register({ manifest, mount });现在清单由外壳从**已装包**的 dsh.workbench 读出来,模块里的 id 从 deps.manifest.id 拿(ADR-0038 §2.2)"]);
|
|
6537
|
+
const source = options.declarationOf(unit);
|
|
6538
|
+
if (source === void 0) return refuse(void 0, [`没有哪个已装模块声明了 unit「${unit}」—— 查过这份 profile 的已装清单。要么 package.json 里 dsh.workbench.unit 写错了(改完要重启工作台),要么这个包根本没被装进这份 profile`]);
|
|
6539
|
+
const read = readDeclaration(source.pkg, source.declaration);
|
|
6540
|
+
if (read.manifest === void 0) return refuse(source.pkg, read.problems);
|
|
6541
|
+
return add({
|
|
6542
|
+
unit,
|
|
6543
|
+
manifest: read.manifest,
|
|
6544
|
+
face,
|
|
6545
|
+
source: {
|
|
6546
|
+
kind: "package",
|
|
6547
|
+
pkg: source.pkg
|
|
6548
|
+
}
|
|
6549
|
+
});
|
|
5348
6550
|
},
|
|
5349
|
-
|
|
5350
|
-
|
|
6551
|
+
registerOwned(face) {
|
|
6552
|
+
return add({
|
|
6553
|
+
unit: face.manifest.id,
|
|
6554
|
+
manifest: face.manifest,
|
|
6555
|
+
face,
|
|
6556
|
+
source: { kind: "owned" }
|
|
6557
|
+
});
|
|
6558
|
+
},
|
|
6559
|
+
all: () => hosts,
|
|
6560
|
+
owned: () => hosts.filter((host) => host.source.kind === "owned").map((host) => host.unit),
|
|
6561
|
+
refusals: () => refusals,
|
|
6562
|
+
manifests: () => hosts.map((host) => host.manifest),
|
|
5351
6563
|
subscribe(listener) {
|
|
5352
6564
|
listeners.add(listener);
|
|
5353
6565
|
return () => listeners.delete(listener);
|
|
@@ -5355,6 +6567,20 @@ function createUnitRegistry() {
|
|
|
5355
6567
|
};
|
|
5356
6568
|
}
|
|
5357
6569
|
//#endregion
|
|
6570
|
+
//#region src/shared/api/host/unit-roster-facts.ts
|
|
6571
|
+
function rosterKeyOf(units) {
|
|
6572
|
+
return units.all().map((host) => host.unit).join("\0");
|
|
6573
|
+
}
|
|
6574
|
+
function bindRosterFacts(options) {
|
|
6575
|
+
let seen = rosterKeyOf(options.units);
|
|
6576
|
+
return options.units.subscribe(() => {
|
|
6577
|
+
const now = rosterKeyOf(options.units);
|
|
6578
|
+
if (now === seen) return;
|
|
6579
|
+
seen = now;
|
|
6580
|
+
options.bump([UNIT_MANIFESTS_ROUTE]);
|
|
6581
|
+
});
|
|
6582
|
+
}
|
|
6583
|
+
//#endregion
|
|
5358
6584
|
//#region src/shared/api/host/tool-scope.ts
|
|
5359
6585
|
function agentOf(value) {
|
|
5360
6586
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
@@ -5482,8 +6708,24 @@ function apply(ctx) {
|
|
|
5482
6708
|
};
|
|
5483
6709
|
const renderers = /* @__PURE__ */ new Map();
|
|
5484
6710
|
const shell = ctx.get("shell");
|
|
5485
|
-
|
|
6711
|
+
let roster;
|
|
6712
|
+
const profileDir = profileDirOf(ctx);
|
|
6713
|
+
const units = createUnitRegistry({
|
|
6714
|
+
declarationOf: (unit) => {
|
|
6715
|
+
if (roster === void 0) roster = new Map(profileModules(profileDir).map((row) => [row.unit, row.pkg]));
|
|
6716
|
+
const pkg = roster.get(unit);
|
|
6717
|
+
return pkg === void 0 ? void 0 : {
|
|
6718
|
+
pkg,
|
|
6719
|
+
declaration: moduleDeclarationOf(profileDir, pkg)
|
|
6720
|
+
};
|
|
6721
|
+
},
|
|
6722
|
+
logger: ctx.logger
|
|
6723
|
+
});
|
|
5486
6724
|
ctx.provide(UNITS_SERVICE, units);
|
|
6725
|
+
routes[UNIT_MANIFESTS_ROUTE] = () => ({
|
|
6726
|
+
ok: true,
|
|
6727
|
+
manifests: units.manifests()
|
|
6728
|
+
});
|
|
5487
6729
|
assembleHostUnits(units);
|
|
5488
6730
|
ctx.inject(["workspaceRegistry"], (scoped) => {
|
|
5489
6731
|
const registry = scoped.get("workspaceRegistry");
|
|
@@ -5548,14 +6790,15 @@ function apply(ctx) {
|
|
|
5548
6790
|
for (const unit of item.plan.routesOfUnit.keys()) routesOfUnitMap.delete(unit);
|
|
5549
6791
|
for (const resource of item.plan.owners.keys()) ownersMap.delete(resource);
|
|
5550
6792
|
}
|
|
5551
|
-
function mountUnit(
|
|
5552
|
-
const id =
|
|
6793
|
+
function mountUnit(host) {
|
|
6794
|
+
const id = host.unit;
|
|
5553
6795
|
let one;
|
|
5554
6796
|
try {
|
|
5555
|
-
one = mountUnits([
|
|
6797
|
+
one = mountUnits([host], {
|
|
5556
6798
|
ctx,
|
|
5557
6799
|
logger: ctx.logger,
|
|
5558
6800
|
manifests: liveManifests,
|
|
6801
|
+
ownedUnits: () => units.owned(),
|
|
5559
6802
|
store,
|
|
5560
6803
|
opencliCommand: opencli.command,
|
|
5561
6804
|
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
@@ -5568,7 +6811,7 @@ function apply(ctx) {
|
|
|
5568
6811
|
} catch (error) {
|
|
5569
6812
|
mountFailures.set(id, {
|
|
5570
6813
|
reason: failureReasonOf(error),
|
|
5571
|
-
|
|
6814
|
+
host
|
|
5572
6815
|
});
|
|
5573
6816
|
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(装配期):${failureReasonOf(error)}`);
|
|
5574
6817
|
return;
|
|
@@ -5577,7 +6820,7 @@ function apply(ctx) {
|
|
|
5577
6820
|
for (const failure of one.failed) {
|
|
5578
6821
|
mountFailures.set(failure.id, {
|
|
5579
6822
|
reason: failure.reason,
|
|
5580
|
-
|
|
6823
|
+
host
|
|
5581
6824
|
});
|
|
5582
6825
|
ctx.logger?.warn?.(`[dsh-workbench] ${failure.id} 套件起不来,已摘掉它这一份:${failure.reason}`);
|
|
5583
6826
|
}
|
|
@@ -5586,7 +6829,7 @@ function apply(ctx) {
|
|
|
5586
6829
|
one.disposeLongTasks();
|
|
5587
6830
|
return;
|
|
5588
6831
|
}
|
|
5589
|
-
if (slot.readiness.missingSoft.length > 0) ctx.logger?.warn?.(`[dsh-workbench] ${
|
|
6832
|
+
if (slot.readiness.missingSoft.length > 0) ctx.logger?.warn?.(`[dsh-workbench] ${host.unit} 套件降级:缺 ${slot.readiness.missingSoft.join(", ")}`);
|
|
5590
6833
|
const added = [];
|
|
5591
6834
|
try {
|
|
5592
6835
|
Object.assign(routes, one.routes);
|
|
@@ -5595,7 +6838,7 @@ function apply(ctx) {
|
|
|
5595
6838
|
for (const [unit, fn] of one.factsOf) factsOfMap.set(unit, fn);
|
|
5596
6839
|
for (const [unit, list] of one.routesOfUnit) routesOfUnitMap.set(unit, list);
|
|
5597
6840
|
for (const [resource, unit] of one.owners) ownersMap.set(resource, unit);
|
|
5598
|
-
mounted.set(
|
|
6841
|
+
mounted.set(host.unit, {
|
|
5599
6842
|
result: slot.result,
|
|
5600
6843
|
plan: one,
|
|
5601
6844
|
toolDisposers: slot.result.registerTools(),
|
|
@@ -5611,7 +6854,7 @@ function apply(ctx) {
|
|
|
5611
6854
|
one.disposeLongTasks();
|
|
5612
6855
|
mountFailures.set(id, {
|
|
5613
6856
|
reason: failureReasonOf(error),
|
|
5614
|
-
|
|
6857
|
+
host
|
|
5615
6858
|
});
|
|
5616
6859
|
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(注册期):${failureReasonOf(error)}`);
|
|
5617
6860
|
}
|
|
@@ -5635,14 +6878,14 @@ function apply(ctx) {
|
|
|
5635
6878
|
if (provision !== void 0) undoPost.push(registerProvision(ctx, provision, facts));
|
|
5636
6879
|
}
|
|
5637
6880
|
function sync() {
|
|
5638
|
-
const present = new Set(units.all().map((
|
|
6881
|
+
const present = new Set(units.all().map((one) => one.unit));
|
|
5639
6882
|
for (const id of [...mounted.keys()]) if (!present.has(id)) unmountUnit(id);
|
|
5640
6883
|
for (const id of [...mountFailures.keys()]) if (!present.has(id)) mountFailures.delete(id);
|
|
5641
|
-
for (const
|
|
5642
|
-
if (mounted.has(
|
|
5643
|
-
const already = mountFailures.get(
|
|
5644
|
-
if (already !== void 0 && already.
|
|
5645
|
-
mountUnit(
|
|
6884
|
+
for (const host of units.all()) {
|
|
6885
|
+
if (mounted.has(host.unit)) continue;
|
|
6886
|
+
const already = mountFailures.get(host.unit);
|
|
6887
|
+
if (already !== void 0 && already.host === host) continue;
|
|
6888
|
+
mountUnit(host);
|
|
5646
6889
|
}
|
|
5647
6890
|
rebindPost();
|
|
5648
6891
|
}
|
|
@@ -5682,6 +6925,10 @@ function apply(ctx) {
|
|
|
5682
6925
|
for (const dispose of undoPost) dispose?.();
|
|
5683
6926
|
}, `${PLUGIN_ID}: 卸载时把单元摘干净`);
|
|
5684
6927
|
ctx.effect(() => units.subscribe(sync), `${PLUGIN_ID}: 注册表变了就增删(装/卸一个模块 = 挂/卸它的面)`);
|
|
6928
|
+
ctx.effect(() => bindRosterFacts({
|
|
6929
|
+
units,
|
|
6930
|
+
bump: (names) => facts.bump(names)
|
|
6931
|
+
}), `${PLUGIN_ID}: 花名册变了就把清单那条事实记一笔(浏览器据此重读,不用刷新页面)`);
|
|
5685
6932
|
ctx.effect(() => {
|
|
5686
6933
|
const off = ctx.on?.("tools/post-execute", (exec, result, next) => {
|
|
5687
6934
|
const name = exec?.name === void 0 ? void 0 : String(exec.name);
|