DSH 插件開發指南
官方文檔講清了概念,但從「讀懂」到「裝得上」之間有一段距離。 這篇是我們寫 插件測試平台 和 我們自己的插件 時的真實路徑 —— 包括踩的坑。
一、最小插件:三個文件
一個能裝進 DSH 的插件,最少需要三個文件:
my-plugin/ ├── package.json # 声明这是 dsh 插件,patch 指向哪 ├── cordis.patch.yml # 告诉 DSL 装载器插什么 └── lib/index.js # 真正的代码
package.json
{
"name": "dsh-my-plugin",
"version": "0.1.0",
"type": "module",
"main": "lib/index.js",
"files": ["lib", "cordis.patch.yml"],
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
}
},
"engines": {
"dsh": ">=0.2.0-rc.2"
}
}
dsh.bundle.patch 告訴裝載器去哪找 patch 文件;
engines.dsh 聲明兼容的 DSH 版本範圍。
我們測的 122 個插件裡,8 個因為 engines.dsh 寫死而裝不上 —— 見第五節。cordis.patch.yml
- insert:
- id: dsh-my-plugin
name: dsh-my-plugin
只有三行。id 是這個插件在配置樹裡的名字(要唯一),
name 是模塊名(npm 包名,或相對路徑)。
lib/index.js
export const name = "dsh-my-plugin";
export function apply(ctx) {
console.log("hello from my plugin");
}
就這麼簡單。一個具名導出 apply 函數,Cordis 加載時會調用它,
傳入 ctx(上下文)—— 插件的所有能力都通過 ctx 註冊。
apply 的函數(最常用);
② 有 apply 方法的對象;
③ 繼承 Service 的類(要對外提供服務時才用)。
新手用第一種就行。二、註冊一個工具
大部分插件做的事是給 AI 加工具。API 是 ctx.tools.register(defineTool({...}))。
import { defineTool } from "@deepseek-ai/dsh-tools";
export const inject = ["tools"]; // ← 见第三节,这行最容易漏
export function apply(ctx) {
ctx.tools.register(defineTool({
name: "my_lookup",
description: "Look up something and return text.",
parameters: {
query: {
type: "string",
required: true,
description: "What to look up.",
},
limit: {
type: "number",
description: "Max results. Defaults to 5.",
},
},
output: {
schema: {
type: "object",
additionalProperties: false,
properties: {
text: { type: "string", required: true },
},
},
render: (_args, value) => [
{ type: "text", text: value.text || "" },
],
},
isConcurrencySafe: () => true,
async execute(args, exec) {
const res = await fetch("https://example.com/api?q=" + encodeURIComponent(args.query));
const data = await res.json();
return { text: JSON.stringify(data, null, 2) };
},
}));
}
四段結構
| 欄位 | 作用 |
|---|---|
parameters | 聲明參數的 JSON Schema。模型靠這個知道怎麼調 |
output.schema | 聲明返回值的結構。execute 的返回值必須匹配 |
output.render | 把返回值變成給模型看的內容塊 |
execute | 真正幹活。exec.signal 是取消信號,要透傳給 fetch |
三、inject:最容易踩的坑(我們真的踩了)
dsh-deepseekbase: Error: cannot get property "systemPrompt" without inject原因:插件裡用了
ctx.systemPrompt.section(...),
但 inject 只聲明了 ["tools"]。修法:改成
inject = ["tools", "systemPrompt"],重測即通過。
規則
你在 apply 裡用了 ctx.某某,就必须在 inject 裡聲明它。
export const inject = ["tools", "systemPrompt"];
export function apply(ctx) {
ctx.systemPrompt.section({ // 需要 "systemPrompt"
name: "tool:mine",
order: 120,
text: "Use my_lookup when asked about X.",
});
ctx.tools.register(defineTool({ // 需要 "tools"
/* ... */
}));
}
為什麼要聲明
Cordis 會等到所有 inject 列出的服務都存在,才調用 apply。
這解決了加載順序問題 —— 你不用管誰先誰後,依賴關係決定啟動時機。
但也意味著:inject 漏寫的服務,在 apply 裡訪問會直接報錯。
常用服務
| 服務 | 用途 |
|---|---|
tools | 註冊 AI 可以調用的工具 |
systemPrompt | 往系統提示裡加一段說明 |
fs | 讀寫文件(需要權限) |
llm | 調用模型 |
shell | 執行命令(高風險權限) |
四、調試三招
① 看配置樹
dsh --profile web --dump-config | grep -A2 my-plugin
如果這裡沒有你的插件,說明 cordis.patch.yml 寫錯了,或者包名解析不到。
② 看是不是「沒激活」
啟動時如果出現 dsh: warning: 1 entry did not activate,
說明插件被掛上了但 apply 執行時報錯了。往上翻日誌找 Error。
③ 乾淨環境重裝
dsh plugin --profile web remove dsh-my-plugin dsh plugin --profile web add ./my-plugin
很多「為什麼沒生效」是舊版本還在緩存裡。
五、engines.dsh:第二容易踩的坑
dsh-ops@0.4.4 聲明 "engines.dsh": ">=0.2.0-rc.2 <0.2.2-0"。這排除了
0.2.2-rc.1 —— 於是在最新版上直接拒絕安裝:dsh: installation rejected: Plugin dsh-ops@0.4.4 is incompatible with dsh 0.2.2-rc.1它的 CI 是綠的、單元測試也過了,但用戶裝不上。 122 個插件裡有 8 個栽在這。
建議寫法
"engines": {
"dsh": ">=0.2.0"
}
只寫下界,不寫上界。除非你確知未來版本會破壞兼容,否則不要封頂。
六、配置
插件可以從 patch 文件接收配置:
- insert:
- id: dsh-my-plugin
name: dsh-my-plugin
config:
endpoint: "https://example.com/api"
limit: 5
export function apply(ctx, config) {
const endpoint = (config && config.endpoint) || "https://default.example.com";
const limit = (config && config.limit) || 5;
// ...
}
永遠給默認值。用戶可能不寫 config 就裝上。
七、權限與安全
DSH 插件跑在宿主進程裡,能力很大。所以權限要寫清楚。
| 權限 | 能做什麼 | 審核建議 |
|---|---|---|
fs-read | 讀文件 | 會看到你的代碼和密鑰文件 |
fs-write | 寫文件 | 能改你的項目 |
network | 發 HTTP 請求 | 能外傳數據 |
shell | 執行命令 | 等於完全控制 |
credentials | 讀憑證 | 紅線 |
dynamic-code | 運行時生成代碼 | 紅線 |
「出站 HTTPS 只到 deepseekbase.com;不讀寫文件;不讀憑證;不執行命令。 整個實現是一個文件、零依賴,五分鐘可以審完。」
八、完整範例:我們的插件
我們的 dsh-deepseekbase 就是按上面寫的,而且完全開源:
| 文件 | 內容 |
|---|---|
package.json | 34 行,含 dsh.bundle.patch 和 engines.dsh |
cordis.patch.yml | 3 行 |
lib/index.js | 約 250 行,4 個工具,零運行時依賴 |
.github/workflows/test.yml | CI:push 時自動跑四階段測試 |
它做的四件事:
deepseekbase_price— 查 DeepSeek 價格與服務商報價deepseekbase_plugin— 搜插件(帶風險等級)deepseekbase_specs— 查模型技術參數deepseekbase_trend— 查 token 調用量走勢
它用我們自己的 MCP Server 作為數據源 —— 一個插件背後接一層服務,是最省事的架構。
九、从写完到有人装
代码写完只是开始。这一步决定「有人装」还是「没人知道」。
9.1 发布到 npm
npm publish --access public
package.json 的 files 包含 lib 和
cordis.patch.yml(否则装了缺文件);
② name 和你的 npm token 权限匹配(scoped 包要 @你:包名);
③ engines.dsh 只写下界(见第五节)。9.2 我们自己踩过的三个 npm 坑
| 错误 | 原因 | 修法 |
|---|---|---|
EOTP |
token 没开「Bypass 2FA」 | 去 npm 网站重新生成 token,勾选 Bypass two-factor authentication |
403 Forbidden |
token 是 @liangjiehappy 作用域,包却是无作用域名 |
包改成 @liangjiehappy/xxx,或换 token |
latest 指向 0.0.0-stage |
用 npm stage 分段发布,占位版本占了 dist-tag |
npm dist-tag add 包名@版本 latest |
9.3 关键字怎么写
npm 搜索和第三方市场抓取都靠 keywords。至少带这三个:
"keywords": [ "deepseek-harness", "dsh", "dsh-plugin", "extensions" // 再加一两个描述功能的词 ]
9.4 三个分发渠道
| 渠道 | 怎么进 | 现状 |
|---|---|---|
| npm 直接搜 | 带对 keywords 就会被搜到 |
✅ 立刻生效 |
| 第三方市场 | 自动抓取 npm + GitHub,无需提交 (dsh.so、dshbase.com、我们 deepseekbase.com 都是这样) |
✅ 几小时内 |
| 官方市场 | 官方尚未上线(2026-11-05 公布,无时间表) | ⏳ 等 |
9.5 第一句话最重要
市场列表和搜索结果显示的是 description,不是 README 全文。
一句话说清楚「做什么、给谁用」:
"description": "DeepSeek Harness 里的实时汇率查询:一次调用返回 40 种货币, 数据来自欧洲央行,零依赖,只读。"
不要写「一个很棒的插件」「powerful toolkit」—— 这类描述没人会点。
9.6 发布后检查清单
| # | 检查项 | 怎么验 |
|---|---|---|
| 1 | npm 上能搜到 | npm search dsh-plugin <你的关键词> |
| 2 | 能装上 | 干净环境 dsh plugin add 包名 |
| 3 | 装完能用 | 问 AI 一个该插件负责的问题,看它会不会调用 |
| 4 | 进市场 | 等几小时,去三个市场搜你的包名 |
| 5 | 有测试记录 | 提交到我们的 插件测试,拿到分级 |
十、寫完之後:讓別人驗證
我們在隔離沙箱裡對插件做四階段真實測試:
① 裝進乾淨容器 → ② 確認出現在 plugin list → ③ 確認掛載進配置樹 → ④ 啟動 profile,確認進程存活
後三步完全無網絡(
--network none),插件即使有惡意行為也無處可去。
看已測的 122 個結果 →
十一、官方文檔
本文是實戰補充,概念和完整 API 請看官方:
| 文檔 | 內容 |
|---|---|
| cordis-primer | Cordis 五個核心概念 |
| 01 first plugin | 最小插件與三種形狀 |
| 03 services | 服務與 inject |
| tool catalog | 內置工具的完整 schema |
| config catalog | 所有配置項 |
相關
插件測試結果 · 插件目錄 · MCP Server