启动与配置
一句话版:
dsh的启动 =app-boot粘合层把「环境 → profile → bundle 层 → 你的 patch 层」组装成一颗配置树,Loader 装载、断言、激活,失败时 fail-loud(dsh:前缀退出)。
dsh web / dsh --profile <name> 都走同一个 boot。理解它,你就知道"改哪层生效、哪层覆盖哪层、配置树怎么查"。
一、启动流程(boot 粘合层)
二、分层环境变量
loadLayeredEnv 的优先级:
继承环境 > 项目 .env > 用户 .env
- 文件里的 bootstrap-only 变量会被拒绝
- 在不替换继承值的前提下物化文件值
三、Profile 机制
| 函数 | 职责 |
|---|---|
resolveProfileDir / initProfile | 定位 / 初始化 $DSH_HOME/profiles/<name> |
readProfileManifest | 读 profile 的 dsh.profile.bundles 列表 |
composeEntries | 把 bundle 层组合成条目 |
loadOptionalPatches | 解析 cordis.patch.yml(顶层 YAML:insert / 覆盖 / !!js) |
watchUserPatches | HMR:patch 变更时事务性重新组合 |
关键细节:
- 内置模板只有
web与headless(自动初始化,默认挂@deepseek-ai/dsh-base);tui等需自建目录 initProfile创建:目录 + package.json(含dsh.profile.bundles)+ 空cordis.patch.yml+pnpm-workspace.yamlhealProfilesModuleFallback维护$DSH_HOME/profiles/node_modules扁平 symlink 兜底,让 out-of-tree 插件解析到同一个 cordis- 家目录级
cordis.patch.yml($DSH_HOME/cordis.patch.yml)越权于 per-profile 层
对应 插件解剖 的完整 patch 应用序。
四、Fail-loud 行为
当 $DSH_SNAPSHOT === 'replay' 时,resolveConfigPath 把 cordis.yml 换成 cordis.snapshot.yml(快照重放)。加载失败时:
$ dsh web # 插件解析失败时
dsh: plugin tree failed to load: ...
# 或
dsh: fatal load failure: <Error stack>
# 标签前缀固定 dsh:,进程 exit(1),绝不带病运行
两个断言函数的区别:
| 断言 | 检查什么 |
|---|---|
assertEntriesLoaded | 树结算后存在已启用但没有 fiber 的条目 → 抛错 |
assertEntriesActivated | 再等每个已启用配置项激活;错误带原始堆栈 |
五、配置查看:--dump-config
dsh web --dump-config # 渲染当前组合树(带 # == 层来源注释)
dsh web --dump-default-config # 只渲染 bundle 层(无用户层)
renderConfigDump 用 Loader 自己的解析器离线合成,结果与真实启动一致:这是排查"哪层覆盖了什么"的权威工具。每行的 # == 注释标了它来自哪个层(base / web-app / 你的 profile)。
六、验证
dsh web --dump-config | head -30 # 看层叠结构与来源注释