dsh-web-restart:给 DSH WebUI 加重启按钮,配 launchd 自动拉起

前言

用 DeepSeek Harness(DSH)跑 WebUI 的开发者会遇到一个很具体的麻烦:dsh web 是单个 Node 进程。改完配置、装了新 bundle,或者进程干脆崩了,UI 就断线——你得回到终端,找到那个进程,手动再敲一遍启动命令,然后回浏览器刷新。重启这个动作本身不难,但每次都要离开浏览器去做,很打断。

反过来拆一下需求就是两件事:在 WebUI 里能直接触发重启;进程退出之后,有东西负责把它拉起来,让页面自己恢复。dsh-web-restart 就是把这两件事配成一套的插件。DSH 的理念是「一切皆插件」,这类基建能力正好适合以插件形式补上。

这是什么

dsh-web-restart 由 QIN-SMART 维护,MIT 许可证,当前版本 0.1.0,运行时依赖 @deepseek-ai/schemastery ^3.18.1。它做两件事:

1、在 DSH WebUI 侧边栏底部加一个「⇄ 重启 DSH」按钮,点击后进程退出,页面进入遮罩状态并轮询,服务恢复后自动刷新;
2、配套 macOS launchd LaunchAgent(launcher/macos/),用 KeepAlivedsh web 退出或崩溃后自动拉起,支持登录自启。

有一点要先说清楚:插件本身无法在死后自举——进程都没了,按钮自然也渲染不出来。所以重启按钮与外部守护是配套的两半,缺一不可。

核心功能

重启按钮(client 端)

  • 注入侧边栏底部槽 sidebar.footer.action,渲染「⇄ 重启 DSH」按钮;
  • 两段式确认防误触。实现上不依赖 window.confirm,因为在嵌入/沙箱 WebView 里它会被静默禁用;
  • 触发后全屏遮罩挂在 documentElement 上(z-index 最大),提示「正在重启 DSH…」,每 2 秒轮询服务是否恢复,恢复后自动刷新页面。

host 端:端点、命令与退出模式

  • 通过 webServer 服务注册同源 HTTP 端点 POST /api/_dsh/restart。浏览器与 server 同源,fetch 直接可用,不依赖活跃会话;
  • 端点命中后返回 202,稍作延迟让响应落盘,再执行退出;
  • 退出模式两种:
  • mode: "exit"(默认):process.exit(),交给 launchd KeepAlive 拉起;
  • mode: "exec":同进程内 spawn 一个新的 dsh webunref,旧进程退出,适合没有外部守护、从终端手动跑的场景;
  • 可选 token 校验,支持 Authorization / X-Dsh-Restart-Token 两种头;
  • 同时注册 /restart slash 命令(enableCommand)。触发机制是双通道:首选 DSH 官方 RPC(remote.commands.execute("/restart")),回退同源 fetch

launchd 守护与启动台图标

  • KeepAlive 自动拉起崩溃/退出的进程,支持登录自启;
  • 安装脚本会把 dsh-web-start.sh 复制到 ~/Library/Application Support/dsh-web/ 再交给 launchd——因为 macOS TCC 隐私保护下 launchd 无法读取 ~/Documents 等目录里的脚本;
  • 启动脚本自动探测 nodedsh 的路径(nvm / PATH),也可用 NODE_BIN / DSH_ENTRY / DSH_HOME 环境变量覆盖;
  • 附带 AppleScript 源码,可编译成启动台 .app 图标,点击启动 + 打开浏览器;
  • launchd 日志位于 ~/.dsh/dsh-web.launchd.log~/.dsh/dsh-web.launchd.err.log

安装与启用

插件安装到你的 DSH profile 目录,默认是 ~/.dsh/profiles/web/

方式 A,从 GitHub 安装(README 推荐)。注意 <你的用户名>/<仓库名> 是 README 原文的占位符,请替换为你实际托管的仓库:

cd ~/.dsh/profiles/web
pnpm add "dsh-web-restart@github:<你的用户名>/<仓库名>"

然后在 ~/.dsh/profiles/web/package.jsondsh.profile.bundles 数组末尾加上 "dsh-web-restart"

"dsh": {
  "profile": {
    "bundles": [
      "@deepseek-ai/dsh-base",
      "@deepseek-ai/dsh-web-app",
      "dsh-web-restart"        // ← 加这一行
    ]
  }
}

重启 dsh web 后生效。

方式 B,本地 link(开发用):

cd ~/.dsh/profiles/web
pnpm add "link:/绝对/路径/dsh-web-restart"

同样在 bundles 里加上 "dsh-web-restart",重启生效。

默认配置的四项(mode / token / flushMs / enableCommand)可以在 profile 的 cordis.patch.yml 里覆盖,例如设置 token:

- id: web-restart
  name: dsh-web-restart
  token: my-secret

macOS:装守护和启动台图标

先安装 launchd 守护:

chmod +x launcher/macos/*.sh
launcher/macos/dsh-web-control.sh install

常用子命令:

命令 作用
dsh-web-control.sh status 查看 launchd 状态
dsh-web-control.sh restart 手动重启
dsh-web-control.sh stop / start 停 / 启(停止不解除自启)
dsh-web-control.sh uninstall 卸载

再编译启动台图标,用 macOS 自带的 osacompile

mkdir -p build
osacompile -o build/DSH.app launcher/macos/DSH-Web-Launcher.applescript
cp -R build/DSH.app /Applications/

README 还给了一个 Automator 的替代做法:新建「应用程序」→ 运行 shell 脚本:

~/Library/Application Support/dsh-web/dsh-web-start.sh
sleep 3
open http://127.0.0.1:3080

另存为 DSH.app,拖到 Dock / 启动台即可。

开发自测

改完代码先过两步:

node --check index.mjs lib/client.js   # 语法检查
node validate.mjs                      # host 逻辑自测(桩 ctx,不启动服务)

适用场景与注意

适合的人:在 macOS 上把 DSH WebUI 当常驻服务用,希望从浏览器里直接重启、崩溃或退出后能自动恢复的用户。如果当前没有外部守护、从终端手动跑,把 mode 改成 "exec" 即可;仓库自带的守护脚本只覆盖 launchd。

几点注意:

1、重启按钮与外部守护是配套的两半。只装插件不装 launchd 守护时,默认的 mode "exit" 按下去进程就没了,回不来;
2、macOS TCC 限制:launchd 读不了 ~/Documents 等目录里的脚本,安装脚本已经处理(复制到 ~/Library/Application Support/dsh-web/),不要自己把启动脚本挪回文档目录;
3、如果重启端点不只本机访问,建议在 cordis.patch.yml 里配置 token 校验。

安全方面要说明:这个插件以当前 dsh 进程的权限运行,并且具备触发进程退出的能力。安装前建议先检查源码与许可证(MIT),确认行为符合预期再装进 profile。

小结

dsh-web-restart 解决的是一个很小但高频的问题:让「重启 DSH」从回到终端敲命令,变成浏览器里点一下按钮,再由 launchd 保证进程一定能回来。按钮负责触发和恢复 UI,launchd 负责拉起进程,两半配齐才算完整。

  • 社区目录页:https://www.skillhub.cn/plugins/QIN-SMART/dsh-web-restart (社区维护的独立站点,与 DeepSeek / 幻方无官方从属关系)
  • GitHub:https://github.com/QIN-SMART/dsh-web-restart
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye