import { type FleetConfig } from '../config.js'; import type { WatchdogReport, WatchdogRoleStatus } from './report.js'; import { type RunListEntry } from './store.js'; export interface WatchdogRoleFinding { watchdog: string; status: WatchdogRoleStatus; reason: string; } /** Shared configured-or-surviving-history addressability rule. */ export declare function watchdogAddressable(name: string, configured: readonly string[], historyExists?: (validName: string) => boolean): boolean; /** * Needs-attention integration: worst current finding per role across * every configured watchdog, for FleetQueryService.status() to fold into a * role's problems. An `error`-status report carries no role evidence (the run * itself failed) so it's skipped outright, matching alerts.ts's * reconcileLedger rule. Healthy/idle findings (rank 0) never surface here — * only actionable anomalies do. "Worst" is decided by WATCHDOG_STATUS_RANK; * ties keep whichever watchdog was seen first. Takes `latestReport` as a * parameter (rather than importing store.ts directly) so it stays a pure, * disk-free function for unit testing; runtime.ts wires the real store. * * Skips disabled watchdogs: a watchdog turned off in config * still has its last stored report sitting on disk, and without this check * that stale report's findings would pin roles in "Needs attention" forever * — an operator disabling a noisy/broken watchdog has no way to make the * findings it already produced go away. */ export declare function buildWatchdogFindings(cfg: { watchdogs: Array<{ name: string; enabled: boolean; }>; }, latestReport: (name: string) => WatchdogReport | undefined): Map; /** * Memoizes a findings-map builder with a short TTL (mirrors runtime.ts's * cachedConfigProvider pattern). status() calls its injected watchdogFindings * provider once per role, so an unmemoized thunk turns a single list() sweep * into O(roles x watchdogs) disk reads, repeated every 1-3s of console * polling. `now` is injectable (defaults to Date.now) so the TTL boundary is * unit-testable without real timers. */ export declare function cachedWatchdogFindingsProvider(build: () => Map, ttlMs: number, now?: () => number): () => Map; export interface WatchdogSummary { name: string; enabled: boolean; heldDown: boolean; heldSince: string | null; intervalMs: number; coordinator: string; watch: string[]; lastRunAt: string | null; nextRunAt: string | null; latest: RunListEntry | null; } /** * Read-only view over watchdog config + on-disk scheduler state + stored * reports, for the authenticated web console. Never mutates: `list()` reads * config plus `readSchedulerState`/`listRuns` (both already tolerant of a * corrupt/missing state.json or an empty/nonexistent reports dir, per * store.ts and scheduler.ts), and `reports()`/`report()` reject unknown * watchdog names before touching disk. */ export declare class WatchdogQueryService { private readonly cfgProvider; constructor(cfgProvider: () => FleetConfig); list(): { watchdogs: WatchdogSummary[]; }; reports(name: string, limit?: number): { runs: RunListEntry[]; }; report(name: string, runId: string): WatchdogReport; /** * Known means: configured today, OR a store directory already exists for a * watchdog that used to be configured (its history should stay readable). * The regex check runs before any filesystem lookup so a hostile `name` * (path separators, '..', etc.) can never reach `join(watchdogsRoot(), name)`. */ private requireKnown; }