DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

dsh-plugin-upgrader

Plugin Upgrader

DeepSeek Harness Plugins 页面上的插件一键升级、版本历史记录和回滚

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:zuwenyin/dsh-plugin-upgrader#9f1d0693fecd9c57a28c7bb5e7757f8152d12a6b
README兼容性版本

兼容性与来源证明

Plugin Upgrader 以 dsh-plugin-upgrader 发布,当前版本为 1.7.5。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/11/4

版本

1.7.5stable
2026/11/4
1.7.4stable
2026/11/4
1.7.3stable
2026/11/4
查看其余 21 个版本收起版本
1.7.2stable
2026/11/3
1.7.1stable
2026/11/3
1.7.0stable
2026/11/3
1.6.3stable
2026/11/2
1.6.2stable
2026/11/2
1.6.1stable
2026/10/30
1.6.0stable
2026/10/30
1.5.0stable
2026/10/30
1.4.3stable
2026/10/28
1.4.2stable
2026/10/28
1.4.1stable
2026/10/28
1.4.0stable
2026/10/28
1.3.0stable
2026/10/27
1.2.0stable
2026/10/27
1.1.2stable
2026/10/27
1.1.1stable
2026/10/27
1.1.0stable
2026/10/27
1.0.3stable
2026/10/26
1.0.2stable
2026/10/23

相关插件

正在加载相关插件…

最新版
1.7.5
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 3
周下载
2,665
最近提交
2026/11/4
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
1.0.1
stable
2026/10/23
1.0.0stable
2026/10/22

README

dsh-plugin-upgrader

DSH Market 已收录

给 DeepSeek Harness(DSH)的「插件」页补上一键升级和版本历史 / 回滚。

DSH 自带的插件页能装、能卸、能看已安装列表,但缺两件事:一次把全部插件升到最新,以及退回某个旧版本。 这个插件在插件页里加一个「版本」标签,把这两件事补齐。

English · 中文

版本标签

功能

  • 一键升级 —— 一次把全部可升级插件升到最新版,带进度、逐项结果和失败原因。
  • 升级不卸载 —— 换版本走 in-place 替换,插件不会被卸下来再装回去(详见下文)。 拿不到 profile 写入锁、或 in-place 那一步被拒时,会退回「先卸载再装」的老路并写明原因。
  • 看得见的进度 —— 升级时直接显示 pnpm 的原始输出(含换源重试、lockfile 校验), 每步都有已用时计时和不确定态进度条,卡住时可以一键取消。
  • 多选升级 —— 勾选任意几个插件,只升级选中的这些。
  • 版本历史 / 回滚 —— 展开任意插件即可看到已发布的全部版本(含发布时间与引擎要求),点一下切到那个版本。 每个版本都会先跟你当前跑的 DSH 比对一次:装不了的版本会被标出来,切换按钮也是禁用的 (宿主的兼容性闸门是在 bundle 已经卸下之后才拒绝的,不能让它走到那一步)。
  • 一键跳 GitHub —— 每行的 GH 按钮打开插件仓库主页;点版本号直接进该版本的 GitHub 发布页。
  • 镜像落后会标出来 —— 配置的镜像还没同步、但上游已经发布的版本会单独标一行, 而不是假装已经是最新版。
  • 自己选安装源 —— 版本页顶部就能切换 npm 源(官方源 / 大陆镜像 / 自己填的私有源), 选完立刻按新源重读版本列表,升级也走这个源;选择写进 profile,重启和换浏览器都还在。
  • 安装源管理 —— 「注册表」按钮打开一个对话框,里面列出自带源、pluginManager 配的源 和用户自己加的源;自带源不能改也不能删,自己加的可以随时改/删。每一行都能单独 「测试」能不能连上,当前在用的那个也能一键测。
  • 要重启时告诉你怎么重启 —— 升级结果里带 restart-required 时,提示条上多出一个 「重启 DSH」按钮,点开说明为什么这次改动要等,并按你的启动方式给出那一条能完成重启的 操作:桌面版是「退出应用再打开」,命令行启动的是可复制的原命令。确认重启后它会盯着 宿主的 pid,换了进程才刷新页面,而不是让你对着一个不确定的页面猜。

安装源管理

要改这个仓库?先读 AGENTS.md。 那里逐条写了「已经实现并且必须继续成立」 的行为、对应代码位置和守护它的测试。上面这些功能里,GH 链接和 in-place 升级都曾经 被无意改掉过一次。

界面位置

插件页顶部标签栏多出一个「版本」标签,与「已安装」以及市场标签并列 (市场标签在装了 dshmarket 的界面里叫「插件市场」,DSH 自带的那个叫「发现」):

  • 顶部统计:可升级数量 / 已安装数量 / 每个插件的版本数
  • 顶部「注册表」按钮打开安装源对话框:
    • 自带源(官方源、大陆镜像):只能切过去和测试,不能改也不能删
    • pluginManager 里配的源:可以「收进列表」变成自己的常用源,或直接用
    • 自己加的源:可以改、可以删,删掉的如果正在用会自动退回「跟随 pnpm 配置」
    • 每行「测试」同时打 /-/ping 和根地址,谁先答应用谁,整次探测 5 秒预算 (串行探测会把大陆镜像报成连不上:/-/ping 有时要 15 秒,根地址只要 0.3 秒), 结果显示在那一行下面
    • 底部「跟随 pnpm 配置」用 pnpm 自己的源(通常是 ~/.npmrc 里的那个)
  • 「一键升级」升全部可升级插件;下面一条选择栏负责「升级选中」
  • 「检查更新」强制刷新 registry 缓存(默认缓存 5 分钟)
  • 搜索框按包名过滤
  • 每个插件一行,展开「历史 (n)」后是该插件的完整版本列表
  • GH 打开仓库主页(没有仓库的插件不显示该按钮)
  • 版本列表里每个版本号本身就是链接,点开直接进 GitHub 的发布页:
    • 该版本打过 tag → releases/tag/v<version>(tag 是否真实存在由 GitHub tag 列表确认, 不是猜的,所以不会点进 404)
    • 该版本没打 tag,或 GitHub 列表读不到(限流 / 离线)→ 退回仓库的发布列表 releases, 不在页面上放一个点进去是 404 的版本号

展开历史版本

安装

在「插件」页的市场标签里搜索 dsh-plugin-upgrader(自带的是「发现」, 装了 dshmarket 的界面里叫「插件市场」),点安装即可。 桌面版请用它自带的插件管理界面安装,命令行改动不了正在运行的桌面 profile。

用 dsh 命令行(把 web 换成你自己的 profile 名):

dsh plugin --profile web add dsh-plugin-upgrader

从源码安装:

git clone https://github.com/zuwenyin/dsh-plugin-upgrader.git
cd dsh-plugin-upgrader
dsh plugin --profile web add "$PWD"

装好后打开「插件」页,顶部会多出「版本」标签。不需要重新构建,也不用重启。

工作原理

浏览器端读不到 profile 的依赖清单,也无法可靠直连 npm(CORS、私有源、token), 因此所有包操作都放在宿主端:

路由作用
GET /api/plugin-upgrader/overview已安装插件 + 各插件已发布版本(registry packument,5 分钟缓存,20 秒超时,最多 4 个并发)
POST /api/plugin-upgrader/job提交一批升级 / 切换步骤,立即返回 job id
GET /api/plugin-upgrader/job?id=…轮询进度、当前阶段、已用时、逐项结果与实时日志
POST /api/plugin-upgrader/cancel中断当前步骤(任何一次安装,包括退回老路径后的重试)
GET /api/plugin-upgrader/host本进程的 pid 与启动方式,供「重启 DSH」对话框说明怎么重启

所有路由仅限本机回环访问:批量操作会安装软件包,配对设备或跨站页面一律拒绝。

为什么升级不卸载插件

pluginManager.installBundle 对已安装的包名会以 already-installed 拒绝, pnpm 根本不会跑。所以换版本的顺序是:

  1. 先直接 install;
  2. 被拒绝时,把 profile package.json 里那条依赖临时摘掉(先写 journal),再 install —— pnpm 写回新版本,插件不被卸载,加载器里的实例不断,启用状态也不用恢复;
  3. 只有第 2 步主动放弃(manifest 读不了 / 拿不到写入锁 / install 失败 / 被拒绝)时, 才退回 remove + install 的老路径,并在日志里说明是哪一种。

第 2 步改动 package.json 时会拿 harness 自己的写入锁(package.json.lock), 与 plugin-manager 用同一把锁:拿不到锁就一秒内放弃、一个字节都不写, 然后走第 3 步那条永远正确的老路——比在别人的 read-modify-write 中间插一脚、 把条目弄丢要好得多。

旧版本号会回传给界面,用于提示这是降级。

实现在 1.2.0 就有了,但 1.2.0–1.4.0 在桌面端从未真正生效: profileDirFrom() 只认 DSH_PROFILE_DIR / DSH_HOME + DSH_PROFILE,而桌面壳启动宿主时 不导出这些变量(它直接把 profile 目录当作子进程的 cwd),于是第 2 步第一行就放弃了, 每次升级都悄悄走 remove + install。1.4.1 加了 cwd 回退之后才真正走这条路。 细节见 AGENTS.md 第 2 条「换版本时不先卸载」。

升级中断后 profile 还能自己长回来

第 2 步要在「依赖条目已经删掉、新版还没装上」的窗口里改 profile 的 package.json。 如果进程正好在这段时间被杀掉,插件就从 manifest 里消失了,等于装不回去。 所以删除之前先写 package.json.upgrader-journal.json:下次插件加载时会读它, manifest 里确实缺这条就补回去,已经装上就只清掉记录,记录和 manifest 对不上就忽略。 恢复失败只是打日志,不会拦住标签页加载。

记录只在锁内确认条目真的回到盘上之后才清掉。还原那一步如果拿不到写入锁, 它宁可失败并留下记录,让下次加载再修一次——把记录清了却没能写回去, 才是真的没法自愈。

enabled 状态保留

卸载会丢掉插件原有的启用 / 禁用状态,因此任何真的卸载过插件的步骤都会在装完后 按操作前的状态重新 setBundleEnabled(src/host/install.ts:251 restoreEnabledState), 禁用中的插件升级后仍然是禁用状态。in-place 那条路不卸载,所以本来就不需要恢复。

为什么升级有时候要好几分钟

不是这个插件慢,是 pnpm 慢。pnpm 11 在 install 前会拿锁文件里的每个条目去 registry 重新核对一次(Verifying lockfile against supply-chain policies),国内镜像 (registry.npmmirror.com)对不少包会 ETIMEDOUT,于是走 10 秒 → 1 分钟的重试阶梯。 真正卸载再安装的那种降级路径要跑两条 pnpm 命令,两段各等一次。

旧版本在这里什么都不显示,进度条只在「整步完成」时动一格,所以看起来就是死住了。 现在宿主端订阅了 plugin-manager/install-log 和 plugin-manager/install-state, 把 pnpm 的原始输出、当前阶段、重试次数实时转进 job 日志,界面同时显示已用时和 不确定态进度条:慢的时候你能看见它在重试,而不是盯着一个不动的 0%。

阶段一共四个:verifying(在校验锁文件)→ installing → applying / cancelling。 锁文件校验那一行会连续几分钟不动,单看和「卡死」一样,所以单独给它一个阶段名。

阶段是按 pnpm 自己的话判的:manager 是在 pnpm 启动之前就上报 installing 的, 所以 pnpm 后来的校验行必须还能把阶段改写成 verifying,一直到它给出结论 (✓ Lockfile passes supply-chain policies)为止。

「还在动」问的是宿主,不是 pnpm。pnpm 重试的时候一分钟不打印一行是正常的, 按「日志没变」判停滞会把健康的升级误报成卡住;宿主每 2 秒盖一次心跳, 面板用心跳判定活性,宿主真的不说话了才会说「状态已停止更新」。

顺带修掉的两个坑:

  • 心跳改成定时器(2 秒),不再按「完成一步」写。单步超过 15 分钟以前会被误判成 「job 已放弃」,于是一个健康的任务被作废,还能再并进来一批。
  • 退回老路径时那一次重新安装以前用 requestId: undefined,导致那一段无法取消; 现在每次安装都有自己的 id,取消在任何阶段都有效。

写入锁超时说人话

profile 的 package.json 由 harness 用同目录的 package.json.lock 串行化。 manager 自己的预算是 120 秒,而 atomic-write 给不写预算的调用方的默认值是 2 秒, 所以偶尔能看到:

atomic-write: timed out waiting for the writer lock at …/package.json.lock

这句话只报一个文件名和一个 pid,用户无法处理。现在失败信息会先读那把锁,再说清楚是 「另一个操作正在改写,等它结束再试」还是「锁没释放,重启 DSH 会自愈」, 原文放在方括号里保留,方便搜索或贴进 issue。

自己那一行不能自己升级

批量升级的第一步是卸载,而卸载的正是此刻在跑的那段代码。所以「一键升级」永远跳过本插件自己, 自己那一行落后时走插件页自己的安装流程;页面没有提供安装入口时明确说一句, 而不是让人点一个必然失败的按钮。

同一时刻只跑一批

宿主端一次只允许一个 job。重复提交会拿到 409 busy(带正在跑的那个 job id), 界面据此接管那一批的进度显示,而不是弹一条看不懂的错误:

  • 新开一个插件页标签、或在进度跑着时再点「一键升级」,都会看到正在进行的那一批
  • overview 里带 active 字段,标签页一挂载就能接上正在跑的任务
  • job 每 2 秒刷一次心跳时间戳,宿主重启后遗留的「僵尸 job」会按心跳超时自动作废, 不会永久占着位置让之后所有升级都撞 busy

主进程重启会让正在跑的 job 中断,界面会把这种情况显示为失败而不是永远转圈。

权限、依赖与外部服务

这一节把插件实际用到的能力写清楚,方便审核和排障时对照,而不是让人去读源码猜。

读写什么

能力范围说明
文件仅 profile 目录读写 package.json、写 package.json.upgrader-journal.json、读写并短暂持有 package.json.lock。不遍历文件系统,不碰 profile 以外的路径
网络(出站)配置的 npm 源、GitHub API只发 GET,见下
命令无不调用 child_process,不 spawn 任何进程;安装动作交给宿主的 pluginManager
凭据无不读 token,不读 .npmrc,不做任何认证。私有源依赖 pnpm 自己的配置

安装源的选择(registry / customRegistries)由宿主的设置服务写进 profile 的 cordis.patch.yml,不是插件自己改文件——插件只调 configForms 的 set()。

宿主进程的环境变量只用了 DSH_PROFILE_DIR / DSH_HOME / DSH_PROFILE 三个来定位 profile 目录(桌面壳不导出它们时,退回「当前工作目录看起来像不像一个 profile」)。它们不是密钥,但 静态扫描器会把裸 process.env 记成一次「凭据」信号,这里说明清楚它的真实用途。

外部服务

目标用途失败时
{配置的 npm 源}/{包名}拉 packument,列出已发布版本(5 分钟缓存,20 秒超时,最多 4 个并发)该行显示 versionError 和「重试」,其它行不受影响
https://registry.npmjs.org/{包名}只当配置的不是官方源时,额外查一次上游,报告「镜像落后」读不到就当作没有缺口
https://api.github.com/repos/{owner}/{repo}/tags?per_page=100取 tag 列表,判断某个版本号能不能链到 releases/tag/v…(30 分钟缓存,8 秒超时)退到 releases 列表页,绝不猜一个 404 的 tag
{被测试的地址}/-/ping 与根地址「注册表」对话框里点「测试」时,并发探测这两条,谁先 2xx 用谁(整次探测 5 秒预算)报最具体的原因:状态码优先,其次超时,最后网络错误原文
本机 DSH Web 服务/api/plugin-upgrader/* 自己的五个路由—

默认源是 pnpm 自己配置的那个(通常是 .npmrc 里的),也可以在「版本」页的「注册表」按钮里 换成官方源、大陆镜像或自己加的源。没有任何遥测,不向第三方发送使用数据。

依赖

运行时没有自带依赖。唯一的外部模块 @deepseek-ai/schemastery(Config schema 用,要求 ~3.18.5)声明在 peerDependencies 里,由宿主提供——DSH 桌面端把它作为自身依赖发布, dsh-plugin-manager 等第一方包也依赖它,所以 profile 里一定解析得到。构建和测试需要的那一份 放在 devDependencies。

devDependencies 其余各项(TypeScript、esbuild、@types/*、@deepseek-ai/dsh-*)只在开发和 构建时使用,不会进入发布的 tarball 运行时。

失败边界

  • 一次只跑一批:第二个提交拿 409 busy,界面接管正在跑的那一批,不报错。
  • job 心跳 2 秒,15 分钟没进展判废,不会永久占位;主进程重启会中断正在跑的 job, 界面显示为失败而不是永远转圈。
  • 写入锁:manager 自己的预算是 120 秒,超时会把锁的状态翻成人话(谁持有、要不要重启)。 in-place 那条路自己也要这把锁,拿不到就在一秒内放弃并改走 remove + install, 不会去改别人的 manifest。
  • 升级失败:in-place 替换会先把被摘掉的依赖记进 journal,失败就还原;进程被杀掉时, 下次加载会把依赖补回去。还原时拿不到锁就保留 journal 等下次,不会先把记录清掉。 失败了插件仍在、启用状态仍在。
  • 还原了但没激活:旧版本装回磁盘、宿主却没把它加载起来时,会明确说「文件在盘上, 去『已安装』里重新启用」,而不是说「没装、去重装一遍」(重装只会得到 already-installed)。 只有插件真的跑起来了,才算「已还原」。
  • registry 读不到 / GitHub 限流 / 离线:都降级成「这一项读不到 + 原因 + 重试入口」, 不会伪装成「没有新版本」「没有历史版本」。

DSH 兼容性

要求 Node.js >=20,DSH >=0.2.1-rc.3 <0.3.0-0(package.json 的 engines)。 dsh.compatibility.dshReleases 只记录真的跑过的版本:

DSH 版本状态
0.2.1-rc.5compatible

0.2.1-rc.4 和 0.2.2-alpha.2 虽然落在 engines 范围内,但没有逐一实测过,所以不写进去 (未声明的版本按 unknown 处理)。这是来源侧的兼容声明,不代替逐项的操作验收; dshOperations(install / start / uninstall / rollback)同理,没有逐版本的实测记录就留空, 不写 passed。

每个版本对不对得上现在的 DSH

DSH 在装插件时会检查插件声明的 DSH 要求,不满足就拒绝安装。这个检查发生在版本已经被 摘掉之后(老路径是先卸再装),所以「点了切换,结果插件没了」是真实发生过的事。因此页面自己 先算一遍,规则和宿主完全一致:

  • 插件声明了 engines.dsh 时,只看它,peerDependencies 被忽略;
  • 否则,peerDependencies 里每一个 @deepseek-ai/dsh、@deepseek-ai/dsh-* 都要满足, workspace:^ / ~ / * 视为「就是当前这个运行时」;
  • 预发布版本参与比较(includePrerelease),与宿主的闸门相同。

两种方向都会标出来:DSH 升级之后想降级插件,历史里那些不再接受当前运行时的版本会 被标成「不支持当前 DSH」,切换按钮禁用;DSH 还停在旧版本时想升级,如果 latest 不接受当前运行时,它就不再算「可升级」,行上会写明它要的是哪个范围。

运行时版本是从宿主自己的入口读的,不是从 profile 读的:profile 里的 @deepseek-ai/dsh-app-boot 是指向 npx 缓存的链接,实测是 0.1.2-rc.1,而宿主真正报的是 0.2.1-rc.5。读不到时什么都不标——猜一个版本会把本来能装的版本说成不能装。

判定用的是 src/host/version.ts 里自己实现的区间匹配,已经在 49 个 range × 41 个版本共 2035 组上跟 node-semver 逐一对过(全部一致)。不引入 semver 依赖,是因为这条链路上 多一个运行时依赖就多一次供应链审阅,而这里需要的语义只有 includePrerelease 那一档。

项目结构

dsh-plugin-upgrader/
├── AGENTS.md             **改代码前先读**:判据 + 守护测试,每条指向 docs/rules/ 的详证
├── docs/rules/           每条规则的详证页(事故现场、行号核对、原文存档;按需读,不自动加载)
├── src/                  源码(TypeScript)
│   ├── index.ts          宿主端入口:路由注册、job 调度、加载时恢复 manifest
│   ├── host/             宿主端逻辑(Node,无 DOM)
│   │   ├── config.ts     Config schema(volatile 的 registry / customRegistries)、读取用户选择
│   │   ├── overview.ts   组装 overview 载荷(含并发上限)
│   │   ├── registry.ts   registry packument 拉取 / 裁剪、GitHub tag 列表、镜像缺口
│   │   ├── install.ts    单步安装:in-place 替换、remove 回退、enabled 状态恢复
│   │   ├── manifest.ts   profile package.json 读写、journal、加载时恢复
│   │   ├── lock.ts       读 profile 写入锁、在锁内改 manifest、把锁超时翻成人话
│   │   ├── progress.ts   从 pnpm 原始输出推断 job 阶段
│   │   ├── bundle.ts     插件元数据(来源分类、多语言字段、图标)
│   │   ├── repo.ts       仓库 URL 归一化
│   │   ├── version.ts    semver 比较、发布页链接
│   │   ├── http.ts       回环校验、JSON 读写
│   │   └── manager.ts    宿主 pluginManager 服务的最小类型契约
│   └── client/           浏览器端(拆成模块,由 esbuild 打成一个文件)
│       ├── index.ts      createPlugin(require):槽位注册入口
│       ├── components/   Tab / PluginRow / VersionLine / JobPanel / SourceDialog
│       ├── registry.ts   读写安装源设置、可选源列表的增删改
│       ├── boot.ts       reload 前确认 boot 文档还有效(见 AGENTS.md 第 4 条「不能 reload 进一个已经不存在的 boot 文档」)
│       ├── copy.ts       zh / en 文案
│       ├── styles.ts     作用域 CSS
│       ├── hooks.ts      locale 订阅、overview 拉取、外观插件判定
│       ├── api.ts        fetch 封装、job 轮询
│       ├── format.ts     版本号 / 日期 / 外部链接
│       ├── env.ts        window 上的最小类型声明
│       └── types.ts      载荷类型
├── lib/                  构建产物(**已提交**,见 AGENTS.md 第 13 条「`lib/` 是提交进仓库的」)
│   ├── index.js          宿主端入口(tsc 输出)
│   ├── host/*.js         宿主端模块 + .d.ts
│   └── client.js         浏览器端单文件 bundle(含 __ModuleLoader__ 包装)
├── scripts/build.mjs     构建脚本:tsc 编译宿主端 + esbuild 打包 client(加 --watch 即 dev)
├── scripts/verify-pack.mjs            发布前核对 tarball 里都有什么
├── scripts/verify-build-committed.mjs 核对 `lib/` 与源码一致且已提交
├── test/                 node:test 单元测试(.ts,Node 24 直接剥离类型)
├── locale/               (保留)语言包
├── assets/icon.svg       图标
├── cordis.patch.yml      bundle 注册
└── package.json

浏览器端虽然拆成了多个 .ts 模块,但最终由 esbuild 打成一个 lib/client.js, 外面套上 window.__ModuleLoader__.load({ id, factory(require) { … } })。 运行时它只能 require('react') 这类由宿主提供的模块,不能 require 自己的其它文件, 所以「拆模块」只发生在源码层,产物仍然是单文件。

开发

本项目用 pnpm 管理依赖(package.json 里钉了 packageManager,CI 也用 pnpm):

pnpm install       # 只装依赖(不会构建,见下)
pnpm run dev       # 监听 src/,宿主端和浏览器端改完即编译(Ctrl+C 停止)
pnpm run build     # tsc 编译宿主端 + esbuild 打包 client → lib/
pnpm run typecheck # tsc 检查 src 和 test
pnpm test          # node:test 单元测试(.ts 直接运行)
pnpm run check     # 以上全部(typecheck + build + test + pack 检查 + 产物是否已提交)

lib/ 是提交进仓库的,不是构建垃圾:DSH Store 审核固定 commit,不会为了补齐 运行时文件去执行 prepare。所以 pnpm install 不再触发构建(安装期脚本会被审核 直接否决),构建挂在 prepack 上;改完 src/ 必须 pnpm run build 并把 lib/ 一起提交,pnpm run check 会替你核对。详见 AGENTS.md 第 13 条 「lib/ 是提交进仓库的,不是构建垃圾」。

改任何代码前先读 AGENTS.md。 那里列的是「已经实现、不能再退回」的行为, 每条都附了涉及的符号、守护它的测试名,以及一个 docs/rules/ 详证页;pnpm run check 全绿 只说明没写坏,不说明没改坏(很多行为没有对应测试,比如「自己那一行不出现在批量升级里」 之外的边界情形)。

新增或修改测试后,问一句:这个测试真的会红吗? 把被测的字符串或分支临时改成哨兵值 再跑一次,确认它失败,然后改回来。不会失败的测试等于没写。

宿主端在 src/index.ts(webServer 前缀路由 /api/plugin-upgrader), 浏览器端在 src/client/index.ts(注册 plugins.tab 槽位)。

用户设置有两个:registry(当前在用的源,空 = 跟随 pnpm)和 customRegistries (用户自己加的源),都在 src/host/config.ts 里声明成 .volatile()——这是宿主 设置服务愿意为它生成表单、并且写入能进到正在运行的插件里而不重启的唯一形状。 浏览器端通过 configForms 服务读写(src/client/registry.ts);没有那个服务的 宿主照样能渲染这一页,只是源只能看不能改。两个自带源是常量,永远不写进 customRegistries,所以没有任何点击能改它们或删它们。

pnpm run dev 会并行跑两个监听器:tsc --watch 负责 lib/index.js 和 lib/host/**,esbuild 负责 lib/client.js,两者输出互不覆盖。注意 esbuild 打包时会直接剥掉类型而不做检查,所以改浏览器端时顺手跑一次 pnpm run typecheck。

改完代码后必须重新构建(profile 里的 link: 指向本目录,但加载的是 lib/, 不是 src/),并把重建后的 lib/ 一起提交。pnpm run dev 已经替你重建了产物, 但插件本身不会热更新——要让宿主重新加载本插件,无需重启应用:先 pluginManager.setBundleEnabled(name, false) 再 setBundleEnabled(name, true)。

发布

见 RELEASING.md。首次手动发一次,之后打 v* tag 由 CI 通过 npm trusted publishing 自动发布:

pnpm publish --access public   # 仅首次

许可

MIT

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Experimental Session Titles Bundle@deepseek-ai/dsh-experimental-session-titles-bundle新的用户提示到达时刷新会话标题。Personalized Recommendations@deepseek-ai/dsh-personalized-recommendations回答下方以卡片形式提供个性化页面和内置插件推荐Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义