跳到正文
dsh-market 浏览插件 GitHub EN

Neptune810/dsh-power-button

在 DeepSeek Harness Web 界面加一个可拖动的电源胶囊:实时状态点,加上重启与结束两个动作;重启交给脱离终端的辅助进程,等端口释放后按原启动命令重新拉起。

Star 数 ★ 0 分类 UI 增强 收录于 2026-09-25

安装

在 DeepSeek Harness 里通过 dsh-market 安装

dsh plugin --profile web add dshmarket

或使用命令行

dsh plugin --profile web add github:Neptune810/dsh-power-button

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。

README

DeepSeek Harness 的 Web 插件:界面里一个可拖动的电源胶囊,带实时状态点,以及针对正在运行的 dsh web 服务的重启与结束两个动作。

纯宿主插件、零依赖、不需要构建。

为什么需要它

dsh web 不会自己退出。外面套了看门狗(启动器 / 脚本 / 托盘程序)的部署,通常要等一个空闲窗口 才动手关服务,于是最后一个浏览器标签关掉之后端口还会被占一段时间;这段时间里重启,打开的是一个 马上就要失效的页面。

这个插件把两个动作都放回你手上:界面里一个胶囊,外加一个告诉你宿主还在不在的状态点。

使用

一个半透明的小胶囊停在配置的角落(默认右下角)。

  • 状态点 —— 上一次探活有回应就是绿色,没回应就是红色。探活是每 5 秒一次的同源 GET,不消耗任何模型 token。
  • 按一下胶囊 —— 弹出一个小菜单,两个动作:
    • 重启服务 —— 宿主原地重新拉起自己,端口恢复应答后自动打开新页面。
    • 结束服务 —— 进程立刻退出,端口马上释放。
    • 再按一下胶囊、或点页面别处,菜单就关掉。
    • 设 restart: false 则没有菜单,按一下直接结束。
  • 按住拖动 —— 鼠标、触摸都行。位置存在 localStorage 里,刷新页面后还在。
  • 拖动不会误触发动作:指针移动超过 4px 才算拖动,浏览器随后补发的那次 click 会被吞掉。
  • 拖出窗口会被夹回边缘,窗口变小后也会自动重新夹紧,永远点得到。
  • 它挂在应用 React 根节点之外,任何重渲染都弄不丢。

实现方式

宿主插件。不构建任何前端 bundle,也不 import 任何 @deepseek-ai/* 包 —— 用 link: 安装的插件会被 Node 解析到真实路径,而 harness 的包并不在那里。它只用 Web 载体已有的两个扩展点:

扩展点 用途
webServer.tapIndex() 把胶囊的 markup、CSS、脚本注入 index.html
webServer.register() 提供 /dsh-power-button/status、/restart、/stop

结束走启动器提供的 ctx.appExit:和 Ctrl+C 同一条有界关闭路径,先优雅 dispose 整棵树, 再让进程退出。这条路径只有在事件循环真的排空时才结束进程,所以插件另外挂了一个 unref 的 2 秒看门狗 —— 干净退出时它不拖后腿,有别的插件握着句柄时它也保证进程一定消失。

重启没法由宿主自己做,因为要离开的正是宿主本身。重启路由会拉起一个脱离终端的辅助进程 (同一个 Node 二进制跑一段内联模块),它依次:

  1. 等这个进程消失,
  2. 等端口不再应答,
  3. 用同样的环境变量和工作目录,按记录下来的启动命令重新拉起,
  4. 需要的话,等端口恢复应答后打开页面。

辅助进程比宿主活得久(detached + stdio: "ignore" + unref()),所以它能活过触发它的 那次关闭。

Windows 11 / Node 24 实测:结束在 POST 返回 200 后约 0.2–0.5 秒释放端口;重启几秒内端口就回来。 失败也是安全的 —— 辅助进程起不来时路由回 500,服务保持运行。

安全

三个路由都必须同时满足:

  1. 带自定义请求头 x-dsh-power-button: stop —— 跨域页面要带这个头就得先过 CORS 预检, 而本服务从不批准预检,所以随便一个网页既重启不了也关不掉你的服务;
  2. Host 是回环地址(127.0.0.1 / localhost / [::1]),堵掉 DNS rebinding;
  3. 请求若带 Origin,必须与该 Host 同源。

其余一律 403。status 只接受 GET/HEAD;stop 与 restart 只接受 POST, 方法不符一律 405。

插件不碰 profile 之外的任何东西:重启辅助进程只按本进程当初的启动命令,或你显式配置的 restartCommand 执行。

配置

全部可省略,写在插件行上:

`@yaml

  • insert:
    • id: power-button name: dsh-power-button config: label: 结束服务 # 胶囊文字与 tooltip position: bottom-right # 初始角落,或 bottom-left draggable: true # false 则钉死在初始角落 status: true # false 则去掉状态点、停止探活 statusPollMs: 5000 # 探活间隔,最小 1000 restart: true # false 则去掉重启动作和菜单 restartCommand: [] # argv;留空 = 按本次启动命令原样重来 restartOpenBrowser: true # 命令自己会开页面时设为 false restartWaitMs: 30000 # 辅助进程最多等端口多久 enabled: true # false 则完全不注入

`@

通过启动器重启

默认情况下辅助进程重来的是本进程自己那条命令 —— 同一个 Node、同一个脚本、同一组参数。 这是插件唯一能确定的事情,代价是重新拉起的宿主背后没有看门狗:它会一直活着,直到你按结束。

如果你的部署是由某个启动器拉起来的,而你希望它一起回来(比如它自带空闲看门狗),把 restartCommand 指向它,并让它自己负责开浏览器:

`@yaml

  • insert:
    • id: power-button name: dsh-power-button config: restartCommand: - 'C:\path\to\YourLauncher.exe' restartOpenBrowser: false

`@

安装

@sh dsh plugin --profile web add dsh-power-button @

装完必须重启 dsh web:插件改动不会影响已经跑起来的进程。

测试

@sh node --test test/plugin.test.js @

测试零依赖。注入到页面的脚本会被真的执行,跑在一套手搭的 DOM 上 —— 覆盖按下与拖动的区分、 移动阈值、边缘夹紧、位置持久化、菜单、重启动作、状态探活;三个路由则用假的 node:http 请求/响应来驱动,包含那条会静默丢掉 res.end 回调的压缩路径。脱离终端的辅助进程源码也会 按 ESM 做语法检查,坏掉的重启不可能发出去。

许可

MIT

内容来自项目 README(GitHub)↗

评论

评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。