Files
tspm/ts/cli/commands/daemon/index.ts

192 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as plugins from '../../plugins.js';
import * as paths from '../../../paths.js';
import { tspmIpcClient } from '../../../client/tspm.ipcclient.js';
import { Logger } from '../../../shared/common/utils.errorhandler.js';
import type { CliArguments } from '../../types.js';
import { formatMemory } from '../../helpers/memory.js';
export function registerDaemonCommand(smartcli: plugins.smartcli.Smartcli) {
const cliLogger = new Logger('CLI');
smartcli.addCommand('daemon').subscribe({
next: async (argvArg: CliArguments) => {
const subCommand = argvArg._[1];
switch (subCommand) {
case 'start':
try {
const status = await tspmIpcClient.getDaemonStatus();
if (status) {
console.log('TSPM daemon is already running');
console.log(` PID: ${status.pid}`);
console.log(
` Uptime: ${Math.floor((status.uptime || 0) / 1000)}s`,
);
console.log(` Processes: ${status.processCount}`);
return;
}
console.log('Starting TSPM daemon manually...');
// Import spawn to start daemon process
const { spawn } = await import('child_process');
const daemonScript = plugins.path.join(
paths.packageDir,
'dist_ts',
'daemon.js',
);
// Start daemon as a detached background process
// Use 'inherit' for stdio to see any startup errors when debugging
const daemonProcess = spawn(process.execPath, [daemonScript], {
detached: true,
stdio: process.env.TSPM_DEBUG === 'true' ? 'inherit' : 'ignore',
env: {
...process.env,
TSPM_DAEMON_MODE: 'true',
},
});
// Detach the daemon so it continues running after CLI exits
daemonProcess.unref();
console.log(`Started daemon with PID: ${daemonProcess.pid}`);
// Wait for daemon to be ready
await new Promise((resolve) => setTimeout(resolve, 2000));
const newStatus = await tspmIpcClient.getDaemonStatus();
if (newStatus) {
console.log('✓ TSPM daemon started successfully');
console.log(` PID: ${newStatus.pid}`);
console.log(
'\nNote: This daemon will run until you stop it or logout.',
);
console.log('For automatic startup, use "tspm enable" instead.');
} else {
console.warn('\n⚠ Warning: Daemon process started but is not responding.');
console.log('The daemon may have crashed on startup.');
console.log('\nTo debug, try:');
console.log(' TSPM_DEBUG=true tspm daemon start');
console.log('\nOr check if the socket file exists:');
console.log(` ls -la ~/.tspm/tspm.sock`);
}
// Disconnect from the daemon after starting
await tspmIpcClient.disconnect();
} catch (error) {
console.error('Error starting daemon:', error.message);
process.exit(1);
}
break;
case 'restart':
try {
console.log('Restarting TSPM daemon...');
await tspmIpcClient.stopDaemon(true);
// Reuse the manual start logic from 'start'
const statusAfterStop = await tspmIpcClient.getDaemonStatus();
if (statusAfterStop) {
console.warn('Daemon still appears to be running; proceeding to start anyway.');
}
console.log('Starting TSPM daemon manually...');
const { spawn } = await import('child_process');
const daemonScript = plugins.path.join(
paths.packageDir,
'dist_ts',
'daemon.js',
);
const daemonProcess = spawn(process.execPath, [daemonScript], {
detached: true,
stdio: process.env.TSPM_DEBUG === 'true' ? 'inherit' : 'ignore',
env: { ...process.env, TSPM_DAEMON_MODE: 'true' },
});
daemonProcess.unref();
console.log(`Started daemon with PID: ${daemonProcess.pid}`);
await new Promise((resolve) => setTimeout(resolve, 2000));
const newStatus = await tspmIpcClient.getDaemonStatus();
if (newStatus) {
console.log('✓ TSPM daemon restarted successfully');
console.log(` PID: ${newStatus.pid}`);
} else {
console.warn('\n⚠ Warning: Daemon restart attempted but status is unavailable.');
}
await tspmIpcClient.disconnect();
} catch (error) {
console.error('Error restarting daemon:', (error as any).message || String(error));
process.exit(1);
}
break;
case 'start-service':
// This is called by systemd - start the daemon directly
console.log('Starting TSPM daemon for systemd service...');
const { startDaemon } = await import('../../../daemon/tspm.daemon.js');
await startDaemon();
break;
case 'stop':
try {
console.log('Stopping TSPM daemon...');
await tspmIpcClient.stopDaemon(true);
console.log('✓ TSPM daemon stopped successfully');
// Disconnect from the daemon after stopping
await tspmIpcClient.disconnect();
} catch (error) {
console.error('Error stopping daemon:', error.message);
process.exit(1);
}
break;
case 'status':
try {
const status = await tspmIpcClient.getDaemonStatus();
if (!status) {
console.log('TSPM daemon is not running');
console.log('Use "tspm daemon start" to start it');
return;
}
console.log('TSPM Daemon Status:');
console.log('─'.repeat(40));
console.log(`Status: ${status.status}`);
console.log(`PID: ${status.pid}`);
console.log(
`Uptime: ${Math.floor((status.uptime || 0) / 1000)}s`,
);
console.log(`Processes: ${status.processCount}`);
console.log(
`Memory: ${formatMemory(status.memoryUsage || 0)}`,
);
console.log(`CPU: ${status.cpuUsage?.toFixed(1) || 0}s`);
// Disconnect from daemon after getting status
await tspmIpcClient.disconnect();
} catch (error) {
console.error('Error getting daemon status:', error.message);
process.exit(1);
}
break;
default:
console.log('Usage: tspm daemon <command>');
console.log('\nCommands:');
console.log(' start Start the TSPM daemon');
console.log(' restart Restart the TSPM daemon');
console.log(' stop Stop the TSPM daemon');
console.log(' status Show daemon status');
break;
}
},
error: (err) => {
cliLogger.error(err);
},
complete: () => {},
});
}