Files
JARVIS/backend/app/agents/tools/builtins/system_tools.py

194 lines
5.8 KiB
Python
Raw Normal View History

"""系统工具 - Phase 6.4"""
import asyncio
import shlex
from typing import Any
from app.agents.tools.base import ExternalTool
from app.agents.tools.manifest import (
PermissionClass,
SideEffectScope,
)
class BashTool(ExternalTool):
"""Bash 命令执行工具"""
def __init__(self, working_dir: str = "."):
super().__init__(
name="bash",
description="执行 Bash 命令",
permission_class=PermissionClass.EXTERNAL,
side_effect_scope=SideEffectScope.LOCAL_STATE,
requires_confirmation=True,
tags=["system", "bash", "shell"],
)
self.working_dir = working_dir
def get_parameters(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要执行的 Bash 命令",
},
"timeout": {
"type": "integer",
"description": "超时时间(秒)",
},
"working_dir": {
"type": "string",
"description": "工作目录(可选)",
},
},
"required": ["command"],
}
def get_return_schema(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {
"stdout": {"type": "string"},
"stderr": {"type": "string"},
"returncode": {"type": "integer"},
},
}
async def execute(
self, command: str, timeout: int = 30, working_dir: str | None = None
) -> dict[str, Any]:
import os
cwd = working_dir or self.working_dir
try:
process = await asyncio.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=cwd,
)
try:
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)
except asyncio.TimeoutError:
process.kill()
await process.wait()
return {
"stdout": "",
"stderr": f"Command timed out after {timeout} seconds",
"returncode": -1,
}
return {
"stdout": stdout.decode("utf-8", errors="replace"),
"stderr": stderr.decode("utf-8", errors="replace"),
"returncode": process.returncode,
}
except Exception as e:
return {
"stdout": "",
"stderr": str(e),
"returncode": -1,
}
class PowerShellTool(ExternalTool):
"""PowerShell 命令执行工具"""
def __init__(self, working_dir: str = "."):
super().__init__(
name="powershell",
description="执行 PowerShell 命令",
permission_class=PermissionClass.EXTERNAL,
side_effect_scope=SideEffectScope.LOCAL_STATE,
requires_confirmation=True,
tags=["system", "powershell", "shell"],
)
self.working_dir = working_dir
def get_parameters(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要执行的 PowerShell 命令",
},
"timeout": {
"type": "integer",
"description": "超时时间(秒)",
},
"working_dir": {
"type": "string",
"description": "工作目录(可选)",
},
},
"required": ["command"],
}
def get_return_schema(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {
"stdout": {"type": "string"},
"stderr": {"type": "string"},
"returncode": {"type": "integer"},
},
}
async def execute(
self, command: str, timeout: int = 30, working_dir: str | None = None
) -> dict[str, Any]:
import platform
# 检测是否是 Windows 平台
is_windows = platform.system() == "Windows"
if not is_windows:
# 非 Windows 平台,可能没有 PowerShell
return {
"stdout": "",
"stderr": "PowerShell is not available on this platform",
"returncode": -1,
}
cwd = working_dir or self.working_dir
try:
process = await asyncio.create_subprocess_exec(
"powershell.exe",
"-NoProfile",
"-Command",
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=cwd,
)
try:
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)
except asyncio.TimeoutError:
process.kill()
await process.wait()
return {
"stdout": "",
"stderr": f"Command timed out after {timeout} seconds",
"returncode": -1,
}
return {
"stdout": stdout.decode("utf-8", errors="replace"),
"stderr": stderr.decode("utf-8", errors="replace"),
"returncode": process.returncode,
}
except Exception as e:
return {
"stdout": "",
"stderr": str(e),
"returncode": -1,
}