@dsh-workbench/core 1.1.4 → 1.1.5
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 +945 -341
- package/dist/client/client.body.js +945 -341
- package/dist/client/index.js +807 -203
- package/dist/host/index.js +540 -108
- package/dist/types/app/layouts/approval-detail.d.ts +6 -3
- package/dist/types/app/layouts/market/add-module-registry-prefs.d.ts +11 -0
- package/dist/types/app/layouts/market/market-check.d.ts +3 -1
- package/dist/types/app/layouts/rightbar/AskAgentButton.d.ts +6 -0
- package/dist/types/app/layouts/rightbar/FailureCard.d.ts +8 -0
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +2 -1
- package/dist/types/app/layouts/rightbar/SharedSection.d.ts +5 -2
- package/dist/types/app/layouts/rightbar/failure-card-text.d.ts +14 -0
- package/dist/types/app/layouts/rightbar/market-act.d.ts +2 -5
- package/dist/types/app/layouts/rightbar/market-change.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/market-hint.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/market-install.d.ts +11 -0
- package/dist/types/app/layouts/rightbar/market-row-types.d.ts +7 -1
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +12 -1
- package/dist/types/app/layouts/rightbar/market-section.d.ts +5 -3
- package/dist/types/app/layouts/rightbar/market-transient-state.d.ts +3 -8
- package/dist/types/app/layouts/rightbar/market-view-text.d.ts +2 -0
- package/dist/types/kernel/client/registry.d.ts +1 -0
- package/dist/types/kernel/contract/bundle-install.d.ts +1 -1
- package/dist/types/modules/panels/model/market-install-error.d.ts +3 -0
- package/dist/types/modules/panels/model/market-install-error.test.d.ts +1 -0
- package/dist/types/modules/panels/model/market-registry.d.ts +2 -1
- package/dist/types/modules/panels/model/market-remove.d.ts +13 -0
- package/dist/types/modules/panels/model/market-remove.test.d.ts +1 -0
- package/dist/types/modules/panels/model/market-route.d.ts +1 -1
- package/dist/types/modules/panels/model/market.d.ts +1 -0
- package/package.json +3 -3
- package/skills/workbench-module-diy/TOOLS.md +11 -2
package/dist/host/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import path, { basename, dirname, join, resolve } from "node:path";
|
|
1
|
+
import path, { basename, delimiter, dirname, join, resolve } from "node:path";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
import utc from "dayjs/plugin/utc.js";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
-
import fs, { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
+
import fs, { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
import writeFileAtomic from "write-file-atomic";
|
|
@@ -142,7 +142,9 @@ var NEED_KEYS = [
|
|
|
142
142
|
"commands",
|
|
143
143
|
"version",
|
|
144
144
|
"sources",
|
|
145
|
-
"secret"
|
|
145
|
+
"secret",
|
|
146
|
+
"install",
|
|
147
|
+
"docs"
|
|
146
148
|
];
|
|
147
149
|
var SOURCE_KEYS = [
|
|
148
150
|
"kind",
|
|
@@ -203,7 +205,7 @@ function sourcesProblemsOf(raw, at, allowed) {
|
|
|
203
205
|
if (raw === void 0) return [];
|
|
204
206
|
if (!Array.isArray(raw)) return [`${at}.sources 要是数组`];
|
|
205
207
|
if (raw.length === 0) return [`${at}.sources 是空数组 —— 不代装就整条删掉`];
|
|
206
|
-
if (allowed.length === 0) return [`${at} 这种需要不支持 sources ——
|
|
208
|
+
if (allowed.length === 0) return [`${at} 这种需要不支持 sources —— 要代装就写 install(一条命令)`];
|
|
207
209
|
const errors = [];
|
|
208
210
|
raw.forEach((source, index) => {
|
|
209
211
|
const one = sourceProblemsOf(source, `${at}.sources[${index}]`);
|
|
@@ -222,11 +224,40 @@ function commonProblemsOf(raw, at) {
|
|
|
222
224
|
if (raw["id"] !== void 0 && text$3(raw["id"]) === "") errors.push(`${at}.id 是空串`);
|
|
223
225
|
return errors;
|
|
224
226
|
}
|
|
227
|
+
function binInstallProblemsOf(raw, at) {
|
|
228
|
+
if (raw === void 0) return [];
|
|
229
|
+
if (!isRecord$1(raw)) return [`${at}.install 要是对象`];
|
|
230
|
+
const errors = [];
|
|
231
|
+
for (const key of Object.keys(raw)) if (![
|
|
232
|
+
"kind",
|
|
233
|
+
"line",
|
|
234
|
+
"platforms",
|
|
235
|
+
"package",
|
|
236
|
+
"registryEnv",
|
|
237
|
+
"registryDefault"
|
|
238
|
+
].includes(key)) errors.push(`${at}.install 有不认识的字段 ${key}`);
|
|
239
|
+
const kind = text$3(raw["kind"]);
|
|
240
|
+
if (kind !== "command" && kind !== "npm") errors.push(`${at}.install.kind 只认 command(一条命令)或 npm(一个包,装进隔离运行时)`);
|
|
241
|
+
if (kind === "command" && text$3(raw["line"]) === "") errors.push(`${at}.install.line 没写 —— 要一条能直接粘进终端的命令`);
|
|
242
|
+
if (kind === "npm" && text$3(raw["package"]) === "") errors.push(`${at}.install.package 没写 —— 这个命令由哪个 npm 包提供`);
|
|
243
|
+
if (raw["registryDefault"] !== void 0 && !REGISTRY_PATTERN.test(text$3(raw["registryDefault"]))) errors.push(`${at}.install.registryDefault 要是 http(s):// 或 file:// 的源地址`);
|
|
244
|
+
if (raw["platforms"] !== void 0) {
|
|
245
|
+
if (!Array.isArray(raw["platforms"]) || raw["platforms"].length === 0) errors.push(`${at}.install.platforms 要是非空数组`);
|
|
246
|
+
else if (raw["platforms"].some((one) => ![
|
|
247
|
+
"darwin",
|
|
248
|
+
"linux",
|
|
249
|
+
"win32"
|
|
250
|
+
].includes(text$3(one)))) errors.push(`${at}.install.platforms 只认 darwin / linux / win32`);
|
|
251
|
+
}
|
|
252
|
+
return errors;
|
|
253
|
+
}
|
|
225
254
|
function perKindProblemsOf(kind, raw, at) {
|
|
226
255
|
const errors = [];
|
|
227
256
|
if (kind === "bin" || kind === "lib" || kind === "env") {
|
|
228
257
|
if (text$3(raw["name"]) === "") errors.push(`${at}.name 没写`);
|
|
229
258
|
}
|
|
259
|
+
if (kind === "bin") errors.push(...binInstallProblemsOf(raw["install"], at));
|
|
260
|
+
if (raw["docs"] !== void 0 && text$3(raw["docs"]) === "") errors.push(`${at}.docs 是空串`);
|
|
230
261
|
if (kind === "extension") {
|
|
231
262
|
if (text$3(raw["runtime"]) === "") errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
|
|
232
263
|
if (text$3(raw["name"]) === "") errors.push(`${at}.name 没写`);
|
|
@@ -1100,7 +1131,7 @@ var PHASES = [
|
|
|
1100
1131
|
"ready",
|
|
1101
1132
|
"error"
|
|
1102
1133
|
];
|
|
1103
|
-
function asRecord$
|
|
1134
|
+
function asRecord$7(value) {
|
|
1104
1135
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
1105
1136
|
}
|
|
1106
1137
|
function textOf$5(value, max) {
|
|
@@ -1114,7 +1145,7 @@ function atOf(value) {
|
|
|
1114
1145
|
return Math.trunc(value);
|
|
1115
1146
|
}
|
|
1116
1147
|
function errorOf(raw) {
|
|
1117
|
-
const record = asRecord$
|
|
1148
|
+
const record = asRecord$7(raw);
|
|
1118
1149
|
if (record === void 0) return void 0;
|
|
1119
1150
|
const code = textOf$5(record["code"], 120);
|
|
1120
1151
|
const message = textOf$5(record["message"], PANEL_STATE_MAX_TEXT);
|
|
@@ -1131,7 +1162,7 @@ function errorOf(raw) {
|
|
|
1131
1162
|
* 或一条都认不出(`unit` / `route` / `phase` / `at` 缺一)时 `ok: false` + 中文原因;认不出的单条静默跳过。
|
|
1132
1163
|
*/
|
|
1133
1164
|
function parseCardsReport(payload) {
|
|
1134
|
-
const record = asRecord$
|
|
1165
|
+
const record = asRecord$7(payload);
|
|
1135
1166
|
if (record === void 0) return {
|
|
1136
1167
|
ok: false,
|
|
1137
1168
|
message: "请求体要是一个对象"
|
|
@@ -1148,7 +1179,7 @@ function parseCardsReport(payload) {
|
|
|
1148
1179
|
};
|
|
1149
1180
|
const cards = [];
|
|
1150
1181
|
for (const item of raw.slice(0, 32)) {
|
|
1151
|
-
const card = asRecord$
|
|
1182
|
+
const card = asRecord$7(item);
|
|
1152
1183
|
if (card === void 0) continue;
|
|
1153
1184
|
const unit = textOf$5(card["unit"], 200);
|
|
1154
1185
|
const route = textOf$5(card["route"], 400);
|
|
@@ -1182,7 +1213,7 @@ function parseCardsReport(payload) {
|
|
|
1182
1213
|
* @returns `workspace` / `unit` / `reason` 都不能空,缺一给 `ok: false` + 中文原因;`at` 缺省或不合法时取当前时间。
|
|
1183
1214
|
*/
|
|
1184
1215
|
function parseCrashReport(payload) {
|
|
1185
|
-
const record = asRecord$
|
|
1216
|
+
const record = asRecord$7(payload);
|
|
1186
1217
|
if (record === void 0) return {
|
|
1187
1218
|
ok: false,
|
|
1188
1219
|
message: "请求体要是一个对象"
|
|
@@ -1343,7 +1374,7 @@ var TARGET_CODE = {
|
|
|
1343
1374
|
name: "workbench/no-name",
|
|
1344
1375
|
requestId: "workbench/no-request-id"
|
|
1345
1376
|
};
|
|
1346
|
-
function asRecord$
|
|
1377
|
+
function asRecord$6(value) {
|
|
1347
1378
|
return typeof value === "object" && value !== null ? value : {};
|
|
1348
1379
|
}
|
|
1349
1380
|
function stringsOf$3(value) {
|
|
@@ -1386,7 +1417,7 @@ function requestOf$1(action, value) {
|
|
|
1386
1417
|
};
|
|
1387
1418
|
}
|
|
1388
1419
|
function parseProvisionBody(body) {
|
|
1389
|
-
const raw = asRecord$
|
|
1420
|
+
const raw = asRecord$6(body);
|
|
1390
1421
|
const action = raw.action;
|
|
1391
1422
|
if (typeof action !== "string" || !PROVISION_ACTIONS.includes(action)) return {
|
|
1392
1423
|
ok: false,
|
|
@@ -1426,6 +1457,7 @@ var WorkbenchDataError = class extends Error {
|
|
|
1426
1457
|
hint;
|
|
1427
1458
|
project;
|
|
1428
1459
|
helpUrl;
|
|
1460
|
+
failure;
|
|
1429
1461
|
constructor(code, message, extra = {}) {
|
|
1430
1462
|
super(message);
|
|
1431
1463
|
this.name = "WorkbenchDataError";
|
|
@@ -1434,6 +1466,7 @@ var WorkbenchDataError = class extends Error {
|
|
|
1434
1466
|
if (extra.hint !== void 0) this.hint = extra.hint;
|
|
1435
1467
|
if (extra.project !== void 0) this.project = extra.project;
|
|
1436
1468
|
if (extra.helpUrl !== void 0) this.helpUrl = extra.helpUrl;
|
|
1469
|
+
if (extra.failure !== void 0) this.failure = extra.failure;
|
|
1437
1470
|
}
|
|
1438
1471
|
};
|
|
1439
1472
|
//#endregion
|
|
@@ -1481,7 +1514,8 @@ function toErrorPayload(error) {
|
|
|
1481
1514
|
...data.detail === void 0 ? {} : { detail: data.detail },
|
|
1482
1515
|
...data.hint === void 0 ? {} : { hint: data.hint },
|
|
1483
1516
|
...data.helpUrl === void 0 ? {} : { helpUrl: data.helpUrl },
|
|
1484
|
-
...data.project === void 0 ? {} : { project: data.project }
|
|
1517
|
+
...data.project === void 0 ? {} : { project: data.project },
|
|
1518
|
+
...data.failure === void 0 ? {} : { failure: data.failure }
|
|
1485
1519
|
};
|
|
1486
1520
|
}
|
|
1487
1521
|
return {
|
|
@@ -2231,7 +2265,10 @@ function tokenFamilyOf(name) {
|
|
|
2231
2265
|
return cut === -1 ? rest : rest.slice(0, cut);
|
|
2232
2266
|
}
|
|
2233
2267
|
//#endregion
|
|
2234
|
-
//#region ../shared/dist/
|
|
2268
|
+
//#region ../shared/dist/contract/approval.js
|
|
2269
|
+
/**
|
|
2270
|
+
* agent 自己那句"这次想干什么"的参数名:插件命令族必填,审批屏把它画在详情座里,不进标题。
|
|
2271
|
+
*/
|
|
2235
2272
|
var APPROVAL_NOTE_ARG = "approval_note";
|
|
2236
2273
|
function oneLine(text) {
|
|
2237
2274
|
return text.replace(/\s+/g, " ").trim();
|
|
@@ -2295,10 +2332,11 @@ async function approvalFor(call, context = {}) {
|
|
|
2295
2332
|
reason: facts.blocked
|
|
2296
2333
|
};
|
|
2297
2334
|
if (context.skipAsk === true) return void 0;
|
|
2335
|
+
const note = context.declaredNote === true && verdict.noteInDetail !== true ? approvalNoteOf(call) : void 0;
|
|
2298
2336
|
return {
|
|
2299
2337
|
kind: "ask",
|
|
2300
2338
|
reason: verdict.reason,
|
|
2301
|
-
displayReason: compose(facts, verdict,
|
|
2339
|
+
displayReason: compose(facts, verdict, note)
|
|
2302
2340
|
};
|
|
2303
2341
|
}
|
|
2304
2342
|
//#endregion
|
|
@@ -2518,6 +2556,188 @@ function backgroundLine(command, argv) {
|
|
|
2518
2556
|
].join(" ");
|
|
2519
2557
|
}
|
|
2520
2558
|
//#endregion
|
|
2559
|
+
//#region ../shared/dist/host/install-failure.js
|
|
2560
|
+
var TARGET_FLAGS = [
|
|
2561
|
+
"--prefix",
|
|
2562
|
+
"--cache",
|
|
2563
|
+
"--dir",
|
|
2564
|
+
"--store-dir"
|
|
2565
|
+
];
|
|
2566
|
+
var INSTALL_VERBS = /^\s*npm\s+(i|install|add)\b/;
|
|
2567
|
+
var NPM_PATH = /^npm (?:error|ERR!) path (.+)$/m;
|
|
2568
|
+
function commandTokens(command) {
|
|
2569
|
+
const tokens = [];
|
|
2570
|
+
const token = /'([^']*)'|"([^"]*)"|(\S+)/g;
|
|
2571
|
+
for (let match = token.exec(command); match !== null; match = token.exec(command)) tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
2572
|
+
return tokens;
|
|
2573
|
+
}
|
|
2574
|
+
function installTargets(command) {
|
|
2575
|
+
const tokens = commandTokens(command);
|
|
2576
|
+
const targets = [];
|
|
2577
|
+
for (let index = 0; index < tokens.length - 1; index += 1) if (TARGET_FLAGS.includes(tokens[index] ?? "")) targets.push(tokens[index + 1] ?? "");
|
|
2578
|
+
return targets.filter((target) => target !== "");
|
|
2579
|
+
}
|
|
2580
|
+
function failurePathIn(output) {
|
|
2581
|
+
const path = NPM_PATH.exec(output)?.[1]?.trim();
|
|
2582
|
+
return path === void 0 || path === "" ? void 0 : path;
|
|
2583
|
+
}
|
|
2584
|
+
function nearestDir(path) {
|
|
2585
|
+
let at = path;
|
|
2586
|
+
for (;;) {
|
|
2587
|
+
const here = () => {
|
|
2588
|
+
try {
|
|
2589
|
+
return statSync(at).isDirectory();
|
|
2590
|
+
} catch {
|
|
2591
|
+
return false;
|
|
2592
|
+
}
|
|
2593
|
+
};
|
|
2594
|
+
if (here()) return at;
|
|
2595
|
+
const up = dirname(at);
|
|
2596
|
+
if (up === at) return void 0;
|
|
2597
|
+
at = up;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function writeStateOf(dir) {
|
|
2601
|
+
try {
|
|
2602
|
+
accessSync(dir, constants.W_OK);
|
|
2603
|
+
return "ok";
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
return error.code === "EACCES" ? "denied" : "unknown";
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
function dirFacts(dir) {
|
|
2609
|
+
try {
|
|
2610
|
+
const stats = statSync(dir);
|
|
2611
|
+
return {
|
|
2612
|
+
mode: (stats.mode & 511).toString(8),
|
|
2613
|
+
owner: {
|
|
2614
|
+
uid: stats.uid,
|
|
2615
|
+
gid: stats.gid
|
|
2616
|
+
},
|
|
2617
|
+
writable: writeStateOf(dir) === "ok"
|
|
2618
|
+
};
|
|
2619
|
+
} catch {
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
function currentUser() {
|
|
2624
|
+
return {
|
|
2625
|
+
uid: process.getuid?.() ?? -1,
|
|
2626
|
+
gid: process.getgid?.() ?? -1
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
function classifyInstallFailure(output) {
|
|
2630
|
+
const text = output.toLowerCase();
|
|
2631
|
+
if (/enospc|no space left/.test(text)) return "disk-full";
|
|
2632
|
+
if (/eacces|eperm|permission denied|operation not permitted|access is denied/.test(text)) return "permission";
|
|
2633
|
+
return "unknown";
|
|
2634
|
+
}
|
|
2635
|
+
function installFailureOf(facts) {
|
|
2636
|
+
const kind = facts.kind ?? classifyInstallFailure(facts.output);
|
|
2637
|
+
const path = facts.path;
|
|
2638
|
+
const base = {
|
|
2639
|
+
command: facts.command,
|
|
2640
|
+
exitCode: facts.exitCode,
|
|
2641
|
+
output: facts.output,
|
|
2642
|
+
...path === void 0 ? {} : { path },
|
|
2643
|
+
...facts.mode === void 0 ? {} : { mode: facts.mode },
|
|
2644
|
+
...facts.owner === void 0 ? {} : { owner: facts.owner },
|
|
2645
|
+
current: facts.current
|
|
2646
|
+
};
|
|
2647
|
+
if (kind === "disk-full") return {
|
|
2648
|
+
...base,
|
|
2649
|
+
kind,
|
|
2650
|
+
fix: path === void 0 ? { kind: "handoff" } : {
|
|
2651
|
+
kind: "space",
|
|
2652
|
+
path
|
|
2653
|
+
}
|
|
2654
|
+
};
|
|
2655
|
+
if (kind === "permission" && path !== void 0) {
|
|
2656
|
+
const owner = facts.owner;
|
|
2657
|
+
if (owner !== void 0 && owner.uid !== facts.current.uid && facts.current.uid >= 0) return {
|
|
2658
|
+
...base,
|
|
2659
|
+
kind,
|
|
2660
|
+
fix: {
|
|
2661
|
+
kind: "chown",
|
|
2662
|
+
path,
|
|
2663
|
+
uid: facts.current.uid,
|
|
2664
|
+
gid: facts.current.gid
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
if (facts.writable === false) return {
|
|
2668
|
+
...base,
|
|
2669
|
+
kind,
|
|
2670
|
+
fix: {
|
|
2671
|
+
kind: "chmod",
|
|
2672
|
+
path
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2675
|
+
if (facts.writable === true) return {
|
|
2676
|
+
...base,
|
|
2677
|
+
kind: "sandbox",
|
|
2678
|
+
fix: { kind: "handoff" }
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
return {
|
|
2682
|
+
...base,
|
|
2683
|
+
kind,
|
|
2684
|
+
fix: { kind: "handoff" }
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function diagnoseInstallFailure(input) {
|
|
2688
|
+
const current = currentUser();
|
|
2689
|
+
const candidates = [
|
|
2690
|
+
failurePathIn(input.output),
|
|
2691
|
+
...installTargets(input.command),
|
|
2692
|
+
input.fallback
|
|
2693
|
+
].filter((path) => path !== void 0 && path !== "");
|
|
2694
|
+
for (const candidate of candidates) {
|
|
2695
|
+
const dir = nearestDir(candidate);
|
|
2696
|
+
if (dir === void 0) continue;
|
|
2697
|
+
const facts = dirFacts(dir);
|
|
2698
|
+
if (facts === void 0) continue;
|
|
2699
|
+
return installFailureOf({
|
|
2700
|
+
command: input.command,
|
|
2701
|
+
exitCode: input.exitCode,
|
|
2702
|
+
output: input.output,
|
|
2703
|
+
path: dir,
|
|
2704
|
+
mode: facts.mode,
|
|
2705
|
+
owner: facts.owner,
|
|
2706
|
+
writable: facts.writable,
|
|
2707
|
+
current
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
return installFailureOf({
|
|
2711
|
+
command: input.command,
|
|
2712
|
+
exitCode: input.exitCode,
|
|
2713
|
+
output: input.output,
|
|
2714
|
+
current
|
|
2715
|
+
});
|
|
2716
|
+
}
|
|
2717
|
+
function installWriteProblem(input) {
|
|
2718
|
+
if (!INSTALL_VERBS.test(input.command)) return void 0;
|
|
2719
|
+
const current = currentUser();
|
|
2720
|
+
const candidates = [...installTargets(input.command), input.fallback].filter((path) => path !== void 0 && path !== "");
|
|
2721
|
+
for (const candidate of candidates) {
|
|
2722
|
+
const dir = nearestDir(candidate);
|
|
2723
|
+
if (dir === void 0) continue;
|
|
2724
|
+
if (writeStateOf(dir) !== "denied") continue;
|
|
2725
|
+
const facts = dirFacts(dir);
|
|
2726
|
+
if (facts === void 0) continue;
|
|
2727
|
+
return installFailureOf({
|
|
2728
|
+
command: input.command,
|
|
2729
|
+
exitCode: null,
|
|
2730
|
+
output: "",
|
|
2731
|
+
kind: "permission",
|
|
2732
|
+
path: dir,
|
|
2733
|
+
mode: facts.mode,
|
|
2734
|
+
owner: facts.owner,
|
|
2735
|
+
writable: facts.writable,
|
|
2736
|
+
current
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
//#endregion
|
|
2521
2741
|
//#region ../shared/dist/host/paths.js
|
|
2522
2742
|
function isolatedRoot() {
|
|
2523
2743
|
return `${workbenchRoot()}/opencli`;
|
|
@@ -2531,6 +2751,46 @@ function vendorPluginsDir() {
|
|
|
2531
2751
|
function libsPrefix() {
|
|
2532
2752
|
return `${isolatedRoot()}/libs`;
|
|
2533
2753
|
}
|
|
2754
|
+
var OPENCLI_NPM_PACKAGE = "@jackwener/opencli";
|
|
2755
|
+
function toolchainOpencliDir() {
|
|
2756
|
+
return `${isolatedRoot()}/toolchain/node_modules/${OPENCLI_NPM_PACKAGE}`;
|
|
2757
|
+
}
|
|
2758
|
+
function libsBinDir() {
|
|
2759
|
+
return `${libsPrefix()}/node_modules/.bin`;
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* 把隔离运行时的共用依赖 bin 目录放到 PATH 最前面(可执行文件都从这儿找)。
|
|
2763
|
+
* @param env 改哪份环境(默认当前进程)。
|
|
2764
|
+
* @returns 真的插进去了就 `true`;已经在里面就 `false`。
|
|
2765
|
+
*/
|
|
2766
|
+
function ensureLibsBinOnPath(env = process.env) {
|
|
2767
|
+
const dir = libsBinDir();
|
|
2768
|
+
const parts = (env.PATH ?? "").split(delimiter).filter((one) => one !== "");
|
|
2769
|
+
if (parts.includes(dir)) return false;
|
|
2770
|
+
env.PATH = [dir, ...parts].join(delimiter);
|
|
2771
|
+
return true;
|
|
2772
|
+
}
|
|
2773
|
+
//#endregion
|
|
2774
|
+
//#region ../shared/dist/host/runtime-links.js
|
|
2775
|
+
/**
|
|
2776
|
+
* 让共用依赖也能 import opencli:把隔离运行时里那份 opencli 链接到 libs 的 node_modules。
|
|
2777
|
+
* 跑在 libs 里的命令(比如 opencli-workflow 带的 gitops)会按包名找 @jackwener/opencli,
|
|
2778
|
+
* 找不到就起不来 —— 它不在 libs 的依赖里,是运行时提供的那一份。
|
|
2779
|
+
* @returns 这次真的建了链接就 `true`;已经有了、或运行时那份还没装,就是 `false`。
|
|
2780
|
+
*/
|
|
2781
|
+
function ensureLibsOpencliPeer() {
|
|
2782
|
+
const link = `${libsPrefix()}/node_modules/${OPENCLI_NPM_PACKAGE}`;
|
|
2783
|
+
if (existsSync(link)) return false;
|
|
2784
|
+
const target = toolchainOpencliDir();
|
|
2785
|
+
if (!existsSync(target)) return false;
|
|
2786
|
+
try {
|
|
2787
|
+
mkdirSync(dirname(link), { recursive: true });
|
|
2788
|
+
symlinkSync(target, link);
|
|
2789
|
+
return true;
|
|
2790
|
+
} catch {
|
|
2791
|
+
return false;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2534
2794
|
//#endregion
|
|
2535
2795
|
//#region ../shared/dist/host/adapters.js
|
|
2536
2796
|
var WORKBENCH_ADAPTERS = "workbenchAdapters";
|
|
@@ -4066,7 +4326,7 @@ function registryFailure(change) {
|
|
|
4066
4326
|
//#endregion
|
|
4067
4327
|
//#region ../shared/dist/host/plugin.js
|
|
4068
4328
|
var OPENCLI_MANIFEST_FILE = "opencli-plugin.json";
|
|
4069
|
-
function asRecord$
|
|
4329
|
+
function asRecord$5(value) {
|
|
4070
4330
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
4071
4331
|
}
|
|
4072
4332
|
function isCommandFile(name) {
|
|
@@ -4078,7 +4338,7 @@ function pluginManifestOf(dir) {
|
|
|
4078
4338
|
try {
|
|
4079
4339
|
const parsed = JSON.parse(readFileSync(join(dir, OPENCLI_MANIFEST_FILE), "utf8"));
|
|
4080
4340
|
if (Array.isArray(parsed)) return void 0;
|
|
4081
|
-
return asRecord$
|
|
4341
|
+
return asRecord$5(parsed);
|
|
4082
4342
|
} catch {
|
|
4083
4343
|
return;
|
|
4084
4344
|
}
|
|
@@ -4685,20 +4945,20 @@ var MARKET_INDEX_README_LINES = [
|
|
|
4685
4945
|
var NPM_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
4686
4946
|
var GIT_COMMIT = /^[0-9a-f]{40}$/;
|
|
4687
4947
|
var MAX_ICON_CHARS = 32768;
|
|
4688
|
-
function asRecord$
|
|
4948
|
+
function asRecord$4(value) {
|
|
4689
4949
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4690
4950
|
}
|
|
4691
|
-
function asText$
|
|
4951
|
+
function asText$4(value) {
|
|
4692
4952
|
return typeof value === "string" ? value.trim() : "";
|
|
4693
4953
|
}
|
|
4694
4954
|
function asTextArray(value) {
|
|
4695
4955
|
return Array.isArray(value) ? value.flatMap((item) => typeof item === "string" && item.trim() !== "" ? [item.trim()] : []) : [];
|
|
4696
4956
|
}
|
|
4697
4957
|
function asLocalized(value) {
|
|
4698
|
-
const record = asRecord$
|
|
4958
|
+
const record = asRecord$4(value);
|
|
4699
4959
|
if (record === void 0) return void 0;
|
|
4700
|
-
const zh = asText$
|
|
4701
|
-
const en = asText$
|
|
4960
|
+
const zh = asText$4(record["zh"]);
|
|
4961
|
+
const en = asText$4(record["en"]);
|
|
4702
4962
|
return zh === "" && en === "" ? void 0 : {
|
|
4703
4963
|
...zh === "" ? {} : { zh },
|
|
4704
4964
|
...en === "" ? {} : { en }
|
|
@@ -4715,19 +4975,19 @@ function compareSemver(left, right) {
|
|
|
4715
4975
|
return compareVersions(left, right);
|
|
4716
4976
|
}
|
|
4717
4977
|
function sourceOf(raw, at) {
|
|
4718
|
-
const record = asRecord$
|
|
4978
|
+
const record = asRecord$4(raw);
|
|
4719
4979
|
if (record === void 0) return problem("source", "source 必须是一个对象", at);
|
|
4720
4980
|
if (record["type"] === "npm") {
|
|
4721
4981
|
const registry = record["registry"];
|
|
4722
|
-
if (registry !== void 0 && asText$
|
|
4982
|
+
if (registry !== void 0 && asText$4(registry) === "") return problem("source", "npm source 的 registry 必须是非空字符串", at);
|
|
4723
4983
|
return {
|
|
4724
4984
|
type: "npm",
|
|
4725
|
-
...registry === void 0 ? {} : { registry: asText$
|
|
4985
|
+
...registry === void 0 ? {} : { registry: asText$4(registry) }
|
|
4726
4986
|
};
|
|
4727
4987
|
}
|
|
4728
4988
|
if (record["type"] === "git") {
|
|
4729
|
-
const url = asText$
|
|
4730
|
-
const commit = asText$
|
|
4989
|
+
const url = asText$4(record["url"]);
|
|
4990
|
+
const commit = asText$4(record["commit"]);
|
|
4731
4991
|
if (url === "") return problem("source", "git source 缺 url", at);
|
|
4732
4992
|
if (!GIT_COMMIT.test(commit)) return problem("git-commit", `git source 的 commit 必须是 40 位小写 SHA(现在是 ${JSON.stringify(commit)})`, at);
|
|
4733
4993
|
const path = record["path"];
|
|
@@ -4735,22 +4995,22 @@ function sourceOf(raw, at) {
|
|
|
4735
4995
|
type: "git",
|
|
4736
4996
|
url,
|
|
4737
4997
|
commit,
|
|
4738
|
-
...path === void 0 || asText$
|
|
4998
|
+
...path === void 0 || asText$4(path) === "" ? {} : { path: asText$4(path) }
|
|
4739
4999
|
};
|
|
4740
5000
|
}
|
|
4741
5001
|
return problem("source", "source.type 只能是 npm 或 git", at);
|
|
4742
5002
|
}
|
|
4743
5003
|
function versionOf(raw, at) {
|
|
4744
|
-
const record = asRecord$
|
|
5004
|
+
const record = asRecord$4(raw);
|
|
4745
5005
|
if (record === void 0) return problem("version", "版本必须是一个对象", at);
|
|
4746
|
-
const version = asText$
|
|
5006
|
+
const version = asText$4(record["version"]);
|
|
4747
5007
|
if (!isValidVersion(version)) return problem("version", `版本号不是合法 SemVer:${JSON.stringify(version)}`, at);
|
|
4748
5008
|
const source = sourceOf(record["source"], `${at}.source`);
|
|
4749
5009
|
if ("kind" in source) return source;
|
|
4750
5010
|
const timestamp = record["publishTimestamp"];
|
|
4751
5011
|
if (timestamp !== void 0 && (!Number.isInteger(timestamp) || timestamp < 0)) return problem("timestamp", "publishTimestamp 必须是 Unix 秒整数", at);
|
|
4752
|
-
const engines = asRecord$
|
|
4753
|
-
const dsh = engines === void 0 ? "" : asText$
|
|
5012
|
+
const engines = asRecord$4(record["engines"]);
|
|
5013
|
+
const dsh = engines === void 0 ? "" : asText$4(engines["dsh"]);
|
|
4754
5014
|
return {
|
|
4755
5015
|
version,
|
|
4756
5016
|
source,
|
|
@@ -4788,14 +5048,14 @@ function ownerProblem(owner) {
|
|
|
4788
5048
|
if (owner.length > MAX_OWNER_CHARS) return problem("owner", "owner 超过 200 字");
|
|
4789
5049
|
}
|
|
4790
5050
|
function metadataOf(raw) {
|
|
4791
|
-
const record = asRecord$
|
|
5051
|
+
const record = asRecord$4(raw);
|
|
4792
5052
|
if (record === void 0) return void 0;
|
|
4793
5053
|
const workbench = record["workbench"] === true;
|
|
4794
|
-
const unit = asText$
|
|
4795
|
-
const category = asText$
|
|
5054
|
+
const unit = asText$4(record["unit"]);
|
|
5055
|
+
const category = asText$4(record["category"]);
|
|
4796
5056
|
const labels = asTextArray(record["labels"]);
|
|
4797
5057
|
const keywords = asTextArray(record["keywords"]);
|
|
4798
|
-
const sortKeysRecord = asRecord$
|
|
5058
|
+
const sortKeysRecord = asRecord$4(record["sortKeys"]);
|
|
4799
5059
|
const sortKeys = sortKeysRecord === void 0 ? void 0 : Object.fromEntries(Object.entries(sortKeysRecord).flatMap(([key, value]) => typeof value === "string" || typeof value === "number" ? [[key, value]] : []));
|
|
4800
5060
|
return {
|
|
4801
5061
|
...workbench ? { workbench: true } : {},
|
|
@@ -4807,26 +5067,26 @@ function metadataOf(raw) {
|
|
|
4807
5067
|
};
|
|
4808
5068
|
}
|
|
4809
5069
|
function entryOf$1(raw, at) {
|
|
4810
|
-
const record = asRecord$
|
|
5070
|
+
const record = asRecord$4(raw);
|
|
4811
5071
|
if (record === void 0) return problem("plugin", "插件条目必须是对象", at);
|
|
4812
|
-
const name = asText$
|
|
5072
|
+
const name = asText$4(record["name"]);
|
|
4813
5073
|
if (name === "" || name.length > 214 || !NPM_NAME.test(name)) return problem("name", `不是合法 npm 包名:${JSON.stringify(name)}`, at);
|
|
4814
5074
|
const versions = versionsOf$1(record["versions"], `${at}.versions`);
|
|
4815
5075
|
if ("kind" in versions) return versions;
|
|
4816
|
-
const icon = asText$
|
|
5076
|
+
const icon = asText$4(record["icon"]);
|
|
4817
5077
|
if (icon !== "") {
|
|
4818
5078
|
const badIcon = iconProblem(icon);
|
|
4819
5079
|
if (badIcon !== void 0) return badIcon;
|
|
4820
5080
|
}
|
|
4821
5081
|
const title = asLocalized(record["title"]);
|
|
4822
5082
|
const description = asLocalized(record["description"]);
|
|
4823
|
-
const author = asText$
|
|
4824
|
-
const owner = asText$
|
|
5083
|
+
const author = asText$4(record["author"]);
|
|
5084
|
+
const owner = asText$4(record["owner"]);
|
|
4825
5085
|
if (owner !== "") {
|
|
4826
5086
|
const badOwner = ownerProblem(owner);
|
|
4827
5087
|
if (badOwner !== void 0) return badOwner;
|
|
4828
5088
|
}
|
|
4829
|
-
const homepage = asText$
|
|
5089
|
+
const homepage = asText$4(record["homepage"]);
|
|
4830
5090
|
const metadata = metadataOf(record["metadata"]);
|
|
4831
5091
|
if (metadata?.unit !== void 0 && !UNIT_ID.test(metadata.unit)) return problem("unit", `metadata.unit 只能是小写字母开头的 a-z0-9-(现在是 ${JSON.stringify(metadata.unit)})`, at);
|
|
4832
5092
|
return {
|
|
@@ -4842,7 +5102,7 @@ function entryOf$1(raw, at) {
|
|
|
4842
5102
|
};
|
|
4843
5103
|
}
|
|
4844
5104
|
function parseMarketIndex(raw) {
|
|
4845
|
-
const record = asRecord$
|
|
5105
|
+
const record = asRecord$4(raw);
|
|
4846
5106
|
if (record === void 0) return {
|
|
4847
5107
|
ok: false,
|
|
4848
5108
|
problems: [problem("not-object", "索引必须是一个 JSON 对象")]
|
|
@@ -4878,7 +5138,7 @@ function parseMarketIndex(raw) {
|
|
|
4878
5138
|
};
|
|
4879
5139
|
const title = asLocalized(record["title"]);
|
|
4880
5140
|
const description = asLocalized(record["description"]);
|
|
4881
|
-
const metadata = asRecord$
|
|
5141
|
+
const metadata = asRecord$4(record["metadata"]);
|
|
4882
5142
|
return {
|
|
4883
5143
|
ok: true,
|
|
4884
5144
|
index: {
|
|
@@ -4932,15 +5192,15 @@ function marketSourceKey(source) {
|
|
|
4932
5192
|
return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
4933
5193
|
}
|
|
4934
5194
|
function parseSourceConfig(raw) {
|
|
4935
|
-
const record = asRecord$
|
|
5195
|
+
const record = asRecord$4(raw);
|
|
4936
5196
|
if (record === void 0) return {
|
|
4937
5197
|
ok: false,
|
|
4938
5198
|
problem: problem("source", "索引源必须是对象")
|
|
4939
5199
|
};
|
|
4940
|
-
const id = asText$
|
|
4941
|
-
const label = asText$
|
|
5200
|
+
const id = asText$4(record["id"]);
|
|
5201
|
+
const label = asText$4(record["label"]);
|
|
4942
5202
|
const kind = record["kind"];
|
|
4943
|
-
const url = asText$
|
|
5203
|
+
const url = asText$4(record["url"]);
|
|
4944
5204
|
if (id === "") return {
|
|
4945
5205
|
ok: false,
|
|
4946
5206
|
problem: problem("source", "索引源缺 id")
|
|
@@ -4973,9 +5233,9 @@ function parseSourceConfig(raw) {
|
|
|
4973
5233
|
ok: false,
|
|
4974
5234
|
problem: problem("source", "git 地址不合格(只收 https / ssh / scp 形式,且不能带凭据)")
|
|
4975
5235
|
};
|
|
4976
|
-
const ref = asText$
|
|
4977
|
-
const path = asText$
|
|
4978
|
-
const localPath = asText$
|
|
5236
|
+
const ref = asText$4(record["ref"]);
|
|
5237
|
+
const path = asText$4(record["path"]);
|
|
5238
|
+
const localPath = asText$4(record["localPath"]);
|
|
4979
5239
|
const policy = record["policy"];
|
|
4980
5240
|
if (policy !== void 0 && policy !== "artifacts-in-repo" && policy !== "catalog-only") return {
|
|
4981
5241
|
ok: false,
|
|
@@ -5285,6 +5545,72 @@ function normalizeWhitespace(rows) {
|
|
|
5285
5545
|
return out.join("\n").replace(/\n+$/, "");
|
|
5286
5546
|
}
|
|
5287
5547
|
//#endregion
|
|
5548
|
+
//#region ../shared/dist/host/installed-sources.js
|
|
5549
|
+
var FILE_NAME = ".dsh-workbench-sources.json";
|
|
5550
|
+
var FILE_VERSION = 1;
|
|
5551
|
+
function asRecord$3(value) {
|
|
5552
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
5553
|
+
}
|
|
5554
|
+
function asText$3(value) {
|
|
5555
|
+
return typeof value === "string" ? value.trim() : "";
|
|
5556
|
+
}
|
|
5557
|
+
function installedSourcesFile(profileDir) {
|
|
5558
|
+
return path.join(profileDir, FILE_NAME);
|
|
5559
|
+
}
|
|
5560
|
+
function readInstalledSources(profileDir) {
|
|
5561
|
+
if (profileDir === null) return {};
|
|
5562
|
+
let parsed;
|
|
5563
|
+
try {
|
|
5564
|
+
parsed = JSON.parse(fs.readFileSync(installedSourcesFile(profileDir), "utf8"));
|
|
5565
|
+
} catch {
|
|
5566
|
+
return {};
|
|
5567
|
+
}
|
|
5568
|
+
const packages = asRecord$3(asRecord$3(parsed)?.packages);
|
|
5569
|
+
if (packages === void 0) return {};
|
|
5570
|
+
const found = {};
|
|
5571
|
+
for (const [pkg, value] of Object.entries(packages)) {
|
|
5572
|
+
const record = asRecord$3(value);
|
|
5573
|
+
if (record === void 0) continue;
|
|
5574
|
+
const version = asText$3(record.version);
|
|
5575
|
+
const registry = asText$3(record.registry);
|
|
5576
|
+
if (pkg === "" || version === "" || registry === "") continue;
|
|
5577
|
+
found[pkg] = {
|
|
5578
|
+
version,
|
|
5579
|
+
registry
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
return found;
|
|
5583
|
+
}
|
|
5584
|
+
function packageSourceOf(profileDir, pkg) {
|
|
5585
|
+
return readInstalledSources(profileDir)[pkg];
|
|
5586
|
+
}
|
|
5587
|
+
function writeInstalledSources(profileDir, sources) {
|
|
5588
|
+
const file = installedSourcesFile(profileDir);
|
|
5589
|
+
const temp = `${file}.tmp`;
|
|
5590
|
+
const text = `${JSON.stringify({
|
|
5591
|
+
version: FILE_VERSION,
|
|
5592
|
+
packages: sources
|
|
5593
|
+
}, null, 2)}\n`;
|
|
5594
|
+
fs.writeFileSync(temp, text, { mode: 384 });
|
|
5595
|
+
fs.renameSync(temp, file);
|
|
5596
|
+
}
|
|
5597
|
+
function rememberPackageSource(profileDir, pkg, source) {
|
|
5598
|
+
const trimmed = pkg.trim();
|
|
5599
|
+
if (trimmed === "" || source.version.trim() === "" || source.registry.trim() === "") return;
|
|
5600
|
+
const all = { ...readInstalledSources(profileDir) };
|
|
5601
|
+
all[trimmed] = {
|
|
5602
|
+
version: source.version.trim(),
|
|
5603
|
+
registry: source.registry.trim()
|
|
5604
|
+
};
|
|
5605
|
+
writeInstalledSources(profileDir, all);
|
|
5606
|
+
}
|
|
5607
|
+
function forgetPackageSource(profileDir, pkg) {
|
|
5608
|
+
const all = { ...readInstalledSources(profileDir) };
|
|
5609
|
+
if (all[pkg] === void 0) return;
|
|
5610
|
+
delete all[pkg];
|
|
5611
|
+
writeInstalledSources(profileDir, all);
|
|
5612
|
+
}
|
|
5613
|
+
//#endregion
|
|
5288
5614
|
//#region ../shared/dist/contract/protocol/dsh-plugins.js
|
|
5289
5615
|
var DSH_PLUGINS_ROUTE = "dsh-plugins";
|
|
5290
5616
|
//#endregion
|
|
@@ -5483,8 +5809,10 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
5483
5809
|
const force = param(search, "force") === "1";
|
|
5484
5810
|
const fallback = (await manager.registries())?.resolved ?? void 0;
|
|
5485
5811
|
const modules = await Promise.all(found.map(async (module) => {
|
|
5486
|
-
const
|
|
5487
|
-
const
|
|
5812
|
+
const local = module.local === true;
|
|
5813
|
+
const fromRegistry = module.fromRegistry !== false;
|
|
5814
|
+
const registry = fromRegistry ? module.registry ?? fallback : void 0;
|
|
5815
|
+
const latest = local || !fromRegistry || shell === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
5488
5816
|
shell,
|
|
5489
5817
|
name: module.pkg,
|
|
5490
5818
|
registry,
|
|
@@ -5516,7 +5844,10 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
5516
5844
|
...title === void 0 ? {} : { title },
|
|
5517
5845
|
...latest === void 0 ? {} : { latest },
|
|
5518
5846
|
...latestBlocked === void 0 ? {} : { latestBlocked },
|
|
5519
|
-
...
|
|
5847
|
+
...local ? {
|
|
5848
|
+
local: true,
|
|
5849
|
+
latestUnavailable: "local"
|
|
5850
|
+
} : latest !== void 0 ? {} : { latestUnavailable: !fromRegistry ? "not-registry" : registry === void 0 ? "no-registry" : "probe-failed" },
|
|
5520
5851
|
...refused === void 0 ? {} : { refused }
|
|
5521
5852
|
};
|
|
5522
5853
|
}));
|
|
@@ -11708,6 +12039,22 @@ function registerRecordTools(ctx, store) {
|
|
|
11708
12039
|
];
|
|
11709
12040
|
}
|
|
11710
12041
|
//#endregion
|
|
12042
|
+
//#region src/modules/panels/model/market-install-error.ts
|
|
12043
|
+
var INSTALL_PROBLEM = {
|
|
12044
|
+
permission: "没有写入权限",
|
|
12045
|
+
"disk-full": "磁盘满了",
|
|
12046
|
+
sandbox: "被系统拦住了",
|
|
12047
|
+
unknown: "命令失败"
|
|
12048
|
+
};
|
|
12049
|
+
function installError(failure) {
|
|
12050
|
+
const tail = tailLines(failure.output, 6);
|
|
12051
|
+
const stamp = failure.exitCode === null ? "" : `(退出码 ${failure.exitCode})`;
|
|
12052
|
+
return new WorkbenchDataError("install-failed", `${INSTALL_PROBLEM[failure.kind]}${stamp}${tail === "" ? "" : `:${tail}`}`, {
|
|
12053
|
+
detail: failure.output,
|
|
12054
|
+
failure
|
|
12055
|
+
});
|
|
12056
|
+
}
|
|
12057
|
+
//#endregion
|
|
11711
12058
|
//#region src/modules/panels/model/market-dependency.ts
|
|
11712
12059
|
var DEPENDENCY_KINDS = ["extension", "lib"];
|
|
11713
12060
|
function dependencyId(kind, name) {
|
|
@@ -11929,7 +12276,8 @@ async function settle(deps, spec, options, change) {
|
|
|
11929
12276
|
kind: "unknown",
|
|
11930
12277
|
pkg
|
|
11931
12278
|
};
|
|
11932
|
-
|
|
12279
|
+
const registry = options?.registry ?? (module.kind === "module" ? module.registry : void 0);
|
|
12280
|
+
await deps.rememberSource(deps.manager, pkg, registry, module.kind === "module" ? module.version : void 0);
|
|
11933
12281
|
return {
|
|
11934
12282
|
change,
|
|
11935
12283
|
module,
|
|
@@ -12117,6 +12465,110 @@ async function updateModuleBundle(unit, deps) {
|
|
|
12117
12465
|
};
|
|
12118
12466
|
}
|
|
12119
12467
|
//#endregion
|
|
12468
|
+
//#region src/modules/panels/model/market-registry.ts
|
|
12469
|
+
async function rememberInstalledSource(deps, manager, pkg, registry, version) {
|
|
12470
|
+
const { registries, profileDir, logger } = deps;
|
|
12471
|
+
const wanted = registry?.trim() ?? "";
|
|
12472
|
+
if (wanted === "") return;
|
|
12473
|
+
if (profileDir !== void 0 && profileDir !== null && version !== void 0 && version.trim() !== "") try {
|
|
12474
|
+
rememberPackageSource(profileDir, pkg, {
|
|
12475
|
+
version,
|
|
12476
|
+
registry: wanted
|
|
12477
|
+
});
|
|
12478
|
+
} catch (error) {
|
|
12479
|
+
logger?.warn?.(`[dsh-workbench] 记 ${pkg} 这次的来源失败:${String(error)}`);
|
|
12480
|
+
}
|
|
12481
|
+
if (registries === void 0) return;
|
|
12482
|
+
const scope = scopeOf$2(pkg);
|
|
12483
|
+
if (scope === null) return;
|
|
12484
|
+
const known = await manager.registries().catch(() => null);
|
|
12485
|
+
if (known !== null && sameRegistry(known.resolved, wanted)) return;
|
|
12486
|
+
try {
|
|
12487
|
+
const added = registries.remember([{
|
|
12488
|
+
scope,
|
|
12489
|
+
registry: wanted
|
|
12490
|
+
}]);
|
|
12491
|
+
if (added.length > 0) logger?.info?.(`[dsh-workbench] 把 ${added.join("、")} 记进 profile 的 .npmrc(卸载只认 pnpm 的配置)`);
|
|
12492
|
+
} catch (error) {
|
|
12493
|
+
logger?.warn?.(`[dsh-workbench] 记这个 scope 的源失败:${String(error)}`);
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
async function retryAfterRegistryFix(deps, manager, pkg, change) {
|
|
12497
|
+
const { registries, logger } = deps;
|
|
12498
|
+
if (registries === void 0 || !registryFailure(change)) return change;
|
|
12499
|
+
const known = await manager.registries().catch(() => null);
|
|
12500
|
+
const missing = registries.sources().filter((entry) => !sameRegistry(entry.registry, known?.resolved ?? null));
|
|
12501
|
+
if (missing.length === 0) return change;
|
|
12502
|
+
let added;
|
|
12503
|
+
try {
|
|
12504
|
+
added = registries.remember(missing);
|
|
12505
|
+
} catch (error) {
|
|
12506
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源,但补配置失败:${String(error)}`);
|
|
12507
|
+
return change;
|
|
12508
|
+
}
|
|
12509
|
+
if (added.length === 0) return change;
|
|
12510
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
12511
|
+
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
12512
|
+
}
|
|
12513
|
+
//#endregion
|
|
12514
|
+
//#region src/modules/panels/model/market-remove.ts
|
|
12515
|
+
var chain = Promise.resolve();
|
|
12516
|
+
function serialized(operation) {
|
|
12517
|
+
const run = chain.then(operation, operation);
|
|
12518
|
+
chain = run.then(() => void 0, () => void 0);
|
|
12519
|
+
return run;
|
|
12520
|
+
}
|
|
12521
|
+
function forgetSource(options) {
|
|
12522
|
+
const dir = options.profileDir;
|
|
12523
|
+
if (dir === void 0 || dir === null) return;
|
|
12524
|
+
try {
|
|
12525
|
+
forgetPackageSource(dir, options.pkg);
|
|
12526
|
+
} catch (error) {
|
|
12527
|
+
options.logger?.warn?.(`[dsh-workbench] 清 ${options.pkg} 的来源记录失败:${String(error)}`);
|
|
12528
|
+
}
|
|
12529
|
+
}
|
|
12530
|
+
function swapScopeFor(options) {
|
|
12531
|
+
const dir = options.profileDir;
|
|
12532
|
+
const notebook = options.registryFix.registries;
|
|
12533
|
+
if (dir === void 0 || dir === null || notebook?.swapScope === void 0) return void 0;
|
|
12534
|
+
const source = packageSourceOf(dir, options.pkg);
|
|
12535
|
+
const scope = scopeOf$2(options.pkg);
|
|
12536
|
+
if (source === void 0 || scope === null) return void 0;
|
|
12537
|
+
try {
|
|
12538
|
+
return notebook.swapScope(scope, source.registry);
|
|
12539
|
+
} catch (error) {
|
|
12540
|
+
options.logger?.warn?.(`[dsh-workbench] 临时把 ${scope} 指到 ${source.registry} 失败,卸载照走:${String(error)}`);
|
|
12541
|
+
return;
|
|
12542
|
+
}
|
|
12543
|
+
}
|
|
12544
|
+
async function removeOnce(options) {
|
|
12545
|
+
const { manager, pkg, registryFix, logger } = options;
|
|
12546
|
+
const first = await manager.remove(pkg);
|
|
12547
|
+
if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
|
|
12548
|
+
const settled = await retryAfterRegistryFix(registryFix, manager, pkg, first);
|
|
12549
|
+
if (settled.application !== "failed" && settled.application !== "cancelled") {
|
|
12550
|
+
forgetSource(options);
|
|
12551
|
+
return settled;
|
|
12552
|
+
}
|
|
12553
|
+
const put = await manager.setEnabled?.(pkg, true);
|
|
12554
|
+
logger?.warn?.(`[dsh-workbench] ${pkg} 没卸成(${settled.errorCode ?? settled.application})—— 已把它那一行放回名单,别让一次失败留在"装着但关着"`);
|
|
12555
|
+
return put ?? settled;
|
|
12556
|
+
}
|
|
12557
|
+
function removeDshBundle(options) {
|
|
12558
|
+
return serialized(async () => {
|
|
12559
|
+
const restore = swapScopeFor(options);
|
|
12560
|
+
try {
|
|
12561
|
+
return await removeOnce(options);
|
|
12562
|
+
} finally {
|
|
12563
|
+
try {
|
|
12564
|
+
restore?.();
|
|
12565
|
+
} catch (error) {
|
|
12566
|
+
options.logger?.warn?.(`[dsh-workbench] 还原 .npmrc 失败:${String(error)}`);
|
|
12567
|
+
}
|
|
12568
|
+
}
|
|
12569
|
+
});
|
|
12570
|
+
}
|
|
12571
|
+
//#endregion
|
|
12120
12572
|
//#region src/modules/panels/model/market-dependency-update.ts
|
|
12121
12573
|
async function updateDependencyRow(target, deps) {
|
|
12122
12574
|
if (target.conflict !== void 0) throw new Error(`这一行有两份来源,先统一声明再更新:${target.conflict}`);
|
|
@@ -12489,42 +12941,6 @@ function createEnvDropper(options) {
|
|
|
12489
12941
|
};
|
|
12490
12942
|
}
|
|
12491
12943
|
//#endregion
|
|
12492
|
-
//#region src/modules/panels/model/market-registry.ts
|
|
12493
|
-
async function rememberInstalledSource(deps, manager, pkg, registry) {
|
|
12494
|
-
const { registries, logger } = deps;
|
|
12495
|
-
if (registries === void 0 || registry === void 0) return;
|
|
12496
|
-
const scope = scopeOf$2(pkg);
|
|
12497
|
-
if (scope === null) return;
|
|
12498
|
-
const known = await manager.registries().catch(() => null);
|
|
12499
|
-
if (known !== null && sameRegistry(known.resolved, registry)) return;
|
|
12500
|
-
try {
|
|
12501
|
-
const added = registries.remember([{
|
|
12502
|
-
scope,
|
|
12503
|
-
registry
|
|
12504
|
-
}]);
|
|
12505
|
-
if (added.length > 0) logger?.info?.(`[dsh-workbench] 把 ${added.join("、")} 记进 profile 的 .npmrc(卸载只认 pnpm 的配置)`);
|
|
12506
|
-
} catch (error) {
|
|
12507
|
-
logger?.warn?.(`[dsh-workbench] 记这个 scope 的源失败:${String(error)}`);
|
|
12508
|
-
}
|
|
12509
|
-
}
|
|
12510
|
-
async function retryAfterRegistryFix(deps, manager, pkg, change) {
|
|
12511
|
-
const { registries, logger } = deps;
|
|
12512
|
-
if (registries === void 0 || !registryFailure(change)) return change;
|
|
12513
|
-
const known = await manager.registries().catch(() => null);
|
|
12514
|
-
const missing = registries.sources().filter((entry) => !sameRegistry(entry.registry, known?.resolved ?? null));
|
|
12515
|
-
if (missing.length === 0) return change;
|
|
12516
|
-
let added;
|
|
12517
|
-
try {
|
|
12518
|
-
added = registries.remember(missing);
|
|
12519
|
-
} catch (error) {
|
|
12520
|
-
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源,但补配置失败:${String(error)}`);
|
|
12521
|
-
return change;
|
|
12522
|
-
}
|
|
12523
|
-
if (added.length === 0) return change;
|
|
12524
|
-
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
12525
|
-
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
12526
|
-
}
|
|
12527
|
-
//#endregion
|
|
12528
12944
|
//#region src/modules/panels/model/market-restart.ts
|
|
12529
12945
|
function restartLogLine(change) {
|
|
12530
12946
|
if (change === null || change === void 0 || change.application !== "restart-required") return;
|
|
@@ -12534,7 +12950,7 @@ function restartLogLine(change) {
|
|
|
12534
12950
|
//#endregion
|
|
12535
12951
|
//#region src/modules/panels/model/market.ts
|
|
12536
12952
|
function createMarketService(options) {
|
|
12537
|
-
const { manifests, manifestsNow, probe, shell, logger, supply: supplyOption, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, ledger, references, dshHome, profileName } = options;
|
|
12953
|
+
const { manifests, manifestsNow, probe, shell, logger, supply: supplyOption, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, profileDir, ledger, references, dshHome, profileName } = options;
|
|
12538
12954
|
const supply = supplyOption ?? requireSupply();
|
|
12539
12955
|
const command = options.command ?? "opencli";
|
|
12540
12956
|
const catalog = () => manifestsNow?.() ?? manifests;
|
|
@@ -12573,11 +12989,18 @@ function createMarketService(options) {
|
|
|
12573
12989
|
}
|
|
12574
12990
|
async function run(line, optional = false) {
|
|
12575
12991
|
if (shell === void 0) throw new Error("没有执行缝(shell 服务不可用),装 / 卸做不了。");
|
|
12992
|
+
const blocked = installWriteProblem({
|
|
12993
|
+
command: line,
|
|
12994
|
+
fallback: isolatedRoot()
|
|
12995
|
+
});
|
|
12996
|
+
if (blocked !== void 0) throw installError(blocked);
|
|
12576
12997
|
const result = await runShell(shell, line, { timeoutMs: INSTALL_TIMEOUT_MS });
|
|
12577
|
-
if (result.exitCode !== 0 && !optional) {
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12998
|
+
if (result.exitCode !== 0 && !optional) throw installError(diagnoseInstallFailure({
|
|
12999
|
+
command: line,
|
|
13000
|
+
exitCode: result.exitCode,
|
|
13001
|
+
output: result.stderr || result.stdout,
|
|
13002
|
+
fallback: isolatedRoot()
|
|
13003
|
+
}));
|
|
12581
13004
|
}
|
|
12582
13005
|
async function runAll(commands) {
|
|
12583
13006
|
for (const step of commands) await run(step.line, step.optional === true);
|
|
@@ -12662,26 +13085,24 @@ function createMarketService(options) {
|
|
|
12662
13085
|
}
|
|
12663
13086
|
const registryFix = {
|
|
12664
13087
|
...registries === void 0 ? {} : { registries },
|
|
13088
|
+
...profileDir === void 0 || profileDir === null ? {} : { profileDir },
|
|
12665
13089
|
...logger === void 0 ? {} : { logger }
|
|
12666
13090
|
};
|
|
12667
|
-
async function removeInstalledBundle(manager, pkg) {
|
|
12668
|
-
const first = await manager.remove(pkg);
|
|
12669
|
-
if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
|
|
12670
|
-
const settled = await retryAfterRegistryFix(registryFix, manager, pkg, first);
|
|
12671
|
-
if (settled.application !== "failed" && settled.application !== "cancelled") return settled;
|
|
12672
|
-
const put = await manager.setEnabled?.(pkg, true);
|
|
12673
|
-
logger?.warn?.(`[dsh-workbench] ${pkg} 没卸成(${settled.errorCode ?? settled.application})—— 已把它那一行放回名单,别让一次失败留在"装着但关着"`);
|
|
12674
|
-
return put ?? settled;
|
|
12675
|
-
}
|
|
12676
13091
|
function routeDeps(manager) {
|
|
12677
13092
|
return {
|
|
12678
13093
|
manager,
|
|
12679
13094
|
command,
|
|
12680
13095
|
supply,
|
|
12681
13096
|
runAll,
|
|
12682
|
-
rememberSource: (manager, pkg, registry) => rememberInstalledSource(registryFix, manager, pkg, registry),
|
|
13097
|
+
rememberSource: (manager, pkg, registry, version) => rememberInstalledSource(registryFix, manager, pkg, registry, version),
|
|
12683
13098
|
supplyAfter,
|
|
12684
|
-
remove: (pkg) =>
|
|
13099
|
+
remove: (pkg) => removeDshBundle({
|
|
13100
|
+
manager,
|
|
13101
|
+
pkg,
|
|
13102
|
+
profileDir,
|
|
13103
|
+
registryFix,
|
|
13104
|
+
...logger === void 0 ? {} : { logger }
|
|
13105
|
+
}),
|
|
12685
13106
|
...shell === void 0 ? {} : { shell },
|
|
12686
13107
|
...installed === void 0 ? {} : { installed },
|
|
12687
13108
|
...ledger === void 0 ? {} : { ledger },
|
|
@@ -12716,7 +13137,7 @@ function createMarketService(options) {
|
|
|
12716
13137
|
return snapshot();
|
|
12717
13138
|
},
|
|
12718
13139
|
async install(unit) {
|
|
12719
|
-
const absent = (await capabilityOf(unit, true)).needs.filter((state) => state.status === "missing" && (state.kind === "extension" || state.kind === "lib"));
|
|
13140
|
+
const absent = (await capabilityOf(unit, true)).needs.filter((state) => state.status === "missing" && (state.kind === "extension" || state.kind === "lib" || state.kind === "bin"));
|
|
12720
13141
|
if (absent.length === 0) {
|
|
12721
13142
|
onInstalled?.(unit);
|
|
12722
13143
|
return entryOf(unit, true);
|
|
@@ -12728,6 +13149,8 @@ function createMarketService(options) {
|
|
|
12728
13149
|
}
|
|
12729
13150
|
if (state.command === void 0) throw new Error(`没有可执行的安装命令(${state.detail})`);
|
|
12730
13151
|
await run(state.command);
|
|
13152
|
+
ensureLibsOpencliPeer();
|
|
13153
|
+
ensureLibsBinOnPath();
|
|
12731
13154
|
}
|
|
12732
13155
|
await probeSharing({
|
|
12733
13156
|
action: "install",
|
|
@@ -12813,7 +13236,13 @@ function createMarketService(options) {
|
|
|
12813
13236
|
const unit = module.kind === "module" ? module.unit : void 0;
|
|
12814
13237
|
const manifest = unit === void 0 ? void 0 : catalog().find((item) => item.id === unit);
|
|
12815
13238
|
const before = unit === void 0 ? void 0 : await capabilityOf(unit, true);
|
|
12816
|
-
const change = await
|
|
13239
|
+
const change = await removeDshBundle({
|
|
13240
|
+
manager,
|
|
13241
|
+
pkg,
|
|
13242
|
+
profileDir,
|
|
13243
|
+
registryFix,
|
|
13244
|
+
...logger === void 0 ? {} : { logger }
|
|
13245
|
+
});
|
|
12817
13246
|
if (change.application !== "applied" || unit === void 0) return {
|
|
12818
13247
|
change,
|
|
12819
13248
|
keptFor: []
|
|
@@ -15342,6 +15771,7 @@ var OWNED = [{
|
|
|
15342
15771
|
modules: () => supply.profileModules(profileDir),
|
|
15343
15772
|
workbench: (pkg) => supply.readProfilePackage(profileDir, pkg),
|
|
15344
15773
|
registries: supply.createRegistryNotebook(profileDir),
|
|
15774
|
+
profileDir,
|
|
15345
15775
|
ledger,
|
|
15346
15776
|
references: () => {
|
|
15347
15777
|
const gaps = supply.declarationGaps(profileDir);
|
|
@@ -16878,6 +17308,8 @@ async function apply(ctx, config) {
|
|
|
16878
17308
|
}
|
|
16879
17309
|
if (shell === void 0) ctx.logger?.warn?.("[dsh-workbench] 没有 shell 服务:需要执行缝的套件会降级");
|
|
16880
17310
|
else {
|
|
17311
|
+
ensureLibsOpencliPeer();
|
|
17312
|
+
if (ensureLibsBinOnPath() && ctx.logger?.info !== void 0) ctx.logger.info("[dsh-workbench] PATH 前插共用依赖的 bin 目录:装了的包,命令直接可用");
|
|
16881
17313
|
adapters.probe.registerPreflight({
|
|
16882
17314
|
routes,
|
|
16883
17315
|
ctx,
|