dsh-agent-preflight
DiscoveredSandbox pre-check plugin (split from `dsh-agent-watch`): mandatory pre-check service before restart/start — plugin static health (`lib/src` freshness/schema DSL), disk, profile manifest validation, patch file validation, peer dependencies, environment variables,
dsh-agent-preflight
一句话:重启前的保险丝——在任何「把当前实例换成新实例」的不可逆动作之前,先真的加载一次新组合,加载不起来就拦住(fail-closed)。
为什么值得用:坏组合一旦被装上,代价是服务整体不可用(web 起不来 → 守护熔断 → 只能靠兜底冷启动)。本插件把「验证」提前到「替换」之前,并且失败不 kill 旧 web——旧实例可能有毛病,但新实例是没验证过的,两害相权取旧。它还会在试运行就绪后再多等一个稳定窗口才判 PASS:webserver 先 listen(约 2.6s 即 HTTP ready),而插件 loader 的 apply 抛错可能发生在之后(约 8s 才 exit code=1)——就绪即杀会把必崩组合误判 PASS。
能力
本插件不注册任何工具,它对外的能力是一个 cordis 服务:
| 能力 | 形态 | 用途 |
|---|---|---|
ctx.preflight.run(workspace, mode) | 服务(ctx.provide('preflight')) | mode='full'(含试运行,哨兵/门控场景)或 mode='quick'(静态项,崩溃自愈场景)。返回 PreflightResult:pass + 逐项 checks |
dsh-agent-preflight/core | 子路径导出 | runPreflightCore(cfg, mode):零 ctx 依赖的检查核心。dsh-agent-plugin-manager 的 preflight_check 工具直调同一份实现(D1 唯一化,避免两份判据漂移) |
消费方(谁在用):
| 消费方 | 场景 |
|---|---|
dsh-agent-sentinel | 热重载/重启前门控(预检失败不 kill 旧 web) |
dsh-agent-guardian | 崩溃自愈拉起前,quick 模式秒级放行 |
dsh-agent-plugin-manager | preflight_check 工具 → 同一核心;挂载/启停/改配置后强制完整试运行 |
检查项(checks 的 8 个键):
| 键 | 检查什么 |
|---|---|
pluginStatic | 插件静态健康:lib 存在 / src 是否比 lib 新(未重新构建)/ schema DSL 合法性 |
disk | 磁盘剩余空间 |
profile | profile manifest:JSON 对象 / bundles 列表 / 每个包必须有 bundle 声明 |
patch | cordis.patch.yml 校验(空文件 / 注释-only 会抛错) |
sessionLog | 会话日志完整性——未知事件类型会让重启卡死(2026-08-26 事故) |
peerDeps | peer 依赖完整性(cordis / schemastery / dsh-tools 等缺失) |
env | 环境变量(loadLayeredEnv 语义:bootstrap-only 文件变量拒绝) |
trialRun | 真试运行:spawn 一个完整 web(同 profile/凭据/DSH_HOME),对齐 harness 启动的 assertEntriesLoaded / assertEntriesActivated |
full模式默认probeExistingFirst: true:先探活现有实例端口,HTTP 2xx/401/403 = 当前组合可加载的活证据 → 直接 PASS(毫秒级,不 spawn 重复实例)。仅当现有实例不健康时才走完整试运行。组合变更场景(挂载/启停/改配置后)应传false强制完整试运行。
快速开始
1) 装依赖(自研插件家园 self-plugins/,在目标 profile 的 package.json 加 link 依赖):
"dsh-agent-preflight": "link:<工作区>/self-plugins/dsh-agent-preflight"
2) 构建:
cd self-plugins/dsh-agent-preflight && npm install && npm run build && npm test
3) 挂组合(提供服务的这一侧——典型是 watch profile,与 sentinel / guardian 同侧):
- id: agent-preflight
name: dsh-agent-preflight
config:
profile: web
targetPort: 3080
4) 30 秒验证(本插件没有工具可调,验证走「消费者触发 + 读轨迹」这条路):
# ① 核心逻辑自证:24 例离线测试全过
cd self-plugins/dsh-agent-preflight && npm test
# ② 触发一次真实预检(任选其一的消费者):
# - 重启/热重载时由 sentinel 门控自动触发
# - 或在 plugin-manager 侧调用工具 preflight_check(它直调同一核心)
# 然后读判定:
tail -2 "$DSH_HOME/preflight-trace.jsonl" # 期望:出现 verdict 行,verdict=PASS/FAIL
配置
(键名与 src/index.ts 的 Config schema 一致;默认值取自源码)
| 项 | 默认 | 说明 |
|---|---|---|
dshHome | $DSH_HOME(空串则回退进程 cwd) | profiles/ / sessions/ / 轨迹文件所在目录 |
bin | ''(从 @deepseek-ai/dsh/lib/bin.js 解析) | 试运行要 spawn 的入口;留空则自动解析,解析不到为空串 |
profile | web | 预检的目标 profile |
preflightReadyMs | 20000 | 试运行「存活判定」时长——子进程存活即算就绪 |
preflightGraceMs | 10000 | HTTP 就绪后的存活确认窗口:抓到 2xx/401/403 后不立即判 PASS,需再存活满该窗口(须大于本机组合全量加载时长)。防「就绪即杀」误判(2026-09-01 事故) |
trialProgressWindowMs | 15000 | 试运行「仍在推进」窗口:到期时该窗口内仍有输出 → 判为慢启动并延长,而非判死(2026-09-13 假 FAIL 事故) |
trialHardMaxMs | 240000 | 试运行硬上限:无论是否在推进都不再延长(fail-closed 兜底) |
defaultWorkspace | ''(回退进程 cwd) | run(workspace) 未传时的兜底工作区 |
targetPort | 3080 | 目标 web 端口:probeExistingFirst 探活它 |
probeExistingFirst | true | full 模式是否先探活现有实例短路(真试运行 = 慢路径)。组合变更场景传 false |
落盘与自证(出问题时先看这里)
每次预检落一行 JSONL 到 <DSH_HOME>/preflight-trace.jsonl(DSH_HOME 缺省 ~/.dsh):
| 阶段 | 含义 |
|---|---|
start | 预检开始(一笔记账的起点) |
trialRun/begin | 进入试运行(quick 模式不出现此阶段;静态项快速失败时同样不出现) |
trialRun/end | 试运行结束(含 classifyTrialFailure 的断点分类) |
verdict | 最终判定:verdict = PASS/FAIL,FAIL 时附 failedChecks[] + error(首条失败详情,≤300 字符) |
一条命令答五问:
tail -3 "$DSH_HOME/preflight-trace.jsonl"
# ① 跑的是哪个构建 → build = "<version>@<core 模块 mtime ms>";builds[] = 自建 + 试运行目标 bin(版本读不到时退化为 unknown@mtime——版本会说谎,mtime 不会)
# ② 谁发起 → caller(调用栈首个非本插件帧;全自有帧则 unknown)+ pid(预检在 watch 与 web 两侧都跑,pid 区分进程)
# ③ 断在哪一段 → phase 枚举;失败时看 error 的断点分类(「组合加载抛错」还是「全程无输出的卡死」)
# ④ 结果质量 → verdict / failedChecks[] / shortcut(现有实例健康短路 = 没真试运行)
# ⑤ 耗时与预算 → durationMs(trialRun/* = 试运行实耗;verdict = 全程)对比 preflightReadyMs / trialHardMaxMs
两个「已调用预检」标记不要混淆:
| 文件 | 谁写 | 语义 |
|---|---|---|
<DSH_HOME>/preflight-trace.jsonl | 本插件 | 运行证据(这次跑了什么、结论如何) |
<DSH_HOME>/.preflight-invoked.json | dsh-agent-plugin-manager | 门控记录(唯一真源):daemon_restart 重启前据此放行。判据是进程级(本 web 进程内调用过),不比对会话 id |
观测纪律:轨迹写盘失败一律吞错返回 false,绝不影响预检结论(日志失败不改变判定,也不抛)。
生效判据与回退
生效判据(三选一,按可靠性排序):
tail -1 "$DSH_HOME/preflight-trace.jsonl"里build的 mtime 等于lib/index.js(或lib/core.js)的 mtime ⇒ 进程在跑当前构建;- 生态级:
plugin_boot_status(dsh-plugin-bootreport)返回的liveNow含本插件 ⇒ 同上; - 行为级:消费方能拿到
ctx.preflight服务并返回PreflightResult(sentinel 门控跑通,或preflight_check工具返回结论)⇒ 服务已挂载。
注意:重新构建 ≠ 生效——产物 mtime 新只证明「构建过」,进程启动时间晚于产物 mtime 才算「在跑它」。提供服务的这一侧(watch)没有
hasUnverifiedBuilds()类兜底,构建完必须重启该进程才生效。
回退(三档):
- 源码级:
git -C self-plugins/dsh-agent-preflight revert <commit>→npm run build→npm test→ 预检 → 重启; - 组合级:在服务侧 profile 给
agent-preflight行加disabled: true(或移除该行)→ 重启该 profile; - 运行期:无需回退(无持久业务状态;
preflight-trace.jsonl可随时删除)。
测试
npm test # = node --test "tests/*.test.mjs"(跑 lib/ 产物,需先 npm run build)
24 例离线测试全部通过(# pass 24 / # fail 0):
| 文件 | 覆盖 |
|---|---|
tests/clip.test.mjs | 失败详情截断 clipHeadTail:头尾双保留、超限标注省略字符数、边界(超 1 字符即裁);尸体测试——旧行为 slice(-300) 会丢掉原因开头,新行为必须保留 |
tests/trace.test.mjs | 轨迹层:resolveHome(DSH_HOME 优先 / 空白与缺失回退)、路径锚定、序列化单行且键序固定、解析容错(坏行/半行/空行/null 全跳过不抛)、追加不覆盖、尸体测试(父路径是普通文件 → 返回 false 且不抛)、captureCaller(跳过本插件帧;全自有帧 → unknown)、collectBuilds(版本读不到 → unknown@mtime) |
tests/trial-deadline.test.mjs | 试运行截止裁决 decideTrialDeadline:尸体样本取自事故现场(90s 到期但 6.9s 前仍有输出 → 延长而非判死)、真静默 → 判死并点名「卡死非慢启动」、硬上限优先于推进、三处边界(静默 == 推进窗口、elapsed == 硬上限、延长链不越过硬上限) |
tests/wiring.test.mjs | 端到端接线:quick 模式真实落盘 start → verdict(无 trialRun 阶段);full 模式静态项全过 → 出现 trialRun/begin 与 trialRun/end(含断点分类) |
无网络依赖、无真实外部服务依赖:测试链路由注入式配置 + 临时目录驱动,不 spawn 真 web(真试运行在生产路径上由消费方触发)。
设计要点
- fail-closed 的语义:预检失败 ⇒ 不替换。旧实例可能有毛病,但至少是已经在跑的;新实例没验证过。这是免疫层的全部意义——误判失败的代价(一次多余的重启)远小于误放行的代价(服务不可用 + 守护熔断)。
- 「就绪」不等于「加载完」:
listen发生在 loader apply 之前。preflightGraceMs是为此刻意保留的跨层不变量——改试运行判定逻辑时不得省掉就绪后的存活确认窗口,否则必崩组合会被放行。 - 「超时」不等于「卡死」:负载下启动可能很慢但仍在推进。
trialProgressWindowMs用「窗口内有输出」区分慢启动与真卡死,trialHardMaxMs作为不可突破的兜底——判死必须有硬上限,判活必须有推进证据,两者缺一都会误判。 - 判据单一真源(D1):检查逻辑全部在
core.ts(模块级导出、零ctx依赖),服务壳与preflight_check工具共用。禁止在消费方复制一份判据——两处各自维护「什么算通过」必然漂移。 - 观测不反噬:轨迹写入是纯副作用,失败不影响判定,也不抛。排障时若轨迹缺失,先怀疑
DSH_HOME不可写,而不是怀疑预检本身。
安全与边界(重要)
- 本插件只回答「能不能动」,不执行「动」:不监听哨兵、不 kill、不拉起 web。那些是
dsh-agent-sentinel(重启)与dsh-agent-guardian(保活拉起)的职责。 - 不是沙箱、不是安全边界:它防的是「组合装载失败」这类工程损坏,不防恶意代码、不防越权操作(能力 ≠ 沙箱)。
- 不是组合语义校验器:只验证「能否加载」,不判断「这套组合是不是我想要的」。
- 试运行会 spawn 一个完整 web(同 profile / 凭据 /
DSH_HOME)。实现会给它打标记让副作用型插件让位;即便如此,凭据敏感的部署应先在隔离环境验证。 - 不受 Windows 文件沙箱约束:试运行子进程与调用方同权限运行。
相关文档
| 文档 | 内容 |
|---|---|
docs/semantic.md | 权威契约:定位与反定位、术语、概念模型与不变量、契约(含调用点清单)、边界与信任、可证伪验收清单、实践修订记录、未决问题 |
| alice-digital-life | 本插件所属生态的中心索引(全部自研插件) |
技能 plugin-maintainability / guardian-lifecycle / preventive-lifecycle | 可维护性五问与自证证据层、守护型进程的安全替换、预防性存活 |
License
MIT © jonah791
本插件属于我的数字生命爱丽丝(alice-digital-life)的 DSH 自研插件生态——50 个插件按生命/认知/感知/行动/通信/治理/呈现七层组织。
Comments
Loading…
Similar plugins
by zdjmrq
DSH 用户插件管理器:在 设置→插件 统一管理插件目录散件、运行树插件与 npm 插件包——挂载/卸载/启用/停用(cordis.patch.yml 补丁层 + HMR 热生效)
★ 4
MIT
JavaScript
Aug 18, 2026
dsh plugin --profile web add dsh-pluginmanagerby GHJIVHIDD
Docker 容器沙箱部署级插件(与 dsh-plugin-vm-sandbox 全能力对齐,无需 OrbStack):39 个 docker_* 模型工具、快照/回滚、文件传输、端口转发、后台任务、审计、共享/配额/回收、网络策略与华丽原生 UI。
★ 3
Apache-2.0
JavaScript
Aug 20, 2026
dsh plugin --profile web add @dsh-community/dsh-plugin-containerby zxmqq1234
DeepSeek Harness(dsh)自动重试插件:报错自动重试、中断自动继续、挂起自动唤醒、 每一步右上角弹窗告诉你,不用再手动发"继续",数据看板还原每一次重试的真相。与 dsh系统重试互补而非替代——在内置重试耗尽或不覆盖的错误码上追加保障。
★ 4
MIT
TypeScript
Sep 30, 2026
dsh plugin --profile web add dsh-auto-retryby LX2000WASD
在 Web UI 中一键管理 DeepSeek Harness (DSH) 插件:查看、实时启停、安装/卸载、更新检测、健康检查(依赖/冲突/兼容性分析)、环境管理、插件市场。bundle 与非 bundle 插件全覆盖
★ 67
MIT
TypeScript
Sep 20, 2026
dsh plugin --profile web add dsh-web-plugin-managerby chenw2759-wq
害怕插件装了就崩溃?用这个插件帮你检测插件是否正常/是否含木马!
★ 15
MIT
JavaScript
Aug 15, 2026
dsh plugin --profile web add dsh-plugin-healthcheckby sunchendd
DSH 配置备份插件:侧边栏一键导出/导入 ~/.dsh 配置(设置/profile/SSH/预设/皮肤/看板),支持敏感字段脱敏与恢复前安全快照
★ 0
JavaScript
Aug 21, 2026
dsh plugin --profile web add dsh-config-export