feat(dev): add containerized development environment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
128b30cdf1
commit
20f857a55a
6 changed files with 385 additions and 24 deletions
119
Dockerfile
119
Dockerfile
|
|
@ -1,38 +1,109 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
# ── Stage 1: Build ────────────────────────────────────────────
|
# ── Stage 1: Build ────────────────────────────────────────────
|
||||||
FROM rust:1.93-slim-bookworm AS builder
|
FROM rust:1.93-slim AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY Cargo.toml Cargo.lock ./
|
|
||||||
COPY src/ src/
|
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 1. Copy manifests to cache dependencies
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
# Create dummy main.rs to build dependencies
|
||||||
|
RUN mkdir src && echo "fn main() {}" > src/main.rs
|
||||||
|
RUN cargo build --release --locked
|
||||||
|
RUN rm -rf src
|
||||||
|
|
||||||
|
# 2. Copy source code
|
||||||
|
COPY . .
|
||||||
|
# Touch main.rs to force rebuild
|
||||||
|
RUN touch src/main.rs
|
||||||
RUN cargo build --release --locked && \
|
RUN cargo build --release --locked && \
|
||||||
strip target/release/zeroclaw
|
strip target/release/zeroclaw
|
||||||
|
|
||||||
# ── Stage 2: Runtime (distroless, runs as root for /data write access) ──
|
# ── Stage 2: Permissions & Config Prep ───────────────────────
|
||||||
FROM gcr.io/distroless/cc-debian12
|
FROM busybox:latest AS permissions
|
||||||
|
# Create directory structure (simplified workspace path)
|
||||||
|
RUN mkdir -p /zeroclaw-data/.zeroclaw /zeroclaw-data/workspace
|
||||||
|
|
||||||
|
# Create minimal config for PRODUCTION (allows binding to public interfaces)
|
||||||
|
# NOTE: Provider configuration must be done via environment variables at runtime
|
||||||
|
RUN cat > /zeroclaw-data/.zeroclaw/config.toml << 'EOF'
|
||||||
|
workspace_dir = "/zeroclaw-data/workspace"
|
||||||
|
config_path = "/zeroclaw-data/.zeroclaw/config.toml"
|
||||||
|
api_key = ""
|
||||||
|
default_provider = "openrouter"
|
||||||
|
default_model = "anthropic/claude-sonnet-4-20250514"
|
||||||
|
default_temperature = 0.7
|
||||||
|
|
||||||
|
[gateway]
|
||||||
|
port = 3000
|
||||||
|
host = "[::]"
|
||||||
|
allow_public_bind = true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUN chown -R 65534:65534 /zeroclaw-data
|
||||||
|
|
||||||
|
# ── Stage 3: Development Runtime (Debian) ────────────────────
|
||||||
|
FROM debian:bookworm-slim AS dev
|
||||||
|
|
||||||
|
# Install runtime dependencies + basic debug tools
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ca-certificates \
|
||||||
|
openssl \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
iputils-ping \
|
||||||
|
vim \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=permissions /zeroclaw-data /zeroclaw-data
|
||||||
COPY --from=builder /app/target/release/zeroclaw /usr/local/bin/zeroclaw
|
COPY --from=builder /app/target/release/zeroclaw /usr/local/bin/zeroclaw
|
||||||
|
|
||||||
# Default workspace and data directory (owned by nonroot user)
|
# Overwrite minimal config with DEV template (Ollama defaults)
|
||||||
VOLUME ["/data"]
|
COPY dev/config.template.toml /zeroclaw-data/.zeroclaw/config.toml
|
||||||
ENV ZEROCLAW_WORKSPACE=/data/workspace
|
RUN chown 65534:65534 /zeroclaw-data/.zeroclaw/config.toml
|
||||||
|
|
||||||
# ── Environment variable configuration (Docker-native setup) ──
|
# Environment setup
|
||||||
# These can be overridden at runtime via docker run -e or docker-compose
|
# Use consistent workspace path
|
||||||
#
|
ENV ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace
|
||||||
# Required:
|
ENV HOME=/zeroclaw-data
|
||||||
# API_KEY or ZEROCLAW_API_KEY - Your LLM provider API key
|
# Defaults for local dev (Ollama) - matches config.template.toml
|
||||||
#
|
ENV PROVIDER="ollama"
|
||||||
# Optional:
|
ENV ZEROCLAW_MODEL="llama3.2"
|
||||||
# PROVIDER or ZEROCLAW_PROVIDER - LLM provider (default: openrouter)
|
ENV ZEROCLAW_GATEWAY_PORT=3000
|
||||||
# Options: openrouter, openai, anthropic, ollama
|
|
||||||
# ZEROCLAW_MODEL - Model to use (default: anthropic/claude-sonnet-4-20250514)
|
|
||||||
# PORT or ZEROCLAW_GATEWAY_PORT - Gateway port (default: 3000)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# docker run -e API_KEY=sk-... -e PROVIDER=openrouter zeroclaw/zeroclaw
|
|
||||||
|
|
||||||
|
# Note: API_KEY is intentionally NOT set here to avoid confusion.
|
||||||
|
# It is set in config.toml as the Ollama URL.
|
||||||
|
|
||||||
|
WORKDIR /zeroclaw-data
|
||||||
|
USER 65534:65534
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
ENTRYPOINT ["zeroclaw"]
|
ENTRYPOINT ["zeroclaw"]
|
||||||
CMD ["gateway"]
|
CMD ["gateway", "--port", "3000", "--host", "[::]"]
|
||||||
|
|
||||||
|
# ── Stage 4: Production Runtime (Distroless) ─────────────────
|
||||||
|
FROM gcr.io/distroless/cc-debian12:nonroot AS release
|
||||||
|
|
||||||
|
COPY --from=builder /app/target/release/zeroclaw /usr/local/bin/zeroclaw
|
||||||
|
COPY --from=permissions /zeroclaw-data /zeroclaw-data
|
||||||
|
|
||||||
|
# Environment setup
|
||||||
|
ENV ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace
|
||||||
|
ENV HOME=/zeroclaw-data
|
||||||
|
# Defaults for prod (OpenRouter)
|
||||||
|
ENV PROVIDER="openrouter"
|
||||||
|
ENV ZEROCLAW_MODEL="anthropic/claude-sonnet-4-20250514"
|
||||||
|
ENV ZEROCLAW_GATEWAY_PORT=3000
|
||||||
|
|
||||||
|
# API_KEY must be provided at runtime!
|
||||||
|
|
||||||
|
WORKDIR /zeroclaw-data
|
||||||
|
USER 65534:65534
|
||||||
|
EXPOSE 3000
|
||||||
|
ENTRYPOINT ["zeroclaw"]
|
||||||
|
CMD ["gateway", "--port", "3000", "--host", "[::]"]
|
||||||
|
|
|
||||||
71
dev/README.md
Normal file
71
dev/README.md
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# ZeroClaw Development Environment
|
||||||
|
|
||||||
|
A fully containerized development sandbox for ZeroClaw agents. This environment allows you to develop, test, and debug the agent in isolation without modifying your host system.
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
- **`agent/`**: (Merged into root Dockerfile)
|
||||||
|
- The development image is built from the root `Dockerfile` using the `dev` stage (`target: dev`).
|
||||||
|
- Based on `debian:bookworm-slim` (unlike production `distroless`).
|
||||||
|
- Includes `bash`, `curl`, and debug tools.
|
||||||
|
- **`sandbox/`**: Dockerfile for the simulated user environment.
|
||||||
|
- Based on `ubuntu:22.04`.
|
||||||
|
- Pre-loaded with `git`, `python3`, `nodejs`, `npm`, `gcc`, `make`.
|
||||||
|
- Simulates a real developer machine.
|
||||||
|
- **`docker-compose.yml`**: Defines the services and `dev-net` network.
|
||||||
|
- **`cli.sh`**: Helper script to manage the lifecycle.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run all commands from the repository root using the helper script:
|
||||||
|
|
||||||
|
### 1. Start Environment
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh up
|
||||||
|
```
|
||||||
|
Builds the agent from source and starts both containers.
|
||||||
|
|
||||||
|
### 2. Enter Agent Container (`zeroclaw-dev`)
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh agent
|
||||||
|
```
|
||||||
|
Use this to run `zeroclaw` CLI commands manually, debug the binary, or check logs internally.
|
||||||
|
- **Path**: `/zeroclaw-data`
|
||||||
|
- **User**: `nobody` (65534)
|
||||||
|
|
||||||
|
### 3. Enter Sandbox (`sandbox`)
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh shell
|
||||||
|
```
|
||||||
|
Use this to act as the "user" or "environment" the agent interacts with.
|
||||||
|
- **Path**: `/home/developer/workspace`
|
||||||
|
- **User**: `developer` (sudo-enabled)
|
||||||
|
|
||||||
|
### 4. Development Cycle
|
||||||
|
1. Make changes to Rust code in `src/`.
|
||||||
|
2. Rebuild the agent:
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh build
|
||||||
|
```
|
||||||
|
3. Test changes inside the container:
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh agent
|
||||||
|
# inside container:
|
||||||
|
zeroclaw --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Persistence & Shared Workspace
|
||||||
|
The local `playground/` directory (in repo root) is mounted as the shared workspace:
|
||||||
|
- **Agent**: `/zeroclaw-data/workspace`
|
||||||
|
- **Sandbox**: `/home/developer/workspace`
|
||||||
|
|
||||||
|
Files created by the agent are visible to the sandbox user, and vice versa.
|
||||||
|
|
||||||
|
The agent configuration lives in `target/.zeroclaw` (mounted to `/zeroclaw-data/.zeroclaw`), so settings persist across container rebuilds.
|
||||||
|
|
||||||
|
### 6. Cleanup
|
||||||
|
Stop containers and remove volumes and generated config:
|
||||||
|
```bash
|
||||||
|
./dev/cli.sh clean
|
||||||
|
```
|
||||||
|
**Note:** This removes `target/.zeroclaw` (config/DB) but leaves the `playground/` directory intact. To fully wipe everything, manually delete `playground/`.
|
||||||
114
dev/cli.sh
Executable file
114
dev/cli.sh
Executable file
|
|
@ -0,0 +1,114 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Detect execution context (root or dev/)
|
||||||
|
if [ -f "dev/docker-compose.yml" ]; then
|
||||||
|
BASE_DIR="dev"
|
||||||
|
HOST_TARGET_DIR="target"
|
||||||
|
elif [ -f "docker-compose.yml" ] && [ "$(basename "$(pwd)")" == "dev" ]; then
|
||||||
|
BASE_DIR="."
|
||||||
|
HOST_TARGET_DIR="../target"
|
||||||
|
else
|
||||||
|
echo "❌ Error: Run this script from the project root or dev/ directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMPOSE_FILE="$BASE_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
function ensure_config {
|
||||||
|
CONFIG_DIR="$HOST_TARGET_DIR/.zeroclaw"
|
||||||
|
CONFIG_FILE="$CONFIG_DIR/config.toml"
|
||||||
|
WORKSPACE_DIR="$CONFIG_DIR/workspace"
|
||||||
|
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
echo -e "${YELLOW}⚙️ Config file missing in target/.zeroclaw. Creating default dev config from template...${NC}"
|
||||||
|
mkdir -p "$WORKSPACE_DIR"
|
||||||
|
|
||||||
|
# Copy template
|
||||||
|
cat "$BASE_DIR/config.template.toml" > "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_help {
|
||||||
|
echo -e "${YELLOW}ZeroClaw Development Environment Manager${NC}"
|
||||||
|
echo "Usage: ./dev/cli.sh [command]"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo -e " ${GREEN}up${NC} Start dev environment (Agent + Sandbox)"
|
||||||
|
echo -e " ${GREEN}down${NC} Stop containers"
|
||||||
|
echo -e " ${GREEN}shell${NC} Enter Sandbox (Ubuntu)"
|
||||||
|
echo -e " ${GREEN}agent${NC} Enter Agent (ZeroClaw CLI)"
|
||||||
|
echo -e " ${GREEN}logs${NC} View logs"
|
||||||
|
echo -e " ${GREEN}build${NC} Rebuild images"
|
||||||
|
echo -e " ${GREEN}clean${NC} Stop and wipe workspace data"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
up)
|
||||||
|
ensure_config
|
||||||
|
echo -e "${GREEN}🚀 Starting Dev Environment...${NC}"
|
||||||
|
# Build context MUST be set correctly for docker compose
|
||||||
|
docker compose -f "$COMPOSE_FILE" up -d
|
||||||
|
echo -e "${GREEN}✅ Environment is running!${NC}"
|
||||||
|
echo -e " - Agent: http://127.0.0.1:3000"
|
||||||
|
echo -e " - Sandbox: running (background)"
|
||||||
|
echo -e " - Config: target/.zeroclaw/config.toml (Edit locally to apply changes)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
down)
|
||||||
|
echo -e "${YELLOW}🛑 Stopping services...${NC}"
|
||||||
|
docker compose -f "$COMPOSE_FILE" down
|
||||||
|
echo -e "${GREEN}✅ Stopped.${NC}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
shell)
|
||||||
|
echo -e "${GREEN}💻 Entering Sandbox (Ubuntu)... (Type 'exit' to leave)${NC}"
|
||||||
|
docker exec -it zeroclaw-sandbox /bin/bash
|
||||||
|
;;
|
||||||
|
|
||||||
|
agent)
|
||||||
|
echo -e "${GREEN}🤖 Entering Agent Container (ZeroClaw)... (Type 'exit' to leave)${NC}"
|
||||||
|
docker exec -it zeroclaw-dev /bin/bash
|
||||||
|
;;
|
||||||
|
|
||||||
|
logs)
|
||||||
|
docker compose -f "$COMPOSE_FILE" logs -f
|
||||||
|
;;
|
||||||
|
|
||||||
|
build)
|
||||||
|
echo -e "${YELLOW}🔨 Rebuilding images...${NC}"
|
||||||
|
docker compose -f "$COMPOSE_FILE" build
|
||||||
|
ensure_config
|
||||||
|
docker compose -f "$COMPOSE_FILE" up -d
|
||||||
|
echo -e "${GREEN}✅ Rebuild complete.${NC}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
clean)
|
||||||
|
echo -e "${RED}⚠️ WARNING: This will delete 'target/.zeroclaw' data and Docker volumes.${NC}"
|
||||||
|
read -p "Are you sure? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
docker compose -f "$COMPOSE_FILE" down -v
|
||||||
|
rm -rf "$HOST_TARGET_DIR/.zeroclaw"
|
||||||
|
echo -e "${GREEN}🧹 Cleaned up (playground/ remains intact).${NC}"
|
||||||
|
else
|
||||||
|
echo "Cancelled."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
print_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
12
dev/config.template.toml
Normal file
12
dev/config.template.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
workspace_dir = "/zeroclaw-data/workspace"
|
||||||
|
config_path = "/zeroclaw-data/.zeroclaw/config.toml"
|
||||||
|
# This is the Ollama Base URL, not a secret key
|
||||||
|
api_key = "http://host.docker.internal:11434"
|
||||||
|
default_provider = "ollama"
|
||||||
|
default_model = "llama3.2"
|
||||||
|
default_temperature = 0.7
|
||||||
|
|
||||||
|
[gateway]
|
||||||
|
port = 3000
|
||||||
|
host = "[::]"
|
||||||
|
allow_public_bind = true
|
||||||
59
dev/docker-compose.yml
Normal file
59
dev/docker-compose.yml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Development Environment for ZeroClaw Agentic Testing
|
||||||
|
#
|
||||||
|
# Use this for:
|
||||||
|
# - Running the agent in a sandboxed environment
|
||||||
|
# - Testing dangerous commands safely
|
||||||
|
# - Developing new skills/integrations
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd dev && ./cli.sh up
|
||||||
|
# or from root: ./dev/cli.sh up
|
||||||
|
name: zeroclaw-dev
|
||||||
|
services:
|
||||||
|
# ── The Agent (Development Image) ──
|
||||||
|
# Builds from source using the 'dev' stage of the root Dockerfile
|
||||||
|
zeroclaw-dev:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
target: dev
|
||||||
|
container_name: zeroclaw-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- API_KEY
|
||||||
|
- PROVIDER
|
||||||
|
- ZEROCLAW_MODEL
|
||||||
|
- ZEROCLAW_GATEWAY_PORT=3000
|
||||||
|
- SANDBOX_HOST=zeroclaw-sandbox
|
||||||
|
volumes:
|
||||||
|
# Mount single config file (avoids shadowing other files in .zeroclaw)
|
||||||
|
- ../target/.zeroclaw/config.toml:/zeroclaw-data/.zeroclaw/config.toml
|
||||||
|
# Mount shared workspace
|
||||||
|
- ../playground:/zeroclaw-data/workspace
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:3000"
|
||||||
|
networks:
|
||||||
|
- dev-net
|
||||||
|
|
||||||
|
# ── The Sandbox (Ubuntu Environment) ──
|
||||||
|
# A fully loaded Ubuntu environment for the agent to play in.
|
||||||
|
sandbox:
|
||||||
|
build:
|
||||||
|
context: sandbox # Context relative to dev/
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: zeroclaw-sandbox
|
||||||
|
hostname: dev-box
|
||||||
|
command: ["tail", "-f", "/dev/null"]
|
||||||
|
working_dir: /home/developer/workspace
|
||||||
|
user: developer
|
||||||
|
environment:
|
||||||
|
- TERM=xterm-256color
|
||||||
|
- SHELL=/bin/bash
|
||||||
|
volumes:
|
||||||
|
- ../playground:/home/developer/workspace # Mount local playground
|
||||||
|
networks:
|
||||||
|
- dev-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
dev-net:
|
||||||
|
driver: bridge
|
||||||
34
dev/sandbox/Dockerfile
Normal file
34
dev/sandbox/Dockerfile
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
# Prevent interactive prompts during package installation
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install common development tools and runtimes
|
||||||
|
# - Node.js: Install v20 (LTS) from NodeSource
|
||||||
|
# - Core: curl, git, vim, build-essential (gcc, make)
|
||||||
|
# - Python: python3, pip
|
||||||
|
# - Network: ping, dnsutils
|
||||||
|
RUN apt-get update && apt-get install -y curl && \
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||||
|
apt-get install -y \
|
||||||
|
nodejs \
|
||||||
|
wget git vim nano unzip zip \
|
||||||
|
build-essential \
|
||||||
|
python3 python3-pip \
|
||||||
|
sudo \
|
||||||
|
iputils-ping dnsutils net-tools \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& node --version && npm --version
|
||||||
|
|
||||||
|
# Create a non-root user 'developer' with UID 1000
|
||||||
|
# Grant passwordless sudo to simulate a local dev environment (using safe sudoers.d)
|
||||||
|
RUN useradd -m -s /bin/bash -u 1000 developer && \
|
||||||
|
echo "developer ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/developer && \
|
||||||
|
chmod 0440 /etc/sudoers.d/developer
|
||||||
|
|
||||||
|
# Set up the workspace
|
||||||
|
USER developer
|
||||||
|
WORKDIR /home/developer/workspace
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["/bin/bash"]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue