* Start a command process
(params: StartCommandParams)
| 362 | * Start a command process |
| 363 | */ |
| 364 | async function handleStartCommand(params: StartCommandParams): Promise<StartProcessResponse> { |
| 365 | const startTime = Date.now() |
| 366 | logger.info("[Process:startCommand] Starting command", JSON.stringify({ |
| 367 | cmd: params.cmd, |
| 368 | args: params.args, |
| 369 | cwd: params.cwd, |
| 370 | hasEnv: !!params.env, |
| 371 | timeoutMs: params.timeoutMs, |
| 372 | })) |
| 373 | |
| 374 | try { |
| 375 | validateCwd(params.cwd) |
| 376 | |
| 377 | // Check process limit |
| 378 | if (activeProcesses.size >= MAX_ACTIVE_PROCESSES) { |
| 379 | throw new Error(`Maximum active processes (${MAX_ACTIVE_PROCESSES}) reached`) |
| 380 | } |
| 381 | |
| 382 | const processId = generateProcessId() |
| 383 | const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS |
| 384 | |
| 385 | // Spawn the process in a new process group so we can kill the entire tree |
| 386 | const childProcess = spawn(params.cmd, params.args || [], { |
| 387 | cwd: params.cwd, |
| 388 | env: { ...process.env, ...params.env }, |
| 389 | stdio: ["ignore", "pipe", "pipe"], |
| 390 | detached: true, |
| 391 | }) |
| 392 | |
| 393 | // Store in active processes |
| 394 | activeProcesses.set(processId, { |
| 395 | process: childProcess, |
| 396 | outputBuffer: [], |
| 397 | bufferSizeBytes: 0, |
| 398 | exitCode: null, |
| 399 | signal: null, |
| 400 | completed: false, |
| 401 | }) |
| 402 | |
| 403 | // Setup handlers |
| 404 | setupProcessHandlers(processId, childProcess, timeoutMs) |
| 405 | emitLifecycle({ |
| 406 | type: "started", |
| 407 | processId, |
| 408 | pid: childProcess.pid, |
| 409 | cwd: params.cwd, |
| 410 | label: [params.cmd, ...(params.args || [])].join(" "), |
| 411 | }) |
| 412 | |
| 413 | logger.info("[Process:startCommand] Command started", JSON.stringify({ processId, pid: childProcess.pid, duration: Date.now() - startTime })) |
| 414 | return { processId } |
| 415 | } catch (error: unknown) { |
| 416 | const errorMessage = error instanceof Error ? error.message : "Unknown error" |
| 417 | logger.error("[Process:startCommand] Error:", JSON.stringify({ error: errorMessage, duration: Date.now() - startTime })) |
| 418 | throw error |
| 419 | } |
| 420 | } |
| 421 |
no test coverage detected