@deepseek-ai/dsh-app-boot 0.2.1-alpha.1 → 0.2.1-alpha.3
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 +20 -23
- package/README.md +14 -14
- package/README.zh.md +14 -14
- package/lib/index.js +399 -87
- package/lib/types/index.d.ts +43 -21
- package/lib/types/official-bundle-packages.d.ts +4 -0
- package/lib/types/official-bundles.d.ts +12 -0
- package/lib/types/official-bundles.generated.d.ts +5 -0
- package/lib/types/package-meta.d.ts +1 -1
- package/lib/types/profile-context.d.ts +7 -0
- package/lib/types/profile-resolution/legacy-links.d.ts +8 -1
- package/lib/types/profile-resolution/resolver.d.ts +14 -6
- package/lib/types/profile-resolution/service.d.ts +3 -1
- package/lib/worker/profile-resolution-bootstrap.js +133 -11
- package/package.json +21 -22
package/README.i18n.yaml
CHANGED
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
en: 2fdfe02f2ed4a258
|
|
22
22
|
zh: d748228ece579c4a
|
|
23
23
|
/deepseek-ai-dsh-app-boot/use-this-package/starting-the-app:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: 4a424cd401556f20
|
|
25
|
+
zh: 0b34dd18e0db6cea
|
|
26
26
|
/deepseek-ai-dsh-app-boot/use-this-package/profiles:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: 8cc455f3711b5ab3
|
|
28
|
+
zh: e3f1847bc3fcd2cf
|
|
29
29
|
/deepseek-ai-dsh-app-boot/use-this-package/previewing-the-effective-configuration:
|
|
30
30
|
en: 646a0ccdd7881ad1
|
|
31
31
|
zh: e3696acb1573dd7b
|
|
@@ -33,38 +33,35 @@
|
|
|
33
33
|
en: c4a301652b851ce9
|
|
34
34
|
zh: 784871e19cd9db0a
|
|
35
35
|
/deepseek-ai-dsh-app-boot/use-this-package/reading-plugin-display-metadata:
|
|
36
|
-
en:
|
|
37
|
-
zh:
|
|
36
|
+
en: a9afb263b03d112c
|
|
37
|
+
zh: 31f5e797cd953c15
|
|
38
38
|
/deepseek-ai-dsh-app-boot/use-this-package/startup-and-reload-failures:
|
|
39
|
-
en:
|
|
40
|
-
zh:
|
|
41
|
-
/deepseek-ai-dsh-app-boot/use-this-package/telling-the-agent-where-the-harness-lives:
|
|
42
|
-
en: 07c459f26f4f8517
|
|
43
|
-
zh: 210f1e4894731b1a
|
|
39
|
+
en: dc13dfa1f79292e4
|
|
40
|
+
zh: d51682ced7a18ac2
|
|
44
41
|
/deepseek-ai-dsh-app-boot/understand-the-implementation:
|
|
45
42
|
en: f4a2695cb4c8a570
|
|
46
43
|
zh: 89837235d2f376cf
|
|
47
44
|
/deepseek-ai-dsh-app-boot/understand-the-implementation/design-notes:
|
|
48
|
-
en:
|
|
49
|
-
zh:
|
|
45
|
+
en: 9ecb744556705fd1
|
|
46
|
+
zh: d83aaf2ebb91d0fc
|
|
50
47
|
/deepseek-ai-dsh-app-boot/understand-the-implementation/helper-behavior:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
48
|
+
en: 6fa850d1bb000d0e
|
|
49
|
+
zh: aa83a40a9d3db295
|
|
53
50
|
/deepseek-ai-dsh-app-boot/understand-the-implementation/source-map:
|
|
54
|
-
en:
|
|
55
|
-
zh:
|
|
51
|
+
en: 541700d5c39e0ab4
|
|
52
|
+
zh: 7522dbd9cd68c930
|
|
56
53
|
/deepseek-ai-dsh-app-boot/further-exploration:
|
|
57
54
|
en: 401269cc5ea8593e
|
|
58
55
|
zh: cd337c356b320565
|
|
59
56
|
/deepseek-ai-dsh-app-boot/model-experience:
|
|
60
|
-
en:
|
|
61
|
-
zh:
|
|
57
|
+
en: d2d0a21502d88c50
|
|
58
|
+
zh: 82a60c7a490a6834
|
|
62
59
|
/deepseek-ai-dsh-app-boot/model-experience/kv-cache-effect:
|
|
63
|
-
en:
|
|
64
|
-
zh:
|
|
60
|
+
en: 5a1d9344c8fdc790
|
|
61
|
+
zh: 0b32579f658b2ca1
|
|
65
62
|
/deepseek-ai-dsh-app-boot/known-limitations-and-deferred-work:
|
|
66
|
-
en:
|
|
67
|
-
zh:
|
|
63
|
+
en: 1400c4f22a79b59a
|
|
64
|
+
zh: 269f6c91584aec3e
|
|
68
65
|
/deepseek-ai-dsh-app-boot/known-limitations-and-deferred-work/dev-note:
|
|
69
66
|
en: d2f0d68fe29fb750
|
|
70
67
|
zh: 323d21ee73a9e3f6
|
package/README.md
CHANGED
|
@@ -40,14 +40,14 @@ installFailLoud('dsh')
|
|
|
40
40
|
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
`installFailLoud` writes one labelled `util.inspect` diagnostic to stderr for an unhandled rejection or an uncaught exception, awaits the surface's release hook under a fixed timeout, and exits 1; control never returns to the failed operation, because only the throw site knows which state is intact, and the event loop runs only until the release settles or times out. 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`.
|
|
43
|
+
`installFailLoud` writes one labelled `util.inspect` diagnostic to stderr for an unhandled rejection or an uncaught exception, awaits the surface's release hook under a fixed timeout, and exits 1; control never returns to the failed operation, because only the throw site knows which state is intact, and the event loop runs only until the release settles or times out. 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`, `web-runtime`, `modules`, `connection`, `headless-runner`, `acp`, and `sdk-jsonrpc-server`.
|
|
44
44
|
|
|
45
45
|
<a id="profiles"></a>
|
|
46
46
|
### Profiles
|
|
47
47
|
|
|
48
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
|
-
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 with its own `cordis.patch.yml`. A bundle's `dsh.bundle.patch` names one patch file or an ordered list of files; `bundlePatchFiles` validates the declaration and `bundlePatchPaths` resolves it to absolute paths; the layer concatenates their patch lists in that order. The YAML composition enables or disables HMR. 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
|
|
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 with its own `cordis.patch.yml`. A bundle's `dsh.bundle.patch` names one patch file or an ordered list of files; `bundlePatchFiles` validates the declaration and `bundlePatchPaths` resolves it to absolute paths; the layer concatenates their patch lists in that order. The YAML composition enables or disables HMR. 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 or replaces a custom profile at a non-shipped name from one shipped template, while `dsh plugin` initializes a base-backed profile and manages its installed bundles. Bundle resolution, manifest, and patch-loading failures skip that bundle without changing its selection; the loaded profile lists each skip in `skippedBundles`, and the launcher prints them once per start with `reportSkippedBundles`. Remaining bundles keep their order; profile and user-patch errors still fail startup. Skipping a bundle does not guarantee that the remaining composition can provide the required services. 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
|
|
|
52
52
|
Before a profile imports a plugin, DSH checks its `peerDependencies` on `@deepseek-ai/dsh` and `@deepseek-ai/dsh-*` against the single runtime version returned by `getDshRuntimeVersion()`. Every declared range must match; prereleases participate in range matching. Source-workspace `workspace:^`, `workspace:~`, and `workspace:*` refer to that same runtime. Missing DSH peers impose no constraint; invalid ranges are incompatible. These checks use peer declarations, not `engines.dsh`, and are not a sandbox against malicious package code.
|
|
53
53
|
|
|
@@ -82,12 +82,16 @@ The projector preserves native omission behavior by checking literal defaults ag
|
|
|
82
82
|
|
|
83
83
|
### Reading plugin display metadata
|
|
84
84
|
|
|
85
|
+
`resolvePluginResource(specifier, parentURL)` resolves a plugin module or exported resource to its local filesystem path through the active Node ESM resolver without evaluating it. It throws when the resolver is unavailable or the resource cannot resolve to a local file.
|
|
86
|
+
|
|
85
87
|
Use `readPluginMeta(specifier, parentURL)` or `ctx.pluginPackages.metaOf(specifier, parentURL)` to read installed package display text without importing or activating the plugin. Lookup uses the complete package specifier and the caller's resolution base, respecting Node exports. File paths and file URLs return no metadata without resolving resources. For a package-root specifier, missing locale fields fall back to the accessible `package.json`; a subpath specifier never reads a `package.json`. Malformed metadata returns an `error` diagnostic. Results retain translations for Client-side language selection. The reader also loads an image data URL, even when locale text is complete: a package root uses its manifest `icon`, or `<package>/icon` when that field is omitted; a subpath uses `<specifier>/icon`. An icon error preserves valid text alongside the diagnostic. See [Plugin display metadata](../../../docs/cookbook/adding-a-package.md#plugin-display-metadata) for the author format.
|
|
86
88
|
|
|
89
|
+
`ON_DEMAND_BUNDLES` names the public Official packages installed only on request. `OFFICIAL_ON_DEMAND_CATALOG` embeds their package-owned localized metadata and icons for offline discovery; `pnpm gen-official-bundle-catalog` explicitly updates generated content, and `verify-official-bundle-catalog` checks resource completeness and freshness; builds consume the committed catalog without importing provider code. The catalog contains no install target: [Plugin Manager](../plugin-manager/README.md#use-this-package) derives it from the running version and installation and uses the ordinary bundle installer. The [product-use check](../../../scripts/verify-product-use.ts) composes shipped and on-demand selections against Web without executing providers.
|
|
90
|
+
|
|
87
91
|
<a id="startup-and-reload-failures"></a>
|
|
88
92
|
### Startup and reload failures
|
|
89
93
|
|
|
90
|
-
Profile reconciliation returns diagnostics for unchanged inactive entries without failing an unrelated mutation. A new inactive entry, a changed configuration or fiber, or a changed diagnostic fails reconciliation; removed fibers must still finish disposal. Explicit enablement targets must activate even when their failure predates the operation. Successful reconciliation emits `app-boot/config-reload` after lifecycle settlement and diagnostic checks, including programmatic updates without HMR. The event carries no diff or parsed config. Successful reconciliation returns after lifecycle settlement and diagnostic checks; volatile-only entry changes are committed by Loader during the update.
|
|
94
|
+
Profile reconciliation returns diagnostics for unchanged inactive entries without failing an unrelated mutation. A new inactive entry, a changed configuration or fiber, or a changed diagnostic fails reconciliation; removed fibers must still finish disposal. Explicit enablement targets must activate even when their failure predates the operation. `recomposeProfile(ctx, patches, binName, { requiredIds, running })` runs one recomposition together with the runtime resolution: it removes the rows the generation no longer lists and waits for their disposal while their packages are still mapped, publishes the latest generation of a recomputable resolution, runs the compatibility preflight against the published packages, applies the generation, starts the `running` entries and entries whose import failed that the update left without a running fiber, and judges activation. `running` lists the entries that ran before the caller stopped some of them; each that failed afterwards counts as a new failure. A failure before the generation applies puts the removed rows back at their places, which starts them again, and starts the `running` entries again. An entry waiting for a service is reported as a warning unless it is required, so it starts once a provider appears; `reconcileProfilePatches()` judges it the same way. A publication the resolution rejects is such a failure and reports `package-moved`. `reconcileProfilePatches()`, which the Config Editor uses, applies and judges the patches without publishing the resolution or retrying failed imports. An entry whose import failed is reported with the error Loader's import returns again, so judging it evaluates the module when the import now succeeds; the state recorded before the change imports nothing, so an entry whose import failed before counts as the same failure whatever its error now is. Successful reconciliation emits `app-boot/config-reload` after lifecycle settlement and diagnostic checks, including programmatic updates without HMR. The event carries no diff or parsed config. Successful reconciliation returns after lifecycle settlement and diagnostic checks; volatile-only entry changes are committed by Loader during the update.
|
|
91
95
|
|
|
92
96
|
After the Loader settles, app-boot warns when only optional entries are inactive. If an enabled required entry cannot activate, `boot()` rejects with `StartupError` after disposal. An independently owned logger exporter retains warning and error records through asynchronous disposal and is released before `boot()` settles. Its message groups all failed plugins and pending services, marks required entries, and retains original stacks, nested causes, and aggregate members. The CLI prints that message once and saves [full startup diagnostics](../../../apps/cli/reference/README.md#startup-diagnostics) before exiting with code 1; unrelated exceptions retain their normal stack output. 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.
|
|
93
97
|
|
|
@@ -112,10 +116,6 @@ The [Web process matrix](../../../apps/cli/tests/profiles/web/tests/web-failure-
|
|
|
112
116
|
|
|
113
117
|
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.
|
|
114
118
|
|
|
115
|
-
### Telling the agent where the harness lives
|
|
116
|
-
|
|
117
|
-
When your app boots a model-backed agent, you can tell the agent where the DSH implementation checkout lives: it learns that path and that it must not infer the working directory from it — it should use `pwd`. The instruction appears once near the top of the system prompt. Apps without a system prompt service skip it; in development, reloading the system prompt drops it until the next boot.
|
|
118
|
-
|
|
119
119
|
-----
|
|
120
120
|
|
|
121
121
|
<a id="understand-the-implementation"></a>
|
|
@@ -135,8 +135,8 @@ This section explains how the outcomes above are realized and points at the code
|
|
|
135
135
|
- **Package metadata.** `ctx.pluginPackages.packageOf` locates the owning package without loading code or requiring an exported `package.json`; a subpath selects its package without validating that file. The installed runtime resolution owns selection even for a miss. Low-level embedders that install the service without one retain native lookup. Display metadata uses the separate entry-aware reader described above.
|
|
136
136
|
- **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.
|
|
137
137
|
- **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 Loader subtree 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.
|
|
138
|
-
- **One runtime resolution.** The installation-first and ordered-bundle breadth-first traversal produces the runtime table. Runtime resolution creates no links; runtime resolution entries occupy their package names at `$DSH_HOME/profiles/node_modules`, and every other name sees that directory as an ordinary ancestor. Profile load removes the `.dsh-module-fallback` projections that link-backend releases wrote into a profile; pnpm-installed packages stay. External bare targets selected by package `imports` use the same package order, while Node retains mapping, conditions, and exact target resolution. A complete successor may add package names, remove profile-scoped mappings and profile-local names, and update linked-root membership atomically; retained mappings keep their normalized directory
|
|
139
|
-
- **Removing linked interception.** A successor may remove a linked root without restart. Directories outside all remaining roots use native lookup for subsequent requests, which may find a development copy or report a missing package. Existing module references and Node caches stay unchanged.
|
|
138
|
+
- **One runtime resolution.** The installation-first and ordered-bundle breadth-first traversal produces the runtime table. Runtime resolution creates no links; runtime resolution entries occupy their package names at `$DSH_HOME/profiles/node_modules`, and every other name sees that directory as an ordinary ancestor. Profile load removes the `.dsh-module-fallback` projections that link-backend releases wrote into a profile; pnpm-installed packages stay. External bare targets selected by package `imports` use the same package order, while Node retains mapping, conditions, and exact target resolution. A complete successor may add package names, remove profile-scoped mappings and profile-local names, and update linked-root membership atomically; retained mappings keep their normalized directory and scope. Installation mappings keep their version and anchor; a profile mapping may change its declarer and, for a package upgraded in place, its version. [Plugin Manager](../plugin-manager/README.md) publishes successors after package operations ([decision](../../../.agents/notes/implemented/architecture/2026-09-30-profile-package-refresh-and-manifest-invalidation.md)).
|
|
139
|
+
- **Removing linked interception.** A successor may remove a linked root without restart. Directories outside all remaining roots use native lookup for subsequent requests, which may find a development copy or report a missing package. Existing module references and Node caches stay unchanged. A link name that is no profile mapping may point to another target, also after removal, and so may one whose mapping was removed first; Node's cached real paths of the previous target are relocated ([generation rules](../../../.agents/notes/implemented/architecture/2026-09-09-profile-resolution-generations.md#immutable-generations)).
|
|
140
140
|
- **Application-owned profiles.** Application-owned profiles use the same runtime resolution. Links within the active profile directory, including pnpm store links, are not external roots even when the profile is outside the shared profiles tree. Resolution does not modify their `node_modules`; pnpm owns installed packages.
|
|
141
141
|
- **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 runtime resolution 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.
|
|
142
142
|
- **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.
|
|
@@ -147,13 +147,13 @@ The startup error also retains inactive-entry metadata and raw startup warning/e
|
|
|
147
147
|
|
|
148
148
|
### Helper behavior
|
|
149
149
|
|
|
150
|
-
The exports each own one stage of the boot: config resolution and snapshot replay, layered environment loading, fail-loud reporting, activation auditing, patch parsing, root-include mounting, config dump rendering, profile composition
|
|
150
|
+
The exports each own one stage of the boot: config resolution and snapshot replay, layered environment loading, fail-loud reporting, activation auditing, patch parsing, root-include mounting, config dump rendering, and profile composition. Per-export contracts live in the code, not this README — see [`src/index.ts`](src/index.ts) and [`src/profile.ts`](src/profile.ts).
|
|
151
151
|
|
|
152
152
|
### Source map
|
|
153
153
|
|
|
154
154
|
| File | Role |
|
|
155
155
|
|---|---|
|
|
156
|
-
| [`src/index.ts`](src/index.ts) | Boot helpers: config resolution, environment loading, fail-loud guard, activation audit, patch parsing, config dump
|
|
156
|
+
| [`src/index.ts`](src/index.ts) | Boot helpers: config resolution, environment loading, fail-loud guard, activation audit, patch parsing, config dump |
|
|
157
157
|
| [`src/profile.ts`](src/profile.ts) | Profile discovery, initialization, bundle resolution, runtime resolution construction |
|
|
158
158
|
| [`src/profile-plugins.ts`](src/profile-plugins.ts) | Installed dependencies, bundle activation policy, and manifest updates |
|
|
159
159
|
| [`src/profile-sanitize.ts`](src/profile-sanitize.ts) | Profile patch backup and recovery bundle activation |
|
|
@@ -183,11 +183,11 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
183
183
|
<a id="model-experience"></a>
|
|
184
184
|
## Model Experience
|
|
185
185
|
|
|
186
|
-
Indirectly, through the loaded plugin tree, which alone contributes model context;
|
|
186
|
+
Indirectly, through the loaded plugin tree, which alone contributes model context; no export adds model-visible text.
|
|
187
187
|
|
|
188
188
|
#### KV Cache effect
|
|
189
189
|
|
|
190
|
-
Boot itself changes no request prefix.
|
|
190
|
+
Boot itself changes no request prefix.
|
|
191
191
|
|
|
192
192
|
## Known Limitations and Deferred Work
|
|
193
193
|
|
|
@@ -196,7 +196,7 @@ Boot itself changes no request prefix. `addHarnessSourceSection` places its sour
|
|
|
196
196
|
These limits describe when this boot library is a poor fit or needs special care. They are current package constraints, not a task backlog.
|
|
197
197
|
|
|
198
198
|
- **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 runtime resolution bootstrap; third-party Workers and custom `vm` linkers keep native resolution.
|
|
199
|
-
- **
|
|
199
|
+
- **Moving a profile package needs its removal first** — `replace()` rejects a retained profile mapping that moved to another directory or link target with `ResolutionRestartRequired`; removing the package and installing it again maps the new directory. A linked root may point to another directory once its profile mapping was removed. Node keeps the real path of every link it resolved through; once a link points elsewhere, or its name is installed again as a directory, a native lookup that selects a file below its previous target, for an importer outside it, selects the same file below the current one when that exists, including an explicit absolute path into the previous target. Installation mappings and local overrides still require a restart.
|
|
200
200
|
- **Linked scope follows recorded real directories** — a hoisted dependency outside every linked root uses native Node. A peer read never invalidates Node caches or changes which files are watched.
|
|
201
201
|
- **Source launches use an ESM-only hook** — CommonJS requests still need the JavaScript files selected by package exports; resolution does not supply missing build outputs.
|
|
202
202
|
- **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.
|
package/README.zh.md
CHANGED
|
@@ -40,14 +40,14 @@ installFailLoud('dsh')
|
|
|
40
40
|
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
`installFailLoud` 会为未处理 rejection 或未捕获异常向 stderr 写一条带标签的 `util.inspect` 诊断,在固定超时内等待界面的 release 钩子,然后以 1 退出;控制流不会回到失败的操作,因为只有抛出点知道哪些状态仍然完整,事件循环只运行到 release 结束或超时。有了这个入口,启动会保留所有能够激活的插件。启用但失败的插件会产生带标签的警告。required entry 失败时,启动会拆卸整个应用并以非零码退出;profile 中不存在的 required id 和已禁用的 required entry 不影响启动。全局 required list 覆盖共享 Agent 执行、应用 endpoint,以及 Web 启动与传输:`agent-loop`、`webserver`、`modules`、`connection`、`headless-runner`、`acp` 和 `sdk-jsonrpc-server`。
|
|
43
|
+
`installFailLoud` 会为未处理 rejection 或未捕获异常向 stderr 写一条带标签的 `util.inspect` 诊断,在固定超时内等待界面的 release 钩子,然后以 1 退出;控制流不会回到失败的操作,因为只有抛出点知道哪些状态仍然完整,事件循环只运行到 release 结束或超时。有了这个入口,启动会保留所有能够激活的插件。启用但失败的插件会产生带标签的警告。required entry 失败时,启动会拆卸整个应用并以非零码退出;profile 中不存在的 required id 和已禁用的 required entry 不影响启动。全局 required list 覆盖共享 Agent 执行、应用 endpoint,以及 Web 启动与传输:`agent-loop`、`webserver`、`web-runtime`、`modules`、`connection`、`headless-runner`、`acp` 和 `sdk-jsonrpc-server`。
|
|
44
44
|
|
|
45
45
|
<a id="profiles"></a>
|
|
46
46
|
### Profile
|
|
47
47
|
|
|
48
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>`,由可安装组合包和自身 `cordis.patch.yml` 组成。组合包的 `dsh.bundle.patch` 指定一个 patch 文件或一个有序的文件列表;`bundlePatchFiles` 校验该声明,`bundlePatchPaths` 把它解析为绝对路径;该层按此顺序拼接各文件的 patch 列表。YAML 组合决定是否启用 HMR。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立组合包,其他模板保留 base 加模式的组合包栈。`dsh --profile <name> --from-default-profile <template>`
|
|
50
|
+
profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/<name>`,由可安装组合包和自身 `cordis.patch.yml` 组成。组合包的 `dsh.bundle.patch` 指定一个 patch 文件或一个有序的文件列表;`bundlePatchFiles` 校验该声明,`bundlePatchPaths` 把它解析为绝对路径;该层按此顺序拼接各文件的 patch 列表。YAML 组合决定是否启用 HMR。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立组合包,其他模板保留 base 加模式的组合包栈。`dsh --profile <name> --from-default-profile <template>` 从一个随附模板,在非内置名称处创建或替换自定义 profile;`dsh plugin` 则初始化以 base 为基础的 profile,并管理其中安装的组合包。组合包解析、manifest 读取或 patch 加载失败时会跳过该组合包,不改变其选择状态;加载结果在 `skippedBundles` 中列出每个被跳过的组合包,启动器每次启动时通过 `reportSkippedBundles` 输出一次。其余组合包保持原顺序;profile 和用户 patch 错误仍会导致启动失败。跳过组合包不保证剩余组合能够提供所需服务。由应用持有的 npm 项目(例如 Electron 保留的 Desktop profile)通过 `loadProfileDirectory` 加载已经初始化的目录,而不会将它暴露给 CLI profile 查找。
|
|
51
51
|
|
|
52
52
|
profile 导入插件前,DSH 会检查其 `peerDependencies` 中对 `@deepseek-ai/dsh` 和 `@deepseek-ai/dsh-*` 的依赖,与 `getDshRuntimeVersion()` 返回的唯一运行时版本比较。每个声明的版本范围都必须匹配;预发布版本参与范围匹配。源码工作区的 `workspace:^`、`workspace:~` 和 `workspace:*` 指向同一个运行时。未声明 DSH peer 时不施加版本约束;无效范围视为不兼容。这些检查使用 peer 声明,而不是 `engines.dsh`,也不是防范恶意包代码的沙箱。
|
|
53
53
|
|
|
@@ -82,12 +82,16 @@ profile 导入插件前,DSH 会检查其 `peerDependencies` 中对 `@deepseek-
|
|
|
82
82
|
|
|
83
83
|
### 读取插件展示元信息
|
|
84
84
|
|
|
85
|
+
`resolvePluginResource(specifier, parentURL)` 通过活动的 Node ESM 解析器将插件模块或导出资源解析为本地文件路径,不执行其代码。解析器不可用或资源无法解析为本地文件时会抛出错误。
|
|
86
|
+
|
|
85
87
|
使用 `readPluginMeta(specifier, parentURL)` 或 `ctx.pluginPackages.metaOf(specifier, parentURL)` 读取已安装包的展示文本,无需导入或激活插件。查询使用完整包标识与调用方的解析基准,并遵循 Node exports。文件路径与文件 URL 不解析资源,直接返回无元信息。包根标识缺失的 locale 字段回退到可访问的 `package.json`;子路径标识从不读取 `package.json`。格式错误的元信息返回 `error` 诊断。结果保留翻译,由 Client 选择语言。即使 locale 文本完整,读取器也会加载图片 data URL:包根使用清单 `icon`,省略该字段时使用 `<包名>/icon`;子路径使用 `<标识>/icon`。图标出错时,保留有效文本并附上诊断。作者格式见[插件展示元信息](../../../docs/cookbook/adding-a-package.zh.md#plugin-display-metadata)。
|
|
86
88
|
|
|
89
|
+
`ON_DEMAND_BUNDLES` 列出仅按需安装的官方公开包。`OFFICIAL_ON_DEMAND_CATALOG` 内嵌这些包拥有的本地化元信息和图标,支持离线发现;`pnpm gen-official-bundle-catalog` 显式更新生成内容,`verify-official-bundle-catalog` 检查资源完整性与新鲜度;构建只读取已提交的目录,不导入提供者代码。目录不保存安装目标;[插件管理器](../plugin-manager/README.zh.md#use-this-package)根据当前版本和安装位置确定目标,并使用普通组合包安装器。[产品用途检查](../../../scripts/verify-product-use.ts)将随附和按需选择与 Web 组合,但不执行提供者。
|
|
90
|
+
|
|
87
91
|
<a id="startup-and-reload-failures"></a>
|
|
88
92
|
### 启动与重载失败
|
|
89
93
|
|
|
90
|
-
profile 重载返回未变化的已有故障诊断,不让无关修改因此失败。新增未激活条目、配置或 fiber 变化、诊断变化都会使重载失败;被移除的 fiber
|
|
94
|
+
profile 重载返回未变化的已有故障诊断,不让无关修改因此失败。新增未激活条目、配置或 fiber 变化、诊断变化都会使重载失败;被移除的 fiber 仍须完成释放。显式启用的目标必须成功激活,即使它的故障早于本次操作。`recomposeProfile(ctx, patches, binName, { requiredIds, running })` 与 runtime resolution 一起执行一次重新组合:它移除新一代组合不再列出的行,并在其包仍有映射时等待这些行释放完成,发布可重新计算的 resolution 的最新一代,使兼容性预检读取已发布的包,应用新一代组合,启动更新后没有运行中 fiber 的 `running` 条目和导入失败的条目,然后判定激活结果。`running` 列出调用方停止其中部分条目之前正在运行的条目;之后每个失败的条目都计为新的故障。新一代组合应用之前的失败会把移除的行放回原位置,从而重新启动它们,并重新启动 `running` 条目。等待服务的条目报告为警告(required 条目除外),因此出现提供方后它即启动;`reconcileProfilePatches()` 以同样方式判定它。resolution 拒绝的发布属于这种失败,并报告 `package-moved`。Config Editor 使用的 `reconcileProfilePatches()` 应用并判定 patch,不发布 resolution,也不重试失败的导入。导入失败的条目以 Loader 再次导入时返回的错误报告,因此若导入现在成功,判定该条目时会对模块求值;变更前记录的激活状态不执行任何导入,因此之前导入失败的条目无论现在的错误是什么,都计为同一个故障。成功重载在生命周期结束及诊断检查通过后发出 `app-boot/config-reload`,包括未启用 HMR 时的程序化更新。事件不携带 diff 或解析后的配置。成功重载在生命周期结束及诊断检查通过后返回;仅 volatile 的条目变化由 Loader 在更新过程中提交。
|
|
91
95
|
|
|
92
96
|
Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如果已启用的 required 条目无法激活,`boot()` 会在释放资源后以 `StartupError` 拒绝。独立管理生命周期的 logger exporter 会保留异步资源释放期间的警告和错误记录,并在 `boot()` 结算前释放。其消息分组列出所有失败插件和等待的服务,标记 required 条目,并保留原始堆栈、嵌套原因和聚合错误成员。CLI 仅输出该消息一次,并在保存[完整启动诊断](../../../apps/cli/reference/README.zh.md#startup-diagnostics)后以退出码 1 结束;其他异常保留正常堆栈输出。表中的“终止启动”指释放已挂载插件并以非零码退出,不报告就绪;“继续”指保留成功运行的插件。后续配置 HMR 不会再次执行 required 启动审计,也不会回滚整个更新。
|
|
93
97
|
|
|
@@ -112,10 +116,6 @@ Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如
|
|
|
112
116
|
|
|
113
117
|
如果你的应用持有终端,它可以在进程退出前把终端交还,你的 shell 绝不会残留在 raw 模式。交还过程有界:卡住的清理只会延迟致命退出,而不会取消它。
|
|
114
118
|
|
|
115
|
-
### 告诉 agent(智能体)harness 所在位置
|
|
116
|
-
|
|
117
|
-
当你的应用启动模型驱动的 agent 时,你可以告诉 agent DSH 实现代码 checkout 的位置:它得知该路径,也知道不得据此推断工作目录——它应使用 `pwd`。这条指示在系统提示词靠前位置出现一次。没有系统提示词服务的应用会跳过;开发环境中,重新加载系统提示词后它会消失,直至下次启动。
|
|
118
|
-
|
|
119
119
|
-----
|
|
120
120
|
|
|
121
121
|
<a id="understand-the-implementation"></a>
|
|
@@ -135,8 +135,8 @@ Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如
|
|
|
135
135
|
- **包元数据。** `ctx.pluginPackages.packageOf` 定位所属包,不加载代码,也不要求导出 `package.json`;子路径选择其所属包,不校验该文件。安装 runtime resolution 后,即使查询未命中也以其选包规则为准。仅安装服务而不提供 runtime resolution 的底层嵌入方保留原生查询。展示元数据使用上文另述的入口感知读取器。
|
|
136
136
|
- **两个 Loader builtin。** `mountRootInclude` 把 `cordis:include` 与 `cordis:group` 注册为 Loader builtin:group 行能把一个提供方与它的消费方放进同一个 `isolate` realm,而位于本工作区之外的 agent preset 无法按名称解析 `@deepseek-ai/cordis-plugin-group`。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析。
|
|
137
137
|
- **由 consumer 持有严格语义。** 普通 Loader group 保留成功 sibling。App-boot 在首次结算后应用全局 required-entry policy;agent preset 与动态多 entry 组合在需要 all-or-nothing setup 时,持有并拆卸各自的独立 Loader 子树。App-boot 读取 failed fiber 来报告已记录的错误,并在一个进程检查点内合并 Loader 重复的 rejection 通知。
|
|
138
|
-
- **唯一 runtime resolution。** 安装优先、有序 bundle 逐根 breadth-first 遍历生成运行时表。runtime 解析不创建链接;runtime resolution 条目占据 `$DSH_HOME/profiles/node_modules` 上各自的包名位置,其余包名把该目录当作普通祖先。profile 加载时删除 Link 后端发布版写进 profile 的 `.dsh-module-fallback` 投影;pnpm 安装的包保留。package `imports` 选中的外部 bare target 使用相同的选包顺序,映射、conditions 和精确 target 解析仍由 Node 负责。完整后继 runtime resolution 可以原子地增加 package name、移除 profile 范围的映射和 profile 本地包名、更新 linked root
|
|
139
|
-
- **移除链接拦截。** 后继 generation 可以移除 linked root,无需重启。目录不再被任何剩余 root 覆盖时,后续请求使用原生查询,可能找到开发副本,也可能报告缺包。已有模块引用和 Node
|
|
138
|
+
- **唯一 runtime resolution。** 安装优先、有序 bundle 逐根 breadth-first 遍历生成运行时表。runtime 解析不创建链接;runtime resolution 条目占据 `$DSH_HOME/profiles/node_modules` 上各自的包名位置,其余包名把该目录当作普通祖先。profile 加载时删除 Link 后端发布版写进 profile 的 `.dsh-module-fallback` 投影;pnpm 安装的包保留。package `imports` 选中的外部 bare target 使用相同的选包顺序,映射、conditions 和精确 target 解析仍由 Node 负责。完整后继 runtime resolution 可以原子地增加 package name、移除 profile 范围的映射和 profile 本地包名、更新 linked root 集合;保留映射的规范化目录和作用域不变。安装范围的映射保持版本和锚点不变;profile 映射可以更换声明来源,对原地升级的包还可以更改版本。[插件管理器](../plugin-manager/README.zh.md)在包操作之后发布后继代([决策](../../../.agents/notes/implemented/architecture/2026-09-30-profile-package-refresh-and-manifest-invalidation.zh.md))。
|
|
139
|
+
- **移除链接拦截。** 后继 generation 可以移除 linked root,无需重启。目录不再被任何剩余 root 覆盖时,后续请求使用原生查询,可能找到开发副本,也可能报告缺包。已有模块引用和 Node 缓存保持不变。不属于 profile 映射的链接名称可以指向其他目标,移除之后也可以;其 profile 映射先被移除的链接名称同样可以;Node 缓存的原目标真实路径会被重新定位([generation 规则](../../../.agents/notes/implemented/architecture/2026-09-09-profile-resolution-generations.zh.md#immutable-generations))。
|
|
140
140
|
- **应用自有 profile。** 应用自有 profile 使用相同的 runtime resolution。目标位于当前 profile 目录内的链接(包括 pnpm store 链接)不算外部 root,即使 profile 位于共享 profiles 树外。解析过程不修改其 `node_modules`;已安装包由 pnpm 管理。
|
|
141
141
|
- **自有 Worker。** Worker 构建 banner 会在业务 bundle 前导入 `@deepseek-ai/dsh-app-boot/worker/profile-resolution-bootstrap`。每个 Worker 在自己的 isolate 中安装结构化克隆的 runtime resolution。bootstrap bundle 不静态导入任何包。源码 Worker 入口保留自包含依赖,第三方 Worker 不接受注入。
|
|
142
142
|
- **更新完成。** App boot 通过 `internal/update` waterfall 观察重启失败。实时 patch 重载在检查激活状态前等待配置树中的 fiber;单独调用 `Fiber.update()` 或 `Entry.update()` 不能确定重启成功。
|
|
@@ -147,13 +147,13 @@ Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如
|
|
|
147
147
|
|
|
148
148
|
### Helper 行为
|
|
149
149
|
|
|
150
|
-
每个导出各负责启动的一个阶段:配置解析与快照回放、分层环境加载、明确报错的保护机制、激活审计、patch 解析、根 include 挂载、配置 dump
|
|
150
|
+
每个导出各负责启动的一个阶段:配置解析与快照回放、分层环境加载、明确报错的保护机制、激活审计、patch 解析、根 include 挂载、配置 dump 渲染与 profile 组合。各导出的约定在代码中,不在本 README——见 [`src/index.ts`](src/index.ts) 与 [`src/profile.ts`](src/profile.ts)。
|
|
151
151
|
|
|
152
152
|
### 源码地图
|
|
153
153
|
|
|
154
154
|
| 文件 | 职责 |
|
|
155
155
|
|---|---|
|
|
156
|
-
| [`src/index.ts`](src/index.ts) | 启动 helper:配置解析、环境加载、会明确报错的保护机制、激活审计、patch 解析、配置 dump
|
|
156
|
+
| [`src/index.ts`](src/index.ts) | 启动 helper:配置解析、环境加载、会明确报错的保护机制、激活审计、patch 解析、配置 dump |
|
|
157
157
|
| [`src/profile.ts`](src/profile.ts) | profile 发现、初始化、组合包解析、runtime resolution 构造 |
|
|
158
158
|
| [`src/profile-plugins.ts`](src/profile-plugins.ts) | 已安装依赖、bundle 启用策略与 manifest 更新 |
|
|
159
159
|
| [`src/profile-sanitize.ts`](src/profile-sanitize.ts) | profile patch 备份与恢复 bundle 启用状态 |
|
|
@@ -183,11 +183,11 @@ Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如
|
|
|
183
183
|
<a id="model-experience"></a>
|
|
184
184
|
## 模型体验
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
模型通过此包加载的插件树间接受影响——只有该树贡献模型上下文;没有导出会添加模型可见文本。
|
|
187
187
|
|
|
188
188
|
#### KV Cache 影响
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
启动本身不改变请求前缀。
|
|
191
191
|
|
|
192
192
|
## 已知限制与延期工作
|
|
193
193
|
|
|
@@ -196,7 +196,7 @@ Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如
|
|
|
196
196
|
这些限制说明此启动库在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。
|
|
197
197
|
|
|
198
198
|
- **运行时解析依赖 Node 内部机制**——受支持的 Node 版本需要 native builtin access addon 和可执行兼容验证。只有构建后的 Harness 自有 Worker 接收 runtime resolution bootstrap;第三方 Worker 与自定义 `vm` linker 保持原生解析。
|
|
199
|
-
-
|
|
199
|
+
- **移动 profile 包需要先卸载**——`replace()` 以 `ResolutionRestartRequired` 拒绝移动到其他目录或链接目标的保留 profile 映射;卸载该包后重新安装,即映射到新目录。linked root 的 profile 映射被移除后,它可以指向其他目录。Node 保留其解析过的每个链接的真实路径;链接指向别处后,或其名称作为目录重新安装后,原生查询为原目标之外的导入方选中原目标之下的文件时,若当前目标之下存在同一文件,会改为选中该文件,包括指向原目标的显式绝对路径。安装映射和本地覆盖仍需要重启。
|
|
200
200
|
- **链接作用域以记录的真实目录为准**——提升后的依赖若在所有 linked root 之外,就使用原生 Node。每次读取 peer 都不会使 Node 缓存失效,也不会改变被监视的文件。
|
|
201
201
|
- **源码启动只安装 ESM 钩子**——CommonJS 请求仍需要 package exports 选中的 JavaScript 文件,解析器不会补出缺失的构建产物。
|
|
202
202
|
- **快照回放替换仅识别特定 basename**——只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。
|