插件工坊 · dsh-plugin-studio
一个页面管好 DeepSeek Harness 的插件和技能:三级自动分类、批量删除、AI 大白话讲解、最后使用时间、一键打包。
它是 dsh-modpack(插件整合包)的下一代:原来的整合包只负责"打包带走",工坊负责"平时怎么管"。

上面这张是真实运行截图,不是设计稿。31 个插件按三级树排列,每行前面有复选框,右侧是最后使用时间和操作按钮。
目录
为什么要有它
装了十几个插件之后,Harness 的设置页会变成一长串平铺的列表。你想删掉几个不用的,但:
- 看不出哪个是干什么的 —— 名字是
dsh-plugin-rea,描述是一句英文,删了怕坏; - 不敢删 —— 不知道删了会不会影响正在用的东西;
- 不知道哪个真的没用 —— "我好像三个月没碰它了"是感觉,不是证据;
- 插件和技能是两套地方 —— 管了插件还得另外管技能。
工坊把这四件事合到一个页面里:分类 → 看懂 → 看使用记录 → 批量删。
六个功能
1. 三级自动分类,不留"无法判断"
每个插件和技能都会被归到 一级(大类)→ 二级(能力域)→ 三级(它自己) 的三层树里。
一级分类共 8 个(lib/classify.js 的 FAMILIES):
| 一级 | 管什么 | 举例 |
|---|---|---|
界面与交互 ui |
给用户看和点的 | 壁纸、主题、侧栏、浮层 |
工具与执行 tools |
给模型加能力的 | 新工具、斜杠命令、MCP 服务 |
网络与数据 net |
联网或读写外部数据 | 抓取、同步、接口、账号 |
模型与推理 model |
接模型或改推理过程 | 模型接入、提示词、上下文压缩 |
会话与工作区 session |
管会话和文件 | 会话增删、工作区、终端 |
运维与诊断 ops |
管插件本身 | 插件管理、技能管理、诊断、备份 |
系统与内核 core |
Harness 自带底座 | 通常不该删 |
其它与待确认 other |
证据不足但已归档 | 附原因,可人工改判 |
关于"不再出现无法判断":other 这一级在分类法里保留着,但它是一个有归属的归档,不是"判断失败"——每一行无论证据多弱,都会拿到一个 family、一个 domain 和一条 evidence 说明理由,confidence 会诚实地标 low。实际在本机 28 行真实数据上,other 的命中数是 0。
分类不是"第一条规则命中就定案",而是加权评分:
- PURPOSE(10 分):作者自己写的标题和描述,只投一票,按"最窄的声明优先"排序;
- CAPABILITY(3–10 分):代码里真正调用的服务。调
pluginManager得 10 分、llm8 分、tools7 分、sessionQuery6 分……这是最有辨识度的证据; - AMBIENT(1–2 分):几乎所有插件都有的东西。注册一个 UI 插槽、
dsh.client字段、fetch、fs——这些只用来打平手。
为什么不用"注册了 UI 插槽就是 UI 插件"? 因为那回答的是"它画不画东西",不是"它是干什么的"。优惠券插件、会话清理插件、插件管理器都画东西,但它们分属网络、会话、运维三个大类。早期版本正是踩了这个坑:9 个插件里 7 个被塞进"界面",一级分类直接塌成一支,三级树失去意义。
同分时按 TIE_ORDER = ['tools','net','model','session','ops','core','ui','other'] 裁决——更具体的赢,ui 永远最后。
2. 复选框批量删除 + 重启提示
每个插件/技能前面有复选框,可以单选也可以多选,选中后底部出现操作条。点删除会先弹确认框,列出具体哪几个会被删。
删除走的是 Harness 自己的 pluginManager.removeBundle(),不是手删文件。返回结果里的 application 字段决定要不要提示重启:
applied→ 已生效,不用重启;restart-required→ 弹提示,并说明为什么要重启(lib/remove.js的RESTART_TEXT把restartReason翻译成人话):no-hmr—— 这个包没有热重载,改完得重启才能换掉正在跑的代码package-moved—— 包被移走了,进程还拿着旧路径host-module—— 改的是宿主侧模块,只有重启才重新加载unmanaged-plugin—— 这个插件不是包管理器装的,得重启module-comparison—— 模块版本对不上,重启才能统一
有些行是不可删的(官方底座、只读、来源不明),它们会显示锁图标,复选框直接禁用,并在 removable=false 时给出原因。
删除是串行的:每删一个都会重写 profile 的依赖清单和 patch 文件,并发会丢改动。
3. AI 大白话分析
点每一行的「讲解」按钮,它会结合三样东西推理:
- 代码实际做了什么 —— 读
package.json的入口、dsh.bundle.patch、dsh.client,读源码里的slots.register(...)、tools.register(...)、llm.prepareCall等调用,提取注册了哪些插槽、提供了哪些工具名、碰了哪些子系统; - 作者自己怎么说的 —— 包的
description、README 开头、locale 里的meta.description; - GitHub 上能找到什么 —— 按包名搜 GitHub,校验仓库名或描述里真的出现了这个包名才算命中(防止同名项目误认),再取 README 开头作为佐证。结果缓存 7 天。
输出是三个字段,全部说人话:
what—— 它帮你做什么("会在设置里加一个页面,让你给对话窗口换动态壁纸");remove—— 删了会怎样("删掉之后壁纸变回纯色,不影响聊天");why—— 依据是什么("代码里注册了 settings.section,并且调用了 theme.overrideTokens")。
提示词里写死了一条规矩:先说帮你做什么,再说删了会怎样。不给术语,不说"该插件实现了 X 接口"。
4. 最后使用时间
这一列回答"我多久没碰它了"。数据来自 Harness 自己的会话日志(sessionQuery.listSessions() + readSession(),没有这个服务时回落到直接读 ~/.dsh/sessions/**/session.v4.jsonl.zstd)。
关键设计:区分"用过"和"提过"。
- 真的用过(
usedAt/usedVia):会话日志里出现过tool/call,工具名匹配这个插件注册的工具。这才显示成"最后使用"。 - 只是被提到(
mentionedAt):名字在对话里被说起过。这不算使用,单独用次要样式显示。
为什么较真? 早期版本把"被提到"也算成使用,结果我们一边讨论插件、一边所有插件都被标成"刚刚使用"——28 行里 19 行都有时间,这一列直接失去意义。一条错误的信息比没有信息更糟。
日志读取有个坑:session.v4.jsonl.zstd 是多帧 zstd 拼接,zstdDecompressSync(整个文件) 只解第一帧。lib/usage.js 的 decompressFrames() 按 zstd magic 28 B5 2F FD 扫描全部帧偏移,逐帧解压再拼接。
5. 技能管理页(和插件页功能对等)
第二个 tab 管技能,功能和插件页一致:三级分类、搜索筛选、复选框批量删除、一键打包。
技能从四个根目录扫描(lib/skills.js 的 skillRoots()):
| 根目录 | scope |
|---|---|
<DSH_HOME>/skills |
user-dsh |
<DSH_HOME>/agents/skills |
user-agents |
<cwd>/.dsh/skills |
project-dsh |
<cwd>/.agents/skills |
project-agents |
优先用 skills.list() 服务拿清单,拿不到就自己扫目录(读 SKILL.md 的 frontmatter 取 name / description / when_to_use)。
删除技能有两道守卫:必须是目录、目录里必须有 SKILL.md——防止路径算错时删掉无关目录。
技能的二级分类比插件更细:tools.skill.doc(文档处理,office 系列)、tools.skill.dev(开发与诊断)、tools.skill(其它)。
6. 重命名 + 重新设计的界面
名字从「插件整合包」改成「插件工坊」,包名从 dsh-modpack 改成 dsh-plugin-studio,接口前缀改成 /api/dsh-plugin-studio。
界面设计上遵守了 Harness 客户端的三条硬规矩:
- 只用主题 token —— 13 个
--dsw-alias-*/--dsw-specific-*变量,零硬编码色值,深浅色自动跟随; - 不碰别人的 DOM —— 不 append 到
document.body、不查询别人的元素、不用innerHTML; - 样式可清理 —— 一个
<style>标签,用ctx.effect()注册,卸载时自动移除。
三个 tab:插件 / 技能 / 整合包。视图可以在「三级树」和「平铺列表」之间切换,树可以逐级折叠,每个分组都有「全选本组」。
安装
方式一:从 GitHub 安装(推荐)
plugin_manager action: install_bundle
target: github:KERBALMans/dsh-plugin-studio
装特定版本:
plugin_manager action: install_bundle
target: github:KERBALMans/dsh-plugin-studio#v1.0.0
方式二:从 tgz 安装(离线/内网)
从 Releases 下载 dsh-plugin-studio-1.0.0.tgz,然后:
plugin_manager action: install_bundle
target: E:\下载路径\dsh-plugin-studio-1.0.0.tgz
方式三:从本地目录安装(改代码时用)
plugin_manager action: install_bundle
target: E:\你的路径\dsh-plugin-studio
安装后 profile 里会出现一个指向源目录的 Junction,改源码即刻同步到 profile 磁盘。
安装完成后
打开 设置 → 插件工坊(在侧栏设置里,位于「插件」下面、「智能体预设」上面)。
装完看不到? 如果页面没出现,重启一次客户端。运行中的 Host 会缓存已加载的 ES 模块,
plugin_manager报告application: "applied"只说明依赖装好了,不说明新代码已经在跑。
改完代码不生效? 不用重启,禁用再启用一次即可:
plugin_manager action: set_plugin target: "include:dsh-plugin-studio" enabled: false plugin_manager action: set_plugin target: "include:dsh-plugin-studio" enabled: true
界面说明

顶部:三个 tab
| tab | 管什么 |
|---|---|
| 插件 | 已安装的插件包(bundle) |
| 技能 | 技能工具(skill),功能与插件页对等 |
| 整合包 | 已保存的整合包,可一键装回 |
每个 tab 右边有计数,比如「插件 31」。右上角是刷新按钮。
工具栏
从左到右:
- 搜索框 —— 按名字、包名、描述搜,实时过滤;
- 筛选 chips ——
全部/已启用/90 天未用/可删除/低置信度,可以同时选多个(是"与"关系); - 视图切换 ——
三级树↔列表视图; - 展开全部 / 收起全部 —— 只在树视图下出现。
三级树
界面与交互 2 个插件 [全选本组]
├─ 主题与外观 1
│ └─ ☐ dsh-plugin-wallpaper-engine
│ Wallpaper Engine backgrounds for the DSH web GUI...
│ 最后使用:3 天前 [讲解] [展开]
└─ 界面插件 1
└─ ☐ ...
- 一级(粗体):8 个大类之一;
- 二级:能力域,每个大类下 1–5 个;
- 三级:插件本身。
每一级都能点击折叠。二级标题右边有「全选本组」,勾上就选中这一组的所有插件。
每一行长什么样
☐ dsh-plugin-rea 官方 已启用 高置信度
MCP server for reverse engineering tools...
最后使用:2 小时前(工具调用)
▸ 依据:代码里注册了 tools.register,并且调用了 ...
[讲解] [展开]
- 复选框 —— 最左边,点它选中;
- 名称 + 徽章 ——
官方(Harness 自带)、已启用/已停用、高置信度/中置信度/低置信度、只读(不可删); - 描述 —— 作者写的一句话;
- 最后使用 —— 见下方说明;
- 依据 —— 折叠的判定理由,展开能看到具体证据;
- 讲解 —— 点它调 AI 生成大白话说明;
- 展开 —— 显示完整描述、包名、版本、来源、注册了哪些插槽/工具。
关于「最后使用」这一列
有两种显示,含义不同:
| 显示 | 含义 |
|---|---|
最后使用:2 小时前(工具调用) |
真的用过 —— 会话日志里有工具调用命中它注册的工具 |
最后使用:— + 灰字 5 天前提到过 |
只是被提到 —— 名字在对话里出现过,但没有实际调用 |
为什么要区分? 因为"提到"不等于"使用"。你在对话里说"这个插件不错",不代表它工作了。如果混在一起,讨论插件的时候所有插件都会显示"刚刚使用",这一列就失去意义了。
底部操作栏
勾选任意插件后,底部浮出操作栏:
已选 3 个 [打包] [删除] [取消选择]
- 打包 —— 把这 3 个打包成整合包,可以保存或分享;
- 删除 —— 弹确认框,列出具体哪几个会被删,确认后执行。
删除确认框
删除 3 个插件?
dsh-plugin-wallpaper-engine
dsh-aliyun-coupon
dsh-session-clean
这个操作会从 profile 里移除它们,并重写依赖清单。
[取消] [确认删除]
删除后,如果某个包需要重启才能完全卸载,会再弹一个提示说明原因:
- 「这个包没有热重载,改完得重启才能换掉正在跑的代码」
- 「改的是宿主侧模块,只有重启才重新加载」
整合包 tab
显示已保存的整合包列表,每个显示名称、条目数、创建时间。可以删除。
使用场景
场景一:清理长期不用的插件
- 打开插件工坊
- 点筛选 chip
90 天未用 - 列表只剩很久没碰过的插件
- 逐个看描述和最后使用时间,勾选要删的
- 点删除,确认
场景二:搞不清某个插件是干什么的
- 找到那个插件
- 点
讲解 - 等几秒,AI 会给出三段说明:
- 它帮你做什么 —— "会在设置里加一个页面,让你给对话窗口换动态壁纸"
- 删了会怎样 —— "删掉之后壁纸变回纯色,不影响聊天"
- 依据是什么 —— "代码里注册了 settings.section,并且调用了 theme.overrideTokens"
场景三:换电脑,把插件配置带走
- 在插件页勾选想带走的插件
- 点
打包 - 起个名字,保存
- 把
整合包tab 里的文件拷到新电脑 - 在新电脑上用
plugin_manager应用,或者用studio_list工具
场景四:管理技能
技能页和插件页功能一样:三级分类、搜索、筛选、批量删除、打包。8 个内置技能会被分成「文档处理技能」(office 系列 7 个)和「开发与诊断技能」(1 个)。
常见问题
装完之后设置里没有「插件工坊」
- 确认安装成功:
plugin_manager action: list_bundles,找dsh-plugin-studio - 如果列表里有,但页面没有,重启客户端
- 如果列表里没有,重新安装
页面打开是空白的
页面顶部如果有红条提示「客户端与宿主版本不一致」,重启客户端即可。这是防止新旧代码混跑的保护。
AI 讲解一直转圈或报错
AI 功能需要可用的模型。检查:
设置 → 模型里有没有配置好的模型- 网络是否可达(GitHub 检索需要联网)
没有模型时,讲解按钮会显示"当前没有可用的模型",不会卡住。
最后使用时间一直是空的
说明这个插件注册的工具从来没被调用过。这是正常的——很多插件是纯 UI 的,不注册工具,所以永远不会有"工具调用"记录。这种情况下会显示"未使用过"。
删除按钮是灰的
那一行不可删。原因可能是:
- 它是 Harness 官方底座(
removable: false) - 它是只读的
- 它的来源不明(不是通过包管理器装的)
鼠标悬停能看到具体原因。
删除后插件还在
- 看删除时有没有弹重启提示。弹了就重启客户端
- 重启后还在的话,检查
plugin_manager action: list_bundles确认 - 如果确实还在,可能是删除失败,重新删一次看错误信息
技能删除后还在
技能的启用/禁用是通过写 .disabled 标记文件实现的。删除会真的删目录,但需要重启才能让 Harness 重新扫描。
开发
目录
dsh-plugin-studio/
├── index.js Host 半:apply()、HTTP 路由、Agent 工具
├── client.js Client 半:设置页 UI
├── cordis.patch.yml bundle 插入声明
├── package.json
├── icon.svg
├── locale/{zh,en}.json
├── lib/
│ ├── env.js 路径、请求体、JSON 响应、服务查找
│ ├── caps.js 能力探测(按方法存在性,不看版本号)
│ ├── inventory.js 插件清单(pluginManager 优先,文件回落)
│ ├── classify.js 三级分类:FAMILIES / DOMAINS / 加权评分
│ ├── usage.js 最后使用时间(会话日志扫描)
│ ├── skills.js 技能清单、删除、启停、打包
│ ├── ai.js AI 讲解与 AI 分类
│ ├── remote.js GitHub 检索与缓存
│ └── remove.js 删除与重启提示
└── scripts/ 测试套件(见下)
测试
八个套件,全部不需要跑起 GUI:
node scripts/smoke.mjs # 能力探测、清单回落、分类法自检
node scripts/analyse.mjs # 真实数据跑完整管线
node scripts/routes.mjs # 每个 HTTP 路由
node scripts/actions.mjs # 打包、保存、删除、分类的读写
node scripts/ai.mjs # AI 路由在无模型时的降级
node scripts/style.mjs # 样式与模块契约审计
node scripts/render.mjs # 用 React 替身渲染出真实 HTML
node scripts/compat.mjs # 跨版本契约
调试用:
node scripts/scores.mjs # 打印每行的评分明细
node scripts/why.mjs <包名片段> # 某一行为什么被分到那里
node scripts/measure.mjs # 用 headless Edge 量布局
改完代码怎么生效
不用重启客户端:
plugin_manager action: set_plugin target: "include:dsh-plugin-studio" enabled: false
plugin_manager action: set_plugin target: "include:dsh-plugin-studio" enabled: true
禁用再启用会强制 Host 重新加载模块。
跨版本兼容
engines.dsh 写的是 >=0.2.0-rc.1。真正让插件跨版本活下来的不是版本号,是按方法存在性探测能力:
function has(target, name) {
return !!target && typeof target[name] === 'function'
}
版本号告诉你发布者想给你什么,缺失的函数告诉你这个进程实际能做什么。
scripts/compat.mjs 为五个契约建模并逐个验证:
| 契约 | 模拟什么 | 结果 |
|---|---|---|
0.2.1-rc.1 |
没有 removeBundle、没有 inspect、没有 skills 服务 |
降级运行,删除按钮禁用 |
0.2.1-rc.2 |
加上 removeBundle |
删除可用 |
0.2.1-rc.3 |
当前版本,全量服务 | 全部功能 |
future-additive |
未来版本:只加不改,多出 somethingBrandNew 等未知方法 |
未知方法被忽略,行为不变 |
future-renamed |
未来版本:删掉 listBundles |
回落到读 profile 文件,清单仍出得来 |
每个契约都断言同一组事:能力探测不抛、路由构建不抛、根路由返回 apiVersion、清单非空且每行有三级分类、无 other、分类有区分度、官方可选包没被判成内核、壁纸判成界面、会话清理判成会话、技能路由不崩、删除不存在的行被拒绝且不产生删除记录。
最后还有一段「空 ctx」:所有服务都拿不到时,capabilityReport() 要报出缺失项而不是抛异常,根路由仍要返回 200——这样 UI 能告诉用户"这个版本缺什么",而不是白屏。
已知限制:本机只装了 0.2.1-rc.3,rc.1 / rc.2 无法真实安装验证,所以用的是契约级验证(模拟各版本的服务面)。这在工程上等价于"按接口编程"的验证,但不等价于"真的在 rc.1 上跑过"。
HTTP 接口
所有接口在 /api/dsh-plugin-studio 下,先过 connection.admit() 鉴权。
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | / |
版本与接口清单 |
| GET | /inventory |
插件清单 + 三级树 + 使用时间 |
| GET | /skills |
技能清单 + 三级树 |
| GET | /saved |
已保存的整合包列表 |
| GET | /diag |
能力探测、模型、各根目录 |
| POST | /pack |
打包成整合包 |
| POST | /save |
保存整合包到磁盘 |
| POST | /delete-pack |
删除已保存的整合包 |
| POST | /categories/save |
手动改判分类 |
| POST | /categories/reset |
清空手动改判 |
| POST | /suggest |
AI 批量分类 |
| POST | /explain |
AI 大白话讲解 |
| POST | /remove |
删除插件 |
| POST | /skills/remove |
删除技能 |
| POST | /skills/enable |
启用/禁用技能 |
| POST | /skills/pack |
打包技能 |
| POST | /plan |
预览应用整合包会做什么 |
| POST | /apply |
应用整合包 |
Agent 工具
给模型用的三个工具:
studio_list—— 列出插件或技能(带三级分类);studio_explain—— 讲解某个插件;studio_remove—— 删除插件(支持dryRun)。
数据存放
<DSH_HOME>/storages/dsh-plugin-studio/
├── plugin.json 安装标记
├── categories.json 手动改判 + AI 讲解缓存
├── remote-cache.json GitHub 检索缓存(7 天)
└── packs/ 已保存的整合包
整合包格式:
{
"format": "dsh-plugin-studio-pack",
"formatVersion": 1,
"meta": { "name": "...", "description": "...", "createdAt": 0, "kind": "plugin", "count": 0 },
"entries": [
{
"key": "bundle:xxx",
"kind": "bundle",
"name": "xxx",
"version": "1.0.0",
"spec": "xxx@1.0.0",
"category": { "family": "ui", "domain": "ui.theme", "familyLabel": "界面与交互", "domainLabel": "主题与外观" },
"enabled": true,
"official": false,
"description": "..."
}
]
}
许可
MIT
相关文档
- 手动上传到 GitHub 网页版 · 小白超详细教程 —— 如果你想自己把这个插件传到自己的仓库,或者把它改成你的项目发布出去,这份教程从注册账号开始,一步一步点鼠标,不需要命令行。
- 更新日志
No comments yet. Be the first to write one.