Skip to main content
Glama
bsim0927
by bsim0927
executor.js4.71 kB
/** * Execute the TexasSolver binary */ import { spawn } from 'child_process'; import fs from 'fs/promises'; import path from 'path'; export class SolverExecutor { constructor(solverPath, options = {}) { this.solverPath = solverPath; this.timeout = options.timeout || 10 * 60 * 1000; // 10 minutes default this.solverDir = path.dirname(solverPath); } /** * Execute the solver with a command file * @param {string} commandFilePath - Path to command file * @param {string} outputFilePath - Path where output should be written * @returns {Promise<Object>} Execution result */ async execute(commandFilePath, outputFilePath) { return new Promise((resolve, reject) => { const startTime = Date.now(); let stdout = ''; let stderr = ''; let timedOut = false; // Spawn solver process const solverProcess = spawn(this.solverPath, [], { cwd: this.solverDir, stdio: ['pipe', 'pipe', 'pipe'], timeout: this.timeout }); // Read and pipe command file to stdin fs.readFile(commandFilePath, 'utf8') .then(commandText => { solverProcess.stdin.write(commandText); solverProcess.stdin.end(); }) .catch(error => { solverProcess.kill(); reject(new Error(`Failed to read command file: ${error.message}`)); }); // Capture stdout solverProcess.stdout.on('data', (data) => { stdout += data.toString(); }); // Capture stderr solverProcess.stderr.on('data', (data) => { stderr += data.toString(); }); // Handle process completion solverProcess.on('close', async (code) => { const executionTime = Date.now() - startTime; // Verify output file exists const outputExists = await this._outputFileExists(outputFilePath); if (code === 0 && outputExists) { resolve({ success: true, output_file: outputFilePath, command_file: commandFilePath, execution_time_ms: executionTime, solver_log: stdout, stderr: stderr }); } else if (timedOut) { reject(new Error(`Solver execution timed out after ${this.timeout}ms`)); } else if (!outputExists) { reject(new Error( `Solver did not produce output file. Exit code: ${code}\n` + `Stderr: ${stderr}\n` + `Stdout: ${stdout}` )); } else { reject(new Error( `Solver exited with code ${code}\n` + `Stderr: ${stderr}\n` + `Stdout: ${stdout}` )); } }); // Handle process errors solverProcess.on('error', (err) => { reject(new Error(`Failed to start solver process: ${err.message}`)); }); // Set timeout const timeoutHandle = setTimeout(() => { timedOut = true; solverProcess.kill('SIGTERM'); // If SIGTERM doesn't work, try SIGKILL after 5 seconds const killTimeout = setTimeout(() => { solverProcess.kill('SIGKILL'); }, 5000); solverProcess.on('exit', () => clearTimeout(killTimeout)); }, this.timeout); // Clear timeout when process ends solverProcess.on('exit', () => clearTimeout(timeoutHandle)); }); } /** * Check if solver binary is executable * @param {string} solverPath - Path to solver * @returns {Promise<boolean>} */ static async validateSolver(solverPath) { try { const stats = await fs.stat(solverPath); if (!stats.isFile()) { return false; } // Check if executable (Unix permission check) return (stats.mode & parseInt('0111', 8)) !== 0; } catch { return false; } } /** * Check if output file exists and has content * @private * @param {string} filepath * @returns {Promise<boolean>} */ async _outputFileExists(filepath) { try { const stats = await fs.stat(filepath); return stats.isFile() && stats.size > 0; } catch { return false; } } /** * Get information about the solver * @returns {Promise<Object>} */ async getSolverInfo() { try { const stats = await fs.stat(this.solverPath); return { path: this.solverPath, exists: true, size_bytes: stats.size, is_executable: (stats.mode & parseInt('0111', 8)) !== 0, modified: stats.mtime.toISOString() }; } catch (error) { return { path: this.solverPath, exists: false, error: error.message }; } } } export default SolverExecutor;

Latest Blog Posts

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/bsim0927/texas-solver-mcp-server'

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