-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.worker
More file actions
91 lines (82 loc) · 5.16 KB
/
Copy pathDockerfile.worker
File metadata and controls
91 lines (82 loc) · 5.16 KB
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
# ─── Deckent Worker Container ──────────────────────────────────────────────
# Lightweight container for isolated worker execution.
# Each worker runs in its own container with:
# - Claude CLI (or Codex/Gemini CLI)
# - Git for diff operations
# - Node.js for tsc/vitest verification
# - Project mounted read-only, .tasks/ mounted read-write
FROM node:24-trixie-slim
# Install essential tools
# ca-certificates (Sprint 252 PSL-1 verify): REQUIRED for non-claude provider CLIs
# in-container. The codex CLI is a Rust binary whose TLS/websocket client uses the
# SYSTEM root CA store; without ca-certificates it fails with "no native root CA
# certificates found" connecting to chatgpt.com (Sprint 252 codex-in-docker verify).
# (Node-based CLIs like gemini bundle their own CAs, so this was previously latent.)
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
util-linux \
zstd \
&& rm -rf /var/lib/apt/lists/*
# Exact Docker custody executes its native capability probe from the immutable
# worker image before any project mount or provider work. Keep this runtime
# image-owned; the host compares its capability manifest with the trusted host
# runtime and fails closed on absence or drift.
COPY package.json npm-shrinkwrap.json /app/
RUN npm ci --prefix /app --include=dev
COPY dist/core/exec-authority-native.js /app/dist/core/exec-authority-native.js
COPY native/exec-authority /app/native/exec-authority
RUN chmod -R a=rX /app/dist /app/native && chmod -R a+rX /app/node_modules && chmod a=r /app/package.json /app/npm-shrinkwrap.json && \
node --input-type=module -e "import { lstatSync } from 'node:fs'; import { loadExecAuthorityNative } from '/app/dist/core/exec-authority-native.js'; const native = loadExecAuthorityNative(); const dependencies = lstatSync('/app/node_modules'); if (!native.available || !native.effect || native.effect.available === false || !dependencies.isDirectory() || dependencies.isSymbolicLink()) process.exit(78);" && \
setpriv --reuid 65532 --regid 65532 --clear-groups node --input-type=module -e "import { lstatSync } from 'node:fs'; import { loadExecAuthorityNative } from '/app/dist/core/exec-authority-native.js'; const native = loadExecAuthorityNative(); const dependencies = lstatSync('/app/node_modules'); if (!native.available || !native.effect || native.effect.available === false || !dependencies.isDirectory() || dependencies.isSymbolicLink()) process.exit(78);"
# Install the exact Claude Code release admitted by the built-in model catalog.
# Claude Fable 5.1 rejects clients older than 2.1.251; 2.1.259 is the host- and
# Docker-proven release for this catalog revision (2026-09-03). Pinning keeps a
# cached rebuild from silently retaining an older, presence-only CLI.
ARG CLAUDE_CODE_VERSION=2.1.259
RUN npm i -g @anthropic-ai/claude-code@$CLAUDE_CODE_VERSION
# Optional provider CLIs — default lean (Claude-only). Opt-in via build args:
# docker build --build-arg INSTALL_CODEX=true ...
# docker build --build-arg INSTALL_GEMINI=true ...
# docker build --build-arg INSTALL_OLLAMA=true ...
# docker build --build-arg INSTALL_CURSOR=true ...
# Manual uncomment alternative (without build args):
# RUN npm i -g @openai/codex
# RUN npm i -g @google/gemini-cli
# RUN curl -fsSL https://ollama.ai/install.sh | sh
ARG INSTALL_CODEX=false
ARG CODEX_VERSION=0.148.0
ARG INSTALL_GEMINI=false
ARG INSTALL_OLLAMA=false
ARG INSTALL_CURSOR=false
RUN if [ "$INSTALL_CODEX" = "true" ]; then npm i -g "@openai/codex@$CODEX_VERSION"; fi
RUN if [ "$INSTALL_GEMINI" = "true" ]; then npm i -g @google/gemini-cli; fi
RUN if [ "$INSTALL_OLLAMA" = "true" ]; then curl -fsSL https://ollama.ai/install.sh | sh; fi
# Cursor's official installer is HOME-scoped. Install into a shared, read-only
# prefix instead of /root so DockerSpawnBackend's host UID/GID can execute the
# CLI, then expose both official binary names on the system PATH. Download and
# execution are separate so an HTTP failure cannot be hidden by a successful
# empty shell pipeline.
RUN if [ "$INSTALL_CURSOR" = "true" ]; then \
mkdir -p /opt/cursor-agent && \
curl -fsSL https://cursor.com/install --output /tmp/cursor-agent-install.sh && \
HOME=/opt/cursor-agent bash /tmp/cursor-agent-install.sh && \
rm -f /tmp/cursor-agent-install.sh && \
chmod -R a+rX /opt/cursor-agent/.local && \
ln -s /opt/cursor-agent/.local/bin/cursor-agent /usr/local/bin/cursor-agent && \
ln -s /opt/cursor-agent/.local/bin/agent /usr/local/bin/agent && \
cursor-agent --version; \
fi
# Create writable home directory for non-root execution
# spawn-backend-docker.ts uses --tmpfs /tmp/deckent-home, but this ensures
# the base path exists even without tmpfs (e.g., docker exec debugging)
RUN mkdir -p /tmp/deckent-home && chmod 777 /tmp/deckent-home
ENV HOME=/tmp/deckent-home
# Set workspace directory
WORKDIR /workspace
# Health check: verify claude CLI is available
HEALTHCHECK --interval=30s --timeout=5s --retries=1 \
CMD claude --version || exit 1
# No entrypoint — command passed by DockerSpawnBackend
CMD ["echo", "deckent-worker ready"]