前言¶
DSH 的设计是「一切皆插件」,Host 升级、配置改动、服务异常之后,都需要重启 Host 让改动生效。常规做法是打开终端手动结束进程再拉起来,Web 界面上没有任何入口,跑着的回合和终端也会被中断,因此很难在浏览器里完成一次靠谱的重启。
下面介绍的 dsh-restart 就是为了把这个动作放进 Web GUI:在 Settings → General 里加一行,在侧边栏账号菜单里加一项,同时也暴露一个 agent 工具 dsh_restart。插件由 dengnianchen 维护,托管在 GitHub,属于客户端分类,主站目录页有收录。
这是什么¶
一句话:给 DeepSeek Harness 的 Web 界面加一个 Host 重启入口,并且在没有 systemd 时也能工作。
它解决的问题是:
- Web GUI 上无法触发 Host 重启,改完配置只能靠终端。
- 在进程内直接调用
systemctl restart会连自己一起杀掉(下面的实现部分解释原因)。 - 重启后的页面刷新需要可靠判断,不能把一次短暂断连当成成功。
插件是普通的 bundle plugin,包含 Host 半部分和 Web 半部分(一个 ESM 模块加一段客户端代码),不改动侧边栏 footer 图标、Host 文件和 profile 里手写的配置。
核心功能¶
三个入口,做的是同一件事:
| 入口 | 位置 | 实现方式 |
|---|---|---|
| 设置行 | Settings → General,排在 “Performance & usage” 之后 | 官方 settings.general.item 插槽,order: 35,行外壳沿用 Host 自己的 General 行样式 |
| 账号菜单项 | Settings / Feedback / Restart dsh / Sign out 之间 | DOM 桥接,原因见下面的限制说明 |
| Agent 工具 | dsh_restart({ delayMs? }) |
与上面两个 UI 入口走同一套重启逻辑 |
重启动作的交互流程:
- 第一次点击进入警告态,提示
Click again to confirm,4.5 秒后自动失效。 - 第二次点击排定重启,界面显示
Restarting…。 - 页面每 0.7 秒轮询
/dsh-restart/status,只有当 Host 返回的 pid 与之前不同时才重新加载页面——这样重启过程中的短暂断连不会被误判为成功。 - 如果 120 秒内没有出现新进程,控件转为错误态并提供重试。
重启过程中,正在运行的回合和终端会被中断;会话、transcript 和设置都会保留。
安装与启用¶
安装命令来自插件 README:
plugin_manager install_bundle @dyapple/dsh-restart
插件需要 DeepSeek Harness ≥ 0.2.1-rc.3,因为它使用了 ctx.connection.requestRejection 和 settings.general.item 插槽。
重启是怎么发生的¶
Host 侧按运行环境分两条路径,两者都会先延时,保证 HTTP 响应在 Host 退出之前回到页面。
- systemd 环境。插件通过
INVOCATION_ID和/proc/self/cgroup里的普通.service判断自己是否跑在 systemd 下,并确认systemd-run --user可用;此时会用一个 detached transient unit 延时执行systemctl --user restart <unit>。
这里不能直接从进程内调用 systemctl restart:dsh-web 的 KillMode=control-group,重启会先把执行调用的辅助进程一起杀掉。transient unit 跑在另一个 cgroup 里,可以绕开这个问题。
- 非 systemd 环境(例如手工启动的进程)。用一个 detached 的
/bin/sh等待旧 pid 消失、端口稳定,然后exec同一条命令行,同时旧进程退出。
HTTP 接口¶
插件注册了两个接口:
| 路径 | 方法 | 用途 |
|---|---|---|
/dsh-restart/status |
GET | 返回 {ok, version, pid, mode, unit, command, startedAt, uptimeMs, restarting} |
/dsh-restart/restart |
POST | Body 为 {delayMs?},默认 700 ms |
两个接口都先经过 ctx.connection.requestRejection()(Connection 的 Host/Origin 限制加上签名 Cookie);POST 还额外要求 content-type: application/json,跨站的简单请求无法携带这个头。没有 Cookie 时两个接口都返回 401。
账号菜单项的限制¶
账号菜单项是 DOM 桥接实现,不是官方插槽。原因是:@deepseek-ai/dsh-client-ui-settings-account 里的菜单项是硬编码数组,没有为额外行预留插槽;它唯一的位置 settings.launcher 是单值的,占用它等于替换整个账号菜单。
因此插件的做法是:监听 [role="menu"] 节点,用同时包含 Settings 和 Feedback 标签来定位账号菜单,克隆随包发布的 Settings 行(继承其 class、几何与 hover 样式),替换图标和文案,再插到 Sign out / Sign in 之前。
具体限制:
- 它在 React 的菜单项列表之外,Tab 键可以到达,方向键不行。
- 它依赖随包发布的菜单文案和结构。如果后续版本改了文案,它会静默消失——不报错,也不影响其他 UI。设置行不受影响,因为它用的是官方插槽。
- 每次菜单打开时节点会被重建,用
WeakSet防止重复插入。
适用场景与注意¶
- 适合:已经用 systemd user service 托管 DSH Host、并且主要通过 Web GUI 操作的人;想在 agent 里通过工具触发重启的开发者。
- 不适合:需要重启立刻完成的场景。即使使用默认延时,从点击到 Host 恢复也会有数秒停顿,正在运行的回合和终端会被中断。
使用时需要注意:
- 插件以当前 dsh 进程的权限运行,重启动作因此继承了启动该进程的用户权限。安装前应先检查源码(仓库公开在 GitHub)和许可证。本插件使用 MIT 许可证,版权人为 Nianchen Deng。
- 账号菜单项依赖客户端 UI 的文案结构,升级客户端后如果入口消失,可回退到 Settings → General 里的那一行,或直接使用
dsh_restart工具。
开发调试¶
从源码开发时按 README 的步骤:
git clone https://github.com/dengnianchen/dsh-restart.git
plugin_manager install_bundle ./dsh-restart
profile 会链接这份 checkout,因此 client.js 的改动通过 HMR 生效;index.js 的改动需要重启 Host——恰好可以用这个插件自己来做。Host 半部分是一个单文件 ESM 模块,每个进程只引入一次。
小结¶
dsh-restart 把 Host 重启从终端搬进 Web GUI,用官方插槽保证设置行的可靠性,用 pid 变化判断重启是否真正完成,并在 systemd 和非 systemd 两种环境下都给出可工作的实现。代价是账号菜单项依赖客户端 UI 的当前结构,升级客户端后可能需要留意。
相关信息页:
- 社区目录页:https://www.skillhub.cn/plugins/dengnianchen/dsh-restart
- 代码仓库:https://github.com/dengnianchen/dsh-restart