GOAL.md's first gap: without a runner a standalone image is a read-only viewer, because launching `claude -p` needed a host-side process. The Claude Code CLI is a self-contained native binary, so the image can just carry it — and the *same* sidecar/sidecar.py the homelab runs on its host runs in-container against it. With RUNNER_IN_CONTAINER=1 (the standalone default) docker-entrypoint.sh starts the runner on the loopback, mints a SIDECAR_TOKEN if none was given, overrides SIDECAR_URL to point at it, and gives the CLI a writable $HOME on the data volume. The transcripts it writes there become a third live source (RUNNER_TRANSCRIPTS_DIR → SOURCE_DIRS), so an in-container session streams into the viewer like any other. Verified end to end: a standalone container (workspace + data, no homelab, no host sidecar) spawns a session, the CLI runs it, and the turn renders in the conversation list with its model tag. The CLI authenticates from ANTHROPIC_API_KEY or a Claude home mounted at RUNNER_HOME. The homelab is unchanged: it leaves the flag off and keeps its host sidecar, which is what lets a run use the host's own hooks, skills and credentials. standalone-smoke.sh now also asserts the CLI is on PATH and the runner is healthy — the packaging property that would otherwise regress silently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
3.6 KiB
YAML
76 lines
3.6 KiB
YAML
# Standalone ai-agent — the viewer against any repo, with no homelab around it.
|
|
#
|
|
# This is the generic counterpart of the homelab `docker-compose.yml`: same image,
|
|
# but nothing homelab-specific is required. Every mount below is either generic
|
|
# (a repo to look at, the machine's own Claude Code transcripts) or optional, and
|
|
# the app degrades to an empty catalog for whatever isn't there.
|
|
#
|
|
# AI_AGENT_WORKSPACE=/path/to/your/repo \
|
|
# docker compose -f docker-compose.standalone.yml up -d --build
|
|
#
|
|
# Then open http://localhost:8096. `scripts/standalone-smoke.sh` runs exactly this
|
|
# shape against a throwaway workspace and asserts the API comes up clean.
|
|
#
|
|
# Sessions are spawned by the **in-image runner** (RUNNER_IN_CONTAINER=1): the
|
|
# Claude Code CLI is baked into the image and driven by the same sidecar the
|
|
# homelab runs on its host — no host process, no systemd unit. It needs the CLI to
|
|
# be able to authenticate, which is either:
|
|
# • ANTHROPIC_API_KEY (below), or
|
|
# • an already-logged-in Claude home mounted at RUNNER_HOME (its OAuth
|
|
# credentials come with it) — e.g. `-v ~/.claude:/data/home/.claude`.
|
|
# With neither, everything still works except spawning.
|
|
#
|
|
# One caveat remains, tracked in GOAL.md: building needs the homelab's private npm
|
|
# registry (frontend/.npmrc pulls @gabvdl/ui from verdaccio on localhost:4873), so
|
|
# today a standalone *build* only works on the homelab — a prebuilt image runs
|
|
# anywhere. Publishing @gabvdl/ui to public npm is what closes this.
|
|
#
|
|
# On the homelab itself the default container name collides with the live one, so
|
|
# docker refuses the run — set AI_AGENT_NAME (and AI_AGENT_PORT) to try it here.
|
|
services:
|
|
ai-agent:
|
|
container_name: ${AI_AGENT_NAME:-ai-agent}
|
|
build:
|
|
context: .
|
|
# Only needed while @gabvdl/ui comes from the host-bound private registry.
|
|
network: host
|
|
image: ${AI_AGENT_IMAGE:-ai-agent:standalone}
|
|
restart: unless-stopped
|
|
# Match the owner of the workspace you mount, so edits keep their ownership.
|
|
user: "${AI_AGENT_UID:-1000}:${AI_AGENT_GID:-1000}"
|
|
environment:
|
|
TZ: ${TZ:-UTC}
|
|
WORKSPACE: /workspace
|
|
STATIC_DIR: /app/static
|
|
DB_PATH: /data/ai-agent.db
|
|
REPO_DIR: /workspace
|
|
PROJECTS_DIR: /workspace/projects
|
|
TEMPLATES_DIR: /workspace/projects/templates
|
|
TRANSCRIPTS_DIR: /transcripts
|
|
# Real token counts + dollar costs (Anthropic count_tokens), and the CLI's
|
|
# auth for the in-image runner. Optional: without it files carry no
|
|
# token/cost figures and spawning needs a logged-in Claude home instead.
|
|
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
|
# Spawn sessions from inside the container: the entrypoint runs the bundled
|
|
# runner (the Claude Code CLI + sidecar.py) on the loopback and points the
|
|
# backend at it. Set to 0 to fall back to a host sidecar (SIDECAR_URL).
|
|
RUNNER_IN_CONTAINER: ${RUNNER_IN_CONTAINER:-1}
|
|
# The CLI's HOME — config, credentials and the transcripts it writes. It
|
|
# lives on the data volume, so sessions and logins survive a restart.
|
|
RUNNER_HOME: /data/home
|
|
SIDECAR_MODEL: ${SIDECAR_MODEL:-opus}
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
volumes:
|
|
# The repo to look at: its CLAUDE.md, .claude/ tree and projects/.
|
|
- ${AI_AGENT_WORKSPACE:-./workspace}:/workspace
|
|
# This machine's Claude Code transcripts — the same path on any machine.
|
|
- ${AI_AGENT_TRANSCRIPTS:-${HOME}/.claude/projects}:/transcripts:ro
|
|
# SQLite store, transcript archive, conversation metadata, uploads.
|
|
- ai-agent-data:/data
|
|
ports:
|
|
- "127.0.0.1:${AI_AGENT_PORT:-8096}:8080"
|
|
|
|
volumes:
|
|
ai-agent-data:
|