FROM node:22-slim

# System deps for Claude Code/Codex/Gemini + Python for MCP bridge + display for auth login
RUN apt-get update && apt-get install -y --no-install-recommends \
    python3 python3-pip git ca-certificates curl unzip chrony tmux bubblewrap tini \
    xvfb x11vnc novnc websockify chromium dbus-x11 xterm \
    && rm -rf /var/lib/apt/lists/*

# Use upstream rclone instead of the distro package so OAuth config generation
# and relay-side mount behavior match current rclone releases.
ARG DOWNLOAD_RCLONE_VERSION=current
RUN curl -fsSL "https://downloads.rclone.org/rclone-${DOWNLOAD_RCLONE_VERSION}-linux-amd64.zip" -o /tmp/rclone.zip \
    && unzip -q /tmp/rclone.zip -d /tmp/rclone-dist \
    && install -m 0755 /tmp/rclone-dist/*/rclone /usr/local/bin/rclone \
    && rclone version \
    && rm -rf /tmp/rclone.zip /tmp/rclone-dist

# Install agent CLIs (Claude Code + Codex + Gemini + Antigravity) — single image hosts them;
# the pool decides which binary to exec per-spawn so they share base layers + caches.
#
# CLI versions are injected as build args by build.sh, which resolves the latest
# published npm version of each at build time. Pinning to the resolved version
# makes the version part of this layer's cache key: an unchanged version reuses
# the cached layer (no reinstall), while a new upstream release changes the arg
# and invalidates the layer so the rebuild installs the new CLI. Defaults to
# `latest` for direct `docker build` invocations that do not pass the args.
ARG CLAUDE_CODE_VERSION=latest
ARG CODEX_VERSION=latest
ARG GEMINI_VERSION=latest
RUN npm install -g \
        "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
        "@openai/codex@${CODEX_VERSION}" \
        "@google/gemini-cli@${GEMINI_VERSION}" \
    && curl -fsSL https://antigravity.google/cli/install.sh | bash -s -- --dir /usr/local/bin

# Create non-root user (Claude Code refuses --dangerously-skip-permissions as root)
# node:22-slim has UID 1000 as 'node' — rename to 'pawflow' for consistency
RUN if id -u node 2>/dev/null; then usermod -l pawflow -d /home/pawflow -m node && groupmod -n pawflow node; \
    elif ! id -u pawflow 2>/dev/null; then groupadd -g 1000 pawflow && useradd -u 1000 -g 1000 -m -s /bin/bash pawflow; fi

# /cc_sessions is the host CLAUDE_SESSIONS_DIR mount (set up by the pool
# at `docker run`). The per-exec mount-namespace then binds the user's
# slot `/cc_sessions/<user>` over `/cc_sessions` so CC sees its conv
# tree at `/cc_sessions/<conv>/<agent>` — the same canonical path the
# user's relay exposes via server-fs FUSE. CLAUDE_CONFIG_DIR is set
# per-exec to that conv path; the value here is just a sane default for
# diagnostics outside an exec.
WORKDIR /home/pawflow

ENV HOME="/home/pawflow"
ENV CLAUDE_CONFIG_DIR="/home/pawflow"
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ENV USER="pawflow"

# No interactive prompts
ENV CI=true

# Auth login entrypoints (server-side OAuth login via noVNC + Chromium).
# One per CLI: claude / codex / gemini. Each spawns Xvfb + x11vnc +
# websockify and drives its CLI's interactive OAuth flow, then copies
# the resulting credentials file to /workspace for the host action to pick up.
COPY auth_login.sh /opt/pawflow/auth_login.sh
COPY codex_auth_login.sh /opt/pawflow/codex_auth_login.sh
COPY gemini_auth_login.sh /opt/pawflow/gemini_auth_login.sh
COPY agy_auth_login.sh /opt/pawflow/agy_auth_login.sh
COPY rclone_auth_login.sh /opt/pawflow/rclone_auth_login.sh
RUN chmod +x /opt/pawflow/auth_login.sh /opt/pawflow/codex_auth_login.sh /opt/pawflow/gemini_auth_login.sh /opt/pawflow/agy_auth_login.sh /opt/pawflow/rclone_auth_login.sh

# Browser wrapper for Claude Code auth login (Chromium with Docker-safe flags)
RUN printf '#!/bin/bash\nexec chromium --no-sandbox --disable-gpu --disable-dev-shm-usage --disable-software-rasterizer --window-size=1280,800 "$@"\n' \
    > /usr/local/bin/open-browser && chmod +x /usr/local/bin/open-browser
RUN mkdir -p /usr/local/share/applications && \
    printf '[Desktop Entry]\nType=Application\nName=Chromium\nExec=/usr/local/bin/open-browser %%U\nMimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;\n' \
    > /usr/local/share/applications/chromium.desktop

# Python deps for MCP bridge and observer proxies
RUN pip3 install --break-system-packages --no-cache-dir json-repair h2

# MCP bridge + PawFlow SDK are NOT baked — the CC pool spawn in
# core/claude_code_pool.py dev-mounts them onto /opt/pawflow/*.py from
# the host tree, so iterating on the bridge is rebuild-free. We just
# pre-create the directory so the binds have a target.
RUN mkdir -p /opt/pawflow /workspace /relay /cc_sessions \
    && chown pawflow:pawflow /opt/pawflow /workspace /relay /cc_sessions
ENV PYTHONPATH="/opt/pawflow"

USER pawflow
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["claude"]
