# syntax=docker/dockerfile:1.7

# Keep the Rust toolchain explicit so the runner artifact is reproducible and
# is always built for the same platform as the final Daytona image.
ARG RUST_VERSION=1.97
FROM rust:${RUST_VERSION}-bookworm AS runnerd-build

WORKDIR /workspace/packages/paperclip-runner/runner
COPY packages/paperclip-runner/runner/Cargo.toml packages/paperclip-runner/runner/Cargo.lock ./
COPY packages/paperclip-runner/runner/crates ./crates
COPY packages/paperclip-runner/protocol ../protocol
RUN cargo build --locked --release -p paperclip-runner-core --bin paperclip-runnerd \
    && strip /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd

FROM node:24.11-bookworm AS provider-pack-build
ARG PAPERCLIP_RUNNER_SOURCE_REVISION
RUN test -n "${PAPERCLIP_RUNNER_SOURCE_REVISION}"
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
WORKDIR /workspace
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc tsconfig.base.json ./
COPY patches ./patches
COPY scripts/link-plugin-dev-sdk.mjs ./scripts/link-plugin-dev-sdk.mjs
COPY packages/paperclip-eval-kernel ./packages/paperclip-eval-kernel
COPY packages/paperclip-runner ./packages/paperclip-runner
RUN pnpm install --frozen-lockfile --filter '@paperclipai/paperclip-runner...'
RUN pnpm --filter @paperclipai/paperclip-runner build:typescript \
    && PAPERCLIP_RUNNER_SOURCE_REVISION="${PAPERCLIP_RUNNER_SOURCE_REVISION}" \
      node packages/paperclip-runner/scripts/build-provider-pack.mjs /provider-pack

# Fleet sandbox base image. Keep this section aligned with
# paperclipai/paperclip-cloud/fleet-sandbox-image/Dockerfile. The only Paperclip
# runner-specific addition is /usr/local/bin/paperclip-runnerd below.
FROM daytonaio/sandbox:0.8.0

USER root

ENV PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT=/opt/paperclip-runner/provider-pack
ENV PATH=${PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT}/node_modules/.bin:/usr/local/share/nvm/current/bin:/usr/local/python/current/bin:/usr/local/py-utils/bin:${PATH}

RUN npm install -g \
      @anthropic-ai/claude-code@2.1.19 \
      @openai/codex@0.148.0 \
      @xai-official/grok@1.0.3 \
      @google/gemini-cli@0.56.0 \
      @moonshot-ai/kimi-code@0.38.0 \
      @earendil-works/pi-coding-agent@0.84.2 \
      opencode-ai@1.18.17 \
    && npm cache clean --force

RUN pip install --no-cache-dir --break-system-packages hermes-agent==0.15.2

ARG CURSOR_VERSION=2026.08.11-e8db854
ARG CURSOR_SHA256_AMD64=bfff4bf6f4e9dd30c1d0ef0a70b6077b074015dd2948e4c50685d53afdcfce5a
RUN set -eu; \
    arch="$(dpkg --print-architecture)"; \
    [ "$arch" = "amd64" ] || { echo "FATAL: cursor pin only covers amd64, not $arch" >&2; exit 1; }; \
    dir="/opt/cursor/versions/${CURSOR_VERSION}"; mkdir -p "$dir"; \
    curl -fsSL "https://downloads.cursor.com/lab/${CURSOR_VERSION}/linux/x64/agent-cli-package.tar.gz" -o /tmp/cursor.tgz; \
    printf '%s  /tmp/cursor.tgz\n' "${CURSOR_SHA256_AMD64}" > /tmp/cursor.sha256; \
    sha256sum -c /tmp/cursor.sha256; \
    tar --strip-components=1 -xzf /tmp/cursor.tgz -C "$dir"; \
    rm -f /tmp/cursor.tgz /tmp/cursor.sha256; \
    ln -sf "$dir/cursor-agent" /usr/local/bin/cursor-agent; \
    ln -sf /usr/local/bin/cursor-agent /usr/local/bin/agent; \
    chmod -R a+rX /opt/cursor

ARG GH_VERSION=2.98.0
ARG GH_SHA256_AMD64=3b8ac6b30336802fc1a858d7c084e11cdf24ac1a761ca90b68022d7d729208de
RUN set -eu; \
    arch="$(dpkg --print-architecture)"; \
    [ "$arch" = "amd64" ] || { echo "FATAL: gh pin only covers amd64, not $arch" >&2; exit 1; }; \
    curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tgz; \
    printf '%s  /tmp/gh.tgz\n' "${GH_SHA256_AMD64}" > /tmp/gh.sha256; \
    sha256sum -c /tmp/gh.sha256; \
    tar -xzf /tmp/gh.tgz -C /tmp; \
    install "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh; \
    rm -rf /tmp/gh.tgz /tmp/gh.sha256 "/tmp/gh_${GH_VERSION}_linux_amd64"

COPY --from=runnerd-build /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd /usr/local/bin/paperclip-runnerd
COPY --from=provider-pack-build /provider-pack /opt/paperclip-runner/provider-pack

RUN set -eu; \
    chmod -R a+rX /opt/paperclip-runner/provider-pack; \
    printf '%s\n' 'export PATH=/opt/paperclip-runner/provider-pack/node_modules/.bin:$PATH' \
      > /etc/profile.d/01-paperclip-runner-provider-pack.sh; \
    chmod 0644 /etc/profile.d/01-paperclip-runner-provider-pack.sh; \
    for command_name in acpx claude-agent-acp codex-acp pi-acp claude codex grok gemini kimi pi opencode cursor-agent agent hermes gh paperclip-runnerd; do \
      command -v "$command_name" >/dev/null || { echo "FATAL: $command_name not on PATH after build" >&2; exit 1; }; \
    done; \
    metadata="$(paperclip-runnerd --build-metadata)"; \
    /opt/paperclip-runner/provider-pack/node_modules/node/bin/node -e "const [major,minor,patch]=process.versions.node.split('.').map(Number); if (major<24 || (major===24 && (minor<11 || (minor===11 && patch<0)))) process.exit(1)"; \
    test -f /opt/paperclip-runner/provider-pack/provider-pack.json; \
    printf '%s' "$metadata" | grep -q '"dial_ws_loopback"'; \
    printf '%s' "$metadata" | grep -q '"dial_wss"'; \
    printf '%s' "$metadata" | grep -q '"listen_ws"'; \
    echo "all agent CLIs and runnerd transport modes are available"

USER daytona

RUN /bin/sh -lc 'set -eu; \
    test -r /opt/paperclip-runner/provider-pack/provider-pack.json; \
    /opt/paperclip-runner/provider-pack/node_modules/node/bin/node -e \
      "JSON.parse(require(\"node:fs\").readFileSync(\"/opt/paperclip-runner/provider-pack/provider-pack.json\", \"utf8\"))"; \
    for command_name in acpx claude-agent-acp codex-acp pi-acp; do \
      command -v "$command_name" >/dev/null || { echo "FATAL: $command_name not on PATH for daytona user" >&2; exit 1; }; \
    done; \
    test "$(acpx --version)" = "0.13.1"; \
    test "$(claude-agent-acp --version)" = "0.70.0"; \
    test "$(codex-acp --version)" = "@agentclientprotocol/codex-acp 1.6.2"'
