feat(boot): isolate external bundles and expose the booted profile

Mount every external bundle layer as a cordis:contained-group entry
`bundle/<package>` whose rows carry a `<package>/<id>` prefix, so one
community bundle that no longer loads records a failure in the root
`pluginFailures` registry instead of rejecting the whole Loader
transaction, and two bundles may declare the same row id. Classify
layers by origin (`builtin` for the template bundle and
`dsh.profile.firstParty`, `external` for profile dependencies) and let
`dsh.bundle.stage: boot` or `dsh.profile.stages` opt a bundle out of
isolation when it provides a service built-in rows inject; a built-in
row left pending beside an isolated bundle still fails boot and names
the bundle.

Keep `installFailLoud` for the boot transaction only and install
runtime guards afterwards: an unhandled rejection is reported and
contained, an uncaught exception is reported and exits. Report nested
fiber failures under built-in entries as advisory lines.

Provide `ctx.profileRuntime` from the launcher (layers with trust and
stage, row origin, user-disabled rows, recomposition through the root
include) and probe installed packages in a child process
(`probePackage`, cached under `profiles/<name>/.dsh-plugins/`). Split
installation from enablement in `reconcileInstalledBundles`; the CLI
keeps install-means-enable. The plugin inventory now reports `trust`,
`package`, `disabledBy`, and `failure` per row and lists rows only the
failure registry knows.
This commit is contained in:
Yichen Jiang committed 2026-09-04 02:26:01 +08:00
1 parent b4ea3efcf6
commit d419e7e553
52 files changed
+2455 -99

No files matched your search

+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/user/develop/basic/publish.md
publish.md: 89a28e2e44a5171a3f5c48c003f5af5ec2e9e750
publish.zh.md: 590fee6f035af3009e25bf3669ba4321951f7d07
publish.md: 816b36ac99695c95332ae99439c1ccfc3c4a8732
publish.zh.md: c3918913d6cb09b212eae35902a375539bb047cb
+2
View File
@@ -109,6 +109,8 @@ dsh --profile demo
`dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer.
An installed bundle mounts as an external layer: the dump shows its rows inside one group named `bundle/dsh-hello-plugin`, and each row id carries the package prefix, so the row above is `dsh-hello-plugin/hello` in the mounted tree — a user patch that targets it names that id. A row of yours that fails to start is isolated and reported in the plugin list instead of stopping `dsh`; if your bundle provides a service the built-in rows inject, declare `dsh.bundle.stage: boot` so it mounts like a built-in one and fails loud. Prefixing does not rewrite string literals, so a `!!js` disabled expression that compares `e.options.id` to your own id should compare `e.options.name` instead.
## The loading order
The effective configuration composes over an empty root by applying, in order:
+2
View File
@@ -109,6 +109,8 @@ dsh --profile demo
`dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。
已安装的组合包作为外部层挂载:dump 会把它的行显示在一个名为 `bundle/dsh-hello-plugin` 的组里,每个行 id 带上包名前缀,因此上面那一行在挂载后的树里是 `dsh-hello-plugin/hello`——针对它的用户 patch 要写这个 id。你的某一行启动失败时会被隔离并在插件列表里报告,而不是让 `dsh` 停下;如果你的组合包提供内置行所注入的服务,请声明 `dsh.bundle.stage: boot`,让它像内置行一样挂载并明确失败。前缀不会改写字符串字面量,因此用 `e.options.id` 与自己 id 比较的 `!!js` disabled 表达式应改为比较 `e.options.name`。
## 加载顺序
生效配置在空根之上按以下顺序逐层组合: