refactor: shared source — replace per-group agent-runner copies with single RO mount

Replace the per-group agent-runner-src copy model with a single shared
read-only mount. Source and skills are now RO + shared; personality,
config, working files, and Claude state stay RW + per-group.

Key changes:
- Mount container/agent-runner/src/ RO at /app/src (all groups share one copy)
- Mount container/skills/ RO at /app/skills; per-group skill selection via
  symlinks in .claude-shared/skills/ based on container.json "skills" field
- Mount container.json as nested RO bind on top of RW group dir
- Move all NANOCLAW_* env vars to container.json (runner reads at startup)
- New runner config.ts module replaces process.env reads
- Move command gate (filtered/admin) from container to host router
- Dockerfile: remove source COPY, split CLI installs (claude-code last),
  move agent-runner deps above CLIs for better layer caching
- Add writeOutboundDirect for router denial responses
- Design doc at docs/shared-src.md

Not included (follow-up): DB migration to drop agent_provider columns,
cleanup of orphaned agent-runner-src directories.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
exe.dev user
2026-04-21 12:05:19 +00:00
committed by gavrielc
parent 596035be09
commit 8a12fa61ac
14 changed files with 715 additions and 249 deletions

View File

@@ -3,8 +3,12 @@
# Runs Claude Agent SDK in isolated Linux VM with browser automation.
#
# Runtime split:
# - agent-runner (our TypeScript code): Bun
# - agent-runner (our TypeScript code): Bun, mounted RO at /app/src by host
# - globally-installed Node CLIs (claude-code, agent-browser, vercel): pnpm + Node
#
# Source is never baked in — /app/src is provided by a shared read-only
# bind mount at runtime (see src/container-runner.ts). Source-only changes
# never require an image rebuild.
FROM node:22-slim
@@ -66,36 +70,39 @@ RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" && \
install -m 0755 /root/.bun/bin/bun /usr/local/bin/bun && \
rm -rf /root/.bun
# ---- pnpm + global Node CLIs -------------------------------------------------
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
# agent-browser has a postinstall build script — pnpm skips these by default.
# Allowlist it via .npmrc so the install doesn't silently produce a broken
# package. Pinned versions so every rebuild is reproducible.
RUN --mount=type=cache,target=/root/.cache/pnpm \
echo "only-built-dependencies[]=agent-browser" > /root/.npmrc && \
echo "only-built-dependencies[]=@anthropic-ai/claude-code" >> /root/.npmrc && \
pnpm install -g \
"@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
"agent-browser@${AGENT_BROWSER_VERSION}" \
"vercel@${VERCEL_VERSION}"
# ---- agent-runner ------------------------------------------------------------
# ---- agent-runner deps -------------------------------------------------------
# Deps are cached independently of CLI versions. Source is NOT baked in —
# it's provided by the shared RO mount at runtime.
WORKDIR /app
# Copy manifest + lockfile first so the install layer caches independently of
# source edits.
COPY agent-runner/package.json agent-runner/bun.lock ./
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install --frozen-lockfile
# Source. Bun runs TS directly — no tsc build step. The host remounts this
# path at runtime via `src/container-runner.ts` so source edits on the host
# take effect without rebuilding the image; the baked copy is the fallback.
COPY agent-runner/ ./
# ---- pnpm + global Node CLIs -------------------------------------------------
# Most stable first, most frequently bumped last. Bumping claude-code
# (the most common change) only invalidates one layer.
#
# only-built-dependencies gates pnpm's supply-chain policy:
# - agent-browser has a postinstall build step.
# - @anthropic-ai/claude-code's postinstall downloads the native Claude
# binary (linux-arm64 variant on our image). Without the allowlist
# the SDK fails at spawn time with "native binary not found".
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN --mount=type=cache,target=/root/.cache/pnpm \
echo "only-built-dependencies[]=agent-browser" > /root/.npmrc && \
echo "only-built-dependencies[]=@anthropic-ai/claude-code" >> /root/.npmrc && \
pnpm install -g "vercel@${VERCEL_VERSION}"
RUN --mount=type=cache,target=/root/.cache/pnpm \
pnpm install -g "agent-browser@${AGENT_BROWSER_VERSION}"
RUN --mount=type=cache,target=/root/.cache/pnpm \
pnpm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"
# ---- Entrypoint --------------------------------------------------------------
COPY entrypoint.sh /app/entrypoint.sh