fix(cli): Use server-side start-by-id flow for starting processes
This commit is contained in:
		@@ -1,5 +1,12 @@
 | 
			
		||||
# 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)
 | 
			
		||||
Persist desired process states and add daemon restart command
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,6 @@
 | 
			
		||||
 */
 | 
			
		||||
export const commitinfo = {
 | 
			
		||||
  name: '@git.zone/tspm',
 | 
			
		||||
  version: '4.4.0',
 | 
			
		||||
  version: '4.4.1',
 | 
			
		||||
  description: 'a no fuzz process manager'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,8 @@ export function registerStartCommand(smartcli: plugins.smartcli.Smartcli) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const desc = await tspmIpcClient.request('describe', { id }).catch(() => null);
 | 
			
		||||
      if (!desc) {
 | 
			
		||||
        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(`Starting process id ${id}...`);
 | 
			
		||||
      const response = await tspmIpcClient.request('startById', { id });
 | 
			
		||||
      console.log('✓ Process started');
 | 
			
		||||
      console.log(`  ID: ${response.processId}`);
 | 
			
		||||
      console.log(`  PID: ${response.pid || 'N/A'}`);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import * as plugins from './plugins.js';
 | 
			
		||||
import { tspmIpcClient } from '../client/tspm.ipcclient.js';
 | 
			
		||||
import * as paths from '../paths.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();
 | 
			
		||||
  // 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);
 | 
			
		||||
 | 
			
		||||
  // Register all commands
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,20 @@ export class TspmDaemon {
 | 
			
		||||
  private socketPath: string;
 | 
			
		||||
  private heartbeatInterval: NodeJS.Timeout | null = null;
 | 
			
		||||
  private daemonPidFile: string;
 | 
			
		||||
  private version: string;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.tspmInstance = new ProcessManager();
 | 
			
		||||
    this.socketPath = plugins.path.join(paths.tspmDir, 'tspm.sock');
 | 
			
		||||
    this.daemonPidFile = plugins.path.join(paths.tspmDir, 'daemon.pid');
 | 
			
		||||
    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(
 | 
			
		||||
      'stop',
 | 
			
		||||
      async (request: RequestForMethod<'stop'>) => {
 | 
			
		||||
@@ -321,6 +352,7 @@ export class TspmDaemon {
 | 
			
		||||
          processCount: this.tspmInstance.processes.size,
 | 
			
		||||
          memoryUsage: memUsage.heapUsed,
 | 
			
		||||
          cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds
 | 
			
		||||
          version: this.version,
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,17 @@ export interface StartResponse {
 | 
			
		||||
  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
 | 
			
		||||
export interface StopRequest {
 | 
			
		||||
  id: string;
 | 
			
		||||
@@ -191,6 +202,7 @@ export interface DaemonStatusResponse {
 | 
			
		||||
  processCount: number;
 | 
			
		||||
  memoryUsage?: number;
 | 
			
		||||
  cpuUsage?: number;
 | 
			
		||||
  version?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Daemon shutdown command
 | 
			
		||||
@@ -238,6 +250,7 @@ export interface RemoveResponse {
 | 
			
		||||
// Type mappings for methods
 | 
			
		||||
export type IpcMethodMap = {
 | 
			
		||||
  start: { request: StartRequest; response: StartResponse };
 | 
			
		||||
  startById: { request: StartByIdRequest; response: StartByIdResponse };
 | 
			
		||||
  stop: { request: StopRequest; response: StopResponse };
 | 
			
		||||
  restart: { request: RestartRequest; response: RestartResponse };
 | 
			
		||||
  delete: { request: DeleteRequest; response: DeleteResponse };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user