import * as plugins from '../plugins.js'; import * as paths from '../paths.js'; import type { IProcessLog } from '../shared/protocol/ipc.types.js'; import type { ProcessId } from '../shared/protocol/id.js'; const smartfs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode()); /** * Manages persistent log storage for processes */ export class LogPersistence { private logsDir: string; constructor() { this.logsDir = plugins.path.join(paths.tspmDir, 'logs'); } /** * Get the log file path for a process */ private getLogFilePath(processId: ProcessId): string { return plugins.path.join(this.logsDir, `process-${processId}.json`); } /** * Ensure the logs directory exists */ private async ensureLogsDir(): Promise { await smartfs.directory(this.logsDir).create(); } /** * Save logs to disk */ public async saveLogs(processId: ProcessId, logs: IProcessLog[]): Promise { await this.ensureLogsDir(); const filePath = this.getLogFilePath(processId); // Write logs as JSON await smartfs.file(filePath).encoding('utf8').write(JSON.stringify(logs, null, 2)); } /** * Load logs from disk */ public async loadLogs(processId: ProcessId): Promise { const filePath = this.getLogFilePath(processId); try { const exists = await smartfs.file(filePath).exists(); if (!exists) { return []; } const content = await smartfs.file(filePath).encoding('utf8').read() as string; const logs = JSON.parse(content) as IProcessLog[]; // Convert date strings back to Date objects return logs.map(log => ({ ...log, timestamp: new Date(log.timestamp) })); } catch (error) { console.error(`Failed to load logs for process ${processId}:`, error); return []; } } /** * Delete logs from disk after loading */ public async deleteLogs(processId: ProcessId): Promise { const filePath = this.getLogFilePath(processId); try { const exists = await smartfs.file(filePath).exists(); if (exists) { await smartfs.file(filePath).delete(); } } catch (error) { console.error(`Failed to delete logs for process ${processId}:`, error); } } /** * Calculate approximate memory size of logs in bytes */ public static calculateLogMemorySize(logs: IProcessLog[]): number { // Estimate based on JSON string size // This is an approximation but good enough for our purposes return JSON.stringify(logs).length; } /** * Clean up old log files (for maintenance) */ public async cleanupOldLogs(): Promise { try { await this.ensureLogsDir(); const entries = await smartfs.directory(this.logsDir).list(); const files = entries.filter(e => e.name.endsWith('.json')); for (const entry of files) { const filePath = plugins.path.join(this.logsDir, entry.name); const stats = await smartfs.file(filePath).stat(); // Delete files older than 7 days const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24); if (ageInDays > 7) { await smartfs.file(filePath).delete(); } } } catch (error) { console.error('Failed to cleanup old logs:', error); } } }