DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

deadbushxw /

deadbushxw/dsh-plugin-query-enhance

Verified

DSH 插件:精简 profile 中插件管理列表的回传信息,每项只回一行摘要,并新增支持参数输入的条件查询工具 | DSH (DeepSeek Harness) plugin: keep plugin manager list actions from returning the whole table by answering with one summary line per record, and add a parameterized query tool for filter-based lookups

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@a732e2eb

dsh-plugin-query-enhance

English | 中文

一个 DSH Host 侧插件,让"查询当前 profile 的 bundle"这件事变便宜。

它做两件事,两者互补而非二选一:

  • 给 list 类 action 的返回瘦身。 plugin_manager list_bundles 与 plugin_manager list_plugins 都是管理面,都会回传多于调用方所需的内容。 list_bundles 回传每个 bundle 以及 该 bundle 声明的完整插件行清单 —— 实测 某个真实 profile 里(DSH 0.2.0-rc.2),这份回传的大头集中在两个 bundle 上: @deepseek-ai/dsh-base 的声明行有 94 条,@deepseek-ai/dsh-web-app 有 89 条。而 list_plugins 回传的是一页调用方并没有挑选过的插件条目。现在两者都变成每条记录一行摘要。 投影在 Host 内部、序列化之前完成,所以这些字节是根本没有产生,而不是产生了 再被读者忽略。
  • 新增 plugin_query:一个只读工具,接受过滤条件,只回传通过筛选的记录 —— 精细 到"哪个 bundle 声明了这个模块"。

两条机制都没有改动 @deepseek-ai/dsh-plugin-manager。核心保持原样,Web 侧边栏 继续直接读服务,卸载这个 bundle 即完全恢复原有行为。

环境要求

操作系统 不限(插件是纯 JavaScript,不调用任何平台 API)
DSH profile 中挂载了提供 pluginManager 服务的管理插件
Node.js 20+ —— 仅测试与边界自查脚本需要,运行插件本身不需要

包名、仓库目录名、Loader 行 id、显示名统一使用同一个标识 —— dsh-plugin-query-enhance —— 只有一个名字需要检索。Loader 只读 package.json,目录名无关紧要。


问题,以及量化

plugin_manager 有两个 list 动作。两者都只接受 offset 与 limit,没有按名 字、按状态、按字段的筛选。默认页大小是 25,而一个 profile 大约二十来个 bundle, 所以第一次调用就会把整张表取回来。分页在这里帮不上忙,因为根本没有"下一页" 可言。

每个 bundle 回传的是管理记录:标识、四个状态布尔值、包描述、完整的 rows 声明行数组、它覆盖的内置行,以及可能的错误与诊断。其中 rows 占绝对大头:整张 表共 247 条声明行,其中 183 条(74%)来自上面那两个 bundle(94 + 89, DSH 0.2.0-rc.2 实测),每条都带 rowId、moduleName 与实时的 entryId。投影 之后,这两个 bundle 在 list_bundles 里各自只剩一行摘要 —— 声明行本身根本不会 被序列化。

由此引出两个后果,本插件分别应对:

后果 对应机制
模型不知道有更合适的工具,直接调 list_bundles,为整张表付费 投影:list_bundles 照常可用,但每个 bundle 只回一行摘要
模型清楚自己要什么,却没有参数可以表达 plugin_query:过滤器进入 schema,筛选在 Host 侧完成,只回命中的记录

安装

安装即 bundle 安装:DSH 会把包写进当前 profile、注册 Loader 行并热应用。不要手工 编辑 profile。

1. 把代码放到机器上

git clone https://github.com/deadbushxw/dsh-plugin-query-enhance "%USERPROFILE%\.dsh\dsh-plugins\dsh-plugin-query-enhance"

任何目录都可以,上面的路径只是示例。没有构建步骤,但有一次依赖安装(见第 3 步)。

2. 装进一个 profile

在目标 profile 里对 Agent 说,可以用克隆下来的目录,也可以直接用仓库地址:

用 plugin_manager install_bundle 安装 %USERPROFILE%\.dsh\dsh-plugins\dsh-plugin-query-enhance 这个 bundle。

或者不克隆,直接从 GitHub 安装:

用 plugin_manager install_bundle 安装 github:deadbushxw/dsh-plugin-query-enhance。

也可以在 GUI 里操作:设置 → 插件,把该包目录作为本地 bundle 添加并启用。

变更生效时 plugin_manager 会返回 application: "applied"。若返回 restart-required,重启 DSH。用新代码替换已安装的包同样需要重启,因为 Host 会 缓存模块实例。

3. 安装运行时依赖

本包有一个运行时依赖 @deepseek-ai/schemastery,它不在仓库里(见「有意不提交 什么」)。上面第一种安装方式(克隆到本地目录)需要这一步 —— 进克隆目录执行:

npm install --no-audit --no-fund

跳过它的后果是静默的:install_bundle 返回成功、npm test 也全绿,但插件不会 装载 —— lib/index.js 静态导入的 lib/schema.js 解析不到依赖,模块加载整体失败, apply() 从未执行,于是投影与 plugin_query 一起失效,而且没有任何报错。一个可用 的判据:plugin_manager list_plugins 里本条目是唯一 enabled: true 却 fiberPhase: null 的条目(对照第 4 步)。

补装依赖之后需要重启 DSH 才会重新装载:Node 的 ESM 模块缓存已记住那次加载失败, Loader 不会重试。

走上面第二种方式(github: 仓库地址)不需要这一步:插件被装进 profile 自己的 node_modules,而 profile 里已经有它需要的依赖。

4. 确认它能用

先请求 bundle 列表,看返回的形状:

plugin_manager list_bundles

现在每条记录只带 name、version、enabled、installed、optional、 removable 与 rowCount(只读时另有 readOnlyReason,有覆盖时另有 overrideCount,出错时 error 退化为错误码字符串),没有 rows 数组、没有 description、没有 meta。 再问一个只有新工具能回答的问题:

plugin_query match="@deepseek-ai/dsh-plugin-manager"

第一次回答是每个 bundle 一行、第二次回答指名了声明该模块的 bundle —— 两半都 在工作。

卸载

plugin_manager remove_bundle dsh-plugin-query-enhance

不会留下任何残留。list_bundles 恢复为回传完整表格。


用法

现在 list 类调用回传什么

动作本身没变,包括 offset、limit、total 与 nextOffset。只是每条记录更小。 bundle 长这样(plugin_manager list_bundles limit=1 的返回,DSH 0.2.0-rc.2 本机实测,为便于阅读折行):

{"entries":[{"name":"@deepseek-ai/dsh-base","version":"0.2.0-rc.2","enabled":true,
"installed":false,"optional":false,"removable":false,
"readOnlyReason":"management-required","rowCount":94}],
"total":21,"nextOffset":1}

被去掉的字段,以及为什么可以去掉:

字段 为什么可以去掉
rows 大头。需要时用 plugin_query includeRows=true 指定单个 bundle 取回
description 包自带的说明文字,写给在包管理器里浏览的人看
meta 本地化标题、描述与图标路径,供 Web 客户端渲染;该路径本就不经过这里
overrides bundle 覆盖的内置行 id;已归结为 overrideCount
error.diagnostic、error.incompatible 长文本失败详情。错误的 code 保留,因为决定下一步的是它

另有两个字段专门说明"被省略了什么":rowCount 始终存在,使读者能区分"这个 bundle 不声明任何插件"和"这份回答把声明行藏起来了";overrideCount 仅在确有覆盖 时出现。

插件条目长这样(plugin_manager list_plugins limit=1 的返回,同一台机器,折行 同上):

{"entries":[{"entryId":"96732430","moduleName":"@deepseek-ai/dsh-host-directory-picker-native",
"enabled":true,"fiberPhase":"active","readOnlyReason":"unaddressable"}],
"total":202,"nextOffset":1}

摘要唯一丢弃的字段是 patchId:它是寻址该条目的 profile patch 行 id,没有任何 已文档化的操作以它为参数。entryId 保留,因为 set_plugin 收的就是它; readOnlyReason 也保留,因为它解释了某个操作被拒绝的原因。

需要 id 去调 set_plugin?plugin_query name="..." includeRows=true 可以取回某个 bundle 声明的行,而 list_plugins 的摘要本身就是带 entryId 的。再多的内容 —— 完整的 bundle 记录、插件条目的 patchId、错误诊断 —— 只由 plugin_query 配合 detail: "full" 提供,那是唯一会返回完整记录的路径。

plugin_query

只读,不接受任何会改变状态的参数,也不需要审批。

参数 适用范围 含义
kind 两者 "bundles"(默认)或 "plugins",后者查询单个插件条目
name bundles 精确的 bundle 包名,例如 @deepseek-ai/dsh-base
match 两者 不区分大小写的子串,匹配包名、描述,以及 bundle 声明行的模块名
enabled 两者 只保留保存的启用状态等于该值的记录
installed bundles 只保留已安装状态等于该值的 bundle
optional bundles 只保留"出厂关闭、供用户自行开启"的 bundle
hasError bundles true 只保留加载失败的;false 只保留加载正常的
detail 两者 "summary"(默认)或 "full",后者是含描述与声明行的管理记录
includeRows bundles 在摘要中一并带上声明的插件行
limit 两者 页大小,1 到 100,默认 10
offset 两者 在筛选后结果中的零基偏移,默认 0

多个条件之间是 AND。kind: "plugins" 搭配 bundle 专用过滤器会报错,而不是被 静默忽略;name 或 match 传空串同样报错 —— 空过滤器会悄悄匹配一切,而这正是 本包要避免的结果。

每次回答都带 total(清单总量)、matched(通过筛选的数量)与 nextOffset。 total/matched 这组字段让"零结果"不再有歧义:matched: 0 而 total: 21 意味着 bundle 存在但被筛掉了,不必为了确认这一点再发一次不带条件的调用。

示例:

plugin_query name="@deepseek-ai/dsh-base"
plugin_query match="@deepseek-ai/dsh-plugin-manager"   # 哪个 bundle 声明了这个模块?
plugin_query enabled=false                        # 哪些是关闭的?
plugin_query hasError=true detail="full"          # 某个东西为什么加载失败?
plugin_query optional=true                        # 哪些是出厂关闭的?
plugin_query kind="plugins" enabled=false limit=50

配置

没有配置文件,也没有设置页。两项设置都在该 bundle 自己的 cordis.patch.yml 里, 由包导出的 Config schema 校验。改这里并重新安装该 bundle。

- insert:
    - id: dsh-plugin-query-enhance
      name: 'dsh-plugin-query-enhance'
      config:
        intercept: true
        defaultDetail: summary
字段 默认值 含义
intercept true 是否对 plugin_manager list_bundles 的结果做投影。设为 false 可保留工具但不再改写该动作的输出
defaultDetail "summary" 调用未指定 detail 时 plugin_query 采用的详细级别

实现方式

两个扩展点,不 fork:

机制 扩展点 为什么选它
投影 tools/post-execute 工具运行时把它文档化为"转换结果"的位置,也是插件能触及的、唯一能改变模型从另一个工具收到什么的点。它同时覆盖两个 list action,因为调用方会挑哪一个无法预判
查询工具 ctx.tools.register() 加 pluginManager 服务 新增工具不会与任何东西冲突;而该服务正是管理工具自己读取的来源

三个值得说明的取舍:

  • 投影在做出任何判断之前先调用 next(),即使是对它要改写的调用。一个不向下 委托就返回决定的 waterfall 监听器会终止它身后整条链 —— 比如一个本该记录或拦截 该调用的工具钩子插件。改写"整条链最终达成的决定",并在别人已经替换结果时让位, 才能保住其他插件的决定。
  • 无法解析的载荷原样放行。 一个跑不起来的投影必须对调用方零成本,绝不能破坏 一次本来可用的管理调用。同一道护栏也覆盖未来改变信封结构的 Host。
  • 工具用注册表实际校验的原始 JSON Schema 定义,而不是走官方 defineTool 辅助 函数。那个辅助函数住在 @deepseek-ai/dsh-tools 里,引入它会让本包绑死在某个 Host 包的某个版本上:克隆下来要先联网解析依赖才能加载,Host 升级还可能让两份副本 失去同步。注册表公开的契约是 ctx.tools.register(),而它接收的正是编译后的形式。 代价是辅助函数本该安装的参数校验,改由这里手写并测试覆盖。

仓库边界

这个仓库可以直接公开发布,而本节是让这句话可被检验、而不是一句声明的契约。

提交什么

源码、测试、工具脚本、文档与元数据:

.gitattributes  .gitignore  LICENSE
README.md  README.en.md
package.json  cordis.patch.yml
icon.svg  locale/{en,zh}.json
lib/**                 插件本体
test/**                测试套件,无需安装即可运行
tools/verify-repo-boundary.mjs   下文所述的边界自查脚本

有意不提交什么

排除项 原因
DESIGN.md 内部工作稿。其中引用了撰写时所在机器的绝对目录,发布即泄露该机器的布局
node_modules/ 可由 package.json 复现;在 diff 里只是噪声
package-lock.json、pnpm-lock.yaml、yarn.lock 依赖由使用者在插件目录执行 npm install 负责(见「安装」第 3 步)。DSH 的 install_bundle 只负责 profile 自己的依赖,不会往 link 目标目录里装;多一份锁文件只会描述另一个解析器并造成漂移
*.bak、*.orig、*.rej、*.log 编辑与安装过程中留下的本地临时产物
config.json、plugin-data/、.env*、*.pem、*.key、.credentials.yaml 运行状态与凭据。本插件两样都没有,这些模式是第二道防线
编辑器与系统噪声(.vscode/、.DS_Store、Thumbs.db 等) 不属于本项目

本项目不含任何密钥:它不保存账号、令牌或 API key,也不发起网络请求。

自己验证这条边界

npm run verify-boundary

verify-boundary 读取的是 git 会发布的文件集合(git ls-files),而不是工作 区:未被跟踪的本地文件正是这条边界要挡住的东西。它会报告机器相关的绝对路径、 凭据形态的文本、运行状态与异常大文件,并在发现任何问题时以非零码退出。每次推送 前运行一次。


开发

npm test        # 全部测试;无需安装
npm run verify-boundary

测试套件没有任何依赖,也没有副作用:它不碰文件系统、不联网、不接触 Host。这并非 偶然 —— lib/config.js 刻意不 import 任何东西,唯一的运行时依赖单独放在 lib/schema.js,因此测试所引入的模块都不需要解析依赖。npm test 因此不需要安装 任何东西;但要让插件真的装载(或加载 lib/index.js),必须先 npm install(见 「安装」第 3 步)。

结构与拆分理由:

文件 职责
lib/index.js 入口。接上 waterfall 监听器与工具,并负责拆除
lib/config.js 常量与生效设置。不引任何东西
lib/schema.js Config schema。唯一带依赖的文件
lib/shape.js 剥离展示元数据,两条机制共用
lib/project.js 机制一:把整份 list 载荷归结为摘要,两个 action 共用
lib/intercept.js 机制一:tools/post-execute 监听器
lib/filter.js 机制二:查询引擎
lib/tool.js 机制二:工具定义与参数契约

已知限制

  • 无法扩展管理工具自身的 schema。 插件不能给另一个插件注册的工具加参数,所以 list 类调用只能被默认瘦身,无法被过滤。过滤是 plugin_query 的职责;投影是 给"仍然伸手去够老工具"的调用方准备的兜底。
  • list_plugins 本来就被管理工具剥掉了一层。 它在返回前已去掉展示元数据; 投影在这里再去掉 patchId 并保持同一信封,所以收益真实但有限,大头仍然是 list_bundles。
  • 对声明行很多的 bundle,detail: "full" 依然很大。 它按定义就是管理记录。用 match 可以把行收窄到命中的那些,通常这才让它变得可负担。
  • 若 pluginManager 服务不存在,plugin_query 不会出现。 投影那一半照常工作, 只是不注册该工具。可通过插件自身的 debug 日志区分这两种情况。
  • 该工具会给每次请求增加一点固定开销。 无论是否使用,它的 schema 与描述都会 被发送。描述刻意写短正是为此;而投影那一半零开销。
  • 没有设置页。 两项设置都是行配置,在 cordis.patch.yml 中修改。加一个设置页 意味着把字段标记为 volatile 并附带一个客户端半边,对两个布尔值来说动静太大。
  • 投影是策略,不是过滤器。 它决定的是"没有更好的问法时 list_bundles 回传 什么"。intercept: false 可精确恢复原行为。

许可证

MIT © 2026 deadbushxw。

—/ 5

No ratings yet

Verified DSH bundle

Commit a732e2eb0fd9

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout