npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mcd0luo/celes-dsh-watchdog

v0.1.1

Published

Celes 调度中心 DSH 插件:单例会话巡检看门狗(lock 文件 + flock 全局单例;session.list 巡检卡死/实例死亡并汇报主 worker 会话,冷却去重不轰炸)

Readme

@mcd0luo/celes-dsh-watchdog

Celes 调度中心 DSH 插件:单例会话巡检看门狗(host-only,无 client 半部)。仅面向 DSH web profile 宿主使用(经 ctx.get("apiProxy") 走内部 RPC 语义;经 HTTP 调各实例的 session.list 信封 RPC 与 session.prompt 唤醒投递 / celes-center-session-bridge 汇报通道)。

定位

4 个 DSH 实例(默认 127.0.0.1:3180-3183)都安装本插件,但全局只有一只 watchdog 在巡检—— 用 lock 文件 + flock 单例互斥;抢到锁的实例跑巡检并向主 worker 会话汇报,其余实例静默待命、 周期重试抢锁;原锁持有者进程死亡时锁自动释放,其他实例自动接管。

能力

  1. 巡检循环:每 intervalSeconds(默认 60s)一轮,对每个配置端口逐个调 session.list(client-request 信封 RPC,HTTP POST /api/session.list,与 celes-worker-spawn 同款信封:{type:"client-request", rpcId, method:"session.list", payload:{}})。
  2. 卡死检测:running=true 的非 blank 会话,复合产出信号全部冻结超过 stuckThresholdMinutes(默认 15min)→ 卡死。复合产出信号 = sessionStats 的 turns/steps/decodeTokens/llmMs/decodeMs,任一增长即视为仍在产出并重置计时—— 避免长 turn / 多 step 的单 turn worker 因 turns 冻结被误判(W160 审计已坐实)。长 turn (单 turn 内多 step 或已有生成产出)时阈值 × longTurnMultiplier(默认 2)宽限。 空闲 worker 检测(W149):running=false(非 blank,已发生 turn/start)且标题匹配 worker 命名 W<数字> 且 updatedAt 距今 ≤ idleThresholdMinutes(默认 10min)的形态判定; 附加产出冻结前提 + 连续 sweep 确认——已产出真实工作(steps>0 或 decodeTokens>0)的会话 需连续 idleConfirmSweeps(默认 2)轮 sweep 才确认 idle,且排除 reportTo 主会话, 避免长 turn 的 turn 间隙 / 协调·汇报会话被误唤醒;仅 registry RUNNING 的在途 worker 才会被 自动唤醒(开关 wakeIdleWorkers)。
  3. 失败检测(实例进程死亡 =「进程已死但列表残留」):对某实例的 session.list RPC 传输层失败 (连接拒绝/超时等)→ 该 DSH 进程已死,其会话残留于磁盘、列表不可服务 → 实例级失败。 W233:dsh 0.1.2+ 的 /api/* 挂浏览器会话 cookie 认证篱笆——匿名信封 RPC 返回 HTTP 401/403 = 实例进程活着但不可匿名观测(例:lab 3182 升级 0.1.2 后被误判 dead)。判定逻辑对 401/403 单列 gated(跳过巡检、不列 dead、不升级汇报),仅连接拒绝/超时/5xx 判实例死亡。 W237 判死去抖:连接拒绝/超时/5xx 需连续 deadConfirmCount(默认 3)次失败才判实例死亡—— 单次瞬失败(连接复用陈旧连接 RST、实例重载 head-of-line 卡顿、瞬时超时)只记日志不误报; 期间任何一次成功或 401/403 都清零计数。探活 RPC 走独立超时 sessionListTimeoutMs (默认 30000ms,长于其他 RPC 的 rpcTimeoutMs 10000ms),避免大实例/重载下 session.list 慢响应被误判为死。
  4. 汇报(唤醒 + 巡检报告):汇总卡死/失败清单 → 一条消息注入主 worker 会话(reportTo,默认 session-a72078e7-b3c3-4ee1-a586-ca2c6aabca1f)——主通道 session.prompt RPC(mode:"steer", 内建唤起):目标会话非 running(离线/挂起)时先拉起 agent 再投递,报告正文标注 「这是一条唤醒 + 巡检报告」。去重:同会话未恢复时按 reportCooldownMinutes(默认 60min) 冷却,不轰炸;已恢复会话状态清除,复发立即重报。
  5. 单例:lock 文件 + flock(默认 /tmp/celes-dsh-watchdog.lock)。
  6. 职责边界(检测 + 报告 + 自动唤醒):卡死/空闲会话先自动唤醒(wakeThreshold 次,默认 3), 达阈值才升级汇报主会话(reportTo);除此之外绝不 restore / kill / retry / 改动会话内容, 修复动作由主会话决策。

判定依据(对 dsh-host-apiproxy 源码核实,2026-08-31)

session.list 的 SessionSummary 行(lib/types/api/sessions.schema.js / lib/index.js)字段仅:

sessionId / updatedAt / running / blank / parentSessionId? / origin? / cwd? / agentPreset? / projections?
  • updatedAt = max(header.createdAt, lastPromptAt)(sessionListUpdatedAt);lastPromptAt 只被 source.kind === "user" 的 user/message 事件推进(applySessionListMetadata)。session.prompt 注入的消息 source.kind 正是 "user"(host-apiproxy session.prompt 实现),故 worker 简报 prompt 会推进 updatedAt——running 会话的 updatedAt 停止推进 = 该轮 turn 无新 prompt,即无进展。
  • running = agent.status === "running"(dsh-agent-loop get status:phase.kind 仅 idle|running 两态)。
  • blank = 从未发生 turn/start。
  • projections.values.sessionStats 提供复合产出计数器:turns(已完成 turn 数)、 steps(agent step 数)、decodeTokens(生成 token 累计)、llmMs/decodeMs(累计耗时)。 卡死/空闲判定以「steps/decodeTokens/llmMs/decodeMs 任一增长 = 仍在产出」为准; turns 是 turn 级粒度,长 turn 单 turn 内长期冻结,不能单独作为产出信号(W160 审计结论)。

失败信号的调研结论:SessionSummary 没有 error/status 字段(agent/error 是事件流信号, session.list 上不可观测;agent status 仅 idle|running 两态),因此会话级「错误态」在本插件的 数据边界(session.list)上不可实现;可实现的失败信号是 传输层实例死亡——RPC 连接失败 = 进程已死、会话残留不可服务("进程已死但列表残留"的实例级表达)。报告里已如实写明该判定依据。

单例方案(flock)

利用 util-linux flock(1)(零 npm 依赖、真 flock(2) 排他语义):

spawn("flock", ["-n", <lockPath>, "-c", "cat"]),stdin 管道保持打开
  • -n 非阻塞:锁被占时 flock 立即 exit(1) → 本实例待命,按 lockRetrySeconds 重试抢锁;
  • 子进程存活超过 300ms 宽限期 = 抢锁成功(flock 持锁期间 cat 阻塞读 stdin);
  • 释放:end(stdin) → cat EOF → flock 退出 → 内核自动放锁;
  • 持锁进程崩溃:内核关闭管道写端 → cat EOF → 自动放锁 → 其他实例无缝接管 (无 stale-pid 竞态、无 O_EXCL 锁文件的清理问题)。

汇报通道(唤醒 + 巡检报告)

  • 主通道(自动唤醒):session.prompt RPC(HTTP POST /api/session.prompt,client-request 信封, payload {sessionId, mode:"steer", content:[{type:"text", text}]},与 server-go sessionbridge 的 promptSend 同语义)——内建唤起(非 live resume):目标会话离线/挂起时该 RPC 拉起 agent 处理该消息,在线时 followup 排队下一轮;逐实例尝试、命中目标会话的实例才成功,不依赖锁持有 实例与目标实例是否同机。汇报目标非 running 时,报告正文首行标注「这是一条唤醒 + 巡检报告」。
  • 兜底:celes-center-session-bridge HTTP POST /celes-center/bridge/send {target, content, from} ——离线会话持久投递、消息不丢,但不保证唤起(仅 live agent 存在时 followup 唤起)。
  • 报告发送成功才推进冷却状态;发送失败保持旧状态,下轮自动重试(不吞报)。
  • 报告内容只含 sessionId/标题(≤40 字)/空闲分钟/cwd,不含 prompt、目标等敏感内容; 运行日志与审计同样不落敏感内容。

职责边界(只检测 + 报告)

看门狗绝不自动修复:不 restore、不 kill、不 retry、不改动任何卡死/失败会话——只巡检、汇总、 唤醒主会话并汇报。修复动作一律由主 worker 会话(reportTo)决策执行。

配置(schemastery z.object,settings 命名空间 celestea-dsh-watchdog,live 热更新)

| 字段 | 默认 | 说明 | |---|---|---| | intervalSeconds | 60 | 巡检间隔(秒) | | stuckThresholdMinutes | 15 | 卡死阈值(分钟):复合产出信号全冻结超此值即判卡死(长 turn × longTurnMultiplier=2);W1075/L4 起与代码一致 | | reportCooldownMinutes | 60 | 同会话未恢复的最小重复上报间隔(分钟) | | reportTo | session-a72078e7-… | 主 worker 会话 id | | host | 127.0.0.1 | 巡检实例主机 | | ports | ["3180","3181","3182","3183"] | 巡检实例端口列表 | | lockPath | /tmp/celes-dsh-watchdog.lock | 单例锁文件(flock) | | lockRetrySeconds | 10 | 待命重试抢锁间隔(秒) | | statePath | /tmp/celes-dsh-watchdog-state.json | 去重状态(跨重启持久,tmp+rename 原子写) | | logPath | /tmp/celes-dsh-watchdog.log | 运行日志 | | reportMaxSessions | 20 | 单条汇报最多列出的会话数 | | excludeSessionIds | [] | 跳过巡检的会话 id(可选) | | idleThresholdMinutes | 10 | 空闲 worker 判定窗口(分钟) | | idleConfirmSweeps | 2 | 空闲 worker 需连续 N 轮 sweep 确认(产出过的会话,防 turn 间隙误唤醒) | | longTurnMultiplier | 2 | 长 turn(单 turn 多 step/有生成)卡死阈值放大倍数 | | rpcTimeoutMs | 10000 | 单实例 RPC 超时(毫秒,session.list 除外) | | sessionListTimeoutMs | 30000 | session.list 探活专用超时(毫秒),比 rpcTimeoutMs 长 | | deadConfirmCount | 3 | 实例判死去抖:连续 N 次 session.list 失败才判 dead | | bridgeToken | "" | 桥接/审计 HTTP token(空 = 仅信任本机回环) |

自测

node --check lib/index.js lib/core.js lib/locker.js scripts/self-test.mjs
npm test          # scripts/self-test.mjs:卡死检测 / 冷却去重 / 汇报文本 / flock 单例 / apply 装配(fetch 全 stub)

安装注意(沿袭系列踩坑记录)

  • Config 为 schemastery z.object(standard-schema 协议,普通对象会崩);
  • 命名导出 {Config, apply, inject, name}(export default 静默不挂);
  • package.json files 白名单含 cordis.patch.yml(漏了会 boot ENOENT crash-loop);
  • 本包自带 dsh.bundle.patch 装载点;一旦基座 @mcd0luo/dsh-celestea 注册了 celes-dsh-watchdog 成员行,须移除本包 dsh 块,避免 duplicate loader entry id。

许可

MIT