@deepseek-ai/dsh-app-boot 0.1.5-rc.1 → 0.1.6-alpha.1
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 +2 -2
- package/README.md +37 -12
- package/README.zh.md +42 -17
- package/lib/index.js +1219 -185
- package/lib/types/index.d.ts +25 -30
- package/lib/types/profile-resolution/legacy-links.d.ts +35 -0
- package/lib/types/profile-resolution/resolver.d.ts +43 -0
- package/lib/types/profile-resolution/service.d.ts +51 -0
- package/lib/types/profile-resolution/worker-bootstrap.d.ts +3 -0
- package/lib/types/profile.d.ts +39 -10
- package/lib/types/watch-config.d.ts +13 -0
- package/lib/worker/profile-resolution-bootstrap.js +820 -0
- package/package.json +19 -13
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/boot/app-boot/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: b22a05eeced93c646c9ac4b554aca2775804a7fa
|
|
6
|
+
README.zh.md: c81373bcf803cae13c089ce8b2fee701c0d37d13
|
package/README.md
CHANGED
|
@@ -40,12 +40,12 @@ installFailLoud('dsh')
|
|
|
40
40
|
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
With that entry point,
|
|
43
|
+
With that entry point, startup keeps every plugin that can activate. An enabled failed plugin produces a labelled warning. A failed required entry makes startup dispose the whole app and exit nonzero; required ids absent from a profile and disabled required entries do not affect startup. The global required list covers shared Agent execution, application endpoints, and Web bootstrap/transport: `agent-loop`, `webserver`, `modules`, `connection`, `headless-runner`, `acp`, and `sdk-jsonrpc-server`.
|
|
44
44
|
|
|
45
45
|
<a id="profiles"></a>
|
|
46
46
|
### Profiles
|
|
47
47
|
|
|
48
|
-
Import profile and bundle declaration types from [`@deepseek-ai/dsh-package-manifest`](../../util/package-manifest/README.md). App-boot owns profile loading, JSON validation, and resolved runtime data.
|
|
48
|
+
Import profile and bundle declaration types from [`@deepseek-ai/dsh-package-manifest`](../../util/package-manifest/README.md). App-boot adapts `DshPackageManifest` to `ProfileManifest` with optional package identity because local profiles need no published version. App-boot owns profile loading, JSON validation, and resolved runtime data.
|
|
49
49
|
|
|
50
50
|
A profile is how one dsh installation ships different app surfaces: `web`, `headless`, `acp`, `sdk`, and `sdk-minimal` start distinct compositions from the same launcher. A profile lives at `$DSH_HOME/profiles/<name>` and combines installable bundles, its own `cordis.patch.yml`, and `patchReload: live | startup`; omitted reload policy keeps the historical `live` default for custom profiles. The shipped `web` template uses live reload, while the other shipped templates apply patches only at startup. `sdk-minimal` names only its standalone bundle; the other templates retain base-plus-mode stacks. `dsh --profile <name> --from-default-profile <template>` creates a custom profile at a new non-shipped name from one shipped template, while `dsh plugin` initializes a base-backed profile and manages its installed bundles. A missing bundle or one without a patch declaration fails startup loudly. Application-owned npm projects, such as Electron's reserved Desktop profile, use `loadProfileDirectory` to load an already initialized directory without exposing it through CLI profile lookup.
|
|
51
51
|
|
|
@@ -54,17 +54,38 @@ Your machine-local preferences also live in the Harness home:
|
|
|
54
54
|
- **`.env`** — your ordinary environment layers: the invoking directory's file outranks the Harness-home file, and both sit below the inherited environment. Variables that decide how the process starts (`PATH`, `DSH_*`, `XDG_*` and similar) are rejected from files: export them instead. The four proxy names (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY`) are accepted from the Harness-home file only, never from the invoking directory's, which arrives with a clone. For a non-product bin that just wants one directory's `.env`, a missing file is fine and an unloadable one prints one labelled warning line.
|
|
55
55
|
- **`cordis.patch.yml`** — your tweak layer, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): replace one entry's whole config (restating the fields you keep), insert new entries, or interpolate `!!js` expressions at boot. A patch naming an entry that does not exist prints a stderr warning; an empty or comments-only file fails boot — disable the layer with `[]` instead.
|
|
56
56
|
|
|
57
|
-
Profiles with `patchReload: live` watch both user patch files
|
|
57
|
+
Profiles with `patchReload: live` watch both user patch files and apply the [reload failure policy](#startup-and-reload-failures). A `startup` profile installs neither those watchers nor the launcher's watch-only HMR fallback.
|
|
58
58
|
|
|
59
59
|
Inserted plugin names may be absolute filesystem paths, file URLs, or package specifiers. Patch loading converts absolute paths and patch-relative `./` or `../` paths to file URLs within `insert` rows and their nested groups; existing-entry name assertions and replacement `config` values remain literal.
|
|
60
60
|
|
|
61
|
+
Before mounting profile rows, the `dsh` launcher computes one immutable package-resolution generation from the installation and ordered bundle dependency graphs. The default link mode materializes the existing shared and profile-owned fallback links, so supported launch behavior stays unchanged. Internal callers and test harnesses can instead install the generation through Node's ESM and CommonJS resolvers in runtime mode, or materialize and verify the same generation in dual mode.
|
|
62
|
+
|
|
61
63
|
### Previewing the effective configuration
|
|
62
64
|
|
|
63
65
|
Before you boot, you can print the exact configuration the app will mount: the dump shows the composed entry list with `!!js` expressions verbatim, grouped under comments naming each source file and the patch layers that changed it, as one loadable YAML document. Patches that match no row are reported with their layer label; a missing, unparsable, or invalid config fails the dump.
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
<a id="startup-and-reload-failures"></a>
|
|
68
|
+
### Startup and reload failures
|
|
69
|
+
|
|
70
|
+
After the Loader settles, app-boot reports optional failures as warnings and rejects startup if an enabled required entry cannot activate. In the table, stopping startup means disposing any mounted plugins and exiting nonzero without reporting readiness; continuing keeps successful plugins running. Later configuration HMR does not repeat the required-startup audit and does not roll back the whole update.
|
|
71
|
+
|
|
72
|
+
| Failure pattern | Optional entry at startup | Required entry at startup | Later configuration HMR |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| Root config or required overlay is missing, unreadable, malformed, or contains invalid entries | Stop startup | Stop startup | Malformed or invalid live patches are rejected without changing the running configuration; a valid edit applies |
|
|
75
|
+
| Module import fails or module evaluation throws | Warn; continue | Stop startup | Report the error; keep successful siblings; a corrected import can activate |
|
|
76
|
+
| Plugin config schema validation fails | Warn; continue | Stop startup | A new entry stays inactive; an existing entry retains its prior instance and config; a valid correction applies |
|
|
77
|
+
| Config `!!js` evaluation throws | Warn; continue | Stop startup | Report the error; keep successful siblings; a valid correction can activate |
|
|
78
|
+
| `disabled: !!js` evaluation throws | Warn; continue | Stop startup | Report the evaluation error rather than treating the entry as disabled; a valid correction can activate |
|
|
79
|
+
| Synchronous `apply()` throws | Warn; continue | Stop startup | Report the error; keep successful siblings; corrected config can activate |
|
|
80
|
+
| Asynchronous `apply()` throws | Warn after settlement; continue | Stop startup after settlement | Report the error after settlement; keep successful siblings; corrected config can activate |
|
|
81
|
+
| An injected service is unavailable | Warn; continue while the entry waits for its dependencies | Stop startup | Keep the entry waiting; adding the missing provider can activate it |
|
|
82
|
+
| HTTP port binding fails | Warn; continue without that endpoint | Stop startup | Keep the process running without the failed endpoint; corrected config can restore it |
|
|
83
|
+
| Detached asynchronous work outside the `apply()` return Promise produces an unhandled rejection | Fatal: dispose the app and exit nonzero | Fatal: dispose the app and exit nonzero | Fatal: dispose the app and exit nonzero, regardless of entry id |
|
|
84
|
+
| Entry is absent or explicitly disabled | Ignore it | Ignore it | Do not activate it; no required-startup audit |
|
|
85
|
+
|
|
86
|
+
The required list above includes `modules` and `connection`; Web startup cannot succeed when either enabled entry fails. Failure of an optional provider can also prevent a required consumer from activating. Schema rejection before an existing entry updates is not a transactional rollback of sibling changes.
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
The [Web process matrix](../../../apps/cli/tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts) and [startup acceptance](../../../apps/cli/tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts) verify these outcomes through the shipped Web profile; [app-boot tests](tests/app-boot.spec.ts) also exercise root Include failures.
|
|
68
89
|
|
|
69
90
|
If your app owns the terminal, it can hand the terminal back before the process exits, so your shell is never left in raw mode. The handoff is bounded: a stuck cleanup delays the fatal exit but never cancels it.
|
|
70
91
|
|
|
@@ -84,11 +105,14 @@ This section explains how the outcomes above are realized and points at the code
|
|
|
84
105
|
|
|
85
106
|
### Design notes
|
|
86
107
|
|
|
87
|
-
- **
|
|
108
|
+
- **Process-local module resolution.** Runtime and dual modes install one generation on Node's internal ESM and CommonJS resolvers before profile rows mount; link mode leaves both resolvers unchanged. Node still owns exports, conditions, subpaths, module caches, and error codes; routed ESM failures report the original importer instead of the internal lookup anchor. `ctx.pluginPackages` exposes package metadata from the same generation without recording Entry imports; an installed generation is authoritative even for a miss, while low-level embedders that install the service without one retain native lookup.
|
|
88
109
|
- **Two Loader builtins.** `mountRootInclude` registers `cordis:include` and `cordis:group` as Loader builtins: a group row gives one `isolate` realm to a provider and its consumers together, and an agent preset outside this workspace cannot resolve `@deepseek-ai/cordis-plugin-group` by name. Both load through the ambient module pipeline rather than the included tree's own specifier resolution.
|
|
89
|
-
- **
|
|
110
|
+
- **Consumer-owned strictness.** Ordinary Loader groups keep successful siblings. App-boot applies the global required-entry policy after initial settlement; agent presets and dynamic multi-entry compositions own and dispose their separate generation when they require all-or-nothing setup. App-boot reads failed fibers to report their recorded errors and coalesces duplicate Loader rejection notifications through one process checkpoint.
|
|
111
|
+
- **One fallback generation.** The installation-first and ordered-bundle breadth-first traversal produces both the runtime table and the retained disk materializer. Runtime mode creates no resolution links and ignores stale projections at their former lookup positions. External bare targets selected by package `imports` use the same package order, while Node retains mapping, conditions, and exact target resolution. Link mode materializes the same table; dual mode also compares Node's disk result with the table. A complete successor may add package names atomically, while changing or removing an existing mapping requires restart.
|
|
112
|
+
- **Owned Workers.** Worker build banners import `@deepseek-ai/dsh-app-boot/worker/profile-resolution-bootstrap` before bundled business code. Each Worker installs the structured-cloned generation in its own isolate. The bootstrap bundle has no static package imports. Source Worker entries retain their self-contained dependency closure, and third-party Workers receive no injection.
|
|
113
|
+
- **Update completion.** App boot observes restart failures through the `internal/update` waterfall. Live patch reloads wait for the tree's fibers before auditing activation; `Fiber.update()` and `Entry.update()` alone do not establish restart success.
|
|
90
114
|
- **One rejection checkpoint.** `assertEntriesActivated` keeps the exact reasons it folds into the boot diagnostic visible through the next process rejection checkpoint, so `installFailLoud` coalesces Loader's duplicate notification while unrelated unhandled rejections remain fatal.
|
|
91
|
-
- **Two-stage failure labels.** `boot()` distinguishes `host preparation failed` — `prepare` threw before any config-tree entry mounted — from `plugin tree failed to load`, and appends the deepest plugin error's stack
|
|
115
|
+
- **Two-stage failure labels.** `boot()` distinguishes `host preparation failed` — `prepare` threw before any config-tree entry mounted — from `plugin tree failed to load`, and appends the deepest plugin error's stack. Plugin diagnostics retain nested causes and aggregate member failures; cyclic causes stop traversal without replacing the original error.
|
|
92
116
|
|
|
93
117
|
### Helper behavior
|
|
94
118
|
|
|
@@ -100,7 +124,8 @@ The exports each own one stage of the boot: config resolution and snapshot repla
|
|
|
100
124
|
|---|---|
|
|
101
125
|
| [`src/index.ts`](src/index.ts) | Boot helpers: config resolution, environment loading, fail-loud guard, activation audit, patch parsing, config dump, harness-source section |
|
|
102
126
|
| [`src/profile.ts`](src/profile.ts) | Profile discovery, initialization, bundle resolution, module fallback |
|
|
103
|
-
|
|
|
127
|
+
| [`src/profile-resolution/`](src/profile-resolution/) | Runtime resolver, package-metadata service, and built Worker bootstrap |
|
|
128
|
+
| — | No runtime invariant companion is published; one registration owns each resolver generation, and dual mode compares the independently materialized result at resolution time. |
|
|
104
129
|
|
|
105
130
|
</details>
|
|
106
131
|
|
|
@@ -118,7 +143,7 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
118
143
|
- [dsh-home-paths](../../util/home-paths/README.md) — the Harness-home resolver (`resolveDshHome`).
|
|
119
144
|
- [Configuration source ownership](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md) — why a discovered file may not decide bootstrap behavior.
|
|
120
145
|
- [Profile plugin bundles](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md) — the profile and bundle composition design.
|
|
121
|
-
- [User-patch HMR tests](../../../.agents/notes/implemented/testing/2026-09-09-user-patch-hmr-test-delivery.md) — ownership of
|
|
146
|
+
- [User-patch HMR tests](../../../.agents/notes/implemented/testing/2026-09-09-user-patch-hmr-test-delivery.md) — ownership of live-patch behavior and native filesystem delivery.
|
|
122
147
|
|
|
123
148
|
-----
|
|
124
149
|
|
|
@@ -138,7 +163,7 @@ Boot itself changes no request prefix. `addHarnessSourceSection` places its sour
|
|
|
138
163
|
|
|
139
164
|
These limits describe when this boot library is a poor fit or needs special care. They are current package constraints, not a task backlog.
|
|
140
165
|
|
|
141
|
-
- **
|
|
166
|
+
- **Runtime resolution depends on Node internals** — supported Node versions require the native builtin-access addon and executable compatibility coverage. Only built Harness-owned Workers receive the generation bootstrap; third-party Workers and custom `vm` linkers keep native resolution.
|
|
142
167
|
- **Snapshot replay swapping is basename-specific** — only a config ending in `cordis.yml` or `cordis.yaml` maps to the sibling `cordis.snapshot.yml`; custom config names require caller-managed selection.
|
|
143
168
|
- **Environment discovery is launch-scoped** — `loadLayeredEnv` reads only the invocation directory and Harness home once; it does not search parents or follow a workspace selected later. `loadEnv` remains the one-directory helper for non-product bins.
|
|
144
169
|
- **A user patch replaces the whole matched config** — an id-targeted patch does not deep-merge, so a profile override restates the bundle fields it keeps.
|
|
@@ -153,6 +178,6 @@ This Dev Note is working context for maintainers: open design questions and dire
|
|
|
153
178
|
|
|
154
179
|
#### Open: config dump stability
|
|
155
180
|
|
|
156
|
-
`renderConfigDump` output is a loadable YAML document whose `# ==`
|
|
181
|
+
`renderConfigDump` output is a loadable YAML document whose `# ==` source comments and `!!js`-verbatim rendering serve the `--dump-config` diagnostic. Nothing promises byte stability across package versions; decide whether the dump becomes a serialization contract before anything consumes it programmatically.
|
|
157
182
|
|
|
158
183
|
</details>
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-library"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-app-boot` 是 `dsh` profile(包括 Python 运行时 wheel
|
|
12
|
+
`dsh-app-boot` 是 `dsh` profile(包括 Python 运行时 wheel 包所含的 CLI(命令行界面))背后的共享 Loader 启动库。它加载环境层、组合 profile 组合包与 patch、启动每个插件,再返回运行中的应用,或指出失败插件与原因。产品应用使用 `dsh` launcher 而不发布单独 bin;直接配置 helper 只保留给低层嵌入方与测试。你还可以在启动前预览生效配置,按 profile 选择实时或仅启动时应用 patch,并让持有终端的应用在致命退出前恢复终端。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ kind: "package-library"
|
|
|
29
29
|
|
|
30
30
|
### 何时使用
|
|
31
31
|
|
|
32
|
-
在实现共享 `dsh` launcher 或嵌入其低层启动 helper 时使用它。产品功能应放入 profile
|
|
32
|
+
在实现共享 `dsh` launcher 或嵌入其低层启动 helper 时使用它。产品功能应放入 profile 组合包,而不是新增应用 bin;只向已运行应用添加插件的代码直接挂载插件即可。
|
|
33
33
|
|
|
34
34
|
### 启动应用
|
|
35
35
|
|
|
@@ -40,35 +40,56 @@ installFailLoud('dsh')
|
|
|
40
40
|
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
有了这个入口,启动会保留所有能够激活的插件。启用但失败的插件会产生带标签的警告。required entry 失败时,启动会拆卸整个应用并以非零码退出;profile 中不存在的 required id 和已禁用的 required entry 不影响启动。全局 required list 覆盖共享 Agent 执行、应用 endpoint,以及 Web 启动与传输:`agent-loop`、`webserver`、`modules`、`connection`、`headless-runner`、`acp` 和 `sdk-jsonrpc-server`。
|
|
44
44
|
|
|
45
45
|
<a id="profiles"></a>
|
|
46
46
|
### Profile
|
|
47
47
|
|
|
48
|
-
Profile
|
|
48
|
+
Profile 与组合包的声明类型从 [`@deepseek-ai/dsh-package-manifest`](../../util/package-manifest/README.zh.md) 导入。App-boot 将 `DshPackageManifest` 适配为包身份可选的 `ProfileManifest`,因为本地 profile 无需发布版本。App-boot 负责 profile 加载、JSON 校验和解析后的运行时数据。
|
|
49
49
|
|
|
50
|
-
profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/<name
|
|
50
|
+
profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/<name>`,由可安装组合包、自身 `cordis.patch.yml` 与 `patchReload: live | startup` 组成;自定义 profile 省略 reload 策略时保留历史 `live` 默认值。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立组合包,其他模板保留 base 加模式的组合包栈。`dsh --profile <name> --from-default-profile <template>` 从一个随附模板,在新的非内置名称处创建自定义 profile;`dsh plugin` 则初始化以 base 为基础的 profile,并管理其中安装的组合包。缺失组合包或未声明 patch 的组合包会让启动明确失败。由应用持有的 npm 项目(例如 Electron 保留的 Desktop profile)通过 `loadProfileDirectory` 加载已经初始化的目录,而不会将它暴露给 CLI profile 查找。
|
|
51
51
|
|
|
52
52
|
你的机器本地偏好同样位于 harness home 中:
|
|
53
53
|
|
|
54
|
-
- **`.env`**——你的普通环境层:调用目录的文件优先于 harness home
|
|
55
|
-
- **`cordis.patch.yml`**——你的 tweak 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home
|
|
54
|
+
- **`.env`**——你的普通环境层:调用目录的文件优先于 harness home 的文件,两者都低于继承环境。在文件中设置的进程启动变量(如 `PATH`、`DSH_*`、`XDG_*`)会被拒绝:请改为导出这些变量。四个代理名(`HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY`、`NO_PROXY`)只从 harness home 的文件接受,绝不从调用目录的文件接受——后者随 clone 一起到来。对于只想加载某个目录 `.env` 的非产品 bin,文件缺失不影响启动,文件无法加载时输出一行带标签的警告。
|
|
55
|
+
- **`cordis.patch.yml`**——你的 tweak 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):替换某个条目的整个配置(重述你要保留的字段)、插入新条目,或在启动时插值 `!!js` 表达式。patch 指定的条目不存在时输出 stderr 警告;空文件或仅含注释的文件会导致启动失败——如需禁用该层,请改用 `[]`。
|
|
56
56
|
|
|
57
|
-
带 `patchReload: live` 的 profile 会监视两份用户 patch
|
|
57
|
+
带 `patchReload: live` 的 profile 会监视两份用户 patch 文件,并应用[重载失败策略](#startup-and-reload-failures)。`startup` profile 既不安装这些监视器,也不安装 launcher 的仅监视 HMR(热模块替换)回退。
|
|
58
58
|
|
|
59
59
|
插入条目的插件名可以是绝对文件系统路径、文件 URL 或包标识符。patch 加载会把 `insert` 条目及其嵌套分组中的绝对路径以及相对于 patch 文件的 `./` 或 `../` 路径转换为文件 URL;对已有条目名称的断言及替换用的 `config` 值保持原样。
|
|
60
60
|
|
|
61
|
+
挂载 profile 条目前,`dsh` launcher 会从安装依赖图与有序 bundle 依赖图计算一份不可变的 package resolution generation。默认 link 模式会物化现有的共享 fallback 链接与 profile 自有 fallback 链接,因此受支持的启动行为保持不变。内部调用方和测试工具可以改用 runtime 模式,把 generation 安装到 Node 的 ESM 与 CommonJS resolver;也可以使用 dual 模式,同时物化并校验同一份 generation。
|
|
62
|
+
|
|
61
63
|
### 预览生效配置
|
|
62
64
|
|
|
63
65
|
启动前,你可以打印应用将挂载的确切配置:dump 会以 `!!js` 表达式原样展示组合后的条目列表,并按注释分组标明每个源文件及其 patch 层,输出是一份可加载的 YAML 文档。未匹配到任何行的 patch 会连同其层标签一起报告;配置缺失、无法解析或字段无效都会使 dump 失败。
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
<a id="startup-and-reload-failures"></a>
|
|
68
|
+
### 启动与重载失败
|
|
69
|
+
|
|
70
|
+
Loader 结算后,app-boot 将 optional 失败报告为警告;若已启用的 required 条目无法激活,则拒绝启动。表中的“终止启动”指释放已挂载插件并以非零码退出,不报告就绪;“继续”指保留成功运行的插件。后续配置 HMR 不会再次执行 required 启动审计,也不会回滚整个更新。
|
|
71
|
+
|
|
72
|
+
| 失败模式 | Optional 条目启动时 | Required 条目启动时 | 后续配置 HMR |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| 根配置或必需 overlay 缺失、不可读、格式错误,或包含无效条目 | 终止启动 | 终止启动 | 拒绝格式错误或无效的实时 patch,不改变运行中的配置;有效修改可以应用 |
|
|
75
|
+
| 模块 import 失败或模块求值抛出异常 | 警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;修正 import 后可以激活 |
|
|
76
|
+
| 插件配置 schema 校验失败 | 警告;继续 | 终止启动 | 新条目保持未激活;现有条目保留原实例与配置;有效修正可以应用 |
|
|
77
|
+
| 配置 `!!js` 求值抛出异常 | 警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;有效修正后可以激活 |
|
|
78
|
+
| `disabled: !!js` 求值抛出异常 | 警告;继续 | 终止启动 | 报告求值错误,不将条目当作已禁用;有效修正后可以激活 |
|
|
79
|
+
| 同步 `apply()` throw | 警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;修正配置后可以激活 |
|
|
80
|
+
| 异步 `apply()` throw | 结算后警告;继续 | 结算后终止启动 | 结算后报告错误;保留成功的兄弟插件;修正配置后可以激活 |
|
|
81
|
+
| 注入的服务不可用 | 警告;继续,条目等待依赖 | 终止启动 | 条目继续等待;补上缺失的提供方后可以激活 |
|
|
82
|
+
| HTTP 端口绑定失败 | 警告;继续,但该端点不可用 | 终止启动 | 进程继续运行,但失败的端点不可用;修正配置后可以恢复 |
|
|
83
|
+
| 脱离 `apply()` 返回 Promise 的异步任务产生未处理 rejection | 致命错误:释放应用并以非零码退出 | 致命错误:释放应用并以非零码退出 | 致命错误:释放应用并以非零码退出,与条目 id 无关 |
|
|
84
|
+
| 条目缺失或被显式禁用 | 忽略 | 忽略 | 不激活该条目;不执行 required 启动审计 |
|
|
85
|
+
|
|
86
|
+
上面的 required 列表包含 `modules` 与 `connection`;只要其中一个已启用条目失败,Web 就无法成功启动。Optional 提供方失败也可能使 required 消费方无法激活。现有条目的新配置在更新前被 schema 校验拒绝,并不等于对兄弟插件的变更做事务回滚。
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
[Web 进程矩阵](../../../apps/cli/tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts)和[启动验收测试](../../../apps/cli/tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts)通过随附 Web profile 验证这些结果;[app-boot 测试](tests/app-boot.spec.ts)还覆盖根 Include 失败。
|
|
68
89
|
|
|
69
90
|
如果你的应用持有终端,它可以在进程退出前把终端交还,你的 shell 绝不会残留在 raw 模式。交还过程有界:卡住的清理只会延迟致命退出,而不会取消它。
|
|
70
91
|
|
|
71
|
-
### 告诉 agent
|
|
92
|
+
### 告诉 agent(智能体)harness 所在位置
|
|
72
93
|
|
|
73
94
|
当你的应用启动模型驱动的 agent 时,你可以告诉 agent DSH 实现代码 checkout 的位置:它得知该路径,也知道不得据此推断工作目录——它应使用 `pwd`。这条指示在系统提示词靠前位置出现一次。没有系统提示词服务的应用会跳过;开发环境中,重新加载系统提示词后它会消失,直至下次启动。
|
|
74
95
|
|
|
@@ -84,11 +105,14 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
|
|
|
84
105
|
|
|
85
106
|
### 设计说明
|
|
86
107
|
|
|
87
|
-
-
|
|
108
|
+
- **进程内模块解析。** runtime 和 dual 模式会在挂载 profile 条目前,将一份 generation 安装到 Node 的 ESM 与 CommonJS 内部 resolver;link 模式不修改这两个 resolver。exports、conditions、subpath、模块缓存和错误码仍由 Node 负责;路由后的 ESM 失败会报告原始 importer,而不是内部查找锚点。`ctx.pluginPackages` 从同一 generation 提供 package metadata,不记录 Entry import;安装 generation 后,即使查询未命中也以 generation 为准,仅安装服务而未提供 generation 的底层嵌入方仍使用 Node 原生查找。
|
|
88
109
|
- **两个 Loader builtin。** `mountRootInclude` 把 `cordis:include` 与 `cordis:group` 注册为 Loader builtin:group 行能把一个提供方与它的消费方放进同一个 `isolate` realm,而位于本工作区之外的 agent preset 无法按名称解析 `@deepseek-ai/cordis-plugin-group`。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析。
|
|
89
|
-
-
|
|
110
|
+
- **由 consumer 持有严格语义。** 普通 Loader group 保留成功 sibling。App-boot 在首次结算后应用全局 required-entry policy;agent preset 与动态多 entry 组合在需要 all-or-nothing setup 时,持有并拆卸各自的独立 generation。App-boot 读取 failed fiber 来报告已记录的错误,并在一个进程检查点内合并 Loader 重复的 rejection 通知。
|
|
111
|
+
- **唯一 fallback generation。** 安装优先、有序 bundle 逐根 breadth-first 遍历同时生成运行时表和保留的磁盘 materializer。runtime 模式不创建解析链接,并在旧链接原来的查找位置忽略陈旧投影。package `imports` 选中的外部 bare target 使用相同的选包顺序,映射、conditions 和精确 target 解析仍由 Node 负责。link 模式物化同一张表;dual 模式还会比较 Node 的磁盘结果与表。完整后继 generation 可以原子增加 package name,修改或删除既有映射则要求重启。
|
|
112
|
+
- **自有 Worker。** Worker 构建 banner 会在业务 bundle 前导入 `@deepseek-ai/dsh-app-boot/worker/profile-resolution-bootstrap`。每个 Worker 在自己的 isolate 中安装结构化克隆的 generation。bootstrap bundle 不静态导入任何包。源码 Worker 入口保留自包含依赖,第三方 Worker 不接受注入。
|
|
113
|
+
- **更新完成。** App boot 通过 `internal/update` waterfall 观察重启失败。实时 patch 重载在检查激活状态前等待配置树中的 fiber;单独调用 `Fiber.update()` 或 `Entry.update()` 不能确定重启成功。
|
|
90
114
|
- **单一 rejection 检查点。** `assertEntriesActivated` 把折入启动诊断的确切原因保持到下一个进程级 rejection 检查点可见,使 `installFailLoud` 能合并 Loader 的重复通知,而所有无关的未处理 rejection 仍然致命。
|
|
91
|
-
- **两阶段失败标签。** `boot()` 区分 `host preparation failed`(`prepare` 在任何配置树条目挂载前抛出)与 `plugin tree failed to load
|
|
115
|
+
- **两阶段失败标签。** `boot()` 区分 `host preparation failed`(`prepare` 在任何配置树条目挂载前抛出)与 `plugin tree failed to load`,并追加最深层插件错误的堆栈。插件诊断保留嵌套原因和聚合错误中的各项失败;原因链出现循环时会停止遍历,但不会替换原始错误。
|
|
92
116
|
|
|
93
117
|
### Helper 行为
|
|
94
118
|
|
|
@@ -100,7 +124,8 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
|
|
|
100
124
|
|---|---|
|
|
101
125
|
| [`src/index.ts`](src/index.ts) | 启动 helper:配置解析、环境加载、会明确报错的保护机制、激活审计、patch 解析、配置 dump、harness 源码段落 |
|
|
102
126
|
| [`src/profile.ts`](src/profile.ts) | profile 发现、初始化、组合包解析、模块后备机制 |
|
|
103
|
-
|
|
|
127
|
+
| [`src/profile-resolution/`](src/profile-resolution/) | 运行时 resolver、package metadata 服务与构建后 Worker bootstrap |
|
|
128
|
+
| — | 不发布运行时不变式伴生入口;每个 resolver generation 只有一个 registration 所有,dual 模式在解析时比较独立物化的结果。 |
|
|
104
129
|
|
|
105
130
|
</details>
|
|
106
131
|
|
|
@@ -118,7 +143,7 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
|
|
|
118
143
|
- [dsh-home-paths](../../util/home-paths/README.zh.md)——harness home 解析器(`resolveDshHome`)。
|
|
119
144
|
- [配置来源归属](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.zh.md)——被发现的文件为何不得决定 bootstrap 行为。
|
|
120
145
|
- [Profile 插件组合包](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md)——profile 与组合包组合设计。
|
|
121
|
-
- [用户 patch HMR 测试](../../../.agents/notes/implemented/testing/2026-09-09-user-patch-hmr-test-delivery.zh.md)
|
|
146
|
+
- [用户 patch HMR 测试](../../../.agents/notes/implemented/testing/2026-09-09-user-patch-hmr-test-delivery.zh.md)——实时 patch 行为与原生文件系统投递的验证归属。
|
|
122
147
|
|
|
123
148
|
-----
|
|
124
149
|
|
|
@@ -138,7 +163,7 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
|
|
|
138
163
|
|
|
139
164
|
这些限制说明此启动库在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。
|
|
140
165
|
|
|
141
|
-
-
|
|
166
|
+
- **运行时解析依赖 Node 内部机制**——受支持的 Node 版本需要 native builtin access addon 和可执行兼容验证。只有构建后的 Harness 自有 Worker 接收 generation bootstrap;第三方 Worker 与自定义 `vm` linker 保持原生解析。
|
|
142
167
|
- **快照回放替换仅识别特定 basename**——只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。
|
|
143
168
|
- **环境发现以启动为界**——`loadLayeredEnv` 只读取一次调用目录与 harness home 中的 `.env`;它不搜索父目录,也不跟随之后选择的 workspace。`loadEnv` 仍是非产品 bin 使用的单目录 helper。
|
|
144
169
|
- **用户 patch 会替换匹配到的整个配置**——按 id 定位的 patch 不做深度合并,因此 profile 覆盖必须重述需要保留的组合包字段。
|