BREAKING CHANGE(daemon): Refactor daemon and service management: remove IPC auto-spawn, add TspmServiceManager, tighten IPC/client/CLI behavior and tests

This commit is contained in:
2025-08-28 15:52:29 +00:00
parent 8e3cfb624b
commit e73f4acd63
38 changed files with 810 additions and 580 deletions

View File

@@ -36,9 +36,9 @@ export class TspmIpcClient {
if (!daemonRunning) {
throw new Error(
'TSPM daemon is not running.\n\n' +
'To start the daemon, run one of:\n' +
' tspm daemon start - Start daemon for this session\n' +
' tspm enable - Enable daemon as system service (recommended)\n'
'To start the daemon, run one of:\n' +
' tspm daemon start - Start daemon for this session\n' +
' tspm enable - Enable daemon as system service (recommended)\n',
);
}
@@ -59,20 +59,20 @@ export class TspmIpcClient {
heartbeatInterval: 5000,
heartbeatTimeout: 20000,
heartbeatInitialGracePeriodMs: 10000,
heartbeatThrowOnTimeout: false // Don't throw, emit events instead
heartbeatThrowOnTimeout: false, // Don't throw, emit events instead
});
// Connect to the daemon
try {
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);
@@ -117,19 +117,22 @@ export class TspmIpcClient {
throw error;
}
}
/**
* Subscribe to log updates for a specific process
*/
public async subscribe(processId: string, handler: (log: any) => void): Promise<void> {
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
*/
@@ -137,7 +140,7 @@ export class TspmIpcClient {
if (!this.ipcClient || !this.isConnected) {
throw new Error('Not connected to daemon');
}
const topic = `logs.${processId}`;
await this.ipcClient.unsubscribe(`topic:${topic}`);
}
@@ -160,7 +163,7 @@ export class TspmIpcClient {
// Check if process is running
try {
process.kill(pid, 0);
// PID is alive, daemon is running
// Socket check is advisory only - the connect retry will handle transient socket issues
try {
@@ -184,8 +187,6 @@ export class TspmIpcClient {
}
}
/**
* Stop the daemon
*/