integration: worker image, bridge, compose, Makefile, deploy scripts
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
# Copy to .env (gitignored) and fill.
|
||||||
|
# Shared secret for plugin, daemon, and web UI.
|
||||||
|
LVMH_TOKEN=change-me
|
||||||
|
# LLM provider key (injected into spawned containers).
|
||||||
|
ZAI_RENAUD_API_KEY=
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
REMOTE ?= alarm
|
||||||
|
REMOTE_DIR ?= /zdata/root/dockerFiles/lvmh
|
||||||
|
|
||||||
|
.PHONY: help build verify worker-image daemon web test deploy e2e clean
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "build build daemon + web (dev checks)"
|
||||||
|
@echo "verify vet + staticcheck + tests + web build"
|
||||||
|
@echo "worker-image build lvmh-worker:latest (requires plugin/)"
|
||||||
|
@echo "deploy rsync to alarm:$(REMOTE_DIR) and docker compose up -d"
|
||||||
|
|
||||||
|
build: daemon web
|
||||||
|
|
||||||
|
daemon:
|
||||||
|
cd daemon && go build ./...
|
||||||
|
|
||||||
|
web:
|
||||||
|
cd web && npm install --no-fund --no-audit && npm run build
|
||||||
|
|
||||||
|
worker-image:
|
||||||
|
docker build -f docker/worker.Dockerfile \
|
||||||
|
--build-arg LVMH_PLUGIN=build-plugin/lvmh-agent.ts \
|
||||||
|
-t lvmh-worker:latest .
|
||||||
|
@# build-plugin/ is prepared by deploy/prep-worker.sh
|
||||||
|
|
||||||
|
verify: daemon
|
||||||
|
cd daemon && go vet ./...
|
||||||
|
cd daemon && staticcheck ./... || true
|
||||||
|
cd daemon && go test ./... -count=1
|
||||||
|
cd web && npm run build
|
||||||
|
cd plugin && npx tsc --noEmit -p tsconfig.json
|
||||||
|
|
||||||
|
test:
|
||||||
|
cd daemon && go test ./... -count=1
|
||||||
|
|
||||||
|
deploy: verify worker-image
|
||||||
|
bash deploy/deploy.sh $(REMOTE) $(REMOTE_DIR)
|
||||||
|
|
||||||
|
e2e:
|
||||||
|
bash deploy/e2e.sh
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf web/dist web/node_modules daemon/lvmh-daemon .pi/scratch
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Deploy lvmh to alarm over ssh (tailnet). Source is rsynced; compose builds there.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REMOTE="${1:-alarm}"
|
||||||
|
REMOTE_DIR="${2:-/zdata/root/dockerFiles/lvmh}"
|
||||||
|
|
||||||
|
if ! ssh "$REMOTE" "test -f $REMOTE_DIR/.env" 2>/dev/null; then
|
||||||
|
echo "ERROR: $REMOTE:$REMOTE_DIR/.env missing. Create it first (see .env.example):"
|
||||||
|
echo " ssh $REMOTE 'mkdir -p $REMOTE_DIR && cat > $REMOTE_DIR/.env' <<EOF
|
||||||
|
LVMH_TOKEN=...
|
||||||
|
ZAI_RENAUD_API_KEY=...
|
||||||
|
EOF"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rsync -az --delete \
|
||||||
|
--exclude .git --exclude node_modules --exclude web/dist \
|
||||||
|
--exclude .env --exclude .scratch --exclude .pi \
|
||||||
|
./ "$REMOTE:$REMOTE_DIR/"
|
||||||
|
|
||||||
|
ssh "$REMOTE" "cd $REMOTE_DIR && docker compose build lvmh && docker compose up -d && docker compose ps"
|
||||||
|
echo "Deployed: http://$REMOTE:8686"
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Post-deploy smoke test. Requires LVMH_HOST (default alarm), LVMH_TOKEN env.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST="${LVMH_HOST:-alarm}"
|
||||||
|
TOKEN="${LVMH_TOKEN:?set LVMH_TOKEN}"
|
||||||
|
BASE="http://$HOST:8686"
|
||||||
|
|
||||||
|
auth=(-H "Authorization: Bearer $TOKEN")
|
||||||
|
|
||||||
|
echo "== sessions =="
|
||||||
|
curl -fsS "${auth[@]}" "$BASE/api/sessions" | head -c 400; echo
|
||||||
|
|
||||||
|
echo "== gitlab status =="
|
||||||
|
curl -fsS "${auth[@]}" "$BASE/api/gitlab/status"; echo
|
||||||
|
|
||||||
|
echo "== web dist =="
|
||||||
|
curl -fsS "$BASE/" | grep -o '<title>[^<]*</title>' || echo "no title tag"
|
||||||
|
|
||||||
|
echo "SMOKE OK"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# lvmh daemon + worker builder context. Build worker image first (make worker-image).
|
||||||
|
services:
|
||||||
|
lvmh:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: daemon/Dockerfile
|
||||||
|
image: lvmh-daemon:latest
|
||||||
|
container_name: lvmh-daemon
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8686:8686"
|
||||||
|
environment:
|
||||||
|
LVMH_TOKEN: ${LVMH_TOKEN}
|
||||||
|
ZAI_RENAUD_API_KEY: ${ZAI_RENAUD_API_KEY}
|
||||||
|
# How spawned containers reach the daemon: alias on the shared network.
|
||||||
|
LVMH_CONTAINER_LVMH_URL: ws://lvmh:8686/agent/ws
|
||||||
|
GITLAB_BASE_URL: https://git.westphal.fr
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- lvmh-data:/data
|
||||||
|
- .:/app/build:ro # repo checkout (rsynced by deploy.sh)
|
||||||
|
# daemon rebuilds lvmh-worker from /app/build/docker/worker.Dockerfile
|
||||||
|
networks:
|
||||||
|
lvmh-net:
|
||||||
|
aliases: [lvmh]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
lvmh-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
lvmh-net:
|
||||||
|
name: lvmh-net
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// lvmh worker bridge: headless pi session via SDK; the lvmh plugin (installed
|
||||||
|
// at /root/.pi/agent/extensions/lvmh-agent.ts) dials the daemon and delivers
|
||||||
|
// web prompts through pi.sendUserMessage. This process just hosts the session.
|
||||||
|
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (err) => {
|
||||||
|
console.error("[lvmh-bridge] unhandledRejection:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { session } = await createAgentSession();
|
||||||
|
console.error(`[lvmh-bridge] session up, cwd=${process.cwd()}`);
|
||||||
|
|
||||||
|
session.subscribe((event) => {
|
||||||
|
// Keep the loop warm; the extension does the mirroring.
|
||||||
|
if (event.type === "agent_settled") {
|
||||||
|
console.error("[lvmh-bridge] agent settled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const sig of ["SIGTERM", "SIGINT"]) {
|
||||||
|
process.on(sig, () => {
|
||||||
|
console.error(`[lvmh-bridge] ${sig}, exiting`);
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Stay alive forever.
|
||||||
|
setInterval(() => {}, 1 << 30);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[lvmh-bridge] failed to create agent session", err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"zai-renaud": {
|
||||||
|
"baseUrl": "https://api.z.ai/api/coding/paas/v4",
|
||||||
|
"api": "openai-completions",
|
||||||
|
"apiKey": "$ZAI_RENAUD_API_KEY",
|
||||||
|
"compat": {
|
||||||
|
"supportsDeveloperRole": false,
|
||||||
|
"thinkingFormat": "zai",
|
||||||
|
"supportsReasoningEffort": true
|
||||||
|
},
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "glm-5.3",
|
||||||
|
"name": "GLM-5.3",
|
||||||
|
"reasoning": true,
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"thinkingLevelMap": { "xhigh": "xhigh", "max": "max" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# lvmh worker image: headless pi with golang toolchain, git, ripgrep.
|
||||||
|
# Built by the daemon on first spawn if missing (LVMH_WORKER_DOCKERFILE),
|
||||||
|
# or manually via `make worker-image`.
|
||||||
|
FROM node:24-bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
bash ca-certificates git ripgrep curl xz-utils \
|
||||||
|
golang-go \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
||||||
|
|
||||||
|
# lvmh plugin: dial-home mirror (built from plugin/lvmh-agent.ts at image build)
|
||||||
|
ARG LVMH_PLUGIN=./plugin-placeholder
|
||||||
|
COPY ${LVMH_PLUGIN} /root/.pi/agent/extensions/lvmh-agent.ts
|
||||||
|
COPY docker/worker-models.json /root/.pi/agent/models.json
|
||||||
|
COPY docker/bridge /bridge
|
||||||
|
|
||||||
|
# Per-session pi sessions persist here (volume lvmh-sessions)
|
||||||
|
ENV PI_SESSION_DIR=/pi-sessions
|
||||||
|
WORKDIR /workspace
|
||||||
|
CMD ["node", "/bridge/index.mjs"]
|
||||||
Reference in New Issue
Block a user