# Nucleic sandbox image — the agent's in-container toolchain.
#
# Built in CI and pushed to GHCR (see .github/workflows/sandbox-image.yml); the macOS app pulls +
# unpacks it at runtime via ContainerEngine (no on-device build, no user-installed tools). Keep the
# tag in sync with `ProjectSandbox.defaultImage` (Sources/NucleicCore/Project.swift) — bump the tag
# whenever this file changes so the app pulls the new image instead of a stale cache.
#
# A Node base with git, the GitHub CLI (`gh`), a cross-language build toolchain, and the Claude Code
# CLI preinstalled. The Swift/Xcode toolchain is deliberately ABSENT — Swift builds run on the host
# via host-exec, not in the sandbox — but agents still get the common build tools (make/gcc from
# build-essential, python3/pip, plus the base image's node/npm) to build and test most repos
# in-container. `gh` is installed from GitHub's official apt repository (authenticated in-sandbox via
# the injected GITHUB_TOKEN) so the agent can open PRs and call the GitHub API. `openssh-client`
# supplies `ssh-keygen`, which git invokes to sign commits when `gpg.format=ssh` is configured —
# without it signed commits fail.
#
# A headless browser (Playwright + a bundled Chromium) is also preinstalled so agents can drive a
# real browser in-sandbox — screenshots, PDFs, scraping, and end-to-end web checks — with no host
# round-trip. See the Playwright layer near the bottom of this file.
FROM node:22-bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        git openssh-client ca-certificates curl iproute2 \
        build-essential make pkg-config \
        python3 python3-pip python3-venv \
    && mkdir -p -m 755 /etc/apt/keyrings \
    && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
        -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
    && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
    && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
        > /etc/apt/sources.list.d/github-cli.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends gh \
    && rm -rf /var/lib/apt/lists/*

# Pin to @latest so every image rebuild (and every tag bump) ships the current CLI — new Claude
# Code / Codex releases, and the models they unlock, arrive with a rebuilt image. The same packages
# can also be refreshed in place in a running container via Settings ▸ Control ▸ "Check for updates"
# (ContainerManager.updateAgentCLIs) without waiting for an image re-pull.
RUN npm install -g @anthropic-ai/claude-code@latest

# OpenAI Codex (npm resolves the arch binary; arm64 = codex-aarch64-unknown-linux-musl). It stores
# its login as a plain ~/.codex/auth.json (no Keychain), seeded per session host-side.
RUN npm install -g @openai/codex@latest

# xAI Grok Build via its official installer. The installer drops the toolchain under /root/.grok and
# points /usr/local/bin/grok at /root/.grok/bin/grok — but /root is 0700, so the non-root agent uid
# can't reach it (and the installer's own /usr/local/bin/grok symlink can't simply be re-pointed at
# itself). Relocate the whole install to a world-traversable /opt and relink onto the global PATH so
# the agent can run it. `grok --version` fails the build loudly if the arm64 binary didn't land.
RUN curl -fsSL https://x.ai/cli/install.sh | bash \
    && rm -f /usr/local/bin/grok /usr/local/bin/agent \
    && mv /root/.grok /opt/grok \
    && chmod -R a+rX /opt/grok \
    && ln -s /opt/grok/bin/grok /usr/local/bin/grok \
    && ln -s /opt/grok/bin/agent /usr/local/bin/agent \
    && /opt/grok/bin/grok --version

# The in-container control bridge (loopback TCP -> the vsock-relayed host control socket). The
# container's root init launches it only when Nucleic relays a control socket in; it lets the agent
# + interceptor shims reach the host approval server with no IP listener (no macOS network prompts).
# See docs/VSOCK_CONTROL_PLANE.md. Runs on the base image's node.
COPY control-bridge.js /opt/nucleic/control-bridge.js

# Headless browser tool — Playwright with a bundled Chromium, so an agent can drive a real browser
# in-sandbox (screenshots, PDFs, scraping, headless end-to-end web checks) without a host round-trip.
# The `playwright` CLI is global; `playwright install --with-deps chromium` fetches the browser plus
# the shared libraries it needs (nss, fonts, libx11, …) — resolving the arch-correct Chromium on both
# arm64 (Apple-silicon sandbox runs) and amd64 (the Covalence runner base), and the build fails loudly
# via the trailing `--version` if either slice has no browser. Because the agent runs as a non-root
# uid (see the Grok note above) — and Playwright otherwise caches browsers under $HOME/.cache — the
# browsers are pinned to a world-readable /opt path via PLAYWRIGHT_BROWSERS_PATH and made a+rX, so
# any user can launch them and an agent's local `npm install playwright` / `pip install playwright`
# reuses them instead of re-downloading. `--with-deps` apt-installs system libs, so it must run as
# root here (it does — Dockerfile RUN is root).
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
RUN npm install -g playwright@latest \
    && mkdir -p /opt/playwright-browsers \
    && playwright install --with-deps chromium \
    && chmod -R a+rX /opt/playwright-browsers \
    && rm -rf /var/lib/apt/lists/* \
    && playwright --version

WORKDIR /workspace
