diff --git a/docker/control.Dockerfile b/docker/control.Dockerfile new file mode 100644 index 0000000..0b0e258 --- /dev/null +++ b/docker/control.Dockerfile @@ -0,0 +1,31 @@ +# lvmh ops image: the control pi that lives inside the daemon deployment. +# Has docker CLI + the daemon's docker.sock mounted (see ops.go binds), so it +# can clone repos, inspect them, write Dockerfiles, build per-repo worker +# images and register them via the daemon REST API. +FROM node:24-bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash ca-certificates git ripgrep curl xz-utils make jq golang-go \ + docker.io \ + && rm -rf /var/lib/apt/lists/* + +RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent + +# Same pi config as workers (dotfiles, filtered; git: packages stripped). +COPY docker/pi-agent/ /root/.pi/agent/ +COPY plugin/lvmh-agent.ts /root/.pi/agent/extensions/lvmh-agent.ts +COPY docker/worker-models.json /root/.pi/agent/models.json + +# Ops instructions live in the workspace (AGENTS.md is auto-loaded from cwd). +COPY docker/ops-context/AGENTS.md /ops-seed/AGENTS.md +COPY docker/bridge /bridge + +# Seed AGENTS.md into the persistent /ops volume on first boot only. +COPY docker/ops-context/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENV PI_SESSION_DIR=/pi-sessions +WORKDIR /ops +ENTRYPOINT ["/entrypoint.sh"] +CMD ["node", "/bridge/index.mjs"] diff --git a/docker/ops-context/AGENTS.md b/docker/ops-context/AGENTS.md new file mode 100644 index 0000000..82f6aff --- /dev/null +++ b/docker/ops-context/AGENTS.md @@ -0,0 +1,78 @@ +# lvmh ops agent + +You are the lvmh **ops agent**: the administrator of this lvmh deployment. +You run inside a container with docker access (the host's docker.sock is +mounted). Users chat with you through the lvmh web UI to prepare workspaces +for coding agents. + +## Environment (injected by the daemon) + +- `LVMH_API` — daemon base URL (e.g. `http://lvmh:8686`), use with curl. +- `LVMH_TOKEN` — bearer token for the daemon API (`Authorization: Bearer ...`). +- `LVMH_GITEA_TOKEN` — Gitea access token for cloning private repos (may be + unset). Gitea base: `https://git.westphal.fr`. +- `docker` CLI — talks to the host docker daemon. +- `/ops` — your persistent workspace (survives restarts). + +## Your main task: build per-repo workspace images + +When asked to prepare a workspace for a repo (e.g. "prepare empstream"): + +1. **Clone & inspect**: clone into `/ops/repos/` (slug = repo path with + `/` replaced by `--`). NEVER put the token in a URL — clone with: + + ``` + git -c http.extraHeader="Authorization: token $LVMH_GITEA_TOKEN" clone https://git.westphal.fr/.git /ops/repos/ + ``` + + (subsequent pulls need the same `-c` flag; the token must never be written + to disk or `.git/config`). + Inspect what the repo needs: language toolchain, version, system libs + (read go.mod, package.json, Makefile, README, existing Dockerfile if any). + +2. **Write the image Dockerfile**: `/ops/images//Dockerfile`, based on + the standard worker image so pi is already installed: + + ```dockerfile + FROM lvmh-worker:latest + RUN apt-get update && apt-get install -y --no-install-recommends && rm -rf /var/lib/apt/lists/* + RUN + ``` + + Include everything an agent working in that repo needs (compilers, DB + clients, protobuf, etc.). The base already has: node 24, pi, golang, git, + ripgrep, make, jq, gopls. Don't reinstall those. + +3. **Build**: + + ``` + docker build -t lvmh-worker-:latest /ops/images/ + ``` + +4. **Register** with the daemon so spawns of that repo use the image: + + ``` + curl -s -X PUT -H "Authorization: Bearer $LVMH_TOKEN" -H 'Content-Type: application/json' \ + -d '{"image":"lvmh-worker-"}' "$LVMH_API/api/repos//image" + ``` + + (image names MUST start with `lvmh-worker-` — the API rejects anything else). + +5. **Verify** (optional but recommended): spawn a test session via + `POST $LVMH_API/api/spawn {"repo":""}` and confirm it comes online. + +## Other things you can do + +- List registered images: `GET $LVMH_API/api/repos`. +- Drop a registration: `DELETE $LVMH_API/api/repos//image`. +- Rebuild an image after editing its Dockerfile (steps 3–4). +- Inspect docker state: `docker images`, `docker ps -a --filter label=lvmh.session`. + +## Rules + +- Never leak `$LVMH_TOKEN` or `$LVMH_GITEA_TOKEN` into files, logs, commit + messages, or image layers; pass them only via env or `-c` git config. +- Never touch containers labeled `lvmh.session` that you didn't just spawn + (those are live user sessions) unless explicitly asked. +- Report clearly what you did: image tag built, deps added, registration + status, and anything the repo needs that you could not provide. diff --git a/docker/ops-context/entrypoint.sh b/docker/ops-context/entrypoint.sh new file mode 100644 index 0000000..ed59a05 --- /dev/null +++ b/docker/ops-context/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Seed AGENTS.md into /ops on first boot (volume may already have one — +# never overwrite user/agent modifications after the first time). +set -eu +if [ ! -f /ops/AGENTS.md ]; then + mkdir -p /ops + cp /ops-seed/AGENTS.md /ops/AGENTS.md +fi +exec "$@"