# The shared mission toolchain: everything a mission phase needs that is NOT
# the agent CLI itself. The per-CLI images (agent-claude / agent-kimi /
# agent-glm, plan A6) are FROM this, so the toolchain facts live in one place.
#
# Why a base image rather than three self-contained Dockerfiles: this layer is
# ~3 GB and ~15 minutes to build. Copying it into each per-CLI image would mean
# three builds, three copies on disk, and — the actual risk — three places for
# the scanner and toolchain versions to drift apart. A mission's `done_when`
# evaluator runs the project's own suite to verify a claim rather than believe
# it; if `cargo` is present in one image and absent in another, the same mission
# passes or fails depending on which backend it landed on, and nothing says why.
#
# The contents are the ones proven in production by
# `deploy/clawmates-runtime/Dockerfile`, minus the zeroclaw daemon. A microVM
# mission runs the direct-session model — `claude -p` exec'd over vsock, see
# `session_executor::run_session` — so there is no daemon to host and no
# gateway port to expose. Layer order is copied from that Dockerfile too:
# cheapest-and-most-stable first, so a version bump low down does not
# invalidate the expensive layers above it.
#
# NOT in `AGENT_IMAGES` in scripts/deploy.sh, deliberately: at this size a
# `docker save | load` to every fleet node on each deploy would dominate the
# deploy, and only nodes that actually run microVMs need it. Build it on the
# node that will run it:
#
#   ssh osobh@tank "cd ~/clawmates && \
#     docker build -f images/agent-toolchain/Dockerfile -t clawmates/agent-toolchain:dev images/agent-toolchain/"
FROM debian:bookworm-slim

# Node 22 (Kimi Code needs >= 22.19; Claude Code is fine on it) is the runtime
# for every agent CLI, so it belongs to the shared base rather than to any one
# of them.
#
# `iproute2` is load-bearing on the microVM path, not a convenience. The guest
# has no network interface at all; its only route out is the agent's proxy on
# 127.0.0.1, and the guest's `lo` starts DOWN. While it is down a listener on
# loopback *binds successfully* and then refuses every connection with
# ENETUNREACH — so without `ip link set lo up` the VM has no egress and nothing
# says so. Nothing else in the image needs it.
RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates curl gnupg git jq less procps iproute2 \
    && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
    && apt-get install -y --no-install-recommends nodejs \
    && npm cache clean --force \
    && rm -rf /var/lib/apt/lists/* /root/.npm \
    && node -v && git --version

# Upstream Gitea SDLC tooling for agents that operate on git.redclaw.dev.
# `tea` covers shell-level ops (clone/push, PR checkout); `gitea-mcp` gives the
# LLM a structured tool surface. Both official upstream binaries.
ARG TEA_VERSION=0.14.2
ARG GITEA_MCP_VERSION=1.3.0
RUN set -eux; \
    arch="$(dpkg --print-architecture)"; \
    case "$arch" in \
      amd64) mcp_asset="Linux_x86_64" ;; \
      arm64) mcp_asset="Linux_arm64" ;; \
      *) echo "unsupported arch: $arch"; exit 1 ;; \
    esac; \
    curl -fsSL "https://dl.gitea.com/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-${arch}" \
      -o /usr/local/bin/tea && chmod +x /usr/local/bin/tea; \
    tmp="$(mktemp -d)" && \
      curl -fsSL "https://gitea.com/gitea/gitea-mcp/releases/download/v${GITEA_MCP_VERSION}/gitea-mcp_${mcp_asset}.tar.gz" \
        -o "$tmp/gitea-mcp.tgz" && \
      tar -xzf "$tmp/gitea-mcp.tgz" -C "$tmp" && \
      install -m 0755 "$tmp/gitea-mcp" /usr/local/bin/gitea-mcp && \
      rm -rf "$tmp"; \
    tea --version | head -1

# Security scanners. `security_scan.rs` shells out to all four; when they were
# missing, every scan produced `<tool>:tool_error` rows instead of findings —
# a scan that scanned nothing and reported cleanly.
ARG GITLEAKS_VERSION=8.30.1
ARG TRIVY_VERSION=0.72.0
RUN set -eux; \
    arch="$(dpkg --print-architecture)"; \
    case "$arch" in \
      amd64) gl_arch=x64;   tv_arch=64bit ;; \
      arm64) gl_arch=arm64; tv_arch=ARM64 ;; \
      *) echo "unsupported arch: $arch"; exit 1 ;; \
    esac; \
    curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_${gl_arch}.tar.gz" \
      | tar -xz -C /usr/local/bin gitleaks; \
    curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-${tv_arch}.tar.gz" \
      | tar -xz -C /usr/local/bin trivy; \
    gitleaks version; trivy --version | head -1

# semgrep in its own venv so its pinned dependency tree cannot collide with
# anything else installed here.
RUN apt-get update && apt-get install -y --no-install-recommends \
      python3 python3-pip python3-venv \
    && python3 -m venv /opt/semgrep \
    && /opt/semgrep/bin/pip install --no-cache-dir semgrep \
    && ln -s /opt/semgrep/bin/semgrep /usr/local/bin/semgrep \
    && rm -rf /var/lib/apt/lists/* \
    && semgrep --version

# Rust last: the largest layer and the one most likely to be bumped, so it sits
# where a rebuild costs the least cache. `templates/teams/rust_sdlc.toml` tells
# the coder to run `cargo test`, and the evaluator runs it again to check.
ENV RUSTUP_HOME=/usr/local/rustup \
    CARGO_HOME=/usr/local/cargo \
    PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RUN apt-get update && apt-get install -y --no-install-recommends \
      gcc libc6-dev pkg-config libssl-dev make cmake build-essential python3-dev \
    && curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable \
    && cargo install cargo-audit --locked --no-default-features \
    && rm -rf /var/lib/apt/lists/* "$CARGO_HOME/registry" "$CARGO_HOME/git" \
    && chmod -R a+rX "$RUSTUP_HOME" "$CARGO_HOME" \
    && rustc --version && cargo audit --version

# PATH is spelled out in full above rather than as `/usr/local/cargo/bin:$PATH`
# on purpose. `scripts/fc-build-rootfs.sh` reads the image's resolved
# `Config.Env` into the guest's /etc/profile.d, and that is the only way the
# VM learns its PATH — `docker export` carries no image metadata at all. An
# interpolated value resolves at build time, so this is equivalent; being
# explicit means the guest's PATH is readable here rather than inferred.

# Where a mission's checkout is injected, on both the container and the microVM
# path. Created here so the guest agent's first write is not also a mkdir.
RUN mkdir -p /mission/repo
WORKDIR /mission/repo

# Idle keep-alive. On the container path the orchestrator execs work in; on the
# microVM path pid 1 is the guest agent instead and this is never reached.
CMD ["sleep", "infinity"]
