dsh-restart-wake
让 Agent 安全地重启 DeepSeek Harness,并在重启后自动回到原会话继续干活。
给 DeepSeek Harness 用的 Host 插件:六个 Agent 工具 + 一个启动唤醒器 + 一套参数化的重启脚本。装上之后,模型可以自己安排重启、自己回来验收,不需要人盯着;同时顺手治掉「重启约 70 次后启动崩溃」的 cookie 堆积问题。
- 插件名:
dsh-restart-wake - 平台:重启与 cookie 清理仅 Windows;唤醒、登记、状态查询跨平台
- 依赖:无(不引入任何 npm 包)
- DSH 版本:
0.2.1-rc.5实测
它解决什么问题
改 src/、package.json、cordis.patch.yml、插件包之后,Host 必须重启才生效。但「让 Harness 重启自己」有三道物理障碍,每一步都踩过坑:
| 障碍 | 现象 | 原因 |
|---|---|---|
| 在 Harness 内启动重启脚本 | 脚本日志停在 killing,永远不回来 |
Harness 把子进程放进 Windows Job Object,杀 Harness 时脚本一起被砍 |
| 杀完立刻启动 | 新实例刚起来就退出 | Electron 单实例锁还没释放,新实例把参数交给正在退出的旧实例后自杀 |
| 重启后没人通知 | 会话停在原地,等人来敲 | 脚本在进程外跑,没有能力在「刚启动」那一刻通知会话 |
本插件的做法:
- 计划任务在 Harness 进程树之外执行重启(绕过 Job Object);
- 先杀浏览器主进程、再杀子进程,并等待单实例锁释放(没有崩溃弹窗和报错音);
- 重启前把「醒来要做什么」写成登记文件,启动唤醒器在新进程就绪后通过
sessionController.prompt投递——该接口会自动 resume 冷会话。
安装
dsh plugin --profile desktop add github:zgq0106/dsh-restart-wake
装完重启一次 Harness(Host 插件模块按包名缓存,启用/禁用不会重读)。
如果 pnpm 不认 github: 前缀,用完整地址:
dsh plugin --profile desktop add "git+https://github.com/zgq0106/dsh-restart-wake.git"
安装后自检
在新会话里让 Agent 调用 restart_status。它会报告:
- 探测到的 Harness 可执行文件路径、是否存在;
- 重启脚本 / 计划任务入口是否就位;
- 唤醒登记目录、重启锁状态、运行中的会话、日志末尾。
exePath 显示 ⚠ 不存在 时,在插件配置里显式指定(见下方「配置」)。
六个工具
| 工具 | 作用 |
|---|---|
restart_now |
登记唤醒 → 检查互斥锁 → 检查阻塞会话 → 挂计划任务 → 在 delaySeconds 秒后重启 |
wake_register |
只登记唤醒,不重启。本会话收尾后等别的会话触发重启时把自己叫醒 |
wake_list |
查看待唤醒登记、最近归档、重启锁状态 |
wake_cancel |
取消本会话的唤醒登记 |
restart_status |
自检与验收:路径、脚本、锁、运行中会话、日志末尾 |
cookie_status |
查看 dsh-auth-* cookie 堆积情况与清理阈值(431 崩溃的根因) |
调用方不需要手填会话 id:工具从执行上下文 exec.agent.id 自动读取。
典型用法(Agent 侧)
模型:改完插件配置,需要重启生效。
1. restart_status → 确认 exeExists / scriptExists 都为 true,没有阻塞会话
2. restart_now({ prompt: "重启完成,读取日志确认 RESULT: OK,然后继续完成 P2.4 连线可视化" })
3. 回复主人:「已安排重启,约 12 秒后回来」,本回合结束
4. 重启后插件自动投递 prompt,会话继续
5. restart_status → 验收:锁已释放、登记已归档、日志末尾 RESULT: OK
重启过程
Agent 调用 restart_now
├─ 写 wake-pending-<sessionId>.json (重启后要投递的话)
├─ 写 restart-config.json (脚本参数)
├─ 写 restart.lock (互斥锁)
└─ schtasks /Create + /Run (一次性任务,12 秒后执行)
↓ 12 秒(给回复和日志留时间)
restart-dsh.ps1(进程外)
├─ 优雅关窗(Harness 只是隐藏到托盘)
├─ 识别浏览器主进程 → 先杀它 → 再杀子进程
├─ 等所有进程消失
├─ cookie 清理:dsh-auth-* 超过阈值就删(防 431 崩溃)
├─ cookie 快照(只读)
├─ 等单实例锁释放(3 秒)
├─ 启动 → 等 8 秒 → 数进程,最多重试 3 次
├─ 写 RESULT: OK / FAILED
└─ 删除一次性计划任务
↓
新 Host 启动
├─ 清除 restart.lock
├─ 扫描 wake-pending-*.json
└─ sessionController.prompt(...) → 会话被唤醒
没有登记时,唤醒器完全静默,只写一行 no-pending 日志。
配置
默认值即开即用:可执行文件从运行中的进程自动探测,登记目录在 ~/.dsh/restart-wake,脚本随包分发。
需要改时,在 profile 的 cordis.patch.yml 里覆盖这一行(config 整体替换,但插件对缺失字段回落默认值,所以只写要改的即可):
- id: restart-wake
name: dsh-restart-wake
config:
pendingDir: 'D:\dsh-wake'
delaySeconds: 15
| 配置项 | 默认值 | 作用 |
|---|---|---|
pendingDir |
~/.dsh/restart-wake |
唤醒登记、锁、日志、脚本参数的存放目录 |
logPath |
<pendingDir>/wake.log |
唤醒器与重启日志 |
lockPath |
<pendingDir>/restart.lock |
重启互斥锁 |
exePath |
自动探测 | Harness 可执行文件;探测失败时必须显式指定 |
processName |
自动推导 | 进程名(不含 .exe),用于杀进程与计数 |
scriptPath |
包内 scripts/restart-dsh.ps1 |
重启脚本 |
commandPath |
包内 scripts/run-restart.cmd |
计划任务入口 |
taskName |
DshRestartWake |
一次性计划任务名;多份安装要改成不同的名字 |
delaySeconds |
12 |
挂任务后等多久再重启 |
launchWaitSeconds |
8 |
每次启动后等待多久再数进程 |
maxLaunchAttempts |
3 |
启动尝试次数 |
graceSeconds |
8 |
优雅关窗等待秒数 |
killTimeoutSeconds |
20 |
强杀后等待进程消失的上限 |
singleInstanceWaitSeconds |
3 |
等 Electron 单实例锁释放 |
lockStaleMs |
600000 |
超过这个年龄的锁视为失效(默认 10 分钟) |
maxAgeMs |
86400000 |
登记超过这个年龄不投递(默认 24 小时) |
maxAttempts |
3 |
单条投递重试次数 |
retryDelayMs |
15000 |
重试间隔 |
startupDelayMs |
3000 |
启动后延迟多久开始扫描 |
cookieDbPath |
自动探测 Desktop 的 Cookies 库 | Chromium cookie 库路径 |
cookieProbePath |
包内 scripts/cookie-probe.mjs |
cookie 快照脚本 |
cookieCleanupPath |
包内 scripts/cookie-cleanup.mjs |
cookie 清理脚本 |
cookieThreshold |
50 |
dsh-auth-* 达到这个条数时,重启窗口自动清理(约 11 KB 请求头) |
crashLogDir |
空 | 可选:崩溃日志目录,用于统计「杀进程有没有新增渲染崩溃」 |
pendingDir的路径会影响计划任务命令行长度(上限 260 字符)。路径过长时restart_now会直接报错并建议换到更短的目录。
431 启动崩溃与 cookie 清理
Harness 桌面端每次启动都监听随机端口,并下发一个 dsh-auth-<authority哈希> cookie。端口变、名字就变,Chromium 只累加不清理;攒到约 70 条时 Cookie 请求头超过 Node 的 16 KB 上限,Host 返回 431 Request Header Fields Too Large,外壳只检查 response.ok,于是报「Web authentication failed」并退出。
这个文件在 Harness 运行期间被独占,唯一可读写的窗口就是「浏览器进程已死、新实例未起」的那几秒——正好是重启脚本执行的时候。所以插件每次重启都做两件事:
scripts/cookie-cleanup.mjs:统计dsh-auth-*条数,达到cookieThreshold(默认 50,约 11 KB)就只删这一族,其它 cookie 与账号凭据不碰;scripts/cookie-probe.mjs:记录新实例启动前的 cookie 数量与剩余寿命。
日志行形如:
cookie cleanup: authCookies=52 others=38 headerKB=11 threshold=50 action=purged removed=52
cookie snapshot: authCookies=0 ttl=[]
要手动清理(例如已经启动不了、连重启脚本都跑不了):
node <包目录>\scripts\cookie-cleanup.mjs "$env:APPDATA\@deepseek-ai\dsh-desktop\Network\Cookies" --dry-run
node <包目录>\scripts\cookie-cleanup.mjs "$env:APPDATA\@deepseek-ai\dsh-desktop\Network\Cookies"
手动清理必须完全退出 Harness。cookie_status 能读到时会报当前条数;运行中读不到是正常的(文件被独占)。
另一个缓解手段在 profile 配置里:把 client-connection.cookieMaxAgeDays 从默认 30 调到 1,把累积窗口压到单日。插件不替你改这份配置。
安全边界
- 只投递文本,不执行命令;
- 每条登记最多投递 3 次,失败归档,绝不无限重试;
- 登记超过 24 小时自动过期;
- 互斥锁保证同一时刻只有一次重启;
- 默认拒绝在「其他未登记的运行中会话」存在时重启(
force: true可覆盖); - 重启前校验可执行文件、脚本、计划任务入口,缺一个就不动手;
- 不碰凭据、不上传任何数据、不包含任何遥测。
平台与限制
- 重启:仅 Windows(
schtasks+ PowerShell)。非 Windows 上restart_now直接报错,不做任何破坏性动作。 - cookie 清理:仅 Windows(Desktop 默认路径)。手动清理需要完全退出 Harness。
- 唤醒:纯 Node,跨平台可用。
- 重启会中断当前回合;未保存的输入会丢失。
- 前提是 Harness 桌面端会恢复上次窗口(默认会)。即使不恢复,登记也不会丢,主人在下次打开会话时补投,只是不再「自动回来」。
- 如果 Harness 同时装了别的「启动即投递」插件并共用同一个登记目录,会造成重复投递——请给两份插件配置不同的
pendingDir。
故障排查
| 现象 | 检查 |
|---|---|
restart_now 返回 blocked |
有别的会话在跑且没登记;等它收尾,或确认可打断后传 force: true |
restart_now 返回 busy |
10 分钟内已有别人挂了重启任务;等它执行完,不要重复挂 |
schtasks /Create 报「找不到路径」 |
先跑全量 schtasks /Query /FO LIST 判断是权限还是任务不存在,别把「任务不存在」误读成被拦 |
日志停在 killing |
脚本被 Job Object 连带杀了:确认是计划任务在执行,不是从 Harness 内 Start-Process |
日志停在 attempt N : running processes = 0 |
启动失败:手动双击 Harness,查 RESULT: FAILED 附近的 throw 信息 |
| 重启后有 Windows 报错音 | 杀进程顺序错了:必须先杀浏览器主进程,再杀子进程 |
| 醒来后没有收到 prompt | 看 wake.log:delivered = 成功;controller-unavailable = 会话服务没就绪;expired = 登记超过 24 小时 |
开发
node --check index.js
node test/smoke.mjs # 离线测试,不需要 Harness / 计划任务
目录:
index.js 宿主插件入口(唤醒 + 注册工具)
lib/config.js 配置解析、可执行文件与 cookie 库探测
lib/cookie.js cookie 库只读检查
lib/pending.js 唤醒登记文件
lib/wake.js 启动唤醒循环
lib/restart.js 互斥锁、会话检查、计划任务、日志
lib/tools.js 六个 Agent 工具
scripts/restart-dsh.ps1 参数化重启脚本(纯 ASCII)
scripts/run-restart.cmd 计划任务入口
scripts/cookie-cleanup.mjs 重启窗口内的 cookie 清理
scripts/cookie-probe.mjs cookie 快照
docs/restart-protocol.md 给 Agent 看的重启协议
许可
MIT
No comments yet. Be the first to write one.