feat(agents): implement Code Commander module (Phases 1-5)
- Phase 1: Infrastructure (state, prompts, registry) - Phase 2: Execution engine (AI adapters, security classifier, executors) - Phase 3: Agent integration (graph nodes, routing) - Phase 4: Streaming interaction (PTY terminal, WebSocket) - Phase 5: Frontend integration (Vue components)
This commit is contained in:
112
backend/app/agents/tools/direct_executor.py
Normal file
112
backend/app/agents/tools/direct_executor.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Direct Executor - 直接执行器
|
||||
用于低风险任务,直接执行不隔离
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from app.agents.tools.ai_adapter import AICLIAdapter
|
||||
|
||||
|
||||
class ExecutionResult:
|
||||
"""执行结果"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
success: bool,
|
||||
exit_code: int,
|
||||
stdout: str,
|
||||
stderr: str,
|
||||
):
|
||||
self.success = success
|
||||
self.exit_code = exit_code
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
|
||||
|
||||
class DirectExecutor:
|
||||
"""直接执行器(用于低风险任务)"""
|
||||
|
||||
def __init__(self, adapter: AICLIAdapter, timeout: int = 60):
|
||||
self.adapter = adapter
|
||||
self.timeout = timeout
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
prompt: str,
|
||||
) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
直接执行,不需要沙盒
|
||||
|
||||
Args:
|
||||
prompt: 任务描述
|
||||
|
||||
Yields:
|
||||
str: 实时输出
|
||||
"""
|
||||
# 1. 检查 CLI 是否安装
|
||||
if not self.adapter.is_installed():
|
||||
yield f"[ERROR] {self.adapter.cli_name} is not installed\n"
|
||||
yield f"[ERROR] Please install {self.adapter.cli_name} first\n"
|
||||
return
|
||||
|
||||
# 2. 构建命令
|
||||
cmd = self.adapter.build_command(prompt, None)
|
||||
|
||||
# 3. 异步执行,实时 yield 输出
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
env={**os.environ, "TERM": "xterm-256color"},
|
||||
)
|
||||
|
||||
# 4. 实时读取输出
|
||||
stdout_lines = []
|
||||
stderr_lines = []
|
||||
|
||||
while True:
|
||||
try:
|
||||
line_bytes = await asyncio.wait_for(
|
||||
process.stdout.readline(),
|
||||
timeout=self.timeout,
|
||||
)
|
||||
if not line_bytes:
|
||||
break
|
||||
line = line_bytes.decode("utf-8", errors="replace")
|
||||
stdout_lines.append(line)
|
||||
yield line
|
||||
except asyncio.TimeoutError:
|
||||
process.kill()
|
||||
yield f"\n[ERROR] Execution timed out after {self.timeout}s\n"
|
||||
break
|
||||
|
||||
# 5. 读取 stderr
|
||||
stderr_bytes = await process.communicate()
|
||||
if stderr_bytes[1]:
|
||||
stderr = stderr_bytes[1].decode("utf-8", errors="replace")
|
||||
stderr_lines.append(stderr)
|
||||
yield f"\n[STDERR]\n{stderr}\n"
|
||||
|
||||
# 6. 完成标记
|
||||
yield f"\n[EXIT_CODE] {process.returncode or 0}\n"
|
||||
yield f"\n[COMPLETE] success={process.returncode == 0}\n"
|
||||
|
||||
async def execute_sync(self, prompt: str) -> ExecutionResult:
|
||||
"""同步执行并返回完整结果"""
|
||||
output_parts = []
|
||||
async for line in self.execute(prompt):
|
||||
output_parts.append(line)
|
||||
|
||||
output = "".join(output_parts)
|
||||
return ExecutionResult(
|
||||
success="[COMPLETE] success=True" in output,
|
||||
exit_code=0,
|
||||
stdout=output,
|
||||
stderr="",
|
||||
)
|
||||
Reference in New Issue
Block a user