dsh-relaunch
Verifieddsh-relaunch · v0.1.0 · MIT
重启宿主:给模型一个 restart_host 工具,调用后退出进程,由启动脚本按原样拉起,重启完成后自动往原会话投一条「继续」消息。
Install
dsh plugin add dsh-relaunch Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
dsh-relaunch(重启DSH)
给模型一个 restart_host 工具:调用后让当前进程退出,由启动脚本按原样重新拉起;新进程起来后自动往原会话投一条「继续」的消息。
为什么要有它
改插件、改挂载配置之后要重启DSH。手动重启要关窗口、重开、再找回会话,打断节奏。这个插件让模型自己完成这件事,而且重启完把话接上。
怎么工作
插件做三件事,重启本身有两条腿,谁先到算谁的:
- 工具
restart_host(无参数)- 先判重,再动手:已经有一只助手在等(锁文件里那个进程还活着),或者本进程已经安排过退出,
就直接拒绝——请求不重写、不派助手、不重挂退出,只返回
already-pending并告诉模型“已经有一个了”。 派第二个进程出来再让它自己让路,仍然算派了一个,这种事根本不该发生; - 判重通过后,写一份重启请求到
E:\dsh\restart-request.json:会话编号、要发的话、时间、进程编号; - 先派一个独立的重启助手(
relaunch-helper.mjs,detached 进程),再由它负责拉起后端; - 等当前这一轮变空闲(最多两分钟),再等两秒半让会话记录落盘,然后退出进程。
- 先判重,再动手:已经有一只助手在等(锁文件里那个进程还活着),或者本进程已经安排过退出,
就直接拒绝——请求不重写、不派助手、不重挂退出,只返回
- 重启助手
- 单实例:入口也抢
E:\dsh\relaunch-helper.lock兜一层。正常路径下工具已经挡住了重复派, 这层是为了防止工具层判断漏掉(比如跨进程)——抢不到锁就直接让路,什么都不做; - 等旧进程真的消失、端口真的空出来;
- 若这时端口已被别人监听,说明启动脚本抢先干了,它安静退出,不抢;
- 否则自己用
%APPDATA%\npm\dsh.cmd web --port <原端口> --no-open拉起后端,输出写进E:\dsh\logs\; - 拉起之后这个进程就结束了,不会留在后台——它只负责把后端救回来这一件事。
- 单实例:入口也抢
- 启动时
- 读到遗留的请求、且请求里那个旧进程确实已经退出,就往请求里的会话投一条消息;
- 投递成功后删掉请求文件;投递失败就留着,下次启动再试。
- 投递必须带中止信号:DSH签名是
prompt(request, signal),第二个参数必填, 漏传会让DSH内部调signal.throwIfAborted()时抛错,消息静默失败(2026-10-24 踩过)。 插件里显式传AbortController的 signal,测试也钉住这一点。
启动脚本那一侧(dsh-app-launcher.ps1)守着一个循环:后端退出后,如果看到请求文件,就跳过开窗、等端口释放、重新跑 dsh web,最多重试三次。
为什么加助手:脚本有个分支——首次启动时若端口已被别人占着,它只开窗口然后 exit 0,不进守护循环。一旦落进这个分支,后端退出后再无接管者,会话断死(2026-10-24 实际踩到过)。助手补的就是这一环,它不依赖脚本还在不在。
为什么重启不必关前端
登录状态用的是签名 Cookie,签名密钥保存在凭据文件里,重启不变;每次启动会变的只有一次性的启动令牌,而旧页面已经有 Cookie,不需要令牌。前端断线后按指数退避自动重连(第一次 0.5 秒,最长 10 秒)。所以旧页面自己接上新后端,启动脚本在重启轮不打开新窗口,屏幕上始终只有一个前端。
配置
- 请求文件路径可以用环境变量
DSH_RESTART_REQUEST覆盖(插件和启动脚本都认),方便在另一个端口上做隔离测试。 - 重启轮不打开新窗口;如果旧窗口已经被关掉,需要手动再开一次。
测试
node test/tool.mjs # 工具写出请求文件
node test/deliver.mjs # 启动时投递并删除请求