""" JavaScript Runtime Node.js stdio protocol runtime for JavaScript tools. """ import asyncio import json import shutil from pathlib import Path from typing import Any, Dict, Optional from tools.runtime.base import BaseRuntime class JavaScriptRuntime(BaseRuntime): """JavaScript runtime using Node.js stdio protocol""" def __init__(self, node_path: Optional[str] = None): self.node_path = node_path or self._detect_node() self._validated: bool = False def get_name(self) -> str: return "javascript" def _detect_node(self) -> str: """Detect Node.js executable path""" node = shutil.which("node") if node: return node # Fallback for Windows return "node" async def validate(self, entry: str) -> bool: """Validate Node.js runtime and entry file""" # Check node is available try: result = await asyncio.create_subprocess_exec( self.node_path, "--version", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) await result.wait() if result.returncode != 0: return False except Exception: return False # Check entry file exists path = Path(entry) if not path.exists(): return False self._validated = True return True async def execute( self, entry: str, command: str, parameters: Dict[str, Any], timeout: int, ) -> Dict[str, Any]: """Execute a JavaScript tool via stdio protocol""" if not self._validated: is_valid = await self.validate(entry) if not is_valid: return { "status": "error", "error": "JavaScript runtime not available or entry file invalid", } # Build input data per stdio protocol input_data = { "command": command, "parameters": parameters, } try: process = await asyncio.create_subprocess_exec( self.node_path, entry, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await asyncio.wait_for( process.communicate(input=json.dumps(input_data).encode()), timeout=timeout / 1000, ) if process.returncode != 0: return { "status": "error", "error": stderr.decode() if stderr else "Unknown error", } result_text = stdout.decode() if not result_text: return { "status": "error", "error": "Empty response from Node.js runtime", } try: result = json.loads(result_text) return result except json.JSONDecodeError: return { "status": "success", "result": result_text, } except asyncio.TimeoutError: return { "status": "error", "error": f"Execution timed out after {timeout}ms", } except Exception as e: return { "status": "error", "error": str(e), }