feat(tools): Phase T.1-T.4 complete - manifest system, registry, implementations, runtime, collaboration, scheduler

This commit is contained in:
2026-04-05 11:54:57 +08:00
parent fca7a7cf3d
commit 10d9340c53
30 changed files with 2891 additions and 4 deletions

View File

@@ -0,0 +1,125 @@
"""
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),
}