import * as plugins from '../../../plugins.js'; import { tspmIpcClient } from '../../../classes.ipcclient.js'; import type { CliArguments } from '../../types.js'; import { registerIpcCommand } from '../../registration/index.js'; import { getBool, getNumber } from '../../helpers/argv.js'; import { formatLog } from '../../helpers/formatting.js'; import { withStreamingLifecycle } from '../../helpers/lifecycle.js'; export function registerLogsCommand(smartcli: plugins.smartcli.Smartcli) { registerIpcCommand(smartcli, 'logs', async (argvArg: CliArguments) => { const id = argvArg._[1]; if (!id) { console.error('Error: Please provide a process ID'); console.log('Usage: tspm logs [options]'); console.log('\nOptions:'); console.log(' --lines Number of lines to show (default: 50)'); console.log(' --follow Stream logs in real-time (like tail -f)'); return; } const lines = getNumber(argvArg, 'lines', 50); const follow = getBool(argvArg, 'follow', 'f'); const response = await tspmIpcClient.request('getLogs', { id, lines }); if (!follow) { // One-shot mode - auto-disconnect handled by registerIpcCommand console.log(`Logs for process: ${id} (last ${lines} lines)`); console.log('─'.repeat(60)); for (const log of response.logs) { const timestamp = new Date(log.timestamp).toLocaleTimeString(); const prefix = log.type === 'stdout' ? '[OUT]' : log.type === 'stderr' ? '[ERR]' : '[SYS]'; console.log(`${timestamp} ${prefix} ${log.message}`); } return; } // Streaming mode console.log(`Logs for process: ${id} (streaming...)`); console.log('─'.repeat(60)); let lastSeq = 0; for (const log of response.logs) { const timestamp = new Date(log.timestamp).toLocaleTimeString(); const prefix = log.type === 'stdout' ? '[OUT]' : log.type === 'stderr' ? '[ERR]' : '[SYS]'; console.log(`${timestamp} ${prefix} ${log.message}`); if (log.seq !== undefined) lastSeq = Math.max(lastSeq, log.seq); } await withStreamingLifecycle( async () => { await tspmIpcClient.subscribe(id, (log: any) => { if (log.seq !== undefined && log.seq <= lastSeq) return; if (log.seq !== undefined && log.seq > lastSeq + 1) { console.log(`[WARNING] Log gap detected: expected seq ${lastSeq + 1}, got ${log.seq}`); } const timestamp = new Date(log.timestamp).toLocaleTimeString(); const prefix = log.type === 'stdout' ? '[OUT]' : log.type === 'stderr' ? '[ERR]' : '[SYS]'; console.log(`${timestamp} ${prefix} ${log.message}`); if (log.seq !== undefined) lastSeq = log.seq; }); }, async () => { console.log('\n\nStopping log stream...'); try { await tspmIpcClient.unsubscribe(id); } catch {} try { await tspmIpcClient.disconnect(); } catch {} } ); }, { actionLabel: 'get logs', keepAlive: (argv) => getBool(argv, 'follow', 'f') }); }