fix(cli): Use server-side start-by-id flow for starting processes

This commit is contained in:
2025-08-29 21:10:01 +00:00
parent 611b756670
commit dfe0677cab
6 changed files with 72 additions and 9 deletions

View File

@@ -1,5 +1,12 @@
# Changelog # Changelog
## 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) ## 2025-08-29 - 4.4.0 - feat(daemon)
Persist desired process states and add daemon restart command Persist desired process states and add daemon restart command

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/tspm', name: '@git.zone/tspm',
version: '4.4.0', version: '4.4.1',
description: 'a no fuzz process manager' description: 'a no fuzz process manager'
} }

View File

@@ -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'}`);

View File

@@ -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,22 @@ 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');
}
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

View File

@@ -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';
}
} }
/** /**
@@ -128,6 +136,29 @@ export class TspmDaemon {
}, },
); );
// Start by id (resolve config on server)
this.ipcServer.onMessage(
'startById',
async (request: RequestForMethod<'startById'>) => {
try {
const config = this.tspmInstance.processConfigs.get(request.id);
if (!config) {
throw new Error(`Process ${request.id} not found`);
}
await this.tspmInstance.setDesiredState(request.id, 'online');
await this.tspmInstance.start(config);
const processInfo = this.tspmInstance.processInfo.get(request.id);
return {
processId: request.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'>) => {
@@ -321,6 +352,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,
}; };
}, },
); );

View File

@@ -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 };