Skip to content
dsh.fish
Bundle

dsh-restart-btn

重启 Web 服务按钮:在设置页 General 区添加一行重启按钮,宿主端复用桌面启动器的机制(node + lib/bin.js、日志重定向、TCP 就绪轮询)重启 dsh web;不加 --no-open,交给 dsh 自己打印并打开已授权地址。

Source
SiriusWJ
License
MIT
Updated
Updated 5 hours ago

Readme

# dsh-restart-btn

在 DSH 设置页 **General** 区加一行「重启 Web 服务」按钮:点一下就把 `dsh web` 重启一遍,
用于让插件 / 主题 / 配置改动生效,不用再去双击桌面快捷方式。

页面会在服务重启期间短暂断开,服务回来后**自动重新载入**——不需要手动刷新。

## 为什么会有这个插件

在 Windows 上重启 dsh 有个隐蔽的坑:调度任务(`schtasks /TR`)的命令行是按**ANSI 代码页**
写入的,只要命令里出现非 ASCII 字符(例如中文工作区 `...\OneDrive\文档\txt2`),路径就会被
静默损坏——任务照样报 `Last Result: 0`(成功),但脚本**一行都没执行**。

早期版本把重启脚本写进「客户端传来的工作区路径」,在这些机器上表现为:
**点重启完全没反应,也没有任何报错。**

本版本的处理方式:

1. 重启助手脚本固定放在**纯 ASCII、无空格**的暂存目录(`%LOCALAPPDATA%\dsh-restart-btn`,
   不可用时依次退到 `%ProgramData%` / `%SystemRoot%\Temp` / `%TEMP%` / `~/.dsh`),
   调度任务的命令行里因此永远只有 ASCII。
2. 所有用户相关路径(node、bin.js、工作目录、日志、启动器)走 **UTF-8 JSON 配置文件**,
   完全不进命令行。
3. 助手脚本本身是纯 ASCII,并用显式 UTF-8 解码器读 JSON——绕开 PowerShell 5.1
   默认按 ANSI 读文件的第二个坑。

## 它怎么重启

复刻桌面快捷方式(`start-dsh.ps1`)验证过的机制,而不是另发明一套:

| 步骤 | 做法 |
| --- | --- |
| 1 | 延迟若干秒,让 HTTP 响应先回到浏览器 |
| 2 | 找到监听该端口的进程,`taskkill /T /F` 连子进程一起结束 |
| 3 | 轮询等端口真正释放 |
| 4 | `Start-Process <node> <lib/bin.js> web --port <port>`,**stdout/stderr 重定向到日志文件**(与快捷方式一致,崩溃才查得到) |
| 5 | TCP 轮询就绪(最多 45s)——不用 HTTP 探测,避免把「401 未授权」误判成失败 |
| 6 | 直连启动失败时,回退执行桌面启动器 `%LOCALAPPDATA%\DeepSeekHarness\start-dsh.ps1`(它自带自愈与浏览器拉起) |

服务端身份(`instance`)在每次进程启动时不同,前端只有在 ping 到**另一个** instance 时才刷新页面,
因此不会把「还没死掉的旧服务」误认成「新服务已就绪」。

## 重启后为什么会话有时要重新授权

dsh 的 Web 认证是**绑定 authority 的签名 cookie**(`dsh-auth-<sha256(host:port)>`)。
它没有「关闭认证」的开关:`requestRejection()` 先做 Host/Origin 围栏(403),
再要求 cookie 通过(401);`trustedHosts` 只放宽围栏,不能绕过 cookie。

签名密钥存在凭据库里、重启不变,所以**同一个 cookie 重启后依然有效**(实测:重启前后都是 200)。
但 cookie 有寿命,而一个长期开着的标签页可以活得比它的 cookie 更久——页面还能用只是因为连接早就建好了,
这时一次普通刷新就会撞上 `dsh web authentication required`。

**token 的唯一作用是把 cookie 种进浏览器。** 桌面快捷方式之所以「总是能用」,正是因为
`dsh web` 启动时会打印 `http://127.0.0.1:<port>/?token=…` 并打开它,于是**每次启动都重新签发 cookie**。

所以本插件不自己转发任何 token:重启时不加 `--no-open`,让 dsh 照常打印并打开那个已授权地址
(行为与桌面快捷方式完全一致)。新页面完成 token 兑换后,**同一个 authority 的 cookie 被刷新**,
旧标签页的探测(`GET /` 是否 200)随之通过并自动重载——因此前端**不会盲目刷新**,
探测不通过就提示用桌面快捷方式,绝不会把页面刷成一片 401。

## 安装

```bash
# 本地目录
dsh plugin --profile web add D:/Code/dsh/restart-btn-plugin

# 或从 GitHub
dsh plugin --profile web add github:SiriusWJ/dsh-restart-btn
```

改完 bundle 需要重启一次 dsh 生效(第一次请手动重启)。

## 路由

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/dsh-restart-btn/restart` | 同源校验;body `{"port":"3080"}`;返回 `instance` / `log` / `trigger` |
| `GET` | `/dsh-restart-btn/ping` | 返回 `{ok, instance, pid}`,供页面判断新进程是否就绪 |

## 日志与排错

| 文件 | 内容 |
| --- | --- |
| `%LOCALAPPDATA%\dsh-restart-btn\restart.log` | 重启全过程(停进程 / 端口释放 / 启动 / 就绪 / 失败原因 + stderr 尾部) |
| `%LOCALAPPDATA%\dsh-restart-btn\dsh.out.log` | 新服务的 stdout(含它打印的 token 地址) |
| `%LOCALAPPDATA%\dsh-restart-btn\dsh.err.log` | 新服务的 stderr |

若暂存目录不是 `%LOCALAPPDATA%`(用户名含非 ASCII 时会自动退到 `%ProgramData%` 等),
以 `POST /dsh-restart-btn/restart` 返回的 `log` 字段为准。

## 已知取舍

- 重启时**不加 `--no-open`**,所以每次重启浏览器都会多开一个已授权的标签页(与桌面快捷方式一致)。
  旧标签页靠 cookie 刷新后自动重载。想改成不弹新标签页,需要让插件把新 launch url 转交给页面,
  代价是插件要对外提供一次完整会话凭据。
- 端口取自浏览器地址栏,因此非 3080 端口也能正确重启;只有 3080 才回退到
  `start-dsh.ps1`(那个脚本自身写死了 3080)。
- 仅 Windows 走 Task Scheduler + PowerShell 路径;其他平台需自行改 `buildHelperScript()`。

## License

MIT

Install

dsh plugin --profile web add github:SiriusWJ/dsh-restart-btn

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source