import { readFile } from "node:fs/promises"; import { resolve } from "node:path"; import type { RuntimeEvent } from "../telemetry/runtime-events.js"; type RuntimeEventFilter = { sessionId?: string; types?: string[]; severities?: Array; limit?: number; }; function safeParseLine(line: string): RuntimeEvent | undefined { const trimmed = line.trim(); if (!trimmed) { return undefined; } try { const parsed = JSON.parse(trimmed) as unknown; if (!parsed || typeof parsed !== "object") { return undefined; } const record = parsed as Partial; if ( typeof record.id !== "string" || typeof record.timestamp !== "string" || typeof record.type !== "string" || typeof record.severity !== "string" || typeof record.message !== "string" ) { return undefined; } return record as RuntimeEvent; } catch { return undefined; } } export async function readRuntimeEvents(logPath: string): Promise { const absolutePath = resolve(logPath); let content = ""; try { content = await readFile(absolutePath, "utf8"); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") { return []; } throw error; } const parsed: RuntimeEvent[] = []; const lines = content.split(/\r?\n/); for (const line of lines) { const event = safeParseLine(line); if (event) { parsed.push(event); } } parsed.sort((left, right) => left.timestamp.localeCompare(right.timestamp)); return parsed; } export function filterRuntimeEvents( events: readonly RuntimeEvent[], filter: RuntimeEventFilter, ): RuntimeEvent[] { const filtered: RuntimeEvent[] = []; const types = filter.types ? new Set(filter.types) : undefined; const severities = filter.severities ? new Set(filter.severities) : undefined; for (const event of events) { if (filter.sessionId && event.sessionId !== filter.sessionId) { continue; } if (types && !types.has(event.type)) { continue; } if (severities && !severities.has(event.severity)) { continue; } filtered.push(event); } if (!filter.limit || filter.limit < 1 || filtered.length <= filter.limit) { return filtered; } return filtered.slice(-filter.limit); }