Skip to main content
Glama
execution.ts4.83 kB
import { spawn, ChildProcess } from "child_process"; import * as fs from "fs/promises"; import * as path from "path"; import { CodeLanguage } from "./types.js"; /** * Validate a filename to prevent path traversal attacks * @param filename User-provided filename * @throws Error if filename contains path separators or special sequences */ function validateFilename(filename: string): void { if (filename.includes('/') || filename.includes('\\') || filename.includes('..')) { throw new Error("Invalid filename: path separators not allowed"); } if (filename.trim().length === 0) { throw new Error("Invalid filename: cannot be empty"); } } export interface ExecutionResult { stdout: string; stderr: string; exitCode: number | null; success: boolean; } export interface ExecutionOptions { cwd: string; env?: Record<string, string>; timeout?: number; // milliseconds } /** * Execute a JavaScript file using Node.js */ export async function executeJavaScript( filepath: string, options: ExecutionOptions ): Promise<ExecutionResult> { return executeCommand("node", [filepath], options); } /** * Execute a TypeScript file using tsx */ export async function executeTypeScript( filepath: string, options: ExecutionOptions ): Promise<ExecutionResult> { // tsx is a fast TypeScript execution engine return executeCommand("npx", ["tsx", filepath], options); } /** * Install npm dependencies in a directory */ export async function installDependencies( options: ExecutionOptions ): Promise<ExecutionResult> { return executeCommand("npm", ["install"], options); } /** * Generic command execution helper */ async function executeCommand( command: string, args: string[], options: ExecutionOptions ): Promise<ExecutionResult> { return new Promise((resolve) => { const timeout = options.timeout || 30000; // 30 seconds default let stdout = ""; let stderr = ""; let exitCode: number | null = null; let timedOut = false; const child: ChildProcess = spawn(command, args, { cwd: options.cwd, env: { ...process.env, ...options.env }, // shell: false (default) - Removed shell: true to prevent command injection }); // Set up timeout const timeoutId = setTimeout(() => { timedOut = true; child.kill("SIGTERM"); stderr += `\nExecution timed out after ${timeout}ms`; }, timeout); // Capture stdout child.stdout?.on("data", (data: Buffer) => { stdout += data.toString(); }); // Capture stderr child.stderr?.on("data", (data: Buffer) => { stderr += data.toString(); }); // Handle exit child.on("exit", (code: number | null) => { clearTimeout(timeoutId); exitCode = code; }); // Handle close (fired after exit) child.on("close", () => { resolve({ stdout, stderr, exitCode: timedOut ? -1 : exitCode, success: !timedOut && exitCode === 0, }); }); // Handle errors child.on("error", (error: Error) => { clearTimeout(timeoutId); resolve({ stdout, stderr: stderr + `\nError: ${error.message}`, exitCode: -1, success: false, }); }); }); } /** * Execute a code cell by language */ export async function executeCodeCell( filename: string, language: CodeLanguage, options: ExecutionOptions ): Promise<ExecutionResult> { const filepath = path.join(options.cwd, "src", filename); // Check if file exists try { await fs.access(filepath); } catch (error) { return { stdout: "", stderr: `Error: File not found: ${filepath}`, exitCode: -1, success: false, }; } // Execute based on language if (language === "javascript") { return executeJavaScript(filepath, options); } else { return executeTypeScript(filepath, options); } } /** * Write a code cell to disk */ export async function writeCodeCellToDisk( notebookDir: string, filename: string, source: string ): Promise<void> { // Validate filename to prevent path traversal validateFilename(filename); const srcDir = path.join(notebookDir, "src"); await fs.mkdir(srcDir, { recursive: true }); const filepath = path.join(srcDir, filename); await fs.writeFile(filepath, source, "utf8"); } /** * Write package.json to disk */ export async function writePackageJsonToDisk( notebookDir: string, source: string ): Promise<void> { const filepath = path.join(notebookDir, "package.json"); await fs.writeFile(filepath, source, "utf8"); } /** * Write tsconfig.json to disk */ export async function writeTsconfigToDisk( notebookDir: string, source: string ): Promise<void> { const filepath = path.join(notebookDir, "tsconfig.json"); await fs.writeFile(filepath, source, "utf8"); }

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/glassBead-tc/Thoughtbox'

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