/** * `celilo alerts list` — what is currently wrong, and what is already accounted for. * * Thin adapter (Rule 10.5): read, delegate, format, return. The ordering, * wording, and the "explained by something upstream" column all live in * services/alerting/format.ts where they are unit-tested. */ import { getDb } from '../../db/client'; import { people } from '../../db/schema'; import { renderAlertTable, toAlertRow } from '../../services/alerting/format'; import { listMonitors } from '../../services/alerting/monitors'; import { policyForAlert } from '../../services/alerting/notify-deps'; import { loadAllLiveAlerts } from '../../services/alerting/store'; import type { CommandResult } from '../types'; export async function handleAlertsList( _args: string[], flags: Record = {}, ): Promise { const db = getDb(); const now = new Date(); const live = loadAllLiveAlerts(db); if (flags.json) { const monitorsById = new Map(listMonitors(db).map((m) => [m.id, m.target])); const peopleById = new Map( db .select() .from(people) .all() .map((p) => [p.id, p.name]), ); return { success: true, message: JSON.stringify( live.map((alert) => ({ // The row's own identifier, needed by anything that acts on ONE // alert. `key` identifies the condition and is stable across // restarts; `id` identifies this occurrence of it. id: alert.id, key: alert.key, state: alert.state, severity: alert.severity, // Who owns this alert and who it would page. Without these an // operator told "1 live alert has no escalation policy" had no way to // find out which one, from any command celilo offered (#481). monitor: monitorsById.get(alert.monitorId) ?? null, escalationPolicy: policyForAlert(db, alert)?.name ?? null, escalationStep: alert.escalationStep, nextEscalationAt: alert.nextEscalationAt, firstFiredAt: alert.firstFiredAt, lastSeenAt: alert.lastSeenAt, suppressed: alert.state === 'suppressed', suppressedByAlertId: alert.suppressedByAlertId, suppressedByWindowId: alert.suppressedByWindowId, awaitingConfirmation: alert.awaitingConfirmation, // Resolved to a name. The column is a `people` foreign key, and a // bare id tells a reader nothing about who acknowledged their alert. ackedBy: alert.ackedBy ? (peopleById.get(alert.ackedBy) ?? alert.ackedBy) : null, ackedAt: alert.ackedAt, silencedUntil: alert.silencedUntil, message: alert.message, })), null, 2, ), rawOutput: true, }; } const table = renderAlertTable(live.map((alert) => toAlertRow(alert, now))); console.log(''); console.log(table); console.log(''); const firing = live.filter((a) => a.state === 'firing').length; const suppressed = live.filter((a) => a.state === 'suppressed').length; return { success: true, message: live.length === 0 ? 'No alerts' : `${firing} firing, ${suppressed} suppressed, ${live.length} live`, }; }