Skip to main content
Glama

MCP Console Automation Server

by ooples
debug-process-spawning.js11.1 kB
import { spawn, execSync } from 'child_process'; import fs from 'fs'; /** * Debug script to analyze why PowerShell processes aren't completing * * This will test various scenarios: * 1. Basic PowerShell command execution * 2. Different spawn options * 3. Event handler behavior * 4. Alternative approaches (execSync) */ function log(message) { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] ${message}`; console.log(logMessage); fs.appendFileSync('./debug-spawn.log', logMessage + '\n'); } function clearLogFile() { try { fs.writeFileSync('./debug-spawn.log', ''); log('=== DEBUG SPAWN LOG CLEARED ==='); } catch (error) { console.error('Could not clear log file:', error); } } async function testExecSync() { log('=== TESTING EXECSYNC APPROACH ==='); try { const result = execSync('powershell.exe -Command "echo Test successful"', { encoding: 'utf8', timeout: 10000 }); log(`ExecSync SUCCESS: ${result.trim()}`); return { success: true, stdout: result }; } catch (error) { log(`ExecSync FAILED: ${error.message}`); return { success: false, error: error.message }; } } async function testBasicSpawn() { log('=== TESTING BASIC SPAWN APPROACH ==='); return new Promise((resolve) => { const process = spawn('powershell.exe', ['-Command', 'echo Test successful'], { stdio: ['pipe', 'pipe', 'pipe'] }); let stdout = ''; let stderr = ''; let hasExited = false; let timeoutId; log(`Process spawned with PID: ${process.pid}`); // Set up timeout timeoutId = setTimeout(() => { if (!hasExited) { log('TIMEOUT: Process did not exit within 5 seconds'); process.kill('SIGTERM'); setTimeout(() => { if (!hasExited) { log('FORCE KILL: Process still alive after SIGTERM'); process.kill('SIGKILL'); } }, 1000); } }, 5000); // Event handlers process.stdout.on('data', (data) => { const text = data.toString(); stdout += text; log(`STDOUT received: "${text.trim()}"`); }); process.stderr.on('data', (data) => { const text = data.toString(); stderr += text; log(`STDERR received: "${text.trim()}"`); }); process.on('spawn', () => { log('EVENT: spawn - Process has been spawned'); }); process.on('close', (code, signal) => { hasExited = true; clearTimeout(timeoutId); log(`EVENT: close - Code: ${code}, Signal: ${signal}`); log(`Final stdout: "${stdout.trim()}"`); log(`Final stderr: "${stderr.trim()}"`); resolve({ success: code === 0, stdout, stderr, code }); }); process.on('exit', (code, signal) => { log(`EVENT: exit - Code: ${code}, Signal: ${signal}`); }); process.on('error', (error) => { hasExited = true; clearTimeout(timeoutId); log(`EVENT: error - ${error.message}`); resolve({ success: false, error: error.message }); }); process.on('disconnect', () => { log('EVENT: disconnect'); }); }); } async function testSpawnWithExitCommand() { log('=== TESTING SPAWN WITH EXPLICIT EXIT ==='); return new Promise((resolve) => { // Test with explicit exit command const process = spawn('powershell.exe', ['-Command', 'echo Test successful; exit'], { stdio: ['pipe', 'pipe', 'pipe'] }); let stdout = ''; let stderr = ''; let hasExited = false; let timeoutId; log(`Process spawned with PID: ${process.pid}`); timeoutId = setTimeout(() => { if (!hasExited) { log('TIMEOUT: Process with exit command did not complete within 5 seconds'); process.kill('SIGTERM'); } }, 5000); process.stdout.on('data', (data) => { const text = data.toString(); stdout += text; log(`STDOUT (with exit): "${text.trim()}"`); }); process.stderr.on('data', (data) => { const text = data.toString(); stderr += text; log(`STDERR (with exit): "${text.trim()}"`); }); process.on('close', (code, signal) => { hasExited = true; clearTimeout(timeoutId); log(`CLOSE (with exit): Code: ${code}, Signal: ${signal}`); resolve({ success: code === 0, stdout, stderr, code }); }); process.on('error', (error) => { hasExited = true; clearTimeout(timeoutId); log(`ERROR (with exit): ${error.message}`); resolve({ success: false, error: error.message }); }); }); } async function testSpawnNoShell() { log('=== TESTING SPAWN WITH SHELL:FALSE ==='); return new Promise((resolve) => { const process = spawn('powershell.exe', ['-Command', 'echo Test successful; exit'], { stdio: ['pipe', 'pipe', 'pipe'], shell: false // Explicitly disable shell }); let stdout = ''; let stderr = ''; let hasExited = false; let timeoutId; log(`Process (no shell) spawned with PID: ${process.pid}`); timeoutId = setTimeout(() => { if (!hasExited) { log('TIMEOUT: No-shell process did not complete within 5 seconds'); process.kill('SIGTERM'); } }, 5000); process.stdout.on('data', (data) => { const text = data.toString(); stdout += text; log(`STDOUT (no shell): "${text.trim()}"`); }); process.stderr.on('data', (data) => { const text = data.toString(); stderr += text; log(`STDERR (no shell): "${text.trim()}"`); }); process.on('close', (code, signal) => { hasExited = true; clearTimeout(timeoutId); log(`CLOSE (no shell): Code: ${code}, Signal: ${signal}`); resolve({ success: code === 0, stdout, stderr, code }); }); process.on('error', (error) => { hasExited = true; clearTimeout(timeoutId); log(`ERROR (no shell): ${error.message}`); resolve({ success: false, error: error.message }); }); }); } async function testInteractiveMode() { log('=== TESTING INTERACTIVE MODE (CURRENT IMPLEMENTATION) ==='); return new Promise((resolve) => { // This mimics the current LocalProtocol implementation const process = spawn('powershell.exe', ['-NoLogo', '-NoExit'], { stdio: ['pipe', 'pipe', 'pipe'], shell: false }); let stdout = ''; let stderr = ''; let hasExited = false; let timeoutId; log(`Interactive process spawned with PID: ${process.pid}`); timeoutId = setTimeout(() => { if (!hasExited) { log('TIMEOUT: Interactive process test completed (this is expected)'); // Send exit command if (process.stdin) { process.stdin.write('exit\n'); setTimeout(() => { if (!hasExited) { process.kill('SIGTERM'); } }, 1000); } } }, 3000); process.stdout.on('data', (data) => { const text = data.toString(); stdout += text; log(`STDOUT (interactive): "${text.trim()}"`); }); process.stderr.on('data', (data) => { const text = data.toString(); stderr += text; log(`STDERR (interactive): "${text.trim()}"`); }); process.on('close', (code, signal) => { hasExited = true; clearTimeout(timeoutId); log(`CLOSE (interactive): Code: ${code}, Signal: ${signal}`); resolve({ success: true, stdout, stderr, code, note: 'Interactive session closed' }); }); process.on('error', (error) => { hasExited = true; clearTimeout(timeoutId); log(`ERROR (interactive): ${error.message}`); resolve({ success: false, error: error.message }); }); // Send a command after a short delay setTimeout(() => { if (process.stdin && !hasExited) { log('Sending command to interactive session: echo Test successful'); process.stdin.write('echo Test successful\n'); } }, 500); }); } async function runAllTests() { clearLogFile(); log('=== STARTING COMPREHENSIVE PROCESS SPAWNING DIAGNOSTIC ==='); log(`Platform: ${process.platform}`); log(`Node.js version: ${process.version}`); log(`Working directory: ${process.cwd()}`); const results = {}; try { log('\n--- TEST 1: ExecSync ---'); results.execSync = await testExecSync(); log('\n--- TEST 2: Basic Spawn ---'); results.basicSpawn = await testBasicSpawn(); log('\n--- TEST 3: Spawn with Exit ---'); results.spawnWithExit = await testSpawnWithExitCommand(); log('\n--- TEST 4: Spawn No Shell ---'); results.spawnNoShell = await testSpawnNoShell(); log('\n--- TEST 5: Interactive Mode ---'); results.interactiveMode = await testInteractiveMode(); } catch (error) { log(`CRITICAL ERROR: ${error.message}`); } log('\n=== DIAGNOSTIC SUMMARY ==='); for (const [test, result] of Object.entries(results)) { log(`${test}: ${result.success ? 'SUCCESS' : 'FAILED'}`); if (result.stdout) log(` stdout: "${result.stdout.trim()}"`); if (result.stderr) log(` stderr: "${result.stderr.trim()}"`); if (result.error) log(` error: ${result.error}`); if (result.note) log(` note: ${result.note}`); } log('\n=== RECOMMENDATIONS ==='); if (results.execSync.success && !results.basicSpawn.success) { log('RECOMMENDATION: Use execSync for one-shot commands instead of spawn'); } if (results.spawnWithExit.success && !results.basicSpawn.success) { log('RECOMMENDATION: Always append explicit exit commands to PowerShell'); } if (results.spawnNoShell.success && !results.basicSpawn.success) { log('RECOMMENDATION: Set shell: false in spawn options'); } log('\n=== DIAGNOSTIC COMPLETE ==='); log('Check debug-spawn.log for detailed output'); } // Run the diagnostic runAllTests().catch(error => { console.error('Diagnostic failed:', error); log(`DIAGNOSTIC FAILED: ${error.message}`); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ooples/mcp-console-automation'

If you have feedback or need assistance with the MCP directory API, please join our Discord server