已安装 DSH 插件的书架:按名称/安装时间/大小排序,自由分组与一键启停,批量检查并可断点续装的自动更新。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-plugin-shelf
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:QYF-888/dsh-plugin-shelf
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
一个 DSH 第三方 bundle 插件,用来管理你已经装好的插件。

安装
四种方式任选其一(装完重启 DSH,侧栏出现「插件书架」):
- 从 npm 装(推荐:npm 来源才能被本插件自己的自动更新接管):
plugin_manager install_bundle target=dsh-plugin-shelf - 插件市场:在 DSH 的插件市场里搜
dsh-plugin-shelf/ 「插件书架」,一键安装。 - 从 GitHub 装:
plugin_manager install_bundle target=github:QYF-888/dsh-plugin-shelf - 本地目录装(开发时):
plugin_manager install_bundle target=D:\\path\\to\\dsh-plugin-shelf
装到 profile 后若来源是
file:/link:/git+/github:,本插件不会把它算作可自动更新项(结果里标non-npm-source),其余管理功能照常。
最近更新
1.1.2 — 移出分组、卸载、加插件只列未分组
| 变化 | 行为 |
|---|---|
| 移出分组 | 分组内每行行首一个红色「−」,点一下回到「未分组」;未分组的行没有这个按钮 |
| 卸载 | 每行行尾一个红色「✕」,第一次点击变「确认卸载」,5 秒内再点一次才执行;host 侧 pluginManager.removeBundle,并同步清掉该插件的分组记录与 journal 残留 |
| 加插件浮窗 | 只列出当前不在任何分组的插件,已在本组或其他组的不再出现 |
| 被挂载时卸载 | 管理器会先停用该插件并写 step: 'uninstall' 的 journal;重启 DSH 后由 repairUpdates 直接完成卸载,不会再误当成“重装” |
本机装在 profile 里的依赖必须是
1.1.2或link:指向源码目录;若被「一键更新」换成git+https://github.com/QYF-888/dsh-plugin-shelf.git且该地址仍停在旧提交,面板会跑回旧客户端。

兼容
dsh >= 0.2.0-rc.1 < 0.3.0,profile:desktop / web / headless。host 半只依赖 Node 内置模块
(node:fs / node:path / node:os / node:url),client 半的 React 由 DSH 浏览器模块表提供,无需打包。
功能
| 能力 | 说明 |
|---|---|
| 排序 | 按 名称 / 安装时间 / 大小 排序,可切升序降序(下拉是自绘浮层,与面板同风格) |
| 分组 | 任意新建分组;分组可行内重命名(✎ → 输入框,输入即生效 / Enter 提交 / Esc 取消)、上下移序、折叠、删除;插件可自由移到某个分组或回到「未分组」 |
| 加插件浮窗 | 分组头「+ 加插件」或新建分组后自动弹出选择浮窗:只列当前不在任何分组的插件(已在本组或其他组的不会出现),支持搜索、全选、清空、勾选后一次加入 |
| 移出分组 | 分组内的每一行最前面有一个红色「−」按钮,点一下就把该插件移出这个分组(回到未分组);「未分组」里的行没有这个按钮 |
| 卸载 | 每一行最后有一个红色「✕」按钮,第一次点击变成「确认卸载」(5 秒内再点一次才真正执行),host 侧走 pluginManager.removeBundle,同时清掉该插件的分组记录与 journal 残留;内置插件(removable === false)按钮禁用 |
| 未分组 | 「未分组」也能行内重命名、折叠(✎ / 折叠箭头都支持),名字与折叠状态会持久化 |
| 筛选 | 状态下拉(全部 / 已启用 / 已停用)、分组下拉(全部 / 未分组 / 单组)、「只看可更新」,与搜索框叠加生效;计数行显示 命中/总数,带「清空筛选」 |
| 一键启停 | 每个插件单独开关;每个分组「启用本组 / 停用本组」;顶部「全部启用 / 全部停用」 |
| 自动更新 | 定期查 registry 上的 latest(不再用 pluginManager.inspect,见下)与本地版本比对;可只检查、也可自动安装;可限定只处理某些分组 |
| 一键更新 | 自动更新卡片右侧「一键更新(N)」:两次点击确认(第一次变「确认一键更新?」,4 秒不点自动复原),随后检查全部 profile 插件并逐个执行 removeBundle → installBundle,完成后给出 成功/总数 · 排队 · 失败 汇总卡(可 ✕ 关闭) |
| 更新失败处置 | 展示失败原因、pendingBuilds(需要放行的构建脚本)+「批准并重试」;restart-required 会明确提示 |
| 中断修复 | 「移除→安装」之间被打断会写 journal(pending-updates.json),面板顶部显示待修复项并可一键续装;横幅带「✕ 关闭(不删除记录)」与「清理记录」,关闭状态按 名字@目标版本:阶段 记账,同一插件再次失败会重新出现 |
| 运行日志 | 每次自动更新的检查数、可更新项、安装结果都落在状态文件里,面板可展开查看 |
组成
index.js— host 半:在webServer上注册一条 exact 路由/plugin-shelf/api,负责- 汇总已安装插件(
pluginManager.listBundles())并量出node_modules里每个包的真实磁盘占用与安装时间; - 把分组 / 排序 / 自动更新配置持久化到
$DSH_HOME/plugin-shelf/state.json(saveState是深合并:只发{ui:{ungroupedName}}不会清空分组与排序,数组整体替换); - 执行启停(
setBundleEnabled)与更新(registry 探测版本 +removeBundle→installBundle('pkg@ver')); - 用
setInterval每 60s 检查一次是否到点,到点就跑一轮自动更新(面板关掉也照样跑)。
- 汇总已安装插件(
client.js— 浏览器半:注册sidebar.panellist侧栏入口与main页面板,全部数据来自上面那条路由。
版本探测为什么不用 inspect
pluginManager.inspect(spec) 在 registry 分支开头就把「已安装的包」判为 refused('already-installed')
(dsh-plugin-manager/lib/index.js 里 known.has(parsed.name) 直接拒绝),所以对已装插件永远拿不到
最新版本号。本插件改为直接读 npm registry 的 dist-tags.latest:
- 默认源
https://registry.npmjs.org,面板上可改(写进state.autoUpdate.registry); - scoped 包名把
/转成%2f; - 15s 超时、4 路并发,404 记
not-found,401/403 记registry HTTP <status>; - 结果 reason:
non-npm-source | up-to-date | newer-version | unknown-installed-version | probe-failed: ...。
更新为什么是「移除 + 安装」
本版 DSH 的 installBundle 对已安装在 profile 里的包会直接抛 already-installed(有 hmr 时
Object.hasOwn(before, known) 即失败),没有原地升级。所以一次更新 = removeBundle → installBundle('name@version'),
全过程写 journal($DSH_HOME/plugin-shelf/pending-updates.json),失败会自动 rollbackTo(beforeVersion);
进程崩溃留下的记录由 repairUpdates 续装。
两种已知阻塞及处置:
| 阻塞 | 现象 | 处置 |
|---|---|---|
build-blocked |
installBundle 返回 pendingBuilds(如 dsh-purge@git+...#<sha> 需放行构建脚本) |
面板点「批准并重试」→ 写入 state.autoUpdate.approvedBuilds → 重试时带 approvedBuilds;若首次带审批反而 stale-approval,会自动退回不带审批再试 |
bundle-in-use |
插件仍被活进程挂载(dsh-plugin-manager 检查 loader entries 的 fiber uid),removeBundle 报 failed: bundle-in-use |
该插件被停用并排队(journal 记 step:'queued',面板提示「重启 DSH 后自动完成」),重启后 repairUpdates 完成安装并恢复启用 |
pnpm-workspace.yaml的allowBuilds由插件管理器托管,每次安装都会被重写,靠手改文件放行不可靠; 批准状态因此存在插件自己的 state 里,由每次installBundle显式透传。
数据与安全
- 状态文件:
$DSH_HOME/plugin-shelf/state.json(DSH_HOME未设时为~/.dsh),tmp + rename 原子写。 - UI 偏好(筛选条件、未分组名字与折叠)双写:
state.ui.filter / ungroupedName / ungroupedCollapsed,同时缓存到浏览器localStorage['dsh-plugin-shelf:ui-prefs'];服务端还没加载新版 host(旧normalizeState会丢掉这几个字段)时以本地缓存为准,重启 DSH 后两端一致。 - journal:
$DSH_HOME/plugin-shelf/pending-updates.json。 - 路由带同源校验:带
Origin时必须与Host一致,否则 403;未带Origin的读取放行。 - 更新只处理
availability === 'profile'(真正装进 profile 的包)且来源是 npm-like 的插件;file:/link:/workspace:/portal:/git+/github:/http(s):来源会跳过并在结果里标记non-npm-source。 - 面板默认只列
installed === true且availability === 'profile'的插件,「显示内置」可看到 DSH 自带包(它们不可管理)。
自测
node test/host-harness.mjs
脚本会造一个假 profile(两个假插件),用 stub ctx 挂载真实的 index.js,mock globalThis.fetch
模拟 registry,然后逐个驱动 snapshot / saveState / setEnabled / checkUpdates / update / runAutoUpdate / approveBuilds / repairUpdates 与跨源拒绝分支,覆盖:
- 正常更新:
removeBundle → installBundle@2.4.0 → applied; - 失败更新:
installBundle@9.9.9(failed) → 回滚 installBundle@2.3.0(restored); - 活挂载:
removeBundle → bundle-in-use→ 排队(停用 + journalstep:'queued'),重启后 repair 完成安装并恢复启用; approveBuilds后installBundle的 options 里带上approvedBuilds;saveState深合并:只发{ui:{ungroupedName}}时分组、排序、自动更新配置全部保留;- 跨源请求返回
{"ok":false,"error":"cross-origin request refused"}; - effect 释放后路由注销。
许可
MIT,见 LICENSE。改动记录见 CHANGELOG.md。
链接
同类插件
yjh051108/dsh-routing-suite★ 6994
一个仓库三件套:DSH 插件包的运行时注入器(注入、热重载、卸载、开发侧挂区一键转正、路由自愈,外带设置页插件管理:列出、卸载、拖入文件夹内化)、任务感知的思维模式路由 agent 预设(router-standard / router-spec / router-react)、以及分级两级任务协议(commit_star / lock_stage / revise_do / edit_plan / mark_task / redteam_verdict 六个工具,任务状态落盘)。注入器实现直接在库内,安装的是它自己的行为而不是一份依赖清单。
strukto-ai/mirage#dsh★ 3762
把文件系统与 bash 提供者换成 mirage 虚拟工作区:文件工具与 shell 命令作用于挂载的资源(RAM、S3、Redis、Slack、Gmail、Notion、Postgres)而非宿主磁盘,支持按挂载点设置读/写/执行模式、按命令选择沙箱(进程内 monty、pyodide、quickjs;远程 docker、e2b、daytona),并可在虚拟终端中安装 CLI(git、gh、slack、linear、ntn、gws,或自行注册的程序树)作为命令头词。
first-fluke/oh-my-agent#dsh★ 1357
在工具执行前后以及尝试结束当前轮次时运行项目 OMA 检查。随插件安装 OMA CLI 依赖,并在首次使用时初始化未配置的项目。需要 Node.js 22+;首次初始化需要网络连接和项目目录写入权限。
MichengAI/dsh-skills-manager★ 444
在设置里管理本地 DSH 技能,并只读查看公共 Agent 技能。
hust-open-atom-club/oh-dsh★ 324
社区发行版:TUI、桌面端与 Web UI 统一体验,分层安装、一步到位。
Fishquito7/dsh-skill-mcp-panel★ 248
在 DSH Web 设置中管理技能与 MCP 服务器:技能卡片热启停、工作区作用域、分组、批量迁移与拖拽导入,以及 stdio/HTTP MCP 增删改查、连接测试、密钥脱敏,并附带统一 dsh-panel 命令行。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。