第 8 章 打包与发布:把插件变成别人能装的东西
本地 --patch 只能自己玩。想分享,就得把插件打包成 bundle,装进别人的 profile。
8.1 两个概念,两种 manifest
再强调一次(这是最容易绕晕的地方):
•
组合包(bundle):附带一个配置层的 npm 包,package.json 声明 dsh.bundle,回答"这个包贡献什么";
•
Profile:$DSH_HOME/profiles/
bundle 是你编写并分发的东西;profile 是用户启动的东西。没有东西同时是两者。
8.2 做一个 bundle
目录结构:
代码块
Plain Text
hello-plugin/
├── package.json # 声明 dsh.bundle
├── cordis.patch.yml # profile 列出本 bundle 时应用的层
└── index.js # 插件模块
package.json:
代码块
Plain Text
{
"name": "dsh-hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"files": ["index.js", "cordis.patch.yml"],
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
index.js(插件入口,和第 6 章一样的逻辑):
代码块
Plain Text
export const name = 'hello-plugin'
export function apply() {
console.log('[hello-plugin] plugin loaded!')
}
cordis.patch.yml(和 --patch 一样的 patch 数组,区别是插件行按包名引用,Node 才能解析到已安装的代码):
代码块
Plain Text
- insert:
- id: hello
name: dsh-hello-plugin
小知识:没有 dsh.bundle 声明的包也能装,但只是普通依赖,不会激活任何层。库类包(供其他插件 import)就用这种格式。
8.3 安装进 profile
在包含 hello-plugin 的目录里:
代码块
Plain Text
dsh plugin --profile demo add ./hello-plugin
首次使用会初始化 profile(自动把 @deepseek-ai/dsh-base 作为第一个 bundle),pnpm 链接你的 checkout,dsh 检测到 dsh.bundle 后把它追加进 dsh.profile.bundles。
先验证再启动:
代码块
Plain Text
dsh --profile demo --dump-config # 可以看到 "# == dsh-hello-plugin" 层
移除同样简单:
代码块
Plain Text
dsh plugin --profile demo remove dsh-hello-plugin
8.4 加载顺序(重要)
生效配置在空根之上按顺序叠加:
dsh.profile.bundles 所列各 bundle 的 patch(按列表顺序);
profile 自己的 cordis.patch.yml;
home 级 $DSH_HOME/cordis.patch.yml;
每个 --patch
推论(给 bundle 作者的两条铁律):
•
你的 patch 覆盖前面各层时,必须重述目标行需要的每一个键,因为 patch 替换整个 config,不是深合并;
•
用户能在自己 profile 的 cordis.patch.yml 里覆盖你的行,所以给用户大概率会保留的默认值,把其余交给 schema。
8.5 三种分发方式
方式一:发布到 npm(推荐)
代码块
Plain Text
pnpm publish
用户 dsh plugin add dsh-hello-plugin 安装的就是预构建代码,不需要任何构建权限。在 pnpm publish 前把 lib/ 构建好即可。
方式二:交付 tarball
代码块
Plain Text
pnpm pack
用户执行:
代码块
Plain Text
dsh plugin add ./hello-plugin-0.1.0.tgz
方式三:从 GitHub 安装(有坑,务必看)
代码块
Plain Text
dsh plugin --profile demo add github:you/hello-plugin
坑在于:git 安装拉的是源码,不是构建产物,没有任何环节运行你的 build 脚本,TypeScript 包到手时没有 lib/ 输出,加载会失败。两边各要做一件事:
•
作者:提供 prepare 脚本(pnpm 在 git 安装后运行),且必须自包含——不能假设旁边有 monorepo checkout;
•
用户:pnpm ≥10 默认拒绝运行 git 依赖的 prepare 脚本,首次 add 会失败。dsh 会提示把 pnpm 打印的包键写进该 profile 的 pnpm-workspace.yaml:
代码块
Plain Text
allowBuilds:
dsh-hello-plugin: true
然后重新 add。
8.6 安全警告:不要随便授权
请如实看待 allowBuilds 这项授权:它允许该包的代码在安装时于你的机器上执行,且不在 agent 运行的任何沙箱之内。
•
只对源码可信的包授权;
•
建议锁定 commit:github:you/hello-plugin#
•
不想让用户做这项授权?用 npm 发布或 tarball 分发构建产物。
8.7 本章小结
•
bundle = 带 dsh.bundle 的 npm 包;profile = 带 dsh.profile 的可启动目录;
•
dsh plugin --profile
•
四层加载顺序:bundles → profile patch → home patch → --patch;
•
patch 整行替换 config,覆盖时必须重述所有键;
•
分发三选一:npm / tarball / git(git 需要 prepare + allowBuilds,注意安全)。
下一章,往深处走:看看"一切皆插件"的架构到底长什么样。