dsh-plugin-desk
DeepSeek Harness 插件管家:用人话说明每个插件是做什么的,一键停用/启用(热生效),安全卸载。Settings → Plugins → 插件管家
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 11, 2026
- Updated
- Sep 11, 2026
Introduction
dsh-plugin-desk · 插件管家
用人话看懂每个插件和技能是做什么的,一键停用/启用,安全卸载。
DeepSeek Harness 的「设置 → 插件」页是一份只读清单:它列出模块名,却不告诉你这个插件到底干什么用;想关掉一个插件,只能去手改 cordis.patch.yml。技能那边更糟 —— 装了 57 个技能,其中 14 个模型根本看不到,只能靠记住 /handoff 这类命令来用。
这个插件在同一个「插件」页里加一个插件管家标签页,分「插件」「技能」两段,补上这些事。
四个痛点
| 痛点 | 官方界面 | 插件管家 |
|---|---|---|
| 看不懂插件是做什么的 | 只显示英文模块名 | 中文名 + 一句话说明 + 来源徽章 |
| 关不掉 / 卸不掉插件 | 纯只读,「已启用」只是个标签 | 一键停用/启用(热生效)+ 卸载 |
| 不知道技能怎么用 | 模型目录与 / 菜单互不可见 | 每个技能标明唯一可用的触发方式 |
| 不知道技能怎么关 | 没有开关,只能手改 SKILL.md | 两个真开关(模型自动加载 / / 命令),热生效 |
插件管理
功能
1. 用人话说明每个插件的作用
按质量优先级取说明:dshhub.summary → package.json 的 description → README.md 首段(自动剥离 Markdown / HTML、按语义边界截断)。
实测覆盖率(本机 168 个已安装包):dshhub 仅 1 个包使用,description 覆盖 164 个 —— 所以 description 才是主力,dshhub 是锦上添花。
2. 来源徽章
| 徽章 | 判定依据 |
|---|---|
| 官方 | 包名在 @deepseek-ai/ 命名空间下 |
| 社区 | lockfile 里是普通 semver 版本号 |
| GitHub 直装 | lockfile specifier 为 github:owner/repo#tag |
| 本地链接 | specifier 为 file: / link: |
判定读 pnpm-lock.yaml 的 importers 段,不看目录类型 —— 本环境用 nodeLinker: hoisted,真实目录并不代表来自 registry。
3. 一键停用 / 启用
走 Cordis 官方的 entry 更新通道 Entry.update({ disabled }):停用时 loader 会 await _dispose(fiber),该插件的工具、路由、监听立即注销;启用时重新 init() 装配。热生效,不需要重启。
选择会记在 ~/.dsh/plugin-manager.json,每次启动自动重放,所以重启后仍然保持你选的状态。
4. 安全卸载
- 先快照 entry 的 options,再
loader.remove(),然后校验确实移除;若仍在,按快照loader.create()恢复原状。 - 官方
@deepseek-ai/*组件禁止卸载(卸载会破坏启动),UI 上按钮直接置灰,只允许停用。 - 卸载后同样记入状态文件,重启不会自己装回来。
5. 噪声过滤
本机装了 168 个包,其中绝大多数是传递依赖,真正能管理的插件只有个位数。列表会自动剔除:
- 非插件库(没有
dsh.bundle的普通依赖)—— 实测过滤 154 个 - 框架内部条目(
cordis:include、node:*等)—— 实测过滤 4 个
技能管理
插件管家页顶部有 插件 | 技能 分段控制。技能页复用同一套卡片语言。
1. 每个技能标明「唯一可用的触发方式」
技能的 invocation 策略有两个互相独立的维度,界面把它们翻译成一句人话:
| 模型自动加载 | / 命令可用 | 界面直接告诉你 |
|---|---|---|
| ✓ | ✓ | 模型会自己调用;也可用 /name 手动唤起 |
| ✗ | ✓ | 模型看不到它 —— 只能用 /name 手动唤起 |
| ✓ | ✗ | 仅模型自动调用(/name 不可用) |
| ✗ | ✗ | 已停用 |
第二行是本机 14 个技能的真实状态。它们在模型侧完全不存在,靠翻文件夹是发现不了的。
实测(本机 60 个技能):
模型看不到、只能用 / 唤起 = 14
两个开关都开的 = 46
来源:用户安装 57 / 插件提供 3
2. 两个真开关
不是「障眼法」:插件注册一个 rank 0 的影子 provider,把策略按需置 false。注册表按层合并、同层内 rank 升序取胜,而三个消费点都按策略过滤:
| 消费点 | 过滤 | 后果 |
|---|---|---|
tool-skill 模型目录 | filter(isModelInvocable) | 不进 <available_skills> |
tool-skill 加载守卫 | isModelInvocable | skill 工具拒绝加载 |
session-controller 命令目录 | filter(isUserInvocable) | 不进 / 菜单 |
关键实现细节:这些技能住在 agent preset 作用域,不在全局层。而服务读取走 topology-sensitive 属性代理 —— ctx.skills(声明式注入)返回绑定到访问上下文的反射,ctx.get('skills') 读的是全局存储。插件必须为每个 preset createScope 并走该作用域的 .skills 反射注册,否则候选会被更近的层直接盖掉。这一步不需要修改任何预设文件(shipped 与用户预设一并覆盖)。
3. 清理线索
| 标记 | 实测数量 | 判定依据 |
|---|---|---|
| 目录内有不生效的 SKILL.md | 40 | 加载器只认 <技能名>/SKILL.md,segments.length > 2 一律忽略 |
| 目录内混入非技能文件 | 2 | 根目录出现 package.json / package-lock.json 等 |
| 缺少描述 | 0 | 模型与命令菜单都无法判断用途 |
嵌套那份不只是冗余 —— 抽样确认它与生效那份内容不同(brainstorming 10047 vs 15456 字节)。
4. 来源与升级对象
从指令文件路径反解归属:
- 路径含
node_modules/<pkg>/→ 「插件提供 · 升级对象<pkg>v<version>」 - 路径在
~/.agents/skills/或~/.dsh/skills/下 → 「用户安装」
source字段在本部署不可信:host 进程 cwd 是家目录,预设把用户根报成project-agents。所以分类以路径为准。
5. 没有「自动升级」按钮
实测本机 57 个技能:0 个是 git 仓库、无清单、无安装元数据(~/.agents/plugins/marketplace.json 与技能无关,它只有一条指向不存在路径的条目)。通用自动升级路径不存在,因此不提供按钮 —— 只标注该升级什么。
安装
dsh plugin --profile web add "github:Amouren7/dsh-plugin-desk#v0.2.1"
装完重启 dsh web,然后打开 设置 → 插件 → 插件管家。
页面是懒加载的,浏览器可能缓存旧前端;若看不到新标签页,按
Ctrl+Shift+R强制刷新。
暂未发布到 npm:
dsh-plugin-manager/dsh-plugin-hub/dsh-plugin-console三个相近名字均已被他人占用, 因此先用 GitHub 源码安装。本包名dsh-plugin-desk在 npm 上是可用的。
兼容性
| 项 | 值 |
|---|---|
| DSH | >=0.1.5-rc.1(依赖 0.1.5 的 settings.plugins.tab 插槽) |
| Node | >=20 |
| Profile | web |
| 实测环境 | Windows 11 · DSH 0.1.5-rc.1 · Node 24.18.1 |
技能功能需要部署提供 skills 与 agentPresets 服务,以及可解析的 @deepseek-ai/dsh-scope;缺任一项时技能开关会如实拒绝并在界面说明原因,插件页其余功能不受影响。
@deepseek-ai/dsh-scope 从两个解析根依次尝试:profile 根(<DSH_HOME>/profiles/)与 DSH 自身的安装根。任一可用即可,因为预设作用域覆盖要靠它建立;两个都不可用时才会降级,且失败信息会列出实际找过的路径。
自检与测试
node scripts/selftest.mjs # 离线复验插件元数据链路(不启动 loader)
npm test # 22 项单元测试:路由自愈 / 客户端错误硬化 / 技能派生 / 作用域解析 / apply 冒烟
技能功能的端到端实测记录见 tests/acceptance.md(含未验证项的如实标注)。
已验证(全部为实测结果)
界面(设置 → 插件 → 插件管家):
| 验证项 | 结果 |
|---|---|
| 标签页接入 | 与官方 插件配置/插件列表 并列出现,官方页面 data-slot-error 为空 |
| 插件卡片 | 5 张卡片,名称/说明/来源徽章/版本/状态齐全 |
| 噪声过滤 | 168 个已安装包 → 只保留 5 个真插件,过滤 154 个非插件依赖 + 4 个框架内部条目 |
| 分段控制 | `插件 |
| 技能卡片 | 60 张,含作用、whenToUse、来源、指令文件路径、用法提示 |
| 技能开关 | 60/60 可开关;写入后赢得该名字的 provider 变成本插件 |
| 界面一键停用 | 点「停用」→ fiber 已 dispose,徽章变「已停用」,按钮变「启用」 |
| 界面一键启用 | 点「启用」→ fiber 重新装配完成,徽章回「运行中」 |
| 状态持久化 | ~/.dsh/plugin-manager.json / ~/.dsh/plugin-desk-skills.json 落盘,启动时重放 |
系统级验证技能停用真的生效(由 harness 自身确认,非插件自证):
| 动作 | 本会话 <available_skills> 目录的变化 |
|---|---|
设 brainstorming 模型自动加载 = 关 | brainstorming 消失 |
| 恢复默认 | 回来 |
设 grilling 模型自动加载 = 关 | grilling 消失 |
| 恢复默认 | 回来 |
覆盖跨插件重载存活(开机即装,非懒装)。
宿主能力(HTTP 接口层):
| 验证项 | 结果 |
|---|---|
| 元数据解析 | 5 个插件全部解析出中文名与说明;来源判定 github/community/linked/official 全部正确 |
| 安全卸载 | 移除 → 校验确认 → 该插件路由端点随之失效(证明真 dispose)→ 记忆落盘 |
| 官方组件保护 | @deepseek-ai/* 卸载被拒,只允许停用 |
| 技能盘点完整性 | 60 个技能;与独立文件系统扫描(57 个目录 / 14 个模型不可见)逐项吻合 |
| 预设作用域安装 | 连续三次读取 installed=[standard,ptc,minimal,cordis] 稳定不变 |
| 解析根回退 | 人为移除 profile 根的 @deepseek-ai/dsh-scope 后:旧代码 installed=[] 且报「不可解析」,新代码仍为 installed=[standard,ptc,minimal,cordis];恢复后无差异 |
实现说明
- 零构建:
lib/*.js既是源码也是产物,没有打包步骤,改完直接生效。 - 文件职责:
lib/index.js插件清单与 API;lib/skills.js技能盘点、派生函数与覆盖 provider;lib/skill-overrides.js可按预设单独装配的入口;lib/client.js前端。 - 宿主侧零依赖:只用 Node 内置模块。技能作用域需要
@deepseek-ai/dsh-scope时动态解析并 import(不是静态依赖,保住装配可移植性),并依次尝试 profile 根与 DSH 安装根两个解析根。 - 前端模块信封:
lib/client.js按 DSH 的 client-modules 约定包成惰性 CJS 工厂(window.__ModuleLoader__.load({ id, factory })),模块体在首次物化时才执行。 - React 取自 shell 的冻结模块表:
require('react')即可,无需 import;但必须在模块作用域解析一次,放进组件体内会在渲染期炸掉整个 section。详见docs/SLOT-CONTRACT.md。 - 注册值是函数组件:
SlotComponent<P> = (props) => ReactNode。返回 DOM 节点或{ render() }对象会被 React 拒绝,并让官方插件页变成空白。 - 持久化为什么不写 patch 层:bundle 装配会用自己的 id 组合出同名条目,patch 层的
disabled:条目并不能可靠阻断它。因此状态由本插件自管,启动时重放。
权限与数据
| 项 | 说明 |
|---|---|
| 读取 | profile 的 node_modules(仅 package.json / README.md)、pnpm-lock.yaml、运行中的 loader 条目、技能注册表、技能指令文件所在目录(判断清理标记) |
| 写入 | ~/.dsh/plugin-manager.json(插件停用/卸载记忆)、~/.dsh/plugin-desk-skills.json(技能开关覆盖) |
| 网络 | 无 |
| 凭据 | 不读写 |
技能开关不修改任何 SKILL.md:状态由影子 provider 覆盖,删掉状态文件即完全复原。
HTTP 接口
界面之外,宿主能力也可直接调用:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /dsh-plugin-desk/api/list | 插件清单(含中文名、说明、来源、状态) |
| POST | /dsh-plugin-desk/api/toggle | { entryId, enabled } 热启停 |
| POST | /dsh-plugin-desk/api/uninstall | { entryId } 安全卸载 |
| GET | /dsh-plugin-desk/api/skills | 技能清单(含用法提示、来源、清理标记、presetInstall 诊断) |
| POST | /dsh-plugin-desk/api/skills/toggle | { name, model?, user? } 设置技能开关 |
| POST | /dsh-plugin-desk/api/skills/reset | { name } 恢复该技能的默认策略 |
卸载本插件
dsh plugin --profile web remove dsh-plugin-desk
状态文件 ~/.dsh/plugin-manager.json 与 ~/.dsh/plugin-desk-skills.json 会保留;若不再需要,可自行删除。删除技能状态文件后,所有技能立即回到默认策略。
License
MIT