前言¶
在 DeepSeek Harness(DSH)桌面版里开发或维护插件时,常见流程是改代码、装新包或 git pull 更新 bundle,然后关掉应用再打开,才能确认改动生效。完全重启会清掉正在跑的轮次、子代理、终端会话、排队消息和未发送草稿——成本比「刷新一下界面」高得多。
Harness 宿主侧其实有 HMR:在进程仍存活的前提下驱逐已变更模块并重新加载。缺的是一条从 UI 触达这条路径的入口,以及在 HMR 无法完成时再走完全重启的明确分支。下面介绍社区插件 accoutmissing/dsh-restart-button 如何把这个流程收成侧边栏底部的一个按钮。
这是什么¶
dsh-restart-button 由维护者 accoutmissing 发布,分类为客户端插件(Cordis bundle,当前 package 版本 0.3.0,MIT 许可证)。它在 DSH 桌面版侧边栏底部提供重载 / 重启按钮:默认尝试原地热重载已变更的插件代码;仅当宿主报告热重载无法完成时,按钮才切换为完全重启,再点一次才会杀掉应用并重新拉起。
社区目录页:skillhub.cn/plugins/accoutmissing/dsh-restart-button。源码与说明:github.com/accoutmissing/dsh-restart-button。目录站点为社区生态索引,与 DeepSeek / 幻方无官方从属关系。
为什么优先热重载¶
README 中的设计取舍很直接:app.relaunch() 并不便宜。插件包替换后,Harness 的 HMR 层可以在运行中的宿主进程内驱逐变更模块并重新启动,会话与进行中的任务仍留在同一进程里。完全重启是兜底——例如 hmr.reconcileProfile() 返回 restartReason: 'module-comparison' 或 'package-moved' 时,模块无法就地 reconcile 或包在磁盘上移动了位置。
按钮默认走热重载;只有 Harness 明确需要重启时,才暴露「完全重启」路径。
点击 -> 原地热重载插件
|
+-- 热重载不成立 -> 按钮变成「完全重启」
再点一次 -> 应用重新拉起
核心功能¶
侧边栏按钮与状态¶
客户端挂在 sidebar.footer.action(order 90),状态机大致如下(文案以 README 表格为准):
| 按钮显示 | 含义 | 点击会发生什么 |
|---|---|---|
| ⟳ 刷新插件 | 空闲 | 原地热重载已变更的插件代码 |
| ⟳ 正在刷新… | 进行中 | — |
| ✓ 已刷新 | 完成 | 3 秒后回到空闲 |
| ⟳ 完全重启 | 热重载无法原地完成 | 杀掉应用并重新拉起 |
| ⚠ 刷新失败 | 出错(同时写日志) | 3 秒后回到空闲 |
安装 bundle 成功后按钮即出现;README 写明安装本身不需要重启。
宿主 HTTP 路由¶
宿主在 index.js 通过 webServer 注册三条路由:
| 路由 | 方法 | 用途 |
|---|---|---|
/plugins/dsh-restart-button/__state |
GET | 版本、代号、能力、pid 等自描述 |
/plugins/dsh-restart-button/__reload |
POST | hmr.runExclusive(() => hmr.reconcileProfile()) |
/plugins/dsh-restart-button/__restart |
POST | 完整 kill 后重新拉起 |
热重载在 hmr.runExclusive() 内执行,避免与自动重载并发时出现 transactions cannot be nested;该错误会识别并退避重试(3 次,400 ms × n),另有 20 秒超时上限。
安全与调试¶
两条 POST 路由要求请求头 x-dsh-restart-button,并校验 loopback 来源。对写路由发 GET 返回 405。
日志:~/.dsh/restart-button.log(约 1 MB 轮转)。Windows 下重启脚本与临时目录在 %TEMP%\dsh-restart\<timestamp>\;重启路由支持 ?dry=1(只生成脚本)与 ?probe=1(替身 helper 跑链路、不真杀进程)。
平台说明: 完全重启的启动链路在 README 中仅针对 Windows 实现并验证;热重载依赖有文档的宿主 HMR 服务,不限于 Windows,但作者注明尚未在其他平台实测完全重启。
安装与启用¶
这是一个 Cordis bundle。官方 README 给出的步骤是先检出源码,再对目录执行 install_bundle(Harness 插件管理器能力;由 agent 驱动时为 plugin_manager 工具)。
先从源码检出:
git clone https://github.com/accoutmissing/dsh-restart-button.git
然后用本地目录安装 bundle(将 target 换成你机器上的实际路径):
{ "action": "install_bundle", "target": "D:/path/to/dsh-restart-button" }
安装成功应返回 application: "applied"。以后更新:在仓库目录 git pull,再点侧边栏按钮触发热重载即可。
注意: 插件与当前 DSH 进程同权限运行。安装前请自行阅读仓库源码与 MIT 许可证,确认行为与安全约束(请求头、loopback)符合你的环境。
典型用法¶
- 安装 bundle 后,在侧边栏底部找到刷新按钮。
- 修改或更新插件代码后,在空闲态点击 刷新插件,等待热重载完成(成功时短暂显示 已刷新)。
- 若按钮变为 完全重启,说明宿主判定必须重启;再点一次执行完全重启(Windows 上经文档描述的 launch 链路)。
- 排查问题时查看
~/.dsh/restart-button.log;在 Windows 上还可查看%TEMP%\dsh-restart\下保留的最近 20 次launch.log/ 脚本产物。
实现要点(读者可选)¶
客户端状态放在模块级 store 而非组件 useState:sidebar.footer.action 在 store 更新时会重挂载组件,组件内 state 会在两次点击间被重置。判断新客户端是否加载时,宿主返回 clientStamp(client.js 的 mtime),页面记录 LOADED_AT;若 clientStamp 比加载时刻新超过约 1 秒,则整页重载——README 说明早期用 clientModules.graph().rev 会误判,已改为文件戳。
与 dsh-unlag 对照:本插件借鉴了 nested transaction 重试、超时与日志消毒、自定义请求头 CSRF、热重载优先等思路;未采用其 /plugins 路由 shim,而选择在需要时用整页重载。完全重启、Windows Job Object 逃逸、wscript 快捷方式启动链等为独立实现,README 有完整链路说明。
结尾¶
dsh-restart-button 把「装插件 / 更插件就要关应用」收成侧边栏一步:能热重载就不重启,不能时才完全拉起。适合长期在 DSH 桌面版里迭代 Cordis 插件、又不想频繁丢会话的开发者。
- 目录页:https://www.skillhub.cn/plugins/accoutmissing/dsh-restart-button
- GitHub:https://github.com/accoutmissing/dsh-restart-button