dsh-hello-plugin
· 4 min read
一个 0 依赖的 DeepSeek Harness 插件, 用来演示 Cordis 插件系统的四个核心机制,并且能在任意目录直接安装。
这个文件夹里的插件就是为
D:\bowen\git-project\dsh-hello-plugin\准备的, 不需要放进 DSH 仓库,也不需要联网装依赖。
目录结构与“每步作用在哪”
dsh-hello-plugin/
├── index.js # 插件本体(唯一源码,0 依赖)
├── package.json # 打包安装路线(dsh plugin add)用的 bundle 清单
├── cordis.yml # 开发期路线:--patch 覆盖层(引用上面的 index.js)
├── cordis.patch.yml # 打包路线:bundle 在被安装时应用的那一层
└── README.md # 本文件
index.js 里的插件扮演了这四种角色,各自作用在 DSH 的不同层:
| # | 插件里的一段 | 作用在哪个机制/层 | 怎么观察 |
|---|---|---|---|
| 0 | export const name | Cordis 插件的身份 | — |
| 1 | export const inject = ['tools'] | Cordis 依赖注入 + 生命周期 gating:框架等到 ctx.tools 就绪才把本插件从 PENDING 激活到 ACTIVE 并调用 apply() | 加载顺序不再是手排的,而是“等服务” |
| 2 | apply(ctx) 第一行日志 | fiber 进入 ACTIVE(插件生命周期入口) | 启动终端打印 [dsh-hello-plugin] ACTIVE … |
| 3 | ctx.effect(() => … setInterval …) | 可逆副作用:卸载/HMR 时自动 clearInterval | 改配置热替换后定时器不留残留 |
| 4 | ctx.on('session/created', …) | 钩住 Harness 应用生命周期:订阅会话子系统(packages/core/session)的真实 emit 事件 | 新建聊天时终端打印 session/created |
| 5 | ctx.tools.register({…}) | 插入工具执行流水线(packages/core/tools):注册 greet 工具,schema 自动流入系统提示词装配 | 在 UI 里让模型调用 greet |
| 6 | apply() 末尾 return () => … | 卸载清理最后一步(disposer,与各 effect 逆序执行) | 热替换/退出时打印 DISPOSED |
前提
你的 DSH 是从源码跑起来的(界面上应该是 http://127.0.0.1:3080),且在
D:\bowen\github\deepseek-harness 有可执行的 checkout、pnpm 可用。
(如果不是从源码跑,见「路线 B」。)
路线 A · 开发期安装(最快,推荐)
这一步不“安装”到任何地方,而是让 DSH 在启动时叠加一层 patch 把插件挂进去。
-
停掉当前正在跑的 DSH Web(占用 3080 的那个进程)。
-
在 DSH 源码根目录执行:
cd D:\bowen\github\deepseek-harnesspnpm dsh web --patch D:/bowen/git-project/dsh-hello-plugin/cordis.yml -
打开
http://127.0.0.1:3080。
验证四件事:
- 终端出现
[dsh-hello-plugin] ACTIVE — apply(ctx) 执行 …(插件装上了)。 - 在 UI 开一个新对话 → 终端出现
[dsh-hello-plugin] 生命周期钩子触发 → session/created …(挂上生命周期的证据)。 - 输入:
用 greet 工具跟 Ada 打个招呼→ 模型调用greet,得到你好, Ada!(工具进流水线的证据)。 - 改一下
cordis.yml里的greeting/heartbeatMs再重启,观察配置生效、旧实例干净卸载。
为什么要用
file:///D:/...而不是D:/...?D:\...会被 Node 当成 URL 的 schemed:解析而报错;file:///D:/...才是合法的模块标识符(见vendor/loader/src/config/tree.ts的import()解析)。
卸载路线 A:去掉启动命令里的 --patch … 参数即可,或者把 - insert 整段删掉。
