# Base pinned by digest (the node:24-bookworm-slim multi-arch OCI index) so the
# image is reproducible and base/security updates are a deliberate, reviewed
# change rather than silent drift on a floating tag. To refresh the base: resolve
# the new INDEX digest (must cover linux/amd64 + linux/arm64), update it here, and
# bump _IMAGE_SPEC_VERSION in bin/cleat so users are offered the rebuilt image.
# See concept/24-image-spec-versioning.md.
FROM node:24-bookworm-slim@sha256:c2d5ade763cacfb03fe9cb8e8af5d1be5041ff331921fa26a9b231ca3a4f780a

# Install packages (node/npm pre-installed in base image)
# gh:     GitHub CLI from official apt repo
# docker: Docker CE CLI + Compose plugin from Docker's official repo. The daemon
#         is NOT installed. The CLI talks to the host daemon over a socket
#         mount when the `docker` capability is enabled. See
#         concept/15-docker-capability.md for the full rationale.
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    bash \
    curl \
    git \
    python3 \
    python3-pip \
    openssh-client \
    jq \
    socat \
    sudo \
    ca-certificates \
    gpg && \
    curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
      | gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg && \
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
      > /etc/apt/sources.list.d/github-cli.list && \
    install -m 0755 -d /etc/apt/keyrings && \
    curl -fsSL https://download.docker.com/linux/debian/gpg \
      | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
    chmod a+r /etc/apt/keyrings/docker.gpg && \
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" \
      > /etc/apt/sources.list.d/docker.list && \
    apt-get update && \
    apt-get install -y --no-install-recommends gh docker-ce-cli docker-compose-plugin && \
    apt-get purge -y gpg && \
    apt-get autoremove -y && \
    rm -rf /var/lib/apt/lists/*

# Create non-root user (UID/GID will be remapped at runtime via entrypoint)
# node:24-bookworm-slim ships with node:node at 1000:1000, remove it first
ARG USER_UID=1000
ARG USER_GID=1000
RUN userdel -r node 2>/dev/null || true && \
    groupdel node 2>/dev/null || true && \
    groupadd -g $USER_GID coder && \
    useradd -m -u $USER_UID -g coder -d /home/coder -s /bin/bash coder && \
    echo "coder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/coder && \
    mkdir -p /home/coder/.claude /home/coder/.local/bin /home/coder/.config/gh && \
    chown -R coder:coder /home/coder

# Install Claude Code CLI as coder user (native installer)
USER coder
ENV HOME=/home/coder
# Disable Claude Code's launch-time/background self-updater image-wide. Cleat
# owns Claude's version (baked here, updated via `cleat upgrade-claude`); an
# in-container self-update is ephemeral and has hung the TUI at launch. The CLI
# also sets this on `docker exec`, but baking it covers manual exec too. Manual
# `claude update` still works. docs.claude.com/en/setup#disable-auto-updates
ENV DISABLE_AUTOUPDATER=1
RUN curl -fsSL https://claude.ai/install.sh | bash && \
    echo 'export PATH="$HOME/.local/bin:$PATH"' >> /home/coder/.bashrc
USER root

# Clipboard: shims for standard commands (file bridge primary, OSC 52 fallback)
COPY clip /usr/local/bin/clip
COPY clip-daemon /usr/local/bin/clip-daemon
RUN chmod +x /usr/local/bin/clip /usr/local/bin/clip-daemon && \
    ln -s clip /usr/local/bin/pbcopy && \
    ln -s clip /usr/local/bin/xclip && \
    ln -s clip /usr/local/bin/xsel

# Browser bridge: forward URLs to the host browser
COPY open-bridge /usr/local/bin/open-bridge
RUN chmod +x /usr/local/bin/open-bridge && \
    ln -sf open-bridge /usr/local/bin/open && \
    ln -sf open-bridge /usr/local/bin/xdg-open && \
    ln -sf open-bridge /usr/local/bin/sensible-browser
# Claude Code 2.1.191+ only invokes an opener (and keeps the hands-free
# loopback login instead of the manual code-paste flow) when a browser is
# discoverable; the box has no display, so name the shim explicitly. The CLI
# also passes this at `docker run` so containers created from older images get
# it on recreate.
ENV BROWSER=/usr/local/bin/open-bridge

# Hook event forwarder directory (used when hooks capability is active)
RUN mkdir -p /var/log/cleat && chown coder:coder /var/log/cleat

# User-level CLAUDE.md (won't conflict with project-level CLAUDE.md files)
COPY CLAUDE.md /home/coder/.claude/CLAUDE.md
RUN chown coder:coder /home/coder/.claude/CLAUDE.md

# Entrypoint remaps UID/GID to match host, then drops to coder
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

WORKDIR /workspace
ENTRYPOINT ["/entrypoint.sh"]
CMD ["bash"]
