feat: 增加shell操作

This commit is contained in:
ViperEkura 2026-04-15 21:00:29 +08:00
parent 99e7de9efd
commit 22a4b8a4bb
4 changed files with 109 additions and 3 deletions

View File

@ -1,7 +1,7 @@
# 配置文件
app:
secret_key: ${APP_SECRET_KEY}
debug: false
debug: true
host: 0.0.0.0
port: 8000

View File

@ -10,7 +10,7 @@ from luxx.config import config
engine = create_engine(
config.database_url,
connect_args={"check_same_thread": False} if "sqlite" in config.database_url else {},
echo=config.debug
echo=False
)
# Create session factory

View File

@ -4,5 +4,6 @@ from luxx.tools.builtin import crawler
from luxx.tools.builtin import code
from luxx.tools.builtin import data
from luxx.tools.builtin import file
from luxx.tools.builtin import shell
__all__ = ["crawler", "code", "data", "file"]
__all__ = ["crawler", "code", "data", "file", "shell"]

105
luxx/tools/builtin/shell.py Normal file
View File

@ -0,0 +1,105 @@
"""Shell command execution tool"""
import subprocess
from typing import Dict, Any
from luxx.tools.factory import tool
from luxx.tools.core import ToolContext, CommandPermission
from luxx.config import config
from pathlib import Path
def get_workspace_from_context(context: ToolContext) -> str:
"""Get workspace from context, auto-create if needed"""
import hashlib
from pathlib import Path
if context.workspace:
return context.workspace
if context.user_id is not None:
workspace_root = Path(config.workspace_root).resolve()
user_hash = hashlib.sha256(str(context.user_id).encode()).hexdigest()[:16]
user_workspace = workspace_root / user_hash
if config.workspace_auto_create and not user_workspace.exists():
user_workspace.mkdir(parents=True, exist_ok=True)
return str(user_workspace)
return None
@tool(
name="shell_exec",
description="Execute a shell command in the user's workspace directory",
parameters={
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "Shell command to execute"
},
"timeout": {
"type": "integer",
"description": "Timeout in seconds (default 30)",
"default": 30
},
"cwd": {
"type": "string",
"description": "Working directory relative to workspace (default: workspace root)",
"default": "."
}
},
"required": ["command"]
},
required_params=["command"],
category="shell",
required_permission=CommandPermission.EXECUTE
)
def shell_exec(arguments: Dict[str, Any], context: ToolContext = None):
"""
Execute a shell command in the user's workspace.
Args:
command: Shell command to execute
timeout: Timeout in seconds (default 30)
cwd: Working directory relative to workspace (default: workspace root)
Returns:
{"stdout": str, "stderr": str, "returncode": int}
"""
workspace = get_workspace_from_context(context)
if not workspace:
return {"error": "Workspace not configured"}
command = arguments.get("command", "")
timeout = arguments.get("timeout", 30)
cwd_rel = arguments.get("cwd", ".")
# Build working directory
workspace_path = Path(workspace).resolve()
cwd_path = (workspace_path / cwd_rel).resolve()
# Security check: ensure cwd is within workspace
if not cwd_path.is_relative_to(workspace_path):
return {"error": "Working directory is outside workspace"}
try:
result = subprocess.run(
command,
shell=True,
cwd=str(cwd_path),
capture_output=True,
text=True,
timeout=min(timeout, 120) # Max 2 minutes
)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode,
"command": command,
"cwd": str(cwd_path.relative_to(workspace_path))
}
except subprocess.TimeoutExpired:
return {"error": f"Command timed out after {timeout} seconds"}
except Exception as e:
return {"error": f"Execution error: {str(e)}"}