Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
e507b75c40 | |||
97a8377a75 | |||
3676bff04c | |||
dfe0677cab | |||
611b756670 | |||
2291348774 |
0
.tspm_home/.npmextra/kv/@git.zone__tspm.json
Normal file
0
.tspm_home/.npmextra/kv/@git.zone__tspm.json
Normal file
25
changelog.md
25
changelog.md
@@ -1,5 +1,30 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-29 - 4.4.2 - fix(daemon)
|
||||||
|
Fix daemon IPC id handling, reload configs on demand and correct CLI daemon start path
|
||||||
|
|
||||||
|
- Normalize process IDs in daemon IPC handlers (trim strings) to avoid lookup mismatches
|
||||||
|
- Attempt to reload saved process configurations when a startById request cannot find a config (handles races/stale state)
|
||||||
|
- Use normalized IDs in responses and messages for stop/restart/delete/remove/describe handlers
|
||||||
|
- Fix CLI daemon start path to point at dist_ts/daemon/tspm.daemon.js when launching the background daemon
|
||||||
|
- Ensure the IPC client disconnects after showing CLI version/status to avoid leaked connections
|
||||||
|
|
||||||
|
## 2025-08-29 - 4.4.1 - fix(cli)
|
||||||
|
Use server-side start-by-id flow for starting processes
|
||||||
|
|
||||||
|
- CLI: 'tspm start <id>' now calls a new 'startById' IPC method instead of fetching the full config via 'describe' and submitting it back to 'start'.
|
||||||
|
- Daemon: Added server-side handler for 'startById' which resolves the stored process config and starts the process on the daemon.
|
||||||
|
- Protocol: Added StartByIdRequest/StartByIdResponse types and registered 'startById' in the IPC method map.
|
||||||
|
|
||||||
|
## 2025-08-29 - 4.4.0 - feat(daemon)
|
||||||
|
Persist desired process states and add daemon restart command
|
||||||
|
|
||||||
|
- Persist desired process states: ProcessManager now stores desiredStates to user storage (desiredStates key) and reloads them on startup.
|
||||||
|
- Start/stop operations update desired state: IPC handlers in the daemon now set desired state when processes are started, stopped, restarted or when batch start/stop is invoked.
|
||||||
|
- Resume desired state on daemon start: Daemon loads desired states and calls startDesired() to bring processes to their desired 'online' state after startup.
|
||||||
|
- Remove desired state on deletion/reset: Deleting a process or resetting clears its desired state; reset clears all desired states as well.
|
||||||
|
- CLI: Added 'tspm daemon restart' — stops the daemon (gracefully) and restarts it in the foreground for the current session, with checks and informative output.
|
||||||
|
|
||||||
## 2025-08-29 - 4.3.1 - fix(daemon)
|
## 2025-08-29 - 4.3.1 - fix(daemon)
|
||||||
Fix daemon describe handler to return correct process info and config; bump @push.rocks/smartipc to ^2.2.2
|
Fix daemon describe handler to return correct process info and config; bump @push.rocks/smartipc to ^2.2.2
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tspm",
|
"name": "@git.zone/tspm",
|
||||||
"version": "4.3.1",
|
"version": "4.4.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "a no fuzz process manager",
|
"description": "a no fuzz process manager",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tspm',
|
name: '@git.zone/tspm',
|
||||||
version: '4.3.1',
|
version: '4.4.2',
|
||||||
description: 'a no fuzz process manager'
|
description: 'a no fuzz process manager'
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,8 @@ export function registerDaemonCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
const daemonScript = plugins.path.join(
|
const daemonScript = plugins.path.join(
|
||||||
paths.packageDir,
|
paths.packageDir,
|
||||||
'dist_ts',
|
'dist_ts',
|
||||||
'daemon.js',
|
'daemon',
|
||||||
|
'tspm.daemon.js',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start daemon as a detached background process
|
// Start daemon as a detached background process
|
||||||
@@ -80,6 +81,48 @@ export function registerDaemonCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
}
|
}
|
||||||
break;
|
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':
|
case 'start-service':
|
||||||
// This is called by systemd - start the daemon directly
|
// This is called by systemd - start the daemon directly
|
||||||
console.log('Starting TSPM daemon for systemd service...');
|
console.log('Starting TSPM daemon for systemd service...');
|
||||||
@@ -135,6 +178,7 @@ export function registerDaemonCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
console.log('Usage: tspm daemon <command>');
|
console.log('Usage: tspm daemon <command>');
|
||||||
console.log('\nCommands:');
|
console.log('\nCommands:');
|
||||||
console.log(' start Start the TSPM daemon');
|
console.log(' start Start the TSPM daemon');
|
||||||
|
console.log(' restart Restart the TSPM daemon');
|
||||||
console.log(' stop Stop the TSPM daemon');
|
console.log(' stop Stop the TSPM daemon');
|
||||||
console.log(' status Show daemon status');
|
console.log(' status Show daemon status');
|
||||||
break;
|
break;
|
||||||
|
@@ -17,14 +17,8 @@ export function registerStartCommand(smartcli: plugins.smartcli.Smartcli) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const desc = await tspmIpcClient.request('describe', { id }).catch(() => null);
|
console.log(`Starting process id ${id}...`);
|
||||||
if (!desc) {
|
const response = await tspmIpcClient.request('startById', { id });
|
||||||
console.error(`Process with id '${id}' not found. Use 'tspm add' first.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Starting process id ${id} (${desc.config.name || id})...`);
|
|
||||||
const response = await tspmIpcClient.request('start', { config: desc.config });
|
|
||||||
console.log('✓ Process started');
|
console.log('✓ Process started');
|
||||||
console.log(` ID: ${response.processId}`);
|
console.log(` ID: ${response.processId}`);
|
||||||
console.log(` PID: ${response.pid || 'N/A'}`);
|
console.log(` PID: ${response.pid || 'N/A'}`);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
import { tspmIpcClient } from '../client/tspm.ipcclient.js';
|
||||||
import * as paths from '../paths.js';
|
import * as paths from '../paths.js';
|
||||||
import { Logger, LogLevel } from '../shared/common/utils.errorhandler.js';
|
import { Logger, LogLevel } from '../shared/common/utils.errorhandler.js';
|
||||||
|
|
||||||
@@ -38,6 +39,24 @@ export const run = async (): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const smartcliInstance = new plugins.smartcli.Smartcli();
|
const smartcliInstance = new plugins.smartcli.Smartcli();
|
||||||
|
// Intercept -v/--version to show CLI and daemon versions
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
if (args.includes('-v') || args.includes('--version')) {
|
||||||
|
const cliVersion = tspmProjectinfo.npm.version;
|
||||||
|
console.log(`tspm CLI: ${cliVersion}`);
|
||||||
|
const status = await tspmIpcClient.getDaemonStatus();
|
||||||
|
if (status) {
|
||||||
|
console.log(
|
||||||
|
`Daemon: running v${status.version || 'unknown'} (pid ${status.pid})`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log('Daemon: not running');
|
||||||
|
}
|
||||||
|
// Ensure we disconnect any IPC client connection used for status
|
||||||
|
try { await tspmIpcClient.disconnect(); } catch {}
|
||||||
|
return; // do not start parser
|
||||||
|
}
|
||||||
|
// Keep Smartcli version info for help output but not used for -v now
|
||||||
smartcliInstance.addVersion(tspmProjectinfo.npm.version);
|
smartcliInstance.addVersion(tspmProjectinfo.npm.version);
|
||||||
|
|
||||||
// Register all commands
|
// Register all commands
|
||||||
|
@@ -25,6 +25,8 @@ export class ProcessManager extends EventEmitter {
|
|||||||
public processInfo: Map<string, IProcessInfo> = new Map();
|
public processInfo: Map<string, IProcessInfo> = new Map();
|
||||||
private config: TspmConfig;
|
private config: TspmConfig;
|
||||||
private configStorageKey = 'processes';
|
private configStorageKey = 'processes';
|
||||||
|
private desiredStateStorageKey = 'desiredStates';
|
||||||
|
private desiredStates: Map<string, IProcessInfo['status']> = new Map();
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -32,6 +34,7 @@ export class ProcessManager extends EventEmitter {
|
|||||||
this.logger = new Logger('Tspm');
|
this.logger = new Logger('Tspm');
|
||||||
this.config = new TspmConfig();
|
this.config = new TspmConfig();
|
||||||
this.loadProcessConfigs();
|
this.loadProcessConfigs();
|
||||||
|
this.loadDesiredStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +70,7 @@ export class ProcessManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await this.saveProcessConfigs();
|
await this.saveProcessConfigs();
|
||||||
|
await this.setDesiredState(config.id, 'stopped');
|
||||||
return config.id;
|
return config.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +283,7 @@ export class ProcessManager extends EventEmitter {
|
|||||||
|
|
||||||
// Save updated configs
|
// Save updated configs
|
||||||
await this.saveProcessConfigs();
|
await this.saveProcessConfigs();
|
||||||
|
await this.removeDesiredState(id);
|
||||||
|
|
||||||
this.logger.info(`Successfully deleted process with id '${id}'`);
|
this.logger.info(`Successfully deleted process with id '${id}'`);
|
||||||
} catch (error: Error | unknown) {
|
} catch (error: Error | unknown) {
|
||||||
@@ -288,6 +293,7 @@ export class ProcessManager extends EventEmitter {
|
|||||||
this.processConfigs.delete(id);
|
this.processConfigs.delete(id);
|
||||||
this.processInfo.delete(id);
|
this.processInfo.delete(id);
|
||||||
await this.saveProcessConfigs();
|
await this.saveProcessConfigs();
|
||||||
|
await this.removeDesiredState(id);
|
||||||
|
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Successfully deleted process with id '${id}' after stopping failure`,
|
`Successfully deleted process with id '${id}' after stopping failure`,
|
||||||
@@ -415,6 +421,80 @@ export class ProcessManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Desired state persistence ===
|
||||||
|
private async saveDesiredStates(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const obj: Record<string, IProcessInfo['status']> = {};
|
||||||
|
for (const [id, state] of this.desiredStates.entries()) {
|
||||||
|
obj[id] = state;
|
||||||
|
}
|
||||||
|
await this.config.writeKey(
|
||||||
|
this.desiredStateStorageKey,
|
||||||
|
JSON.stringify(obj),
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Failed to save desired states: ${error?.message || String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadDesiredStates(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const raw = await this.config.readKey(this.desiredStateStorageKey);
|
||||||
|
if (raw) {
|
||||||
|
const obj = JSON.parse(raw) as Record<string, IProcessInfo['status']>;
|
||||||
|
this.desiredStates = new Map(Object.entries(obj));
|
||||||
|
this.logger.debug(
|
||||||
|
`Loaded desired states for ${this.desiredStates.size} processes`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Failed to load desired states: ${error?.message || String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setDesiredState(
|
||||||
|
id: string,
|
||||||
|
state: IProcessInfo['status'],
|
||||||
|
): Promise<void> {
|
||||||
|
this.desiredStates.set(id, state);
|
||||||
|
await this.saveDesiredStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeDesiredState(id: string): Promise<void> {
|
||||||
|
this.desiredStates.delete(id);
|
||||||
|
await this.saveDesiredStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setDesiredStateForAll(
|
||||||
|
state: IProcessInfo['status'],
|
||||||
|
): Promise<void> {
|
||||||
|
for (const id of this.processConfigs.keys()) {
|
||||||
|
this.desiredStates.set(id, state);
|
||||||
|
}
|
||||||
|
await this.saveDesiredStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async startDesired(): Promise<void> {
|
||||||
|
for (const [id, config] of this.processConfigs.entries()) {
|
||||||
|
const desired = this.desiredStates.get(id);
|
||||||
|
if (desired === 'online' && !this.processes.has(id)) {
|
||||||
|
try {
|
||||||
|
await this.start(config);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Failed to start desired process ${id}: ${
|
||||||
|
(e as Error)?.message || String(e)
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load process configurations from config storage
|
* Load process configurations from config storage
|
||||||
*/
|
*/
|
||||||
@@ -499,10 +579,12 @@ export class ProcessManager extends EventEmitter {
|
|||||||
this.processes.clear();
|
this.processes.clear();
|
||||||
this.processInfo.clear();
|
this.processInfo.clear();
|
||||||
this.processConfigs.clear();
|
this.processConfigs.clear();
|
||||||
|
this.desiredStates.clear();
|
||||||
|
|
||||||
// Remove persisted configs
|
// Remove persisted configs
|
||||||
try {
|
try {
|
||||||
await this.config.deleteKey(this.configStorageKey);
|
await this.config.deleteKey(this.configStorageKey);
|
||||||
|
await this.config.deleteKey(this.desiredStateStorageKey).catch(() => {});
|
||||||
this.logger.debug('Cleared persisted process configurations');
|
this.logger.debug('Cleared persisted process configurations');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback: write empty list if deleteKey fails for any reason
|
// Fallback: write empty list if deleteKey fails for any reason
|
||||||
|
@@ -20,12 +20,20 @@ export class TspmDaemon {
|
|||||||
private socketPath: string;
|
private socketPath: string;
|
||||||
private heartbeatInterval: NodeJS.Timeout | null = null;
|
private heartbeatInterval: NodeJS.Timeout | null = null;
|
||||||
private daemonPidFile: string;
|
private daemonPidFile: string;
|
||||||
|
private version: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tspmInstance = new ProcessManager();
|
this.tspmInstance = new ProcessManager();
|
||||||
this.socketPath = plugins.path.join(paths.tspmDir, 'tspm.sock');
|
this.socketPath = plugins.path.join(paths.tspmDir, 'tspm.sock');
|
||||||
this.daemonPidFile = plugins.path.join(paths.tspmDir, 'daemon.pid');
|
this.daemonPidFile = plugins.path.join(paths.tspmDir, 'daemon.pid');
|
||||||
this.startTime = Date.now();
|
this.startTime = Date.now();
|
||||||
|
// Determine daemon version from package metadata
|
||||||
|
try {
|
||||||
|
const proj = new plugins.projectinfo.ProjectInfo(paths.packageDir);
|
||||||
|
this.version = proj.npm.version || 'unknown';
|
||||||
|
} catch {
|
||||||
|
this.version = 'unknown';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +89,7 @@ export class TspmDaemon {
|
|||||||
|
|
||||||
// Load existing process configurations
|
// Load existing process configurations
|
||||||
await this.tspmInstance.loadProcessConfigs();
|
await this.tspmInstance.loadProcessConfigs();
|
||||||
|
await this.tspmInstance.loadDesiredStates();
|
||||||
|
|
||||||
// Set up log publishing
|
// Set up log publishing
|
||||||
this.tspmInstance.on('process:log', ({ processId, log }) => {
|
this.tspmInstance.on('process:log', ({ processId, log }) => {
|
||||||
@@ -95,6 +104,9 @@ export class TspmDaemon {
|
|||||||
// Set up graceful shutdown handlers
|
// Set up graceful shutdown handlers
|
||||||
this.setupShutdownHandlers();
|
this.setupShutdownHandlers();
|
||||||
|
|
||||||
|
// Start processes that should be online per desired state
|
||||||
|
await this.tspmInstance.startDesired();
|
||||||
|
|
||||||
console.log(`TSPM daemon started successfully on ${this.socketPath}`);
|
console.log(`TSPM daemon started successfully on ${this.socketPath}`);
|
||||||
console.log(`PID: ${process.pid}`);
|
console.log(`PID: ${process.pid}`);
|
||||||
}
|
}
|
||||||
@@ -108,6 +120,7 @@ export class TspmDaemon {
|
|||||||
'start',
|
'start',
|
||||||
async (request: RequestForMethod<'start'>) => {
|
async (request: RequestForMethod<'start'>) => {
|
||||||
try {
|
try {
|
||||||
|
await this.tspmInstance.setDesiredState(request.config.id, 'online');
|
||||||
await this.tspmInstance.start(request.config);
|
await this.tspmInstance.start(request.config);
|
||||||
const processInfo = this.tspmInstance.processInfo.get(
|
const processInfo = this.tspmInstance.processInfo.get(
|
||||||
request.config.id,
|
request.config.id,
|
||||||
@@ -123,14 +136,45 @@ export class TspmDaemon {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Start by id (resolve config on server)
|
||||||
|
this.ipcServer.onMessage(
|
||||||
|
'startById',
|
||||||
|
async (request: RequestForMethod<'startById'>) => {
|
||||||
|
try {
|
||||||
|
const id = String(request.id).trim();
|
||||||
|
let config = this.tspmInstance.processConfigs.get(id);
|
||||||
|
if (!config) {
|
||||||
|
// Try to reload configs if not found (handles races or stale state)
|
||||||
|
await this.tspmInstance.loadProcessConfigs();
|
||||||
|
config = this.tspmInstance.processConfigs.get(id) || null as any;
|
||||||
|
}
|
||||||
|
if (!config) {
|
||||||
|
throw new Error(`Process ${id} not found`);
|
||||||
|
}
|
||||||
|
await this.tspmInstance.setDesiredState(id, 'online');
|
||||||
|
await this.tspmInstance.start(config);
|
||||||
|
const processInfo = this.tspmInstance.processInfo.get(id);
|
||||||
|
return {
|
||||||
|
processId: id,
|
||||||
|
pid: processInfo?.pid,
|
||||||
|
status: processInfo?.status || 'stopped',
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to start process: ${error.message}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.ipcServer.onMessage(
|
this.ipcServer.onMessage(
|
||||||
'stop',
|
'stop',
|
||||||
async (request: RequestForMethod<'stop'>) => {
|
async (request: RequestForMethod<'stop'>) => {
|
||||||
try {
|
try {
|
||||||
await this.tspmInstance.stop(request.id);
|
const id = String(request.id).trim();
|
||||||
|
await this.tspmInstance.setDesiredState(id, 'stopped');
|
||||||
|
await this.tspmInstance.stop(id);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: `Process ${request.id} stopped successfully`,
|
message: `Process ${id} stopped successfully`,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to stop process: ${error.message}`);
|
throw new Error(`Failed to stop process: ${error.message}`);
|
||||||
@@ -142,10 +186,12 @@ export class TspmDaemon {
|
|||||||
'restart',
|
'restart',
|
||||||
async (request: RequestForMethod<'restart'>) => {
|
async (request: RequestForMethod<'restart'>) => {
|
||||||
try {
|
try {
|
||||||
await this.tspmInstance.restart(request.id);
|
const id = String(request.id).trim();
|
||||||
const processInfo = this.tspmInstance.processInfo.get(request.id);
|
await this.tspmInstance.setDesiredState(id, 'online');
|
||||||
|
await this.tspmInstance.restart(id);
|
||||||
|
const processInfo = this.tspmInstance.processInfo.get(id);
|
||||||
return {
|
return {
|
||||||
processId: request.id,
|
processId: id,
|
||||||
pid: processInfo?.pid,
|
pid: processInfo?.pid,
|
||||||
status: processInfo?.status || 'stopped',
|
status: processInfo?.status || 'stopped',
|
||||||
};
|
};
|
||||||
@@ -159,10 +205,11 @@ export class TspmDaemon {
|
|||||||
'delete',
|
'delete',
|
||||||
async (request: RequestForMethod<'delete'>) => {
|
async (request: RequestForMethod<'delete'>) => {
|
||||||
try {
|
try {
|
||||||
await this.tspmInstance.delete(request.id);
|
const id = String(request.id).trim();
|
||||||
|
await this.tspmInstance.delete(id);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: `Process ${request.id} deleted successfully`,
|
message: `Process ${id} deleted successfully`,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to delete process: ${error.message}`);
|
throw new Error(`Failed to delete process: ${error.message}`);
|
||||||
@@ -188,8 +235,9 @@ export class TspmDaemon {
|
|||||||
'remove',
|
'remove',
|
||||||
async (request: RequestForMethod<'remove'>) => {
|
async (request: RequestForMethod<'remove'>) => {
|
||||||
try {
|
try {
|
||||||
await this.tspmInstance.delete(request.id);
|
const id = String(request.id).trim();
|
||||||
return { success: true, message: `Process ${request.id} deleted successfully` };
|
await this.tspmInstance.delete(id);
|
||||||
|
return { success: true, message: `Process ${id} deleted successfully` };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to remove process: ${error.message}`);
|
throw new Error(`Failed to remove process: ${error.message}`);
|
||||||
}
|
}
|
||||||
@@ -207,9 +255,10 @@ export class TspmDaemon {
|
|||||||
this.ipcServer.onMessage(
|
this.ipcServer.onMessage(
|
||||||
'describe',
|
'describe',
|
||||||
async (request: RequestForMethod<'describe'>) => {
|
async (request: RequestForMethod<'describe'>) => {
|
||||||
const result = await this.tspmInstance.describe(request.id);
|
const id = String(request.id).trim();
|
||||||
|
const result = await this.tspmInstance.describe(id);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new Error(`Process ${request.id} not found`);
|
throw new Error(`Process ${id} not found`);
|
||||||
}
|
}
|
||||||
// Return correctly shaped response
|
// Return correctly shaped response
|
||||||
return {
|
return {
|
||||||
@@ -234,6 +283,7 @@ export class TspmDaemon {
|
|||||||
const started: string[] = [];
|
const started: string[] = [];
|
||||||
const failed: Array<{ id: string; error: string }> = [];
|
const failed: Array<{ id: string; error: string }> = [];
|
||||||
|
|
||||||
|
await this.tspmInstance.setDesiredStateForAll('online');
|
||||||
await this.tspmInstance.startAll();
|
await this.tspmInstance.startAll();
|
||||||
|
|
||||||
// Get status of all processes
|
// Get status of all processes
|
||||||
@@ -255,6 +305,7 @@ export class TspmDaemon {
|
|||||||
const stopped: string[] = [];
|
const stopped: string[] = [];
|
||||||
const failed: Array<{ id: string; error: string }> = [];
|
const failed: Array<{ id: string; error: string }> = [];
|
||||||
|
|
||||||
|
await this.tspmInstance.setDesiredStateForAll('stopped');
|
||||||
await this.tspmInstance.stopAll();
|
await this.tspmInstance.stopAll();
|
||||||
|
|
||||||
// Get status of all processes
|
// Get status of all processes
|
||||||
@@ -312,6 +363,7 @@ export class TspmDaemon {
|
|||||||
processCount: this.tspmInstance.processes.size,
|
processCount: this.tspmInstance.processes.size,
|
||||||
memoryUsage: memUsage.heapUsed,
|
memoryUsage: memUsage.heapUsed,
|
||||||
cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds
|
cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds
|
||||||
|
version: this.version,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@@ -66,6 +66,17 @@ export interface StartResponse {
|
|||||||
status: 'online' | 'stopped' | 'errored';
|
status: 'online' | 'stopped' | 'errored';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start by id (server resolves config)
|
||||||
|
export interface StartByIdRequest {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartByIdResponse {
|
||||||
|
processId: string;
|
||||||
|
pid?: number;
|
||||||
|
status: 'online' | 'stopped' | 'errored';
|
||||||
|
}
|
||||||
|
|
||||||
// Stop command
|
// Stop command
|
||||||
export interface StopRequest {
|
export interface StopRequest {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -191,6 +202,7 @@ export interface DaemonStatusResponse {
|
|||||||
processCount: number;
|
processCount: number;
|
||||||
memoryUsage?: number;
|
memoryUsage?: number;
|
||||||
cpuUsage?: number;
|
cpuUsage?: number;
|
||||||
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daemon shutdown command
|
// Daemon shutdown command
|
||||||
@@ -238,6 +250,7 @@ export interface RemoveResponse {
|
|||||||
// Type mappings for methods
|
// Type mappings for methods
|
||||||
export type IpcMethodMap = {
|
export type IpcMethodMap = {
|
||||||
start: { request: StartRequest; response: StartResponse };
|
start: { request: StartRequest; response: StartResponse };
|
||||||
|
startById: { request: StartByIdRequest; response: StartByIdResponse };
|
||||||
stop: { request: StopRequest; response: StopResponse };
|
stop: { request: StopRequest; response: StopResponse };
|
||||||
restart: { request: RestartRequest; response: RestartResponse };
|
restart: { request: RestartRequest; response: RestartResponse };
|
||||||
delete: { request: DeleteRequest; response: DeleteResponse };
|
delete: { request: DeleteRequest; response: DeleteResponse };
|
||||||
|
Reference in New Issue
Block a user