EZO Watcher 是一个告警墙和寻呼系统,直接安装在用户的项目中:将配置作为代码管理在 .watcher/ 中,作为一个本地 Node 进程运行,无需任何账号。你的职责是完成完整的、经过验证的安装过程,而不是提供一串建议列表。
CLI 是真理之源。每个主题都可以通过以下命令打印:watcher skills <topic>(主题包括:install、discover、sources、channels、hosting、cloudflare、netdata、operate、fix、telemetry),或者阅读此文件旁边的 references/<topic>.md。执行每个命令时,请使用 watcher <verb> 格式,切勿使用裸命令(doctor 会与宿主代理自身的 /doctor 冲突)。
安装 CLI
CI=1 npm_config_yes=true npm i -g ezo-watcher
watcher --version
无需全局安装:npx -y ezo-watcher <verb> 也可以运行相同的 CLI。从代码仓库检出:npm ci && npm run build && npm link。
需要 Node 24+。完整详情,包括从 GitHub 安装最新的 main 分支:watcher skills install。
工作流程
如果 .watcher/ 已经存在,请先运行 watcher doctor --json 和 watcher scan --json,然后只添加排名中表明缺失的内容;不要在已有安装上重新 init。
- 初始化。 在项目根目录运行
watcher init。幂等操作,从不提示;会写入.watcher/config.json、.watcher/.env(权限 0600)、.watcher/data/,并为后两个添加 gitignore,选择一个从 7717 开始的空闲端口,并打印出第一份 doctor 摘要。 - 扫描。
watcher scan --json→{signals, suggestedCommands}。在小型手工标注样本上,其原型发现了 19 个 HTTP/TLS 点中的 15 个,但只发现了 10 个静默点中的 2 个(watcher skills discover):将其命令视为候选,然后通过阅读代码执行下面的静默优先扫描。 - 按排名选择(下一节)。每个真实点一个数据源;切勿为了显示绿色而凭空捏造数据源。
- 添加。 对于每个选定的数据源:
watcher add <kind> --id <id> … --found-at <file:line> --read "<它所证明的内容>"。--found-at/--read是溯源信息,缺少它们时watcher doctor会发出警告。--dry-run用于预览。然后添加一个通道:watcher add channel <type> --id <id> …(参见watcher skills channels)。如果没有可用的凭据——无头服务器、没有供应商账号、用户尚未选择寻呼服务——则添加无需凭据的备用方案:watcher add channel file --id local:它会将每次通知写入一行 JSON 到.watcher/data/下,这是一个真实的、可测试的通道,而不是一个存根。watcher add … --replace会重写已有的 id;watcher rm <id>会删除一个。 - Doctor 循环。
watcher doctor --json;如果ok:false,则通过其next修复第一个fail,然后重新运行。如果channel.missing发出警告且用户希望接到电话,则现在执行步骤 8 的通道,然后返回。退出码 0 表示通过,1 表示失败,64 表示使用错误。 - 运行。
watcher run --detach(或者watcher service install以在重启后生存)。然后运行watcher doctor --live --json来读取正在运行的服务器。 - 验证。
watcher doctor --live --prove --json直到它返回{"ok": true, "live": true}。--prove会将每个数据源自身的读数移过其阈值,并且除非规则实际翻转,否则会失败(source.assertion-not-red;空洞的 http-probe 断言会失败并显示source.assertion-vacuous):http-probe 的答案会被修改,heartbeat 会在错过一次运行加上其宽限期后进行评估,host 的读数会移过其磁盘/内存/负载阈值,tcp 连接会失败,expiry 会落在pageDays内。告警由其发送者报告的类型(grafana、vmalert、sentry、netdata、feed、webhook)是一个命名跳过(source.prove-unsupported),永远不会失败。如果项目没有服务器也没有计划任务,则添加watcher add host --id host——即机器本身,使用本地读取,无需安装任何东西——并进行验证。只有没有服务器、没有计划任务且没有要监控的主机的项目,才运行watcher doctor --no-runtime-surface --json→{"ok": true, "live": false, "reason": "no-runtime-surface"},直接报告即可;已配置的数据源总是优先于--no-runtime-surface(此时报告会警告doctor.no-runtime-surface-ignored)。未经验证的安装不能视为完成。 - 寻呼。 添加一个通道并运行
watcher test-alert;确认它已到达重要的设备。缺少凭据是一个命名跳过,绝不视为通过。 - 托管(可选)。 阶梯顺序是:本地 → 服务 → Docker/VPS → Cloudflare Tunnel(默认选项,用于“使用我的 Cloudflare 密钥托管”)→ Containers 测试版。
watcher skills hosting、watcher skills cloudflare。 - 安装技能以备下次使用。
watcher skills install --target all --dir .将其写入项目。在 Claude Code 中,之后运行/reload-skills——在会话期间创建顶级技能目录不会被自动识别。
第二天——查看告警墙、确认、评论、静音、暂存、重新检查——见 watcher skills operate。
当出现问题时
事件是一个带有证据的声明,而不是一个通知。完整的流程见 watcher skills fix:
watcher status --brief查看一行摘要,或watcher incidents --output json查看键(MCP:watch_state,然后list_incidents)。get_fix_context { key }(CLI:watcher incident <key> --fix-context):事件信息、数据源和目标配置(凭据以环境变量名形式显示)、规则的断言以及失败的那个、端点发现的位置、最后一次好观测和最后一次坏观测、准确重新运行检查的命令以及next。- 从该命令复现问题。阈值不是根本原因。
- 修复原因,重新部署或重启以使修复生效。
verify_fix { key, by }(CLI:watcher recheck <key> --wait):它会重新检查,等待数据源的新答案,并在所有告警都解决时记录确认信息——或者指出哪个断言仍然失败,以便修复可以完善。在重新检查通过之前,切勿将事件标记为已解决;park用于已合并但尚未生效的修复,confirm_resolution用于数据源已自行关闭的事件。- 用你的发现
comment。沉默的修复会让后来者一无所知。
排名:需要监控什么,按顺序
- 生产流程最深层的就绪路径,断言一个在依赖项宕机时会发生变化的字段。切勿使用硬编码的
{"ok": true}/{"status": "ok"}——那只是存活检测,无法在关键的故障发生时变红。 - 平台已声明的就绪探针:
/readyz、/health/ready、composehealthcheck、KubernetesreadinessProbe、fly/railway/render 健康路径。从外部使用相同的路径和端口进行镜像。 - 没有服务器 → 每个计划任务一个心跳。 静默本身就是信号。
- 没有服务器也没有计划任务 →
watcher add host --id host(负载、内存、空闲空间,可选 pid 文件;无需安装任何东西)并进行验证。只有没有服务器、没有计划任务且没有要监控的主机 →watcher doctor --no-runtime-surface --json,报告NO_RUNTIME_SURFACE,停止。已配置的数据源总是优先于--no-runtime-surface。切勿凭空捏造数据源。 - 然后是已存在的系统——Sentry、Grafana、Netdata、入站发送者——需要凭据。缺少凭据是一个带有代码和
next的命名跳过,绝不是无声的通过。
静默优先(扫描会遗漏这些)
阅读代码以查找以下六类;每类都变成一个 watcher add heartbeat 或 watcher add webhook --stale-after:
- 安装时生成的计划任务——
install.sh/setup.*中的 heredoc,用于写入 plist、unit 或 crontab、launchctl load、systemctl enable、crontab写入。 - 运行时创建的 launchd/systemd/cron——
.plist模板、OnCalendar=、StartInterval、KeepAlive、node-cron、schedule(、围绕一个任务的setInterval、celery beat。每个任务一个心跳(--every/--schedule+--grace);N 个任务意味着 N 个数据源。 - 拉取队列工作者——
worker、queue、bullmq、sqs、claim、lease、dequeue。失败是队列深度或无进展,而不是端口:在成功路径上发送心跳,或推送一个包含state: failing的checks.list条目。 - 产品自身配置中的过期时间——
staleAfterSec、presenceTtlSec、maxAge、ttl、refreshInterval、lastRunAt。强制执行相同的窗口:watcher add webhook --id <sender> --token-env <ENV> --stale-after-sec <sec>。 - 状态目录可写性——
mkdir、state/、data/、sqlite、锁文件。仅在成功写入后发送心跳;简单的test -w不是数据源。 - 作为代码常量保存的依赖端点——
process.env.X ?? "https://…"、host:port 字面量、gRPC/ClickHouse/LLM 基础地址。watcher add http-probe(HTTPS GET)或watcher add tcp-probe --host --port,并指定--found-at为常量位置。
将心跳放在任务的成功路径上——POST /ingest/heartbeat/<id>、/fail 用于失败运行、/<exit-code> 用于特定退出码。watcher add heartbeat 会在指定变量尚无值时将令牌写入 .watcher/.env(权限 0600),或者写入你传递的 --secret-value,并打印出准确的行(下面的 $NIGHTLY_TOKEN 是它为 --id nightly 选择的名称;你传递的 --token-env 是它打印的变量名):
set -a; . .watcher/.env
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>?token=$NIGHTLY_TOKEN" # 成功运行后
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>/fail?token=$NIGHTLY_TOKEN" # 失败运行后
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>/<exit-code>?token=$NIGHTLY_TOKEN" # 带任务退出码
一个收集器(不是单个任务)应推送其整个列表:POST /ingest/<source>,带有 {"checks":{"list":[{"id":"…","service":"…","state":"ok","lastRunAt":"…"}]}}——完整形状和状态见 watcher skills sources。
如果健康路由只返回一个字面量,请找到一个更深层的路由或添加一个:GET /readyz → 200 {"db":{"ok":true}}(依赖项响应时)、503 {"db":{"ok":false}}(不响应时)。你可以编辑用户的项目——你是负责写入文件的代理。然后断言变化的字段,切勿断言 {"ok":true}。
规则
- 切勿将数据源指向 Watcher 自身的
/healthz——source.self-probe会拒绝它。 - 数据源必须断言一个可以翻转的值。 单纯的 200 只是存活检测(
source.bare-200);watcher doctor --live --prove是关口,--found-at是一个警告(source.untraced)。 - 机密信息在配置中是环境变量名,值只在
.watcher/.env中。 切勿在扫描期间读取.env值,切勿提交.watcher/.env或.watcher/data/。 - 一个能工作的通道胜过五个已配置的通道。 使用
watcher test-alert进行验证。完全没有凭据?添加watcher add channel file --id local并进行验证:该文件是一个真实的通道,缺少凭据是一个命名跳过,绝不视为通过。 - 报告你验证的内容——添加的数据源、命令输出以及每个命名的跳过。如果代理无法完成某个步骤(缺少凭据、没有区域),请用一行说明是哪个以及原因。
当某个步骤需要更多细节时:watcher skills discover(完整信号目录和静默检查清单)、watcher skills sources(每种类型、标志、示例)、watcher skills channels、watcher skills hosting、watcher skills cloudflare、watcher skills netdata、watcher skills operate(MCP 工具和事件分类)。