EZO Watcher é uma parede de alertas e pager instalado no projeto do usuário: configuração como código em .watcher/, um único processo Node local, sem conta. Sua função é realizar a instalação completa e comprovada — não uma lista de sugestões.
O CLI é a fonte da verdade. Cada tópico abaixo é impresso por ele: watcher skills <topic> (tópicos: install, discover, sources, channels, hosting, cloudflare, netdata, operate, fix, telemetry), ou leia references/<topic>.md ao lado deste arquivo. Execute cada comando como watcher <verbo>, nunca a palavra isolada (doctor colide com o /doctor do agente anfitrião).
Instalar o CLI
CI=1 npm_config_yes=true npm i -g ezo-watcher
watcher --version
Sem instalação global: npx -y ezo-watcher <verbo> executa o mesmo CLI. A partir de um checkout: npm ci && npm run build && npm link.
Node 24+. Detalhes completos, incluindo a instalação da última versão principal do GitHub: watcher skills install.
O ciclo
Se .watcher/ já existe, execute watcher doctor --json e watcher scan --json primeiro, depois adicione apenas o que o ranking indica como faltando; não reinicie sobre uma instalação funcional.
- Init.
watcher initna raiz do projeto. Idempotente, nunca pergunta; escreve.watcher/config.json,.watcher/.env(0600),.watcher/data/, adiciona os dois últimos ao gitignore, escolhe uma porta livre de 7717 para cima, imprime um primeiro resumo do doctor. - Scan.
watcher scan --json→{signals, suggestedCommands}. Em uma pequena amostra rotulada manualmente, seu protótipo encontrou 15 de 19 pontos HTTP/TLS mas apenas 2 de 10 pontos de silêncio (watcher skills discover): trate seus comandos como candidatos, depois faça a passagem de silêncio primeiro abaixo lendo o código. - Escolha pelo ranking (próxima seção). Uma fonte por ponto real; nunca invente uma fonte para parecer verde.
- Add. Para cada fonte escolhida:
watcher add <kind> --id <id> … --found-at <file:line> --read "<o que prova>". Os--found-at/--readsão proveniência ewatcher doctoravisa sem eles.--dry-runpré-visualiza. Depois um canal:watcher add channel <type> --id <id> …(vejawatcher skills channels). Se nenhuma credencial estiver disponível — uma máquina headless, nenhuma conta de fornecedor, um usuário que não escolheu um pager — adicione a opção sem credencial,watcher add channel file --id local: ele escreve uma linha JSON por notificação em.watcher/data/e é um canal real e testável, não um esboço.watcher add … --replacereescreve um id existente;watcher rm <id>remove um. - Loop do Doctor.
watcher doctor --json; seok:false, corrija o primeirofailatravés de seunexte execute novamente. Sechannel.missingavisar e o usuário quiser um telefone tocando, faça o canal do passo 8 agora e volte. Exit 0 passa, 1 falha, 64 é uso. - Run.
watcher run --detach(ouwatcher service installpara sobreviver a reboot). Depoiswatcher doctor --live --jsonpara ler o servidor em execução. - Prove.
watcher doctor --live --prove --jsonaté retornar{"ok": true, "live": true}.--provemove a leitura própria de cada fonte além de seu limite e falha a menos que a regra realmente mude (source.assertion-not-red; uma asserção de http-probe vazia falhasource.assertion-vacuous): uma resposta http-probe é mutada, um heartbeat é avaliado após uma execução perdida mais sua graça, uma leitura de host é movida além de seus limites de disco/memória/carga, uma conexão tcp falha, um vencimento cai dentro depageDays. Um tipo cujo alerta é o que seu remetente reporta (grafana, vmalert, sentry, netdata, feed, webhook) é umsource.prove-unsupportednomeado, nunca uma falha. Se o projeto não tem servidor e nenhum job agendado, adicionewatcher add host --id host— a máquina em si, lida localmente sem nada instalado — e prove. Apenas um projeto com nenhum servidor, nenhum job agendado e nenhum host para vigiar executawatcher doctor --no-runtime-surface --json→{"ok": true, "live": false, "reason": "no-runtime-surface"}, reportado claramente; uma fonte configurada sempre vence sobre--no-runtime-surface(o relatório então avisadoctor.no-runtime-surface-ignored). Não chame uma instalação não comprovada de concluída. - Page. Adicione um canal e execute
watcher test-alert; confirme que chegou no dispositivo que importa. Uma credencial ausente é uma omissão nomeada, nunca um passe. - Host (opcional). A escada é local → serviço → Docker/VPS → Cloudflare Tunnel (padrão para "hospedar com minha chave Cloudflare") → Containers beta.
watcher skills hosting,watcher skills cloudflare. - Instale a habilidade para a próxima vez.
watcher skills install --target all --dir .a escreve no projeto. No Claude Code execute/reload-skillsdepois — um diretório de habilidades de nível superior criado no meio da sessão não é detectado de outra forma.
Dia dois — ler a parede, reconhecer, comentar, silenciar, estacionar, verificar novamente — é watcher skills operate.
Quando algo quebra
Um incidente é uma alegação com evidência, não uma notificação. O ciclo, completo, é watcher skills fix:
watcher status --briefpara uma linha, ouwatcher incidents --output jsonpara as chaves (MCP:watch_state, depoislist_incidents).get_fix_context { key }(CLI:watcher incident <key> --fix-context): o incidente, a configuração da fonte e do alvo com credenciais como nomes de variáveis de ambiente, as asserções da regra e a que falhou, onde o endpoint foi encontrado, a última boa ao lado da última má observação, o comando exato que re-executa a verificação, enext.- Reproduza a partir desse comando. Um limite não é uma causa.
- Corrija a causa, reimplante ou reinicie para que a correção entre em vigor.
verify_fix { key, by }(CLI:watcher recheck <key> --wait): ele re-verifica, aguarda a resposta fresca da fonte, e registra a confirmação quando todos os alertas estão resolvidos — ou diz qual asserção ainda falha, para que a correção possa ser reparada. Nunca marque um incidente como resolvido antes de uma re-verificação estar verde;parké para uma correção que foi mesclada e ainda não está ativa,confirm_resolutionpara uma que a fonte já fechou.commento que você encontrou no caminho. Uma correção silenciosa deixa a próxima pessoa sem nada.
Ranking: o que vigiar, em ordem
- A rota de readiness mais profunda do processo de produção, asserindo um campo que muda quando uma dependência está indisponível. Nunca um
{"ok": true}/{"status": "ok"}hardcoded — isso é liveness, e não pode ficar vermelho para a falha que importa. - Uma sonda de readiness que a plataforma já declara:
/readyz,/health/ready, umhealthcheckdo compose, umreadinessProbedo Kubernetes, um caminho de saúde do fly/railway/render. Espelhe de fora com o mesmo caminho e porta. - Nenhum servidor → um heartbeat por job agendado. Silêncio é o sinal.
- Nenhum servidor e nenhum job agendado →
watcher add host --id host(carga, memória, espaço livre, opcionalmente um arquivo pid; nada instalado) e prove. Apenas nenhum servidor, nenhum job agendado e nenhum host para vigiar →watcher doctor --no-runtime-surface --json, reporteNO_RUNTIME_SURFACE, pare. Uma fonte configurada sempre vence sobre--no-runtime-surface. Nunca invente uma fonte. - Depois os sistemas que já existem — Sentry, Grafana, Netdata, um remetente de entrada — vinculados a credencial. Uma credencial ausente é uma omissão nomeada com um código e um
next, nunca um passe silencioso.
Silêncio primeiro (a varredura perde estes)
Leia o código para estas seis classes; cada uma se torna um watcher add heartbeat ou um watcher add webhook --stale-after:
- Schedules gerados em tempo de instalação — heredocs em
install.sh/setup.*que escrevem um plist, unit ou crontab,launchctl load,systemctl enable, escritascrontab. - launchd/systemd/cron criados em tempo de execução — templates
.plist,OnCalendar=,StartInterval,KeepAlive,node-cron,schedule(,setIntervalem torno de um job, celery beat. Um heartbeat por job (--every/--schedule+--grace); N jobs significam N fontes. - Trabalhadores de fila pull —
worker,queue,bullmq,sqs,claim,lease,dequeue. Falha é profundidade de fila ou nenhum progresso, não uma porta: pingue um heartbeat no caminho de sucesso, ou envie uma entradachecks.listcomstate: failing. - Staleness na própria configuração do produto —
staleAfterSec,presenceTtlSec,maxAge,ttl,refreshInterval,lastRunAt. Reforce a mesma janela:watcher add webhook --id <sender> --token-env <ENV> --stale-after-sec <sec>. - Gravabilidade do diretório de estado —
mkdir,state/,data/, sqlite, arquivos de lock. Pingue apenas após uma escrita bem-sucedida; um simplestest -wnão é uma fonte. - Endpoints de dependência mantidos como constantes de código —
process.env.X ?? "https://…", literais host:porta, bases gRPC/ClickHouse/LLM.watcher add http-probe(HTTPS GET) ouwatcher add tcp-probe --host --port, com--found-ata constante.
Coloque o ping no caminho de sucesso do job — POST /ingest/heartbeat/<id>, com /fail para uma execução falha e /<exit-code> para um específico. watcher add heartbeat cunha o token em .watcher/.env (0600) quando a variável nomeada ainda não tem valor, ou escreve o --secret-value que você passar, e imprime as linhas exatas ($NIGHTLY_TOKEN abaixo é o nome que ele escolhe para --id nightly; um --token-env que você passar é a variável que ele imprime em vez disso):
set -a; . .watcher/.env
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>?token=$NIGHTLY_TOKEN" # após uma execução bem-sucedida
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>/fail?token=$NIGHTLY_TOKEN" # após uma execução falha
curl -fsS -m 10 "${WATCHER_URL:-http://127.0.0.1:7717}/ingest/heartbeat/<id>/<exit-code>?token=$NIGHTLY_TOKEN" # com o código de saída do job
Um coletor que não é um job envia sua lista completa: POST /ingest/<source> com {"checks":{"list":[{"id":"…","service":"…","state":"ok","lastRunAt":"…"}]}} — a forma completa e os estados estão em watcher skills sources.
Se uma rota de saúde retorna apenas um literal, encontre uma mais profunda ou adicione uma: GET /readyz → 200 {"db":{"ok":true}} quando a dependência responde, 503 {"db":{"ok":false}} quando não responde. Editar o projeto do usuário é permitido — você é o agente escrevendo o arquivo. Depois asserir no campo que muda, nunca em {"ok":true}.
Regras
- Nunca aponte uma fonte para o próprio
/healthzdo Watcher —source.self-proberejeita. - Uma fonte deve asserir algo que possa mudar. Um 200 simples é apenas liveness (
source.bare-200);watcher doctor --live --proveé o portão,--found-até um aviso (source.untraced). - Segredos são nomes de variáveis de ambiente na configuração e valores apenas em
.watcher/.env. Nunca leia valores de.envdurante a varredura, nunca cometa.watcher/.envou.watcher/data/. - Um canal funcional vale mais que cinco configurados. Prove com
watcher test-alert. Nenhuma credencial?watcher add channel file --id locale prove: o arquivo é um canal real, e uma credencial ausente é uma omissão nomeada, nunca um passe. - Reporte o que você provou — fontes adicionadas, saída de comando e toda omissão nomeada. Se o agente não puder completar um degrau (credencial ausente, nenhuma zona), diga qual e por que em uma linha.
Mais detalhe, quando um passo precisar: watcher skills discover (catálogo completo de sinais e a lista de verificação de silêncio), watcher skills sources (cada tipo, flags, exemplos), watcher skills channels, watcher skills hosting, watcher skills cloudflare, watcher skills netdata, watcher skills operate (ferramentas MCP e triagem de incidentes).