公司动态

从脚本到插件:DeepSeek Harness插件开发与发布完整指南

📅 2026/8/26 6:19:00
从脚本到插件:DeepSeek Harness插件开发与发布完整指南
先抛一个具体场景。你用了几天 DeepSeek Harness终端里那条dsh命令已经成了日常入口。用着用着你会发现有些操作是高频重复的拿到模型输出后做一次固定格式的清洗把一段文本按某种规则重新排版或者把当前会话内容同步到自己的外部系统。你写了脚本跑通了也确实省时间。但脚本一直躺在一个随机目录里换台电脑就找不到了别人想用也用不上。这时候你就会想能不能把它做成一个正式插件。但你打开插件开发相关文档后大概率会遇到一个信息差文档会告诉你插件目录在哪里、manifest 长什么样但没人告诉你这件事真正的难点是什么。真正难的不是写代码而是完成一次完整的交付链路——文件落成什么结构、插件怎么被宿主加载、发布到 GitHub 之后别人怎么安装、怎么升级。这篇文章就沿着这条链路走一遍从最小插件文件到装进插件目录再到发布到 GitHub。我会把每一步的动机和常见坑点都写出来。先说明一点DeepSeek Harness 的插件 API 在不同版本里可能不完全一样下面的文件名、字段和命令是基于这类工具的常见插件约定落地前务必对照自己本机版本的官方文档做一次确认尤其是 manifest 字段和生命周期方法名。1. 先想清楚你真正需要的是一次脚本调用还是一个插件1.1 脚本和插件差的不只是“放哪个目录”很多人以为插件就是把脚本往某个目录里一放再补一个配置文件。这个理解不能说错但它忽略了一个关键差异脚本是客人插件是住进你家的室友。脚本是你主动调用的它负责完成一件事输出结果后结束。你给它什么参数、它输出什么格式、出错了怎么处理完全由你自己决定宿主工具并不知道它的存在。插件则不同。宿主在启动时扫描插件目录读取配置文件按约定加载你的代码在特定时机调用你暴露出的方法。你需要按宿主的规则来声明自己是谁、入口在哪里、要注册什么能力、退出时要清理什么资源。这不是放目录的问题而是控制权反转的问题。用一个表来总结维度普通脚本Harness 插件启动方式手动执行宿主按生命周期加载输入来源命令行参数或文件宿主传递的上下文 / payload输出方式stdout、文件注册命令的返回值 / 界面对话安装分发拷贝文件装入插件目录、发布到插件市场配置方式自己解析参数走宿主配置体系更新管理手动替换版本化发布、自动检测升级这个差异决定了后面的一切步骤。写插件的第一原则不是“让代码跑起来”而是“让宿主愿意加载你、能正确调用你、最后还能干净地卸载你”。1.2 什么场景适合做成插件什么场景不建议适合做成插件的场景通常具备两个特征第一动作你会反复做第二动作和 Harness 的主流程有明确关系。举个例子当你拿到模型输出后需要做固定清洗或者你想在 Harness 界面里新增一个自定义命令这些就适合插件化。不适合的场景也很清楚只跑一次的数据迁移或批处理别为了它引入插件维护成本。和 Harness 主流程几乎无关的独立服务应该独立部署而不是硬塞进插件。你自己都还没稳定下来的流程先写成脚本跑熟再考虑插件化。插件化的前提是流程已经稳定。如果需求每天变插件只会成为负担。2. 理解插件的最小组成manifest、入口、生命周期2.1 三个文件之间的“契约”我用一个最常见的插件结构来拆解。假设这个插件叫dsh-plugin-inspector作用是检查当前文本的字符数和行数dsh-plugin-inspector/ ├── plugin.json # 插件的“身份证” ├── index.js # 插件的“大脑” ├── README.md # 插件的“说明书” └── .gitignore # 插件的“门禁卡”这不是 DeepSeek Harness 独有的结构。VSCode 扩展、IDEA 插件、很多桌面软件插件底层逻辑都类似宿主启动后扫描插件目录 → 读取 manifest → 确认插件身份和入口 → 加载入口文件 → 调用生命周期方法 → 插件在此时注册命令、监听事件 → 宿主退出时调用清理方法插件释放资源。理解这条链路你就明白为什么很多“代码明明没问题”的插件装进去就是不生效——因为宿主的加载顺序没走完某个环节断了。2.2 plugin.json 里的字段分别解决什么问题一个最小化的 plugin.json 大概是这样的{ name: dsh-plugin-inspector, version: 0.1.0, description: Inspect text length and line count in DeepSeek Harness, main: index.js, engines: { harness: 0.9.0 } }每个字段背后都有一个实际问题name是插件的唯一标识尽量避免用太通用的名字比如test、demo后面发布到 GitHub 和插件市场时很容易撞名。version用语义化版本号这是插件能不能被市场正确识别更新的基础。main指向入口文件如果你写成src/index.js文件路径就必须对应上。engines声明兼容的宿主版本这是很多人容易漏掉的字段。不写兼容声明装到一个不支持对应 API 的版本上表现出来就是“加载没报错但功能不响应”。这里有一个特别常见的坑JSON 文件不支持注释也不能有尾逗号。很多人从文档里复制代码顺手加了一行中文注释结果宿主机怎么都扫描不到插件还不报明确错误。3. 从零写出一个能跑的插件代码3.1 入口文件的最小实现这里的代码是示例结构。实际 API 的模块名和注册方法以你安装版本下的插件开发文档为准但整体思路是通用的。const { registerCommand } require(dsh-plugin-api); // 示例导入 let timer null; function activate(context) { context.registerCommand(dsh.inspectText, (payload) { const text payload.text || ; const lines text.split(\n).length; const chars text.length; return { lines, chars, message: 当前文本共 ${chars} 个字符${lines} 行, }; }); timer setInterval(() { // 这里不要放阻塞任务避免拖慢宿主 }, 60 * 1000); } function deactivate() { if (timer) { clearInterval(timer); } } module.exports { activate, deactivate };这个代码做的最小闭环是宿主加载插件时调用activate插件注册一个名为dsh.inspectText的命令命令接收宿主传来的payload从里面取到text计算字符数和行数返回结构化结果。宿主退出前调用deactivate清理掉定时器。activate和deactivate是最常见的生命周期命名但有些宿主会叫init、dispose或者其他名字。所以落地前要确认两件事生命周期方法名是什么注册命令的 API 是什么。3.2 写完之后先做一次“无宿主冒烟测试”不需要先装进插件目录先用 Node 直接模拟宿主调用一次node -e const plugin require(./index.js); const ctx { registerCommand: (name, fn) console.log(registered:, name), }; plugin.activate(ctx); plugin.deactivate(); console.log(smoke test ok); 这一步的意义是把问题分开如果这段命令能跑通说明你的入口文件本身没有语法错误模块导出也正常。接下来装进插件目录还不生效问题就出在集成环节而不是代码本身。3.3 不要一开始就把功能做复杂第一次写插件目标就一个让宿主能识别你、加载你、调用你的一个命令。先别急着加外部 API 调用、异步任务、多命令、配置面板。功能越少出问题时的排查范围越小。等最小闭环跑通了再去加真正的业务逻辑。4. 装进插件目录第一次被 Harness 真正加载4.1 先确认插件目录的位置不同系统下Harness 的插件目录可能不一样。常见的位置有这么几个Linux / macOS~/.dsh/pluginsWindows%USERPROFILE%\.dsh\plugins某些版本可能放在安装目录下面的plugins子目录先直接查一下ls -la ~/.dsh/plugins如果dsh命令本身提供了插件相关子命令比如dsh plugin list就用它确认当前加载了哪些插件、插件目录到底指向哪里。文档里通常也会写明默认路径。注意不要凭直觉把插件塞到项目目录、当前工作目录或者 Home 下其他位置。插件目录是宿主启动时扫描的唯一来源放错了地方代码再好也不会被加载。4.2 开发阶段用符号链接发布阶段用复制开发时我建议用符号链接把你的项目目录链到插件目录里这样每次修改代码后只要重启或重载宿主改动就能生效不用反复复制文件ln -s /path/to/dsh-plugin-inspector ~/.dsh/plugins/dsh-plugin-inspectorWindows 下创建符号链接可能需要开发者模式或管理员权限如果不想折腾就直接复制文件夹。装好之后先列出插件确认加载状态dsh plugin list如果命令支持详细模式比如--verbose也要看一眼输出有时插件虽然被扫描到了但加载阶段有警告。然后在 Harness 界面里选中一段文本执行dsh.inspectText观察返回结果。到这一步你才算真正完成了一次“代码被宿主使用”的完整闭环。4.3 装好却没生效按这个顺序查最常见的安装失败原因我按出现频率排一下plugin.json 解析失败。JSON 里有注释、尾逗号、编码问题或者文件名不是宿主期望的名字。main字段指向的文件路径不对。写的是src/index.js但项目里根本没有这个路径。生命周期方法名和宿主约定不一致。宿主期望activate你导出的是register。插件目录名和 manifest 里的name不一致导致宿主不知道用哪个做唯一标识。依赖没安装。如果你的入口文件require了外部 npm 包但插件目录里没有node_modules加载就会失败。排查时优先看宿主日志。日志通常在你的配置目录下比如~/.dsh/logs。插件加载失败一般都会在这里留下线索。这里额外说一句如果你是用源码方式自己构建 Harness可能会在构建dsh web界面时卡在 pnpm 依赖这一步这是 Harness 自身开发环境的构建问题和插件开发不是同一条链路。先确认你用的是安装包还是源码构建两种模式下插件目录的配置方式可能不一样。5. 发布到 GitHub从本地文件变成可分发项目5.1 先补上项目该有的“公共设施”本地跑通只是第一步。“正式”两个字意味着别人拿到你的项目之后能看懂、能安装、能知道你支持哪些版本。至少需要这几样README.md说明插件是干什么的、怎么装、怎么用、兼容什么版本。LICENSE开源协议。插件类项目用 MIT 或 Apache-2.0 比较常见。.gitignore至少忽略node_modules和临时目录避免把依赖包提交上去。CHANGELOG.md记录每个版本的变更后面迭代时非常有用。然后初始化 Git 仓库并推送到 GitHubgit init git add . git commit -m feat: initial version of dsh-plugin-inspector git branch -M main git remote add origin gitgithub.com:yourname/dsh-plugin-inspector.git git push -u origin main如果你更喜欢用 HTTPS 方式就用 GitHub 页面提示的 HTTPS 地址。网络环境不稳定时也可以先在网页端创建空仓库再到本地执行推送。5.2 版本号和 Release 是插件的“里程碑”没有版本号的插件别人用起来心里是没底的。语义化版本号是最低要求版本号含义0.1.0首发实验版功能和接口都可能变0.2.0增加新功能不破坏已有用法1.0.0接口稳定可以作为正式依赖2.0.0出现破坏性变更旧用法不再兼容在 GitHub 上发一个 Release操作上就是给仓库打一个 taggit tag v0.1.0 git push --tags然后在 GitHub 仓库页面创建 Release选择这个 tag写下版本说明。如果你的插件需要打包分发也可以在 Release 里附加打包好的 zip 文件。如果 Harness 有官方的插件市场或插件注册机制发布时通常还需要把仓库地址或 manifest 的 URL 提交到市场具体流程要看市场的文档。5.3 README 决定插件能不能被别人“用起来”代码写得再好README 没法让人看懂插件也只会躺在仓库里吃灰。一个够用的 README 至少要有这四块# dsh-plugin-inspector 一个用于 DeepSeek Harness 的文本检查插件。 ## 安装 - 方式一将项目复制到插件目录 - 方式二通过插件市场或命令行安装 ## 使用 1. 在 Harness 中打开一段文本 2. 执行命令 dsh.inspectText 3. 查看返回的字符数和行数 ## 兼容性 - 需要 dsh 0.9README 里最好再加一张运行截图。人对于“看见效果”的信任远高于读十行文字描述。5.4 发布之后插件不是写完就结束发布到 GitHub 只是起点。真正让一个插件变靠谱的是后续的迭代节奏每次功能变更都更新 CHANGELOG。每个重要版本都打 tag 并发 Release。处理用户反馈时先看用户用的插件版本和宿主版本再判断是不是兼容性问题。这和你自己本地“改完就跑”的节奏完全不同但这才叫“正式”。6. 一套可复用的排查链路插件不工作先查哪里6.1 五层排查法插件出问题时最容易犯的错误是一上来就怀疑代码逻辑。实际上绝大多数问题发生在集成环节。我建议按固定顺序排查层级要问的问题1. 现象插件完全不显示还是报错还是命令执行了但结果不对2. 输入触发命令时宿主传进来的文本、payload 字段名是否正确3. 环境Node 版本、npm 依赖、插件目录权限是否正常4. 参数manifest 字段、生命周期函数名、注册的命令名是否和文档一致5. 工具边界当前 dsh 版本是否支持这些 API有没有接口变更这个顺序的核心逻辑是先确定“坏在哪一层”再决定“修哪里”。现象层是最容易被跳过的。比如命令执行了但输出不对很多人直接改代码却没有确认是不是触发命令时传进来的 payload 字段名变了。输入层要看的是数据本身。payload.text如果不存在你的插件返回空结果是代码 bug 还是宿主传参变化两种处理方向完全不同。环境层要确认的是运行基础。Node 版本太旧、外部依赖没装上、插件目录没有写权限这些都会导致加载失败。参数层是你的代码和宿主的契约。字段名、函数名、注册名任何一个对不上宿主都会忽略你。边界层最容易被忽视。Harness 更新后某个旧 API 被废弃你的插件可能不会报错但功能会静默失效。6.2 最推荐的调试顺序当插件表现异常时我一般这样处理在activate开头加一条console.log确认宿主是否真的加载了你。用 try-catch 包住registerCommand出错时直接把错误信息打印到终端或宿主的错误面板里。在命令行里启动dsh观察标准输出和错误输出。很多加载失败的原因在 GUI 里被吞掉了但在终端里会露出痕迹。手动把插件目录下的插件移走再放回确认是不是路径冲突或缓存导致的残留问题。先确认“加载没有”再谈“代码对不对”。这个顺序能帮你省掉大量自我怀疑。7. 从“跑通一个插件”到“长期可维护”7.1 生产级插件还差几块拼图让插件能跑和让插件能长期稳定地跑中间还差几块拼图异常处理命令内部不要裸奔报错时要返回给宿主一个可读的错误信息而不是直接抛一个堆栈。配置化把可变的路径、阈值、开关放到宿主配置体系里而不是硬编码在代码里。异步与并发如果要调用外部 API注意超时、重试和并发控制不要拖慢宿主主线程。资源清理定时器、监听器、临时文件在deactivate阶段要释放干净。兼容性维护一个简单的测试矩阵至少确认你声明支持的engines版本范围。这些不是写插件特有的要求而是所有需要长期运行的软件共有的工程能力。7.2 真正值得长期学会的是一整套交付习惯回到文章开头那个判断。插件开发真正难的不是代码而是交付链路。当你把一个临时脚本整理成规范的文件结构、装进宿主能识别的目录、再发布到 GitHub 上打上版本号你做的事情已经超过“写一段功能代码”——你是在学习如何把一个想法变成一个别人可以安装、使用、更新的正式软件。这个过程里沉淀下来的习惯比插件本身值钱得多先跑通再优化的节奏、区分脚本和插件的边界意识、按版本号发布的开源协作方式、以及遇到问题时从现象到输入到环境再到工具边界的排查链路。建议下一步不要做复杂插件就从你自己最常重复的那个小动作开始先跑通一次完整的“本地写文件 → 装入插件目录 → GitHub 发布”流程。做完这一轮你对工具链的理解会比看十篇教程更扎实。