fix(cli): 稳定性专项——ENOBUFS、jlist 超时、恢复广播抑制、loopback 放宽、版本错配警告 - #1005
fix(cli): 稳定性专项——ENOBUFS、jlist 超时、恢复广播抑制、loopback 放宽、版本错配警告#1005ITcathyh wants to merge 7 commits into
Conversation
listPm2GodDaemonPids 的 ps/powershell 扫描及各适配器(reasonix、 zellij、adopt-route、session-discovery、codex-rpc-engine)的 ps 回退 路径均未设 maxBuffer,Node 默认 1MiB,macOS 进程表超 1MiB 时抛 ENOBUFS,导致 start/stop/restart 的 God 查重 fail-closed。统一显式 传 32MiB(与 browser-restart 一致),真 ENOBUFS 仍 fail-closed 抛出。 Co-Authored-By: Claude <noreply@anthropic.com>
高负载机器 pm2 jlist 实测需 9-18s,历史写死 10s 让 restart 安全检查 间歇误判失败。新增 BOTMUX_PM2_JLIST_TIMEOUT_MS(默认 30s,clamp 1s-120s),pm2Capture、readLivePm2Projection、 readVerifiedBotmuxPm2Projection 默认值统一读它;两处 Math.min(10_000) 钳制上限放宽到 60s。preflight 复制解析逻辑而非 import cli.ts 以避免 循环依赖(cli.ts 反向 import preflight)。 Co-Authored-By: Claude <noreply@anthropic.com>
notifyStartupFailure 只检查 failureNotified 和 managedAuxUiSuppressed, 不检查 suppressRecoveryCard。daemon 重启批量恢复会话时每个失败 worker 都往自己会话发「会话启动失败」卡片,形成广播风暴。现在在 failureNotified 检查之后短路:恢复模式下只写 daemon 日志、不发卡片; 故意不置 failureNotified,待抑制解除后新一代失败仍可正常通知。 Co-Authored-By: Claude <noreply@anthropic.com>
三处内部 HMAC 端点(dashboard verifyHmac、daemon-internal-auth、 v3 daemon-ipc-auth)写死 127.0.0.1/::1/::ffff:127.0.0.1 字面量检查, 定制内核把 loopback peer 上报成 LAN IP 时内部请求全 401。抽出 utils/loopback-peers.ts:默认语义逐字节保留,BOTMUX_LOOPBACK_PEERS (逗号分隔 IP/CIDR,IPv4/IPv6 双栈)额外放行,非法项 fail-closed 忽略并告警。 Co-Authored-By: Claude <noreply@anthropic.com>
DaemonDescriptor 增加 version 字段(daemon 启动时从 package.json 写入),botmux status 读取在线 daemon 描述符并与 CLI 版本比较, 不一致时打印醒目警告提示 restart。旧 daemon 无 version 字段自动 跳过(向后兼容),源码 checkout(0.0.0)跳过避免噪音。 Co-Authored-By: Claude <noreply@anthropic.com>
- browser-restart 的 ps 进程表扫描补 maxBuffer 32MiB(统一 ENOBUFS 清扫) - dashboard-ipc-server /api/sessions/migrate-to-chat 迁移到 isLoopbackPeer, BOTMUX_LOOPBACK_PEERS 放行地址对该端点同样生效 - worker-pool suppressRecoveryCard 注释修正:owner 并非总能收到恢复 DM (崩溃/pm2 自动重启无 breadcrumb),失败写 daemon 日志、下一轮用户消息兜底 Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 182414f482
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // (rememberLastCliInput) so later failures notify normally. Do NOT set | ||
| // failureNotified here: a later generation failing after the flag clears | ||
| // must still surface. | ||
| if (ds.suppressRecoveryCard) { |
There was a problem hiding this comment.
Suppress fork-level recovery failures as well
When restoration fails at the child-process fork boundary (for example, spawn EAGAIN before worker IPC is established), execution never reaches this guard: the separate normal and adopt worker.on('error') handlers still call sessionReply without checking suppressRecoveryCard. Under resource pressure during batch restore, this can therefore still post one startup-failure message per restored normal/adopt session—the broadcast storm this change is intended to prevent—so both fork-error paths need the same chat-notification suppression while retaining their logging and lifecycle handling.
AGENTS.md reference: AGENTS.md:L68-L68
Useful? React with 👍 / 👎.
|
感谢这个稳定性专项 PR,5 个修复都是近期用户真实反馈的痛点,方向很好 👍 我们做了一轮自动评审 + 交叉复核,绝大部分逻辑扎实(默认行为字节等价、fail-closed、向后兼容都做得很到位)。有一处建议在合入前修,其余是可选 nit,具体如下。 🟠 建议合前修:Fix3 恢复抑制——queued 两条 refork 路径的 flag 永不清除Fix3 的核心不变量( 但在两条 queued refork 路径上不成立:
这两条 失败链(复合条件,production 可达):
结果:用户零反馈,且后续每条消息重复静默吞掉——体验上比"报错卡片"更糟(用户以为消息发出去了)。 建议修法(很小):在这两条 queued 路径用户触发 refork 时清 🟡 可选 nit(不阻断)
✅ 其余确认无问题
以上为自动评审的初步意见,供参考,最终以维护者审阅为准。 🙏 |
|
补充一个 Fix3 修法的实现细节(供参考):清
核心 bug 场景(fork 被接受 + 异步失败)两种时序都能修好,放在 fork 后只是更干净、且和主路径保持一致。清 flag 后 streaming card 恢复是预期行为(用户已介入 = 恢复结束)。 |
重启恢复的 ordinary 会话带 suppressRecoveryCard=true 且有 queued activation 时,queuedActivationPending / retainedQueuedActivation 两条 refork 路径 forkWorker 后直接 return,从不经过 rememberLastCliInput (全仓唯一常规清 flag 点),flag 永不清除。复合失败链:worker 死后用户 发消息触发 queued refork → fork 同步被接受、随后异步 'error' 携带 queuedActivationTurnId → scheduleTransientStartupRetry 因 turnId 非空 不自治重试 → notifyStartupFailure 被静默 flag 吞掉、fork 未同步抛错 也没有 ingress notice——用户零反馈,且后续每条消息都被静默吞掉。 两条路径在 forkWorker 同步返回后(未 throw)显式 ds.suppressRecoveryCard = undefined:用户已介入即恢复静默结束;只清 恢复标志、不补 rememberLastCliInput(避免 lastCliInput 毒记录破坏空 启动 --resume 契约)。时序对齐主 refork 路径——fork 同步 throw 时 flag 保留,走 ingress notice 通道。 回归测试 3 条:两条 queued 路径 refork 后 flag 清除;fork 同步 throw 时 flag 保留(钉住清 flag 必须在接受之后的时序约定)。 Co-Authored-By: Claude <noreply@anthropic.com>
|
感谢评审,🟠 合前修已处理并推送(commit af7c48c): Fix3:queued 两条 refork 路径清除 suppressRecoveryCard 认同失败链分析——这两条路径 修法按建议实现:
回归测试加在 daemon-ordinary-ingress-failure-notice(复用真实 inbound 路由 + forkWorker mock):
两个 🟡 nit:均为已知/可选,本 PR 不展开(migrate-to-chat 的 endsWith 放宽不可利用已确认;preflight clamp 上界后续可补独立测试)。 验证: |
PR 1: sprint-stability(稳定性修复 5 项)
改了什么
来自「Botmux 交流群」近 7 天用户反馈的稳定性专项,5 个修复:
fix(cli): ps 进程表扫描增加 maxBuffer 防止 ENOBUFSspawnSync('ps')抛 ENOBUFS,导致botmux start/stop/restartfail-closed。统一给 ps/powershell 进程表扫描加maxBuffer: 32MiB(cli.ts、reasonix、zellij-backend、adopt-route、session-discovery、codex-rpc-engine)。fix(cli): pm2 jlist 超时改为 env 可配置并放宽钳制pm2 jlist需 9-18s,写死的 10s 超时导致 restart 安全检查间歇失败。新增BOTMUX_PM2_JLIST_TIMEOUT_MS(默认 30s,clamp 1-120s),两处Math.min(10_000, ...)钳制放宽到 60s。fix(worker-pool): 恢复模式下抑制启动失败卡片广播notifyStartupFailure在suppressRecoveryCard期间只写 daemon 日志、不发卡片(抑制解除后新一代失败仍可通知)。fix(dashboard): loopback 判定支持自定义放行地址127.0.0.1/::1判定导致 401。新建src/utils/loopback-peers.ts,envBOTMUX_LOOPBACK_PEERS(IP/CIDR)额外放行,默认行为逐字节等价,非法项 fail-closed 忽略。feat(cli): status 检测 CLI 与 daemon 版本错配DaemonDescriptor加version字段,botmux status检测到错配时打印醒目警告并提示 restart。旧 daemon 无字段时跳过(向后兼容)。影响面
suppressRecoveryCard解除后行为恢复测试验证
pnpm build通过(tsc + dashboard bundle + audit)pnpm test全量:18180 passed;3 个失败(bwrap MCP 沙箱 ×2、dsh 文件沙箱 ×1)已在干净基线 0517705 复现确认为本机环境预存问题