import Link from "next/link"; import type { Metadata } from "next"; import { Chip, Empty, HealthPill, PageHeader, Panel, Stat, Table, Td } from "@/components/ui"; import { api } from "@/lib/api"; import { fmtBytes, fmtInt, fmtMs, fmtPct, relTime, utcDateTime, utcTime } from "@/lib/format"; export const dynamic = "force-dynamic"; export const metadata: Metadata = { title: "Connector health", description: "Live health of every connector family and sensor: success rate, latency, HTTP codes, noise ratios, queue depth and engine heartbeat." }; /** A heartbeat older than two minutes means the scheduler stopped publishing. */ function heartbeatStale(at: string | undefined): boolean { return Boolean(at) && Date.now() - new Date(at as string).getTime() > 120_000; } export default async function HealthPage() { const h = await api.health(); const byHealth = Object.fromEntries((h?.sensors_by_health ?? []).map((x) => [x.health, x.n])); const tp = h?.throughput; const eng = h?.engine ?? null; const engStale = heartbeatStale(eng?.at); const healthy = byHealth.UP ?? 0; const degraded = (byHealth.DEGRADED ?? 0) + (byHealth.RATE_LIMITED ?? 0); const failed = byHealth.ERROR ?? 0; return ( <>
500 ? "warn" : undefined} hint="sensors due now" />
engine {eng ? (engStale ? "STALE" : "LIVE") : "NO HEARTBEAT"} {eng ? ( <> in flight {eng.inflight ?? 0} / {eng.concurrency ?? "—"} due {fmtInt(eng.due)} busy hosts {eng.busyHosts?.length ?? 0} circuit open {eng.circuitOpen?.length ?? 0} {eng.version && v{eng.version}} {eng.at && heartbeat {utcTime(eng.at)} UTC · {relTime(eng.at)}} ) : ( the scheduler publishes a heartbeat to Redis every few seconds; none is visible right now. )} WS clients {fmtInt(h?.live.clients ?? 0)} · {fmtInt(h?.live.published ?? 0)} published since start
{(h?.connectors ?? []).map((c) => ( {c.connector}}>
Success rate
{fmtPct(c.success_rate ?? null, 1)}
Runs 24 h
{fmtInt(c.runs_24h)}
Errors 24 h
{fmtInt(c.errors_24h)}
Avg latency
{fmtMs(c.avg_latency_ms)}
Changes / events 24 h
{fmtInt(c.changes_24h)} / {fmtInt(c.events_24h)}
Last success
{c.last_success_at ? relTime(c.last_success_at) : "—"}
{Object.entries(c.http_codes ?? {}).sort((a, b) => b[1] - a[1]).map(([code, n]) => ( {code} · {n} ))}
{c.last_error &&

{c.last_error}

}
))} {!h?.connectors.length && Health rollups appear after the first minute of engine activity.}
{h?.sensors_by_connector?.length ? ( {h.sensors_by_connector.map((c) => ( ))}
{c.connector} {fmtInt(c.sensors)} {fmtInt(c.up)} {fmtPct(c.sensors ? c.up / c.sensors : null)} {fmtMs(c.avg_latency_ms)}
) : ( )}
{h?.sensors_by_status?.length ? ( {h.sensors_by_status.map((s) => ( ))}
{fmtInt(s.n)}
) : ( )}

PENDING → VALIDATED → ACTIVE is the sensor lifecycle; DISABLED sensors are kept for evidence but never scheduled.

{h?.degraded_sensors.length ? ( {h.degraded_sensors.map((s) => ( ))}
{s.name}
{s.url}
{s.source_id} {s.consecutive_errors} {s.last_status ?? "—"} {s.last_error} {s.last_check_at ? relTime(s.last_check_at) : "—"}
) : ( All sensors healthy. )}
{h?.noisy_sensors.length ? ( {h.noisy_sensors.map((s) => ( ))}
{s.name} {s.source_id} {s.raw_changes} {s.meaningful_changes} 0.9 ? "text-warn" : ""}>{s.noise_ratio !== null ? fmtPct(Number(s.noise_ratio), 1) : "—"}
) : ( Noise ratios appear once sensors have ≥ 5 raw changes. )}
{h?.top_failing_domains?.length ? ( {h.top_failing_domains.map((f) => ( ))}
{f.host} {fmtInt(f.failures)} {f.last_error ?? "—"}
) : ( No failing domains right now. )}
{h?.slowest_sensors?.length ? ( {h.slowest_sensors.map((s) => ( ))}
{s.name} {s.source_id} {s.connector} 10_000 ? "text-warn" : ""}>{fmtMs(s.avg_latency_ms)}
) : ( )}
{h?.daily.length ? ( {h.daily.map((d) => ( ))}
{String(d.day)} {fmtInt(Number(d.checks))} {fmtInt(Number(d.not_modified))} {fmtBytes(Number(d.bytes))} {fmtInt(Number(d.raw_changes))} {fmtInt(Number(d.events))} {fmtInt(Number(d.silent_events))} {fmtInt(Number(d.errors))} {fmtInt(Number(d.llm_calls))} {fmtInt(Number(d.llm_input_tokens))} / {fmtInt(Number(d.llm_output_tokens))}
) : ( )}

Rollups refresh every minute from sensor runs; raw counters are in /api/v1/health/connectors and /api/metrics (Prometheus). Operators: /ops.

); }