Spaces:
Running
Running
File size: 5,319 Bytes
be8c7bb 47b3c14 be8c7bb 3182e31 be8c7bb 6ad52e8 df96d5e 47b3c14 3182e31 be8c7bb 6ad52e8 47b3c14 9e72cf8 47b3c14 9e72cf8 be8c7bb 3182e31 be8c7bb 3182e31 be8c7bb 9161677 5c47206 e79e3bc 2ba784e e6dbd45 bc525e1 e6dbd45 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 d63dc26 2ba784e be8c7bb 3182e31 be8c7bb 3182e31 47b3c14 be8c7bb 9946cfb c7255ef 5015cf0 9946cfb c7255ef be8c7bb 3182e31 be8c7bb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | # Stage 1: Build Paperclip from source
FROM node:lts-trixie-slim AS paperclip-builder
WORKDIR /build
RUN apt-get update && apt-get install -y \
git \
&& rm -rf /var/lib/apt/lists/* \
&& corepack enable
# Clone Paperclip (depth=1 for speed, uses repo's default branch)
RUN git clone --depth=1 https://github.com/paperclipai/paperclip.git .
# Copy lock files early for cache efficiency: lock changes don't re-clone
RUN ls -la pnpm-lock.yaml package.json 2>/dev/null || true
# Install dependencies (corepack picks correct pnpm version from packageManager field)
RUN pnpm install
# Apply both patches in a single layer (reduces layer count, cleaner git history)
# Patch 1: React Router basename for /app path handling
# Patch 2: Recovery chain depth cap at 500 to prevent stack overflow
RUN sed -i 's|<BrowserRouter>|<BrowserRouter basename="/app">|' ui/src/main.tsx && \
grep -q 'basename="/app"' ui/src/main.tsx || (echo "PATCH 1 FAILED: React Router basename not applied" && exit 1) && \
PATCH_FILE=server/src/services/recovery/issue-graph-liveness.ts && \
test -f "$PATCH_FILE" || (echo "PATCH 2 FAILED: File not found: $PATCH_FILE" && exit 1) && \
sed -i 's/seen\.has(current\.id)/(seen.size > 500 || seen.has(current.id))/' "$PATCH_FILE" && \
grep -q "seen.size > 500" "$PATCH_FILE" || (echo "PATCH 2 FAILED: Chain depth cap not applied" && exit 1)
# Build Paperclip (match official Dockerfile order)
RUN pnpm --filter @paperclipai/ui build
RUN pnpm --filter @paperclipai/plugin-sdk build
RUN pnpm --filter @paperclipai/server build
# Stage 2: Runtime
FROM node:lts-trixie-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
postgresql-client \
postgresql \
postgresql-contrib \
python3 \
python3-pip \
git \
&& rm -rf /var/lib/apt/lists/*
# Create PostgreSQL runtime directories
RUN mkdir -p /var/run/postgresql && chown postgres:postgres /var/run/postgresql
# Install health-server Node dependencies locally in /app
RUN npm init -y && npm install express@4 cors morgan
# Install agent CLIs globally
RUN npm install -g @google/gemini-cli @anthropic-ai/claude-code @openai/codex
# Wrap agent CLIs so they:
# 1. Drop cloudflare-proxy.js NODE_OPTIONS (would conflict with their HTTP)
# 2. Pre-set --max-old-space-size=4096 so gemini doesn't trigger heap-size
# self-relaunch (the spawn fails in HF Spaces containers)
RUN for cmd in claude codex; do \
if [ -e /usr/local/bin/$cmd ]; then \
mv /usr/local/bin/$cmd /usr/local/bin/${cmd}-real && \
printf '#!/bin/sh\nunset NODE_OPTIONS\nexport NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"\nexec /usr/local/bin/%s-real "$@"\n' "$cmd" > /usr/local/bin/$cmd && \
chmod +x /usr/local/bin/$cmd; \
fi; \
done
# Gemini wrapper β fix for "Failed to relaunch the CLI process":
#
# ROOT CAUSE: relaunch.ts::relaunchAppInChildProcess() spawns a child process
# with stdio IPC. That spawn fails when Paperclip pipes gemini's stdio.
# Source: packages/cli/src/utils/relaunch.ts
#
# FIX: GEMINI_CLI_NO_RELAUNCH=1 β documented kill-switch in relaunch.ts that
# makes relaunchAppInChildProcess() return early (skip spawn entirely).
# The process then runs as the main process without a child.
#
# Also bake in other headless vars so they survive even if Paperclip spawns
# gemini with a custom env object:
# GEMINI_SANDBOX=false β skip Docker-sandbox attempt
# GEMINI_CLI_TRUST_WORKSPACE=true β skip interactive workspace-trust prompt
RUN mv /usr/local/bin/gemini /usr/local/bin/gemini-real && \
printf '#!/bin/sh\nunset NODE_OPTIONS\nexport NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"\nexport GEMINI_CLI_NO_RELAUNCH=1\nexport GEMINI_SANDBOX=false\nexport GEMINI_CLI_TRUST_WORKSPACE=true\nexec /usr/local/bin/gemini-real "$@"\n' \
> /usr/local/bin/gemini && \
chmod +x /usr/local/bin/gemini && \
echo "=== gemini wrapper ===" && cat /usr/local/bin/gemini
# Install Python dependencies for sync
RUN pip install --no-cache-dir --break-system-packages huggingface_hub PyYAML
# Copy full Paperclip build (including node_modules for runtime)
COPY --from=paperclip-builder /build /app/paperclip
# Ensure pnpm is available in runtime stage
RUN corepack enable
# Copy orchestration files
COPY start.sh /app/
COPY health-server.js /app/
COPY paperclip-sync.py /app/
COPY cloudflare-proxy.js /app/
COPY cloudflare-proxy-setup.py /app/
COPY cloudflare-worker.js /app/
COPY setup-uptimerobot.sh /app/
RUN chmod +x /app/start.sh /app/setup-uptimerobot.sh
# Create non-root user for running Paperclip + agent CLIs
# Claude Code refuses --dangerously-skip-permissions when running as root
# Note: /app files stay root-owned (644/755 defaults = readable by all).
# /paperclip runtime dir is chowned to paperclip in start.sh after restore.
RUN useradd -m -u 1001 -s /bin/bash paperclip && \
mkdir -p /paperclip /var/lib/postgresql/data && \
chown -R postgres:postgres /var/lib/postgresql/data && \
chown paperclip:paperclip /paperclip
EXPOSE 7861
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
CMD curl -f http://localhost:7861/health || exit 1
CMD ["/app/start.sh"]
|