daemon: agent-driven workspace images — repo→image registry (GET/PUT/DELETE /api/repos), spawn resolves custom image, self-healing lvmh-ops control container with docker.sock; 132 tests, e2e 87/87

This commit is contained in:
Raphael Westphal
2026-08-18 23:54:09 +02:00
parent 8fcaeb7c03
commit 7287b831e4
15 changed files with 1216 additions and 38 deletions
+26
View File
@@ -170,6 +170,9 @@ Errors: `{"error": "message"}` with appropriate status.
| `POST /api/spawn` | `{repo: "group/project", branch?: "main"}``{sessionId, containerId}` | clone (per-repo volume) → run container |
| `GET /api/spawn/status` | → `[{repo, state, containerId, sessionId?}]` | in-flight clone/spawn jobs |
| `GET /api/repos` | → `[{repo, image}]` | repo→custom image registrations, sorted by repo |
| `PUT /api/repos/{repo}/image` | `{image}``{ok}` | register custom image; repo must pass repo validation, image must match `lvmh-worker-*` |
| `DELETE /api/repos/{repo}/image` | → `{ok}` | drop registration (idempotent); repo spawns fall back to `lvmh-worker:latest` |
`POST /api/spawn` semantics:
@@ -187,6 +190,26 @@ Errors: `{"error": "message"}` with appropriate status.
4. Response returns immediately after container create; status via
`/api/spawn/status` and session list.
**Repo→image resolution:** before creating the container the daemon looks up
`repo` in the repo-images registry (`repo_images` table, managed by the
`/api/repos` routes). A registered image replaces `lvmh-worker:latest` in
the created container; the daemon does NOT build custom images — if the
registered image is missing on the docker host the job errors with
`custom image <name> not built (ask the ops agent to build it)`. Image names
are pinned to the `lvmh-worker-*` namespace by validation. Without a
registration (or after DELETE) spawns use the default worker image.
**Ops container.** The daemon keeps one long-lived control container,
`lvmh-ops` (image `lvmh-ops:latest`, built from `docker/control.Dockerfile`
when missing): an agent pi with the docker socket mounted, its own
`lvmh-ops-work:/ops` workspace volume and the shared session/pi-cache
volumes, on the daemon network with a fixed session id `lvmh-ops-control`.
It prepares repos, builds per-repo worker images (docker CLI via the socket)
and registers them through `/api/repos`. A loop re-checks every 30s: stopped
→ started, missing → built+created+started; `DELETE
/api/sessions/lvmh-ops-control/container` removes it and the loop recreates
it on the next tick with a fresh session.
### GitLab
| `GET /api/gitlab/status` | → `{connected: bool, baseUrl, username?}` | |
@@ -244,6 +267,7 @@ CREATE TABLE events(sessionId TEXT NOT NULL, seq INTEGER NOT NULL,
CREATE TABLE sessions(id TEXT PRIMARY KEY, info TEXT NOT NULL, -- session JSON
lastSeq INTEGER NOT NULL DEFAULT 0, lastEventAt INTEGER, online INTEGER DEFAULT 0);
CREATE TABLE settings(key TEXT PRIMARY KEY, value TEXT NOT NULL); -- gitlab PAT etc
CREATE TABLE repo_images(repo TEXT PRIMARY KEY, image TEXT NOT NULL); -- repo→custom worker image
```
File `lvmh.db` on volume `lvmh-data`.
@@ -256,3 +280,5 @@ File `lvmh.db` on volume `lvmh-data`.
| `LVMH_TOKEN` | plugin, daemon, web UI storage | shared secret |
| `ZAI_RENAUD_API_KEY` | daemon, containers | LLM provider key |
| `LVMH_CONTAINER_DOCKER_SOCK` | daemon container | `/var/run/docker.sock` |
| `LVMH_GITEA_TOKEN` | ops container | PAT passed through from the daemon store, for repo clone/push |
| `LVMH_API` | ops container | `http://lvmh:8686` — daemon REST base for curl |