diff --git a/README.md b/README.md new file mode 100644 index 0000000..d16f4d2 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# docker-agent-sandbox + +A Docker-based sandbox and LangChain tool set for LLM agents that need a safe, isolated shell environment. + +## What it provides + +- **`DockerSandbox`** — manages a single long-running Docker container: lifecycle (`start`/`stop`), file writes via the tar archive API, and command execution with timeout enforcement. +- **LangChain tools** — a ready-to-use set of tools that expose the sandbox to an agent: `bash`, `read_file`, `write_file`, `edit_file`, `list_dir`, `delete_file`, `move_file`, `copy_file`, `make_dir`, `search_files`, `grep`. + +The sandbox directory is bind-mounted inside the container, so the agent works with stable paths regardless of where the sandbox lives on the host. + +## Requirements + +- Python ≥ 3.11 +- Docker daemon running locally +- A Docker image to run (either pre-built or built via `build_image_if_missing`) + +## Installation + +```bash +pip install docker-agent-sandbox +``` + +Or with uv, pointing at a local checkout: + +```toml +# pyproject.toml +[tool.uv.sources] +docker-agent-sandbox = { path = "../docker-agent-sandbox" } +``` + +## Usage + +### 1. Create and start a sandbox + +```python +from docker_agent_sandbox import DockerSandbox + +sandbox = DockerSandbox( + sandbox_dir="/tmp/my-sandbox", # bind-mounted into the container + container_name="my-agent-run", + container_workdir="/workspace", # path inside the container + image="my-agent-image", + dockerfile_dir="/path/to/dockerfile", # only needed if building the image +) + +sandbox.build_image_if_missing() +sandbox.start() +``` + +### 2. Run commands and write files + +```python +exit_code, output = sandbox.exec("ls -la") +sandbox.write_file("/workspace/hello.py", "print('hello')") +exit_code, output = sandbox.exec("python /workspace/hello.py") +``` + +### 3. Bind tools to a LangChain agent + +```python +from docker_agent_sandbox import make_bash_tool, make_file_ops_tools + +tools = make_file_ops_tools(sandbox) + [make_bash_tool(sandbox)] + +# Pass `tools` to your LangChain / LangGraph agent as usual. +``` + +### 4. Tear down + +```python +sandbox.stop() +``` + +## API reference + +### `DockerSandbox(sandbox_dir, container_name, container_workdir, pin_mcp_port, image, dockerfile_dir)` + +| Parameter | Default | Description | +|---|---|---| +| `sandbox_dir` | — | Host directory bind-mounted into the container | +| `container_name` | — | Docker container name | +| `container_workdir` | `"/workspace"` | Working directory inside the container | +| `pin_mcp_port` | `8080` | Port exposed by a sidecar MCP server (if any) | +| `image` | `"docker-agent-sandbox"` | Docker image tag to run | +| `dockerfile_dir` | `None` | Directory containing the `Dockerfile`; required only if calling `build_image_if_missing` | + +#### Methods + +| Method | Description | +|---|---| +| `build_image_if_missing()` | Builds the image from `dockerfile_dir` if not already present locally | +| `start()` | Removes any existing container with the same name, starts a fresh one, and waits for the MCP port | +| `stop()` | Removes the container | +| `exec(command, timeout=120)` | Runs a bash command; returns `(exit_code, stdout+stderr)` | +| `write_file(path, content)` | Writes a UTF-8 file inside the container using the tar archive API | + +### Tools + +| Factory | Tool name | Description | +|---|---|---| +| `make_bash_tool(sandbox)` | `bash` | Execute a shell command | +| `make_file_ops_tools(sandbox)` | — | Returns all tools below as a list | +| `make_read_file_tool(sandbox)` | `read_file` | Read a file with offset/length pagination | +| `make_write_file_tool(sandbox)` | `write_file` | Write a file | +| `make_edit_file_tool(sandbox)` | `edit_file` | `str_replace`-style editing | +| `make_list_dir_tool(sandbox)` | `list_dir` | `ls -lA` a directory | +| `make_delete_file_tool(sandbox)` | `delete_file` | Delete a file or empty directory | +| `make_move_file_tool(sandbox)` | `move_file` | Move or rename | +| `make_copy_file_tool(sandbox)` | `copy_file` | Copy a file | +| `make_make_dir_tool(sandbox)` | `make_dir` | `mkdir -p` | +| `make_search_files_tool(sandbox)` | `search_files` | `find` by name glob | +| `make_grep_tool(sandbox)` | `grep` | Extended-regex search with optional recursion | + +## Security notes + +Containers are started with `cap_drop=ALL`, `cap_add=SYS_PTRACE` (needed for debuggers/tracers), and `no-new-privileges`. Network mode is `bridge` with no explicit outbound rules — restrict further via Docker network policies if needed.