读完这篇你能:写出一个最小插件,装进 test profile,并在启动时看到它打印
hello world。前置:01-profile、bundle 与插件的关系。约 15 分钟。
示例代码:dsh-hello-world
DSH 版本:0.2.0-rc.2
目标
写一个最小插件,让它在 DSH 的 test profile 启动时打印一句 hello world。
DSH 设计
01-profile、bundle 与插件的关系已经讲过配置体系的三层概念:插件是功能本身,组合包(bundle)是它的分发格式,profile 是决定哪些分发进入本次启动的方案。落到「写一个插件」这件事上,只需要再记住一条:一个插件进入 DSH,需要的全部东西是代码文件加一份挂载补丁。
这条要求来自启动过程本身。DSH 启动时从空清单开始,把各层补丁逐层叠上去,叠完的那张清单就是插件树。插件想进树,唯一要做的就是让某一层补丁里出现指向自己代码的那一行。插件包的 package.json 用 dsh.bundle 字段声明自己的补丁在哪个文件;把这个字段指对了,剩下的就是写代码本身。
补丁按什么顺序叠、每一层各管什么,见03-用 patch 覆盖 DSH 的默认配置;「行」怎么叠成「树」,见由行组成的树。
目录结构
dsh-hello-world/ ← 插件包(bundle)
├── dsh-hello-world.ts ← 插件本体:name + apply
├── package.json ← 身份 + dsh.bundle 声明
└── patch.yaml ← 挂载指令
最小插件
// dsh-hello-world.ts
import type { Context } from '@deepseek-ai/cordis'
export const name = 'dsh-hello-world'
export function apply(ctx: Context) {
console.log('hello world')
}
name:插件的唯一 idapply:挂载时执行什么
仅此而已。这就是一个合法插件。
但是要区分一下注册和挂载两个动作,注册是我们将 bundle 添加到profile 中。挂载是运行时概念,dsh 启动时将某个功能挂载到 dsh 的运行时上下文中。
bundle 声明
光有代码不够,DSH 不知道”这段代码该怎么进插件树”。所以一个 bundle 需要两个文件:
package.json
{
"name": "dsh-hello-world",
"version": "0.1.0",
"type": "module",
"dependencies": {
"@deepseek-ai/cordis": "^4.0.1"
},
"dsh": {
"bundle": {
"patch": "./patch.yaml"
}
}
}
注意最后那个 dsh.bundle.patch——这是 DSH 专属字段,npm/pnpm 完全不认识它。DSH 靠它找到补丁文件。
patch.yaml
- insert:
- id: dsh-hello-world
name: '/绝对/路径/dsh-hello-world.ts'
意思是:往插件树里插一行——id 叫 dsh-hello-world,代码在哪个文件。
patch.yaml这个名字是我们自己起的。内核不认文件名,只认package.json里dsh.bundle.patch指到哪个文件——上面写./patch.yaml,所以读这个;改成./挂载.yaml也照样跑,官方模板里同一个文件叫cordis.patch.yml。名字里有
cordis.patch.yml时别搞混:它只有在 profile 目录(~/.dsh/profiles/<名字>/)和~/.dsh/下才是内核写死的约定(用户覆盖层,自动读取);插件包里的那份和本文的patch.yaml是同一个角色,只是被dsh.bundle.patch声明进树。
dsh.bundle.patch的值可以是一个文件,也可以是一个有序数组:同一个包想按不同界面/场景挂不同的行,就备多份补丁文件,让命令行或环境决定读哪一份。
小坑:
name写相对路径时要以./开头。以./、../开头的相对路径会按patch.yaml自己所在的目录锚成file://URL;裸写dsh-hello-world.ts会被当成包名去解析,找不到文件。
安装到 profile
cd 你的插件目录
dsh plugin --profile test add link:.
这条命令做了三件事:
- 把
link:.翻译成插件目录的绝对路径 - 在 profile 目录里执行
pnpm add(是的,它只是 pnpm 的”转手人”) - 检查装上的依赖里哪些声明了
dsh.bundle,把它们的包名追加进 profile 的dsh.profile.bundles
注意第 2 步的 cwd 是 profile 目录,不是你的插件目录。所以它只改 profile 的配置清单,永远不碰插件自己的 package.json——插件代码要 import 的包,得你在插件目录里自己加。
验证
dsh --profile test --dump-config
这条命令会把组合好的插件树打印出来。看到这一节就成功了:
# == dsh-hello-world
- id: dsh-hello-world
name: file:///绝对路径/dsh-hello-world.ts
然后启动 DSH,日志里出现 hello world。
