Skip to content

dsh-relaunch

Verified

dsh-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。手动重启要关窗口、重开、再找回会话,打断节奏。这个插件让模型自己完成这件事,而且重启完把话接上。

怎么工作

插件做三件事,重启本身有两条腿,谁先到算谁的:

  1. 工具 restart_host(无参数)
    • 先判重,再动手:已经有一只助手在等(锁文件里那个进程还活着),或者本进程已经安排过退出, 就直接拒绝——请求不重写、不派助手、不重挂退出,只返回 already-pending 并告诉模型“已经有一个了”。 派第二个进程出来再让它自己让路,仍然算派了一个,这种事根本不该发生;
    • 判重通过后,写一份重启请求到 E:\dsh\restart-request.json:会话编号、要发的话、时间、进程编号;
    • 先派一个独立的重启助手(relaunch-helper.mjs,detached 进程),再由它负责拉起后端;
    • 等当前这一轮变空闲(最多两分钟),再等两秒半让会话记录落盘,然后退出进程。
  2. 重启助手
    • 单实例:入口也抢 E:\dsh\relaunch-helper.lock 兜一层。正常路径下工具已经挡住了重复派, 这层是为了防止工具层判断漏掉(比如跨进程)——抢不到锁就直接让路,什么都不做;
    • 等旧进程真的消失、端口真的空出来;
    • 若这时端口已被别人监听,说明启动脚本抢先干了,它安静退出,不抢;
    • 否则自己用 %APPDATA%\npm\dsh.cmd web --port <原端口> --no-open 拉起后端,输出写进 E:\dsh\logs\;
    • 拉起之后这个进程就结束了,不会留在后台——它只负责把后端救回来这一件事。
  3. 启动时
    • 读到遗留的请求、且请求里那个旧进程确实已经退出,就往请求里的会话投一条消息;
    • 投递成功后删掉请求文件;投递失败就留着,下次启动再试。
    • 投递必须带中止信号: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   # 启动时投递并删除请求