DeepSeek Harness Hub
← 返回列表

Mandarin715/dsh-autostart

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

Windows 专用的 DeepSeek Harness 插件:一键开机自启 + 一键重启,而且重启失败也不会把…

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/12 · 已提供中文文档

Windows 专用 DSH 插件:一键开机自启 + 一键重启,而且重启失败也不会把 DSH 关掉不回来;DSH 由常驻看护进程启动,全程无控制台窗口 | Windows-only DSH plugin: one-click boot autostart and a restart that cannot leave DSH down; a resident supervisor starts DSH with no console window ever

综合分
30
GitHub 分
30
用户评分
★ Stars
1
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add Mandarin715/dsh-autostart
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-autostart

Windows 专用的 DeepSeek Harness 插件:一键开机自启 + 一键重启,而且重启失败也不会把 DSH 关掉不回来 —— 全程没有控制台窗口。

⚠️ 免责声明(务必先读)

安装前请自行检验环境。因使用本插件导致对话历史丢失、任务中断或数据损坏的,概不负责。

重启会杀掉承载对话与 Agent 回合的 DSH 宿主进程:

- 有 Agent 在执行回合时重启 → 该回合被硬中断,结果可能不落盘
- 对话尚未写入 ~/.dsh/sessions/ → 该段历史可能丢失且不可恢复
- 并行会话中正在运行的任务也会被中断

使用前请:① 自行确认环境(Windows 版本、Node、DSH 版本、安全软件是否拦注册表);② 重启前确认没有重要任务在跑;③ 重要会话先备份 ~/.dsh/sessions/;④ 自行评估是否启用开机自启(会写入 HKCU\...\Run)。

本软件按「原样」提供(MIT License,无任何担保)。

核心卖点(它到底解决什么)

重启一个程序,难的不是「关掉」,而是「关掉之后还能回来」。这个插件就是把这件事做成有保证的:

1. 重启失败不会让 DSH 停摆。 放手让 DSH 退出之前,插件会 ① 确认「接管者」(看护进程)活着,② 把重启请求写进磁盘并读回来验证。任何一步不成立,它就拒绝这次重启、DSH 原地不动,并说明原因。这是一个真实发生过的故障:端口还没释放,旧逻辑探测一次就放弃,结果 DSH 关了、起不来。

2. 全程没有黑窗口。 DSH 启动时依附在看护进程的隐藏控制台上,所以它派生的每条命令都共用这个隐藏控制台,而不再各自新建一个 Windows Terminal 窗口 —— 以前是「每用一次工具就弹一个黑框」。

3. 接管者不是「看门狗」。 看护进程常驻,但只在「这次退出是你点的重启」时才拉起新实例。你自己关掉 DSH,它就安静收工 —— 一个你关不掉的进程,比一个偶尔不自动回来的服务更糟。

4. 停用 / 卸载不会偷袭你正在用的 DSH。 「停用自启」只取消下次开机自启;当前这次开机里 DSH 照常运行,看护进程会在 DSH 自己退出后收工。

5. 出问题有据可查。 每一步都写进 service.log;失败时它会说明原因,而不是静默不动。

要求

- Windows 10 / 11(需要能通过 PowerShell 调用 Win32_Process.Create —— 点重启而当时还没有看护进程在跑时,会刻意交由 WMI 服务创建一个,以免和宿主一起被杀)
- Node.js ≥ 20(随 DSH 提供)
- DeepSeek Harness ≥ 0.1.0-rc.6(实测于 0.1.2-rc.1;重启所依赖的 Job Object 行为实测于 0.1.5-rc.1)
- HKCU\...\Run 可写(开机自启用);安全软件拦截注册表会导致「启用」失败

环境与重启

DSH 由一个常驻看护进程启动 —— 开机时由 bootstrap.vbs 跑
node service.js start --config (start 是 supervise 的别名,后者才是真正的入口;
生成的 bootstrap.vbs 用的是 start)。活得比 DSH 久的是这个看护进程:它从子进程句柄上
得知 DSH 退出,并且只在「这次退出是你从设置页要的重启」时才拉起新实例。

只有当你点重启、而当时还没有看护进程在跑(例如 DSH 是你手动起来的)时,才需要从 DSH 内部先创建一个
看护进程。这是 WMI 服务唯一出场的地方:DSH 把自己的子进程放在一个 kill-on-close 的 Windows Job
Object 里,所以仅仅 detached 的进程会在 DSH 退出的瞬间被一起杀掉 —— 结果是 DSH 停摆、再也起不来。
交给 WMI 创建,看护进程才能出得了那个 job、活得比宿主久。一旦看护进程已经存在(登录后的常态,以及第一次
重启之后的每一次),就不再经过 WMI。

代价是:这样创建出来的进程不继承你的环境变量。因此:

- 看护进程的 config.json 路径由宿主用 --config  显式告知(而不是让它去按
DSH_HOME 推导 —— 那个变量在这里是缺失的);
- 重启后的实例用 config.json 里记录的 dshHome 重新断言 DSH_HOME。

其余环境变量(自定义 PATH、DSH 读取的其他变量)不会被带到重启后的实例。如果你的
DSH 配置依赖环境变量,请留意这一点,并尽量让 command.execPath 是绝对路径。

安装

dsh plugin --profile web add github:Mandarin715/dsh-autostart

重启 DSH 后,进入「设置 → 通用设置」,拉到最下方即可看到本插件的卡片。

使用

| 控件 | 作用 |
|---|---|
| 服务状态 | 实时探测端口,显示运行中/已停止 |
| 开机自启开关 | 启用时会写 ~/.dsh/dsh-autostart/config.json、生成 bootstrap.vbs,并写入注册表 HKCU\...\Run 的 DSH autostart 项 |
| 当前访问地址 | 从捕获的启动输出里解析出的最新带 token 地址,可一键复制 |
| 重启服务 | 二次确认后重启;DSH 会在数秒内恢复。它会先确保有看护进程在跑、由它把 DSH 拉回来;起步失败则当场拒绝这次重启 |
| 钩子脚本 | 可选。服务起来后会执行它,用于拉起你自己的依赖进程 |

页面完全打不开? 这张卡片只在 DSH 运行时才存在 —— 见下面的「服务没起来时怎么救」。

配置

在 profile 的 cordis.patch.yml 里:

- id: dsh-autostart
name: dsh-autostart
config:
hookScript: ''                 # 可选,服务起来后执行的脚本绝对路径
dshPort: 3080
exitDelayMs: 800               # 重启时旧宿主退出前的延时;上限 5000(见下方说明)
waitForExitMs: 30000           # 自常驻看护进程取代旧重启路径后已无读者 —— 保留只是为了让
已存在的 config.json 仍能加载
startTimeoutMs: 30000
openBrowserOnBoot: false       # true = 开机时自动打开浏览器
blockWhenAgentsRunning: false  # true = 有 Agent 在跑时拒绝重启
allowedHosts: []               # 经反代访问时填你的域名(必须显式填写),如 ['derp.example.com']

exitDelayMs 的有效上限是 5000 毫秒。 这个延时只为让重启响应先刷出去、旧宿主再退出;而新拉起的
看护进程等这个宿主退出只等 30 秒(DEFAULT_TAKEOVER_EXIT_MS)。延时超过那个窗口,接管就会放弃并收工,
重启也就变成了关机 —— 所以超过 5000 的值在使用处被截断到 5000,而不是拒绝加载
(index.js 的 MAX_EXIT_DELAY_MS)。

allowedHosts 必须由你显式填写(opt-in)。 默认是空数组,即不信任任何非回环的 Host。
如果你是通过反向代理访问 DSH(例如 frp + auth-proxy,它会把浏览器发来的原始 Host 转发过来),
那么请求里的 Host 是公网域名而不是 127.0.0.1,写操作按钮(启用/停用自启、重启服务)会被守卫
拒绝并返回 403。把你的域名填进 allowedHosts 后这些按钮才可用;同源校验
(Origin 必须与 Host 完全一致)对这些条目仍然生效——可接受的写法见下。

allowedHosts 可接受的写法:裸主机名,或 host:port,例如 ['derp.example.com'] 或
['derp.example.com:8443']。不要带协议头或路径(https://derp.example.com/ 是错的)。
浏览器访问 https://derp.example.com 时 Host 不带端口,因此对白名单里的权威跳过本地 dshPort
校验——填进白名单本身就是显式授权,而 Origin 与 Host 的同源校验依然必须成立。回环地址仍保持严格端口校验。

改配置后要不要重新启用自启?——要,但原因不是 dispose。
hookScript / dshPort / startTimeoutMs 这些字段是在「启用自启」时快照进
config.json 的,而看护进程读的是 config.json、不是插件的实时配置。所以改了它们必须回设置页
重新点一次「启用自启」才会生效(重复启用是幂等的)。真机实测过:改了 hookScript 却只重启服务,
config.json 里仍是旧值,钩子不会执行。
而 allowedHosts 只影响插件的路由守卫,重载即生效,与自启无关。
waitForExitMs 同样会被快照,但已经没有任何代码读它(见上方配置示例里的说明)。

注册表项不会因为改配置而消失。 插件只在自己真的被卸载时(判据:本插件的 service.js
已不存在)才移除 DSH autostart;DSH 因重载或加载失败而拆解插件树时保留它。

和已有的开机自启项如何共存(重要)

本插件只管 DSH。如果你原本已经有自己的开机自启项(例如 HKCU\...\Run 里的 DSH Web、
DSH frpc、DSH authproxy),那么启用本插件的自启之后,会有两条自启项都想在登录时拉起 DSH。

- ✅ 不会互相覆盖:本插件只写、也只删自己那一条 DSH autostart,不碰你的条目(实测:启用后原有条目一字未动)。
- ⚠️ 但会重复:两者都靠"端口已在监听就跳过"去重,所以最终只会有一个 DSH;然而存在一个窄竞态 ——
两条几乎同时跑、都在对方绑定端口之前探测到"未监听",于是都去拉起,其中一个因端口被占用而失败退出
(无害,但会留下一次失败记录与可能的残留进程)。

做法 A:只留本插件(简单)

删掉你自己的 DSH 启动项:

reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "DSH Web" /f

然后回设置页点一次「启用自启」。

⚠️ 不要顺手删掉 DSH frpc / DSH authproxy —— 本插件不管 frpc 和 auth-proxy。
手机远程若依赖它们,删掉就会失去自启。要合并请用做法 B。

做法 B:合并成「一条自启 + 一个钩子」(维护面最小)

钩子在开机与每次重启之后都会执行,所以把"确保其他进程在跑"的逻辑放进去,自启就只需要本插件这一条。

1. 写一个钩子,例如 ~/.dsh/hooks/after-service-up.ps1:

服务起来后,确保其余依赖进程在跑(路径按你自己的改)
& "$env:USERPROFILE\.dsh\scripts\start-frpc.ps1"
& "$env:USERPROFILE\.dsh\scripts\start-authproxy.ps1"

2. 在 profile 的 cordis.patch.yml 里指向它:

- id: dsh-autostart
name: dsh-autostart
config:
hookScript: 'C:\Users\\.dsh\hooks\after-service-up.ps1'

3. 回设置页重新点一次「启用自启」(hookScript 是启用时快照进 config.json 的),
再删掉 DSH Web / DSH frpc / DSH authproxy 三条,只留插件的 DSH autostart。

钩子失败不会阻断 DSH 启动(只记日志),所以钩子里各步骤可以独立失败。
确认钩子跑了没:看 ~/.dsh/dsh-autostart/service.log 里的 hook exited code=…。

| 你原有的自启项 | 做法 A | 做法 B |
|---|---|---|
| DSH Web(负责拉 DSH) | 删掉 | 删掉 |
| DSH frpc / DSH authproxy | 保留 | 删掉,改由钩子负责 |

生成物位置

~/.dsh/dsh-autostart/
├── config.json               # 记录真实启动命令,service.js 读取
├── bootstrap.vbs             # 开机入口:拉起常驻看护进程(wscript 无窗口)
├── dsh-web-server.log        # DSH stdout(含访问地址)
├── dsh-web-server.err.log
├── service.log               # 看护进程日志,排查问题先看这里
├── supervise.pid             # 看护进程自报的 pid(单实例守卫)
├── restart.request           # 重启时写入,记的是看护进程看到的那个退出的 DSH
└── supervise.stop            # 通知活着的看护进程在 DSH 消失后收工

后三个是临时状态文件:需要时创建、用完删除(全部尽力而为),不存在是正常的。但三者的清理时机并不一样:
supervise.pid 和 supervise.stop 在持有它们的看护进程处理到时就删掉;而 restart.request 只有在它记名的
那个 DSH 真的退出时才会被消费 —— 所以一次被拒绝、或始终没走到那次退出的重启,它写的请求可以一直留在盘上,
直到下一次匹配的退出把它取走。

服务没起来时怎么救

卡片在 DSH 的页面里 —— DSH 没起来时你打不开卡片,所以补救必须走命令行。按下面顺序来。

0)先等几分钟 —— 看护进程会原地重试

DSH 是由常驻看护进程启动的 —— 开机自启那条入口指向的就是它,所以在该有 DSH 的时候它总在。
新实例没起来时,看护进程不会干等就放弃:

1. 它会原地重试,间隔递增 1s → 1.5s → 2.25s → …,最多 5 次、预算 5 分钟;每次尝试还会为等端口
花掉 startTimeoutMs(默认 30 秒)。所以“预算 + 最后一次尝试自己的等待”才是最久要等的时间,而循环通常
比预算更早结束。前提是上一次那个子进程确实已经退出 —— 如果它还活着,它不会另起一个去抢同一个端口。
2. 它不会再安排“以后某次”:看护进程本身就活过这次尝试,重试就发生在那个已经在看着端口的进程里。

因此失败之后先等几分钟再动手 —— 预算 5 分钟,再加上最后一次尝试的 startTimeoutMs。日志里能看到它的动作:

spawned dsh pid=… (attempt 2/5)                        ← 正在重试
giving up: DSH did not come up after 5 attempt(s); supervisor exiting

那行 giving up 报的是实际尝试过的次数,不一定等于配置上限 5。

只有这些都失败,才按下面手动补救。

关于常驻进程(两条必须知道)

1. 它和 DSH 同生共死。 DSH 是附着(attached)在它的隐藏控制台上启动的(正是这个共享让每条命令
不再弹窗),所以看护进程退出时 DSH 会一起结束。
2. 停用自启并不会立刻停掉它。 停用只是取消开机自启 —— 当前这次开机里看护进程和你的 DSH 继续跑,
下次登录后不再出现。
- 卸载会另外写一个停止标记,所以活着的看护进程不会在 DSH 下次消失之后继续留守。
- 要立刻结束两者,直接关闭 DSH —— 子进程正常退出后,看护进程有意不留守。它只在你从设置页请求的
重启时才把 DSH 拉回来,它不是看门狗。
- CLI 里没有 stop 模式(只有 supervise 和它的别名 start),而且写停止标记也不会立刻停掉
看护进程:它只在子进程(DSH)退出之后才读那个标记。这个顺序是有意为之 —— 停用自启不该关掉你正在
用的 DSH。

1)先看日志找原因

~/.dsh/dsh-autostart/service.log            # 看护进程日志:先看这个
~/.dsh/dsh-autostart/dsh-web-server.err.log # DSH 自己的报错

常见三类:

- WARN port 3080 did not come up in time → DSH 被拉起了但没监听,去看 dsh-web-server.err.log
- hook not found: / hook exited code=… → 钩子问题,不会阻断 DSH 启动
- cannot read config → config.json 缺失或损坏

2)通用补救:手动执行一次「开机入口」

这一步等价于“现在立刻跑一次开机自启”,幂等(端口已在监听就跳过),而且无窗口:

wscript.exe "$env:USERPROFILE\.dsh\dsh-autostart\bootstrap.vbs"

它执行   start --config (start 是 supervise 的别名)。这条命令创建出的
就是常驻看护进程,它拥有一个隐藏控制台 —— DSH 附着在它上面启动,所以 DSH 没有可见窗口;DSH 的
stdout 写进 ~/.dsh/dsh-autostart/dsh-web-server.log —— 成功的话那里面就会出现新的带 token 地址。

看护进程会一直活到 DSH 结束,并始终持有 DSH。不要杀它:它是 DSH 的父进程,它退出 DSH 也会跟着结束。

也可以直接跑 CLI

node "\service.js" start --config ""

两个路径都必须是绝对路径(裸写 service.js 只有在你当前的工作目录恰好是插件目录时才能解析到)。它做的
和上面的 bootstrap.vbs 是同一件事,但它把 DSH 的命绑在你运行它的那个控制台上:DSH 是附着启动的,
所以关掉那个窗口就会终止 DSH;而且看护进程会占住前台直到 DSH 退出。用这种形式就把窗口一直留着;
否则优先用上面的 bootstrap.vbs —— 它给看护进程一个自己的隐藏控制台,并且立刻返回。

3) 如果连 config.json 都不存在

说明从未点过「启用自启」。那就先用你平时的方式把 DSH 起来,再到设置页启用一次自启。

4) 用你自己惯用的启动脚本也行 —— 但只有两个要点

要点是隐藏/后台启动,以及不要拿控制台窗口当服务的宿主:

- ✅ Start-Process … -WindowStyle Hidden,或 wscript 跑 VBS,把 DSH 拉成后台进程
- ❌ 不要用 npx @deepseek-ai/dsh web 这种前台占用控制台的方式;更不要"先拿到带 token 的地址,
再把那个窗口关掉" —— 那个窗口就是 DSH 的控制台,关掉窗口 Windows 会终止 DSH,链接随之失效
(此时是"拒绝连接"而不是 401,再粘也没用)

作者自己的机器上这一步是 ~/.dsh/scripts/start-dsh-web.ps1(它用 Start-Process -WindowStyle Hidden
启动,并把 stdout 写进 ~/.dsh/logs/dsh-web-server.log)。这是作者本地的脚本,不属于本插件;
你换成自己的等价脚本即可,或者直接用上面第 2) 步。

5) 别把两种情况搞混

| 现象 | 含义 | 做法 |
|---|---|---|
| 页面 401 Unauthorized | 服务在跑,只是这个浏览器没有会话 | 用「访问地址」里那条带 token 的链接进一次,不要重启服务 |
| 无法访问此网站 / 拒绝了连接 | 服务没起来 | 按上面 1)–4) 处理 |

关于那条带 token 的地址,三个事实:①只对本机有效(它是 127.0.0.1;手机走域名 + 密码,由反代代换);
②换来的会话 cookie 有 30 天有效期,关浏览器、重启 DSH 都不受影响;③但 token 本身每个 DSH 进程一个,
重启即作废 —— 所以要用当前这条(卡片显示的就是最新的),收藏一条长期用是无效的。

6) 补救成功的判据

- 端口 3080 处于 LISTENING
- service.log 里出现 port 3080 is up
- 设置页卡片的「服务」显示运行中

7) 如果最近升级或迁移过 DSH

config.json 记录的是当时的绝对路径(可能含 npx 缓存目录的哈希路径,如
…\_npx\\node_modules\@deepseek-ai\dsh\lib\bin.js)。升级/迁移后该路径可能已失效,于是
自启会静默失败(开机什么都没发生)。回设置页重新点一次「启用自启」让它重新捕获当前命令即可。

卸载

1. 先在设置页停用开机自启(会删除注册表项)

这一步是必需的,不是可选的。原因有两条:①注册表项指向 ~/.dsh/dsh-autostart/bootstrap.vbs,
而它与 config.json 都在插件目录之外,卸载不会删;②pnpm remove 会保留
node_modules/dsh-autostart 这条符号链接,于是本插件的 service.js 依然可达 —— 而插件的清理
判据正是"service.js 是否还存在"。所以只卸载而不先停用,会留下一条死条目(开机跑一次、
静默失败)。也可以卸载后手工删掉 HKCU\...\Run 里的 DSH autostart。

顺带一提:设置页的「设置 → 插件」没有本插件的卸载按钮(那一页只管理从市场安装的插件),
所以第 2 步的命令行是正常路径。
2. 卸载插件:dsh plugin --profile web remove dsh-autostart
3. 如需彻底清理,手动删除 ~/.dsh/dsh-autostart/ 目录

为什么这样实现(踩坑记录)

- 为什么用 wscript.exe 而不是 powershell -WindowStyle Hidden:后者对长时间运行的脚本不可靠,会留下一个无法关闭的空控制台窗口。
- 为什么等端口用条件轮询而不是固定 Start-Sleep:固定等待曾让一次重启耗时 80 秒以上;条件轮询(waitForPort,间隔 250ms、受 startTimeoutMs 约束)把它压到数秒。看护进程不轮询自己持有的那个 DSH 进程 —— 它握有那个子进程的句柄,等的是句柄的 exit 事件。(没有句柄可握的地方仍然按 pid 轮询:接管时等旧 DSH 退出,以及等新看护进程认领 supervise.pid。)
- 为什么端口判定用 TCP 连接而不是 netstat/:port 子串:子串匹配会命中 TIME_WAIT 与客户端连接,导致"其实没启动却报告已在运行"。
- 为什么用 Node 而不是 PowerShell 实现逻辑:PowerShell 脚本里的中文易出现编码问题,且受执行策略限制。
- 为什么必须写 --no-open:DSH 0.1.2-rc.1 每次启动生成新的 token,访问地址会打印在 stdout;插件把它捕获到日志并在设置页展示,因此开机时无需(也不应)弹出浏览器。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

同作者(Mandarin715)的其他插件

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群