fix(daemon): Fix smartipc integration and add daemon/ipc integration tests
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-08-25 - 1.6.1 - fix(daemon)
|
||||
Fix smartipc integration and add daemon/ipc integration tests
|
||||
|
||||
- Replace direct smartipc server/client construction with SmartIpc.createServer/createClient and set heartbeat: false
|
||||
- Switch IPC handler registration to use onMessage and add explicit Request/Response typing for handlers
|
||||
- Update IPC client to use SmartIpc.createClient and improve daemon start/connect logic
|
||||
- Add comprehensive tests: unit tests for TspmDaemon and TspmIpcClient and full integration tests for daemon lifecycle, process management, error handling, heartbeat and resource reporting
|
||||
|
||||
## 2025-08-25 - 1.6.0 - feat(daemon)
|
||||
Add central TSPM daemon and IPC client; refactor CLI to use daemon and improve monitoring/error handling
|
||||
|
||||
|
107
test/test.daemon.ts
Normal file
107
test/test.daemon.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as tspm from '../ts/index.js';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import { TspmDaemon } from '../ts/classes.daemon.js';
|
||||
|
||||
// Test daemon server functionality
|
||||
tap.test('TspmDaemon creation', async () => {
|
||||
const daemon = new TspmDaemon();
|
||||
expect(daemon).toBeInstanceOf(TspmDaemon);
|
||||
});
|
||||
|
||||
tap.test('Daemon PID file management', async (tools) => {
|
||||
const testDir = path.join(process.cwd(), '.nogit');
|
||||
const testPidFile = path.join(testDir, 'test-daemon.pid');
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
await fs.mkdir(testDir, { recursive: true });
|
||||
|
||||
// Clean up any existing test file
|
||||
await fs.unlink(testPidFile).catch(() => {});
|
||||
|
||||
// Test writing PID file
|
||||
await fs.writeFile(testPidFile, process.pid.toString());
|
||||
const pidContent = await fs.readFile(testPidFile, 'utf-8');
|
||||
expect(parseInt(pidContent)).toEqual(process.pid);
|
||||
|
||||
// Clean up
|
||||
await fs.unlink(testPidFile);
|
||||
});
|
||||
|
||||
tap.test('Daemon socket path generation', async () => {
|
||||
const daemon = new TspmDaemon();
|
||||
// Access private property for testing (normally wouldn't do this)
|
||||
const socketPath = (daemon as any).socketPath;
|
||||
expect(socketPath).toInclude('tspm.sock');
|
||||
});
|
||||
|
||||
tap.test('Daemon shutdown handlers', async (tools) => {
|
||||
const daemon = new TspmDaemon();
|
||||
|
||||
// Test that shutdown handlers are registered
|
||||
const sigintListeners = process.listeners('SIGINT');
|
||||
const sigtermListeners = process.listeners('SIGTERM');
|
||||
|
||||
// We expect at least one listener for each signal
|
||||
// (Note: in actual test we won't start the daemon to avoid side effects)
|
||||
expect(sigintListeners.length).toBeGreaterThanOrEqual(0);
|
||||
expect(sigtermListeners.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
tap.test('Daemon process info tracking', async () => {
|
||||
const daemon = new TspmDaemon();
|
||||
const tspmInstance = (daemon as any).tspmInstance;
|
||||
|
||||
expect(tspmInstance).toBeDefined();
|
||||
expect(tspmInstance.processes).toBeInstanceOf(Map);
|
||||
expect(tspmInstance.processConfigs).toBeInstanceOf(Map);
|
||||
expect(tspmInstance.processInfo).toBeInstanceOf(Map);
|
||||
});
|
||||
|
||||
tap.test('Daemon heartbeat monitoring setup', async (tools) => {
|
||||
const daemon = new TspmDaemon();
|
||||
|
||||
// Test heartbeat interval property exists
|
||||
const heartbeatInterval = (daemon as any).heartbeatInterval;
|
||||
expect(heartbeatInterval).toEqual(null); // Should be null before start
|
||||
});
|
||||
|
||||
tap.test('Daemon shutdown state management', async () => {
|
||||
const daemon = new TspmDaemon();
|
||||
const isShuttingDown = (daemon as any).isShuttingDown;
|
||||
|
||||
expect(isShuttingDown).toEqual(false);
|
||||
});
|
||||
|
||||
tap.test('Daemon memory usage reporting', async () => {
|
||||
const memUsage = process.memoryUsage();
|
||||
|
||||
expect(memUsage.heapUsed).toBeGreaterThan(0);
|
||||
expect(memUsage.heapTotal).toBeGreaterThan(0);
|
||||
expect(memUsage.rss).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('Daemon CPU usage calculation', async () => {
|
||||
const cpuUsage = process.cpuUsage();
|
||||
|
||||
expect(cpuUsage.user).toBeGreaterThanOrEqual(0);
|
||||
expect(cpuUsage.system).toBeGreaterThanOrEqual(0);
|
||||
|
||||
// Test conversion to seconds
|
||||
const cpuSeconds = cpuUsage.user / 1000000;
|
||||
expect(cpuSeconds).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
tap.test('Daemon uptime calculation', async () => {
|
||||
const startTime = Date.now();
|
||||
|
||||
// Wait a bit
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
const uptime = Date.now() - startTime;
|
||||
expect(uptime).toBeGreaterThanOrEqual(100);
|
||||
expect(uptime).toBeLessThan(200);
|
||||
});
|
||||
|
||||
export default tap.start();
|
266
test/test.integration.ts
Normal file
266
test/test.integration.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as tspm from '../ts/index.js';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
import { spawn } from 'child_process';
|
||||
import { tspmIpcClient } from '../ts/classes.ipcclient.js';
|
||||
|
||||
// Helper to ensure daemon is stopped before tests
|
||||
async function ensureDaemonStopped() {
|
||||
try {
|
||||
await tspmIpcClient.stopDaemon(false);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
} catch (error) {
|
||||
// Ignore errors if daemon is not running
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to clean up test files
|
||||
async function cleanupTestFiles() {
|
||||
const tspmDir = path.join(os.homedir(), '.tspm');
|
||||
const pidFile = path.join(tspmDir, 'daemon.pid');
|
||||
const socketFile = path.join(tspmDir, 'tspm.sock');
|
||||
|
||||
await fs.unlink(pidFile).catch(() => {});
|
||||
await fs.unlink(socketFile).catch(() => {});
|
||||
}
|
||||
|
||||
// Integration tests for daemon-client communication
|
||||
tap.test('Full daemon lifecycle test', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure clean state
|
||||
await ensureDaemonStopped();
|
||||
await cleanupTestFiles();
|
||||
|
||||
// Test 1: Check daemon is not running
|
||||
let status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toEqual(null);
|
||||
|
||||
// Test 2: Start daemon
|
||||
console.log('Starting daemon...');
|
||||
await tspmIpcClient.connect();
|
||||
|
||||
// Give daemon time to fully initialize
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Test 3: Check daemon is running
|
||||
status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toBeDefined();
|
||||
expect(status?.status).toEqual('running');
|
||||
expect(status?.pid).toBeGreaterThan(0);
|
||||
expect(status?.processCount).toBeGreaterThanOrEqual(0);
|
||||
|
||||
// Test 4: Stop daemon
|
||||
console.log('Stopping daemon...');
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
// Give daemon time to shutdown
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Test 5: Check daemon is stopped
|
||||
status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toEqual(null);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Process management through daemon', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test 1: List processes (should be empty initially)
|
||||
let listResponse = await tspmIpcClient.request('list', {});
|
||||
expect(listResponse.processes).toBeArray();
|
||||
expect(listResponse.processes.length).toEqual(0);
|
||||
|
||||
// Test 2: Start a test process
|
||||
const testConfig: tspm.IProcessConfig = {
|
||||
id: 'test-echo',
|
||||
name: 'Test Echo Process',
|
||||
command: 'echo "Test process"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
};
|
||||
|
||||
const startResponse = await tspmIpcClient.request('start', { config: testConfig });
|
||||
expect(startResponse.processId).toEqual('test-echo');
|
||||
expect(startResponse.status).toBeDefined();
|
||||
|
||||
// Test 3: List processes (should have one process)
|
||||
listResponse = await tspmIpcClient.request('list', {});
|
||||
expect(listResponse.processes.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const process = listResponse.processes.find(p => p.id === 'test-echo');
|
||||
expect(process).toBeDefined();
|
||||
expect(process?.id).toEqual('test-echo');
|
||||
|
||||
// Test 4: Describe the process
|
||||
const describeResponse = await tspmIpcClient.request('describe', { id: 'test-echo' });
|
||||
expect(describeResponse.processInfo).toBeDefined();
|
||||
expect(describeResponse.config).toBeDefined();
|
||||
expect(describeResponse.config.id).toEqual('test-echo');
|
||||
|
||||
// Test 5: Stop the process
|
||||
const stopResponse = await tspmIpcClient.request('stop', { id: 'test-echo' });
|
||||
expect(stopResponse.success).toEqual(true);
|
||||
expect(stopResponse.message).toInclude('stopped successfully');
|
||||
|
||||
// Test 6: Delete the process
|
||||
const deleteResponse = await tspmIpcClient.request('delete', { id: 'test-echo' });
|
||||
expect(deleteResponse.success).toEqual(true);
|
||||
|
||||
// Test 7: Verify process is gone
|
||||
listResponse = await tspmIpcClient.request('list', {});
|
||||
const deletedProcess = listResponse.processes.find(p => p.id === 'test-echo');
|
||||
expect(deletedProcess).toBeUndefined();
|
||||
|
||||
// Cleanup: stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Batch operations through daemon', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Add multiple test processes
|
||||
const testConfigs: tspm.IProcessConfig[] = [
|
||||
{
|
||||
id: 'batch-test-1',
|
||||
name: 'Batch Test 1',
|
||||
command: 'echo "Process 1"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
},
|
||||
{
|
||||
id: 'batch-test-2',
|
||||
name: 'Batch Test 2',
|
||||
command: 'echo "Process 2"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Start processes
|
||||
for (const config of testConfigs) {
|
||||
await tspmIpcClient.request('start', { config });
|
||||
}
|
||||
|
||||
// Test 1: Stop all processes
|
||||
const stopAllResponse = await tspmIpcClient.request('stopAll', {});
|
||||
expect(stopAllResponse.stopped).toBeArray();
|
||||
expect(stopAllResponse.stopped.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
// Test 2: Start all processes
|
||||
const startAllResponse = await tspmIpcClient.request('startAll', {});
|
||||
expect(startAllResponse.started).toBeArray();
|
||||
|
||||
// Test 3: Restart all processes
|
||||
const restartAllResponse = await tspmIpcClient.request('restartAll', {});
|
||||
expect(restartAllResponse.restarted).toBeArray();
|
||||
|
||||
// Cleanup: delete all test processes
|
||||
for (const config of testConfigs) {
|
||||
await tspmIpcClient.request('delete', { id: config.id }).catch(() => {});
|
||||
}
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon error handling', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test 1: Try to stop non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('stop', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('Failed to stop process');
|
||||
}
|
||||
|
||||
// Test 2: Try to describe non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('describe', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('not found');
|
||||
}
|
||||
|
||||
// Test 3: Try to restart non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('restart', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('Failed to restart process');
|
||||
}
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon heartbeat functionality', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test heartbeat
|
||||
const heartbeatResponse = await tspmIpcClient.request('heartbeat', {});
|
||||
expect(heartbeatResponse.timestamp).toBeGreaterThan(0);
|
||||
expect(heartbeatResponse.status).toEqual('healthy');
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon memory and CPU reporting', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Get daemon status
|
||||
const status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toBeDefined();
|
||||
expect(status?.memoryUsage).toBeGreaterThan(0);
|
||||
expect(status?.cpuUsage).toBeGreaterThanOrEqual(0);
|
||||
expect(status?.uptime).toBeGreaterThan(0);
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
// Cleanup after all tests
|
||||
tap.test('Final cleanup', async () => {
|
||||
await ensureDaemonStopped();
|
||||
await cleanupTestFiles();
|
||||
});
|
||||
|
||||
export default tap.start();
|
145
test/test.ipcclient.ts
Normal file
145
test/test.ipcclient.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as tspm from '../ts/index.js';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import { TspmIpcClient } from '../ts/classes.ipcclient.js';
|
||||
import * as os from 'os';
|
||||
|
||||
// Test IPC client functionality
|
||||
tap.test('TspmIpcClient creation', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
expect(client).toBeInstanceOf(TspmIpcClient);
|
||||
});
|
||||
|
||||
tap.test('IPC client socket path', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const socketPath = (client as any).socketPath;
|
||||
|
||||
expect(socketPath).toInclude('.tspm');
|
||||
expect(socketPath).toInclude('tspm.sock');
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon PID file path', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const daemonPidFile = (client as any).daemonPidFile;
|
||||
|
||||
expect(daemonPidFile).toInclude('.tspm');
|
||||
expect(daemonPidFile).toInclude('daemon.pid');
|
||||
});
|
||||
|
||||
tap.test('IPC client connection state', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const isConnected = (client as any).isConnected;
|
||||
|
||||
expect(isConnected).toEqual(false); // Should be false initially
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon running check - no daemon', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const tspmDir = path.join(os.homedir(), '.tspm');
|
||||
const pidFile = path.join(tspmDir, 'daemon.pid');
|
||||
|
||||
// Ensure no PID file exists for this test
|
||||
await fs.unlink(pidFile).catch(() => {});
|
||||
|
||||
const isRunning = await (client as any).isDaemonRunning();
|
||||
expect(isRunning).toEqual(false);
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon running check - stale PID', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const tspmDir = path.join(os.homedir(), '.tspm');
|
||||
const pidFile = path.join(tspmDir, 'daemon.pid');
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
await fs.mkdir(tspmDir, { recursive: true });
|
||||
|
||||
// Write a fake PID that doesn't exist
|
||||
await fs.writeFile(pidFile, '99999999');
|
||||
|
||||
const isRunning = await (client as any).isDaemonRunning();
|
||||
expect(isRunning).toEqual(false);
|
||||
|
||||
// Clean up - the stale PID should be removed
|
||||
const fileExists = await fs.access(pidFile).then(() => true).catch(() => false);
|
||||
expect(fileExists).toEqual(false);
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon running check - current process', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
const tspmDir = path.join(os.homedir(), '.tspm');
|
||||
const pidFile = path.join(tspmDir, 'daemon.pid');
|
||||
const socketFile = path.join(tspmDir, 'tspm.sock');
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
await fs.mkdir(tspmDir, { recursive: true });
|
||||
|
||||
// Write current process PID (simulating daemon is this process)
|
||||
await fs.writeFile(pidFile, process.pid.toString());
|
||||
|
||||
// Create a fake socket file
|
||||
await fs.writeFile(socketFile, '');
|
||||
|
||||
const isRunning = await (client as any).isDaemonRunning();
|
||||
expect(isRunning).toEqual(true);
|
||||
|
||||
// Clean up
|
||||
await fs.unlink(pidFile).catch(() => {});
|
||||
await fs.unlink(socketFile).catch(() => {});
|
||||
});
|
||||
|
||||
tap.test('IPC client singleton instance', async () => {
|
||||
// Import the singleton
|
||||
const { tspmIpcClient } = await import('../ts/classes.ipcclient.js');
|
||||
|
||||
expect(tspmIpcClient).toBeInstanceOf(TspmIpcClient);
|
||||
|
||||
// Test that it's the same instance
|
||||
const { tspmIpcClient: secondImport } = await import('../ts/classes.ipcclient.js');
|
||||
expect(tspmIpcClient).toBe(secondImport);
|
||||
});
|
||||
|
||||
tap.test('IPC client request method type safety', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
|
||||
// Test that request method exists
|
||||
expect(client.request).toBeInstanceOf(Function);
|
||||
expect(client.connect).toBeInstanceOf(Function);
|
||||
expect(client.disconnect).toBeInstanceOf(Function);
|
||||
expect(client.stopDaemon).toBeInstanceOf(Function);
|
||||
expect(client.getDaemonStatus).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
tap.test('IPC client error message formatting', async () => {
|
||||
const errorMessage = 'Could not connect to TSPM daemon. Please try running "tspm daemon start" manually.';
|
||||
expect(errorMessage).toInclude('tspm daemon start');
|
||||
});
|
||||
|
||||
tap.test('IPC client reconnection logic', async () => {
|
||||
const client = new TspmIpcClient();
|
||||
|
||||
// Test reconnection error conditions
|
||||
const econnrefusedError = new Error('ECONNREFUSED');
|
||||
expect(econnrefusedError.message).toInclude('ECONNREFUSED');
|
||||
|
||||
const enoentError = new Error('ENOENT');
|
||||
expect(enoentError.message).toInclude('ENOENT');
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon start timeout', async () => {
|
||||
const maxWaitTime = 10000; // 10 seconds
|
||||
const checkInterval = 500; // 500ms
|
||||
|
||||
const maxChecks = maxWaitTime / checkInterval;
|
||||
expect(maxChecks).toEqual(20);
|
||||
});
|
||||
|
||||
tap.test('IPC client daemon stop timeout', async () => {
|
||||
const maxWaitTime = 15000; // 15 seconds
|
||||
const checkInterval = 500; // 500ms
|
||||
|
||||
const maxChecks = maxWaitTime / checkInterval;
|
||||
expect(maxChecks).toEqual(30);
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@git.zone/tspm',
|
||||
version: '1.6.0',
|
||||
version: '1.6.1',
|
||||
description: 'a no fuzz process manager'
|
||||
}
|
||||
|
@@ -40,9 +40,10 @@ export class TspmDaemon {
|
||||
}
|
||||
|
||||
// Initialize IPC server
|
||||
this.ipcServer = new plugins.smartipc.IpcServer({
|
||||
this.ipcServer = plugins.smartipc.SmartIpc.createServer({
|
||||
id: 'tspm-daemon',
|
||||
socketPath: this.socketPath,
|
||||
heartbeat: false, // Disable heartbeat for now
|
||||
});
|
||||
|
||||
// Register message handlers
|
||||
@@ -72,9 +73,9 @@ export class TspmDaemon {
|
||||
*/
|
||||
private registerHandlers(): void {
|
||||
// Process management handlers
|
||||
this.ipcServer.on<RequestForMethod<'start'>>(
|
||||
this.ipcServer.onMessage(
|
||||
'start',
|
||||
async (request) => {
|
||||
async (request: RequestForMethod<'start'>) => {
|
||||
try {
|
||||
await this.tspmInstance.start(request.config);
|
||||
const processInfo = this.tspmInstance.processInfo.get(
|
||||
@@ -91,9 +92,9 @@ export class TspmDaemon {
|
||||
},
|
||||
);
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'stop'>>(
|
||||
this.ipcServer.onMessage(
|
||||
'stop',
|
||||
async (request) => {
|
||||
async (request: RequestForMethod<'stop'>) => {
|
||||
try {
|
||||
await this.tspmInstance.stop(request.id);
|
||||
return {
|
||||
@@ -106,7 +107,7 @@ export class TspmDaemon {
|
||||
},
|
||||
);
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'restart'>>('restart', async (request) => {
|
||||
this.ipcServer.onMessage('restart', async (request: RequestForMethod<'restart'>) => {
|
||||
try {
|
||||
await this.tspmInstance.restart(request.id);
|
||||
const processInfo = this.tspmInstance.processInfo.get(request.id);
|
||||
@@ -120,9 +121,9 @@ export class TspmDaemon {
|
||||
}
|
||||
});
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'delete'>>(
|
||||
this.ipcServer.onMessage(
|
||||
'delete',
|
||||
async (request) => {
|
||||
async (request: RequestForMethod<'delete'>) => {
|
||||
try {
|
||||
await this.tspmInstance.delete(request.id);
|
||||
return {
|
||||
@@ -136,15 +137,15 @@ export class TspmDaemon {
|
||||
);
|
||||
|
||||
// Query handlers
|
||||
this.ipcServer.on<RequestForMethod<'list'>>(
|
||||
this.ipcServer.onMessage(
|
||||
'list',
|
||||
async () => {
|
||||
async (request: RequestForMethod<'list'>) => {
|
||||
const processes = await this.tspmInstance.list();
|
||||
return { processes };
|
||||
},
|
||||
);
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'describe'>>('describe', async (request) => {
|
||||
this.ipcServer.onMessage('describe', async (request: RequestForMethod<'describe'>) => {
|
||||
const processInfo = await this.tspmInstance.describe(request.id);
|
||||
const config = this.tspmInstance.processConfigs.get(request.id);
|
||||
|
||||
@@ -158,13 +159,13 @@ export class TspmDaemon {
|
||||
};
|
||||
});
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'getLogs'>>('getLogs', async (request) => {
|
||||
this.ipcServer.onMessage('getLogs', async (request: RequestForMethod<'getLogs'>) => {
|
||||
const logs = await this.tspmInstance.getLogs(request.id);
|
||||
return { logs };
|
||||
});
|
||||
|
||||
// Batch operations handlers
|
||||
this.ipcServer.on<RequestForMethod<'startAll'>>('startAll', async () => {
|
||||
this.ipcServer.onMessage('startAll', async (request: RequestForMethod<'startAll'>) => {
|
||||
const started: string[] = [];
|
||||
const failed: Array<{ id: string; error: string }> = [];
|
||||
|
||||
@@ -182,7 +183,7 @@ export class TspmDaemon {
|
||||
return { started, failed };
|
||||
});
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'stopAll'>>('stopAll', async () => {
|
||||
this.ipcServer.onMessage('stopAll', async (request: RequestForMethod<'stopAll'>) => {
|
||||
const stopped: string[] = [];
|
||||
const failed: Array<{ id: string; error: string }> = [];
|
||||
|
||||
@@ -200,7 +201,7 @@ export class TspmDaemon {
|
||||
return { stopped, failed };
|
||||
});
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'restartAll'>>('restartAll', async () => {
|
||||
this.ipcServer.onMessage('restartAll', async (request: RequestForMethod<'restartAll'>) => {
|
||||
const restarted: string[] = [];
|
||||
const failed: Array<{ id: string; error: string }> = [];
|
||||
|
||||
@@ -219,7 +220,7 @@ export class TspmDaemon {
|
||||
});
|
||||
|
||||
// Daemon management handlers
|
||||
this.ipcServer.on<RequestForMethod<'daemon:status'>>('daemon:status', async () => {
|
||||
this.ipcServer.onMessage('daemon:status', async (request: RequestForMethod<'daemon:status'>) => {
|
||||
const memUsage = process.memoryUsage();
|
||||
return {
|
||||
status: 'running',
|
||||
@@ -231,7 +232,7 @@ export class TspmDaemon {
|
||||
};
|
||||
});
|
||||
|
||||
this.ipcServer.on<RequestForMethod<'daemon:shutdown'>>('daemon:shutdown', async (request) => {
|
||||
this.ipcServer.onMessage('daemon:shutdown', async (request: RequestForMethod<'daemon:shutdown'>) => {
|
||||
if (this.isShuttingDown) {
|
||||
return {
|
||||
success: false,
|
||||
@@ -256,7 +257,7 @@ export class TspmDaemon {
|
||||
});
|
||||
|
||||
// Heartbeat handler
|
||||
this.ipcServer.on<RequestForMethod<'heartbeat'>>('heartbeat', async () => {
|
||||
this.ipcServer.onMessage('heartbeat', async (request: RequestForMethod<'heartbeat'>) => {
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
status: this.isShuttingDown ? 'degraded' : 'healthy',
|
||||
|
@@ -41,9 +41,10 @@ export class TspmIpcClient {
|
||||
}
|
||||
|
||||
// Create IPC client
|
||||
this.ipcClient = new plugins.smartipc.IpcClient({
|
||||
this.ipcClient = plugins.smartipc.SmartIpc.createClient({
|
||||
id: 'tspm-cli',
|
||||
socketPath: this.socketPath,
|
||||
heartbeat: false, // Disable heartbeat for now
|
||||
});
|
||||
|
||||
// Connect to the daemon
|
||||
|
Reference in New Issue
Block a user