@deepseek-ai/dsh-app-boot 0.1.1-rc.2 → 0.1.2-alpha.2
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 +126 -33
- package/README.zh.md +131 -38
- package/lib/index.js +383 -59
- package/lib/types/index.d.ts +4 -4
- package/lib/types/profile.d.ts +44 -25
- package/package.json +23 -21
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: 9094950a7b154d0feb0d8bd0b76e1f06ff2a10fb
|
|
6
|
+
README.zh.md: 3a3ef6c82182d23a50dfa460591b20e9ebfd291e
|
package/README.md
CHANGED
|
@@ -1,60 +1,153 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Shared Loader boot support for dsh profiles and the temporary Python SDK runtime: environment layers, patches, diagnostics, and configuration preview."
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-app-boot
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
`dsh-app-boot` is the shared Loader boot library behind `dsh` profiles, including the CLI packaged by the Python runtime wheel. It loads environment layers, composes profile bundles and patches, boots every plugin, and returns the running app or identifies the failed plugin and cause. Product applications use the `dsh` launcher instead of publishing separate bins; direct-config helpers remain only for lower-level embedders and tests. You can preview the effective configuration before booting, select live or startup-only patch application per profile, and let a terminal-owning app restore its terminal before a fatal exit.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Starting an app with this package is a small, explicit entry point: you give it a config file and it runs the whole boot. This section covers what you can do and what you get; the helper calls behind each outcome are documented in the folded implementation section.
|
|
29
|
+
|
|
30
|
+
### When to use it
|
|
31
|
+
|
|
32
|
+
Use it when implementing the shared `dsh` launcher or embedding its lower-level boot helpers. Product features belong in profile bundles instead of new application bins; code that only adds plugins to an already-running app mounts those plugins directly.
|
|
33
|
+
|
|
34
|
+
### Starting the app
|
|
35
|
+
|
|
36
|
+
You give your entry point a config file, and the process starts the whole app: it loads your environment layers, applies patches and profiles, boots every plugin, and returns once the app is running. In replay mode it boots the sibling `cordis.snapshot.yml` instead, so a recorded session reproduces identically. The smallest entry point is two calls:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
installFailLoud('dsh')
|
|
40
|
+
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
With that entry point, success looks like a running app with every plugin active; failure is never silent — one labelled line names the failing plugin and the stage, and the process exits nonzero. The app context is torn down before the error is reported, so nothing keeps running half-started.
|
|
44
|
+
|
|
45
|
+
<a id="profiles"></a>
|
|
46
|
+
### Profiles
|
|
47
|
+
|
|
48
|
+
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 plugin` creates custom profiles, and a missing bundle or one without a patch declaration fails startup loudly.
|
|
49
|
+
|
|
50
|
+
Your machine-local preferences also live in the Harness home:
|
|
51
|
+
|
|
52
|
+
- **`.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`, proxies, `DSH_*`, `XDG_*` and similar) are rejected from files: export them instead. 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.
|
|
53
|
+
- **`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.
|
|
54
|
+
|
|
55
|
+
Profiles with `patchReload: live` watch both user patch files: a valid edit recomposes without restart, while a rejected edit leaves the last good app running. A `startup` profile installs neither those watchers nor the launcher's watch-only HMR fallback.
|
|
56
|
+
|
|
57
|
+
### Previewing the effective configuration
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
### What you see when startup fails
|
|
25
62
|
|
|
26
|
-
|
|
63
|
+
Startup failure is a single labelled line plus a nonzero exit — never a silent hang or a raw stack dump. The message names the failing plugin; a plugin that threw keeps its original error, and an entry that never started is reported with the services it was waiting for.
|
|
27
64
|
|
|
28
|
-
|
|
65
|
+
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.
|
|
29
66
|
|
|
30
|
-
|
|
67
|
+
### Telling the agent where the harness lives
|
|
31
68
|
|
|
32
|
-
|
|
69
|
+
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.
|
|
33
70
|
|
|
34
|
-
|
|
71
|
+
-----
|
|
35
72
|
|
|
36
|
-
|
|
73
|
+
<a id="understand-the-implementation"></a>
|
|
74
|
+
## Understand the implementation
|
|
37
75
|
|
|
38
|
-
|
|
76
|
+
<details>
|
|
77
|
+
<summary>Implementation internals — click to expand</summary>
|
|
39
78
|
|
|
40
|
-
|
|
79
|
+
This section explains how the outcomes above are realized and points at the code that realizes them; everything here is developer-facing and not needed to use the package.
|
|
41
80
|
|
|
42
|
-
|
|
43
|
-
- **`cordis.patch.yml`** (home level) and **`profiles/<name>/cordis.patch.yml`** — the user patch layers, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): an id-targeted patch replaces the named entry's whole `config` (restate unchanged fields), `insert` adds entries, and `!!js` expressions interpolate at mount. A patch naming an entry id absent from the composed tree is a stderr warning. An empty or comments-only file throws (it parses to nothing, not to a list); disable the layer with `[]`.
|
|
81
|
+
### Design notes
|
|
44
82
|
|
|
45
|
-
|
|
83
|
+
- **Channel-neutral library.** The package carries no loader hooks and no dev-mode surface; the [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence, and built consumers use plain Node package resolution.
|
|
84
|
+
- **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.
|
|
85
|
+
- **Profile module fallback.** Bare plugin specifiers resolve through the Loader from the config directory. Plain Node maintains one symlink per package in the installation dependency closure. A packaged executable instead reads each installed export map with Node ESM conditions and writes real proxy packages that re-export virtual module URLs, because an operating-system symlink cannot enter pkg's `/snapshot` tree. Missing exports stay unavailable, malformed maps fail startup, and a cross-process writer lock replaces stale entries without exposing partial proxies. A selected external bundle absent from the installation closure receives a profile-local `.dsh-module-fallback` link; existing pnpm entries win, projected links are excluded from later closure discovery, and cleanup removes only dsh-owned links.
|
|
86
|
+
- **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.
|
|
87
|
+
- **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 so the startup diagnostic preserves the original activation error instead of only the wrap chain.
|
|
46
88
|
|
|
89
|
+
### Helper behavior
|
|
90
|
+
|
|
91
|
+
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, live patch watching, profile composition, and the harness-source section. Per-export contracts live in the code, not this README — see [`src/index.ts`](src/index.ts) and [`src/profile.ts`](src/profile.ts).
|
|
92
|
+
|
|
93
|
+
### Source map
|
|
94
|
+
|
|
95
|
+
| File | Role |
|
|
96
|
+
|---|---|
|
|
97
|
+
| [`src/index.ts`](src/index.ts) | Boot helpers: config resolution, environment loading, fail-loud guard, activation audit, patch parsing, config dump, harness-source section |
|
|
98
|
+
| [`src/profile.ts`](src/profile.ts) | Profile discovery, initialization, bundle resolution, module fallback |
|
|
99
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; boundary and replay tests cover the protocol mapping) |
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
-----
|
|
104
|
+
|
|
105
|
+
<a id="further-exploration"></a>
|
|
106
|
+
## Further Exploration
|
|
107
|
+
|
|
108
|
+
Read these pages when the package-level contract is not enough. They move from the shared boot mechanics to the composition model and the decision evidence behind it.
|
|
109
|
+
|
|
110
|
+
- [Cordis primer](../../../docs/cordis-primer.md) — Loader, `!!js` config expressions, and include/group semantics.
|
|
111
|
+
- [dsh app](../../../apps/cli/README.md) — the `dsh` bin that consumes these helpers.
|
|
112
|
+
- [dsh-cmdline](../cmdline/README.md) — the launcher-to-app command-line handoff the bins use.
|
|
113
|
+
- [Profile bundles](../../bundle/README.md) — installable patch layers composed into `dsh --profile`.
|
|
114
|
+
- [dsh-home-paths](../../util/home-paths/README.md) — the Harness-home resolver (`resolveDshHome`).
|
|
115
|
+
- [Configuration source ownership](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md) — why a discovered file may not decide bootstrap behavior.
|
|
116
|
+
- [Profile plugin bundles](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md) — the profile and bundle composition design.
|
|
117
|
+
|
|
118
|
+
-----
|
|
119
|
+
|
|
120
|
+
<a id="model-experience"></a>
|
|
47
121
|
## Model Experience
|
|
48
122
|
|
|
49
|
-
Indirectly, through the plugin tree
|
|
123
|
+
Indirectly, through the loaded plugin tree, which alone contributes model context; the one export that adds model-visible text, `addHarnessSourceSection`, does so only when a consumer calls it after boot.
|
|
50
124
|
|
|
51
125
|
#### KV Cache effect
|
|
52
126
|
|
|
53
|
-
|
|
127
|
+
Boot itself invalidates nothing in the request prefix. A consumer that calls `addHarnessSourceSection` places one short line near the system prompt's head, before per-request content, so it does not invalidate the cache across turns; any other request-prefix change is owned by the named consumer.
|
|
54
128
|
|
|
55
129
|
## Known Limitations and Deferred Work
|
|
56
130
|
|
|
131
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
These limits describe when this boot library is a poor fit or needs special care. They are current package constraints, not a task backlog.
|
|
135
|
+
|
|
57
136
|
- **Bare package specifiers depend on Loader internals** — production bins need Loader's optional native helper; an in-process caller without it must use resolvable relative/file specifiers or provide its own module-resolution hook.
|
|
58
137
|
- **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.
|
|
59
138
|
- **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.
|
|
60
139
|
- **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.
|
|
140
|
+
|
|
141
|
+
<a id="dev-note"></a>
|
|
142
|
+
### Dev Note
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
146
|
+
|
|
147
|
+
This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
|
|
148
|
+
|
|
149
|
+
#### Open: config dump stability
|
|
150
|
+
|
|
151
|
+
`renderConfigDump` output is a loadable YAML document whose `# ==` provenance 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.
|
|
152
|
+
|
|
153
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,60 +1,153 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "dsh profile 与临时 Python SDK 运行时的共享 Loader 启动支持:环境层、patch、诊断与配置预览。"
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-app-boot
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
`dsh-app-boot` 是 `dsh` profile(包括 Python 运行时 wheel 所打包的 CLI)背后的共享 Loader 启动库。它加载环境层、组合 profile bundle 与 patch、启动每个插件,再返回运行中的应用,或指出失败插件与原因。产品应用使用 `dsh` launcher 而不发布单独 bin;直接配置 helper 只保留给低层嵌入方与测试。你还可以在启动前预览生效配置,按 profile 选择实时或仅启动时应用 patch,并让持有终端的应用在致命退出前恢复终端。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
用此包启动应用是一个小而显式的入口:你给它一个配置文件,它运行整个启动过程。本节说明你能做什么、能得到什么;每个结果背后的 helper 调用记录在下方可折叠的实现章节中。
|
|
29
|
+
|
|
30
|
+
### 何时使用
|
|
31
|
+
|
|
32
|
+
在实现共享 `dsh` launcher 或嵌入其低层启动 helper 时使用它。产品功能应放入 profile bundle,而不是新增应用 bin;只向已运行应用添加插件的代码直接挂载插件即可。
|
|
33
|
+
|
|
34
|
+
### 启动应用
|
|
35
|
+
|
|
36
|
+
你把配置文件交给入口,进程就会启动整个应用:加载环境层、应用 patch 与 profile、启动每个插件,并在应用运行后返回。在回放模式下,它会启动同级的 `cordis.snapshot.yml` 替代文件,使已记录的会话能够原样复现。最小的入口只需两次调用:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
installFailLoud('dsh')
|
|
40
|
+
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
有了这个入口,成功就是每个插件都已激活的运行中应用;失败绝不会悄无声息——一行带标签的信息点名失败的插件与阶段,进程以非零码退出。错误上报前会先拆卸应用上下文,因此不会留下半启动的残留。
|
|
44
|
+
|
|
45
|
+
<a id="profiles"></a>
|
|
46
|
+
### Profile
|
|
47
|
+
|
|
48
|
+
profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/<name>`,由可安装 bundle、自身 `cordis.patch.yml` 与 `patchReload: live | startup` 组成;自定义 profile 省略 reload 策略时保留历史 `live` 默认值。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立 bundle,其他模板保留 base 加模式 bundle 的栈。`dsh plugin` 创建自定义 profile;缺失 bundle 或未声明 patch 的 bundle 会让启动明确失败。
|
|
49
|
+
|
|
50
|
+
你的机器本地偏好同样位于 harness home 中:
|
|
51
|
+
|
|
52
|
+
- **`.env`**——你的普通环境层:调用目录的文件优先于 harness home 的文件,两者都低于继承环境。决定进程如何启动的变量(`PATH`、代理、`DSH_*`、`XDG_*` 等)会被文件拒绝:请改为导出。对于只想加载某个目录 `.env` 的非产品 bin,文件缺失不影响启动,文件无法加载时输出一行带标签的警告。
|
|
53
|
+
- **`cordis.patch.yml`**——你的 tweak 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):替换某个条目的整个 config(重述你要保留的字段)、插入新条目,或在启动时插值 `!!js` 表达式。patch 指定的条目不存在时输出 stderr 警告;空文件或仅含注释的文件会导致启动失败——如需禁用该层,请改用 `[]`。
|
|
54
|
+
|
|
55
|
+
带 `patchReload: live` 的 profile 会监视两份用户 patch 文件:有效编辑无需重启即可重新组合,被拒绝的编辑则让最后一个可用应用继续运行。`startup` profile 既不安装这些监视器,也不安装 launcher 的仅监视 HMR 回退。
|
|
56
|
+
|
|
57
|
+
### 预览生效配置
|
|
58
|
+
|
|
59
|
+
启动前,你可以打印应用将挂载的确切配置:dump 会以 `!!js` 表达式原样展示组合后的条目列表,并按注释分组标明每个源文件及其 patch 层,输出是一份可加载的 YAML 文档。未匹配到任何行的 patch 会连同其层标签一起报告;配置缺失、无法解析或字段无效都会使 dump 失败。
|
|
60
|
+
|
|
61
|
+
### 启动失败时你会看到什么
|
|
25
62
|
|
|
26
|
-
|
|
63
|
+
启动失败是一行带标签的信息加非零退出码——绝不是静默卡死或原始堆栈倾倒。信息会点名失败的插件;抛错的插件保留原始错误,从未启动的条目会连同它等待的服务一起报告。
|
|
27
64
|
|
|
28
|
-
|
|
65
|
+
如果你的应用持有终端,它可以在进程退出前把终端交还,你的 shell 绝不会残留在 raw 模式。交还过程有界:卡住的清理只会延迟致命退出,而不会取消它。
|
|
29
66
|
|
|
30
|
-
|
|
67
|
+
### 告诉 agent harness 所在位置
|
|
31
68
|
|
|
32
|
-
|
|
69
|
+
当你的应用启动模型驱动的 agent 时,你可以告诉 agent DSH 实现代码 checkout 的位置:它得知该路径,也知道不得据此推断工作目录——它应使用 `pwd`。这条指示在系统提示词靠前位置出现一次。没有系统提示词服务的应用会跳过;开发环境中,重新加载系统提示词后它会消失,直至下次启动。
|
|
33
70
|
|
|
34
|
-
|
|
71
|
+
-----
|
|
35
72
|
|
|
36
|
-
|
|
73
|
+
<a id="understand-the-implementation"></a>
|
|
74
|
+
## 理解实现
|
|
37
75
|
|
|
38
|
-
|
|
76
|
+
<details>
|
|
77
|
+
<summary>实现细节——点击展开</summary>
|
|
39
78
|
|
|
40
|
-
|
|
79
|
+
本节解释上述结果如何实现,并指出实现它们的代码位置;这里的内容面向开发者,使用本包并不需要。
|
|
41
80
|
|
|
42
|
-
|
|
43
|
-
- **`cordis.patch.yml`**(home 级)与 **`profiles/<name>/cordis.patch.yml`**:用户 patch 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):按 id 定位的 patch 会替换对应条目的整个 `config`(未改字段也要重述),`insert` 会添加条目,`!!js` 表达式则在挂载时插值。如果 patch 指定的条目 id 不在组合后的树中,则输出一条 stderr 警告。空文件或仅含注释的文件会抛出异常(其解析结果为空,而不是列表);如需禁用该层,请使用 `[]`。
|
|
81
|
+
### 设计说明
|
|
44
82
|
|
|
45
|
-
|
|
83
|
+
- **与渠道无关的库。** 此包不包含 loader 钩子,也不提供开发模式接口;[`dsh` 应用](../../../apps/cli/README.zh.md) 持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper,构建后的消费方则使用普通 Node 包解析。
|
|
84
|
+
- **两个 Loader builtin。** `mountRootInclude` 把 `cordis:include` 与 `cordis:group` 注册为 Loader builtin:group 行能把一个提供方与它的消费方放进同一个 `isolate` realm,而位于本工作区之外的 agent preset 无法按名称解析 `@deepseek-ai/cordis-plugin-group`。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析。
|
|
85
|
+
- **Profile 模块后备机制。** 裸插件 specifier 由 Loader 从配置目录解析。普通 Node 会为安装依赖闭包中的每个包维护一个符号链接。打包可执行文件无法让操作系统符号链接进入 pkg 的 `/snapshot` 树,因此会按 Node ESM 条件读取已安装包的 export map,并写入重新导出虚拟模块 URL 的真实代理包。缺失 export 保持不可用,错误 export map 会让启动失败,跨进程 writer lock 则会在不暴露部分代理的情况下替换陈旧条目。所选外部 bundle 若不在安装闭包中,则会获得 profile 本地的 `.dsh-module-fallback` 链接;已有 pnpm 条目优先,后续闭包发现会排除投影链接,清理也只删除 dsh 自有链接。
|
|
86
|
+
- **单一 rejection 检查点。** `assertEntriesActivated` 把折入启动诊断的确切原因保持到下一个进程级 rejection 检查点可见,使 `installFailLoud` 能合并 Loader 的重复通知,而所有无关的未处理 rejection 仍然致命。
|
|
87
|
+
- **两阶段失败标签。** `boot()` 区分 `host preparation failed`(`prepare` 在任何配置树条目挂载前抛出)与 `plugin tree failed to load`(此后的一切失败),并追加最深层插件错误的堆栈,使启动诊断保留原始激活错误,而不只是包装链。
|
|
46
88
|
|
|
89
|
+
### Helper 行为
|
|
90
|
+
|
|
91
|
+
每个导出各负责启动的一个阶段:配置解析与快照回放、分层环境加载、明确报错的保护机制、激活审计、patch 解析、根 include 挂载、配置 dump 渲染、活动 patch 监视、profile 组合,以及 harness 源码段落。各导出的约定在代码中,不在本 README——见 [`src/index.ts`](src/index.ts) 与 [`src/profile.ts`](src/profile.ts)。
|
|
92
|
+
|
|
93
|
+
### 源码地图
|
|
94
|
+
|
|
95
|
+
| 文件 | 职责 |
|
|
96
|
+
|---|---|
|
|
97
|
+
| [`src/index.ts`](src/index.ts) | 启动 helper:配置解析、环境加载、会明确报错的保护机制、激活审计、patch 解析、配置 dump、harness 源码段落 |
|
|
98
|
+
| [`src/profile.ts`](src/profile.ts) | profile 发现、初始化、组合包解析、模块后备机制 |
|
|
99
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;边界与回放测试覆盖其协议映射) |
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
-----
|
|
104
|
+
|
|
105
|
+
<a id="further-exploration"></a>
|
|
106
|
+
## 进一步探索
|
|
107
|
+
|
|
108
|
+
当包级约定不够用时阅读以下页面。它们从共享启动机制逐步进入组合模型及其背后的决策证据。
|
|
109
|
+
|
|
110
|
+
- [Cordis 入门](../../../docs/cordis-primer.zh.md)——Loader、`!!js` 配置表达式,以及 include/group 语义。
|
|
111
|
+
- [dsh 应用](../../../apps/cli/README.zh.md)——消费这些 helper 的 `dsh` bin。
|
|
112
|
+
- [dsh-cmdline](../cmdline/README.zh.md)——各 bin 使用的启动器到应用命令行交接。
|
|
113
|
+
- [Profile 组合包](../../bundle/README.zh.md)——组合进 `dsh --profile` 的可安装 patch 层。
|
|
114
|
+
- [dsh-home-paths](../../util/home-paths/README.zh.md)——harness home 解析器(`resolveDshHome`)。
|
|
115
|
+
- [配置来源归属](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.zh.md)——被发现的文件为何不得决定 bootstrap 行为。
|
|
116
|
+
- [Profile 插件组合包](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md)——profile 与组合包组合设计。
|
|
117
|
+
|
|
118
|
+
-----
|
|
119
|
+
|
|
120
|
+
<a id="model-experience"></a>
|
|
47
121
|
## 模型体验
|
|
48
122
|
|
|
49
|
-
|
|
123
|
+
模型通过此包加载的插件树间接受影响——只有该树贡献模型上下文;唯一贡献模型可见文本的导出 `addHarnessSourceSection`,也只有在消费方启动后调用它时才会产生影响。
|
|
50
124
|
|
|
51
125
|
#### KV Cache 影响
|
|
52
126
|
|
|
53
|
-
|
|
127
|
+
启动本身不会使请求前缀中的任何内容失效。消费方调用 `addHarnessSourceSection` 时,会在系统提示词靠前位置、逐请求内容之前添加一行短文本,因此不会使跨轮次缓存失效;请求前缀的其他任何变化均由相应的具名消费方负责。
|
|
128
|
+
|
|
129
|
+
## 已知限制与延期工作
|
|
130
|
+
|
|
131
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
这些限制说明此启动库在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。
|
|
135
|
+
|
|
136
|
+
- **裸包 specifier 依赖 Loader 内部机制**——生产 bin 需要 Loader 的可选原生辅助组件;没有该辅助组件的进程内调用方必须使用可解析的相对/file specifier,或提供自己的模块解析钩子。
|
|
137
|
+
- **快照回放替换仅识别特定 basename**——只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。
|
|
138
|
+
- **环境发现以启动为界**——`loadLayeredEnv` 只读取一次调用目录与 harness home 中的 `.env`;它不搜索父目录,也不跟随之后选择的 workspace。`loadEnv` 仍是非产品 bin 使用的单目录 helper。
|
|
139
|
+
- **用户 patch 会替换匹配到的整个配置**——按 id 定位的 patch 不做深度合并,因此 profile 覆盖必须重述需要保留的组合包字段。
|
|
140
|
+
|
|
141
|
+
<a id="dev-note"></a>
|
|
142
|
+
### 开发备注
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
146
|
+
|
|
147
|
+
本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。
|
|
148
|
+
|
|
149
|
+
#### 待定:配置 dump 稳定性
|
|
54
150
|
|
|
55
|
-
|
|
151
|
+
`renderConfigDump` 的输出是一份可加载的 YAML 文档,其 `# ==` 来源注释与 `!!js` 原样渲染服务于 `--dump-config` 诊断。任何内容都不承诺跨包版本的字节稳定性;在程序化消费该输出之前,请决定 dump 是否成为序列化约定。
|
|
56
152
|
|
|
57
|
-
|
|
58
|
-
- **快照回放替换仅识别特定 basename**:只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。
|
|
59
|
-
- **环境发现以启动为界**:`loadLayeredEnv` 只读取一次调用目录与 harness home 中的 `.env`;它不搜索父目录,也不跟随之后选择的 workspace。`loadEnv` 仍是非产品 bin 使用的单目录 helper。
|
|
60
|
-
- **用户 patch 会替换匹配到的整个配置**:按 id 定位的 patch 不做深度合并,因此 profile 覆盖必须重述需要保留的组合包字段。
|
|
153
|
+
</details>
|