feat: Add Hermes setup sub-script and in-container CLI installation

Fixes Hermes CLI not found in PATH by installing hermes-agent inside paperclip_app container. Symlinks hermes executable to PATH, mounts persistent volumes for venv and hermes-data, configures shared API keys, and integrates setup script into deployment.
This commit is contained in:
Jannik committed 2026-10-02 05:10:10 +02:00
1 parent 92c7442df7
commit ddf678fb9c
6 files changed
+180 -127

No files matched your search

+18 -6
View File
@@ -84,13 +84,25 @@ Paperclip ist in seiner Benutzeroberfläche bei der Erstinstallation stark auf o
### Hermes in PaperClip einbinden
Nachdem der Service gestartet ist, kann PaperClip Hermes als Agenten einstellen:
Das Setup-Unterskript `services/hermes/setup_hermes.sh` wird automatisch beim Deployment ausgeführt und richtet die `hermes` CLI vollständig im PaperClip-Container ein (`pip install hermes-agent`).
1. Im PaperClip-Dashboard → **"Hire Agent"**
2. Adapter wählen: **`Hermes Gateway`**
3. Gateway-URL eintragen: `https://hermes.paperclip.orfel.de`
4. Falls nach einem API Key gefragt wird: `hermes_secret_paperclip_7a8f9b2c3d4e5f60` (definiert in `shared.env` als `HERMES_API_KEY`)
5. Hermes-Agent konfigurieren und Tasks zuweisen.
In PaperClip kann Hermes auf zwei Arten eingestellt werden:
1. **`hermes_local` (Standard & empfohlen):**
* Im PaperClip-Dashboard → **"Hire Agent"**
* Adapter: **`hermes_local`**
* PaperClip ruft die CLI `hermes` direkt im Container auf. API Keys werden automatisch aus `services/shared.env` bereitgestellt.
2. **`hermes_gateway` (Standalone):**
* Adapter: **`Hermes Gateway`**
* Gateway-URL: `http://hermes:8642` (intern) oder `https://hermes.paperclip.orfel.de`
### Manuelles Setup
Falls Hermes manuell neu eingerichtet werden soll:
```bash
sudo bash /opt/PaperClip/services/hermes/setup_hermes.sh
```
## Weitere Adapter einbinden
+4
View File
@@ -353,6 +353,10 @@ create_volumes() {
for svc_name in "${ACTIVE_SERVICES[@]}"; do
mkdir -p "${DATA_BASE}/${svc_name}"
done
mkdir -p "${DATA_BASE}/paperclip-hermes-venv"
mkdir -p "${DATA_BASE}/paperclip-hermes-data"
# Node-User (UID 1000) benötigt Schreibrechte auf .hermes
chown -R 1000:1000 "${DATA_BASE}/paperclip-hermes-data" 2>/dev/null || true
}
###############################################################################
+6 -120
View File
@@ -1,128 +1,14 @@
#!/usr/bin/env bash
###############################################################################
# hermes service.init — läuft nach dem Start des Hermes-Containers
# Richtet Hermes vollständig ein:
# 1. Wartet, bis die API antwortet
# 2. Konfiguriert den Standard-Provider (OpenRouter)
# 3. Gibt Status und Dashboard-URL aus
# hermes service.init — aufgerufen durch deploy_script.sh (run_service_inits)
# Delegiert die vollständige Einrichtung an setup_hermes.sh
###############################################################################
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SHARED_ENV="${SCRIPT_DIR}/../shared.env"
CONTAINER="hermes"
API_PORT="8642"
DASHBOARD_PORT="9119"
API_BASE="http://127.0.0.1:${API_PORT}"
SETUP_SCRIPT="${SCRIPT_DIR}/setup_hermes.sh"
log() { echo -e "\e[32m[HERMES-INIT]\e[0m $*"; }
warn() { echo -e "\e[33m[HERMES-INIT][WARN]\e[0m $*"; }
err() { echo -e "\e[31m[HERMES-INIT][ERROR]\e[0m $*" >&2; }
###############################################################################
# 1. Shared API Keys laden
###############################################################################
OPENROUTER_API_KEY=""
MISTRAL_API_KEY=""
HERMES_API_KEY=""
if [[ -f "${SHARED_ENV}" ]]; then
# shellcheck source=/dev/null
source "${SHARED_ENV}"
if [[ -f "${SETUP_SCRIPT}" ]]; then
chmod +x "${SETUP_SCRIPT}"
bash "${SETUP_SCRIPT}"
fi
if [[ -z "${OPENROUTER_API_KEY}" && -z "${MISTRAL_API_KEY}" ]]; then
warn "Kein API Key in shared.env gefunden. Hermes wird ohne Provider-Konfiguration gestartet."
fi
###############################################################################
# 2. Auf Hermes-API warten
###############################################################################
log "Warte auf Hermes API (${API_BASE}/health)..."
for i in {1..45}; do
if docker exec "${CONTAINER}" curl -sf -H "Authorization: Bearer ${HERMES_API_KEY:-}" "${API_BASE}/health" >/dev/null 2>&1 || \
docker exec "${CONTAINER}" curl -sf "${API_BASE}/health" >/dev/null 2>&1 || \
docker exec "${CONTAINER}" wget -qO- --header="Authorization: Bearer ${HERMES_API_KEY:-}" "${API_BASE}/health" >/dev/null 2>&1 || \
docker exec "${CONTAINER}" wget -qO- "${API_BASE}/health" >/dev/null 2>&1; then
log "Hermes API antwortet!"
break
fi
if [[ $i -eq 45 ]]; then
err "Hermes API hat nach 90s nicht geantwortet. Logs:"
docker logs "${CONTAINER}" --tail 20 >&2
exit 1
fi
sleep 2
done
###############################################################################
# 3. Provider konfigurieren (idempotent)
###############################################################################
configure_provider() {
local provider_name="$1"
local api_key="$2"
local base_url="$3"
local model="$4"
log "Konfiguriere Provider: ${provider_name} (${base_url})..."
# Hermes REST API: Provider setzen
# POST /api/config/provider — setzt den Standard-LLM-Provider
local response
response=$(docker exec "${CONTAINER}" wget -qO- \
--header="Content-Type: application/json" \
--post-data="{\"provider\":\"${provider_name}\",\"apiKey\":\"${api_key}\",\"baseUrl\":\"${base_url}\",\"model\":\"${model}\"}" \
"${API_BASE}/api/config/provider" 2>/dev/null || echo "FAILED")
if [[ "${response}" == "FAILED" ]]; then
# Fallback: hermes CLI direkt im Container
log "REST-API-Konfiguration fehlgeschlagen — versuche hermes CLI..."
(docker exec "${CONTAINER}" hermes config set \
--provider "${provider_name}" \
--api-key "${api_key}" \
--base-url "${base_url}" \
--model "${model}" 2>/dev/null || \
warn "CLI-Konfiguration ebenfalls fehlgeschlagen. Manuelle Konfiguration über ${DASHBOARD_URL} nötig.") || true
else
log "Provider ${provider_name} erfolgreich konfiguriert."
fi
}
# Provider wählen: OpenRouter hat Vorrang vor Mistral
if [[ -n "${OPENROUTER_API_KEY}" ]]; then
configure_provider \
"openrouter" \
"${OPENROUTER_API_KEY}" \
"https://openrouter.ai/api/v1" \
"openrouter/auto"
elif [[ -n "${MISTRAL_API_KEY}" ]]; then
configure_provider \
"mistral" \
"${MISTRAL_API_KEY}" \
"https://api.mistral.ai/v1" \
"mistral-large-latest"
else
warn "Kein Provider konfiguriert — Hermes läuft ohne LLM."
fi
###############################################################################
# 4. Status & Dashboard-URL ausgeben
###############################################################################
DASHBOARD_URL="https://hermes.paperclip.orfel.de"
log "========================================================="
log " HERMES SETUP ABGESCHLOSSEN"
log "========================================================="
log " Dashboard: ${DASHBOARD_URL}"
log " Interne API: http://hermes:${API_PORT}"
log " PaperClip Adapter-URL: http://hermes:${API_PORT}"
log "========================================================="
log " In PaperClip einbinden:"
log " → Hire Agent → Adapter: hermes_gateway"
log " → Gateway URL: http://hermes:${API_PORT}"
log "========================================================="
echo ""
echo "Hermes Logs (letzte 10):"
docker logs "${CONTAINER}" --tail 10 2>&1
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env bash
###############################################################################
# PaperClip — Hermes Agent Setup Sub-Script
# Kann eigenständig ausgeführt oder vom Deploy-Skript aufgerufen werden.
#
# Aufgaben:
# 1. Richtet den Hermes CLI Client innerhalb des PaperClip-Containers ein,
# damit PaperClip via 'hermes_local' Adapter direkt Hermes-Agenten
# erstellen und ausführen kann (behebt 'Hermes CLI not found in PATH').
# 2. Konfiguriert die Umgebungsvariablen und API-Keys (OpenRouter etc.)
# für den Node-User von PaperClip.
# 3. Überprüft die Installation mit 'hermes --version' und 'hermes config'.
# 4. Stellt sicher, dass das eigenständige Hermes-Dashboard bereit ist.
###############################################################################
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SERVICES_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
REPO_DIR="$(cd "${SERVICES_DIR}/.." && pwd)"
SHARED_ENV="${SERVICES_DIR}/shared.env"
PAPERCLIP_CONTAINER="paperclip_app"
HERMES_CONTAINER="hermes"
log() { echo -e "\e[32m[HERMES-SETUP]\e[0m $*"; }
warn() { echo -e "\e[33m[HERMES-SETUP][WARN]\e[0m $*"; }
err() { echo -e "\e[31m[HERMES-SETUP][ERROR]\e[0m $*" >&2; }
log "========================================================="
log " Starte vollständige Hermes-Einrichtung für PaperClip..."
log "========================================================="
# -----------------------------------------------------------------------------
# 1. Shared API Keys laden
# -----------------------------------------------------------------------------
OPENROUTER_API_KEY=""
MISTRAL_API_KEY=""
if [[ -f "${SHARED_ENV}" ]]; then
# shellcheck source=/dev/null
source "${SHARED_ENV}"
fi
# -----------------------------------------------------------------------------
# 2. PaperClip Container prüfen
# -----------------------------------------------------------------------------
if ! docker ps --format '{{.Names}}' | grep -q "^${PAPERCLIP_CONTAINER}$"; then
warn "Container '${PAPERCLIP_CONTAINER}' läuft nicht. Überspringe In-Container-Setup."
else
log "1/3 Prüfe Hermes CLI im PaperClip-Container (${PAPERCLIP_CONTAINER})..."
hermes_installed=false
if docker exec "${PAPERCLIP_CONTAINER}" which hermes >/dev/null 2>&1; then
if docker exec "${PAPERCLIP_CONTAINER}" hermes --version >/dev/null 2>&1; then
hermes_installed=true
fi
fi
# Wenn der Venv-Mount existiert, aber nur der Symlink fehlt:
if [[ "${hermes_installed}" == "false" ]] && docker exec "${PAPERCLIP_CONTAINER}" test -x /opt/hermes-venv/bin/hermes; then
log "Existierende Hermes-Venv gefunden. Stelle Symlink her..."
docker exec -u root "${PAPERCLIP_CONTAINER}" ln -sf /opt/hermes-venv/bin/hermes /usr/local/bin/hermes
if docker exec "${PAPERCLIP_CONTAINER}" hermes --version >/dev/null 2>&1; then
hermes_installed=true
fi
fi
# Vollständige Installation wenn noch nicht vorhanden
if [[ "${hermes_installed}" == "false" ]]; then
log "Hermes CLI nicht gefunden — installiere in ${PAPERCLIP_CONTAINER} (pip install hermes-agent)..."
docker exec -u root "${PAPERCLIP_CONTAINER}" bash -c '
set -e
apt-get update -qq
apt-get install -y -qq --no-install-recommends python3-pip python3-venv curl git
mkdir -p /opt/hermes-venv
python3 -m venv /opt/hermes-venv
/opt/hermes-venv/bin/pip install --no-cache-dir hermes-agent
ln -sf /opt/hermes-venv/bin/hermes /usr/local/bin/hermes
chmod -R a+rx /opt/hermes-venv
rm -rf /var/lib/apt/lists/*
'
log "Hermes CLI erfolgreich in ${PAPERCLIP_CONTAINER} installiert!"
else
log "Hermes CLI ist bereits in ${PAPERCLIP_CONTAINER} installiert."
fi
# -------------------------------------------------------------------------
# 3. Hermes Konfiguration & Keys für PaperClip User (node) vorbereiten
# -------------------------------------------------------------------------
log "2/3 Konfiguriere Hermes-Umgebung für PaperClip..."
docker exec -u root "${PAPERCLIP_CONTAINER}" bash -c '
mkdir -p /home/node/.hermes
chown -R node:node /home/node/.hermes
'
# API Keys in ~/.hermes/.env schreiben, damit hermes sie immer findet
if [[ -n "${OPENROUTER_API_KEY}" ]]; then
docker exec -u root "${PAPERCLIP_CONTAINER}" bash -c "cat > /home/node/.hermes/.env <<EOF
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
EOF
chown node:node /home/node/.hermes/.env
chmod 600 /home/node/.hermes/.env
"
fi
# Verifikation im Container
log "Verifiziere Hermes im PaperClip-Container:"
docker exec -u node "${PAPERCLIP_CONTAINER}" hermes --version 2>&1 || true
fi
# -----------------------------------------------------------------------------
# 4. Standalone Hermes Container (Dashboard & Gateway) prüfen
# -----------------------------------------------------------------------------
if docker ps --format '{{.Names}}' | grep -q "^${HERMES_CONTAINER}$"; then
log "3/3 Standalone Hermes Container (${HERMES_CONTAINER}) ist aktiv."
# Sicherstellen, dass .env im Volume existiert
if [[ -n "${OPENROUTER_API_KEY}" ]]; then
docker exec -u root "${HERMES_CONTAINER}" bash -c "cat > /opt/data/.env <<EOF
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
EOF
chown -R hermes:hermes /opt/data/.env 2>/dev/null || true
chmod 600 /opt/data/.env
"
fi
else
log "3/3 Standalone Hermes Container (${HERMES_CONTAINER}) nicht aktiv (wird bei Bedarf gestartet)."
fi
log "========================================================="
log " HERMES VOLLSTÄNDIG EINGERICHTET!"
log "========================================================="
log " In PaperClip kannst du nun direkt einen Hermes-Agenten anlegen:"
log " → Adapter: hermes_local (empfohlen, läuft lokal im Container)"
log " → Oder Adapter: hermes_gateway (http://hermes:8642)"
log " Dashboard: https://hermes.paperclip.orfel.de (falls hermes-Service aktiv)"
log "========================================================="
+2
View File
@@ -38,6 +38,8 @@ services:
PAPERCLIP_OPENCODE_PROVIDERS: '{"openrouter":{"npm":"@ai-sdk/openai-compatible","options":{"baseURL":"https://openrouter.ai/api/v1","apiKey":"{env:OPENROUTER_API_KEY}"},"models":{"anthropic/claude-sonnet-4-5":{},"anthropic/claude-3.5-sonnet":{},"google/gemini-2.5-pro":{},"deepseek/deepseek-r1":{},"openai/gpt-4o":{}}},"mistral":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:MISTRAL_API_KEY}"},"models":{"mistral/mistral-large":{}}},"minimax":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:MINIMAX_API_KEY}"},"models":{"minimax/abab6.5s":{}}},"poe":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:POE_API_KEY}"},"models":{"poe/claude-3-opus":{}}}}'
volumes:
- /opt/PaperClip-Data/paperclip:/paperclip
- /opt/PaperClip-Data/paperclip-hermes-venv:/opt/hermes-venv
- /opt/PaperClip-Data/paperclip-hermes-data:/home/node/.hermes
depends_on:
paperclip-postgres:
condition: service_healthy
+12 -1
View File
@@ -74,7 +74,18 @@ else
fi
###############################################################################
# 4. Diagnose-Logs
# 4. Hermes Agent Setup (falls Hermes-Service vorhanden)
###############################################################################
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
HERMES_SETUP="${SCRIPT_DIR}/../hermes/setup_hermes.sh"
if [[ -f "${HERMES_SETUP}" ]]; then
log "Führe Hermes-Setup für PaperClip aus..."
chmod +x "${HERMES_SETUP}"
bash "${HERMES_SETUP}" || warn "Hermes-Setup hatte Warnungen (nicht fatal)."
fi
###############################################################################
# 5. Diagnose-Logs
###############################################################################
log "========================================================="
log " DEBUG DIAGNOSTICS:"