课程总纲 / 阶段 2 · 使用
L10

进阶一:profile / bundle / patch / preset

不写一行代码,重新组装 dsh。

预计阅读 30 分钟难度 ★★★★☆配置课,概念密度高

一、心智模型:dsh 是"组装"出来的Composition

L02 埋过伏笔:运行中的 dsh = 一棵按配置组装出来的插件树。组装分四层:

① bundle 层按 profile 清单顺序
逐层叠加
② profile 补丁你自己的
cordis.patch.yml
③ 主目录补丁机器级偏好
所有 profile 共享
④ --patch 参数命令行临时叠加
优先级最高

四层都往同一个空的插件行清单上叠,后到的层胜出

二、四个名词逐个讲The Four Terms

名词是什么在哪声明通俗理解
profile一份命名的插件组合主目录 $DSH_HOME/profiles/<名字>/ 的 package.json 里 dsh.profile一份装机单:这台车装哪些选装包、按什么顺序
bundle插件+配置的发行层包的 package.json 里 dsh.bundle 指向补丁文件选装包:dsh-base 是"标配包",web-app 是"豪华内饰包"
patch按行 id 替换/插入插件树的 YAMLcordis.patch.yml改装单:把某一行的配置整个换掉,或插入新行
preset会话级的能力组合preset cordis.yml 文件驾驶模式:同一台车,运动/舒适模式不同调校
两个官方 profile 模板:web(dsh-base + web-app 界面层)和 headless(dsh-base + 一次性执行器)。dsh-base 永远是第一层:模型适配、工具、持久化、沙箱、审批、设置、凭据、遥测——产品的地板。

三、patch 的写法与一条大坑Patch Mechanics

# cordis.patch.yml —— 一个"插入新行"的补丁
- insert:
    - id: hello                        # 行的 id(替换时的定位键)
      name: '/绝对路径/scratch-plugin/src/my-plugin.ts'  # 挂哪个插件
      config:                          # 给它的配置(可选)
        greeting: 'Hi there'
大坑:替换是"整行替换",不是深合并。补丁按 id 定位一行后,会把该行的整个 config 换成你写的——只写想改的键会丢掉其余配置,覆盖别人的行时必须重述该行需要的所有键。反过来,你的 bundle 用户也能这样覆盖你——所以默认值要选用户大概率会保留的。

四、神技:--dump-configSee the Real Tree

dsh --profile web --dump-config

打印你这台机器真实启动的完整插件树(YAML),每层 bundle 的贡献带注释分节;打印出来的任何一行,都可以被你自己的补丁按 id 替换。"不写代码换实现"的完整工作流:

  1. --dump-config 看清现状;
  2. 找到想换的那行,抄下它的 id;
  3. 在自己的 cordis.patch.yml 里按 id 写替换(记得重述全部需要的配置);
  4. --patch 挂上验证。

五、profile 的增删管理:dsh pluginManaging Profiles

# 把一个本地包安装进 demo profile(首次会自动初始化 profile)
dsh plugin --profile demo add ./hello-plugin

# 验证层结构(不启动),然后启动
dsh --profile demo --dump-config
dsh --profile demo

# 移除(依赖和层一起删)
dsh plugin --profile demo remove dsh-hello-plugin

dsh plugin --profile <名> <args> 本质是转发给 profile 目录里的 pnpm,所有 pnpm 动词都能用(add / remove / update……);声明了 dsh.bundle 的包会自动追加进 dsh.profile.bundles 清单。

六、preset:同机不同味Presets

profile 是整机级组合(boot 时定死);preset 是会话级组合——同一个 dsh 实例里,给个别会话配不同的插件(如"代码审查专用"会话模板:只挂审查相关工具)。来自 packages/preset/,从预设的 cordis.yml 读组合。换能力集用 preset,不必为每种用途单独开 profile。

profile
命名的整机插件组合,boot 时组装。
bundle
插件+配置的发行层,package.json 里 dsh.bundle 声明。
patch
按行 id 整行替换/插入的 YAML 覆盖层。
preset
会话级能力组合,同机不同会话不同味。
层序 layering
bundle → profile 补丁 → home 补丁 → --patch,后到者胜。
✏️ 动手练习
  1. pnpm dsh web --dump-config,找到 dsh-base 贡献的行,挑一行看看它的 id 和 config。
  2. 写一个只改一行配置的 cordis.patch.yml,用 --patch 挂上,再用 --dump-config 对比前后差异(注意:被替换行的 config 是整个换掉的)。
  3. 执行 pnpm dsh plugin --profile demo add ./某本地包(可先跳过,L18 会正式做),观察 $DSH_HOME/profiles/demo/ 的变化。
📝 自测(点击展开答案)
1. 四层配置从先到后的叠加顺序?哪层优先级最高?
profile 清单里的 bundle(按列出顺序)→ profile 自己的 cordis.patch.yml → 主目录级 cordis.patch.yml → 命令行 --patch。后到的层胜出,--patch 最高。
2. patch 按 id 替换一行时,没写到的 config 字段会怎样?
消失。替换是整行替换、不做深合并,覆盖时必须重述该行所有需要的键——本课最大的坑。
3. profile 和 preset 的分界线?
作用域和时机:profile 整机级、boot 时定死;preset 会话级、同一实例里不同会话可用不同组合。
4. bundle 的 package.json 里 dsh.bundle 和 dsh.profile 分别回答什么问题?
dsh.bundle 答"这个包贡献什么"(指向补丁文件);dsh.profile 答"这个组合由哪些 bundle、按什么顺序构成"。bundle 是你创作分发的,profile 是用户启动的。
5. 想知道"我这台机器现在到底挂了哪些插件",一条命令是什么?
dsh --profile web --dump-config——打印真实组装结果的完整插件树。
权威出处:architecture.zh.md(Profiles and bundles 节) · publish.zh.md(层序与 dsh plugin) · bundle/base/README.md · preset/README.md