69 lines
1.9 KiB
Python
69 lines
1.9 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from core.subprocess import run_command
|
|
from core.tools.base import BaseTool, ToolContext
|
|
from core.tools.registry import registry
|
|
from core.safety import safety
|
|
|
|
|
|
class SubprocessTool(BaseTool):
|
|
name = "subprocess"
|
|
description = "Run a subprocess command safely"
|
|
|
|
def execute(
|
|
self,
|
|
payload: dict[str, Any],
|
|
ctx: ToolContext
|
|
):
|
|
cmd = payload.get("cmd")
|
|
cwd = payload.get("cwd")
|
|
timeout = payload.get("timeout", 60)
|
|
|
|
# -------------------------
|
|
# Validate command
|
|
# -------------------------
|
|
if not isinstance(cmd, list) or not all(isinstance(c, str) for c in cmd):
|
|
raise ValueError("cmd must be list[str]")
|
|
|
|
# Optional safety: block empty commands
|
|
if not cmd:
|
|
raise ValueError("cmd cannot be empty")
|
|
|
|
# -------------------------
|
|
# Validate cwd
|
|
# -------------------------
|
|
if cwd is not None:
|
|
if not isinstance(cwd, str):
|
|
raise ValueError("cwd must be string")
|
|
|
|
cwd_path = safety.validate_path(cwd)
|
|
cwd = str(cwd_path)
|
|
|
|
# -------------------------
|
|
# Validate timeout
|
|
# -------------------------
|
|
if not isinstance(timeout, int) or timeout <= 0:
|
|
raise ValueError("timeout must be positive int")
|
|
|
|
# -------------------------
|
|
# Dry-run support (future-proofing)
|
|
# -------------------------
|
|
if getattr(ctx, "dry_run", False):
|
|
return {
|
|
"dry_run": True,
|
|
"cmd": cmd,
|
|
"cwd": cwd,
|
|
"timeout": timeout,
|
|
"message": "Would execute subprocess"
|
|
}
|
|
|
|
return run_command(
|
|
cmd=cmd,
|
|
cwd=cwd,
|
|
timeout=timeout
|
|
)
|
|
|
|
|
|
registry.register(SubprocessTool()) |
