Stub implementations for: - base.py: Tool, ToolResult, @tool decorator, registry - calculate.py: Math expressions (simpleeval) - fetch.py: HTTP requests (aiohttp) - files.py: read_file, write_file, list_dir - shell.py: run_command (sandboxed) - search.py: web_search - keyvalue.py: key_value_get/set/delete (in-memory stub) - librarian.py: exist-db integration (store, get, query, search) All stubs return "Not implemented" - ready for real implementation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
135 lines
3.5 KiB
Python
135 lines
3.5 KiB
Python
"""
|
|
File tools - sandboxed file system operations.
|
|
"""
|
|
|
|
from typing import Optional, List
|
|
from pathlib import Path
|
|
from .base import tool, ToolResult
|
|
|
|
|
|
# TODO: Configure allowed paths
|
|
ALLOWED_PATHS: List[Path] = []
|
|
|
|
|
|
def _validate_path(path: str) -> Optional[str]:
|
|
"""Validate path is within allowed directories."""
|
|
# TODO: Implement chroot validation
|
|
# resolved = Path(path).resolve()
|
|
# for allowed in ALLOWED_PATHS:
|
|
# if resolved.is_relative_to(allowed):
|
|
# return None
|
|
# return f"Path {path} not in allowed directories"
|
|
return None # Stub: allow all for now
|
|
|
|
|
|
@tool
|
|
async def read_file(
|
|
path: str,
|
|
encoding: str = "utf-8",
|
|
binary: bool = False,
|
|
) -> ToolResult:
|
|
"""
|
|
Read contents of a file.
|
|
|
|
Args:
|
|
path: Path to file
|
|
encoding: Text encoding (default: utf-8)
|
|
binary: Return base64 if true (default: false)
|
|
|
|
Security:
|
|
- Chroot to allowed directories
|
|
- No path traversal (..)
|
|
- Size limit enforced
|
|
"""
|
|
if error := _validate_path(path):
|
|
return ToolResult(success=False, error=error)
|
|
|
|
# TODO: Implement
|
|
# try:
|
|
# p = Path(path)
|
|
# if binary:
|
|
# import base64
|
|
# content = base64.b64encode(p.read_bytes()).decode()
|
|
# else:
|
|
# content = p.read_text(encoding=encoding)
|
|
# return ToolResult(success=True, data=content)
|
|
# except Exception as e:
|
|
# return ToolResult(success=False, error=str(e))
|
|
|
|
return ToolResult(success=False, error="Not implemented")
|
|
|
|
|
|
@tool
|
|
async def write_file(
|
|
path: str,
|
|
content: str,
|
|
mode: str = "overwrite",
|
|
encoding: str = "utf-8",
|
|
) -> ToolResult:
|
|
"""
|
|
Write content to a file.
|
|
|
|
Args:
|
|
path: Path to file
|
|
content: Content to write
|
|
mode: "overwrite" or "append" (default: overwrite)
|
|
encoding: Text encoding (default: utf-8)
|
|
|
|
Security:
|
|
- Chroot to allowed directories
|
|
- No path traversal
|
|
- Max file size enforced
|
|
"""
|
|
if error := _validate_path(path):
|
|
return ToolResult(success=False, error=error)
|
|
|
|
# TODO: Implement
|
|
# try:
|
|
# p = Path(path)
|
|
# if mode == "append":
|
|
# with open(p, "a", encoding=encoding) as f:
|
|
# f.write(content)
|
|
# else:
|
|
# p.write_text(content, encoding=encoding)
|
|
# return ToolResult(success=True, data={"bytes_written": len(content.encode(encoding))})
|
|
# except Exception as e:
|
|
# return ToolResult(success=False, error=str(e))
|
|
|
|
return ToolResult(success=False, error="Not implemented")
|
|
|
|
|
|
@tool
|
|
async def list_dir(
|
|
path: str,
|
|
pattern: str = "*",
|
|
) -> ToolResult:
|
|
"""
|
|
List directory contents.
|
|
|
|
Args:
|
|
path: Directory path
|
|
pattern: Glob pattern filter (default: *)
|
|
|
|
Returns:
|
|
Array of {name, type, size, modified}
|
|
"""
|
|
if error := _validate_path(path):
|
|
return ToolResult(success=False, error=error)
|
|
|
|
# TODO: Implement
|
|
# try:
|
|
# p = Path(path)
|
|
# entries = []
|
|
# for entry in p.glob(pattern):
|
|
# stat = entry.stat()
|
|
# entries.append({
|
|
# "name": entry.name,
|
|
# "type": "dir" if entry.is_dir() else "file",
|
|
# "size": stat.st_size,
|
|
# "modified": stat.st_mtime,
|
|
# })
|
|
# return ToolResult(success=True, data=entries)
|
|
# except Exception as e:
|
|
# return ToolResult(success=False, error=str(e))
|
|
|
|
return ToolResult(success=False, error="Not implemented")
|