import * as plugins from '../../../plugins.js'; import { tspmIpcClient } from '../../../client/tspm.ipcclient.js'; import type { IProcessConfig } from '../../../classes.tspm.js'; import type { CliArguments } from '../../types.js'; import { parseMemoryString, formatMemory } from '../../helpers/memory.js'; import { registerIpcCommand } from '../../registration/index.js'; export function registerStartCommand(smartcli: plugins.smartcli.Smartcli) { registerIpcCommand( smartcli, 'start', async (argvArg: CliArguments) => { // Get all arguments after 'start' command const commandArgs = argvArg._.slice(1); if (commandArgs.length === 0) { console.error('Error: Please provide a command to run'); console.log('Usage: tspm start [options]'); console.log('\nExamples:'); console.log(' tspm start "npm run dev"'); console.log(' tspm start pnpm start'); console.log(' tspm start node server.js'); console.log(' tspm start script.ts'); console.log('\nOptions:'); console.log(' --name Name for the process'); console.log( ' --memory Memory limit (e.g., "512MB", "2GB")', ); console.log(' --cwd Working directory'); console.log( ' --watch Watch for file changes and restart', ); console.log(' --watch-paths Comma-separated paths to watch'); console.log(' --autorestart Auto-restart on crash'); return; } // Join all command parts to form the full command const script = commandArgs.join(' '); const memoryLimit = argvArg.memory ? parseMemoryString(argvArg.memory) : 512 * 1024 * 1024; const projectDir = argvArg.cwd || process.cwd(); // Parse the command to determine if we need to handle .ts files let actualCommand: string; let processArgs: string[] | undefined = undefined; // Split the script to check if it's a single .ts file or a full command const scriptParts = script.split(' '); const firstPart = scriptParts[0]; // Check if this is a direct .ts file execution (single argument ending in .ts) if (scriptParts.length === 1 && firstPart.endsWith('.ts')) { try { const tsxPath = await (async () => { const { createRequire } = await import('module'); const require = createRequire(import.meta.url); return require.resolve('tsx/dist/cli.mjs'); })(); const scriptPath = plugins.path.isAbsolute(firstPart) ? firstPart : plugins.path.join(projectDir, firstPart); actualCommand = tsxPath; processArgs = [scriptPath]; } catch { actualCommand = 'tsx'; processArgs = [firstPart]; } } else { // For multi-word commands, use the entire script as the command // This handles cases like "pnpm start", "npm run dev", etc. actualCommand = script; processArgs = undefined; } const name = argvArg.name || script; const watch = argvArg.watch || false; const autorestart = argvArg.autorestart !== false; // default true const watchPaths = argvArg.watchPaths ? typeof argvArg.watchPaths === 'string' ? (argvArg.watchPaths as string).split(',') : argvArg.watchPaths : undefined; const processConfig: IProcessConfig = { id: name.replace(/[^a-zA-Z0-9-_]/g, '_'), name, command: actualCommand, args: processArgs, projectDir, memoryLimitBytes: memoryLimit, autorestart, watch, watchPaths, }; console.log(`Starting process: ${name}`); console.log( ` Command: ${script}${scriptParts.length === 1 && firstPart.endsWith('.ts') ? ' (via tsx)' : ''}`, ); console.log(` Directory: ${projectDir}`); console.log(` Memory limit: ${formatMemory(memoryLimit)}`); console.log(` Auto-restart: ${autorestart}`); if (watch) { console.log(` Watch mode: enabled`); if (watchPaths) console.log(` Watch paths: ${watchPaths.join(', ')}`); } const response = await tspmIpcClient.request('start', { config: processConfig, }); console.log(`✓ Process started successfully`); console.log(` ID: ${response.processId}`); console.log(` PID: ${response.pid || 'N/A'}`); console.log(` Status: ${response.status}`); }, { actionLabel: 'start process' }, ); }