@deepseek-ai/dsh-app-boot 0.2.1-alpha.2 → 0.2.1-alpha.4
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/README.i18n.yaml +16 -19
- package/README.md +11 -15
- package/README.zh.md +11 -15
- package/lib/index.js +379 -106
- package/lib/types/index.d.ts +41 -20
- package/lib/types/plugin-compatibility.d.ts +11 -10
- package/lib/types/profile-context.d.ts +7 -0
- package/lib/types/profile-resolution/resolver.d.ts +14 -6
- package/lib/types/profile-resolution/service.d.ts +3 -1
- package/lib/types/profile.d.ts +2 -2
- package/lib/worker/profile-resolution-bootstrap.js +133 -11
- package/package.json +18 -19
package/lib/index.js
CHANGED
|
@@ -249,7 +249,7 @@ var Include = class extends EntryTree {
|
|
|
249
249
|
};
|
|
250
250
|
//#endregion
|
|
251
251
|
//#region lib/types/plugin-compatibility.js
|
|
252
|
-
/** Evaluate plugin dsh
|
|
252
|
+
/** Evaluate plugin dsh requirements (`engines.dsh`, else dsh peers) without importing plugin code. */
|
|
253
253
|
function objectOf$1(value, field) {
|
|
254
254
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${field} must be an object`);
|
|
255
255
|
return value;
|
|
@@ -260,7 +260,7 @@ function runtimeVersionOf(value) {
|
|
|
260
260
|
}
|
|
261
261
|
function identityField(manifest, field) {
|
|
262
262
|
const value = Object.hasOwn(manifest, field) ? manifest[field] : void 0;
|
|
263
|
-
if (typeof value !== "string" || value.trim() === "") throw new Error(`Plugin manifest ${field} must be a non-empty string when dsh
|
|
263
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`Plugin manifest ${field} must be a non-empty string when dsh requirements are unmet`);
|
|
264
264
|
return value;
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
@@ -273,33 +273,46 @@ function getDshRuntimeVersion() {
|
|
|
273
273
|
const manifest = objectOf$1(JSON.parse(fs.readFileSync(filename, "utf8")), "app-boot package.json");
|
|
274
274
|
return runtimeVersionOf(Object.hasOwn(manifest, "version") ? manifest.version : void 0);
|
|
275
275
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
runtimeVersionOf(runtimeVersion);
|
|
288
|
-
const fields = objectOf$1(manifest, "Plugin manifest");
|
|
289
|
-
if (!Object.hasOwn(fields, "peerDependencies")) return void 0;
|
|
276
|
+
function satisfies(runtimeVersion, range) {
|
|
277
|
+
return range.trim() !== "" && semver.satisfies(runtimeVersion, range, { includePrerelease: true });
|
|
278
|
+
}
|
|
279
|
+
function unmetRequirements(fields, runtimeVersion) {
|
|
280
|
+
const engines = Object.hasOwn(fields, "engines") ? objectOf$1(fields.engines, "Plugin manifest engines") : {};
|
|
281
|
+
if (Object.hasOwn(engines, "dsh")) {
|
|
282
|
+
const range = engines.dsh;
|
|
283
|
+
if (typeof range !== "string") throw new Error("Plugin manifest engines.dsh must be a string");
|
|
284
|
+
return satisfies(runtimeVersion, range) ? {} : { "engines.dsh": range };
|
|
285
|
+
}
|
|
286
|
+
if (!Object.hasOwn(fields, "peerDependencies")) return {};
|
|
290
287
|
const dependencies = objectOf$1(fields.peerDependencies, "Plugin manifest peerDependencies");
|
|
291
|
-
const
|
|
288
|
+
const unmet = {};
|
|
292
289
|
for (const [name, range] of Object.entries(dependencies)) {
|
|
293
290
|
if (typeof range !== "string") throw new Error(`Plugin manifest peerDependencies[${JSON.stringify(name)}] must be a string`);
|
|
294
291
|
if (name !== "@deepseek-ai/dsh" && !name.startsWith("@deepseek-ai/dsh-")) continue;
|
|
295
|
-
|
|
292
|
+
if (!satisfies(runtimeVersion, [
|
|
296
293
|
"workspace:^",
|
|
297
294
|
"workspace:~",
|
|
298
295
|
"workspace:*"
|
|
299
|
-
].includes(range) ? runtimeVersion : range;
|
|
300
|
-
if (requirement.trim() === "" || !semver.satisfies(runtimeVersion, requirement, { includePrerelease: true })) peers[name] = range;
|
|
296
|
+
].includes(range) ? runtimeVersion : range)) unmet[name] = range;
|
|
301
297
|
}
|
|
302
|
-
|
|
298
|
+
return unmet;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Check the plugin's dsh requirements against the runtime. An own `engines.dsh` range decides alone and
|
|
302
|
+
* peerDependencies are ignored; otherwise every @deepseek-ai/dsh or @deepseek-ai/dsh-* peer must match.
|
|
303
|
+
* Prereleases participate in ranges. For peers only, workspace:^, workspace:~, and workspace:* refer to
|
|
304
|
+
* the current runtime; other empty or invalid ranges, including any workspace: `engines.dsh`, are unmet.
|
|
305
|
+
* @param manifest - parsed plugin package.json; inherited fields are ignored.
|
|
306
|
+
* @param exemptions - exact plugin name@version keys mapped to exact runtime versions.
|
|
307
|
+
* @param runtimeVersion - running dsh version, defaulting to this app-boot package.
|
|
308
|
+
* @returns unmet requirements and exemption status, or undefined when every requirement is met.
|
|
309
|
+
* @throws for malformed manifest engines or peer fields, invalid runtime versions, or missing identity on a mismatch.
|
|
310
|
+
*/
|
|
311
|
+
function evaluatePluginCompatibility(manifest, exemptions = {}, runtimeVersion = getDshRuntimeVersion()) {
|
|
312
|
+
runtimeVersionOf(runtimeVersion);
|
|
313
|
+
const fields = objectOf$1(manifest, "Plugin manifest");
|
|
314
|
+
const requirements = unmetRequirements(fields, runtimeVersion);
|
|
315
|
+
if (Object.keys(requirements).length === 0) return void 0;
|
|
303
316
|
const name = identityField(fields, "name");
|
|
304
317
|
const version = identityField(fields, "version");
|
|
305
318
|
const key = `${name}@${version}`;
|
|
@@ -307,19 +320,19 @@ function evaluatePluginCompatibility(manifest, exemptions = {}, runtimeVersion =
|
|
|
307
320
|
name,
|
|
308
321
|
version,
|
|
309
322
|
runtimeVersion,
|
|
310
|
-
|
|
323
|
+
requirements,
|
|
311
324
|
exempted: (Object.hasOwn(exemptions, key) ? exemptions[key] : void 0)?.includes(runtimeVersion) === true
|
|
312
325
|
};
|
|
313
326
|
}
|
|
314
327
|
/**
|
|
315
|
-
* Describe
|
|
328
|
+
* Describe unmet requirements, their risk, and the exact-version remedy.
|
|
316
329
|
* Surfaces with their own grant mechanism or locale render the structured result themselves.
|
|
317
330
|
* @param issue - incompatible plugin/runtime result, including exempted mismatches.
|
|
318
331
|
* @returns an English diagnostic for logs and stderr.
|
|
319
332
|
*/
|
|
320
333
|
function pluginCompatibilityWarning(issue) {
|
|
321
334
|
const key = `${issue.name}@${issue.version}`;
|
|
322
|
-
return `Plugin ${key} is incompatible with dsh ${issue.runtimeVersion}:
|
|
335
|
+
return `Plugin ${key} is incompatible with dsh ${issue.runtimeVersion}: requirements ${JSON.stringify(issue.requirements)}. Running it may cause crashes or data loss. Update the plugin or install a plugin version compatible with this dsh runtime. To accept this risk explicitly, grant the exact-version exemption for ${key} on dsh ${issue.runtimeVersion} with \`dsh plugin allow-version\` or the plugin manager, then retry the installation or restart dsh. Exact-version exemption: ${issue.exempted ? "active" : "not active"}.`;
|
|
323
336
|
}
|
|
324
337
|
//#endregion
|
|
325
338
|
//#region lib/types/profile-compatibility.js
|
|
@@ -574,6 +587,7 @@ const OPTIONAL_BUNDLES = [
|
|
|
574
587
|
"@deepseek-ai/dsh-experimental-cot-translation-bundle",
|
|
575
588
|
"@deepseek-ai/dsh-experimental-auto-review",
|
|
576
589
|
"@deepseek-ai/dsh-experimental-inspector-profile",
|
|
590
|
+
"@deepseek-ai/dsh-experimental-plugin-market-bundle",
|
|
577
591
|
"@deepseek-ai/dsh-experimental-tool-worktree"
|
|
578
592
|
];
|
|
579
593
|
const PROFILE_PATCH_TEMPLATE = `# Your patch layer for this dsh profile, applied after every bundle layer:
|
|
@@ -990,7 +1004,7 @@ function resolveBundleDir(binName, packageName, installAnchor, profileDir) {
|
|
|
990
1004
|
* the shared Harness home. This is used by application-owned profiles whose
|
|
991
1005
|
* package project and lifecycle belong to that application.
|
|
992
1006
|
* Retired bundles are removed from the stored bundle list first, rewriting the
|
|
993
|
-
* manifest when it listed one. Unreadable bundles, and bundles whose own dsh
|
|
1007
|
+
* manifest when it listed one. Unreadable bundles, and bundles whose own dsh requirements the profile does not exempt, are skipped
|
|
994
1008
|
* without changing the manifest and listed in `skippedBundles`; nothing is printed.
|
|
995
1009
|
* @param binName - the diagnostic prefix on thrown errors.
|
|
996
1010
|
* @param dir - absolute profile package directory.
|
|
@@ -1095,6 +1109,15 @@ function resolveTelemetryPatch(disabledEnv, hasRow) {
|
|
|
1095
1109
|
disabled: true
|
|
1096
1110
|
};
|
|
1097
1111
|
}
|
|
1112
|
+
/** Read the selected bundles' patches, which apply before the profile patch file.
|
|
1113
|
+
* @param binName Diagnostic prefix for malformed or missing configuration.
|
|
1114
|
+
* @param context Data supplied by the profile launcher.
|
|
1115
|
+
* @param initialProfile Already loaded startup profile; omitted reads the current files.
|
|
1116
|
+
* @returns Bundle patches in application order.
|
|
1117
|
+
*/
|
|
1118
|
+
function readBundlePatches(binName, context, initialProfile) {
|
|
1119
|
+
return (initialProfile ?? loadProfileDirectory(binName, context.dir, context.installAnchor, { userLayer: false })).layers.flatMap((layer) => layer.patches);
|
|
1120
|
+
}
|
|
1098
1121
|
/** Read current bundle and user layers with the launch-time overlays.
|
|
1099
1122
|
* @param binName Diagnostic prefix for malformed or missing configuration.
|
|
1100
1123
|
* @param context Data supplied by the profile launcher.
|
|
@@ -1102,9 +1125,8 @@ function resolveTelemetryPatch(disabledEnv, hasRow) {
|
|
|
1102
1125
|
* @returns Detached ordered patches; this function does not update the Loader.
|
|
1103
1126
|
*/
|
|
1104
1127
|
function readProfilePatches(binName, context, initialProfile) {
|
|
1105
|
-
const profile = initialProfile ?? loadProfileDirectory(binName, context.dir, context.installAnchor, { userLayer: false });
|
|
1106
1128
|
const patches = structuredClone([
|
|
1107
|
-
...
|
|
1129
|
+
...readBundlePatches(binName, context, initialProfile),
|
|
1108
1130
|
...initialProfile?.patches ?? loadOptionalPatches(binName, context.patchPath) ?? [],
|
|
1109
1131
|
...loadOptionalPatches(binName, join(context.home, "cordis.patch.yml")) ?? [],
|
|
1110
1132
|
...context.overlays
|
|
@@ -1238,6 +1260,14 @@ function sanitizeProfile(binName, profileDir, bundles) {
|
|
|
1238
1260
|
/** In-memory profile package routing for Node's default ESM and CommonJS loaders. */
|
|
1239
1261
|
const WORKER_RESOLUTION_KEY = "@deepseek-ai/dsh-app-boot/profile-resolution";
|
|
1240
1262
|
const EMPTY_ATTRIBUTES = Object.freeze({});
|
|
1263
|
+
/** A successor resolution the running process cannot adopt, such as a retained mapping in another directory; it waits for a restart. */
|
|
1264
|
+
var ResolutionRestartRequired = class extends Error {
|
|
1265
|
+
/** @param message Diagnostic naming the rejected changes. */
|
|
1266
|
+
constructor(message) {
|
|
1267
|
+
super(message);
|
|
1268
|
+
this.name = "ResolutionRestartRequired";
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1241
1271
|
/**
|
|
1242
1272
|
* Split a bare request into its package name without allocating path segments.
|
|
1243
1273
|
* @param request - module specifier to classify.
|
|
@@ -1251,6 +1281,15 @@ function barePackageName(request) {
|
|
|
1251
1281
|
const second = request.indexOf("/", first + 1);
|
|
1252
1282
|
return second < 0 ? request : request.slice(0, second);
|
|
1253
1283
|
}
|
|
1284
|
+
/** The JavaScript and native real paths of an existing directory; none for a directory that no longer exists. */
|
|
1285
|
+
function realSpellings(path) {
|
|
1286
|
+
try {
|
|
1287
|
+
const native = Reflect.get(realpathSync, "native");
|
|
1288
|
+
return [realpathSync(path), ...typeof native === "function" ? [String(Reflect.apply(native, realpathSync, [path]))] : []];
|
|
1289
|
+
} catch (_error) {
|
|
1290
|
+
return [];
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1254
1293
|
function canonicalPath(path) {
|
|
1255
1294
|
try {
|
|
1256
1295
|
return realpathSync(path);
|
|
@@ -1265,7 +1304,10 @@ function prefixes(path) {
|
|
|
1265
1304
|
}
|
|
1266
1305
|
function compileResolution(resolution) {
|
|
1267
1306
|
return {
|
|
1268
|
-
entries: new Map(resolution.entries.map((entry) => [entry.name,
|
|
1307
|
+
entries: new Map(resolution.entries.map((entry) => [entry.name, {
|
|
1308
|
+
...entry,
|
|
1309
|
+
realDir: canonicalPath(entry.packageDir)
|
|
1310
|
+
}])),
|
|
1269
1311
|
profilesDir: resolution.profilesDir,
|
|
1270
1312
|
profileDir: resolution.profileDir,
|
|
1271
1313
|
profilePaths: prefixes(resolution.profilesDir),
|
|
@@ -1428,11 +1470,65 @@ function sameResolution(left, right) {
|
|
|
1428
1470
|
if (left === right) return true;
|
|
1429
1471
|
return canonicalPath(left) === canonicalPath(right);
|
|
1430
1472
|
}
|
|
1473
|
+
/** A directory as given and in its JavaScript and native real paths, each with a trailing separator. */
|
|
1474
|
+
function spellings(dir) {
|
|
1475
|
+
const result = new Set([dir + sep]);
|
|
1476
|
+
for (const real of realSpellings(dir)) result.add(real + sep);
|
|
1477
|
+
return [...result];
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* The previous real directories of packages that moved, each mapped to the current one. Node keeps the real path of
|
|
1481
|
+
* every link it resolved through and never refreshes it, so a lookup through a link that now points elsewhere still
|
|
1482
|
+
* selects a file below the target Node saw first; {@link MovedDirectories.relocate} selects the same file below the
|
|
1483
|
+
* current target instead.
|
|
1484
|
+
*/
|
|
1485
|
+
var MovedDirectories = class {
|
|
1486
|
+
/** Previous directory to current directory, both with a trailing separator. */
|
|
1487
|
+
targets = /* @__PURE__ */ new Map();
|
|
1488
|
+
/**
|
|
1489
|
+
* Record that a package moved from one directory to another. Every spelling of the previous directory leads to the
|
|
1490
|
+
* current one. A directory that an earlier move led to the previous one now leads to the current one as well, since
|
|
1491
|
+
* Node still selects files below the earliest target.
|
|
1492
|
+
* @param from Previous real directory.
|
|
1493
|
+
* @param to Current real directory.
|
|
1494
|
+
*/
|
|
1495
|
+
record(from, to) {
|
|
1496
|
+
const previous = spellings(from);
|
|
1497
|
+
const current = to + sep;
|
|
1498
|
+
for (const [key, value] of this.targets) if (previous.includes(value)) this.targets.set(key, current);
|
|
1499
|
+
for (const key of previous) this.targets.set(key, current);
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Forget the previous directories that are current again, in any spelling, and records that lead to themselves.
|
|
1503
|
+
* @param current The real directories the published resolution maps.
|
|
1504
|
+
*/
|
|
1505
|
+
forget(current) {
|
|
1506
|
+
const active = /* @__PURE__ */ new Set();
|
|
1507
|
+
for (const dir of current) for (const spelling of spellings(dir)) active.add(spelling);
|
|
1508
|
+
for (const [key, value] of [...this.targets]) if (active.has(key) || key === value) this.targets.delete(key);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* The same file below the current directory when it exists, for a file below a previous directory that an importer
|
|
1512
|
+
* outside that directory selected. An importer inside the previous directory keeps its own package's files. The
|
|
1513
|
+
* innermost previous directory decides.
|
|
1514
|
+
* @param filename Selected file.
|
|
1515
|
+
* @param parent Importer path.
|
|
1516
|
+
* @returns The file to load.
|
|
1517
|
+
*/
|
|
1518
|
+
relocate(filename, parent) {
|
|
1519
|
+
let from = "";
|
|
1520
|
+
for (const dir of this.targets.keys()) if (filename.startsWith(dir) && dir.length > from.length) from = dir;
|
|
1521
|
+
if (from === "" || parent?.startsWith(from) === true) return filename;
|
|
1522
|
+
const relocated = this.targets.get(from) + filename.slice(from.length);
|
|
1523
|
+
return existsSync(relocated) ? relocated : filename;
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1431
1526
|
/** One mutable pointer to the immutable runtime resolution. */
|
|
1432
1527
|
var ResolutionRouter = class {
|
|
1433
1528
|
nodeModulePaths;
|
|
1434
1529
|
current;
|
|
1435
1530
|
linkedTargets;
|
|
1531
|
+
moved = new MovedDirectories();
|
|
1436
1532
|
constructor(resolution, nodeModulePaths) {
|
|
1437
1533
|
this.nodeModulePaths = nodeModulePaths;
|
|
1438
1534
|
this.current = compileResolution(resolution);
|
|
@@ -1441,21 +1537,49 @@ var ResolutionRouter = class {
|
|
|
1441
1537
|
replace(successor) {
|
|
1442
1538
|
const entries = new Map(successor.entries.map((entry) => [entry.name, entry]));
|
|
1443
1539
|
if (successor.profilesDir !== this.current.profilesDir || successor.profileDir !== this.current.profileDir) throw new Error("profile resolution: a successor cannot change its profile scope");
|
|
1540
|
+
const rejected = [];
|
|
1541
|
+
const moves = [];
|
|
1444
1542
|
for (const [name, current] of this.current.entries) {
|
|
1445
1543
|
const next = entries.get(name);
|
|
1446
1544
|
if (next === void 0 && current.scope === "profile") continue;
|
|
1447
|
-
if (next
|
|
1545
|
+
if (next !== void 0 && current.scope === "profile" && next.scope === "profile") {
|
|
1546
|
+
if (current.realDir === canonicalPath(next.packageDir)) continue;
|
|
1547
|
+
rejected.push(`profile resolution: replacing ${JSON.stringify(name)} requires a process restart`);
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
if (next === void 0 || !sameResolution(current.packageDir, next.packageDir) || current.scope === "installation" && !sameResolution(current.declarer, next.declarer) || current.scope === "installation" && current.version !== next.version || current.scope !== next.scope) rejected.push(`profile resolution: replacing ${JSON.stringify(name)} requires a process restart`);
|
|
1448
1551
|
}
|
|
1449
1552
|
const localPackageNames = new Set(successor.localPackageNames);
|
|
1450
|
-
for (const name of localPackageNames) if (!this.current.localPackageNames.has(name) && this.current.entries.has(name))
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1553
|
+
for (const name of localPackageNames) if (!this.current.localPackageNames.has(name) && this.current.entries.has(name)) rejected.push(`profile resolution: overriding ${JSON.stringify(name)} locally requires a process restart`);
|
|
1554
|
+
const targets = new Map(this.linkedTargets);
|
|
1555
|
+
for (const [name, previous] of this.linkedTargets) {
|
|
1556
|
+
const root = successor.linkedRoots.find((candidate) => candidate.name === name);
|
|
1557
|
+
const entry = entries.get(name);
|
|
1558
|
+
const target = root?.realPath ?? (entry?.scope === "profile" ? canonicalPath(entry.packageDir) : void 0);
|
|
1559
|
+
if (target === void 0 || sameResolution(previous, target)) continue;
|
|
1560
|
+
moves.push([previous, target]);
|
|
1561
|
+
targets.set(name, target);
|
|
1454
1562
|
}
|
|
1563
|
+
if (rejected.length > 0) throw new ResolutionRestartRequired(rejected.join("; "));
|
|
1455
1564
|
const next = compileResolution(successor);
|
|
1456
|
-
for (const { name, realPath } of successor.linkedRoots)
|
|
1565
|
+
for (const { name, realPath } of successor.linkedRoots) targets.set(name, realPath);
|
|
1566
|
+
this.linkedTargets = targets;
|
|
1567
|
+
for (const [from, to] of moves) this.moved.record(from, to);
|
|
1568
|
+
const current = [];
|
|
1569
|
+
for (const entry of next.entries.values()) current.push(entry.realDir);
|
|
1570
|
+
for (const root of successor.linkedRoots) current.push(root.realPath);
|
|
1571
|
+
this.moved.forget(current);
|
|
1457
1572
|
this.current = next;
|
|
1458
1573
|
}
|
|
1574
|
+
/**
|
|
1575
|
+
* The file to load instead of one Node selected through a link that has moved since; see {@link MovedDirectories}.
|
|
1576
|
+
* @param filename Selected file.
|
|
1577
|
+
* @param parent Importer path.
|
|
1578
|
+
* @returns The file to load.
|
|
1579
|
+
*/
|
|
1580
|
+
relocate(filename, parent) {
|
|
1581
|
+
return this.moved.relocate(filename, parent);
|
|
1582
|
+
}
|
|
1459
1583
|
/** Whether a module path has an interception layer: it lies in the profiles tree, the active profile, or a linked root. */
|
|
1460
1584
|
hasInterceptionLayerForPath(path) {
|
|
1461
1585
|
return findInterceptionLayer(path, this.current) !== void 0;
|
|
@@ -1774,7 +1898,23 @@ function installRuntimeInterception(resolution) {
|
|
|
1774
1898
|
delegatedEsm = previous;
|
|
1775
1899
|
}
|
|
1776
1900
|
};
|
|
1777
|
-
|
|
1901
|
+
const relocate = (result, parent) => {
|
|
1902
|
+
if (!result.url.startsWith("file:")) return result;
|
|
1903
|
+
const url = new URL(result.url);
|
|
1904
|
+
const filename = fileURLToPath(url);
|
|
1905
|
+
const relocated = router.relocate(filename, parent?.startsWith("file:") === true ? fileURLToPath(parent) : void 0);
|
|
1906
|
+
if (relocated === filename) return result;
|
|
1907
|
+
url.pathname = pathToFileURL(relocated).pathname;
|
|
1908
|
+
return {
|
|
1909
|
+
...result,
|
|
1910
|
+
url: url.href
|
|
1911
|
+
};
|
|
1912
|
+
};
|
|
1913
|
+
return (request, parent, attributes) => {
|
|
1914
|
+
const result = adapted(request, parent, attributes);
|
|
1915
|
+
/* v8 ignore next -- Node 24+ resolves synchronously; the Node 22 matrix covers its Promise result */
|
|
1916
|
+
return result instanceof Promise ? result.then((resolved) => relocate(resolved, parent)) : relocate(result, parent);
|
|
1917
|
+
};
|
|
1778
1918
|
};
|
|
1779
1919
|
let restoreEsm;
|
|
1780
1920
|
/* v8 ignore else -- CI coverage runs Node 24 v2; the Node 22 matrix exercises the v1 adapter */
|
|
@@ -1898,7 +2038,11 @@ function installRuntimeInterception(resolution) {
|
|
|
1898
2038
|
delegatedCjs--;
|
|
1899
2039
|
}
|
|
1900
2040
|
};
|
|
1901
|
-
|
|
2041
|
+
const relocatedFilename = (request, parent, main, options) => {
|
|
2042
|
+
const filename = wrappedFilename(request, parent, main, options);
|
|
2043
|
+
return isAbsolute(filename) ? router.relocate(filename, parent?.filename ?? void 0) : filename;
|
|
2044
|
+
};
|
|
2045
|
+
cjs._resolveFilename = relocatedFilename;
|
|
1902
2046
|
return {
|
|
1903
2047
|
packageDir(specifier, parentURL) {
|
|
1904
2048
|
return router.packageDir(specifier, parentURL);
|
|
@@ -1908,7 +2052,7 @@ function installRuntimeInterception(resolution) {
|
|
|
1908
2052
|
},
|
|
1909
2053
|
dispose() {
|
|
1910
2054
|
/* v8 ignore else -- interceptions are disposed in reverse installation order */
|
|
1911
|
-
if (cjs._resolveFilename ===
|
|
2055
|
+
if (cjs._resolveFilename === relocatedFilename) cjs._resolveFilename = originalFilename;
|
|
1912
2056
|
restoreEsm();
|
|
1913
2057
|
}
|
|
1914
2058
|
};
|
|
@@ -2183,7 +2327,7 @@ function preflight(ctx, entries, parentURL, report) {
|
|
|
2183
2327
|
const issue = evaluatePluginCompatibility(manifest, exemptions);
|
|
2184
2328
|
return issue === void 0 || issue.exempted ? void 0 : pluginCompatibilityWarning(issue);
|
|
2185
2329
|
} catch (error) {
|
|
2186
|
-
return `its declared
|
|
2330
|
+
return `its declared dsh requirements cannot be validated: ${error instanceof Error ? error.message : String(error)}`;
|
|
2187
2331
|
}
|
|
2188
2332
|
};
|
|
2189
2333
|
const deny = (row, reason) => {
|
|
@@ -3362,10 +3506,14 @@ var PluginPackages = class extends Service {
|
|
|
3362
3506
|
interception.dispose();
|
|
3363
3507
|
}, "profile package resolution");
|
|
3364
3508
|
}
|
|
3509
|
+
/** The published runtime resolution, or `undefined` when Node resolves packages alone. */
|
|
3510
|
+
get resolution() {
|
|
3511
|
+
return this.current;
|
|
3512
|
+
}
|
|
3365
3513
|
/**
|
|
3366
3514
|
* Publish a complete successor generation for this process and subsequently created Workers.
|
|
3367
3515
|
* Profile mappings and local package names may be removed after their plugins stop; linked roots may also be removed.
|
|
3368
|
-
* Retained profile mappings may change their declarer, but not their normalized directory
|
|
3516
|
+
* Retained profile mappings may change their declarer and version, but not their normalized directory or scope;
|
|
3369
3517
|
* installation mappings must remain unchanged. Publication does not unload modules or clear Node caches.
|
|
3370
3518
|
* @param successor - fully constructed generation accepted by {@link RuntimeInterception.replace}.
|
|
3371
3519
|
*/
|
|
@@ -3613,7 +3761,30 @@ function loadLayeredEnv(binName, cwd = process.cwd(), warn = (line) => void proc
|
|
|
3613
3761
|
}
|
|
3614
3762
|
const bootstrapIncludes = /* @__PURE__ */ new WeakMap();
|
|
3615
3763
|
const userPatchesSchema = entryListSchema;
|
|
3616
|
-
/**
|
|
3764
|
+
/**
|
|
3765
|
+
* Recompose the profile from its complete patch list, together with its runtime resolution. The rows the generation no
|
|
3766
|
+
* longer lists are removed first and their disposal finishes while their packages are still mapped; the latest
|
|
3767
|
+
* generation of an installed, recomputable resolution is published; the compatibility preflight reads the published
|
|
3768
|
+
* packages; the generation is applied to the root Include; entries that ran, and enabled entries whose import failed,
|
|
3769
|
+
* start afterwards when the update left them without a running fiber; and activation is judged. A failure before the generation applies,
|
|
3770
|
+
* including a publication the resolution rejects, which reports `package-moved`, puts the removed rows back at their
|
|
3771
|
+
* places, starting them again, and starts the entries that ran again. An entry waiting for a service is reported as a
|
|
3772
|
+
* warning unless it is required. Patches are copied with `structuredClone`, so they hold no functions.
|
|
3773
|
+
* @param ctx Booted root context.
|
|
3774
|
+
* @param patches Complete ordered patch list.
|
|
3775
|
+
* @param binName Diagnostic prefix.
|
|
3776
|
+
* @param options What the caller already did and requires.
|
|
3777
|
+
* @returns Diagnostics for unchanged pre-existing inactive entries, and the restart reason of a rejected publication.
|
|
3778
|
+
* @throws When the root Include is missing; the latest resolution cannot be read or published; preparation or the
|
|
3779
|
+
* Loader update rejects; or for a new or changed failure, an inactive required entry, a failed entry that ran, or a
|
|
3780
|
+
* healthy fiber whose disposal rejected.
|
|
3781
|
+
*/
|
|
3782
|
+
function recomposeProfile(ctx, patches, binName, options = {}) {
|
|
3783
|
+
return recompose(ctx, patches, binName, options, true);
|
|
3784
|
+
}
|
|
3785
|
+
/**
|
|
3786
|
+
* Apply a complete patch list to the root Include and judge activation, leaving the runtime resolution and entries
|
|
3787
|
+
* whose import failed as they are.
|
|
3617
3788
|
* @param ctx Booted root context.
|
|
3618
3789
|
* @param patches Complete ordered patch list.
|
|
3619
3790
|
* @param binName Diagnostic prefix.
|
|
@@ -3621,33 +3792,141 @@ const userPatchesSchema = entryListSchema;
|
|
|
3621
3792
|
* @returns Diagnostics for unchanged pre-existing inactive entries; new or changed failures reject.
|
|
3622
3793
|
*/
|
|
3623
3794
|
async function reconcileProfilePatches(ctx, patches, binName, requiredIds = []) {
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3795
|
+
return (await recompose(ctx, patches, binName, { requiredIds }, false)).warnings;
|
|
3796
|
+
}
|
|
3797
|
+
async function recompose(ctx, patches, binName, options, withResolution) {
|
|
3798
|
+
const { requiredIds = [], running = [] } = options;
|
|
3799
|
+
let applying = false;
|
|
3800
|
+
let stopped = [];
|
|
3801
|
+
try {
|
|
3802
|
+
const entry = bootstrapIncludes.get(ctx);
|
|
3803
|
+
if (entry === void 0) throw new Error(`${binName}: profile reload requires the root Include entry`);
|
|
3804
|
+
const previousFailures = (await inactiveEntries(ctx, false)).map((failure) => ({
|
|
3805
|
+
entry: failure.entry,
|
|
3806
|
+
fiber: failure.entry.fiber,
|
|
3807
|
+
options: JSON.stringify(failure.entry.options),
|
|
3808
|
+
diagnostic: failure.entry.fiber?.uid ? reloadDiagnostic(failure) : void 0
|
|
3809
|
+
}));
|
|
3810
|
+
const previousFibers = [...ctx.loader.entries()].flatMap((row) => row.fiber === void 0 ? [] : [{
|
|
3811
|
+
fiber: row.fiber,
|
|
3812
|
+
failed: row.fiber.state === FIBER_FAILED || row.fiber.state === FIBER_DISPOSED
|
|
3813
|
+
}]);
|
|
3814
|
+
const { patches: previous = [], ...includeConfig } = entry.options.config;
|
|
3815
|
+
const current = withResolution ? ctx.get("pluginPackages")?.resolution : void 0;
|
|
3816
|
+
const successor = current instanceof ProfileRuntimeResolution ? await current.computeLatestResolution() : void 0;
|
|
3817
|
+
if (successor !== void 0) {
|
|
3818
|
+
const listed = rowIds(structuredClone(patches));
|
|
3819
|
+
const composed = rowIds(structuredClone(previous));
|
|
3820
|
+
const tree = entry.subgroup?.tree;
|
|
3821
|
+
const dropped = [...ctx.loader.entries()].filter((row) => row.parent.tree === tree && composed.has(row.options.id) && !listed.has(row.options.id));
|
|
3822
|
+
const ids = new Set(dropped.map((row) => row.options.id));
|
|
3823
|
+
const topmost = dropped.filter((row) => !ids.has(row.parent.ctx.fiber.entry?.options.id));
|
|
3824
|
+
stopped = topmost.map((row) => ({
|
|
3825
|
+
group: row.parent,
|
|
3826
|
+
options: structuredClone(row.options),
|
|
3827
|
+
index: row.parent.data.indexOf(row.options)
|
|
3828
|
+
}));
|
|
3829
|
+
if (topmost.length > 0) {
|
|
3830
|
+
const fibers = dropped.flatMap((row) => row.fiber === void 0 ? [] : [row.fiber]);
|
|
3831
|
+
for (const row of topmost) {
|
|
3832
|
+
row.options.disabled = true;
|
|
3833
|
+
row.parent.remove(row.options.id);
|
|
3834
|
+
}
|
|
3835
|
+
await Promise.allSettled(fibers.map((fiber) => fiber.await()));
|
|
3836
|
+
await ctx.loader.await();
|
|
3837
|
+
}
|
|
3838
|
+
try {
|
|
3839
|
+
publish(ctx, successor);
|
|
3840
|
+
} catch (error) {
|
|
3841
|
+
if (!(error instanceof ResolutionRestartRequired)) throw error;
|
|
3842
|
+
return {
|
|
3843
|
+
warnings: [],
|
|
3844
|
+
restartReason: "package-moved"
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
const parentURL = new URL(".", new URL(includeConfig.path, entry.parent.tree.ctx.baseUrl)).href;
|
|
3849
|
+
const prepared = prepareProfilePatches(ctx, patches, parentURL, binName);
|
|
3850
|
+
applying = true;
|
|
3851
|
+
const retried = [...new Set([...running, ...withResolution ? [...ctx.loader.entries()].filter(importFailed) : []])];
|
|
3852
|
+
try {
|
|
3853
|
+
await entry.update({ config: {
|
|
3854
|
+
...includeConfig,
|
|
3855
|
+
patches: prepared
|
|
3856
|
+
} });
|
|
3857
|
+
} finally {
|
|
3858
|
+
await resume(ctx, retried);
|
|
3859
|
+
}
|
|
3860
|
+
const results = await Promise.allSettled(previousFibers.map(({ fiber }) => fiber.await()));
|
|
3861
|
+
await ctx.loader.await();
|
|
3862
|
+
const failures = await inactiveEntries(ctx);
|
|
3863
|
+
const introduced = failures.filter((failure) => {
|
|
3864
|
+
if (requiredIds.includes(failure.entry.options.id)) return true;
|
|
3865
|
+
if (failure.outcome.kind === "pending") return false;
|
|
3866
|
+
if (running.includes(failure.entry)) return true;
|
|
3867
|
+
return !previousFailures.some((previous) => sameFailure(previous, failure));
|
|
3868
|
+
});
|
|
3869
|
+
if (introduced.length > 0) throw new Error(activationDiagnostic(binName, introduced, false).trimEnd());
|
|
3870
|
+
for (const [index, result] of results.entries()) if (result.status === "rejected" && !previousFibers[index]?.failed) throw result.reason;
|
|
3871
|
+
ctx.emit("app-boot/config-reload");
|
|
3872
|
+
return { warnings: failures.map(reloadDiagnostic) };
|
|
3873
|
+
} finally {
|
|
3874
|
+
if (!applying && (stopped.length > 0 || running.length > 0)) try {
|
|
3875
|
+
for (const { group, options, index } of stopped.toSorted((left, right) => left.index - right.index)) {
|
|
3876
|
+
group.data.splice(index, 0, options);
|
|
3877
|
+
await group.create(options);
|
|
3878
|
+
}
|
|
3879
|
+
await resume(ctx, running);
|
|
3880
|
+
const ran = new Set(running.map((row) => row.options.id));
|
|
3881
|
+
for (const failure of await inactiveEntries(ctx)) if (ran.has(failure.entry.options.id)) ctx.logger.warn(`${binName}: ${reloadDiagnostic(failure)}`);
|
|
3882
|
+
} catch (error) {
|
|
3883
|
+
ctx.logger.warn(error);
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
/** Whether an inactive entry is the failure recorded before: the same entry, fiber and options, and a matching diagnostic. */
|
|
3888
|
+
function sameFailure(previous, failure) {
|
|
3889
|
+
return previous.entry === failure.entry && previous.fiber === failure.entry.fiber && previous.options === JSON.stringify(failure.entry.options) && (previous.diagnostic === void 0 || previous.diagnostic === reloadDiagnostic(failure));
|
|
3890
|
+
}
|
|
3891
|
+
/** The ids of the rows a patch list inserts, including the rows of groups. */
|
|
3892
|
+
function rowIds(patches) {
|
|
3893
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3894
|
+
const visit = (rows) => {
|
|
3895
|
+
for (const row of rows) {
|
|
3896
|
+
ids.add(row.id);
|
|
3897
|
+
if ((row.group === true || row.name === "cordis:group") && Array.isArray(row.config)) visit(row.config);
|
|
3898
|
+
}
|
|
3899
|
+
};
|
|
3900
|
+
visit(applyEntryPatches([], patches, () => {}));
|
|
3901
|
+
return ids;
|
|
3902
|
+
}
|
|
3903
|
+
/** Publish a generation unless it maps exactly what is published, which would only drop the package caches. */
|
|
3904
|
+
function publish(ctx, next) {
|
|
3905
|
+
const table = ({ localPackageNames, entries, linkedRoots }) => JSON.stringify([
|
|
3906
|
+
localPackageNames,
|
|
3907
|
+
entries,
|
|
3908
|
+
linkedRoots
|
|
3909
|
+
]);
|
|
3910
|
+
const packages = ctx.pluginPackages;
|
|
3911
|
+
if (table(packages.resolution) !== table(next)) packages.replace(next);
|
|
3912
|
+
}
|
|
3913
|
+
/** Whether an enabled entry below a running parent has no fiber or keeps the disposed fiber of a failed import. */
|
|
3914
|
+
function importFailed(entry) {
|
|
3915
|
+
return enabledEntry(entry) && entry.parent.ctx.fiber.uid !== null && (entry.fiber === void 0 || entry.fiber.state === FIBER_DISPOSED);
|
|
3916
|
+
}
|
|
3917
|
+
/** Whether an entry is enabled now; a throwing `disabled` expression counts as disabled. */
|
|
3918
|
+
function enabledEntry(entry) {
|
|
3919
|
+
try {
|
|
3920
|
+
return !entry.disabled;
|
|
3921
|
+
} catch (_error) {
|
|
3922
|
+
return false;
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
/** Start invalidated entries the Loader still holds without a running fiber, then wait until it settles. */
|
|
3926
|
+
async function resume(ctx, invalidated) {
|
|
3927
|
+
const entries = new Set(ctx.loader.entries());
|
|
3928
|
+
for (const row of invalidated) if (entries.has(row) && !row.fiber?.uid) await row.update({}, false, true);
|
|
3644
3929
|
await ctx.loader.await();
|
|
3645
|
-
const failures = await inactiveEntries(ctx);
|
|
3646
|
-
const introduced = failures.filter((failure) => requiredIds.includes(failure.entry.options.id) || !previousFailures.some((previous) => previous.entry === failure.entry && previous.fiber === failure.entry.fiber && previous.options === JSON.stringify(failure.entry.options) && previous.diagnostic === inactiveDiagnostic(failure)));
|
|
3647
|
-
if (introduced.length > 0) throw new Error(activationDiagnostic(binName, introduced).trimEnd());
|
|
3648
|
-
for (const [index, result] of results.entries()) if (result.status === "rejected" && !previousFibers[index]?.failed) throw result.reason;
|
|
3649
|
-
ctx.emit("app-boot/config-reload");
|
|
3650
|
-
return failures.map(inactiveDiagnostic);
|
|
3651
3930
|
}
|
|
3652
3931
|
/**
|
|
3653
3932
|
* Load an optional patch-list file: a top-level YAML array of loader patch
|
|
@@ -3999,7 +4278,7 @@ const requiredStartupEntryIds = new Set([
|
|
|
3999
4278
|
"sdk-jsonrpc-server"
|
|
4000
4279
|
]);
|
|
4001
4280
|
/** Render plugin stacks, nested causes, and aggregate member failures once per error. */
|
|
4002
|
-
function formatActivationError(error) {
|
|
4281
|
+
function formatActivationError(error, stacks = true) {
|
|
4003
4282
|
const details = [];
|
|
4004
4283
|
const seen = /* @__PURE__ */ new Set();
|
|
4005
4284
|
function visit(value) {
|
|
@@ -4009,7 +4288,7 @@ function formatActivationError(error) {
|
|
|
4009
4288
|
}
|
|
4010
4289
|
if (seen.has(value)) return;
|
|
4011
4290
|
seen.add(value);
|
|
4012
|
-
details.push(value.stack ?? value.message);
|
|
4291
|
+
details.push(stacks ? value.stack ?? value.message : String(value));
|
|
4013
4292
|
if (value.cause !== void 0) visit(value.cause);
|
|
4014
4293
|
if (value instanceof AggregateError) value.errors.forEach(visit);
|
|
4015
4294
|
}
|
|
@@ -4035,12 +4314,23 @@ var StartupError = class extends Error {
|
|
|
4035
4314
|
});
|
|
4036
4315
|
}
|
|
4037
4316
|
};
|
|
4317
|
+
/** The error importing an entry's module raises now, which Node returns from its cache for a module that failed before. */
|
|
4318
|
+
async function importFailure(entry) {
|
|
4319
|
+
try {
|
|
4320
|
+
await entry.parent.tree.import(entry.options.name);
|
|
4321
|
+
return;
|
|
4322
|
+
} catch (error) {
|
|
4323
|
+
return error;
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4038
4326
|
/**
|
|
4039
4327
|
* Collect Loader activation failures and disabled-expression errors. Failed
|
|
4040
4328
|
* fibers are awaited to recover their recorded rejection reason and coalesce
|
|
4041
4329
|
* duplicate Loader notifications through the next process rejection checkpoint.
|
|
4330
|
+
* An entry whose import failed reports the error importing it raises now; without
|
|
4331
|
+
* `importNow` nothing is imported and the entry reports a failed import without the import error.
|
|
4042
4332
|
*/
|
|
4043
|
-
async function inactiveEntries(ctx) {
|
|
4333
|
+
async function inactiveEntries(ctx, importNow = true) {
|
|
4044
4334
|
const failures = [];
|
|
4045
4335
|
const rejectionReasons = [];
|
|
4046
4336
|
for (const entry of ctx.loader.entries()) {
|
|
@@ -4058,12 +4348,17 @@ async function inactiveEntries(ctx) {
|
|
|
4058
4348
|
continue;
|
|
4059
4349
|
}
|
|
4060
4350
|
const fiber = entry.fiber;
|
|
4061
|
-
if (fiber === void 0) {
|
|
4351
|
+
if (fiber === void 0 || fiber.state === FIBER_DISPOSED) {
|
|
4352
|
+
const error = importNow ? await importFailure(entry) : void 0;
|
|
4062
4353
|
failures.push({
|
|
4063
4354
|
entry,
|
|
4064
|
-
outcome: {
|
|
4355
|
+
outcome: error === void 0 ? {
|
|
4356
|
+
kind: "failed",
|
|
4357
|
+
error: fiber === void 0 ? "failed to import" : `fiber state ${String(fiber.state)}`
|
|
4358
|
+
} : {
|
|
4065
4359
|
kind: "failed",
|
|
4066
|
-
error
|
|
4360
|
+
error,
|
|
4361
|
+
phase: "failed to import"
|
|
4067
4362
|
}
|
|
4068
4363
|
});
|
|
4069
4364
|
continue;
|
|
@@ -4106,17 +4401,21 @@ async function inactiveEntries(ctx) {
|
|
|
4106
4401
|
return failures;
|
|
4107
4402
|
}
|
|
4108
4403
|
/** Render one failed plugin's original error and activation phase. */
|
|
4109
|
-
function failureDetail(outcome) {
|
|
4110
|
-
return `${outcome.phase === void 0 ? "" : `${outcome.phase}: `}${formatActivationError(outcome.error)}`;
|
|
4404
|
+
function failureDetail(outcome, stacks = true) {
|
|
4405
|
+
return `${outcome.phase === void 0 ? "" : `${outcome.phase}: `}${formatActivationError(outcome.error, stacks)}`;
|
|
4111
4406
|
}
|
|
4112
4407
|
/** Render optional-only warnings without changing startup policy. */
|
|
4113
|
-
function activationDiagnostic(binName, failures) {
|
|
4408
|
+
function activationDiagnostic(binName, failures, stacks = true) {
|
|
4114
4409
|
const noun = failures.length === 1 ? "entry" : "entries";
|
|
4115
|
-
return `${binName}: warning: ${String(failures.length)} ${noun} did not activate\n${failures.map(inactiveDiagnostic).join("\n")}\n`;
|
|
4410
|
+
return `${binName}: warning: ${String(failures.length)} ${noun} did not activate\n${failures.map((failure) => inactiveDiagnostic(failure, stacks)).join("\n")}\n`;
|
|
4411
|
+
}
|
|
4412
|
+
/** Per-entry reload text, which people read through the Web UI and agents, so it carries error messages without stack frames. */
|
|
4413
|
+
function reloadDiagnostic(failure) {
|
|
4414
|
+
return inactiveDiagnostic(failure, false);
|
|
4116
4415
|
}
|
|
4117
4416
|
/** Stable per-entry text for reload comparisons and optional warnings. */
|
|
4118
|
-
function inactiveDiagnostic({ entry, outcome }) {
|
|
4119
|
-
const detail = outcome.kind === "failed" ? failureDetail(outcome) : `pending (waiting for ${outcome.missing.length === 1 ? "service" : "services"}: ${outcome.missing.join(", ") || "unknown"})`;
|
|
4417
|
+
function inactiveDiagnostic({ entry, outcome }, stacks = true) {
|
|
4418
|
+
const detail = outcome.kind === "failed" ? failureDetail(outcome, stacks) : `pending (waiting for ${outcome.missing.length === 1 ? "service" : "services"}: ${outcome.missing.join(", ") || "unknown"})`;
|
|
4120
4419
|
return `${entry.options.id} (${entry.options.name}): ${detail}`;
|
|
4121
4420
|
}
|
|
4122
4421
|
/** Group startup failures and pending services, marking every required entry. */
|
|
@@ -4257,37 +4556,11 @@ async function boot(binName, absoluteConfigPath, patches, prepare, bareModuleBas
|
|
|
4257
4556
|
seen.add(deepest);
|
|
4258
4557
|
deepest = deepest.cause;
|
|
4259
4558
|
}
|
|
4260
|
-
const stack = deepest instanceof AggregateError ? `\n${deepest.stack ?? deepest.message}\n${deepest.errors.map(formatActivationError).join("\n")}` : deepest instanceof Error && deepest !== cause ? `\n${deepest.stack ?? deepest.message}` : "";
|
|
4559
|
+
const stack = deepest instanceof AggregateError ? `\n${deepest.stack ?? deepest.message}\n${deepest.errors.map((error) => formatActivationError(error)).join("\n")}` : deepest instanceof Error && deepest !== cause ? `\n${deepest.stack ?? deepest.message}` : "";
|
|
4261
4560
|
throw new Error(`${binName}: ${stage}: ${detail}${stack}`, { cause });
|
|
4262
4561
|
} finally {
|
|
4263
4562
|
await diagnostics.fiber.dispose();
|
|
4264
4563
|
}
|
|
4265
4564
|
}
|
|
4266
|
-
/** Prompt-section name for the harness-source location line an app bin adds after boot. */
|
|
4267
|
-
const HARNESS_SOURCE_SECTION = "harness:source";
|
|
4268
|
-
/**
|
|
4269
|
-
* Add a global prompt section naming the on-disk harness source checkout while
|
|
4270
|
-
* explicitly distinguishing it from the task workspace and current working
|
|
4271
|
-
* directory. The self-referential `dsh-tool-cordis` toolset reads and edits this
|
|
4272
|
-
* checkout. Call once on the settled boot context ({@link boot}); the section
|
|
4273
|
-
* uses the shared first-party placement after reusable instructions
|
|
4274
|
-
* and before the Web surface and persona suffix. A booted tree with no
|
|
4275
|
-
* `systemPrompt` service has no prompt to augment, so this is then a no-op
|
|
4276
|
-
* that returns `undefined`. The section is
|
|
4277
|
-
* registered against the `systemPrompt` service's fiber, so a dev HMR reload of
|
|
4278
|
-
* that plugin drops it until the next boot.
|
|
4279
|
-
* @param ctx - the settled boot context whose global system prompt to augment.
|
|
4280
|
-
* @param sourceRoot - the absolute path to the harness checkout root.
|
|
4281
|
-
* @returns the section disposer, or `undefined` when no `systemPrompt` service is mounted.
|
|
4282
|
-
*/
|
|
4283
|
-
function addHarnessSourceSection(ctx, sourceRoot) {
|
|
4284
|
-
const systemPrompt = ctx.get("systemPrompt");
|
|
4285
|
-
if (systemPrompt === void 0) return void 0;
|
|
4286
|
-
return systemPrompt.section({
|
|
4287
|
-
name: HARNESS_SOURCE_SECTION,
|
|
4288
|
-
order: systemPrompt.getSectionOrder("HARNESS_SOURCE"),
|
|
4289
|
-
text: `The DeepSeek Harness implementation checkout is at ${sourceRoot}. The checkout location and current working directory are separate values and may differ; never infer the working directory from this path. Use pwd to determine the current working directory. Use this checkout only to inspect or extend DSH itself.`
|
|
4290
|
-
});
|
|
4291
|
-
}
|
|
4292
4565
|
//#endregion
|
|
4293
|
-
export { DEFAULT_PROFILE_BUNDLES, FAIL_LOUD_RELEASE_TIMEOUT_MS,
|
|
4566
|
+
export { DEFAULT_PROFILE_BUNDLES, FAIL_LOUD_RELEASE_TIMEOUT_MS, LOADER_EXPRESSION_SCHEMA, OFFICIAL_ON_DEMAND_CATALOG, ON_DEMAND_BUNDLES, OPTIONAL_BUNDLES, PROFILES_DIR, PROFILE_COMPATIBILITY_FILENAME, PROFILE_PATCH_FILENAME, PROFILE_TEMPLATES, PluginPackages, ProfileRuntimeResolution, ResolutionRestartRequired, StartupError, auditStartupEntries, boot, bundlePatchFiles, bundlePatchPaths, composeEntries, createConfigProjector, createRuntimeResolution, evaluatePluginCompatibility, generateConfigSchema, getDshRuntimeVersion, initProfile, installFailLoud, isNativeConfigSchema, loadEnv, loadLayeredEnv, loadOptionalPatches, loadOverlayPatches, loadProfile, loadProfileDirectory, mountRootInclude, pluginCompatibilityWarning, prepareProfileEntries, prepareProfilePatches, readBundlePatches, readPluginMeta, readProfileCompatibility, readProfileManifest, readProfilePatches, readProfilePlugins, readProfileVersionExemptions, recomposeProfile, reconcileProfilePatches, reconcileProfilePlugins, removeLinkProjections, renderConfigDump, reportSkippedBundles, resolveBundleDir, resolveConfigPath, resolvePluginResource, resolveProfileDir, resolveTelemetryPatch, sanitizeProfile, setProfileVersionExemption, writeProfileBundles, writeProfileManifest };
|