description: "dsh profile 与临时 Python SDK 运行时的共享 Loader 启动支持:环境层、patch、诊断与配置预览。"
English | 中文
dsh-app-boot 是 dsh profile(包括 Python 运行时 wheel 包所含的 CLI(命令行界面))背后的共享 Loader 启动库。它加载环境层、组合 profile 组合包与 patch、启动每个插件,再返回运行中的应用,或指出失败插件与原因。产品应用使用 dsh launcher 而不发布单独 bin;直接配置 helper 只保留给低层嵌入方与测试。你还可以在启动前预览生效配置,按 profile 选择实时或仅启动时应用 patch,并让持有终端的应用在致命退出前恢复终端。
用此包启动应用是一个小而显式的入口:你给它一个配置文件,它运行整个启动过程。本节说明你能做什么、能得到什么;每个结果背后的 helper 调用记录在下方可折叠的实现章节中。
在实现共享 dsh launcher 或嵌入其低层启动 helper 时使用它。产品功能应放入 profile 组合包,而不是新增应用 bin;只向已运行应用添加插件的代码直接挂载插件即可。
你把配置文件交给入口,进程就会启动整个应用:加载环境层、应用 patch 与 profile、启动每个插件,并在应用运行后返回。在回放模式下,它会启动同级的 cordis.snapshot.yml 替代文件,使已记录的会话能够原样复现。最小的入口只需两次调用:
installFailLoud('dsh')
const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT))
有了这个入口,启动会保留所有能够激活的插件。启用但失败的插件会产生带标签的警告。required entry 失败时,启动会拆卸整个应用并以非零码退出;profile 中不存在的 required id 和已禁用的 required entry 不影响启动。全局 required list 覆盖共享 Agent 执行、应用 endpoint,以及 Web 启动与传输:agent-loop、webserver、modules、connection、headless-runner、acp 和 sdk-jsonrpc-server。
Profile 与组合包的声明类型从 @deepseek-ai/dsh-package-manifest 导入。App-boot 将 DshPackageManifest 适配为包身份可选的 ProfileManifest,因为本地 profile 无需发布版本。App-boot 负责 profile 加载、JSON 校验和解析后的运行时数据。
profile 是同一套 dsh 安装提供不同应用界面的方式:web、headless、acp、sdk 与 sdk-minimal 从同一 launcher 启动不同组合。profile 位于 $DSH_HOME/profiles/<name>,由可安装组合包和自身 cordis.patch.yml 组成。YAML 组合决定是否启用 HMR。随产品交付的 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 查找。
你的机器本地偏好同样位于 harness home 中:
.env——你的普通环境层:调用目录的文件优先于 harness home 的文件,两者都低于继承环境。在文件中设置的进程启动变量(如 PATH、DSH_*、XDG_*)会被拒绝:请改为导出这些变量。四个代理名(HTTP_PROXY、HTTPS_PROXY、ALL_PROXY、NO_PROXY)只从 harness home 的文件接受,绝不从调用目录的文件接受——后者随 clone 一起到来。对于只想加载某个目录 .env 的非产品 bin,文件缺失不影响启动,文件无法加载时输出一行带标签的警告。cordis.patch.yml——你的 tweak 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):替换某个条目的整个配置(重述你要保留的字段)、插入新条目,或在启动时插值 !!js 表达式。patch 指定的条目不存在时输出 stderr 警告;空文件或仅含注释的文件会导致启动失败——如需禁用该层,请改用 []。启用的 dsh-hmr 插件会监视 profile manifest 与两份用户 patch 文件,重新读取按顺序排列的组合包层,并应用重载失败策略。DSH HMR 将这些重载与插件管理器的配置写入串行化;包操作在其队列之外执行。启动器不安装 HMR 或监视器;HMR 被禁用或不存在时,更改需要重启。
插入条目的插件名可以是绝对文件系统路径、文件 URL 或包标识符。patch 加载会把 insert 条目及其嵌套分组中的绝对路径以及相对于 patch 文件的 ./ 或 ../ 路径转换为文件 URL;对已有条目名称的断言及替换用的 config 值保持原样。
挂载 profile 条目前,dsh launcher 会从安装依赖图与有序 bundle 依赖图计算一份不可变的 package resolution generation。默认使用 runtime 模式,将 generation 安装到 Node 的 ESM 与 CommonJS 解析器中,不创建 fallback 链接。普通 Node 中的 runProfile 调用方可以显式选择 link 模式以物化 generation,选择 dual 模式以物化并校验它,或选择 runtime 模式。打包可执行文件和 Electron Host 始终使用 runtime 模式。
sanitizeProfile(binName, profileDir, bundles) 提供文件恢复,无需加载插件或解析 patch。Desktop 在原生致命错误恢复中调用它。调用前必须停止 profile 并排除并发 profile 写入。它将 profile 的 cordis.patch.yml 重命名为带唯一 .bak-<timestamp> 后缀的同目录备份,并恢复调用方指定的 bundle 列表,保留已安装包和其他 manifest 字段。时间戳为 Unix 毫秒数;同名备份已存在时追加序号(-1、-2、……),时间戳保持不变。返回值为备份路径;patch 不存在时返回 undefined,缺失的 profile 不会被创建。下次启动的 profile 初始化会重新创建空 patch。home 级 patch 不变。无效 profile JSON 在修改前报错;后续错误向调用方抛出,保留已完成的修改供重试。
启动前,你可以打印应用将挂载的确切配置:dump 会以 !!js 表达式原样展示组合后的条目列表,并按注释分组标明每个源文件及其 patch 层,输出是一份可加载的 YAML 文档。未匹配到任何行的 patch 会连同其层标签一起报告;配置缺失、无法解析或字段无效都会使 dump 失败。
profile 重载返回未变化的已有故障诊断,不让无关修改因此失败。新增未激活条目、配置或 fiber 变化、诊断变化都会使重载失败;被移除的 fiber 仍须完成释放。显式启用的目标必须成功激活,即使它的故障早于本次操作。
Loader 结算后,app-boot 在仅 optional 条目未激活时输出警告。如果已启用的 required 条目无法激活,boot() 会在释放资源后以 StartupError 拒绝。独立管理生命周期的 logger exporter 会保留异步资源释放期间的警告和错误记录,并在 boot() 结算前释放。其消息分组列出所有失败插件和等待的服务,标记 required 条目,并保留原始堆栈、嵌套原因和聚合错误成员。CLI 仅输出该消息一次,并在保存完整启动诊断后以退出码 1 结束;其他异常保留正常堆栈输出。表中的“终止启动”指释放已挂载插件并以非零码退出,不报告就绪;“继续”指保留成功运行的插件。后续配置 HMR 不会再次执行 required 启动审计,也不会回滚整个更新。
| 失败模式 | Optional 条目启动时 | Required 条目启动时 | 后续配置 HMR |
|---|---|---|---|
| 根配置或必需 overlay 缺失、不可读、格式错误,或包含无效条目 | 终止启动 | 终止启动 | 拒绝格式错误或无效的实时 patch,不改变运行中的配置;有效修改可以应用 |
| 模块 import 失败或模块求值抛出异常 | 警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;修正 import 后可以激活 |
| 插件配置 schema 校验失败 | 警告;继续 | 终止启动 | 新条目保持未激活;现有条目保留原实例与配置;有效修正可以应用 |
配置 !!js 求值抛出异常 |
警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;有效修正后可以激活 |
disabled: !!js 求值抛出异常 |
警告;继续 | 终止启动 | 报告求值错误,不将条目当作已禁用;有效修正后可以激活 |
同步 apply() throw |
警告;继续 | 终止启动 | 报告错误;保留成功的兄弟插件;修正配置后可以激活 |
异步 apply() throw |
结算后警告;继续 | 结算后终止启动 | 结算后报告错误;保留成功的兄弟插件;修正配置后可以激活 |
| 注入的服务不可用 | 警告;继续,条目等待依赖 | 终止启动 | 条目继续等待;补上缺失的提供方后可以激活 |
| HTTP 端口绑定失败 | 警告;继续,但该端点不可用 | 终止启动 | 进程继续运行,但失败的端点不可用;修正配置后可以恢复 |
脱离 apply() 返回 Promise 的异步任务产生未处理 rejection |
致命错误:释放应用并以非零码退出 | 致命错误:释放应用并以非零码退出 | 致命错误:释放应用并以非零码退出,与条目 id 无关 |
| 条目缺失或被显式禁用 | 忽略 | 忽略 | 不激活该条目;不执行 required 启动审计 |
上面的 required 列表包含 modules 与 connection;只要其中一个已启用条目失败,Web 就无法成功启动。Optional 提供方失败也可能使 required 消费方无法激活。现有条目的新配置在更新前被 schema 校验拒绝,并不等于对兄弟插件的变更做事务回滚。
Web 进程矩阵和启动验收测试通过随附 Web profile 验证这些结果;app-boot 测试还覆盖根 Include 失败。
如果你的应用持有终端,它可以在进程退出前把终端交还,你的 shell 绝不会残留在 raw 模式。交还过程有界:卡住的清理只会延迟致命退出,而不会取消它。
当你的应用启动模型驱动的 agent 时,你可以告诉 agent DSH 实现代码 checkout 的位置:它得知该路径,也知道不得据此推断工作目录——它应使用 pwd。这条指示在系统提示词靠前位置出现一次。没有系统提示词服务的应用会跳过;开发环境中,重新加载系统提示词后它会消失,直至下次启动。
当包级约定不够用时阅读以下页面。它们从共享启动机制逐步进入组合模型及其背后的决策证据。
!!js 配置表达式,以及 include/group 语义。dsh bin。dsh --profile 的可安装 patch 层。resolveDshHome)。模型通过此包加载的插件树间接受影响——只有该树贡献模型上下文;唯一贡献模型可见文本的导出 addHarnessSourceSection,也只有在消费方启动后调用它时才会产生影响。
启动本身不改变请求前缀。addHarnessSourceSection 将源码路径放在第一方可复用指令之后,因此工具与配置一致时,不同 checkout 不会改变前置字节。不保证提供方复用缓存。
这些限制说明此启动库在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。
vm linker 保持原生解析。cordis.yml 或 cordis.yaml 结尾的配置会映射到同级 cordis.snapshot.yml;自定义配置名称需要调用方自行选择。loadLayeredEnv 只读取一次调用目录与 harness home 中的 .env;它不搜索父目录,也不跟随之后选择的 workspace。loadEnv 仍是非产品 bin 使用的单目录 helper。