@dsh-workbench/core 0.1.18 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +1003 -537
- package/dist/client/client.body.js +1003 -537
- package/dist/client/index.js +1004 -538
- package/dist/host/index.js +1162 -145
- package/dist/types/app/client.d.ts +99 -39
- package/dist/types/app/layouts/market/__tests__/add-module-stages.test.d.ts +1 -0
- package/dist/types/app/layouts/market/add-module-text.d.ts +16 -1
- package/dist/types/app/layouts/market/use-add-module.d.ts +2 -8
- package/dist/types/app/layouts/market/use-module-versions.d.ts +2 -1
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +5 -1
- package/dist/types/app/layouts/rightbar/ModulesPage.d.ts +4 -2
- package/dist/types/app/layouts/rightbar/PanelModules.d.ts +2 -2
- package/dist/types/app/layouts/rightbar/PanelPluginRows.d.ts +9 -0
- package/dist/types/app/layouts/rightbar/PanelRowControls.d.ts +14 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-row-icon.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-section-icon.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-workbench.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/panel-plugin-rows.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/panel-scope-ui.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/board-selection.d.ts +11 -0
- package/dist/types/app/layouts/rightbar/market-act.d.ts +21 -0
- package/dist/types/app/layouts/rightbar/market-mock.d.ts +6 -1
- package/dist/types/app/layouts/rightbar/market-plugin-rows.d.ts +3 -1
- package/dist/types/app/layouts/rightbar/market-row-icon.d.ts +7 -0
- package/dist/types/app/layouts/rightbar/market-row-types.d.ts +35 -0
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +8 -35
- package/dist/types/app/layouts/rightbar/market-section-icon.d.ts +3 -0
- package/dist/types/app/layouts/rightbar/market-section.d.ts +18 -0
- package/dist/types/app/layouts/rightbar/market-view-text.d.ts +7 -1
- package/dist/types/app/layouts/rightbar/market-workbench-row.d.ts +5 -0
- package/dist/types/app/layouts/rightbar/panel-mock.d.ts +2 -1
- package/dist/types/app/layouts/rightbar/use-panels.d.ts +15 -1
- package/dist/types/app/units/registry.d.ts +1 -1
- package/dist/types/modules/panels/__tests__/plugin-panel-market-parity.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/plugin-panel.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/plugin-tool-units.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/scope-machine.test.d.ts +1 -0
- package/dist/types/modules/panels/__tests__/scope-support.d.ts +51 -0
- package/dist/types/modules/panels/model/config.d.ts +19 -0
- package/dist/types/modules/panels/model/market-dispatch.d.ts +4 -0
- package/dist/types/modules/panels/model/market-registry.d.ts +12 -0
- package/dist/types/modules/panels/model/market-registry.test.d.ts +1 -0
- package/dist/types/modules/panels/model/market-types.d.ts +1 -0
- package/dist/types/modules/panels/model/market.d.ts +3 -2
- package/dist/types/modules/panels/model/plugin-panel.d.ts +3 -0
- package/dist/types/modules/panels/model/prefs.d.ts +3 -1
- package/dist/types/modules/panels/model/service.d.ts +5 -1
- package/dist/types/shared/api/bundle-install.d.ts +15 -1
- package/dist/types/shared/api/host/__tests__/plugin-catalog.test.d.ts +1 -0
- package/dist/types/shared/api/host/__tests__/plugin-login-hint.test.d.ts +1 -0
- package/dist/types/shared/api/host/__tests__/plugin-tools.test.d.ts +1 -0
- package/dist/types/shared/api/host/bundle-inspect-kind.test.d.ts +1 -0
- package/dist/types/shared/api/host/bundle-routes-self.test.d.ts +1 -0
- package/dist/types/shared/api/host/installed-module.d.ts +10 -0
- package/dist/types/shared/api/host/plugin-catalog.d.ts +60 -0
- package/dist/types/shared/api/host/plugin-login-hint.d.ts +2 -0
- package/dist/types/shared/api/host/plugin-tools.d.ts +10 -0
- package/dist/types/shared/api/host/plugin-units.d.ts +47 -0
- package/dist/types/shared/api/host/unit-registry.d.ts +3 -0
- package/package.json +2 -2
package/dist/host/index.js
CHANGED
|
@@ -213,6 +213,71 @@ function parseJsonLoose(text) {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
//#endregion
|
|
216
|
+
//#region ../shared/dist/host/cli-error.js
|
|
217
|
+
function unquote(raw) {
|
|
218
|
+
const value = raw.trim();
|
|
219
|
+
if (value.length >= 2 && (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'"))) return value.slice(1, -1);
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
function joinBlock(previous, line, fold) {
|
|
223
|
+
if (previous === "") return line;
|
|
224
|
+
return fold ? `${previous} ${line}` : `${previous}\n${line}`;
|
|
225
|
+
}
|
|
226
|
+
function parseCliErrorEnvelope(text) {
|
|
227
|
+
if (text === void 0 || text.trim() === "") return void 0;
|
|
228
|
+
const lines = text.split("\n");
|
|
229
|
+
const start = lines.findIndex((line) => /^error:\s*$/.test(line.trim()) || /^error:\s*\S/.test(line.trim()));
|
|
230
|
+
if (start === -1) return void 0;
|
|
231
|
+
const scalars = {};
|
|
232
|
+
let current;
|
|
233
|
+
let block;
|
|
234
|
+
const errorIndent = lines[start]?.search(/\S/) ?? 0;
|
|
235
|
+
for (const line of lines.slice(start + 1)) {
|
|
236
|
+
if (line.trim() === "") continue;
|
|
237
|
+
const indent = line.search(/\S/);
|
|
238
|
+
if (block !== void 0 && indent > block.indent) {
|
|
239
|
+
scalars[block.key] = joinBlock(scalars[block.key] ?? "", line.trim(), block.fold);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
block = void 0;
|
|
243
|
+
if (indent <= errorIndent) break;
|
|
244
|
+
const match = /^\s*(code|message|help|exitCode):\s*(.*)$/.exec(line);
|
|
245
|
+
if (match === null) {
|
|
246
|
+
if (current !== void 0) scalars[current] = `${scalars[current] ?? ""} ${line.trim()}`.trim();
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const [, key, value] = match;
|
|
250
|
+
if (key === void 0) continue;
|
|
251
|
+
const raw = (value ?? "").trim();
|
|
252
|
+
const marker = /^([|>])[0-9]*([+-]?)$/.exec(raw);
|
|
253
|
+
if (marker !== null) {
|
|
254
|
+
scalars[key] = "";
|
|
255
|
+
block = {
|
|
256
|
+
key,
|
|
257
|
+
fold: marker[1] === ">",
|
|
258
|
+
indent
|
|
259
|
+
};
|
|
260
|
+
current = key;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
scalars[key] = unquote(raw);
|
|
264
|
+
current = key;
|
|
265
|
+
}
|
|
266
|
+
const code = scalars["code"];
|
|
267
|
+
const message = scalars["message"];
|
|
268
|
+
if (code === void 0 || code === "") return void 0;
|
|
269
|
+
const exitCode = scalars["exitCode"];
|
|
270
|
+
return {
|
|
271
|
+
code,
|
|
272
|
+
message: message ?? code,
|
|
273
|
+
...scalars["help"] === void 0 || scalars["help"] === "" ? {} : { help: scalars["help"] },
|
|
274
|
+
...exitCode === void 0 || Number.isNaN(Number(exitCode)) ? {} : { exitCode: Number(exitCode) }
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function helpUrlOf(envelope) {
|
|
278
|
+
return /https?:\/\/[^\s"'`]+/.exec(envelope.help ?? "")?.[0];
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
216
281
|
//#region ../shared/dist/host/data-root.js
|
|
217
282
|
function dshHome() {
|
|
218
283
|
const configured = process.env.DSH_HOME?.trim();
|
|
@@ -282,20 +347,20 @@ var PERMISSION_CODES = /* @__PURE__ */ new Set([
|
|
|
282
347
|
function asRecord$9(value) {
|
|
283
348
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
284
349
|
}
|
|
285
|
-
function textOf$
|
|
350
|
+
function textOf$5(value) {
|
|
286
351
|
return typeof value === "string" ? value.trim() : "";
|
|
287
352
|
}
|
|
288
353
|
function codeOf(page) {
|
|
289
354
|
const record = asRecord$9(page);
|
|
290
|
-
return record === void 0 ? "" : textOf$
|
|
355
|
+
return record === void 0 ? "" : textOf$5(record["code"]);
|
|
291
356
|
}
|
|
292
|
-
function messageOf$
|
|
357
|
+
function messageOf$2(page) {
|
|
293
358
|
const record = asRecord$9(page);
|
|
294
|
-
return record === void 0 ? "" : textOf$
|
|
359
|
+
return record === void 0 ? "" : textOf$5(record["message"]);
|
|
295
360
|
}
|
|
296
361
|
function hintOf$1(page) {
|
|
297
362
|
const record = asRecord$9(page);
|
|
298
|
-
const hint = record === void 0 ? "" : textOf$
|
|
363
|
+
const hint = record === void 0 ? "" : textOf$5(record["hint"]);
|
|
299
364
|
return hint === "" ? void 0 : hint;
|
|
300
365
|
}
|
|
301
366
|
function pageFailed(page) {
|
|
@@ -484,7 +549,7 @@ async function browserRead(options) {
|
|
|
484
549
|
message: "读到了"
|
|
485
550
|
};
|
|
486
551
|
const pageCode = codeOf(page) || "read-failed";
|
|
487
|
-
const message = messageOf$
|
|
552
|
+
const message = messageOf$2(page) || `读不到(${pageCode})`;
|
|
488
553
|
const blocker = blockerFor(pageCode);
|
|
489
554
|
if (blocker === "auth-required") return {
|
|
490
555
|
ok: false,
|
|
@@ -526,7 +591,7 @@ var DEFAULT_TEXT_MAX = 200;
|
|
|
526
591
|
function asRecord$8(value) {
|
|
527
592
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
528
593
|
}
|
|
529
|
-
function textOf$
|
|
594
|
+
function textOf$4(value) {
|
|
530
595
|
return typeof value === "string" ? value.trim() : "";
|
|
531
596
|
}
|
|
532
597
|
function entriesOf(value) {
|
|
@@ -539,8 +604,8 @@ function entriesOf(value) {
|
|
|
539
604
|
if (item === void 0) continue;
|
|
540
605
|
const nth = typeof item["nth"] === "number" ? item["nth"] : entries.length;
|
|
541
606
|
entries.push({
|
|
542
|
-
text: textOf$
|
|
543
|
-
tag: textOf$
|
|
607
|
+
text: textOf$4(item["text"]),
|
|
608
|
+
tag: textOf$4(item["tag"]),
|
|
544
609
|
nth
|
|
545
610
|
});
|
|
546
611
|
}
|
|
@@ -549,7 +614,7 @@ function entriesOf(value) {
|
|
|
549
614
|
function reasonOf(stdout, stderr) {
|
|
550
615
|
const parsed = asRecord$8(parseJsonLoose(stdout));
|
|
551
616
|
const error = parsed === void 0 ? void 0 : asRecord$8(parsed["error"]);
|
|
552
|
-
const message = error === void 0 ? "" : textOf$
|
|
617
|
+
const message = error === void 0 ? "" : textOf$4(error["message"]);
|
|
553
618
|
if (message !== "") return message;
|
|
554
619
|
const lines = (stderr.trim() === "" ? stdout : stderr).split("\n").map((one) => one.trim()).filter((one) => one !== "");
|
|
555
620
|
return (lines[lines.length - 1] ?? "").slice(-200);
|
|
@@ -557,7 +622,7 @@ function reasonOf(stdout, stderr) {
|
|
|
557
622
|
function hintOf(stdout) {
|
|
558
623
|
const parsed = asRecord$8(parseJsonLoose(stdout));
|
|
559
624
|
const error = parsed === void 0 ? void 0 : asRecord$8(parsed["error"]);
|
|
560
|
-
const hint = error === void 0 ? "" : textOf$
|
|
625
|
+
const hint = error === void 0 ? "" : textOf$4(error["hint"]);
|
|
561
626
|
return hint === "" ? void 0 : hint;
|
|
562
627
|
}
|
|
563
628
|
function sameOrigin(left, right) {
|
|
@@ -571,7 +636,7 @@ function urlOf(stdout) {
|
|
|
571
636
|
const bare = (stdout.split("\n").map((one) => one.trim()).filter((one) => one !== "")[0] ?? "").replace(/^url:\s*/i, "").trim().replace(/^["']+|["']+$/g, "");
|
|
572
637
|
if (bare.startsWith("{")) {
|
|
573
638
|
const parsed = asRecord$8(parseJsonLoose(bare));
|
|
574
|
-
const url = parsed === void 0 ? "" : textOf$
|
|
639
|
+
const url = parsed === void 0 ? "" : textOf$4(parsed["url"]);
|
|
575
640
|
if (url !== "") return url;
|
|
576
641
|
}
|
|
577
642
|
return bare;
|
|
@@ -796,7 +861,8 @@ var PROVISION_ACTIONS = [
|
|
|
796
861
|
"cancel-install",
|
|
797
862
|
"update-dependency",
|
|
798
863
|
"update-plugin",
|
|
799
|
-
"uninstall-plugin"
|
|
864
|
+
"uninstall-plugin",
|
|
865
|
+
"update-workbench"
|
|
800
866
|
];
|
|
801
867
|
var DEPENDENCY_ACTIONS = ["update-dependency"];
|
|
802
868
|
function isDependencyAction(action) {
|
|
@@ -807,6 +873,7 @@ function provisionTargetOf(action) {
|
|
|
807
873
|
if (action === "install-bundle") return "spec";
|
|
808
874
|
if (action === "remove-bundle") return "name";
|
|
809
875
|
if (action === "update-plugin" || action === "uninstall-plugin") return "name";
|
|
876
|
+
if (action === "update-workbench") return "name";
|
|
810
877
|
if (action === "cancel-install") return "requestId";
|
|
811
878
|
return "unit";
|
|
812
879
|
}
|
|
@@ -820,7 +887,7 @@ var TARGET_CODE = {
|
|
|
820
887
|
function asRecord$7(value) {
|
|
821
888
|
return typeof value === "object" && value !== null ? value : {};
|
|
822
889
|
}
|
|
823
|
-
function stringsOf$
|
|
890
|
+
function stringsOf$3(value) {
|
|
824
891
|
if (!Array.isArray(value)) return void 0;
|
|
825
892
|
const items = value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
826
893
|
return items.length === 0 ? void 0 : items;
|
|
@@ -842,6 +909,10 @@ function requestOf$1(action, value) {
|
|
|
842
909
|
action,
|
|
843
910
|
name: value
|
|
844
911
|
};
|
|
912
|
+
if (action === "update-workbench") return {
|
|
913
|
+
action,
|
|
914
|
+
name: value
|
|
915
|
+
};
|
|
845
916
|
if (action === "cancel-install") return {
|
|
846
917
|
action,
|
|
847
918
|
requestId: value
|
|
@@ -872,7 +943,7 @@ function parseProvisionBody(body) {
|
|
|
872
943
|
request: requestOf$1(wanted, value.trim())
|
|
873
944
|
};
|
|
874
945
|
const registry = typeof raw.registry === "string" && raw.registry.trim() !== "" ? raw.registry.trim() : void 0;
|
|
875
|
-
const approvedBuilds = stringsOf$
|
|
946
|
+
const approvedBuilds = stringsOf$3(raw.approvedBuilds);
|
|
876
947
|
const requestId = typeof raw.requestId === "string" && raw.requestId.trim() !== "" ? raw.requestId.trim() : void 0;
|
|
877
948
|
return {
|
|
878
949
|
ok: true,
|
|
@@ -886,6 +957,7 @@ function parseProvisionBody(body) {
|
|
|
886
957
|
}
|
|
887
958
|
//#endregion
|
|
888
959
|
//#region ../shared/dist/host/opencli.js
|
|
960
|
+
var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["FORBIDDEN", "PERMISSION_DENIED"]);
|
|
889
961
|
var SITE_SESSION_ARGS = ["--site-session", "persistent"];
|
|
890
962
|
var WorkbenchDataError = class extends Error {
|
|
891
963
|
code;
|
|
@@ -913,6 +985,102 @@ function opencliCommandLine(command, argv, session = "persistent") {
|
|
|
913
985
|
"json"
|
|
914
986
|
].join(" ");
|
|
915
987
|
}
|
|
988
|
+
function createOpencli(options) {
|
|
989
|
+
const { shell, plugin, logger, command = "opencli", timeoutMs = OPENCLI_TIMEOUT_MS, maxConcurrent = 4 } = options;
|
|
990
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
991
|
+
const MAX_CONCURRENT = maxConcurrent;
|
|
992
|
+
let running = 0;
|
|
993
|
+
const waiting = [];
|
|
994
|
+
async function acquire() {
|
|
995
|
+
if (running < MAX_CONCURRENT) {
|
|
996
|
+
running += 1;
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
1000
|
+
}
|
|
1001
|
+
function release() {
|
|
1002
|
+
const next = waiting.shift();
|
|
1003
|
+
if (next === void 0) running -= 1;
|
|
1004
|
+
else next();
|
|
1005
|
+
}
|
|
1006
|
+
async function runLimited(task) {
|
|
1007
|
+
await acquire();
|
|
1008
|
+
try {
|
|
1009
|
+
return await task();
|
|
1010
|
+
} finally {
|
|
1011
|
+
release();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
async function call(commandLine, workdir) {
|
|
1015
|
+
let result;
|
|
1016
|
+
try {
|
|
1017
|
+
result = await runShell(shell, commandLine, {
|
|
1018
|
+
...workdir === void 0 ? {} : { workdir },
|
|
1019
|
+
timeoutMs
|
|
1020
|
+
});
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
throw new WorkbenchDataError("cli-exec-failed", `执行 opencli 失败:${String(error)}`, { hint: "确认 opencli 已安装且在 PATH 里(which opencli)。" });
|
|
1023
|
+
}
|
|
1024
|
+
if (result.timedOut) throw new WorkbenchDataError("cli-timeout", `opencli 超过 ${Math.round(timeoutMs / 1e3)} 秒没有返回`, { hint: "无头浏览器可能没起来,重试一次通常就好。" });
|
|
1025
|
+
if (result.exitCode !== 0) {
|
|
1026
|
+
const detail = tailLines(result.stderr || result.stdout, 6);
|
|
1027
|
+
const envelope = parseCliErrorEnvelope(result.stderr) ?? parseCliErrorEnvelope(result.stdout);
|
|
1028
|
+
const url = envelope === void 0 ? void 0 : helpUrlOf(envelope);
|
|
1029
|
+
if (envelope?.code === "AUTH_REQUIRED") throw new WorkbenchDataError("auth-required", envelope.message, {
|
|
1030
|
+
detail,
|
|
1031
|
+
...url === void 0 ? {} : { helpUrl: url }
|
|
1032
|
+
});
|
|
1033
|
+
if (envelope !== void 0 && FORBIDDEN_CODES.has(envelope.code)) throw new WorkbenchDataError("permission-denied", envelope.message, {
|
|
1034
|
+
detail,
|
|
1035
|
+
...url === void 0 ? {} : { helpUrl: url }
|
|
1036
|
+
});
|
|
1037
|
+
if (envelope !== void 0) throw new WorkbenchDataError("cli-command-failed", `${envelope.code}:${envelope.message}`, {
|
|
1038
|
+
detail,
|
|
1039
|
+
...envelope.help === void 0 ? {} : { hint: envelope.help }
|
|
1040
|
+
});
|
|
1041
|
+
throw new WorkbenchDataError("cli-unclassified", `opencli 这条命令没跑通(退出码 ${result.exitCode}),而且没有结构化错误码`, {
|
|
1042
|
+
detail,
|
|
1043
|
+
hint: "多半是插件里没有这条命令(版本旧就会缺),也可能是命令自己失败了:先确认命令存在,再更新插件重试。"
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
const parsed = parseJsonLoose(result.stdout);
|
|
1047
|
+
if (parsed === void 0) throw new WorkbenchDataError("cli-bad-output", "opencli 输出不是 JSON", { detail: tailLines(result.stdout, 4) });
|
|
1048
|
+
return parsed;
|
|
1049
|
+
}
|
|
1050
|
+
async function json(args, callOptions = {}) {
|
|
1051
|
+
const { key, workdir } = callOptions;
|
|
1052
|
+
const session = callOptions.session ?? "persistent";
|
|
1053
|
+
const callKey = key ?? args.map((arg) => String(arg)).join(" ");
|
|
1054
|
+
const pending = inflight.get(callKey);
|
|
1055
|
+
if (pending !== void 0) return pending;
|
|
1056
|
+
const commandLine = opencliCommandLine(command, [plugin, ...args], session);
|
|
1057
|
+
const once = async () => {
|
|
1058
|
+
return {
|
|
1059
|
+
value: await call(commandLine, workdir),
|
|
1060
|
+
at: Date.now()
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
const task = runLimited(async () => {
|
|
1064
|
+
try {
|
|
1065
|
+
return await once();
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
if (!(error instanceof WorkbenchDataError) || error.code !== "auth-required") throw error;
|
|
1068
|
+
return await once();
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
inflight.set(callKey, task);
|
|
1072
|
+
try {
|
|
1073
|
+
return await task;
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1076
|
+
logger?.warn?.(`[dsh-workbench] opencli 调用失败:${message}`);
|
|
1077
|
+
throw error;
|
|
1078
|
+
} finally {
|
|
1079
|
+
inflight.delete(callKey);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
return { json };
|
|
1083
|
+
}
|
|
916
1084
|
//#endregion
|
|
917
1085
|
//#region ../shared/dist/host/write-result.js
|
|
918
1086
|
function writeResultForClient(value, code) {
|
|
@@ -1447,7 +1615,7 @@ var PLUGIN_LEDGER_FILE = "plugins.json";
|
|
|
1447
1615
|
function ledgerKeyOf(entry) {
|
|
1448
1616
|
return `${entry.opencliHome}\u0000${entry.package}`;
|
|
1449
1617
|
}
|
|
1450
|
-
function textOf$
|
|
1618
|
+
function textOf$3(value) {
|
|
1451
1619
|
if (typeof value !== "string") return void 0;
|
|
1452
1620
|
const trimmed = value.trim();
|
|
1453
1621
|
return trimmed === "" ? void 0 : trimmed;
|
|
@@ -1455,13 +1623,13 @@ function textOf$2(value) {
|
|
|
1455
1623
|
function entryOf(value) {
|
|
1456
1624
|
if (typeof value !== "object" || value === null) return void 0;
|
|
1457
1625
|
const box = value;
|
|
1458
|
-
const opencliHome = textOf$
|
|
1459
|
-
const pkg = textOf$
|
|
1460
|
-
const installedAt = textOf$
|
|
1626
|
+
const opencliHome = textOf$3(box["opencliHome"]);
|
|
1627
|
+
const pkg = textOf$3(box["package"]);
|
|
1628
|
+
const installedAt = textOf$3(box["installedAt"]);
|
|
1461
1629
|
if (opencliHome === void 0 || pkg === void 0 || installedAt === void 0) return void 0;
|
|
1462
|
-
const name = textOf$
|
|
1463
|
-
const registry = textOf$
|
|
1464
|
-
const releasedAt = textOf$
|
|
1630
|
+
const name = textOf$3(box["name"]);
|
|
1631
|
+
const registry = textOf$3(box["registry"]);
|
|
1632
|
+
const releasedAt = textOf$3(box["releasedAt"]);
|
|
1465
1633
|
return {
|
|
1466
1634
|
opencliHome,
|
|
1467
1635
|
package: pkg,
|
|
@@ -1628,7 +1796,7 @@ function resolvePluginSource(source, env, options = {}) {
|
|
|
1628
1796
|
}
|
|
1629
1797
|
//#endregion
|
|
1630
1798
|
//#region ../shared/dist/host/tool-approval.js
|
|
1631
|
-
function messageOf(error) {
|
|
1799
|
+
function messageOf$1(error) {
|
|
1632
1800
|
return error instanceof Error ? error.message : String(error);
|
|
1633
1801
|
}
|
|
1634
1802
|
function compose(detail, verdict) {
|
|
@@ -1646,7 +1814,7 @@ async function approvalFor(call, context = {}) {
|
|
|
1646
1814
|
} catch (error) {
|
|
1647
1815
|
return {
|
|
1648
1816
|
kind: "deny",
|
|
1649
|
-
reason: `问"要不要审批"的时候套件的策略抛了错,已经停下:${messageOf(error)}`
|
|
1817
|
+
reason: `问"要不要审批"的时候套件的策略抛了错,已经停下:${messageOf$1(error)}`
|
|
1650
1818
|
};
|
|
1651
1819
|
}
|
|
1652
1820
|
if (verdict?.kind === "read") return void 0;
|
|
@@ -1660,7 +1828,7 @@ async function approvalFor(call, context = {}) {
|
|
|
1660
1828
|
} catch (error) {
|
|
1661
1829
|
return {
|
|
1662
1830
|
kind: "deny",
|
|
1663
|
-
reason: `拿不到"这次会发生什么",已经停下:${messageOf(error)}`
|
|
1831
|
+
reason: `拿不到"这次会发生什么",已经停下:${messageOf$1(error)}`
|
|
1664
1832
|
};
|
|
1665
1833
|
}
|
|
1666
1834
|
if (facts !== void 0 && "blocked" in facts) return {
|
|
@@ -1679,6 +1847,15 @@ function sessionWorkspace(exec) {
|
|
|
1679
1847
|
const cwd = exec?.agent?.session?.header?.cwd;
|
|
1680
1848
|
return typeof cwd === "string" && cwd.trim() !== "" ? cwd.trim() : null;
|
|
1681
1849
|
}
|
|
1850
|
+
function textOutput(text) {
|
|
1851
|
+
return [{
|
|
1852
|
+
type: "text",
|
|
1853
|
+
text
|
|
1854
|
+
}];
|
|
1855
|
+
}
|
|
1856
|
+
function jsonText(value) {
|
|
1857
|
+
return JSON.stringify(value);
|
|
1858
|
+
}
|
|
1682
1859
|
//#endregion
|
|
1683
1860
|
//#region ../shared/dist/host/value-handoff.js
|
|
1684
1861
|
function handoffId(entry) {
|
|
@@ -2000,7 +2177,7 @@ function profileDirOf(ctx) {
|
|
|
2000
2177
|
const dir = typeof profile?.dir === "string" ? profile.dir.trim() : "";
|
|
2001
2178
|
return dir === "" ? null : dir;
|
|
2002
2179
|
}
|
|
2003
|
-
function stringsOf$
|
|
2180
|
+
function stringsOf$2(value) {
|
|
2004
2181
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
2005
2182
|
}
|
|
2006
2183
|
function profileManifestOf(profileDir) {
|
|
@@ -2014,7 +2191,7 @@ function profileManifestOf(profileDir) {
|
|
|
2014
2191
|
function profileModules(profileDir) {
|
|
2015
2192
|
const record = profileManifestOf(profileDir);
|
|
2016
2193
|
if (record === void 0) return [];
|
|
2017
|
-
const bundles = stringsOf$
|
|
2194
|
+
const bundles = stringsOf$2(asRecord$5(asRecord$5(record.dsh)?.profile)?.bundles);
|
|
2018
2195
|
const dependencies = asRecord$5(record.dependencies) ?? {};
|
|
2019
2196
|
const names = bundles.length > 0 ? bundles : Object.keys(dependencies);
|
|
2020
2197
|
const found = [];
|
|
@@ -2025,7 +2202,7 @@ function profileModules(profileDir) {
|
|
|
2025
2202
|
unit: read.unit,
|
|
2026
2203
|
version: read.version,
|
|
2027
2204
|
...read.registry === void 0 ? {} : { registry: read.registry },
|
|
2028
|
-
local: asText$3(dependencies[read.pkg])
|
|
2205
|
+
local: isLocalDependencySpec(asText$3(dependencies[read.pkg]))
|
|
2029
2206
|
});
|
|
2030
2207
|
}
|
|
2031
2208
|
return found;
|
|
@@ -2070,6 +2247,20 @@ function declarationGaps(profileDir) {
|
|
|
2070
2247
|
}
|
|
2071
2248
|
return gaps;
|
|
2072
2249
|
}
|
|
2250
|
+
function declaredPluginCommands(profileDir) {
|
|
2251
|
+
const declared = /* @__PURE__ */ new Map();
|
|
2252
|
+
for (const module of profileModules(profileDir)) {
|
|
2253
|
+
const read = readDeclaration(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
2254
|
+
for (const need of read.manifest?.env?.plugins ?? []) {
|
|
2255
|
+
const name = need.name.trim();
|
|
2256
|
+
if (name === "") continue;
|
|
2257
|
+
const commands = declared.get(name) ?? /* @__PURE__ */ new Set();
|
|
2258
|
+
for (const command of need.commands ?? []) commands.add(command);
|
|
2259
|
+
declared.set(name, commands);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return declared;
|
|
2263
|
+
}
|
|
2073
2264
|
function readInstalledModule(profileDir, pkg) {
|
|
2074
2265
|
if (profileDir === null || !isPackageName(pkg)) return {
|
|
2075
2266
|
kind: "unknown",
|
|
@@ -2105,6 +2296,32 @@ function readInstalledModule(profileDir, pkg) {
|
|
|
2105
2296
|
...registry === "" ? {} : { registry }
|
|
2106
2297
|
};
|
|
2107
2298
|
}
|
|
2299
|
+
function isLocalDependencySpec(spec) {
|
|
2300
|
+
const trimmed = spec.trim();
|
|
2301
|
+
return trimmed.startsWith("link:") || trimmed.startsWith("file:") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/");
|
|
2302
|
+
}
|
|
2303
|
+
function readProfilePackage(profileDir, pkg) {
|
|
2304
|
+
if (profileDir === null || !isPackageName(pkg)) return void 0;
|
|
2305
|
+
let manifest;
|
|
2306
|
+
try {
|
|
2307
|
+
manifest = JSON.parse(fs.readFileSync(path.join(profileDir, "node_modules", pkg, "package.json"), "utf8"));
|
|
2308
|
+
} catch {
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
const record = asRecord$5(manifest);
|
|
2312
|
+
if (record === void 0) return void 0;
|
|
2313
|
+
const version = asText$3(record.version);
|
|
2314
|
+
if (version === "") return void 0;
|
|
2315
|
+
const declaredRegistry = asText$3(asRecord$5(record.publishConfig)?.registry);
|
|
2316
|
+
const registry = declaredRegistry === "" ? registryFromLockfile(profileDir, pkg) : declaredRegistry;
|
|
2317
|
+
const local = isLocalDependencySpec(asText$3((asRecord$5(profileManifestOf(profileDir)?.dependencies) ?? {})[pkg]));
|
|
2318
|
+
return {
|
|
2319
|
+
pkg,
|
|
2320
|
+
version,
|
|
2321
|
+
...registry === "" ? {} : { registry },
|
|
2322
|
+
...local ? { local: true } : {}
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2108
2325
|
//#endregion
|
|
2109
2326
|
//#region src/shared/api/host/profile-registries.ts
|
|
2110
2327
|
var REGISTRY_MARKER = "# dsh-workbench 写的:这一行的包只在这个源上(dsh 卸载没有源参数,pnpm 得问得到)";
|
|
@@ -2350,6 +2567,13 @@ var panelsApproval = (call) => {
|
|
|
2350
2567
|
zh: `agent 想${verb} opencli 插件 ${what}(会改这台机器上装的东西)。允许吗?`
|
|
2351
2568
|
});
|
|
2352
2569
|
}
|
|
2570
|
+
if (action === "update-workbench") return askOf("workbench_modules update-workbench replaces the workbench itself with the new version from the registry", {
|
|
2571
|
+
en: "it updates the workbench itself on this machine (the panel picks the new version up by itself). Allow?",
|
|
2572
|
+
zh: "会把工作台自己更新到源上的新版(面板会自己换新)。允许吗?"
|
|
2573
|
+
}, {
|
|
2574
|
+
en: "The agent wants to update the workbench itself to the new version from the registry (it changes what is installed on this machine; the panel picks the new version up by itself). Allow?",
|
|
2575
|
+
zh: "agent 想把工作台自己更新到源上的新版(会改这台机器上装的东西,面板会自己换新)。允许吗?"
|
|
2576
|
+
});
|
|
2353
2577
|
if (action === "update-dependency") return askOf(`workbench_modules update-dependency ${id ?? ""} upgrades a shared dependency on this machine`, {
|
|
2354
2578
|
en: "it upgrades that shared dependency on this machine. Allow?",
|
|
2355
2579
|
zh: `会升级共用依赖 ${what}。允许吗?`
|
|
@@ -2423,13 +2647,20 @@ var EMPTY_BOARD = {
|
|
|
2423
2647
|
};
|
|
2424
2648
|
var BOARD_TARGETS = ["workspace", "machine"];
|
|
2425
2649
|
var DEFAULT_BOARD_TARGET = "workspace";
|
|
2426
|
-
function
|
|
2427
|
-
|
|
2428
|
-
|
|
2650
|
+
function overrideOf(board, id) {
|
|
2651
|
+
if (board === null) return void 0;
|
|
2652
|
+
if (board.hidden.includes(id)) return false;
|
|
2653
|
+
if (board.shown.includes(id)) return true;
|
|
2654
|
+
}
|
|
2655
|
+
function defaultOf(board, id) {
|
|
2429
2656
|
if (board === null) return false;
|
|
2430
2657
|
if (board.hidden.includes(id)) return false;
|
|
2431
2658
|
return board.shown.includes(id);
|
|
2432
2659
|
}
|
|
2660
|
+
function resolveScope(query) {
|
|
2661
|
+
const { id, boards } = query;
|
|
2662
|
+
return overrideOf(boards.workspace, id) ?? defaultOf(boards.machine, id);
|
|
2663
|
+
}
|
|
2433
2664
|
function strings(raw) {
|
|
2434
2665
|
return Array.isArray(raw) ? raw.filter((item) => typeof item === "string") : [];
|
|
2435
2666
|
}
|
|
@@ -2446,7 +2677,7 @@ function parseReleased(raw) {
|
|
|
2446
2677
|
return strings(raw.ids);
|
|
2447
2678
|
}
|
|
2448
2679
|
function resolvePanels(manifests, boards) {
|
|
2449
|
-
const ordered = boards.workspace?.shown ?? boards.machine?.shown ?? [];
|
|
2680
|
+
const ordered = [.../* @__PURE__ */ new Set([...boards.workspace?.shown ?? [], ...boards.machine?.shown ?? []])];
|
|
2450
2681
|
const rank = new Map(ordered.map((id, index) => [id, index]));
|
|
2451
2682
|
return manifests.filter((manifest) => manifest.card !== null).map((manifest) => {
|
|
2452
2683
|
const on = resolveScope({
|
|
@@ -2459,11 +2690,39 @@ function resolvePanels(manifests, boards) {
|
|
|
2459
2690
|
titleKey: manifest.card?.titleKey ?? manifest.id,
|
|
2460
2691
|
order,
|
|
2461
2692
|
shown: on,
|
|
2462
|
-
source:
|
|
2693
|
+
source: overrideOf(boards.workspace, manifest.id) === void 0 ? "default" : "user",
|
|
2463
2694
|
hasCard: true
|
|
2464
2695
|
};
|
|
2465
2696
|
}).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
2466
2697
|
}
|
|
2698
|
+
var PLUGIN_PANEL_PREFIX = "plugin:";
|
|
2699
|
+
function pluginPanelId(pkg) {
|
|
2700
|
+
return `${PLUGIN_PANEL_PREFIX}${pkg}`;
|
|
2701
|
+
}
|
|
2702
|
+
function resolvePluginPanels(plugins, boards) {
|
|
2703
|
+
const ordered = [.../* @__PURE__ */ new Set([...boards.workspace?.shown ?? [], ...boards.machine?.shown ?? []])];
|
|
2704
|
+
const rank = new Map(ordered.map((id, index) => [id, index]));
|
|
2705
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2706
|
+
return plugins.map((plugin) => {
|
|
2707
|
+
const id = pluginPanelId(plugin.pkg);
|
|
2708
|
+
return {
|
|
2709
|
+
id,
|
|
2710
|
+
pkg: plugin.pkg,
|
|
2711
|
+
title: plugin.title,
|
|
2712
|
+
...plugin.name === void 0 ? {} : { name: plugin.name },
|
|
2713
|
+
order: rank.get(id) ?? ordered.length,
|
|
2714
|
+
shown: resolveScope({
|
|
2715
|
+
id,
|
|
2716
|
+
boards
|
|
2717
|
+
}),
|
|
2718
|
+
source: overrideOf(boards.workspace, id) === void 0 ? "default" : "user"
|
|
2719
|
+
};
|
|
2720
|
+
}).filter((entry) => {
|
|
2721
|
+
if (seen.has(entry.id)) return false;
|
|
2722
|
+
seen.add(entry.id);
|
|
2723
|
+
return true;
|
|
2724
|
+
}).sort((a, b) => a.order - b.order || a.pkg.localeCompare(b.pkg));
|
|
2725
|
+
}
|
|
2467
2726
|
function withoutReleased(released, id) {
|
|
2468
2727
|
return released.filter((item) => item !== id);
|
|
2469
2728
|
}
|
|
@@ -2601,7 +2860,7 @@ var PLUGIN_PREFIX = "opencli-plugin-";
|
|
|
2601
2860
|
function text(value) {
|
|
2602
2861
|
return typeof value === "string" ? value.trim() : "";
|
|
2603
2862
|
}
|
|
2604
|
-
function stringsOf(value) {
|
|
2863
|
+
function stringsOf$1(value) {
|
|
2605
2864
|
return Array.isArray(value) ? value.filter((one) => typeof one === "string") : [];
|
|
2606
2865
|
}
|
|
2607
2866
|
function manifestCommandsOf(raw) {
|
|
@@ -2632,7 +2891,7 @@ function manifestCommandsOf(raw) {
|
|
|
2632
2891
|
strategy: text(row["strategy"]).toLowerCase(),
|
|
2633
2892
|
browser: row["browser"] === true,
|
|
2634
2893
|
example: text(row["example"]),
|
|
2635
|
-
columns: stringsOf(row["columns"])
|
|
2894
|
+
columns: stringsOf$1(row["columns"])
|
|
2636
2895
|
});
|
|
2637
2896
|
}
|
|
2638
2897
|
return commands;
|
|
@@ -2724,7 +2983,7 @@ function reconOf(commands, plugins, keywords) {
|
|
|
2724
2983
|
};
|
|
2725
2984
|
return {
|
|
2726
2985
|
...withNext,
|
|
2727
|
-
text: textOf$
|
|
2986
|
+
text: textOf$2(withNext)
|
|
2728
2987
|
};
|
|
2729
2988
|
}
|
|
2730
2989
|
function nextOf(answer) {
|
|
@@ -2737,7 +2996,7 @@ function nextOf(answer) {
|
|
|
2737
2996
|
if (plugin !== void 0) return `这台机器另装的插件 ${plugin.name} 里有匹配:opencli ${plugin.name} --help 看全部命令`;
|
|
2738
2997
|
return "这台机器上没有现成的 —— 两条路:① 给这个站点写 adapter:opencli skills read opencli-adapter-author;② 只读一次:workbench_diy action=probe(script 读接口 / css 读 DOM)";
|
|
2739
2998
|
}
|
|
2740
|
-
function textOf$
|
|
2999
|
+
function textOf$2(answer) {
|
|
2741
3000
|
const lines = [];
|
|
2742
3001
|
if (answer.sites.length > 0) {
|
|
2743
3002
|
lines.push(`opencli 自带这 ${String(answer.sites.length)} 组能力(不用装、不用点头):`);
|
|
@@ -2782,7 +3041,7 @@ function installedPluginsOf(dir = vendorPluginsDir()) {
|
|
|
2782
3041
|
}
|
|
2783
3042
|
//#endregion
|
|
2784
3043
|
//#region src/modules/panels/model/diy-tools.ts
|
|
2785
|
-
var ACTIONS$
|
|
3044
|
+
var ACTIONS$2 = [
|
|
2786
3045
|
"list",
|
|
2787
3046
|
"status",
|
|
2788
3047
|
"probe",
|
|
@@ -2919,7 +3178,7 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
2919
3178
|
action: {
|
|
2920
3179
|
type: "string",
|
|
2921
3180
|
description: "list=工坊里有哪些模块(默认);status=某个模块现在走到哪一步、下一步做什么;probe=在用户浏览器里探一条读;recon=先查 opencli 现成的能力(自带 adapter / 本机插件);open=只把它在这个项目上板(不装);install=装上并上板(第一次、会让用户点头)。",
|
|
2922
|
-
enum: [...ACTIONS$
|
|
3181
|
+
enum: [...ACTIONS$2]
|
|
2923
3182
|
},
|
|
2924
3183
|
unit: {
|
|
2925
3184
|
type: "string",
|
|
@@ -2978,7 +3237,7 @@ function registerDiyTools(ctx, market, panels) {
|
|
|
2978
3237
|
async execute(raw, exec) {
|
|
2979
3238
|
const args = asRecord$3(raw) ?? {};
|
|
2980
3239
|
const action = asText$1(args.action) || "list";
|
|
2981
|
-
if (!ACTIONS$
|
|
3240
|
+
if (!ACTIONS$2.includes(action)) throw new Error(`不认识的 action:${action}(可用:${ACTIONS$2.join(" / ")})`);
|
|
2982
3241
|
const packagesDir = workshopPackagesDir(ctx);
|
|
2983
3242
|
if (packagesDir === null) throw new Error("算不出 DIY 工坊在哪(这台机器没给出 dshHomePath)—— 让用户跑一次 dsh-workbench setup");
|
|
2984
3243
|
const session = sessionWorkspace(exec);
|
|
@@ -3907,9 +4166,45 @@ function createEnvDropper(options) {
|
|
|
3907
4166
|
};
|
|
3908
4167
|
}
|
|
3909
4168
|
//#endregion
|
|
4169
|
+
//#region src/modules/panels/model/market-registry.ts
|
|
4170
|
+
async function rememberInstalledSource(deps, manager, pkg, registry) {
|
|
4171
|
+
const { registries, logger } = deps;
|
|
4172
|
+
if (registries === void 0 || registry === void 0) return;
|
|
4173
|
+
const scope = scopeOf(pkg);
|
|
4174
|
+
if (scope === null) return;
|
|
4175
|
+
const known = await manager.registries().catch(() => null);
|
|
4176
|
+
if (known !== null && sameRegistry(known.resolved, registry)) return;
|
|
4177
|
+
try {
|
|
4178
|
+
const added = registries.remember([{
|
|
4179
|
+
scope,
|
|
4180
|
+
registry
|
|
4181
|
+
}]);
|
|
4182
|
+
if (added.length > 0) logger?.info?.(`[dsh-workbench] 把 ${added.join("、")} 记进 profile 的 .npmrc(卸载只认 pnpm 的配置)`);
|
|
4183
|
+
} catch (error) {
|
|
4184
|
+
logger?.warn?.(`[dsh-workbench] 记这个 scope 的源失败:${String(error)}`);
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
async function retryAfterRegistryFix(deps, manager, pkg, change) {
|
|
4188
|
+
const { registries, logger } = deps;
|
|
4189
|
+
if (registries === void 0 || !registryFailure(change)) return change;
|
|
4190
|
+
const known = await manager.registries().catch(() => null);
|
|
4191
|
+
const missing = registries.sources().filter((entry) => !sameRegistry(entry.registry, known?.resolved ?? null));
|
|
4192
|
+
if (missing.length === 0) return change;
|
|
4193
|
+
let added;
|
|
4194
|
+
try {
|
|
4195
|
+
added = registries.remember(missing);
|
|
4196
|
+
} catch (error) {
|
|
4197
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源,但补配置失败:${String(error)}`);
|
|
4198
|
+
return change;
|
|
4199
|
+
}
|
|
4200
|
+
if (added.length === 0) return change;
|
|
4201
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
4202
|
+
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
4203
|
+
}
|
|
4204
|
+
//#endregion
|
|
3910
4205
|
//#region src/modules/panels/model/market.ts
|
|
3911
4206
|
function createMarketService(options) {
|
|
3912
|
-
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, registries, ledger, references } = options;
|
|
4207
|
+
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, ledger, references } = options;
|
|
3913
4208
|
const command = options.command ?? "opencli";
|
|
3914
4209
|
const catalog = () => (manifestsNow?.() ?? manifests).filter((manifest) => (manifest.env?.plugins ?? []).length > 0 || (manifest.env?.libs ?? []).length > 0);
|
|
3915
4210
|
function requireEntry(unit) {
|
|
@@ -4054,44 +4349,16 @@ function createMarketService(options) {
|
|
|
4054
4349
|
};
|
|
4055
4350
|
}
|
|
4056
4351
|
}
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
const known = await manager.registries().catch(() => null);
|
|
4062
|
-
if (known !== null && sameRegistry(known.resolved, registry)) return;
|
|
4063
|
-
try {
|
|
4064
|
-
const added = registries.remember([{
|
|
4065
|
-
scope,
|
|
4066
|
-
registry
|
|
4067
|
-
}]);
|
|
4068
|
-
if (added.length > 0) logger?.info?.(`[dsh-workbench] 把 ${added.join("、")} 记进 profile 的 .npmrc(卸载只认 pnpm 的配置)`);
|
|
4069
|
-
} catch (error) {
|
|
4070
|
-
logger?.warn?.(`[dsh-workbench] 记这个 scope 的源失败:${String(error)}`);
|
|
4071
|
-
}
|
|
4072
|
-
}
|
|
4073
|
-
async function retryAfterRegistryFix(manager, pkg, change) {
|
|
4074
|
-
if (registries === void 0 || !registryFailure(change)) return change;
|
|
4075
|
-
const known = await manager.registries().catch(() => null);
|
|
4076
|
-
const missing = registries.sources().filter((entry) => !sameRegistry(entry.registry, known?.resolved ?? null));
|
|
4077
|
-
if (missing.length === 0) return change;
|
|
4078
|
-
let added;
|
|
4079
|
-
try {
|
|
4080
|
-
added = registries.remember(missing);
|
|
4081
|
-
} catch (error) {
|
|
4082
|
-
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源,但补配置失败:${String(error)}`);
|
|
4083
|
-
return change;
|
|
4084
|
-
}
|
|
4085
|
-
if (added.length === 0) return change;
|
|
4086
|
-
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
4087
|
-
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
4088
|
-
}
|
|
4352
|
+
const registryFix = {
|
|
4353
|
+
...registries === void 0 ? {} : { registries },
|
|
4354
|
+
...logger === void 0 ? {} : { logger }
|
|
4355
|
+
};
|
|
4089
4356
|
function routeDeps(manager) {
|
|
4090
4357
|
return {
|
|
4091
4358
|
manager,
|
|
4092
4359
|
command,
|
|
4093
4360
|
runAll,
|
|
4094
|
-
rememberSource: rememberInstalledSource,
|
|
4361
|
+
rememberSource: (manager, pkg, registry) => rememberInstalledSource(registryFix, manager, pkg, registry),
|
|
4095
4362
|
supplyAfter,
|
|
4096
4363
|
...shell === void 0 ? {} : { shell },
|
|
4097
4364
|
...installed === void 0 ? {} : { installed },
|
|
@@ -4237,7 +4504,7 @@ function createMarketService(options) {
|
|
|
4237
4504
|
const before = unit === void 0 ? void 0 : await capabilityOf(unit, true);
|
|
4238
4505
|
const first = await manager.remove(pkg);
|
|
4239
4506
|
if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
|
|
4240
|
-
const change = await retryAfterRegistryFix(manager, pkg, first);
|
|
4507
|
+
const change = await retryAfterRegistryFix(registryFix, manager, pkg, first);
|
|
4241
4508
|
if (change.application !== "applied" || unit === void 0) return {
|
|
4242
4509
|
change,
|
|
4243
4510
|
keptFor: []
|
|
@@ -4273,6 +4540,24 @@ function createMarketService(options) {
|
|
|
4273
4540
|
await service.update(unit).catch(() => void 0);
|
|
4274
4541
|
return outcome;
|
|
4275
4542
|
},
|
|
4543
|
+
async updateWorkbench(pkg) {
|
|
4544
|
+
const target = pkg.trim() === "" ? PLUGIN_ID : pkg.trim();
|
|
4545
|
+
if (target !== "@dsh-workbench/core") throw new Error(`这条路只更新工作台自己那一份(${PLUGIN_ID}):${target}`);
|
|
4546
|
+
const read = workbench?.(target);
|
|
4547
|
+
if (read === void 0) throw new Error(`读不到 profile 里装的 ${target} —— 没法判断装的是哪一份,更新做不了。`);
|
|
4548
|
+
if (read.local === true) throw new Error("这份工作台是从本仓装进来的(开发副本),不从源上更新。");
|
|
4549
|
+
const registry = read.registry;
|
|
4550
|
+
const latest = shell === void 0 || registry === void 0 ? void 0 : await latestVersion({
|
|
4551
|
+
shell,
|
|
4552
|
+
name: read.pkg,
|
|
4553
|
+
registry,
|
|
4554
|
+
force: true
|
|
4555
|
+
});
|
|
4556
|
+
const spec = latest === void 0 ? `${read.pkg}@latest` : `${read.pkg}@${latest}`;
|
|
4557
|
+
const outcome = await installDshBundle(routeDeps(requireBundles()), spec, { ...registry === void 0 ? {} : { registry } });
|
|
4558
|
+
logger?.info?.(`[dsh-workbench] 已更新工作台 ${read.version} → ${latest ?? "最新版"}`);
|
|
4559
|
+
return outcome;
|
|
4560
|
+
},
|
|
4276
4561
|
async updatePlugin(pkg) {
|
|
4277
4562
|
const outcome = await plugins.update(pkg);
|
|
4278
4563
|
await reprobePlugin(pkg, outcome.name);
|
|
@@ -4337,6 +4622,11 @@ async function dispatchMarket(market, request) {
|
|
|
4337
4622
|
action: "cancel-install",
|
|
4338
4623
|
cancellation: await market.cancelInstall(request.requestId ?? "")
|
|
4339
4624
|
};
|
|
4625
|
+
if (request.action === "update-workbench") return {
|
|
4626
|
+
ok: true,
|
|
4627
|
+
action: "update-workbench",
|
|
4628
|
+
change: (await market.updateWorkbench(request.name ?? "")).change
|
|
4629
|
+
};
|
|
4340
4630
|
if (request.action === "update-plugin") {
|
|
4341
4631
|
await market.updatePlugin(request.name ?? "");
|
|
4342
4632
|
return {
|
|
@@ -4431,12 +4721,16 @@ function describePluginUninstall(pkg, outcome) {
|
|
|
4431
4721
|
if (outcome.removed) return `已卸载 opencli 插件 ${pkg}:没人再用它,文件也删了。`;
|
|
4432
4722
|
return `已从「你装的插件」里去掉 ${pkg}。${outcome.kept === void 0 ? "" : pluginKeptText(outcome.kept)}`;
|
|
4433
4723
|
}
|
|
4724
|
+
function describeWorkbenchUpdate(change) {
|
|
4725
|
+
if (change.application === "failed" || change.application === "cancelled") return `没成功:${change.errorCode ?? change.application}`;
|
|
4726
|
+
return change.application === "restart-required" ? "工作台更新到新版了 —— 这一版要重启一次 dsh 才生效(重启由用户来,别自己重启)。" : "工作台更新到新版了 —— 客户端与宿主自己换上,不用重启。";
|
|
4727
|
+
}
|
|
4434
4728
|
function usersOf(shared, names) {
|
|
4435
4729
|
const hit = shared.find((item) => names.some((name) => name !== void 0 && name !== "" && item.id === `plugin:${name}`));
|
|
4436
4730
|
if (hit === void 0 || hit.usedBy.length === 0) return "";
|
|
4437
4731
|
return `它被 ${hit.usedBy.join("、")} 用着(动的是它们的供给,它们的状态跟着重探了一遍)`;
|
|
4438
4732
|
}
|
|
4439
|
-
var ACTIONS = [
|
|
4733
|
+
var ACTIONS$1 = [
|
|
4440
4734
|
"list",
|
|
4441
4735
|
"install",
|
|
4442
4736
|
"update",
|
|
@@ -4444,7 +4738,8 @@ var ACTIONS = [
|
|
|
4444
4738
|
"install-bundle",
|
|
4445
4739
|
"update-dependency",
|
|
4446
4740
|
"update-plugin",
|
|
4447
|
-
"uninstall-plugin"
|
|
4741
|
+
"uninstall-plugin",
|
|
4742
|
+
"update-workbench"
|
|
4448
4743
|
];
|
|
4449
4744
|
function registerMarketTools(ctx, market) {
|
|
4450
4745
|
const tools = ctx.get("tools");
|
|
@@ -4454,14 +4749,14 @@ function registerMarketTools(ctx, market) {
|
|
|
4454
4749
|
}
|
|
4455
4750
|
const tool = {
|
|
4456
4751
|
name: "workbench_modules",
|
|
4457
|
-
description: "看 / 装 / 升 / 卸工作台的面板模块,以及用户自己装的 opencli 插件。install-bundle 按 spec(包名,可带版本;git / tarball / 本地路径也行)把模块包装进 profile —— 装完会连带装它声明的 opencli 插件;其余 action 按模块 id 动它要的外部供给;update-plugin / uninstall-plugin
|
|
4752
|
+
description: "看 / 装 / 升 / 卸工作台的面板模块,以及用户自己装的 opencli 插件。install-bundle 按 spec(包名,可带版本;git / tarball / 本地路径也行)把模块包装进 profile —— 装完会连带装它声明的 opencli 插件;其余 action 按模块 id 动它要的外部供给;update-plugin / uninstall-plugin 按包名或插件名动用户装的那份插件;update-workbench 把工作台自己更新到源上的新版(客户端与宿主一般会自己换上;真要重启时结果里会说,重启由用户来,别自己重启)。卸载只动工作台运行时,不影响终端那套;共用依赖只有升级。",
|
|
4458
4753
|
parameters: {
|
|
4459
4754
|
type: "object",
|
|
4460
4755
|
properties: {
|
|
4461
4756
|
action: {
|
|
4462
4757
|
type: "string",
|
|
4463
|
-
description: "install-bundle=按 spec 装模块包(要 spec),update-dependency=升级共用依赖(要 id),update-plugin / uninstall-plugin=动用户自己装的 opencli 插件(要 plugin
|
|
4464
|
-
enum: [...ACTIONS]
|
|
4758
|
+
description: "install-bundle=按 spec 装模块包(要 spec),update-dependency=升级共用依赖(要 id),update-plugin / uninstall-plugin=动用户自己装的 opencli 插件(要 plugin),update-workbench=把工作台自己更新到新版(不用别的参数),其余=对模块(要 id)。",
|
|
4759
|
+
enum: [...ACTIONS$1]
|
|
4465
4760
|
},
|
|
4466
4761
|
id: {
|
|
4467
4762
|
type: "string",
|
|
@@ -4495,7 +4790,7 @@ function registerMarketTools(ctx, market) {
|
|
|
4495
4790
|
},
|
|
4496
4791
|
async execute(args) {
|
|
4497
4792
|
const action = args?.action ?? "list";
|
|
4498
|
-
if (!ACTIONS.includes(action)) throw new Error(`不认识的 action:${action}(可用:${ACTIONS.join(" / ")})`);
|
|
4793
|
+
if (!ACTIONS$1.includes(action)) throw new Error(`不认识的 action:${action}(可用:${ACTIONS$1.join(" / ")})`);
|
|
4499
4794
|
if (action === "list") {
|
|
4500
4795
|
const { modules, shared } = await market.list();
|
|
4501
4796
|
return {
|
|
@@ -4516,6 +4811,13 @@ function registerMarketTools(ctx, market) {
|
|
|
4516
4811
|
text: describeInstall(spec, outcome.module, outcome.supply, outcome.change)
|
|
4517
4812
|
};
|
|
4518
4813
|
}
|
|
4814
|
+
if (action === "update-workbench") {
|
|
4815
|
+
const outcome = await market.updateWorkbench("");
|
|
4816
|
+
return {
|
|
4817
|
+
change: outcome.change,
|
|
4818
|
+
text: describeWorkbenchUpdate(outcome.change)
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4519
4821
|
if (action === "update-plugin" || action === "uninstall-plugin") {
|
|
4520
4822
|
const plugin = args?.plugin?.trim() ?? "";
|
|
4521
4823
|
if (plugin === "") throw new Error(`${action} 需要一个 plugin:opencli 插件的包名或插件名 —— 就是市场「已安装 opencli 插件」那一段里的名字。`);
|
|
@@ -4566,53 +4868,70 @@ function registerMarketTools(ctx, market) {
|
|
|
4566
4868
|
//#endregion
|
|
4567
4869
|
//#region src/modules/panels/model/prefs.ts
|
|
4568
4870
|
var PANELS_PREF_RESOURCE = "panels";
|
|
4871
|
+
var ACTIONS = [
|
|
4872
|
+
"show",
|
|
4873
|
+
"hide",
|
|
4874
|
+
"move",
|
|
4875
|
+
"reset",
|
|
4876
|
+
"follow"
|
|
4877
|
+
];
|
|
4569
4878
|
function parsePanelsPrefAction(payload) {
|
|
4570
|
-
if (typeof payload !== "object" || payload === null) throw new Error("偏好写需要一个对象:{ action, id?, index?, path? }");
|
|
4879
|
+
if (typeof payload !== "object" || payload === null) throw new Error("偏好写需要一个对象:{ action, id?, index?, path?, scope? }");
|
|
4571
4880
|
const value = payload;
|
|
4572
4881
|
const action = value.action;
|
|
4573
|
-
if (
|
|
4882
|
+
if (typeof action !== "string" || !ACTIONS.includes(action)) throw new Error(`不认识的 action:${String(action)}(可用:${ACTIONS.join(" / ")})`);
|
|
4883
|
+
if (value.scope !== void 0 && value.scope !== "machine" && value.scope !== "workspace") throw new Error(`不认识的 scope:${String(value.scope)}(可用:workspace / machine)`);
|
|
4574
4884
|
return {
|
|
4575
4885
|
action,
|
|
4576
4886
|
...typeof value.id === "string" ? { id: value.id } : {},
|
|
4577
4887
|
...typeof value.index === "number" ? { index: value.index } : {},
|
|
4578
|
-
...typeof value.path === "string" ? { path: value.path } : {}
|
|
4888
|
+
...typeof value.path === "string" ? { path: value.path } : {},
|
|
4889
|
+
...value.scope === void 0 ? {} : { scope: value.scope }
|
|
4579
4890
|
};
|
|
4580
4891
|
}
|
|
4581
4892
|
function applyPanelsPref(panels, payload) {
|
|
4582
4893
|
const action = parsePanelsPrefAction(payload);
|
|
4583
4894
|
const path = action.path ?? null;
|
|
4895
|
+
const scope = action.scope ?? "workspace";
|
|
4896
|
+
if (action.action === "follow") {
|
|
4897
|
+
if (scope === "machine") throw new Error("机器级默认板没有\"回到跟随\"一说:它就是那份默认本身(要回到的是\"没配过\")。");
|
|
4898
|
+
return {
|
|
4899
|
+
ok: true,
|
|
4900
|
+
units: action.id === void 0 ? panels.follow(path) : panels.follow(path, action.id)
|
|
4901
|
+
};
|
|
4902
|
+
}
|
|
4584
4903
|
if (action.action === "reset") return {
|
|
4585
4904
|
ok: true,
|
|
4586
|
-
units: panels.reset(path)
|
|
4905
|
+
units: panels.reset(path, scope)
|
|
4587
4906
|
};
|
|
4588
4907
|
if (action.id === void 0) throw new Error(`${action.action} 需要一个 id(当前套件:${panels.list(path).map((entry) => entry.id).join(", ")})`);
|
|
4589
4908
|
if (action.action === "show") return {
|
|
4590
4909
|
ok: true,
|
|
4591
|
-
units: panels.show(action.id, path)
|
|
4910
|
+
units: panels.show(action.id, path, scope)
|
|
4592
4911
|
};
|
|
4593
4912
|
if (action.action === "hide") return {
|
|
4594
4913
|
ok: true,
|
|
4595
|
-
units: panels.hide(action.id, path)
|
|
4914
|
+
units: panels.hide(action.id, path, scope)
|
|
4596
4915
|
};
|
|
4597
4916
|
return {
|
|
4598
4917
|
ok: true,
|
|
4599
|
-
units: panels.move(action.id, action.index ?? 0, path)
|
|
4918
|
+
units: panels.move(action.id, action.index ?? 0, path, scope)
|
|
4600
4919
|
};
|
|
4601
4920
|
}
|
|
4602
4921
|
//#endregion
|
|
4603
4922
|
//#region src/modules/panels/model/handoff.ts
|
|
4604
4923
|
var PANELS_FACT = "panels";
|
|
4605
4924
|
var CAPABILITY_FACT = "capability";
|
|
4606
|
-
function textOf(value) {
|
|
4925
|
+
function textOf$1(value) {
|
|
4607
4926
|
return typeof value === "string" ? value.trim() : "";
|
|
4608
4927
|
}
|
|
4609
4928
|
function actionOf(call) {
|
|
4610
|
-
return textOf(call.args["action"]) || "list";
|
|
4929
|
+
return textOf$1(call.args["action"]) || "list";
|
|
4611
4930
|
}
|
|
4612
4931
|
function requestOf(call) {
|
|
4613
4932
|
const action = actionOf(call);
|
|
4614
4933
|
if (!PROVISION_ACTIONS.includes(action)) return void 0;
|
|
4615
|
-
const target = textOf(call.args["unit"]) || textOf(call.args["id"]);
|
|
4934
|
+
const target = textOf$1(call.args["unit"]) || textOf$1(call.args["id"]);
|
|
4616
4935
|
if (target === "") return void 0;
|
|
4617
4936
|
const wanted = action;
|
|
4618
4937
|
return isDependencyAction(wanted) ? {
|
|
@@ -4624,7 +4943,7 @@ function requestOf(call) {
|
|
|
4624
4943
|
};
|
|
4625
4944
|
}
|
|
4626
4945
|
function panelsHandoffEntry(target = {}) {
|
|
4627
|
-
const path = textOf(target.path);
|
|
4946
|
+
const path = textOf$1(target.path);
|
|
4628
4947
|
return path === "" ? void 0 : {
|
|
4629
4948
|
fact: PANELS_FACT,
|
|
4630
4949
|
key: `path:${path}`
|
|
@@ -4635,6 +4954,7 @@ function panelsFactsOf(call, manifests) {
|
|
|
4635
4954
|
if (call.source === "workbench_diy" && actionOf(call) === "list") return [PANELS_FACT];
|
|
4636
4955
|
if (call.source !== "workbench_modules" && call.source !== "workbench_diy") return void 0;
|
|
4637
4956
|
if (actionOf(call) === "list") return [PANELS_FACT, CAPABILITY_FACT];
|
|
4957
|
+
if (actionOf(call) === "update-workbench") return [PANELS_FACT, CAPABILITY_FACT];
|
|
4638
4958
|
const request = requestOf(call);
|
|
4639
4959
|
if (request === void 0) return void 0;
|
|
4640
4960
|
return [
|
|
@@ -4722,7 +5042,14 @@ function createPanelsService(options) {
|
|
|
4722
5042
|
const all = manifests();
|
|
4723
5043
|
if (!all.some((manifest) => manifest.id === id)) throw new Error(`没有这个套件:${id}(可用:${all.map((manifest) => manifest.id).join(", ")})`);
|
|
4724
5044
|
}
|
|
5045
|
+
function pluginRefs() {
|
|
5046
|
+
return options.plugins?.() ?? [];
|
|
5047
|
+
}
|
|
5048
|
+
function pluginIds() {
|
|
5049
|
+
return new Set(pluginRefs().map((plugin) => pluginPanelId(plugin.pkg)));
|
|
5050
|
+
}
|
|
4725
5051
|
function requireSwitchable(id) {
|
|
5052
|
+
if (pluginIds().has(id)) return;
|
|
4726
5053
|
requireKnown(id);
|
|
4727
5054
|
if (!manifests().some((manifest) => manifest.id === id && manifest.card !== null)) throw new Error(`${id} 没有卡片,不能收起(它是把别的套件放回来的入口)`);
|
|
4728
5055
|
}
|
|
@@ -4735,6 +5062,9 @@ function createPanelsService(options) {
|
|
|
4735
5062
|
list(workspace, target) {
|
|
4736
5063
|
return resolvePanels(manifests(), target === "machine" ? boardsOf(null) : boardsOf(workspace));
|
|
4737
5064
|
},
|
|
5065
|
+
listPlugins(workspace, target) {
|
|
5066
|
+
return resolvePluginPanels(pluginRefs(), target === "machine" ? boardsOf(null) : boardsOf(workspace));
|
|
5067
|
+
},
|
|
4738
5068
|
boards(workspace) {
|
|
4739
5069
|
return boardsOf(workspace);
|
|
4740
5070
|
},
|
|
@@ -4792,6 +5122,24 @@ function createPanelsService(options) {
|
|
|
4792
5122
|
reset(workspace, target) {
|
|
4793
5123
|
const { folder, key } = targetOf(target ?? "workspace", workspace);
|
|
4794
5124
|
return withPersisted(folder, EMPTY_BOARD, key);
|
|
5125
|
+
},
|
|
5126
|
+
changedBy() {
|
|
5127
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5128
|
+
for (const folder of store.scan(PANELS_MODULE_ID)) {
|
|
5129
|
+
const board = readBoard(folder);
|
|
5130
|
+
if (board === null) continue;
|
|
5131
|
+
for (const id of /* @__PURE__ */ new Set([...board.shown, ...board.hidden])) counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
5132
|
+
}
|
|
5133
|
+
return counts;
|
|
5134
|
+
},
|
|
5135
|
+
follow(workspace, id) {
|
|
5136
|
+
const { folder, key } = targetOf("workspace", workspace);
|
|
5137
|
+
if (id === void 0) {
|
|
5138
|
+
folder.remove(PANELS_FILE);
|
|
5139
|
+
notify();
|
|
5140
|
+
return resolvePanels(manifests(), boardsOf(key));
|
|
5141
|
+
}
|
|
5142
|
+
return withPersisted(folder, withoutPanel(boardIn(folder), id), key);
|
|
4795
5143
|
}
|
|
4796
5144
|
};
|
|
4797
5145
|
}
|
|
@@ -4802,13 +5150,15 @@ function panelRoutes(panels, values) {
|
|
|
4802
5150
|
const path = param(search, "path") ?? null;
|
|
4803
5151
|
const entry = panelsHandoffEntry(path === null ? {} : { path });
|
|
4804
5152
|
const handed = entry === void 0 ? void 0 : values?.take(entry);
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
units: handed.value
|
|
4808
|
-
};
|
|
5153
|
+
const defaults = [...panels.list(null, "machine").filter((unit) => unit.shown).map((unit) => unit.id), ...panels.listPlugins(null, "machine").filter((plugin) => plugin.shown).map((plugin) => plugin.id)];
|
|
5154
|
+
const own = panels.boards(path).workspace !== null;
|
|
4809
5155
|
return {
|
|
4810
5156
|
ok: true,
|
|
4811
|
-
units: panels.list(path)
|
|
5157
|
+
units: handed === void 0 ? panels.list(path) : handed.value,
|
|
5158
|
+
plugins: panels.listPlugins(path),
|
|
5159
|
+
defaults,
|
|
5160
|
+
own,
|
|
5161
|
+
changed: Object.fromEntries(panels.changedBy())
|
|
4812
5162
|
};
|
|
4813
5163
|
} };
|
|
4814
5164
|
}
|
|
@@ -4867,6 +5217,18 @@ function createPluginLedger(options) {
|
|
|
4867
5217
|
};
|
|
4868
5218
|
}
|
|
4869
5219
|
//#endregion
|
|
5220
|
+
//#region src/modules/panels/model/plugin-panel.ts
|
|
5221
|
+
function pluginPanelRefs(ledger) {
|
|
5222
|
+
return ledger.list().map((entry) => {
|
|
5223
|
+
const name = pluginNameOf(entry);
|
|
5224
|
+
return {
|
|
5225
|
+
pkg: entry.package,
|
|
5226
|
+
title: name ?? entry.package,
|
|
5227
|
+
...name === void 0 ? {} : { name }
|
|
5228
|
+
};
|
|
5229
|
+
});
|
|
5230
|
+
}
|
|
5231
|
+
//#endregion
|
|
4870
5232
|
//#region src/shared/api/installed-plugins.ts
|
|
4871
5233
|
var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
|
|
4872
5234
|
//#endregion
|
|
@@ -4956,24 +5318,25 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
4956
5318
|
}
|
|
4957
5319
|
const tool = {
|
|
4958
5320
|
name: "workbench_panels",
|
|
4959
|
-
description: "看或调右侧面板:启用哪些套件、什么顺序。启用/收起 = 卡片与 agent 工具一起上下线(别的项目不受影响,不删数据)。收起是\"这个工作区明确不要它\",连那些在机器级默认板上开着的套件也照样在这个工作区收起来,之后 show 能放回来。默认只动当前工作区(scope=workspace);scope=machine 动的是\"机器级默认板\" —— 机器级默认板只是还没配过的工作区的默认值,已经自己配过的工作区保持自己的选择,不会被它改。在那份板上 hide = 明确写下\"不要它\"
|
|
5321
|
+
description: "看或调右侧面板:启用哪些套件、什么顺序。启用/收起 = 卡片与 agent 工具一起上下线(别的项目不受影响,不删数据)。收起是\"这个工作区明确不要它\",连那些在机器级默认板上开着的套件也照样在这个工作区收起来,之后 show 能放回来。默认只动当前工作区(scope=workspace);scope=machine 动的是\"机器级默认板\" —— 机器级默认板只是还没配过的工作区的默认值,已经自己配过的工作区保持自己的选择,不会被它改。在那份板上 hide = 明确写下\"不要它\",这一笔比名单更管用:名单里还列着它也算不要,没提过它的工作区因此看不到它;show 会把这笔划掉、把它放回去。本工作区那份板只记**改过的那几行**(逐行覆盖):没提到的行跟着机器级默认板走,所以那份板以后改了,没改过的工作区会跟着变。workbench_panels 默认只动当前工作区,动机器级默认板要显式给 scope=machine。",
|
|
4960
5322
|
parameters: {
|
|
4961
5323
|
type: "object",
|
|
4962
5324
|
properties: {
|
|
4963
5325
|
action: {
|
|
4964
5326
|
type: "string",
|
|
4965
|
-
description: "show=这个工作区要它(之前收起的也放回来);hide=这个工作区明确不要它(卡片与工具一起下线,默认在机器上也开着的套件同样收起,别的项目不受影响);move=调顺序;reset
|
|
5327
|
+
description: "show=这个工作区要它(之前收起的也放回来);hide=这个工作区明确不要它(卡片与工具一起下线,默认在机器上也开着的套件同样收起,别的项目不受影响);move=调顺序;reset=清掉这个工作区改过的那些行(改完就全部回到跟着机器级默认板走);follow=把某一行放回跟随(带 id = 只放回那一行;不带 id = 全部放回)。",
|
|
4966
5328
|
enum: [
|
|
4967
5329
|
"list",
|
|
4968
5330
|
"show",
|
|
4969
5331
|
"hide",
|
|
4970
5332
|
"move",
|
|
4971
|
-
"reset"
|
|
5333
|
+
"reset",
|
|
5334
|
+
"follow"
|
|
4972
5335
|
]
|
|
4973
5336
|
},
|
|
4974
5337
|
id: {
|
|
4975
5338
|
type: "string",
|
|
4976
|
-
description: "show / hide / move
|
|
5339
|
+
description: "show / hide / move 必填;follow 可选(带 id = 只把那一行放回跟随)。"
|
|
4977
5340
|
},
|
|
4978
5341
|
index: {
|
|
4979
5342
|
type: "integer",
|
|
@@ -5009,8 +5372,9 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
5009
5372
|
"show",
|
|
5010
5373
|
"hide",
|
|
5011
5374
|
"move",
|
|
5012
|
-
"reset"
|
|
5013
|
-
|
|
5375
|
+
"reset",
|
|
5376
|
+
"follow"
|
|
5377
|
+
].includes(action)) throw new Error(`不认识的 action:${action}(可用:list / show / hide / move / reset / follow)`);
|
|
5014
5378
|
if (action === "list") {
|
|
5015
5379
|
const units = list();
|
|
5016
5380
|
if (scope === "workspace") {
|
|
@@ -5022,11 +5386,19 @@ function registerPanelTools(ctx, panels, values) {
|
|
|
5022
5386
|
text: render(units, workspace, scope)
|
|
5023
5387
|
};
|
|
5024
5388
|
}
|
|
5389
|
+
if (action === "follow") {
|
|
5390
|
+
if (scope === "machine") throw new Error("机器级默认板没有\"放回跟随\"一说:它就是那份默认本身,直接 show / hide 写它。");
|
|
5391
|
+
const units = panels.follow(workspace, id);
|
|
5392
|
+
return {
|
|
5393
|
+
units,
|
|
5394
|
+
text: id === void 0 ? `这个工作区改过的行全部清掉 —— 每一行都回到跟着机器级默认板走(那份板以后改了,这里跟着变)。\n${render(units, workspace, scope)}` : `${id} 放回跟随 —— 它不再由这个工作区定,改跟机器级默认板走。\n${render(units, workspace, scope)}`
|
|
5395
|
+
};
|
|
5396
|
+
}
|
|
5025
5397
|
if (action === "reset") {
|
|
5026
5398
|
const units = panels.reset(workspace, scope);
|
|
5027
5399
|
return {
|
|
5028
5400
|
units,
|
|
5029
|
-
text: scope === "machine" ?
|
|
5401
|
+
text: scope === "machine" ? `机器级默认板已清空 —— 从今往后,没在这一行改过的工作区默认什么都不开。${MACHINE_CAVEAT}\n${render(units, workspace, scope)}` : `这个工作区改过的行已全部清掉 —— 每一行都回到跟着机器级默认板走。\n${render(units, workspace, scope)}`
|
|
5030
5402
|
};
|
|
5031
5403
|
}
|
|
5032
5404
|
if (id === void 0) throw new Error(`${action} 需要一个 id(例如 coding-issues)。当前套件:${list().map((entry) => entry.id).join(", ")}`);
|
|
@@ -5059,19 +5431,20 @@ var OWNED = [{
|
|
|
5059
5431
|
unit: panelsUnit.id,
|
|
5060
5432
|
manifest: panelsUnit,
|
|
5061
5433
|
mount({ ctx, logger, manifests, ownedUnits, opencliCommand, probe, store, values }) {
|
|
5434
|
+
const ledger = createPluginLedger({
|
|
5435
|
+
store,
|
|
5436
|
+
home: isolatedHome()
|
|
5437
|
+
});
|
|
5062
5438
|
const panels = createPanelsService({
|
|
5063
5439
|
manifests,
|
|
5064
5440
|
store,
|
|
5065
5441
|
owned: ownedUnits,
|
|
5442
|
+
plugins: () => pluginPanelRefs(ledger),
|
|
5066
5443
|
logger
|
|
5067
5444
|
});
|
|
5068
5445
|
ctx.provide(PANELS_SERVICE, panels);
|
|
5069
5446
|
const profileDir = profileDirOf(ctx);
|
|
5070
5447
|
const shell = ctx.get("shell");
|
|
5071
|
-
const ledger = createPluginLedger({
|
|
5072
|
-
store,
|
|
5073
|
-
home: isolatedHome()
|
|
5074
|
-
});
|
|
5075
5448
|
const market = createMarketService({
|
|
5076
5449
|
manifests: manifests(),
|
|
5077
5450
|
manifestsNow: manifests,
|
|
@@ -5081,6 +5454,7 @@ var OWNED = [{
|
|
|
5081
5454
|
bundles: readBundleManager(ctx),
|
|
5082
5455
|
installed: (pkg) => readInstalledModule(profileDir, pkg),
|
|
5083
5456
|
modules: () => profileModules(profileDir),
|
|
5457
|
+
workbench: (pkg) => readProfilePackage(profileDir, pkg),
|
|
5084
5458
|
registries: createRegistryNotebook(profileDir),
|
|
5085
5459
|
ledger,
|
|
5086
5460
|
references: () => {
|
|
@@ -5460,6 +5834,24 @@ function bundleRegistriesHandler(ctx) {
|
|
|
5460
5834
|
};
|
|
5461
5835
|
}
|
|
5462
5836
|
var refusalLog = /* @__PURE__ */ new Map();
|
|
5837
|
+
async function workbenchVersion(input) {
|
|
5838
|
+
const read = readProfilePackage(input.dir, PLUGIN_ID);
|
|
5839
|
+
if (read === void 0) return void 0;
|
|
5840
|
+
const registry = read.registry ?? input.fallback;
|
|
5841
|
+
const queried = read.local === true || input.shell === void 0 || registry === void 0 ? void 0 : await latestVersion({
|
|
5842
|
+
shell: input.shell,
|
|
5843
|
+
name: read.pkg,
|
|
5844
|
+
registry,
|
|
5845
|
+
force: input.force
|
|
5846
|
+
});
|
|
5847
|
+
const latest = queried !== void 0 && isNewerVersion(queried, read.version) ? queried : void 0;
|
|
5848
|
+
return {
|
|
5849
|
+
pkg: read.pkg,
|
|
5850
|
+
installed: read.version,
|
|
5851
|
+
...latest === void 0 ? {} : { latest },
|
|
5852
|
+
...read.local === true ? { local: true } : {}
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5463
5855
|
function moduleVersionsHandler(ctx, shell) {
|
|
5464
5856
|
return async (search) => {
|
|
5465
5857
|
const manager = readBundleManager(ctx);
|
|
@@ -5472,36 +5864,44 @@ function moduleVersionsHandler(ctx, shell) {
|
|
|
5472
5864
|
const refusals = new Map((ctx.get("workbenchUnits")?.refusals() ?? []).filter((one) => one.unit !== "").map((one) => [one.unit, one.reasons]));
|
|
5473
5865
|
const force = param(search, "force") === "1";
|
|
5474
5866
|
const fallback = (await manager.registries())?.resolved ?? void 0;
|
|
5867
|
+
const modules = await Promise.all(found.map(async (module) => {
|
|
5868
|
+
const registry = module.registry ?? fallback;
|
|
5869
|
+
const latest = shell === void 0 || registry === void 0 ? void 0 : await latestVersion({
|
|
5870
|
+
shell,
|
|
5871
|
+
name: module.pkg,
|
|
5872
|
+
registry,
|
|
5873
|
+
force
|
|
5874
|
+
});
|
|
5875
|
+
const raw = moduleDeclarationOf(dir, module.pkg);
|
|
5876
|
+
const declared = raw === void 0 ? [] : declarationProblemsOf(module.pkg, raw);
|
|
5877
|
+
const refused = refusals.get(module.unit) ?? (declared.length === 0 ? void 0 : declared);
|
|
5878
|
+
if (refused === void 0) refusalLog.delete(module.pkg);
|
|
5879
|
+
else {
|
|
5880
|
+
const line = refused.join(";");
|
|
5881
|
+
if (refusalLog.get(module.pkg) !== line) {
|
|
5882
|
+
refusalLog.set(module.pkg, line);
|
|
5883
|
+
ctx.logger?.warn?.(`[dsh-workbench] ${module.pkg} 装上了但用不了:${line}`);
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
return {
|
|
5887
|
+
unit: module.unit,
|
|
5888
|
+
pkg: module.pkg,
|
|
5889
|
+
installed: module.version,
|
|
5890
|
+
...latest === void 0 ? {} : { latest },
|
|
5891
|
+
...module.local === true ? { local: true } : {},
|
|
5892
|
+
...refused === void 0 ? {} : { refused }
|
|
5893
|
+
};
|
|
5894
|
+
}));
|
|
5895
|
+
const workbench = await workbenchVersion({
|
|
5896
|
+
dir,
|
|
5897
|
+
shell,
|
|
5898
|
+
fallback,
|
|
5899
|
+
force
|
|
5900
|
+
});
|
|
5475
5901
|
return {
|
|
5476
5902
|
ok: true,
|
|
5477
|
-
modules
|
|
5478
|
-
|
|
5479
|
-
const latest = shell === void 0 || registry === void 0 ? void 0 : await latestVersion({
|
|
5480
|
-
shell,
|
|
5481
|
-
name: module.pkg,
|
|
5482
|
-
registry,
|
|
5483
|
-
force
|
|
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
|
-
}
|
|
5496
|
-
return {
|
|
5497
|
-
unit: module.unit,
|
|
5498
|
-
pkg: module.pkg,
|
|
5499
|
-
installed: module.version,
|
|
5500
|
-
...latest === void 0 ? {} : { latest },
|
|
5501
|
-
...module.local === true ? { local: true } : {},
|
|
5502
|
-
...refused === void 0 ? {} : { refused }
|
|
5503
|
-
};
|
|
5504
|
-
}))
|
|
5903
|
+
modules,
|
|
5904
|
+
...workbench === void 0 ? {} : { workbench }
|
|
5505
5905
|
};
|
|
5506
5906
|
};
|
|
5507
5907
|
}
|
|
@@ -5528,15 +5928,26 @@ async function declarationRefusal(spec, inspection, ctx, manager) {
|
|
|
5528
5928
|
});
|
|
5529
5929
|
return refusedInspection(declarationRefusalOf(inspection.name, raw));
|
|
5530
5930
|
}
|
|
5531
|
-
async function
|
|
5532
|
-
if (inspection.status !== "accepted") return void 0;
|
|
5931
|
+
async function packageOverride(spec, inspection, ctx, manager, asked) {
|
|
5533
5932
|
const shell = ctx.get("shell");
|
|
5534
|
-
const registry = typeof inspection.registry === "string" ? inspection.registry : (await manager.registries())?.resolved ?? void 0;
|
|
5535
|
-
|
|
5933
|
+
const registry = (inspection.status === "accepted" && typeof inspection.registry === "string" ? inspection.registry : void 0) ?? (typeof asked === "string" ? asked : (await manager.registries())?.resolved ?? void 0);
|
|
5934
|
+
const route = await packageRouteOf({
|
|
5536
5935
|
spec,
|
|
5537
5936
|
...shell === void 0 ? {} : { shell },
|
|
5538
5937
|
...registry === void 0 ? {} : { registry }
|
|
5539
|
-
})
|
|
5938
|
+
});
|
|
5939
|
+
if (route?.kind === "opencli-plugin") {
|
|
5940
|
+
if (inspection.status !== "refused") return void 0;
|
|
5941
|
+
const ref = splitNpmSpec(spec);
|
|
5942
|
+
return {
|
|
5943
|
+
status: "plugin",
|
|
5944
|
+
pkg: route.pkg ?? ref?.name ?? spec.trim(),
|
|
5945
|
+
registry: registry ?? null,
|
|
5946
|
+
...route.pluginName === void 0 ? {} : { pluginName: route.pluginName },
|
|
5947
|
+
...ref?.version === void 0 ? {} : { version: ref.version }
|
|
5948
|
+
};
|
|
5949
|
+
}
|
|
5950
|
+
return route?.kind === "neither" && inspection.status === "accepted" ? {
|
|
5540
5951
|
status: "refused",
|
|
5541
5952
|
problem: "not-a-module-or-plugin",
|
|
5542
5953
|
reason: refusalReason()
|
|
@@ -5560,7 +5971,8 @@ function bundleInspectHandler(ctx) {
|
|
|
5560
5971
|
message: "这个 profile 里没有插件管理器,装不了模块。"
|
|
5561
5972
|
}
|
|
5562
5973
|
};
|
|
5563
|
-
const
|
|
5974
|
+
const asked = registryOf(search);
|
|
5975
|
+
const inspection = await manager.inspect(spec, asked);
|
|
5564
5976
|
if (inspection === null) return {
|
|
5565
5977
|
ok: false,
|
|
5566
5978
|
error: {
|
|
@@ -5570,7 +5982,7 @@ function bundleInspectHandler(ctx) {
|
|
|
5570
5982
|
};
|
|
5571
5983
|
return {
|
|
5572
5984
|
ok: true,
|
|
5573
|
-
inspection: await
|
|
5985
|
+
inspection: await packageOverride(spec, inspection, ctx, manager, asked) ?? await declarationRefusal(spec, inspection, ctx, manager) ?? inspection
|
|
5574
5986
|
};
|
|
5575
5987
|
};
|
|
5576
5988
|
}
|
|
@@ -6496,7 +6908,9 @@ function mountUnits(units, deps) {
|
|
|
6496
6908
|
//#endregion
|
|
6497
6909
|
//#region src/shared/api/host/unit-registry.ts
|
|
6498
6910
|
function sourceLabel(source) {
|
|
6499
|
-
|
|
6911
|
+
if (source.kind === "owned") return "外壳自己";
|
|
6912
|
+
if (source.kind === "plugin") return "按已装 opencli 插件合成";
|
|
6913
|
+
return source.pkg;
|
|
6500
6914
|
}
|
|
6501
6915
|
function createUnitRegistry(options) {
|
|
6502
6916
|
const refusals = [];
|
|
@@ -6556,6 +6970,14 @@ function createUnitRegistry(options) {
|
|
|
6556
6970
|
source: { kind: "owned" }
|
|
6557
6971
|
});
|
|
6558
6972
|
},
|
|
6973
|
+
registerPlugin(face) {
|
|
6974
|
+
return add({
|
|
6975
|
+
unit: face.manifest.id,
|
|
6976
|
+
manifest: face.manifest,
|
|
6977
|
+
face,
|
|
6978
|
+
source: { kind: "plugin" }
|
|
6979
|
+
});
|
|
6980
|
+
},
|
|
6559
6981
|
all: () => hosts,
|
|
6560
6982
|
owned: () => hosts.filter((host) => host.source.kind === "owned").map((host) => host.unit),
|
|
6561
6983
|
refusals: () => refusals,
|
|
@@ -6581,6 +7003,563 @@ function bindRosterFacts(options) {
|
|
|
6581
7003
|
});
|
|
6582
7004
|
}
|
|
6583
7005
|
//#endregion
|
|
7006
|
+
//#region src/shared/api/host/plugin-catalog.ts
|
|
7007
|
+
function recordOf(value) {
|
|
7008
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
7009
|
+
}
|
|
7010
|
+
function textOf(value) {
|
|
7011
|
+
return typeof value === "string" ? value : "";
|
|
7012
|
+
}
|
|
7013
|
+
function stringsOf(value) {
|
|
7014
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
7015
|
+
}
|
|
7016
|
+
function argsOf(raw) {
|
|
7017
|
+
if (!Array.isArray(raw)) return [];
|
|
7018
|
+
return raw.flatMap((item) => {
|
|
7019
|
+
const record = recordOf(item);
|
|
7020
|
+
if (record === void 0) return [];
|
|
7021
|
+
const name = textOf(record["name"]).trim();
|
|
7022
|
+
if (name === "") return [];
|
|
7023
|
+
const fallback = record["default"];
|
|
7024
|
+
return [{
|
|
7025
|
+
name,
|
|
7026
|
+
type: textOf(record["type"]) || "string",
|
|
7027
|
+
required: record["required"] === true,
|
|
7028
|
+
positional: record["positional"] === true,
|
|
7029
|
+
valueRequired: record["valueRequired"] === true,
|
|
7030
|
+
choices: stringsOf(record["choices"]),
|
|
7031
|
+
...fallback === void 0 || fallback === null ? {} : { defaultValue: fallback },
|
|
7032
|
+
help: textOf(record["help"])
|
|
7033
|
+
}];
|
|
7034
|
+
});
|
|
7035
|
+
}
|
|
7036
|
+
function parsePluginRows(stdout) {
|
|
7037
|
+
const parsed = parseJsonLoose(stdout);
|
|
7038
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
7039
|
+
const rows = [];
|
|
7040
|
+
for (const item of parsed) {
|
|
7041
|
+
const record = recordOf(item);
|
|
7042
|
+
if (record === void 0) return void 0;
|
|
7043
|
+
const name = textOf(record["name"]).trim();
|
|
7044
|
+
if (name === "") return void 0;
|
|
7045
|
+
rows.push({
|
|
7046
|
+
name,
|
|
7047
|
+
version: textOf(record["version"]),
|
|
7048
|
+
commands: stringsOf(record["commands"])
|
|
7049
|
+
});
|
|
7050
|
+
}
|
|
7051
|
+
return rows;
|
|
7052
|
+
}
|
|
7053
|
+
function parseCommandRows(stdout) {
|
|
7054
|
+
const parsed = parseJsonLoose(stdout);
|
|
7055
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
7056
|
+
const rows = [];
|
|
7057
|
+
for (const item of parsed) {
|
|
7058
|
+
const record = recordOf(item);
|
|
7059
|
+
if (record === void 0) return void 0;
|
|
7060
|
+
const site = textOf(record["site"]).trim();
|
|
7061
|
+
const name = textOf(record["name"]).trim();
|
|
7062
|
+
if (site === "" || name === "") return void 0;
|
|
7063
|
+
rows.push({
|
|
7064
|
+
site,
|
|
7065
|
+
name,
|
|
7066
|
+
description: textOf(record["description"]),
|
|
7067
|
+
access: textOf(record["access"]),
|
|
7068
|
+
...textOf(record["domain"]) === "" ? {} : { domain: textOf(record["domain"]) },
|
|
7069
|
+
args: argsOf(record["args"])
|
|
7070
|
+
});
|
|
7071
|
+
}
|
|
7072
|
+
return rows;
|
|
7073
|
+
}
|
|
7074
|
+
function commandOf(plugin, row) {
|
|
7075
|
+
return {
|
|
7076
|
+
plugin,
|
|
7077
|
+
name: row.name,
|
|
7078
|
+
description: row.description,
|
|
7079
|
+
access: row.access,
|
|
7080
|
+
args: row.args
|
|
7081
|
+
};
|
|
7082
|
+
}
|
|
7083
|
+
function attributeCommands(plugins, rows) {
|
|
7084
|
+
const bySite = /* @__PURE__ */ new Map();
|
|
7085
|
+
for (const row of rows) {
|
|
7086
|
+
const site = bySite.get(row.site) ?? /* @__PURE__ */ new Map();
|
|
7087
|
+
site.set(row.name, row);
|
|
7088
|
+
bySite.set(row.site, site);
|
|
7089
|
+
}
|
|
7090
|
+
const sets = [];
|
|
7091
|
+
const problems = [];
|
|
7092
|
+
if (plugins.length > 0 && rows.length === 0) problems.push(`opencli 报了 ${String(plugins.length)} 个插件,但命令面(opencli list -f json)里一条命令都没有 —— 两份清单对不上`);
|
|
7093
|
+
for (const plugin of plugins) {
|
|
7094
|
+
const found = bySite.get(plugin.name) ?? /* @__PURE__ */ new Map();
|
|
7095
|
+
if (found.size === 0) {
|
|
7096
|
+
problems.push(`「${plugin.name}」在 opencli 插件清单里,但命令面里没有一条 site=${plugin.name} 的命令 —— 归属不了,它这次不合成工具`);
|
|
7097
|
+
continue;
|
|
7098
|
+
}
|
|
7099
|
+
const declared = plugin.commands.length > 0 ? plugin.commands : [...found.keys()];
|
|
7100
|
+
const missing = declared.filter((name) => !found.has(name));
|
|
7101
|
+
if (missing.length > 0) problems.push(`「${plugin.name}」自己的清单里有 ${String(missing.length)} 条命令在命令面里找不到:${missing.join("、")}(插件版本旧或命令面没更新)`);
|
|
7102
|
+
const commands = declared.flatMap((name) => {
|
|
7103
|
+
const row = found.get(name);
|
|
7104
|
+
return row === void 0 ? [] : [commandOf(plugin.name, row)];
|
|
7105
|
+
});
|
|
7106
|
+
if (commands.length === 0) {
|
|
7107
|
+
problems.push(`「${plugin.name}」没有任何一条能归属的命令 —— 它这次不合成工具`);
|
|
7108
|
+
continue;
|
|
7109
|
+
}
|
|
7110
|
+
sets.push({
|
|
7111
|
+
plugin: plugin.name,
|
|
7112
|
+
version: plugin.version,
|
|
7113
|
+
commands
|
|
7114
|
+
});
|
|
7115
|
+
}
|
|
7116
|
+
return {
|
|
7117
|
+
sets,
|
|
7118
|
+
problems
|
|
7119
|
+
};
|
|
7120
|
+
}
|
|
7121
|
+
function fingerprintOf(plugins) {
|
|
7122
|
+
return plugins.map((plugin) => `${plugin.name}@${plugin.version}:${[...plugin.commands].sort().join(",")}`).sort().join("|");
|
|
7123
|
+
}
|
|
7124
|
+
function messageOf(error) {
|
|
7125
|
+
return error instanceof Error ? error.message : String(error);
|
|
7126
|
+
}
|
|
7127
|
+
function createPluginCatalog(options) {
|
|
7128
|
+
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
7129
|
+
let current = {
|
|
7130
|
+
ok: false,
|
|
7131
|
+
at: 0,
|
|
7132
|
+
sets: [],
|
|
7133
|
+
problems: ["还没探过 opencli 的插件命令面"]
|
|
7134
|
+
};
|
|
7135
|
+
let heavy;
|
|
7136
|
+
async function read(line) {
|
|
7137
|
+
const shell = options.shell;
|
|
7138
|
+
if (shell === void 0) return {
|
|
7139
|
+
ok: false,
|
|
7140
|
+
problem: "没有执行缝(shell 服务不可用),探不了这个命令面"
|
|
7141
|
+
};
|
|
7142
|
+
try {
|
|
7143
|
+
const result = await runShell(shell, line, { timeoutMs });
|
|
7144
|
+
if (result.timedOut) return {
|
|
7145
|
+
ok: false,
|
|
7146
|
+
problem: `${line} 超过 ${String(Math.round(timeoutMs / 1e3))} 秒没返回`
|
|
7147
|
+
};
|
|
7148
|
+
if (result.exitCode !== 0) {
|
|
7149
|
+
const tail = result.stderr.trim() || result.stdout.trim();
|
|
7150
|
+
return {
|
|
7151
|
+
ok: false,
|
|
7152
|
+
problem: `${line} 退出码 ${String(result.exitCode)}${tail === "" ? "" : `:${tail.split("\n").slice(-3).join(" ")}`}`
|
|
7153
|
+
};
|
|
7154
|
+
}
|
|
7155
|
+
return {
|
|
7156
|
+
ok: true,
|
|
7157
|
+
stdout: result.stdout
|
|
7158
|
+
};
|
|
7159
|
+
} catch (error) {
|
|
7160
|
+
return {
|
|
7161
|
+
ok: false,
|
|
7162
|
+
problem: `${line} 没跑起来:${messageOf(error)}`
|
|
7163
|
+
};
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
function fail(problem) {
|
|
7167
|
+
current = {
|
|
7168
|
+
ok: false,
|
|
7169
|
+
at: Date.now(),
|
|
7170
|
+
sets: current.sets,
|
|
7171
|
+
problems: [problem]
|
|
7172
|
+
};
|
|
7173
|
+
return current;
|
|
7174
|
+
}
|
|
7175
|
+
async function refresh(callOptions = {}) {
|
|
7176
|
+
const pluginsRun = await read(`${options.command} plugin list -f json`);
|
|
7177
|
+
if (!pluginsRun.ok) return fail(pluginsRun.problem);
|
|
7178
|
+
const plugins = parsePluginRows(pluginsRun.stdout);
|
|
7179
|
+
if (plugins === void 0) return fail("opencli plugin list -f json 的输出读不懂(不是带 name 的 JSON 数组)");
|
|
7180
|
+
const fingerprint = fingerprintOf(plugins);
|
|
7181
|
+
if (!callOptions.force && heavy !== void 0 && heavy.fingerprint === fingerprint) {
|
|
7182
|
+
current = {
|
|
7183
|
+
ok: true,
|
|
7184
|
+
at: Date.now(),
|
|
7185
|
+
sets: heavy.sets,
|
|
7186
|
+
problems: heavy.problems
|
|
7187
|
+
};
|
|
7188
|
+
return current;
|
|
7189
|
+
}
|
|
7190
|
+
const commandsRun = await read(`${options.command} list -f json`);
|
|
7191
|
+
if (!commandsRun.ok) return fail(commandsRun.problem);
|
|
7192
|
+
const rows = parseCommandRows(commandsRun.stdout);
|
|
7193
|
+
if (rows === void 0) return fail("opencli list -f json 的输出读不懂(不是带 site / name 的 JSON 数组)");
|
|
7194
|
+
const attributed = attributeCommands(plugins, rows);
|
|
7195
|
+
heavy = {
|
|
7196
|
+
fingerprint,
|
|
7197
|
+
sets: attributed.sets,
|
|
7198
|
+
problems: attributed.problems
|
|
7199
|
+
};
|
|
7200
|
+
current = {
|
|
7201
|
+
ok: true,
|
|
7202
|
+
at: Date.now(),
|
|
7203
|
+
sets: attributed.sets,
|
|
7204
|
+
problems: attributed.problems
|
|
7205
|
+
};
|
|
7206
|
+
return current;
|
|
7207
|
+
}
|
|
7208
|
+
return {
|
|
7209
|
+
refresh,
|
|
7210
|
+
snapshot: () => current
|
|
7211
|
+
};
|
|
7212
|
+
}
|
|
7213
|
+
//#endregion
|
|
7214
|
+
//#region src/shared/api/host/plugin-login-hint.ts
|
|
7215
|
+
var NOT_LOGGED_IN = /未登录|请先登录|not logged in/i;
|
|
7216
|
+
function propertyOf$1(error, key) {
|
|
7217
|
+
return typeof error === "object" && error !== null ? error[key] : void 0;
|
|
7218
|
+
}
|
|
7219
|
+
function helpUrlOfError(error) {
|
|
7220
|
+
const raw = propertyOf$1(error, "helpUrl");
|
|
7221
|
+
if (typeof raw !== "string") return void 0;
|
|
7222
|
+
const trimmed = raw.trim();
|
|
7223
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
7224
|
+
}
|
|
7225
|
+
function isAuthFailure(error, message) {
|
|
7226
|
+
if (NOT_LOGGED_IN.test(message)) return true;
|
|
7227
|
+
return propertyOf$1(error, "code") === "auth-required";
|
|
7228
|
+
}
|
|
7229
|
+
function loginUrlOfDomain(domain) {
|
|
7230
|
+
const bare = (domain ?? "").trim().replace(/\/+$/, "");
|
|
7231
|
+
if (bare === "" || bare.includes("*")) return void 0;
|
|
7232
|
+
return /^https?:\/\//.test(bare) ? bare : `https://${bare}/login`;
|
|
7233
|
+
}
|
|
7234
|
+
function pluginLoginHint(error, domain) {
|
|
7235
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7236
|
+
if (!isAuthFailure(error, message)) return message;
|
|
7237
|
+
const url = helpUrlOfError(error) ?? loginUrlOfDomain(domain);
|
|
7238
|
+
if (url !== void 0) return `${message}\n在 opencli 那扇浏览器窗里打开 ${url} 登一次,然后重试。`;
|
|
7239
|
+
return `${message}\n在 opencli 那扇浏览器窗里登录这个站点(插件没给出具体地址,工作台拼不出来)—— 或跑一次工作台的体检/预热,然后重试。`;
|
|
7240
|
+
}
|
|
7241
|
+
//#endregion
|
|
7242
|
+
//#region src/shared/api/host/plugin-tools.ts
|
|
7243
|
+
var SCHEMA_TYPE = {
|
|
7244
|
+
int: "integer",
|
|
7245
|
+
integer: "integer",
|
|
7246
|
+
number: "number",
|
|
7247
|
+
float: "number",
|
|
7248
|
+
bool: "boolean",
|
|
7249
|
+
boolean: "boolean",
|
|
7250
|
+
string: "string"
|
|
7251
|
+
};
|
|
7252
|
+
var KEY_ARG_LIMIT = 3;
|
|
7253
|
+
function pluginToolName(plugin, command) {
|
|
7254
|
+
return `${plugin}_${command}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/_{2,}/g, "_").replace(/^_+|_+$/g, "");
|
|
7255
|
+
}
|
|
7256
|
+
function schemaTypeOf(arg) {
|
|
7257
|
+
return SCHEMA_TYPE[arg.type.toLowerCase()] ?? "string";
|
|
7258
|
+
}
|
|
7259
|
+
function propertyOf(arg) {
|
|
7260
|
+
const type = schemaTypeOf(arg);
|
|
7261
|
+
const enumValues = arg.choices.length > 0 ? arg.choices.map((choice) => choice) : void 0;
|
|
7262
|
+
return {
|
|
7263
|
+
type,
|
|
7264
|
+
...arg.help.trim() === "" ? {} : { description: arg.help.trim() },
|
|
7265
|
+
...enumValues === void 0 ? {} : { enum: enumValues },
|
|
7266
|
+
...arg.defaultValue === void 0 ? {} : { default: arg.defaultValue }
|
|
7267
|
+
};
|
|
7268
|
+
}
|
|
7269
|
+
function pluginToolParameters(command) {
|
|
7270
|
+
const properties = {};
|
|
7271
|
+
for (const arg of command.args) properties[arg.name] = propertyOf(arg);
|
|
7272
|
+
const required = command.args.filter((arg) => arg.required).map((arg) => arg.name);
|
|
7273
|
+
return {
|
|
7274
|
+
type: "object",
|
|
7275
|
+
properties,
|
|
7276
|
+
...required.length === 0 ? {} : { required },
|
|
7277
|
+
additionalProperties: false
|
|
7278
|
+
};
|
|
7279
|
+
}
|
|
7280
|
+
function pluginToolDescription(command) {
|
|
7281
|
+
const described = command.description.trim();
|
|
7282
|
+
return described === "" ? `${command.plugin} ${command.name}(opencli 命令)` : described;
|
|
7283
|
+
}
|
|
7284
|
+
function pluginToolWrites(command) {
|
|
7285
|
+
return command.access !== "read";
|
|
7286
|
+
}
|
|
7287
|
+
function valuesOf(raw) {
|
|
7288
|
+
return typeof raw === "object" && raw !== null ? raw : {};
|
|
7289
|
+
}
|
|
7290
|
+
function pluginToolArgv(command, raw) {
|
|
7291
|
+
const values = valuesOf(raw);
|
|
7292
|
+
const argv = [];
|
|
7293
|
+
for (const arg of command.args) {
|
|
7294
|
+
if (!arg.positional) continue;
|
|
7295
|
+
const value = values[arg.name];
|
|
7296
|
+
if (value === void 0 || value === null) continue;
|
|
7297
|
+
argv.push(String(value));
|
|
7298
|
+
}
|
|
7299
|
+
for (const arg of command.args) {
|
|
7300
|
+
if (arg.positional) continue;
|
|
7301
|
+
const value = values[arg.name];
|
|
7302
|
+
if (value === void 0 || value === null) continue;
|
|
7303
|
+
if (typeof value === "boolean") argv.push(`--${arg.name}`, value ? "true" : "false");
|
|
7304
|
+
else if (Array.isArray(value)) argv.push(`--${arg.name}`, value.map((item) => String(item)).join(","));
|
|
7305
|
+
else argv.push(`--${arg.name}`, String(value));
|
|
7306
|
+
}
|
|
7307
|
+
return argv;
|
|
7308
|
+
}
|
|
7309
|
+
function pluginKeyArgs(command, raw) {
|
|
7310
|
+
const values = valuesOf(raw);
|
|
7311
|
+
const ordered = [...command.args.filter((arg) => arg.required), ...command.args.filter((arg) => !arg.required)];
|
|
7312
|
+
const parts = [];
|
|
7313
|
+
for (const arg of ordered) {
|
|
7314
|
+
const value = values[arg.name];
|
|
7315
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
7316
|
+
parts.push(`${arg.name}=${String(value)}`);
|
|
7317
|
+
if (parts.length === KEY_ARG_LIMIT) break;
|
|
7318
|
+
}
|
|
7319
|
+
return parts.length === 0 ? "没有带参数" : parts.join("、");
|
|
7320
|
+
}
|
|
7321
|
+
function pluginApprovalQuestion(command, raw) {
|
|
7322
|
+
const key = pluginKeyArgs(command, raw);
|
|
7323
|
+
return {
|
|
7324
|
+
zh: `允许执行 ${command.plugin} 的 ${command.name}(${key})吗?`,
|
|
7325
|
+
en: `Run ${command.plugin} ${command.name} (${key})?`
|
|
7326
|
+
};
|
|
7327
|
+
}
|
|
7328
|
+
function pluginApprovalFor(command) {
|
|
7329
|
+
const denied = command.access === "read";
|
|
7330
|
+
return (call) => {
|
|
7331
|
+
if (denied) return { kind: "read" };
|
|
7332
|
+
const question = pluginApprovalQuestion(command, call.arguments);
|
|
7333
|
+
return {
|
|
7334
|
+
kind: "ask",
|
|
7335
|
+
reason: command.access === "write" ? `${command.plugin} ${command.name} 在 opencli 里标着 write` : `${command.plugin} ${command.name} 没有拿到 access(fail-safe:按写处理)`,
|
|
7336
|
+
question,
|
|
7337
|
+
fallback: question
|
|
7338
|
+
};
|
|
7339
|
+
};
|
|
7340
|
+
}
|
|
7341
|
+
//#endregion
|
|
7342
|
+
//#region src/shared/api/host/plugin-units.ts
|
|
7343
|
+
var TOOL_OUTPUT_SCHEMA = {
|
|
7344
|
+
type: "object",
|
|
7345
|
+
properties: {},
|
|
7346
|
+
additionalProperties: true
|
|
7347
|
+
};
|
|
7348
|
+
function signatureOf(version, commands) {
|
|
7349
|
+
return [version, ...commands.map((command) => `${command.name}:${command.access}:${command.args.map((arg) => `${arg.name}${arg.required ? "!" : ""}`).join("+")}`)].join("|");
|
|
7350
|
+
}
|
|
7351
|
+
function toolsOf(plugin, commands, taken) {
|
|
7352
|
+
const tools = [];
|
|
7353
|
+
const problems = [];
|
|
7354
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
7355
|
+
for (const command of commands) {
|
|
7356
|
+
const name = pluginToolName(plugin, command.name);
|
|
7357
|
+
if (taken.has(name) || claimed.has(name)) {
|
|
7358
|
+
problems.push(`工具名 ${name}(${plugin} ${command.name})已经被占着 —— 这条命令这次不合成(先来的那份说了算)`);
|
|
7359
|
+
continue;
|
|
7360
|
+
}
|
|
7361
|
+
claimed.add(name);
|
|
7362
|
+
tools.push({
|
|
7363
|
+
name,
|
|
7364
|
+
command
|
|
7365
|
+
});
|
|
7366
|
+
}
|
|
7367
|
+
return {
|
|
7368
|
+
tools,
|
|
7369
|
+
problems
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
function planOf(options, registered) {
|
|
7373
|
+
const sets = new Map(options.catalog.snapshot().sets.map((set) => [set.plugin, set]));
|
|
7374
|
+
const mine = new Set([...registered.values()].flatMap((item) => item.tools));
|
|
7375
|
+
const taken = new Set([...options.toolNames?.() ?? []].filter((name) => !mine.has(name)));
|
|
7376
|
+
const units = [];
|
|
7377
|
+
const rows = [];
|
|
7378
|
+
const problems = [];
|
|
7379
|
+
for (const row of options.rows()) {
|
|
7380
|
+
const plugin = row.name?.trim() ?? "";
|
|
7381
|
+
if (plugin === "") {
|
|
7382
|
+
problems.push(`面板上的插件行 ${row.id}(${row.pkg})没有 opencli 插件名 —— 认不出它,这次不给它合成工具`);
|
|
7383
|
+
continue;
|
|
7384
|
+
}
|
|
7385
|
+
const set = sets.get(plugin);
|
|
7386
|
+
if (set === void 0) {
|
|
7387
|
+
problems.push(`已装插件「${plugin}」在 opencli 的插件清单 / 命令面里找不到 —— 归属不了,这次不给它合成工具`);
|
|
7388
|
+
continue;
|
|
7389
|
+
}
|
|
7390
|
+
sets.delete(plugin);
|
|
7391
|
+
const declared = options.declared(plugin);
|
|
7392
|
+
const overlapped = set.commands.filter((command) => declared.has(command.name));
|
|
7393
|
+
if (overlapped.length > 0) problems.push(`「${plugin}」有 ${overlapped.length} 条命令由已装模块自己提供(${overlapped.map((command) => command.name).join("、")})—— 通用桥不重复合成`);
|
|
7394
|
+
const commands = set.commands.filter((command) => !declared.has(command.name));
|
|
7395
|
+
const built = toolsOf(plugin, commands, taken);
|
|
7396
|
+
problems.push(...built.problems);
|
|
7397
|
+
for (const tool of built.tools) taken.add(tool.name);
|
|
7398
|
+
if (built.tools.length === 0 && commands.length === set.commands.length) problems.push(`「${plugin}」的命令一条都没能合成工具`);
|
|
7399
|
+
rows.push({
|
|
7400
|
+
plugin,
|
|
7401
|
+
pkg: row.pkg,
|
|
7402
|
+
version: set.version,
|
|
7403
|
+
commands: set.commands.length,
|
|
7404
|
+
synthesized: built.tools.length,
|
|
7405
|
+
tools: built.tools.map((tool) => tool.name)
|
|
7406
|
+
});
|
|
7407
|
+
if (built.tools.length === 0) continue;
|
|
7408
|
+
units.push({
|
|
7409
|
+
id: row.id,
|
|
7410
|
+
plugin,
|
|
7411
|
+
pkg: row.pkg,
|
|
7412
|
+
version: set.version,
|
|
7413
|
+
signature: signatureOf(set.version, commands),
|
|
7414
|
+
commands,
|
|
7415
|
+
tools: built.tools
|
|
7416
|
+
});
|
|
7417
|
+
}
|
|
7418
|
+
for (const set of sets.values()) problems.push(`opencli 里装着「${set.plugin}」(${String(set.commands.length)} 条命令),但它不在插件台账里(不是从市场装的)—— 面板上没有它的开关,它的命令这次不进工具面`);
|
|
7419
|
+
return {
|
|
7420
|
+
units,
|
|
7421
|
+
rows,
|
|
7422
|
+
problems
|
|
7423
|
+
};
|
|
7424
|
+
}
|
|
7425
|
+
function faceOf(unit, opencliFor) {
|
|
7426
|
+
const byTool = new Map(unit.tools.map((tool) => [tool.name, tool.command]));
|
|
7427
|
+
const manifest = {
|
|
7428
|
+
id: unit.id,
|
|
7429
|
+
card: null,
|
|
7430
|
+
tools: unit.tools.map((tool) => ({
|
|
7431
|
+
name: tool.name,
|
|
7432
|
+
writes: pluginToolWrites(tool.command)
|
|
7433
|
+
})),
|
|
7434
|
+
host: { inject: [] }
|
|
7435
|
+
};
|
|
7436
|
+
return {
|
|
7437
|
+
unit: unit.id,
|
|
7438
|
+
manifest,
|
|
7439
|
+
mount(deps) {
|
|
7440
|
+
const tools = deps.ctx.get("tools");
|
|
7441
|
+
if (tools === void 0) {
|
|
7442
|
+
deps.logger?.info?.(`[dsh-workbench] 没有 tools 服务,跳过 ${unit.plugin} 的命令工具注册`);
|
|
7443
|
+
return {
|
|
7444
|
+
routes: {},
|
|
7445
|
+
registerTools: () => []
|
|
7446
|
+
};
|
|
7447
|
+
}
|
|
7448
|
+
const opencli = opencliFor(unit.plugin);
|
|
7449
|
+
const definitions = unit.tools.map((tool) => ({
|
|
7450
|
+
name: tool.name,
|
|
7451
|
+
description: pluginToolDescription(tool.command),
|
|
7452
|
+
parameters: pluginToolParameters(tool.command),
|
|
7453
|
+
output: {
|
|
7454
|
+
schema: TOOL_OUTPUT_SCHEMA,
|
|
7455
|
+
render: (_args, value) => textOutput(jsonText(value))
|
|
7456
|
+
},
|
|
7457
|
+
async execute(raw, exec) {
|
|
7458
|
+
const workspace = sessionWorkspace(exec);
|
|
7459
|
+
try {
|
|
7460
|
+
return (await opencli.json([tool.command.name, ...pluginToolArgv(tool.command, raw)], workspace === null ? {} : { workdir: workspace })).value;
|
|
7461
|
+
} catch (error) {
|
|
7462
|
+
throw new Error(pluginLoginHint(error, tool.command.domain), { cause: error });
|
|
7463
|
+
}
|
|
7464
|
+
}
|
|
7465
|
+
}));
|
|
7466
|
+
return {
|
|
7467
|
+
routes: {},
|
|
7468
|
+
factsOf: () => [],
|
|
7469
|
+
registerTools: () => definitions.map((definition) => tools.register(definition)),
|
|
7470
|
+
needsApproval: (call) => {
|
|
7471
|
+
const command = call.name === void 0 ? void 0 : byTool.get(call.name);
|
|
7472
|
+
return command === void 0 ? void 0 : pluginApprovalFor(command)(call);
|
|
7473
|
+
}
|
|
7474
|
+
};
|
|
7475
|
+
}
|
|
7476
|
+
};
|
|
7477
|
+
}
|
|
7478
|
+
function reconcile(registered, units, options) {
|
|
7479
|
+
const wanted = new Map(units.map((unit) => [unit.id, unit]));
|
|
7480
|
+
for (const [id, item] of [...registered]) {
|
|
7481
|
+
const next = wanted.get(id);
|
|
7482
|
+
if (next !== void 0 && next.signature === item.signature) continue;
|
|
7483
|
+
item.drop();
|
|
7484
|
+
registered.delete(id);
|
|
7485
|
+
}
|
|
7486
|
+
for (const unit of units) {
|
|
7487
|
+
if (registered.has(unit.id)) continue;
|
|
7488
|
+
registered.set(unit.id, {
|
|
7489
|
+
signature: unit.signature,
|
|
7490
|
+
tools: unit.tools.map((tool) => tool.name),
|
|
7491
|
+
drop: options.registry.registerPlugin(faceOf(unit, options.opencliFor))
|
|
7492
|
+
});
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
function createPluginUnits(options) {
|
|
7496
|
+
const registered = /* @__PURE__ */ new Map();
|
|
7497
|
+
let report = {
|
|
7498
|
+
ok: false,
|
|
7499
|
+
at: 0,
|
|
7500
|
+
plugins: [],
|
|
7501
|
+
problems: ["还没探过 opencli 的插件命令面"]
|
|
7502
|
+
};
|
|
7503
|
+
let inflight;
|
|
7504
|
+
let lastTouch = 0;
|
|
7505
|
+
let warned = "";
|
|
7506
|
+
function warn(problems) {
|
|
7507
|
+
const text = [...problems].sort().join("\n");
|
|
7508
|
+
if (text === "" || text === warned) return;
|
|
7509
|
+
warned = text;
|
|
7510
|
+
options.logger?.warn?.(`[dsh-workbench] opencli 插件命令面:\n${text}`);
|
|
7511
|
+
}
|
|
7512
|
+
async function run(force) {
|
|
7513
|
+
const probed = await options.catalog.refresh(force ? { force: true } : {});
|
|
7514
|
+
if (!probed.ok) {
|
|
7515
|
+
const problems = [...probed.problems, "这次探测没成功:上面这些插件行的工具面维持在上一份(不是\"这些插件没有命令\")"];
|
|
7516
|
+
report = {
|
|
7517
|
+
ok: false,
|
|
7518
|
+
at: Date.now(),
|
|
7519
|
+
plugins: report.plugins,
|
|
7520
|
+
problems
|
|
7521
|
+
};
|
|
7522
|
+
warn(problems);
|
|
7523
|
+
return report;
|
|
7524
|
+
}
|
|
7525
|
+
const plan = planOf(options, registered);
|
|
7526
|
+
reconcile(registered, plan.units, options);
|
|
7527
|
+
report = {
|
|
7528
|
+
ok: true,
|
|
7529
|
+
at: Date.now(),
|
|
7530
|
+
plugins: plan.rows,
|
|
7531
|
+
problems: plan.problems
|
|
7532
|
+
};
|
|
7533
|
+
warn(plan.problems);
|
|
7534
|
+
return report;
|
|
7535
|
+
}
|
|
7536
|
+
function refresh(callOptions = {}) {
|
|
7537
|
+
if (inflight !== void 0) return inflight;
|
|
7538
|
+
const task = run(callOptions.force === true).finally(() => {
|
|
7539
|
+
inflight = void 0;
|
|
7540
|
+
});
|
|
7541
|
+
inflight = task;
|
|
7542
|
+
return task;
|
|
7543
|
+
}
|
|
7544
|
+
return {
|
|
7545
|
+
refresh,
|
|
7546
|
+
touch() {
|
|
7547
|
+
const now = Date.now();
|
|
7548
|
+
if (inflight !== void 0) return;
|
|
7549
|
+
if (now - lastTouch < 3e4) return;
|
|
7550
|
+
lastTouch = now;
|
|
7551
|
+
refresh().catch((error) => {
|
|
7552
|
+
options.logger?.warn?.(`[dsh-workbench] 探 opencli 插件命令面时抛了错:${String(error)}`);
|
|
7553
|
+
});
|
|
7554
|
+
},
|
|
7555
|
+
report: () => report,
|
|
7556
|
+
dispose() {
|
|
7557
|
+
for (const item of registered.values()) item.drop();
|
|
7558
|
+
registered.clear();
|
|
7559
|
+
}
|
|
7560
|
+
};
|
|
7561
|
+
}
|
|
7562
|
+
//#endregion
|
|
6584
7563
|
//#region src/shared/api/host/tool-scope.ts
|
|
6585
7564
|
function agentOf(value) {
|
|
6586
7565
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
@@ -6767,6 +7746,23 @@ function apply(ctx) {
|
|
|
6767
7746
|
deniedTools: (workspace) => deniedNow(workspace),
|
|
6768
7747
|
logger: ctx.logger
|
|
6769
7748
|
});
|
|
7749
|
+
const pluginTools = shell === void 0 ? void 0 : createPluginUnits({
|
|
7750
|
+
registry: units,
|
|
7751
|
+
catalog: createPluginCatalog({
|
|
7752
|
+
shell,
|
|
7753
|
+
command: opencli.command
|
|
7754
|
+
}),
|
|
7755
|
+
rows: () => ctx.get("workbenchPanels")?.listPlugins(null) ?? [],
|
|
7756
|
+
declared: (plugin) => declaredPluginCommands(profileDir).get(plugin) ?? /* @__PURE__ */ new Set(),
|
|
7757
|
+
opencliFor: (plugin) => createOpencli({
|
|
7758
|
+
shell,
|
|
7759
|
+
plugin,
|
|
7760
|
+
command: opencli.command,
|
|
7761
|
+
logger: ctx.logger
|
|
7762
|
+
}),
|
|
7763
|
+
toolNames: () => new Set(unitsOfTool().keys()),
|
|
7764
|
+
logger: ctx.logger
|
|
7765
|
+
});
|
|
6770
7766
|
const mounted = /* @__PURE__ */ new Map();
|
|
6771
7767
|
const mountFailures = /* @__PURE__ */ new Map();
|
|
6772
7768
|
const factsOfMap = /* @__PURE__ */ new Map();
|
|
@@ -6920,6 +7916,23 @@ function apply(ctx) {
|
|
|
6920
7916
|
});
|
|
6921
7917
|
}
|
|
6922
7918
|
sync();
|
|
7919
|
+
if (pluginTools !== void 0) {
|
|
7920
|
+
const health = routes.health;
|
|
7921
|
+
routes.health = async (search) => ({
|
|
7922
|
+
...await health?.(search),
|
|
7923
|
+
pluginTools: pluginTools.report()
|
|
7924
|
+
});
|
|
7925
|
+
ctx.effect(() => {
|
|
7926
|
+
pluginTools.refresh().catch((error) => {
|
|
7927
|
+
warn(ctx, `探 opencli 插件的命令面时抛了错:${String(error)}`);
|
|
7928
|
+
});
|
|
7929
|
+
return () => pluginTools.dispose();
|
|
7930
|
+
}, `${PLUGIN_ID}: 已装且上板的 opencli 插件各合成一个单元(工具面照旧由面板那道闸收窄)`);
|
|
7931
|
+
ctx.effect(() => {
|
|
7932
|
+
const off = ctx.get(PANELS_SERVICE)?.subscribe?.(() => pluginTools.touch());
|
|
7933
|
+
return () => off?.();
|
|
7934
|
+
}, `${PLUGIN_ID}: 面板配置一动就重探插件命令面(装上 / 收起都在那一条链上)`);
|
|
7935
|
+
}
|
|
6923
7936
|
ctx.effect(() => () => {
|
|
6924
7937
|
for (const id of [...mounted.keys()]) unmountUnit(id);
|
|
6925
7938
|
for (const dispose of undoPost) dispose?.();
|
|
@@ -6949,11 +7962,15 @@ function apply(ctx) {
|
|
|
6949
7962
|
return () => off?.();
|
|
6950
7963
|
}, `${PLUGIN_ID}: 事实归属(读工具与写工具同一处声明)`);
|
|
6951
7964
|
ctx.effect(() => {
|
|
6952
|
-
const off = ctx.on?.("agent/created", (payload) =>
|
|
7965
|
+
const off = ctx.on?.("agent/created", (payload) => {
|
|
7966
|
+
toolScope.apply(payload?.agent);
|
|
7967
|
+
pluginTools?.touch();
|
|
7968
|
+
});
|
|
6953
7969
|
return () => off?.();
|
|
6954
7970
|
}, `${PLUGIN_ID}: agent 的工具面按工作区收窄(创建时)`);
|
|
6955
7971
|
ctx.effect(() => {
|
|
6956
7972
|
const off = ctx.on?.("system-prompt/assemble", async (_assembly, context, next) => {
|
|
7973
|
+
pluginTools?.touch();
|
|
6957
7974
|
const resolved = await next();
|
|
6958
7975
|
return toolScope.narrow(resolved, context?.scope);
|
|
6959
7976
|
});
|