diff --git a/Dockerfile b/Dockerfile index e9d3497..d475b28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,109 @@ +# syntax=docker/dockerfile:1 + # ── Stage 1: Build ──────────────────────────────────────────── -FROM rust:1.93-slim-bookworm AS builder +FROM rust:1.93-slim AS builder 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 && \ strip target/release/zeroclaw -# ── Stage 2: Runtime (distroless, runs as root for /data write access) ── -FROM gcr.io/distroless/cc-debian12 +# ── Stage 2: Permissions & Config Prep ─────────────────────── +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 -# Default workspace and data directory (owned by nonroot user) -VOLUME ["/data"] -ENV ZEROCLAW_WORKSPACE=/data/workspace +# Overwrite minimal config with DEV template (Ollama defaults) +COPY dev/config.template.toml /zeroclaw-data/.zeroclaw/config.toml +RUN chown 65534:65534 /zeroclaw-data/.zeroclaw/config.toml -# ── Environment variable configuration (Docker-native setup) ── -# These can be overridden at runtime via docker run -e or docker-compose -# -# Required: -# API_KEY or ZEROCLAW_API_KEY - Your LLM provider API key -# -# Optional: -# PROVIDER or ZEROCLAW_PROVIDER - LLM provider (default: openrouter) -# 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 +# Environment setup +# Use consistent workspace path +ENV ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace +ENV HOME=/zeroclaw-data +# Defaults for local dev (Ollama) - matches config.template.toml +ENV PROVIDER="ollama" +ENV ZEROCLAW_MODEL="llama3.2" +ENV ZEROCLAW_GATEWAY_PORT=3000 +# 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 - 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", "[::]"] diff --git a/dev/README.md b/dev/README.md new file mode 100644 index 0000000..d1486e0 --- /dev/null +++ b/dev/README.md @@ -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/`. diff --git a/dev/cli.sh b/dev/cli.sh new file mode 100755 index 0000000..3426417 --- /dev/null +++ b/dev/cli.sh @@ -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 diff --git a/dev/config.template.toml b/dev/config.template.toml new file mode 100644 index 0000000..f768587 --- /dev/null +++ b/dev/config.template.toml @@ -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 diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml new file mode 100644 index 0000000..93de91a --- /dev/null +++ b/dev/docker-compose.yml @@ -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 diff --git a/dev/sandbox/Dockerfile b/dev/sandbox/Dockerfile new file mode 100644 index 0000000..59ddf05 --- /dev/null +++ b/dev/sandbox/Dockerfile @@ -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"]