Spaces:
Running
Running
File size: 6,177 Bytes
be8c7bb 47b3c14 be8c7bb 3182e31 be8c7bb 6ad52e8 df96d5e 47b3c14 3182e31 be8c7bb 6ad52e8 47b3c14 9e72cf8 47b3c14 9e72cf8 be8c7bb 3182e31 be8c7bb 3182e31 be8c7bb 9161677 5c47206 67a5cb1 5ee080c 67a5cb1 5ee080c 67a5cb1 e6dbd45 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 d63dc26 4e5f895 5fa646d 67a5cb1 2ba784e be8c7bb 3182e31 be8c7bb 3182e31 47b3c14 be8c7bb 4b5ca71 be8c7bb 4b5ca71 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | # 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
# Claude Code wrapper — auth mode selection:
# CLAUDE_CODE_OAUTH_TOKEN set → long-lived subscription OAuth token (sk-ant-oat01-...)
# takes priority; unset API key to avoid conflict
# ANTHROPIC_API_KEY set → API key mode (pay-per-use)
# Neither set → uses stored credentials in ~/.claude/
# Also drops cloudflare NODE_OPTIONS and caps heap size.
RUN if [ -e /usr/local/bin/claude ]; then \
mv /usr/local/bin/claude /usr/local/bin/claude-real && \
{ \
echo '#!/bin/sh'; \
echo 'unset NODE_OPTIONS'; \
echo '[ -n "$CLAUDE_CODE_OAUTH_TOKEN" ] && unset ANTHROPIC_API_KEY'; \
echo 'export NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"'; \
echo 'exec /usr/local/bin/claude-real "$@"'; \
} > /usr/local/bin/claude && \
chmod +x /usr/local/bin/claude; \
fi
# Codex wrapper — drops cloudflare NODE_OPTIONS, caps heap size
RUN if [ -e /usr/local/bin/codex ]; then \
mv /usr/local/bin/codex /usr/local/bin/codex-real && \
printf '#!/bin/sh\nunset NODE_OPTIONS\nexport NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"\nexec /usr/local/bin/codex-real "$@"\n' > /usr/local/bin/codex && \
chmod +x /usr/local/bin/codex; \
fi
# 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 && \
{ \
echo '#!/bin/sh'; \
echo 'unset NODE_OPTIONS'; \
echo 'export NODE_OPTIONS="--max-old-space-size=4096 --no-deprecation --no-warnings"'; \
echo 'export GEMINI_CLI_NO_RELAUNCH=1'; \
echo 'export GEMINI_SANDBOX=false'; \
echo 'export GEMINI_CLI_TRUST_WORKSPACE=true'; \
echo '# SANDBOX=1 = "already inside sandbox" — bypasses entire sandbox setup block'; \
echo '# in gemini.tsx regardless of GEMINI_SANDBOX setting or defaults'; \
echo 'export SANDBOX=1'; \
echo 'exec /usr/local/bin/gemini-real "$@"'; \
} > /usr/local/bin/gemini && \
chmod +x /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-keepalive-setup.py /app/
RUN chmod +x /app/start.sh /app/cloudflare-keepalive-setup.py
# 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"]
|