HuggingClip / Dockerfile
somratpro's picture
refactor: remove unused cloudflare-worker.js file and associated Dockerfile copy command
ef67ca7
# 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"]