@dsh-workbench/core 0.1.8 → 0.1.10
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 +983 -391
- package/dist/client/client.body.js +983 -391
- package/dist/client/index.js +984 -392
- package/dist/host/index.js +366 -106
- package/dist/types/app/client.d.ts +140 -12
- package/dist/types/app/layouts/env-gate/__tests__/gate-deadline.test.d.ts +1 -0
- package/dist/types/app/layouts/env-gate/gate-deadline.d.ts +4 -0
- package/dist/types/app/layouts/rightbar/MarketStates.d.ts +7 -0
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +2 -1
- package/dist/types/app/layouts/rightbar/SharedSection.d.ts +11 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-change.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-live.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/__tests__/market-settle.test.d.ts +1 -0
- package/dist/types/app/layouts/rightbar/board-plan.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/market-change.d.ts +4 -0
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +3 -1
- package/dist/types/app/layouts/rightbar/market-settle.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/market-view-text.d.ts +10 -0
- package/dist/types/app/layouts/sidebar/ProjectSwitcher.d.ts +2 -2
- package/dist/types/app/layouts/sidebar/Sidebar.d.ts +1 -1
- package/dist/types/app/layouts/sidebar/WorkspaceRenameDialog.d.ts +8 -0
- package/dist/types/app/layouts/sidebar/WorkspaceRowAction.d.ts +7 -0
- package/dist/types/app/layouts/sidebar/WorkspaceRowLabel.d.ts +8 -0
- package/dist/types/app/layouts/sidebar/WorkspaceSearchField.d.ts +9 -0
- package/dist/types/app/layouts/sidebar/__tests__/workspace-name.test.d.ts +1 -0
- package/dist/types/app/layouts/sidebar/workspace-items.d.ts +11 -0
- package/dist/types/app/layouts/sidebar/workspace-name.d.ts +16 -0
- package/dist/types/app/units/__tests__/registry.test.d.ts +1 -0
- package/dist/types/app/units/fixture-unit-mock.d.ts +3 -1
- package/dist/types/modules/panels/__tests__/market-registries.test.d.ts +1 -0
- package/dist/types/modules/panels/model/market.d.ts +2 -0
- package/dist/types/shared/api/host/__tests__/profile-registries.test.d.ts +1 -0
- package/dist/types/shared/api/host/mount-units.d.ts +13 -6
- package/dist/types/shared/api/host/profile-registries.d.ts +21 -0
- package/dist/types/shared/hooks/mock-mode.d.ts +1 -0
- package/docs/OPERATIONS.md +2 -0
- package/package.json +2 -2
package/dist/host/index.js
CHANGED
|
@@ -1380,7 +1380,7 @@ function readBundleManager(ctx) {
|
|
|
1380
1380
|
}
|
|
1381
1381
|
//#endregion
|
|
1382
1382
|
//#region src/shared/api/host/installed-module.ts
|
|
1383
|
-
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
1383
|
+
var PACKAGE_NAME$1 = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
1384
1384
|
function asRecord$1(value) {
|
|
1385
1385
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
1386
1386
|
}
|
|
@@ -1388,7 +1388,7 @@ function asText$1(value) {
|
|
|
1388
1388
|
return typeof value === "string" ? value : "";
|
|
1389
1389
|
}
|
|
1390
1390
|
function isPackageName(value) {
|
|
1391
|
-
return value.length <= 214 && PACKAGE_NAME.test(value);
|
|
1391
|
+
return value.length <= 214 && PACKAGE_NAME$1.test(value);
|
|
1392
1392
|
}
|
|
1393
1393
|
function profileDirOf(ctx) {
|
|
1394
1394
|
const profile = ctx.get("profileContext");
|
|
@@ -1477,6 +1477,118 @@ function readInstalledModule(profileDir, pkg) {
|
|
|
1477
1477
|
};
|
|
1478
1478
|
}
|
|
1479
1479
|
//#endregion
|
|
1480
|
+
//#region src/shared/api/host/profile-registries.ts
|
|
1481
|
+
var REGISTRY_MARKER = "# dsh-workbench 写的:这一行的包只在这个源上(dsh 卸载没有源参数,pnpm 得问得到)";
|
|
1482
|
+
function scopeOf(pkg) {
|
|
1483
|
+
const slash = pkg.indexOf("/");
|
|
1484
|
+
return pkg.startsWith("@") && slash > 1 ? pkg.slice(0, slash) : null;
|
|
1485
|
+
}
|
|
1486
|
+
function normalizeRegistry(url) {
|
|
1487
|
+
const trimmed = url.trim();
|
|
1488
|
+
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
1489
|
+
}
|
|
1490
|
+
function sameRegistry(a, b) {
|
|
1491
|
+
if (a === null || a === void 0 || b === null || b === void 0) return false;
|
|
1492
|
+
if (a.trim() === "" || b.trim() === "") return false;
|
|
1493
|
+
return normalizeRegistry(a) === normalizeRegistry(b);
|
|
1494
|
+
}
|
|
1495
|
+
function registryRootOf(name, tarball) {
|
|
1496
|
+
const at = tarball.indexOf(`/${name}/-/`);
|
|
1497
|
+
if (at > 0) return normalizeRegistry(tarball.slice(0, at));
|
|
1498
|
+
try {
|
|
1499
|
+
return normalizeRegistry(new URL(tarball).origin);
|
|
1500
|
+
} catch {
|
|
1501
|
+
return null;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
var PACKAGE_KEY = /^ {2}'?(\S+)'?:\s*$/;
|
|
1505
|
+
var PACKAGE_NAME = /^(?:@[^/\s]+\/)?[^/\s@]+$/;
|
|
1506
|
+
var TARBALL = /tarball:\s*(\S+?)\}?\s*$/;
|
|
1507
|
+
function sourcesFromLockfile(text) {
|
|
1508
|
+
const found = /* @__PURE__ */ new Map();
|
|
1509
|
+
let name = null;
|
|
1510
|
+
for (const line of text.split("\n")) {
|
|
1511
|
+
const key = PACKAGE_KEY.exec(line);
|
|
1512
|
+
if (key !== null) {
|
|
1513
|
+
const raw = key[1] ?? "";
|
|
1514
|
+
const at = raw.lastIndexOf("@");
|
|
1515
|
+
const candidate = at > 0 ? raw.slice(0, at) : "";
|
|
1516
|
+
name = PACKAGE_NAME.test(candidate) ? candidate : null;
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
if (name === null) continue;
|
|
1520
|
+
const tarball = TARBALL.exec(line);
|
|
1521
|
+
if (tarball === null) continue;
|
|
1522
|
+
const scope = scopeOf(name);
|
|
1523
|
+
const root = registryRootOf(name, tarball[1] ?? "");
|
|
1524
|
+
name = null;
|
|
1525
|
+
if (scope === null || root === null) continue;
|
|
1526
|
+
const seen = found.get(scope);
|
|
1527
|
+
if (seen === void 0) found.set(scope, root);
|
|
1528
|
+
else if (!sameRegistry(seen, root)) found.set(scope, null);
|
|
1529
|
+
}
|
|
1530
|
+
return [...found.entries()].filter((entry) => entry[1] !== null).map(([scope, registry]) => ({
|
|
1531
|
+
scope,
|
|
1532
|
+
registry
|
|
1533
|
+
}));
|
|
1534
|
+
}
|
|
1535
|
+
function mergeRegistryConfig(text, entries) {
|
|
1536
|
+
const lines = text.split("\n");
|
|
1537
|
+
const present = (key) => lines.some((line) => line.trim().startsWith(`${key}=`));
|
|
1538
|
+
const appended = [];
|
|
1539
|
+
const added = [];
|
|
1540
|
+
for (const entry of entries) {
|
|
1541
|
+
const key = `${entry.scope}:registry`;
|
|
1542
|
+
if (entry.registry.trim() === "" || present(key)) continue;
|
|
1543
|
+
lines.push(`${key}=${normalizeRegistry(entry.registry)}`);
|
|
1544
|
+
appended.push(`${key}=${normalizeRegistry(entry.registry)}`);
|
|
1545
|
+
added.push(key);
|
|
1546
|
+
}
|
|
1547
|
+
if (appended.length === 0) return {
|
|
1548
|
+
text,
|
|
1549
|
+
added: []
|
|
1550
|
+
};
|
|
1551
|
+
const base = text === "" ? "" : text.endsWith("\n") ? text : `${text}\n`;
|
|
1552
|
+
return {
|
|
1553
|
+
text: `${base}${base.includes("# dsh-workbench 写的:这一行的包只在这个源上(dsh 卸载没有源参数,pnpm 得问得到)") ? "" : `${REGISTRY_MARKER}\n`}${appended.join("\n")}\n`,
|
|
1554
|
+
added
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
function registryFailure(change) {
|
|
1558
|
+
if (change.application !== "failed") return false;
|
|
1559
|
+
if (change.packageResult?.kind === "not-found") return true;
|
|
1560
|
+
const output = change.packageResult?.output ?? change.errorDetail ?? "";
|
|
1561
|
+
return /ERR_PNPM_[A-Z_]*FETCH/.test(output);
|
|
1562
|
+
}
|
|
1563
|
+
function createRegistryNotebook(dir) {
|
|
1564
|
+
return {
|
|
1565
|
+
sources() {
|
|
1566
|
+
if (dir === null) return [];
|
|
1567
|
+
try {
|
|
1568
|
+
return sourcesFromLockfile(fs.readFileSync(path.join(dir, "pnpm-lock.yaml"), "utf8"));
|
|
1569
|
+
} catch {
|
|
1570
|
+
return [];
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
remember(entries) {
|
|
1574
|
+
if (dir === null || entries.length === 0) return [];
|
|
1575
|
+
const file = path.join(dir, ".npmrc");
|
|
1576
|
+
let before;
|
|
1577
|
+
try {
|
|
1578
|
+
before = fs.readFileSync(file, "utf8");
|
|
1579
|
+
} catch {
|
|
1580
|
+
before = "";
|
|
1581
|
+
}
|
|
1582
|
+
const merged = mergeRegistryConfig(before, entries);
|
|
1583
|
+
if (merged.added.length === 0) return [];
|
|
1584
|
+
const temp = `${file}.dsh-workbench.tmp`;
|
|
1585
|
+
fs.writeFileSync(temp, merged.text, { mode: 384 });
|
|
1586
|
+
fs.renameSync(temp, file);
|
|
1587
|
+
return merged.added;
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
//#endregion
|
|
1480
1592
|
//#region src/shared/api/host/release.ts
|
|
1481
1593
|
function parseLedgerVersion(stdout, name) {
|
|
1482
1594
|
const parsed = parseJsonLoose(stdout);
|
|
@@ -1703,7 +1815,7 @@ function bundleNameOf(change, spec) {
|
|
|
1703
1815
|
return at > 0 ? spec.slice(0, at) : spec;
|
|
1704
1816
|
}
|
|
1705
1817
|
function createMarketService(options) {
|
|
1706
|
-
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules } = options;
|
|
1818
|
+
const { manifests, manifestsNow, probe, shell, logger, onInstalled, onUninstalled, isReleased, bundles, installed, modules, registries } = options;
|
|
1707
1819
|
const command = options.command ?? "opencli";
|
|
1708
1820
|
const catalog = () => (manifestsNow?.() ?? manifests).filter((manifest) => (manifest.env?.plugins ?? []).length > 0 || (manifest.env?.libs ?? []).length > 0);
|
|
1709
1821
|
function requireEntry(unit) {
|
|
@@ -1850,6 +1962,38 @@ function createMarketService(options) {
|
|
|
1850
1962
|
};
|
|
1851
1963
|
}
|
|
1852
1964
|
}
|
|
1965
|
+
async function rememberInstalledSource(manager, pkg, registry) {
|
|
1966
|
+
if (registries === void 0 || registry === void 0) return;
|
|
1967
|
+
const scope = scopeOf(pkg);
|
|
1968
|
+
if (scope === null) return;
|
|
1969
|
+
const known = await manager.registries().catch(() => null);
|
|
1970
|
+
if (known !== null && sameRegistry(known.resolved, registry)) return;
|
|
1971
|
+
try {
|
|
1972
|
+
const added = registries.remember([{
|
|
1973
|
+
scope,
|
|
1974
|
+
registry
|
|
1975
|
+
}]);
|
|
1976
|
+
if (added.length > 0) logger?.info?.(`[dsh-workbench] 把 ${added.join("、")} 记进 profile 的 .npmrc(卸载只认 pnpm 的配置)`);
|
|
1977
|
+
} catch (error) {
|
|
1978
|
+
logger?.warn?.(`[dsh-workbench] 记这个 scope 的源失败:${String(error)}`);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
async function retryAfterRegistryFix(manager, pkg, change) {
|
|
1982
|
+
if (registries === void 0 || !registryFailure(change)) return change;
|
|
1983
|
+
const known = await manager.registries().catch(() => null);
|
|
1984
|
+
const missing = registries.sources().filter((entry) => !sameRegistry(entry.registry, known?.resolved ?? null));
|
|
1985
|
+
if (missing.length === 0) return change;
|
|
1986
|
+
let added;
|
|
1987
|
+
try {
|
|
1988
|
+
added = registries.remember(missing);
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源,但补配置失败:${String(error)}`);
|
|
1991
|
+
return change;
|
|
1992
|
+
}
|
|
1993
|
+
if (added.length === 0) return change;
|
|
1994
|
+
logger?.warn?.(`[dsh-workbench] 卸 ${pkg} 撞上源:补 ${added.join("、")} 后重试一次`);
|
|
1995
|
+
return await manager.remove(pkg).catch(() => null) ?? change;
|
|
1996
|
+
}
|
|
1853
1997
|
const service = {
|
|
1854
1998
|
async list() {
|
|
1855
1999
|
return snapshot();
|
|
@@ -1955,7 +2099,8 @@ function createMarketService(options) {
|
|
|
1955
2099
|
};
|
|
1956
2100
|
},
|
|
1957
2101
|
async installBundle(spec, options) {
|
|
1958
|
-
const
|
|
2102
|
+
const manager = requireBundles();
|
|
2103
|
+
const change = await manager.install({
|
|
1959
2104
|
spec,
|
|
1960
2105
|
...options?.registry === void 0 ? {} : { registry: options.registry },
|
|
1961
2106
|
...options?.requestId === void 0 ? {} : { requestId: options.requestId },
|
|
@@ -1967,6 +2112,7 @@ function createMarketService(options) {
|
|
|
1967
2112
|
kind: "unknown",
|
|
1968
2113
|
pkg
|
|
1969
2114
|
};
|
|
2115
|
+
await rememberInstalledSource(manager, pkg, options?.registry);
|
|
1970
2116
|
return {
|
|
1971
2117
|
change,
|
|
1972
2118
|
module,
|
|
@@ -1982,8 +2128,9 @@ function createMarketService(options) {
|
|
|
1982
2128
|
const unit = module.kind === "module" ? module.unit : void 0;
|
|
1983
2129
|
const manifest = unit === void 0 ? void 0 : catalog().find((item) => item.id === unit);
|
|
1984
2130
|
const before = unit === void 0 ? void 0 : await capabilityOf(unit, true);
|
|
1985
|
-
const
|
|
1986
|
-
if (
|
|
2131
|
+
const first = await manager.remove(pkg);
|
|
2132
|
+
if (first === null) throw new Error("插件管理器回的卸载结果看不懂。");
|
|
2133
|
+
const change = await retryAfterRegistryFix(manager, pkg, first);
|
|
1987
2134
|
if (change.application !== "applied" || unit === void 0) return {
|
|
1988
2135
|
change,
|
|
1989
2136
|
keptFor: []
|
|
@@ -2682,6 +2829,7 @@ var HOST_UNITS = [{
|
|
|
2682
2829
|
bundles: readBundleManager(ctx),
|
|
2683
2830
|
installed: (pkg) => readInstalledModule(profileDir, pkg),
|
|
2684
2831
|
modules: () => profileModules(profileDir),
|
|
2832
|
+
registries: createRegistryNotebook(profileDir),
|
|
2685
2833
|
onUninstalled: (unit) => {
|
|
2686
2834
|
panels.release(unit);
|
|
2687
2835
|
},
|
|
@@ -3640,16 +3788,43 @@ function withRunsRoute(result, unit) {
|
|
|
3640
3788
|
}
|
|
3641
3789
|
};
|
|
3642
3790
|
}
|
|
3643
|
-
function
|
|
3791
|
+
function failureReasonOf(error) {
|
|
3792
|
+
if (error instanceof Error) return error.message;
|
|
3793
|
+
return typeof error === "string" ? error : String(error);
|
|
3794
|
+
}
|
|
3795
|
+
function fail(ledger, item, reason) {
|
|
3796
|
+
ledger.failed.add(item);
|
|
3797
|
+
ledger.failures.push({
|
|
3798
|
+
id: item.face.manifest.id,
|
|
3799
|
+
reason
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
function collectRoutes(mounted, ledger) {
|
|
3644
3803
|
const routes = {};
|
|
3645
3804
|
const owner = /* @__PURE__ */ new Map();
|
|
3646
3805
|
const routesOfUnit = /* @__PURE__ */ new Map();
|
|
3647
3806
|
for (const item of mounted) {
|
|
3807
|
+
if (ledger.failed.has(item)) continue;
|
|
3648
3808
|
const unit = item.face.manifest.id;
|
|
3809
|
+
const accepted = [];
|
|
3810
|
+
let problem;
|
|
3649
3811
|
for (const [key, handler] of Object.entries(item.result.routes)) {
|
|
3650
|
-
if (key !== unit && !key.startsWith(`${unit}/`))
|
|
3812
|
+
if (key !== unit && !key.startsWith(`${unit}/`)) {
|
|
3813
|
+
problem = `路由名要住在自己那一格的命名空间里(${unit} 或 ${unit}/…):${key} 不属于 ${unit} —— 装 / 升 / 卸时点名不到它,那个模块的数据会停在旧值`;
|
|
3814
|
+
break;
|
|
3815
|
+
}
|
|
3651
3816
|
const previous = owner.get(key);
|
|
3652
|
-
if (previous !== void 0)
|
|
3817
|
+
if (previous !== void 0) {
|
|
3818
|
+
problem = `路由名冲突:${key} 由套件 ${previous} 与 ${unit} 同时声明`;
|
|
3819
|
+
break;
|
|
3820
|
+
}
|
|
3821
|
+
accepted.push([key, handler]);
|
|
3822
|
+
}
|
|
3823
|
+
if (problem !== void 0) {
|
|
3824
|
+
fail(ledger, item, problem);
|
|
3825
|
+
continue;
|
|
3826
|
+
}
|
|
3827
|
+
for (const [key, handler] of accepted) {
|
|
3653
3828
|
owner.set(key, unit);
|
|
3654
3829
|
routes[key] = handler;
|
|
3655
3830
|
const own = routesOfUnit.get(unit);
|
|
@@ -3663,45 +3838,64 @@ function collectRoutes(mounted) {
|
|
|
3663
3838
|
routesOfUnit
|
|
3664
3839
|
};
|
|
3665
3840
|
}
|
|
3666
|
-
function
|
|
3667
|
-
const providers = mounted.filter((item) => item.result.gate !== void 0);
|
|
3668
|
-
if (providers.length > 1) throw new Error(`启停闸门只能有一个提供者,现在是:${providers.map((item) => item.face.manifest.id).join("、")}`);
|
|
3669
|
-
return providers[0]?.result.gate;
|
|
3670
|
-
}
|
|
3671
|
-
function pickProvision(mounted) {
|
|
3672
|
-
const providers = mounted.filter((item) => item.result.provision !== void 0);
|
|
3673
|
-
if (providers.length > 1) throw new Error(`机器供给只能有一个提供者,现在是:${providers.map((item) => item.face.manifest.id).join("、")}`);
|
|
3674
|
-
return providers[0]?.result.provision;
|
|
3675
|
-
}
|
|
3676
|
-
function collectPrefs(mounted) {
|
|
3841
|
+
function collectNamed(mounted, ledger, pickMany, label) {
|
|
3677
3842
|
const owner = /* @__PURE__ */ new Map();
|
|
3678
|
-
const
|
|
3843
|
+
const out = [];
|
|
3679
3844
|
for (const item of mounted) {
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
const
|
|
3683
|
-
if (
|
|
3684
|
-
owner.
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
}
|
|
3845
|
+
if (ledger.failed.has(item)) continue;
|
|
3846
|
+
const unit = item.face.manifest.id;
|
|
3847
|
+
const entries = pickMany(item);
|
|
3848
|
+
if (entries.length === 0) continue;
|
|
3849
|
+
const clash = entries.find((entry) => owner.has(entry.resource));
|
|
3850
|
+
if (clash !== void 0) {
|
|
3851
|
+
fail(ledger, item, `${label}名冲突:${clash.resource} 由套件 ${owner.get(clash.resource)} 与 ${unit} 同时声明`);
|
|
3852
|
+
continue;
|
|
3853
|
+
}
|
|
3854
|
+
for (const entry of entries) {
|
|
3855
|
+
owner.set(entry.resource, unit);
|
|
3856
|
+
out.push({
|
|
3857
|
+
...entry,
|
|
3858
|
+
unit
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3689
3861
|
}
|
|
3690
|
-
return
|
|
3862
|
+
return out;
|
|
3691
3863
|
}
|
|
3692
|
-
function
|
|
3864
|
+
function collectContextRenderers(mounted, ledger) {
|
|
3865
|
+
const table = /* @__PURE__ */ new Map();
|
|
3693
3866
|
const owner = /* @__PURE__ */ new Map();
|
|
3694
|
-
const
|
|
3695
|
-
|
|
3696
|
-
const
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3867
|
+
for (const item of mounted) {
|
|
3868
|
+
if (ledger.failed.has(item)) continue;
|
|
3869
|
+
const unit = item.face.manifest.id;
|
|
3870
|
+
const context = item.result.context;
|
|
3871
|
+
if (context === void 0) continue;
|
|
3872
|
+
const clash = context.kinds.find((kind) => owner.has(kind));
|
|
3873
|
+
if (clash !== void 0) {
|
|
3874
|
+
fail(ledger, item, `对话引用的 kind 冲突:${clash} 由套件 ${owner.get(clash)} 与 ${unit} 同时认领`);
|
|
3875
|
+
continue;
|
|
3876
|
+
}
|
|
3877
|
+
for (const kind of context.kinds) {
|
|
3878
|
+
owner.set(kind, unit);
|
|
3879
|
+
table.set(kind, (ref) => context.render(ref));
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
return table;
|
|
3883
|
+
}
|
|
3884
|
+
function pickProvider(mounted, ledger, pick, label) {
|
|
3885
|
+
let chosen;
|
|
3886
|
+
let chosenBy;
|
|
3887
|
+
for (const item of mounted) {
|
|
3888
|
+
if (ledger.failed.has(item)) continue;
|
|
3889
|
+
const value = pick(item);
|
|
3890
|
+
if (value === void 0) continue;
|
|
3891
|
+
if (chosen === void 0) {
|
|
3892
|
+
chosen = value;
|
|
3893
|
+
chosenBy = item.face.manifest.id;
|
|
3894
|
+
continue;
|
|
3895
|
+
}
|
|
3896
|
+
fail(ledger, item, `${label}只能有一个提供者,现在是:${chosenBy}、${item.face.manifest.id}`);
|
|
3703
3897
|
}
|
|
3704
|
-
return
|
|
3898
|
+
return chosen;
|
|
3705
3899
|
}
|
|
3706
3900
|
function collectWarmReads(mounted) {
|
|
3707
3901
|
const declarations = mounted.map((item) => item.result.warmReads).filter((declare) => declare !== void 0);
|
|
@@ -3717,26 +3911,15 @@ function collectWarmReads(mounted) {
|
|
|
3717
3911
|
return reads;
|
|
3718
3912
|
};
|
|
3719
3913
|
}
|
|
3720
|
-
function collectContextRenderers(mounted) {
|
|
3721
|
-
const table = /* @__PURE__ */ new Map();
|
|
3722
|
-
const owner = /* @__PURE__ */ new Map();
|
|
3723
|
-
for (const item of mounted) {
|
|
3724
|
-
const context = item.result.context;
|
|
3725
|
-
if (context === void 0) continue;
|
|
3726
|
-
for (const kind of context.kinds) {
|
|
3727
|
-
const previous = owner.get(kind);
|
|
3728
|
-
if (previous !== void 0) throw new Error(`对话引用的 kind 冲突:${kind} 由套件 ${previous} 与 ${item.face.manifest.id} 同时认领`);
|
|
3729
|
-
owner.set(kind, item.face.manifest.id);
|
|
3730
|
-
table.set(kind, (ref) => context.render(ref));
|
|
3731
|
-
}
|
|
3732
|
-
}
|
|
3733
|
-
return table;
|
|
3734
|
-
}
|
|
3735
3914
|
function mountUnits(faces, deps) {
|
|
3736
3915
|
const store = deps.store ?? createWorkspaceStore({ root: null });
|
|
3737
3916
|
const mounted = [];
|
|
3738
3917
|
const skipped = [];
|
|
3739
|
-
const longTaskDisposers =
|
|
3918
|
+
const longTaskDisposers = /* @__PURE__ */ new Map();
|
|
3919
|
+
const ledger = {
|
|
3920
|
+
failed: /* @__PURE__ */ new Set(),
|
|
3921
|
+
failures: []
|
|
3922
|
+
};
|
|
3740
3923
|
for (const face of faces) {
|
|
3741
3924
|
const readiness = unitReadiness(face.manifest, (dep) => deps.ctx.get(dep));
|
|
3742
3925
|
if (!readiness.canMount) {
|
|
@@ -3752,48 +3935,71 @@ function mountUnits(faces, deps) {
|
|
|
3752
3935
|
unit,
|
|
3753
3936
|
bump: deps.longTasks.bump
|
|
3754
3937
|
});
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3938
|
+
let result;
|
|
3939
|
+
try {
|
|
3940
|
+
result = face.mount({
|
|
3941
|
+
ctx: deps.ctx,
|
|
3942
|
+
logger: deps.logger,
|
|
3943
|
+
manifests: deps.manifests,
|
|
3944
|
+
...deps.opencliCommand === void 0 ? {} : { opencliCommand: deps.opencliCommand },
|
|
3945
|
+
...deps.probe === void 0 ? {} : { probe: deps.probe },
|
|
3946
|
+
...deps.values === void 0 ? {} : { values: deps.values },
|
|
3947
|
+
...longTasks === void 0 ? {} : { longTasks: longTasks.host },
|
|
3948
|
+
store
|
|
3949
|
+
});
|
|
3950
|
+
} catch (error) {
|
|
3951
|
+
longTasks?.dispose();
|
|
3952
|
+
ledger.failures.push({
|
|
3953
|
+
id: unit,
|
|
3954
|
+
reason: failureReasonOf(error)
|
|
3955
|
+
});
|
|
3956
|
+
continue;
|
|
3957
|
+
}
|
|
3958
|
+
const item = {
|
|
3767
3959
|
face,
|
|
3768
3960
|
readiness,
|
|
3769
3961
|
result: withRunsRoute(result, unit)
|
|
3770
|
-
}
|
|
3962
|
+
};
|
|
3963
|
+
mounted.push(item);
|
|
3964
|
+
if (longTasks !== void 0) longTaskDisposers.set(item, longTasks.dispose);
|
|
3771
3965
|
}
|
|
3772
|
-
const collected = collectRoutes(mounted);
|
|
3773
|
-
const prefs =
|
|
3774
|
-
const actions =
|
|
3966
|
+
const collected = collectRoutes(mounted, ledger);
|
|
3967
|
+
const prefs = collectNamed(mounted, ledger, (item) => item.result.prefs === void 0 ? [] : [item.result.prefs], "偏好资源");
|
|
3968
|
+
const actions = collectNamed(mounted, ledger, (item) => item.result.actions ?? [], "动作资源");
|
|
3775
3969
|
const owners = new Map(collected.owners);
|
|
3776
3970
|
for (const entry of [...prefs, ...actions]) owners.set(entry.resource, entry.unit);
|
|
3777
3971
|
const factsOf = /* @__PURE__ */ new Map();
|
|
3778
3972
|
for (const item of mounted) {
|
|
3973
|
+
if (ledger.failed.has(item)) continue;
|
|
3779
3974
|
const declare = item.result.factsOf;
|
|
3780
3975
|
if (declare !== void 0) factsOf.set(item.face.manifest.id, declare);
|
|
3781
3976
|
}
|
|
3977
|
+
const gate = pickProvider(mounted, ledger, (item) => item.result.gate, "启停闸门");
|
|
3978
|
+
const provision = pickProvider(mounted, ledger, (item) => item.result.provision, "机器供给");
|
|
3979
|
+
const contextRenderers = collectContextRenderers(mounted, ledger);
|
|
3980
|
+
const alive = mounted.filter((item) => !ledger.failed.has(item));
|
|
3981
|
+
for (const item of mounted) {
|
|
3982
|
+
if (!ledger.failed.has(item)) continue;
|
|
3983
|
+
longTaskDisposers.get(item)?.();
|
|
3984
|
+
longTaskDisposers.delete(item);
|
|
3985
|
+
}
|
|
3782
3986
|
return {
|
|
3783
|
-
mounted,
|
|
3987
|
+
mounted: alive,
|
|
3784
3988
|
routes: collected.routes,
|
|
3785
3989
|
owners,
|
|
3786
3990
|
routesOfUnit: collected.routesOfUnit,
|
|
3787
3991
|
factsOf,
|
|
3788
|
-
gate
|
|
3992
|
+
gate,
|
|
3789
3993
|
prefs,
|
|
3790
3994
|
actions,
|
|
3791
|
-
provision
|
|
3792
|
-
contextRenderers
|
|
3995
|
+
provision,
|
|
3996
|
+
contextRenderers,
|
|
3793
3997
|
skipped,
|
|
3794
|
-
|
|
3998
|
+
failed: ledger.failures,
|
|
3999
|
+
warmReads: collectWarmReads(alive),
|
|
3795
4000
|
disposeLongTasks: () => {
|
|
3796
|
-
for (const dispose of longTaskDisposers.
|
|
4001
|
+
for (const dispose of [...longTaskDisposers.values()]) dispose();
|
|
4002
|
+
longTaskDisposers.clear();
|
|
3797
4003
|
}
|
|
3798
4004
|
};
|
|
3799
4005
|
}
|
|
@@ -3968,6 +4174,7 @@ function apply(ctx) {
|
|
|
3968
4174
|
logger: ctx.logger
|
|
3969
4175
|
});
|
|
3970
4176
|
const mounted = /* @__PURE__ */ new Map();
|
|
4177
|
+
const mountFailures = /* @__PURE__ */ new Map();
|
|
3971
4178
|
const factsOfMap = /* @__PURE__ */ new Map();
|
|
3972
4179
|
const routesOfUnitMap = /* @__PURE__ */ new Map();
|
|
3973
4180
|
const ownersMap = /* @__PURE__ */ new Map();
|
|
@@ -3979,6 +4186,7 @@ function apply(ctx) {
|
|
|
3979
4186
|
const item = mounted.get(id);
|
|
3980
4187
|
if (item === void 0) return;
|
|
3981
4188
|
mounted.delete(id);
|
|
4189
|
+
mountFailures.delete(id);
|
|
3982
4190
|
for (const dispose of item.toolDisposers) dispose?.();
|
|
3983
4191
|
item.gateOff?.();
|
|
3984
4192
|
item.plan.disposeLongTasks();
|
|
@@ -3989,34 +4197,72 @@ function apply(ctx) {
|
|
|
3989
4197
|
for (const resource of item.plan.owners.keys()) ownersMap.delete(resource);
|
|
3990
4198
|
}
|
|
3991
4199
|
function mountUnit(face) {
|
|
3992
|
-
const
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4200
|
+
const id = face.manifest.id;
|
|
4201
|
+
let one;
|
|
4202
|
+
try {
|
|
4203
|
+
one = mountUnits([face], {
|
|
4204
|
+
ctx,
|
|
4205
|
+
logger: ctx.logger,
|
|
4206
|
+
manifests: liveManifests,
|
|
4207
|
+
store,
|
|
4208
|
+
...opencliEnv === void 0 ? {} : { opencliCommand: opencliEnv },
|
|
4209
|
+
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
4210
|
+
values,
|
|
4211
|
+
longTasks: {
|
|
4212
|
+
scope: process.env["DSH_HOME"] ?? "",
|
|
4213
|
+
bump: (names) => facts.bump(names)
|
|
4214
|
+
}
|
|
4215
|
+
});
|
|
4216
|
+
} catch (error) {
|
|
4217
|
+
mountFailures.set(id, {
|
|
4218
|
+
reason: failureReasonOf(error),
|
|
4219
|
+
face
|
|
4220
|
+
});
|
|
4221
|
+
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(装配期):${failureReasonOf(error)}`);
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4005
4224
|
for (const item of one.skipped) ctx.logger?.warn?.(`[dsh-workbench] ${item.id} 套件缺硬依赖,跳过:${item.missing.join(", ")}`);
|
|
4225
|
+
for (const failure of one.failed) {
|
|
4226
|
+
mountFailures.set(failure.id, {
|
|
4227
|
+
reason: failure.reason,
|
|
4228
|
+
face
|
|
4229
|
+
});
|
|
4230
|
+
ctx.logger?.warn?.(`[dsh-workbench] ${failure.id} 套件起不来,已摘掉它这一份:${failure.reason}`);
|
|
4231
|
+
}
|
|
4006
4232
|
const slot = one.mounted[0];
|
|
4007
|
-
if (slot === void 0)
|
|
4233
|
+
if (slot === void 0) {
|
|
4234
|
+
one.disposeLongTasks();
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4008
4237
|
if (slot.readiness.missingSoft.length > 0) ctx.logger?.warn?.(`[dsh-workbench] ${face.manifest.id} 套件降级:缺 ${slot.readiness.missingSoft.join(", ")}`);
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4238
|
+
const added = [];
|
|
4239
|
+
try {
|
|
4240
|
+
Object.assign(routes, one.routes);
|
|
4241
|
+
added.push(...Object.keys(one.routes));
|
|
4242
|
+
for (const [kind, render] of one.contextRenderers) renderers.set(kind, render);
|
|
4243
|
+
for (const [unit, fn] of one.factsOf) factsOfMap.set(unit, fn);
|
|
4244
|
+
for (const [unit, list] of one.routesOfUnit) routesOfUnitMap.set(unit, list);
|
|
4245
|
+
for (const [resource, unit] of one.owners) ownersMap.set(resource, unit);
|
|
4246
|
+
mounted.set(face.manifest.id, {
|
|
4247
|
+
result: slot.result,
|
|
4248
|
+
plan: one,
|
|
4249
|
+
toolDisposers: slot.result.registerTools(),
|
|
4250
|
+
gateOff: one.gate?.subscribe(() => toolScope.reapply())
|
|
4251
|
+
});
|
|
4252
|
+
mountFailures.delete(id);
|
|
4253
|
+
} catch (error) {
|
|
4254
|
+
for (const route of added) delete routes[route];
|
|
4255
|
+
for (const kind of one.contextRenderers.keys()) renderers.delete(kind);
|
|
4256
|
+
for (const unit of one.factsOf.keys()) factsOfMap.delete(unit);
|
|
4257
|
+
for (const unit of one.routesOfUnit.keys()) routesOfUnitMap.delete(unit);
|
|
4258
|
+
for (const [resource, unit] of one.owners) if (ownersMap.get(resource) === unit) ownersMap.delete(resource);
|
|
4259
|
+
one.disposeLongTasks();
|
|
4260
|
+
mountFailures.set(id, {
|
|
4261
|
+
reason: failureReasonOf(error),
|
|
4262
|
+
face
|
|
4263
|
+
});
|
|
4264
|
+
ctx.logger?.warn?.(`[dsh-workbench] ${id} 套件起不来(注册期):${failureReasonOf(error)}`);
|
|
4265
|
+
}
|
|
4020
4266
|
}
|
|
4021
4267
|
function rebindPost() {
|
|
4022
4268
|
for (const dispose of undoPost.reverse()) dispose?.();
|
|
@@ -4039,7 +4285,13 @@ function apply(ctx) {
|
|
|
4039
4285
|
function sync() {
|
|
4040
4286
|
const present = new Set(units.all().map((face) => face.manifest.id));
|
|
4041
4287
|
for (const id of [...mounted.keys()]) if (!present.has(id)) unmountUnit(id);
|
|
4042
|
-
for (const
|
|
4288
|
+
for (const id of [...mountFailures.keys()]) if (!present.has(id)) mountFailures.delete(id);
|
|
4289
|
+
for (const face of units.all()) {
|
|
4290
|
+
if (mounted.has(face.manifest.id)) continue;
|
|
4291
|
+
const already = mountFailures.get(face.manifest.id);
|
|
4292
|
+
if (already !== void 0 && already.face === face) continue;
|
|
4293
|
+
mountUnit(face);
|
|
4294
|
+
}
|
|
4043
4295
|
rebindPost();
|
|
4044
4296
|
}
|
|
4045
4297
|
if (shell === void 0) ctx.logger?.warn?.("[dsh-workbench] 没有 shell 服务:需要执行缝的套件会降级");
|
|
@@ -4061,6 +4313,14 @@ function apply(ctx) {
|
|
|
4061
4313
|
...item,
|
|
4062
4314
|
released: releasedOf(item.unit)
|
|
4063
4315
|
})) };
|
|
4316
|
+
const failure = mountFailures.get(unit);
|
|
4317
|
+
if (failure !== void 0) return {
|
|
4318
|
+
unit,
|
|
4319
|
+
status: "failed",
|
|
4320
|
+
required: [],
|
|
4321
|
+
missing: [],
|
|
4322
|
+
detail: failure.reason
|
|
4323
|
+
};
|
|
4064
4324
|
return probeOne(unit, force);
|
|
4065
4325
|
});
|
|
4066
4326
|
}
|