File size: 6,388 Bytes
7fcb6f2 39e315a 7fcb6f2 39e315a 59dc700 c32102b 846ce24 c32102b 39e315a 95b8512 39e315a ba9f187 39e315a 846ce24 c32102b 39e315a 7fcb6f2 39e315a c32102b 4b69257 39e315a 4b69257 39e315a 95b8512 39e315a 59dc700 39e315a c32102b 59dc700 ba9f187 846ce24 39e315a 95b8512 39e315a c32102b 95b8512 c32102b 39e315a 6de4da8 59dc700 c32102b 39e315a ba9f187 c32102b 39e315a 95b8512 c32102b 95b8512 | 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 | ARG BUN_VERSION=latest
# βββ Stage 1: build the vendored OpenClaude bundle ββββββββββββββββββββββ
FROM oven/bun:${BUN_VERSION} AS openclaude-builder
WORKDIR /openclaude
# FIX: Ignored bun.lock and removed --frozen-lockfile to prevent version mismatch crashes
COPY vendor/openclaude/package.json ./
RUN bun install --no-progress
COPY vendor/openclaude/ ./
RUN bun run build && \
test -s dist/cli.mjs && \
echo "[builder] OpenClaude bundle: $(wc -c < dist/cli.mjs) bytes"
# βββ Stage 2: base python+node+bun runtime ββββββββββββββββββββββββββββββ
FROM python:3.12-slim AS base
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl ca-certificates build-essential unzip xz-utils \
nodejs npm \
# βββ camofox-browser runtime deps ββββββββββββββββββββββββββββ
# Camoufox is a Firefox fork; it needs the standard X/GTK
# display libraries even when running headless, plus xvfb so
# we can attach a virtual display when --headless=virtual.
xvfb libgtk-3-0 libdbus-glib-1-2 libxt6 libasound2 \
libx11-xcb1 libxcomposite1 libxcursor1 libxdamage1 libxfixes3 \
libxi6 libxrandr2 libxss1 libxtst6 libnss3 libpango-1.0-0 \
libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libgbm1 \
&& rm -rf /var/lib/apt/lists/*
# uv (fast Python installer used by sandboxed test runs)
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Bun (needed at runtime to launch the daemon: `bun run dev:grpc`)
# FIX: Updated to pull from latest to match Stage 1
COPY --from=oven/bun:latest /usr/local/bin/bun /usr/local/bin/bun
COPY --from=oven/bun:latest /usr/local/bin/bunx /usr/local/bin/bunx
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
mcp-server-fetch mcp-server-git mcp-server-sqlite \
grpcio==1.66.* grpcio-tools==1.66.* protobuf==5.*
# MCP servers used by the runtime β installed globally so `npx -y β¦` is
# instantaneous instead of resolving on every audit.
# NOTE: `@modelcontextprotocol/server-git` and `@modelcontextprotocol/server-sqlite`
# were removed from the npm registry (404) β they are now provided via the
# Python packages `mcp-server-git` and `mcp-server-sqlite` installed above.
RUN npm install -g --quiet \
@modelcontextprotocol/server-github \
@modelcontextprotocol/server-filesystem \
@modelcontextprotocol/server-memory \
@modelcontextprotocol/server-sequential-thinking \
@modelcontextprotocol/server-brave-search
# βββ camofox-browser anti-detection browser server ββββββββββββββββββββββ
# Anti-detection browser for AI agents (https://github.com/jo-inc/camofox-browser).
# Installed under /opt/camofox so the orchestrator can launch it via
# entrypoint.sh on 127.0.0.1:9377. The Camoufox Firefox-fork binary
# (~300MB) is fetched lazily on first launch by camoufox-js β keeping
# the image slim while still giving the orchestrator full access to the
# REST API exposed by camofox_client.py.
RUN mkdir -p /opt/camofox && cd /opt/camofox && \
npm init -y >/dev/null 2>&1 && \
npm install --quiet --omit=dev @askjo/camofox-browser@^1.6.0 || \
npm install --quiet --omit=dev camofox-browser
# βββ Stage 3: final runtime image βββββββββββββββββββββββββββββββββββββββ
FROM base AS runtime
LABEL org.opencontainers.image.title="Rhodawk AI DevSecOps Engine" \
org.opencontainers.image.source="https://github.com/Rhodawk-AI/Rhodawk-devops-engine"
ENV GRADIO_SERVER_NAME=0.0.0.0 \
GRADIO_SERVER_PORT=7860 \
HOME=/home/rhodawk \
PATH="/home/rhodawk/.local/bin:/usr/local/bin:$PATH" \
UV_PYTHON_PREFERENCE=system \
UV_PYTHON=/usr/local/bin/python3 \
OPENCLAUDE_AUTO_APPROVE=1 \
OPENCLAUDE_GRPC_HOST=127.0.0.1 \
OPENCLAUDE_GRPC_PORT_DO=50051 \
OPENCLAUDE_GRPC_PORT_OR=50052 \
MCP_RUNTIME_CONFIG=/tmp/mcp_runtime.json \
# βββ camofox-browser runtime defaults ββββββββββββββββββββββββββββ
# The orchestrator talks to the local camofox server through
# camofox_client.py. CAMOFOX_API_KEY gates cookie-import β leave
# it unset to keep cookie writes disabled (server returns 403).
CAMOFOX_BASE_URL=http://127.0.0.1:9377 \
CAMOFOX_PORT=9377 \
CAMOFOX_HOST=127.0.0.1 \
CAMOFOX_HEADLESS=virtual \
CAMOFOX_PROFILE_DIR=/data/camofox/profiles \
CAMOFOX_COOKIES_DIR=/data/camofox/cookies
# HuggingFace UID 1000 handling (idempotent)
RUN id -u 1000 >/dev/null 2>&1 && (userdel -r "$(id -un 1000)" || true) || true && \
useradd -m -u 1000 -s /bin/bash rhodawk
RUN mkdir -p /data /data/camofox/profiles /data/camofox/cookies /app /opt/openclaude && \
chmod -R 777 /data && \
chown -R rhodawk:rhodawk /app /opt/openclaude /opt/camofox
# Bring the prebuilt OpenClaude bundle in as a vendored artifact.
COPY --from=openclaude-builder --chown=rhodawk:rhodawk /openclaude /opt/openclaude
# Tiny global wrappers β the orchestrator never shells out to these
# directly any more (gRPC bridges everything), but we keep them so admins
# can debug interactively from `docker exec`.
RUN ln -sf /opt/openclaude/bin/openclaude /usr/local/bin/openclaude && \
chmod +x /usr/local/bin/openclaude
WORKDIR /app
USER rhodawk
# Source last so application edits don't bust the heavy node/python layers.
COPY --chown=rhodawk:rhodawk . .
# Generate Python protobuf stubs from the vendored .proto file.
RUN python -m grpc_tools.protoc \
-I /opt/openclaude/src/proto \
--python_out=openclaude_grpc \
--grpc_python_out=openclaude_grpc \
/opt/openclaude/src/proto/openclaude.proto && \
# protoc emits absolute-path imports; rewrite for relative package layout
sed -i 's/^import openclaude_pb2/from . import openclaude_pb2/' \
openclaude_grpc/openclaude_pb2_grpc.py
EXPOSE 7860 9377 50051 50052
ENTRYPOINT ["/app/entrypoint.sh"]
|