Files
deepseek-harness/packages/ui/app-boot/README.zh.md
Yichen Jiang a45aa28ca6 Merge branch 'master' into claude/unified-environment-credentials-c8841a
Master removed the TUI package, the `meta` and `upgrade` subcommands, and
`--config-replace`, and made raw `dsh` require a `--config` overlay. Resolved
onto that shape:

- Dropped this branch's TUI edits with the surface itself, including
  `tui.cordis.yml`, `runTui`, and the TUI keyless PTY smoke.
- Dropped the `--config-replace` plumbing rather than reintroducing a flag
  master deliberately removed. The gap this branch fixed remains: `dsh -p`
  still could not name its composition, so it keeps `--config`.
- Kept this branch's deletion of the personal `$DSH_HOME/config.yaml` layer,
  which master still carried, and provided the environment snapshot in the new
  raw `runConfig` surface alongside web and headless.
- Ported the headless shutdown PTY test off the personal overlay onto a named
  `--config` file, which is what proves that flag now exists on `-p`.
2026-08-04 17:51:44 +08:00

10 KiB
Raw Blame History

@deepseek-ai/dsh-app-boot

English | 中文

供 app bindshdsh-cli-demodsh-acp-demo)共用的启动粘合层:每个 bin 都是在这些 helper 上构建的精简自执行组合并以自身诊断前缀参数化。这样Loader 故障处理知识只需维护一处并接受逐文件覆盖率门禁,不会在已发布产物之间逐渐分化。

导出 职责
resolveConfigPath(path, snapshotMode, cwd?) 生成绝对配置路径;当 snapshotMode === 'replay' 时,把 basename 为 cordis.yml/.yaml 的文件替换为同级 cordis.snapshot.yml
loadEnv(binName, dir?, warn?) 加载已被 git 忽略的 .envNode process.loadEnvFile);文件不存在不影响启动,文件无法加载时输出一行带标签的警告(默认写入 stderr
loadLayeredEnv(binName, cwd?, warn?) dsh 产品 CLI命令行界面的用户环境先对调用目录、再对 Harness home 调用 loadEnv,得到 用户 < 项目 < 继承 的层次。Harness home 先从继承的环境解析,因此项目 .env 无法改变它的指向
installFailLoud(binName, proc?, release?) 将启动期或后续未处理的 Loader rejection 转换为一行带标签的 stderr 消息并执行 exit(1);两者之间会等待可选的 release 拆卸回调(以 FAIL_LOUD_RELEASE_TIMEOUT_MS 为上限),使持有终端的界面能在退出前恢复终端;返回卸载函数(供测试使用)
FAIL_LOUD_RELEASE_TIMEOUT_MS installFailLoud 等待其 release 回调的时长;卡住的 disposer 只会延迟致命退出,而不会取消它
assertEntriesLoaded(ctx, binName) 树结算后,如果其中存在已启用但没有 fiber 的条目,则抛出异常,并以 Cordis 启动故障的形式报告每个未解析插件的名称
assertEntriesActivated(ctx, binName) 先执行 assertEntriesLoaded 检查,再在 Loader 结算后等待每个已启用配置项;抛出的错误包含每个失败插件的原始错误堆栈,或每个等待中插件尚未解析的服务
loadOverlayPatches(binName, file) 解析一份必需的 patch 列表文件surface overlay 或 --config 文件);读取或解析失败时抛出带标签的错误
mountRootInclude(ctx, absoluteConfigPath, patches?) 挂载静态导入的 Include builtin作为本次启动的根配置项
boot(binName, absoluteConfigPath, patches?, prepare?) 创建根上下文,向 Loader !!js 配置表达式暴露 dshHomePath(...segments) 并安装 Loader在配置树条目挂载前执行可选的宿主准备操作prepare 可以使用 Loader也可以提供由启动器拥有的上下文插槽再挂载并等待 include 树结算,断言所有条目均已加载并激活,最后返回根上下文——失败时 dispose资源释放部分构造的上下文并以带标签的错误 reject
renderConfigDump(binName, absoluteConfigPath, layers, warn?) 离线合成基础配置与带标签的覆盖层——使用 include 自己的解析器和补丁算法(entryListSchema/applyEntryPatches),因此结果与 boot() 挂载的内容一致——并渲染为 YAML!!js 表达式原样保留;每段来源相同的连续行之前都有一条 # == 注释,标明贡献该段的文件以及修补过它的层,输出仍是一份可加载的文档;未匹配到行的补丁连同其层标签交给 warn(默认:一行 stderr读取解析形状失败则抛出
addHarnessSourceSection(ctx, sourceRoot) 添加全局 harness:source 提示词段落(顺序紧随 harness 身份、位于 persona 之前),告知 agent智能体DSH 实现代码 checkout 的磁盘路径,同时提醒它不得据此推断当前工作目录,而应使用 pwd;如果已启动树没有此项服务,则不执行操作并返回 undefined。这里的服务是 systemPrompt;该段落注册到它的 fiber因此开发环境 HMR热模块替换重新加载系统提示词后它会消失直至下次启动
HARNESS_SOURCE_SECTION 'harness:source' 段落名称,供 addHarnessSourceSection 注册使用

Loader 结算会在导入或生命周期失败时 reject并携带失败的配置项与阶段boot() 会 dispose 部分构造的上下文,并用 bin 名称包装该失败。结算后遗留的配置项由独立审计处理:assertEntriesLoaded 将已启用却没有 fiber 的配置项转换为 rejection 并列出每个未解析插件;assertEntriesActivated 会显式等待每个失败的 fiber把原始错误堆栈写入启动 rejection并列出每个等待中配置项尚未解析的服务。抛出错误前审计会通过一个进程级检查点标记这些 rejection 的确切原因,从而让 installFailLoud 将 Loader 的重复通知合并为一次,而所有无关的未处理 rejection 仍然致命。

Loader 并发挂载各个条目,因此当其他环节失败时,某个界面可能已经持有终端:此时不经过整棵树自身的拆卸就退出,会把 raw 模式、bracketed paste 和键盘协议残留在用户的 shell 上,而尚未返回的终端查询响应会在下一个提示符处显示为字面文本。配置树失败会经 boot() 结算:它先释放部分构建的上下文(从而执行该界面自身的 shutdown再抛出带标签的 rejection。对于 boot() 看不到的 rejection插件游离的异步工作在挂载期间或挂载完成后失败持有终端的 bin 会传入 release,在提交退出前释放整棵树;dshboot()prepare 回调中捕获根上下文而不是取其返回值使该回调覆盖整个挂载窗口。release 执行期间处理函数保持注册并加闩:被报告的始终是第一个 rejection后续 rejection包括拆卸自身的会被吞掉而不会变成未捕获错误、在拆卸中途杀死进程。

配置中的裸插件 specifier@deepseek-ai/dsh-*、npm 包package通过 Cordis Loader 的内部模块 loader 解析。仓库 bin 会安装 Loader 的可选 peer node-addon-require-builtin;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。相对 specifier 无需原生 helper并以配置目录为基准解析。构建后的 dsh-app-boot 产物内嵌静态挂载的 Include 实现,但仍将 Loader 保持为外部依赖,因此 include 树与 host 会绑定到同一个 Loader peer。dsh 源码启动器还会将 manifest元数据清单声明的 workspace 包映射到其 TypeScript 源码其配置门禁要求每个已交付的原始Web 裸插件都出现在解析所用 manifest 的 dependencies 中。bin 的子进程冒烟测试覆盖内部 loader 路径,而本包的单元测试套件会在进程内使用相对 specifier 配置驱动 boot()

此包不包含 loader 钩子,也不提供开发模式接口。dsh 应用持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper构建后的消费方仍使用普通 Node 包解析。

Harness home

开发者的机器本地状态位于所有仓库之外的 Harness home 中(默认 ~/.dsh,可由 $DSH_HOME 覆盖;统一由根级 resolveDshHome 解析)。本包从中读取的只有一个文件:

  • .env:用户的普通环境层,由 dsh bin 经 loadLayeredEnv 加载,位于调用目录的 .env 与继承环境之下。它是具有普通环境作用域的普通环境值,而不是密钥边界:由 Harness 拥有并隔离的东西放在 .credentials.yaml 里,后者不会被任何表层提升。因此放进本文件的密钥仍然可以解析——但会作为只读的 env 层遮蔽已存储的那一份,并阻断从 Web 设置页轮换密钥。

不存在会被自动发现的组合文件。Loader overlay 只有被点名才会抵达某个界面:裸 dsh --config <path> 在已交付基座上应用一个 patch 列表,dsh webdsh -p 则在各自的已交付 overlay 之上接受同一个标志。把 overlay 放在 ~/.dsh 里没有问题——那只是一个位置,不是一层,启动时不点名就不会加载它(依据)。

子进程测试启动器会把 DSH_HOME 指向每个测试独立的目录,因此开发者自己的文件绝不会泄漏进 fixture。

模型体验

模型通过此包加载的插件树间接受到影响该树决定最终应用中的提示词、schema、消息和模型适配器。唯一贡献模型可见文本的导出 addHarnessSourceSection,也只有在消费方启动后调用它时才会产生影响。

KV Cache 影响

boot() 不会直接使缓存失效;消费方调用 addHarnessSourceSection 时,会在系统提示词靠前位置、逐请求内容之前添加一行短文本,因此不会使跨轮次缓存失效。请求前缀的其他任何变化均由相应的具名消费方持有。

已知限制与延期工作

  • 裸包 specifier 依赖 Loader 内部机制:生产 bin 需要 Loader 的可选原生 helper没有该 helper 的进程内调用方必须使用可解析的相对file specifier或提供自己的模块解析钩子。
  • 快照回放替换仅识别特定 basename:只有以 cordis.ymlcordis.yaml 结尾的配置会映射到同级 cordis.snapshot.yml;自定义配置名称需要调用方自行选择。
  • 环境加载按目录划分且为可选操作:每一层都是一个指定目录下的 .env,失败时发出警告;两个 helper 都不会搜索父目录,也不验证必需变量。loadLayeredEnv 的两层固定为调用目录与 Harness home需要其他层次的调用方请自行组合 loadEnv
  • overlay 采用 patch 形式:按 id 定位的 patch 会替换条目的整个 config,而不是深度合并,因此覆盖必须重述需要保留的基础字段。