initial: NAS agent runner setup
This commit is contained in:
commit
9d8a7fe237
16 changed files with 1121 additions and 0 deletions
147
README.md
Normal file
147
README.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# scrum4me-agent-runner
|
||||
|
||||
Headless Claude Code worker die de Scrum4Me job-queue (M13) leegtrekt vanaf
|
||||
een QNAP NAS via Container Station. Geen Vercel, geen browser, geen
|
||||
toetsenbord — Claude Code draait als daemon, claimt jobs uit
|
||||
`mcp__scrum4me__wait_for_job`, voert ze uit in een per-job clone, en pusht
|
||||
nooit zelf.
|
||||
|
||||
## Architectuur in één plaatje
|
||||
|
||||
```
|
||||
┌─ QNAP TS-664 (Container Station) ─────────────────────────────┐
|
||||
│ │
|
||||
│ ┌─ container: agent-runner ────────────────────────────────┐ │
|
||||
│ │ PID 1: tini → run-agent.sh (daemon-loop) │ │
|
||||
│ │ ├─ health-server.js (8080 → host) │ │
|
||||
│ │ └─ claude -p (per-batch, met MCP via stdio) │ │
|
||||
│ │ └─ scrum4me-mcp → Neon Postgres │ │
|
||||
│ │ │ │
|
||||
│ │ /tmp/job-<id> ephemeral working trees │ │
|
||||
│ │ /var/cache/repos bare git mirrors (volume) │ │
|
||||
│ │ /var/cache/npm npm cache (volume) │ │
|
||||
│ │ /var/log/agent run + job logs (volume) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ /share/Agent/cache /share/Agent/logs /share/Agent/state │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ HTTPS
|
||||
Neon Postgres (Scrum4Me DB)
|
||||
▲
|
||||
│
|
||||
Vercel ─── Scrum4Me UI (gebruikers enqueueen jobs)
|
||||
```
|
||||
|
||||
Eén `claude -p`-invocation roept intern `wait_for_job` aan totdat de
|
||||
queue leeg is (≈600 s lege block-time → afsluiten). De wrapper start
|
||||
`claude -p` opnieuw zodra hij eindigt, met exponentiële backoff bij
|
||||
fouten.
|
||||
|
||||
## Wat zit waar
|
||||
|
||||
| Bestand | Doel |
|
||||
|--------------------------|-----------------------------------------------------------------|
|
||||
| `Dockerfile` | Ubuntu 22.04 + Node 22 + Claude Code + scrum4me-mcp + scripts |
|
||||
| `docker-compose.yml` | Service-definitie, volumes, env-file, restart-policy, limits |
|
||||
| `package.json` | Npm-dependencies van de runner zelf (alleen `scrum4me-mcp` pin) |
|
||||
| `mcp-config.json` | Claude Code MCP-config (verwijst stdio naar scrum4me-mcp) |
|
||||
| `CLAUDE.md` | Agent-rol-instructies, auto-geladen door `claude -p` |
|
||||
| `bin/entrypoint.sh` | Container-startup: dirs, health-server, daemon-loop |
|
||||
| `bin/run-agent.sh` | Daemon-loop met backoff, exit-code-routing en state-writes |
|
||||
| `bin/check-tokens.sh` | Pre-flight: API-token, OAuth-token, DB-bereikbaarheid |
|
||||
| `bin/job-prepare.sh` | Per-job: bare-fetch + clone-via-reference naar `/tmp/job-<id>` |
|
||||
| `bin/job-cleanup.sh` | Per-job: logs naar `/var/log`, working tree weg |
|
||||
| `bin/health-server.js` | HTTP-endpoint op 8080 dat state.json en marker-files leest |
|
||||
| `bin/rotate-logs.sh` | Compress/cleanup van oude `.log`-bestanden |
|
||||
| `.env.example` | Alle env-vars met uitleg |
|
||||
|
||||
## Vereisten op de NAS
|
||||
|
||||
- Container Station 2+ (Docker compose v2)
|
||||
- Drie shares aangemaakt: `/share/Agent/cache`, `/share/Agent/logs`, `/share/Agent/state`
|
||||
- Of één share `/share/Agent` waaronder de drie subdirs vallen
|
||||
- Internet-uitgang naar `api.anthropic.com`, `github.com`, je Neon-host, `registry.npmjs.org`
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
# 1. Op je werkstation: token's regelen
|
||||
# a. CLAUDE_CODE_OAUTH_TOKEN → draai `claude setup-token` (browser-flow)
|
||||
# b. SCRUM4ME_TOKEN → log in als de dedicated agent-user in
|
||||
# Scrum4Me, /settings/tokens, label "NAS-runner"
|
||||
# c. DATABASE_URL/DIRECT_URL → Neon dashboard
|
||||
|
||||
# 2. Repo op de NAS plaatsen
|
||||
ssh admin@nas
|
||||
cd /share/Agent
|
||||
git clone https://github.com/<jij>/scrum4me-agent-runner.git
|
||||
cd scrum4me-agent-runner
|
||||
|
||||
# 3. Env aanmaken
|
||||
cp .env.example .env
|
||||
chmod 600 .env
|
||||
vi .env # vul alle waarden in
|
||||
|
||||
# 4. Build + start
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
|
||||
# 5. Verifiëren
|
||||
curl http://nas.local:8080/health
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
## Updaten (handmatig, bewust)
|
||||
|
||||
`SCRUM4ME_TOKEN` of `CLAUDE_CODE_OAUTH_TOKEN` rouleer je via een rebuild:
|
||||
|
||||
```bash
|
||||
cd /share/Agent/scrum4me-agent-runner
|
||||
git pull
|
||||
vi .env # nieuwe waarden
|
||||
docker compose build # nieuwe scrum4me-mcp-versie als dat veranderd is
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Dezelfde flow voor schema-drift in scrum4me-mcp: pin een nieuwe
|
||||
`MCP_GIT_REF` in `.env` of in `docker-compose.yml`, rebuild.
|
||||
|
||||
## Health-endpoint
|
||||
|
||||
`GET http://<nas>:8080/health` retourneert:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "running", // running | idle | unhealthy | token-expired
|
||||
"lastBatchAt": "2026-05-01T12:34:56Z",
|
||||
"lastBatchExit": 0,
|
||||
"consecutiveFailures": 0,
|
||||
"tokenStatus": { "anthropic": "ok", "scrum4me": "ok", "db": "ok" }
|
||||
}
|
||||
```
|
||||
|
||||
HTTP-status: `200` als running/idle, `503` bij token-expired of als de
|
||||
laatste heartbeat ouder is dan 5 minuten.
|
||||
|
||||
## Filesystem-grenzen
|
||||
|
||||
De agent-user heeft geen SSH-keys, geen `~/.gitconfig` met push-credentials,
|
||||
en geen toegang tot andere shares dan `/share/Agent/*`. Commits worden
|
||||
lokaal in de per-job clone gemaakt; pushen gebeurt door jou op je
|
||||
werkstation na review (CLAUDE.md regel: *"`git push` is altijd expliciet"*).
|
||||
|
||||
## Bekende grenzen
|
||||
|
||||
- **Eén actieve job tegelijk.** De wrapper-loop is sequentieel. Voor
|
||||
parallellisme zou je meerdere containers met dezelfde `SCRUM4ME_TOKEN`
|
||||
kunnen draaien — `wait_for_job` gebruikt `FOR UPDATE SKIP LOCKED` dus
|
||||
dat is veilig op DB-niveau, maar dan moet je je `node_modules`-cache
|
||||
per container scheiden.
|
||||
- **OAuth-token: 1 jaar geldig.** Bij verloop schrijft de wrapper een
|
||||
`TOKEN_EXPIRED`-marker en wordt de container `unhealthy`. Geen
|
||||
auto-rotatie.
|
||||
- **`npm install` per job** kost op een N5095 ~30–60 s per Next.js-clone,
|
||||
óók met de pnpm-store. Voor zeer kleine fixes is dat de dominante
|
||||
factor. Kan later vervangen worden door een persistente warm-`node_modules`
|
||||
per repo als dat een knelpunt wordt.
|
||||
Loading…
Add table
Add a link
Reference in a new issue