/* * Veskslop daemon — persistent action log. * * Writes one JSON line per event: triggers, commands, replies, and errors. * The file rotates at a size limit. Secret-looking strings are redacted. */ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync } from "node:fs"; import { dirname } from "node:path"; import { redactValue } from "./communication.js"; export const ACTION_LOG_MAX_BYTES = 5 * 1024 * 1024; export class ActionLog { constructor( private readonly file = process.env.VESKSLOP_ACTION_LOG ?? ".veskslop-actions.jsonl", private readonly maxBytes = Number(process.env.VESKSLOP_ACTION_LOG_MAX_BYTES ?? ACTION_LOG_MAX_BYTES) ) { try { mkdirSync(dirname(this.file), { recursive: true }); } catch { // the current directory works } } append(entry: Record & { type: string }) { const line = JSON.stringify(redactValue({ timestamp: new Date().toISOString(), ...entry })); try { if (existsSync(this.file) && statSync(this.file).size + line.length + 1 > this.maxBytes) { renameSync(this.file, `${this.file}.1`); } appendFileSync(this.file, line + "\n"); } catch (error) { console.error("[action-log] write failed:", error); } } read(limit = 100): unknown[] { try { if (!existsSync(this.file)) return []; const lines = readFileSync(this.file, "utf8").split("\n").filter(Boolean); return lines.slice(-limit).map(line => { try { return JSON.parse(line); } catch { return { type: "unparsed", line }; } }); } catch (error) { console.error("[action-log] read failed:", error); return []; } } }