"""系统工具 - 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, }