Files
mini-openclaw-v2/backend/tools/terminal_tool.py
T
qiaoqichaoandClaude Code d56c699ce6 chore(v2): 从兴邦预审v2白名单抽取独立可运行基座
按 docs/v2/05-implementation-plan.md §1 候选迁移表与 claude_use/T00 白名单,
从 55e2cdb 抽取 224 个文件(2.48 MB)到独立目录 F:/mini-OpenClaw-v2。

- 排除:凭据(.env/server-deploy.conf/旧DELIVERY.md)、运行数据(xingbang_data 等
  4 个 SQLite)、依赖(venv/node_modules 符号链接)、归档(_backup*/test-evidence/
  ori_file)、Office 临时文件
- 目标目录独立 git 仓库,初始恢复点;Gitea 远程与分支待 OQ-06 Q2 定案
- 未部署、未启动服务、未调用模型

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-29 12:58:26 +08:00

79 lines
2.2 KiB
Python

"""SafeTerminalTool — sandboxed shell execution with command blacklist."""
import subprocess
from pathlib import Path
from typing import Type
from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field
BLACKLISTED_COMMANDS = [
"rm -rf /",
"rm -rf /*",
"mkfs",
"dd if=",
":(){:|:&};:",
"chmod -R 777 /",
"shutdown",
"reboot",
"halt",
"poweroff",
"format c:",
"del /f /s /q c:",
]
class TerminalInput(BaseModel):
command: str = Field(description="The shell command to execute")
class SafeTerminalTool(BaseTool):
name: str = "terminal"
description: str = (
"Execute shell commands in a sandboxed environment. "
"The working directory is restricted to the project root. "
"Use this for file operations, installing packages, running scripts, etc."
)
args_schema: Type[BaseModel] = TerminalInput
root_dir: str = ""
def _is_safe(self, command: str) -> bool:
cmd_lower = command.lower().strip()
for blocked in BLACKLISTED_COMMANDS:
if blocked in cmd_lower:
return False
return True
def _run(self, command: str) -> str:
if not self._is_safe(command):
return f"❌ Command blocked for safety: {command}"
try:
result = subprocess.run(
command,
shell=True,
cwd=self.root_dir,
capture_output=True,
text=True,
timeout=30,
encoding="utf-8",
errors="replace",
)
output = result.stdout
if result.stderr:
output += f"\n[stderr]: {result.stderr}"
if not output.strip():
output = "(command completed with no output)"
# Truncate very long output
if len(output) > 5000:
output = output[:5000] + "\n...[truncated]"
return output
except subprocess.TimeoutExpired:
return "❌ Command timed out (30s limit)"
except Exception as e:
return f"❌ Error: {str(e)}"
def create_terminal_tool(base_dir: Path) -> SafeTerminalTool:
return SafeTerminalTool(root_dir=str(base_dir))