DeepSeek Harness Hub
← 返回列表

无缝重启续跑StvLi/dsh-phoenix

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
✓ 可直接安装

优雅重启不打断任务,页面自动重连并续跑目标

自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node >=22);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/2 · 已提供中文文档

DeepSeek Harness (dsh) 的永不中断、可恢复生命周期:优雅重启 + 客户端自动重连 + 跨重启目标延续。

综合分
32.1
GitHub 分
32.1
用户评分
★ Stars
3
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-phoenix
npm 包 dsh-phoenix 已校验归属本仓库,走 npm 安装最省事
数据截至 2026/9/3(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

npm 包dsh-phoenix @ 0.2.5
Node 引擎要求 >=22 · 基线 Node 22.19 满足
dsh CLI 依赖未声明 dsh 版本约束
入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/18 21:08:19

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-tools@deepseek-ai/dsh-agent-presets
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

🐦 dsh-phoenix

为 DeepSeek Harness(dsh)提供"永不中断、可续跑"的生命周期

dsh-phoenix 是一个持久的 DeepSeek Harness host 插件,它把 "插件更新 → 重启 dsh" 从一次破坏性的中断,变成一条优雅、无缝、可续跑的循环——让正在运行的任务能跑完、浏览器能保持连接、长时目标能跨重启继续进化。

English · 中文版

为什么你需要它

DeepSeek Harness 是"一切都是插件",而 dsh Web 是由 systemd 托管的单个常驻进程。这带来三个痛点:

| 痛点 | dsh-phoenix 的做法 |
| --- | --- |
| 更新插件意味着硬重启 dsh,会切断正在运行的任务 | 优雅重启——等地步空闲才重启,让当前任务先跑完再重启。 |
| 后端重启后,浏览器页面掉线并卡死("已停止") | 自动重连——注入一段极小心跳脚本,后端一恢复页面就自动刷新。 |
| 重启会清空内存中的目标状态,目标无法跨重启继续 | 重新武装目标——重启后重新激活一个被解武装的目标,让长时目标恢复并继续进化。 |

✨ 功能特性

1. 优雅重启——只在安全时重启
- 通过 dsh 插件工具识别插件(重新)激活(cordis_run),不立即重启。
- 检查所有活体 agent(含子代理):只要有 running,就延后,每隔几秒复查,等地步空闲才重启(5 分钟兜底,避免永不重启)。
- 通过 systemd-run --user 以独立瞬时 unit 执行重启,即使被重启的进程本身也能等到重启完成。
- 只响应 cordis_run——dsh 自带 HMR 热载的客户端 bundle 源码改动不属于 cordis_run 操作,因此不会触发 dsh-phoenix 重启。

2. 客户端自动重连——页面始终与后端同步
- 注册一个 /__dsh_health 端点,返回本次启动 token。
- 向被服务的 index 注入一段零依赖心跳:每几秒轮询 /__dsh_health;当 token 变化(后端已重启)时,页面自动 reload。
- 不再有"卡死 / 已停止"。后端重启变得无感。

3. 跨重启目标重新武装——任务继续进化
- 目标由会话日志背书、持久,但 dsh 每次重启都会解武装 active 目标,导致自动续跑停止。
- dsh-phoenix 读取一个持久断点(JSON 状态文件);当其 pendingResume: true 时,找到活体 agent 的 active + disarmed 目标并 goals.resume() 重新武装。
- 重新武装后,harness 的目标轮次驱动会继续驱动下一轮迭代。

🧬 完整目标:自进化循环
把三者组合起来,就得到一条跨重启的自主进化闭环(可复现步骤见 docs/VERIFY.md):

读断点 → 判断下一步 → 测试 / 修改插件
→ 写断点(pendingResume)→ 优雅重启
→ dsh 启动 → dsh-phoenix 重新武装目标 → 下一轮 … 直到完成

断点是循环的持久记忆;目标是它的驱动器;重新武装钩子是它的复活点。

🆚 与同类项目的差异

| 项目 | 关注点 | dsh-phoenix 的补充 |
| --- | --- | --- |
| dsh-doctor | 自愈启动:从插件引发的启动失败中恢复、doctor 运行、卡死回合检测 | 等地步空闲的优雅重启(绝不打断运行中的任务)、客户端自动重连、目标续跑 |
| dsh-daemon | 把 dsh web 注册为自启动、自愈的后台服务 | 在已有进程之上叠加可续跑、自进化的生命周期 |

dsh-doctor 与 dsh-daemon 是在"救活启动";dsh-phoenix 让"生命周期"变得优雅、连接、可续跑。 它们互补——dsh-phoenix 可以舒服地架在 dsh-daemon 管理的进程之上。

互补而非竞争——两个轴,一套防漏的分层

"phoenix" 和 "hot" 常被看作对手——都"围绕插件更新",都为了让变更生效。这种看法误读了系统。一个 dsh 进程有两个相互独立的轴,两家各占其一。

轴 1 · 组合(dsh 由什么构成)
dsh 是一个 Cordis 组合:由 patch 层 diff 出来的插件行树。它在启动时被加载,但不是冻结的——行可以通过 loader 的 diff 机制当场增/删/换。dsh 自带的 HMR(cordis-plugin-hmr)已经能热载这一层,但它刻意忽略 node_modules,所以升级一个已安装的插件包仍需要重启——这正是 hot 家族要补的缺口。

hot 家族拥有这条轴。 当你在 CLI 里 dsh plugin add/remove/update 一个 bundle 时,dsh-hot-installer 监听 profile 清单(dsh.profile.bundles),dsh-hot-reload 监听 pnpm-lock.yaml。它们驱动的是与启动完全相同的挂载路径,只是改为现场执行:解析该包、读它的 dsh.bundle.patch、把行注入运行中的树、重新 import 新模块(缓存失效 + 纤维重建)。失败时回滚到可用旧版并标记"需要重启"。它们从不碰进程本身——从不替你重启 dsh。

轴 2 · 进程生命周期
有些东西不是组合里的行,它们活在进程边界:systemd 用户单元与其 cgroup、HTTP/WebSocket 服务、内存中的目标激活、浏览器的实时连接。这些都无法热换。 一旦重置跨过它们,诚实的选择就是重启——而这个重启正是 dsh-phoenix 拥有的那部分。

dsh-phoenix 把它做得优雅(地步空闲——等到没有 agent 在跑)、连接(注入心跳在后端启动 token 变化时刷新浏览器)、可续跑(重新武装被解武装的目标,长时目标得以继续)。它还通过 systemd-run --user 以脱离的瞬时 unit 执行重启,被杀的进程永远不会把重启序列一起拖下水。

边界由这两个轴自然得出
二者因变更路径不同而不相交,且各是自己所辖轴上"正确的那把工具":

| 变更路径 | 轴 | 处理方 | dsh-phoenix 是否重启 |
| --- | --- | --- | --- |
| dsh plugin add/remove/update(CLI,已安装 bundle) | 组合 | hot 家族 → 现场挂载/重载 | 否 |
| 通过 dsh cordis 工具(cordis_run)改动插件树 | 组合(运行时、agent 驱动) | dsh-phoenix | 是,优雅重启 |
| 热换失败(import 坏 / apply 抛错) | 组合 | hot 家族回滚并标记"需要重启" | dsh-phoenix 把这个重启做安全 |

所以不是"谁抢到同一次变更",而是源头预防 + 兜住泄漏:hot 家族在变更入口消除可避免的重启;dsh-phoenix 是下面那层,保证剩下的、真正必要的重启不会打断任务、不会掉浏览器、不会杀掉进行中的目标。这是经典的分层系统分隔,不是竞争。

[!NOTE]
这是对当前行为的描述,不是保证。 今天 dsh-hot-installer / dsh-hot-reload 监听 profile 清单与 lockfile,不响应 cordis_run;dsh-phoenix 监听 tools/result,不监听清单。若任一方将来扩大触发范围,请复核此表——边界是行为性的,不是架构性的。

🧭 耐久生命周期与安全重启语义

"重启 → 恢复 → 续跑"是一条显式、耐久的状态机。状态原子地持久化到 DSH_PHOENIX_STATE_FILE(.tmp + rename),因此能在任何一点崩溃后存活,且坏/缺失的断点永远不会触发误续跑。

状态

IDLE ──重启请求──▶ DEFERRED ──安全点/截止──▶ RESTARTING ──启动/崩溃──▶ RECOVERING ──续跑──▶ RUNNING
▲                                                                                                │
└────────────────────────────── 新请求(在途时合并) ◀────────────────────────────────────────────┘

| 状态 | 含义 |
| --- | --- |
| idle | 无待重启 |
| deferred | 已请求重启但有 agent 在跑;等待安全点 |
| restarting | 重启已调度(systemd-run) |
| recovering | 重启/崩溃后启动;对记录的目标做有界、幂等的续跑 |
| running | 已安定;无残留待重启 |

迁移与不变量

- idle/running — 重启请求 → deferred(忙)或 restarting(闲);铸造新 generation。
- deferred — 安全点 → restarting;硬截止 → restarting(强制,有日志)。
- restarting — 启动 / 崩溃 → recovering。
- recovering — 续跑(至多一次) → running。

不变量
- RESTARTING:不得再调度第二个重启(请求合并)。
- RECOVERING:只对记录的 generation 续跑;stale goalId(无匹配活体目标)被失效化,绝不续跑。
- RUNNING:不得残留 pendingResume。

续跑语义——持续续跑

pendingResume 不是松散布尔;断点还记录 generation、goalId、resumeAttempt。一个被标记的目标是续跑载体:dsh-phoenix 会在每次重启后都重新续跑它(每个重启/generation 至多一次),只要它仍处于 active。调用 goals.resume() 之前,插件会原子地先递增 resumeAttempt,因此中途崩溃不会在同一重启里重复续跑同一个目标。续跑被保留(goalId/pendingResume 持续存在),直到目标完成、变 stale、或 resumeAttempt 耗尽(maxResumeAttempts)——这样既终止循环,又让目标能跨多次重启存活。

安全截止,而非"到点就重启"

defer 超时是一个带升级的截止期,不是无条件定时器:

deferred(agent 忙)
├─ 软截止   → 记录 WARNING(agent 仍忙)
└─ 硬截止   → 若策略为 'auto' 则强制重启(有日志)
若策略为 'wait' 则继续等(不强制)

DSH_PHOENIX_DEFER_POLICY=auto(默认)保留对无限 defer 的保护;wait 取消强制重启(你接受可能很长的 defer)。Phoenix 无法区分"agent 忙"与"agent 处于关键区"——DSH 不暴露此类信号——因此"忙"被当作不可重启,靠截止期兜底。

验收答案

1. 任意一点崩溃? 耐久状态存活;启动时在途状态(deferred/restarting/recovering)进入 recovering 并幂等地安定。
2. 同一目标在同一重启里被重复续跑? 不会——每个重启/generation 至多一次(调用前原子递增 resumeAttempt)。被标记的目标会在后续每次重启时再次续跑(有意的持续续跑)。
3. stale 断点触发续跑? 不会——缺失/损坏断点归为 idle;匹配不到活体目标的 goalId 被失效化,不续跑。
4. 重复更新事件→重复重启? 不会——在途请求合并,重复事件只产生一次重启。
5. 无限重启/续跑循环? 不会——在途合并、续跑次数上限、失败/耗尽后安定到 running。
6. defer 截止而 agent 仍忙? 软→告警;硬→强制(auto)或继续等(wait),均有日志。
7. 忙 vs 关键区? Phoenix 无法区分(DSH 无关键区信号);把"忙"当作不可重启,靠安全截止兜底。
8. 迁移确定且可测? 是——状态机是纯函数(见 tests/,17 项,含崩溃/stale/重复/失败注入)。

📦 环境要求

[!WARNING]
优雅重启功能要求 dsh 以 systemd --user 服务运行(默认单元 dsh-web)。在其它形态下——macOS、无 systemd 的容器、或 pnpm run dev:web——只有重启这一项无法工作。此时要么设置 DSH_PHOENIX_RESTART_CMD 指向能重启你 dsh 进程的命令,要么接受只有客户端自动重连和目标重新武装生效。插件会在启动时检测并打印 graceful restart DISABLED,确保它永不静默失效。

- 一个 dsh 安装,其 Web 以 systemd --user 服务运行(默认单元 dsh-web)。
- Node >= 22。
- 零外部依赖——只用 dsh 自身的运行时服务(timer、webServer、agents、goals、shell)。

🚀 安装

dsh-phoenix 是一个官方 dsh bundle(声明了 dsh.bundle),用标准工具即可安装。

从 npm(推荐)

dsh plugin --profile  add dsh-phoenix

从 git / tarball

git(需 prepare 构建 + pnpm >= 10 的 allowBuilds)
dsh plugin --profile  add github:StvLi/dsh-phoenix

tarball(先打包;文件名跟随包版本,故动态读取)
TARBALL=$(npm pack 2>/dev/null | tail -1)   # 例:dsh-phoenix-0.2.6.tgz
dsh plugin --profile  add "$TARBALL"

然后校验该层并启动:

dsh --profile  --dump-config   # 期待一个 "# == dsh-phoenix" 层
dsh --profile

安装后,插件行会被自动注入(见 cordis.patch.yml):

- insert:
- id: dsh-phoenix
name: dsh-phoenix

⚙️ 配置

所有参数都是带安全默认值的环境变量——无需配置文件。

| 环境变量 | 默认值 | 作用 |
| --- | --- | --- |
| DSH_PHOENIX_UNIT | dsh-web | 要重启的 systemd 用户单元 |
| DSH_PHOENIX_DELAY | 8 | stop 与 start 之间的秒数 |
| DSH_PHOENIX_ARMING_MS | 5000 | 加载后 N 毫秒内忽略信号(防自触发) |
| DSH_PHOENIX_DEBOUNCE_MS | 3000 | 把突发信号合并为一次重启 |
| DSH_PHOENIX_DEFER_POLL_MS | 3000 | 延后期间的空闲复查间隔 |
| DSH_PHOENIX_DEFER_SOFT_MS | 300000 | 软安全截止——到点记告警,继续等待 |
| DSH_PHOENIX_DEFER_HARD_MS | 900000 | 硬安全截止——到点强制重启(若策略为 auto) |
| DSH_PHOENIX_DEFER_POLICY | auto | auto(硬截止时强制)或 wait(永不强制;可能长久延后) |
| DSH_PHOENIX_HEALTH_MS | 4000 | 浏览器心跳间隔 |
| DSH_PHOENIX_RESTART_CMD | (空) | 非 systemd 部署的自定义重启命令覆盖(见环境要求警告) |
| DSH_PHOENIX_REARM_MS | 8000 | 目标重新武装检查的初始延迟 |
| DSH_PHOENIX_MAX_RESUME_ATTEMPTS | 1 | 每个重启的续跑次数上限(1 = 每次重启续跑一次;>1 重试;达到则结束续跑) |
| DSH_PHOENIX_STATE_FILE | (空) | 启用目标重新武装所需的断点文件路径;为空则禁用 |

🧠 工作原理

- 检测——订阅 tools/result,响应插件(重新)激活(cordis_run)。
- 延后——若有 agent running,暂缓重启并复查直到空闲(或到上限)。
- 重启——systemd-run --user 调度 stop → sleep → start,与 dsh 的 cgroup 解耦。
- 重连——健康端点 + 注入心跳在启动 token 变化时刷新页面。
- 续跑——启动时读断点;若 pendingResume,则通过 goals.resume() 重新武装被解武装的目标。

所有日志都以 [dsh-phoenix] 前缀写入 dsh journal,便于排查。

✅ 如何验证它生效

本 README 中的结论由 docs/VERIFY.md 的可复现清单 + 单测(npm test,17 项)支撑。快速开始:

npm test                                  # 21 项:状态机迁移、持续续跑、stale/损坏断点、合并、defer 升级

在真实插件更新后,观察 journal:
journalctl --user -u dsh-web -f | grep dsh-phoenix
你会看到 "deferring (agent busy)",随后在地步空闲时 "executing deferred restart"
重启后会看到 "re-armed goal after resume (rev=N)"

curl http://127.0.0.1:3080/__dsh_health 应返回 {"token":"…"}。

📄 许可证

MIT © Steven P.LI

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

同作者(StvLi)的其他插件

💬 加入 DPharness 群聊

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

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