feat(daemon): Add real-time log streaming and pub/sub: daemon publishes per-process logs, IPC client subscribe/unsubscribe, CLI --follow streaming, and sequencing for logs
This commit is contained in:
@@ -44,13 +44,33 @@ export class TspmIpcClient {
|
||||
this.ipcClient = plugins.smartipc.SmartIpc.createClient({
|
||||
id: 'tspm-cli',
|
||||
socketPath: this.socketPath,
|
||||
heartbeat: false, // Disable heartbeat for now
|
||||
clientId: `cli-${process.pid}`,
|
||||
connectRetry: {
|
||||
enabled: true,
|
||||
initialDelay: 100,
|
||||
maxDelay: 2000,
|
||||
maxAttempts: 30,
|
||||
totalTimeout: 15000,
|
||||
},
|
||||
registerTimeoutMs: 8000,
|
||||
heartbeat: true,
|
||||
heartbeatInterval: 5000,
|
||||
heartbeatTimeout: 20000,
|
||||
heartbeatInitialGracePeriodMs: 10000,
|
||||
heartbeatThrowOnTimeout: false // Don't throw, emit events instead
|
||||
});
|
||||
|
||||
// Connect to the daemon
|
||||
try {
|
||||
await this.ipcClient.connect();
|
||||
await this.ipcClient.connect({ waitForReady: true });
|
||||
this.isConnected = true;
|
||||
|
||||
// Handle heartbeat timeouts gracefully
|
||||
this.ipcClient.on('heartbeatTimeout', () => {
|
||||
console.warn('Heartbeat timeout detected, connection may be degraded');
|
||||
this.isConnected = false;
|
||||
});
|
||||
|
||||
console.log('Connected to TSPM daemon');
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to daemon:', error);
|
||||
@@ -109,6 +129,30 @@ export class TspmIpcClient {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to log updates for a specific process
|
||||
*/
|
||||
public async subscribe(processId: string, handler: (log: any) => void): Promise<void> {
|
||||
if (!this.ipcClient || !this.isConnected) {
|
||||
throw new Error('Not connected to daemon');
|
||||
}
|
||||
|
||||
const topic = `logs.${processId}`;
|
||||
await this.ipcClient.subscribe(`topic:${topic}`, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from log updates for a specific process
|
||||
*/
|
||||
public async unsubscribe(processId: string): Promise<void> {
|
||||
if (!this.ipcClient || !this.isConnected) {
|
||||
throw new Error('Not connected to daemon');
|
||||
}
|
||||
|
||||
const topic = `logs.${processId}`;
|
||||
await this.ipcClient.unsubscribe(`topic:${topic}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the daemon is running
|
||||
@@ -176,18 +220,15 @@ export class TspmIpcClient {
|
||||
|
||||
console.log(`Started daemon process with PID: ${daemonProcess.pid}`);
|
||||
|
||||
// Wait for daemon to be ready (check for socket file)
|
||||
const maxWaitTime = 10000; // 10 seconds
|
||||
const startTime = Date.now();
|
||||
|
||||
while (Date.now() - startTime < maxWaitTime) {
|
||||
if (await this.isDaemonRunning()) {
|
||||
return;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
// Wait for daemon to be ready using SmartIPC's helper
|
||||
try {
|
||||
await plugins.smartipc.SmartIpc.waitForServer({
|
||||
socketPath: this.socketPath,
|
||||
timeoutMs: 15000,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Daemon failed to start: ${error.message}`);
|
||||
}
|
||||
|
||||
throw new Error('Daemon failed to start within timeout period');
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user