Add Hermes WebUI and Athena operator skill
This commit is contained in:
@@ -819,6 +819,71 @@ services:
|
|||||||
retries: 20
|
retries: 20
|
||||||
start_period: 45s
|
start_period: 45s
|
||||||
|
|
||||||
|
# Optional, fully removable community chat surface. Chat execution goes
|
||||||
|
# through the existing Hermes gateway. Upstream's container entrypoint
|
||||||
|
# requires a writable Hermes home for its ownership/init checks; UI-only
|
||||||
|
# state still remains on a separate bind mount for easy removal.
|
||||||
|
hermes-webui:
|
||||||
|
image: ${HERMES_WEBUI_IMAGE:-mike-ai/hermes-webui:0.52.113-hermes-source-v1}
|
||||||
|
container_name: mike-ai-hermes-webui
|
||||||
|
restart: unless-stopped
|
||||||
|
profiles: [hermes-webui]
|
||||||
|
env_file:
|
||||||
|
- /data/hermes-webui/.env
|
||||||
|
volumes:
|
||||||
|
- /data/hermes:/home/hermeswebui/.hermes
|
||||||
|
- /data/hermes-webui/state:/state
|
||||||
|
- /data/hermes-webui/hermes-agent:/home/hermeswebui/.hermes/hermes-agent:ro
|
||||||
|
- /data/hermes/workspace:/workspace
|
||||||
|
environment:
|
||||||
|
HERMES_HOME: /home/hermeswebui/.hermes
|
||||||
|
HERMES_WEBUI_STATE_DIR: /state
|
||||||
|
HERMES_WEBUI_HOST: 0.0.0.0
|
||||||
|
HERMES_WEBUI_PORT: "8787"
|
||||||
|
HERMES_WEBUI_CHAT_BACKEND: gateway
|
||||||
|
HERMES_WEBUI_GATEWAY_BASE_URL: http://hermes:8642
|
||||||
|
HERMES_API_URL: http://hermes:8642
|
||||||
|
HERMES_WEBUI_AGENT_DIR: /home/hermeswebui/.hermes/hermes-agent
|
||||||
|
HERMES_WEBUI_GATEWAY_USE_RUNS_API: "true"
|
||||||
|
HERMES_SKIP_CHMOD: "1"
|
||||||
|
WANTED_UID: "10000"
|
||||||
|
WANTED_GID: "10000"
|
||||||
|
networks: [frontend]
|
||||||
|
depends_on:
|
||||||
|
hermes:
|
||||||
|
condition: service_healthy
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
healthcheck:
|
||||||
|
test: [CMD, python, -c, "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8787/health', timeout=3)"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 20
|
||||||
|
start_period: 45s
|
||||||
|
|
||||||
|
# Persistent VPN listener for the optional WebUI. Sharing the existing
|
||||||
|
# WireGuard network namespace avoids recreating the remote-access gateway
|
||||||
|
# merely to add one listener.
|
||||||
|
hermes-webui-vpn-proxy:
|
||||||
|
image: mike-ai/wireguard-gateway:local
|
||||||
|
container_name: mike-ai-hermes-webui-vpn-proxy
|
||||||
|
restart: unless-stopped
|
||||||
|
profiles: [hermes-webui]
|
||||||
|
network_mode: "service:wireguard-gateway"
|
||||||
|
entrypoint: [socat]
|
||||||
|
command:
|
||||||
|
- TCP-LISTEN:8787,bind=192.168.1.212,reuseaddr,fork
|
||||||
|
- TCP:hermes-webui:8787
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp:size=4m,mode=1777
|
||||||
|
cap_drop: [ALL]
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
depends_on:
|
||||||
|
wireguard-gateway:
|
||||||
|
condition: service_healthy
|
||||||
|
hermes-webui:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
frontend:
|
frontend:
|
||||||
internal: false
|
internal: false
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ LLAMA_THREADS_BATCH=6
|
|||||||
OPENWEBUI_IMAGE=mike-ai/openwebui:main-01f4282-tool-final-v3
|
OPENWEBUI_IMAGE=mike-ai/openwebui:main-01f4282-tool-final-v3
|
||||||
OPENWEBUI_ENABLE_SIGNUP=false
|
OPENWEBUI_ENABLE_SIGNUP=false
|
||||||
OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION=false
|
OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION=false
|
||||||
|
# Removable community Hermes chat surface. Set false to keep only the official
|
||||||
|
# Hermes Dashboard/API and native clients.
|
||||||
|
INSTALL_HERMES_WEBUI=true
|
||||||
|
HERMES_WEBUI_IMAGE=mike-ai/hermes-webui:0.52.113-hermes-source-v1
|
||||||
PIPER_TTS_VERSION=1.6.0
|
PIPER_TTS_VERSION=1.6.0
|
||||||
PIPER_VOICE=de_DE-thorsten-high
|
PIPER_VOICE=de_DE-thorsten-high
|
||||||
XTTS_IMAGE=ghcr.io/coqui-ai/xtts-streaming-server:latest-cuda121@sha256:f7fb3b1f9d4bc88af94da1b5959d8002f1e0b003c97557164034eb8a29f01b90
|
XTTS_IMAGE=ghcr.io/coqui-ai/xtts-streaming-server:latest-cuda121@sha256:f7fb3b1f9d4bc88af94da1b5959d8002f1e0b003c97557164034eb8a29f01b90
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
| Qwen-Profile | `platform/profiles/` | vollständig, Modelle ausgenommen | Kern |
|
| Qwen-Profile | `platform/profiles/` | vollständig, Modelle ausgenommen | Kern |
|
||||||
| MCP-Tool-Stack | `platform/mcp/compose.yaml` | vollständig | Kern |
|
| MCP-Tool-Stack | `platform/mcp/compose.yaml` | vollständig | Kern |
|
||||||
| Hermes Agent | NousResearch Hermes Agent 0.20.5, OCI-Digest gepinnt | eigener Clientcontainer, Dashboard/API, persistente Daten unter `/data/hermes` | Kern |
|
| Hermes Agent | NousResearch Hermes Agent 0.20.5, OCI-Digest gepinnt | eigener Clientcontainer, Dashboard/API, persistente Daten unter `/data/hermes` | Kern |
|
||||||
|
| Hermes Community-WebUI | nesquena/hermes-webui 0.52.113, OCI-Digest gepinnt, kleine lokale Kompatibilitätsschicht | optionale mobile Browseroberfläche über Hermes-Gateway; installiert Abhängigkeiten aus Hermes 0.20.5 ohne dessen absichtlich gesperrten Wheel-Build; eigener Zustand unter `/data/hermes-webui` | Optional |
|
||||||
|
| Hermes Athena-Operator-Skill | `platform/hermes/skills/athena-operator/SKILL.md` | knappe, versionierte Arbeitslogik für Plattformwissen, Operator, Rollback, Verifikation, Git und Recovery; wird in alle Hermes-Profile synchronisiert | Kern |
|
||||||
| Websuche | SearXNG + TinySearch/Crawl4AI | intern, ohne veröffentlichten Port | Kern |
|
| Websuche | SearXNG + TinySearch/Crawl4AI | intern, ohne veröffentlichten Port | Kern |
|
||||||
| Allgemeines Web | OpenWebUI native Suche; TinySearch-Upstream-MCP auf VPN-Port 8203 für andere Clients | site-unabhängig; keine neue Implementierung pro Website | Kern |
|
| Allgemeines Web | OpenWebUI native Suche; TinySearch-Upstream-MCP auf VPN-Port 8203 für andere Clients | site-unabhängig; keine neue Implementierung pro Website | Kern |
|
||||||
| Frühere Web-MCP-Fassade | `platform/web-search/web_search_mcp.py` | nur Rollback-Profil `legacy-web` | Altbestand |
|
| Frühere Web-MCP-Fassade | `platform/web-search/web_search_mcp.py` | nur Rollback-Profil `legacy-web` | Altbestand |
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ Der Router übernimmt:
|
|||||||
- Standardmodell: `qwen-medium`, 160.000 Kontext, über den Profile Router
|
- Standardmodell: `qwen-medium`, 160.000 Kontext, über den Profile Router
|
||||||
- Dashboard: WireGuard-Port 9119 mit Basic-Auth
|
- Dashboard: WireGuard-Port 9119 mit Basic-Auth
|
||||||
- Agent-API: WireGuard-Port 8642 mit eigenem Bearer-Key
|
- Agent-API: WireGuard-Port 8642 mit eigenem Bearer-Key
|
||||||
|
- Profile: Fast 76,8K, Medium 160K, Large 192K, Ultra 262K und Uncensored
|
||||||
|
80K; alle verwenden dieselben MCPs, Skills, Sprach- und Sicherheitsvorgaben
|
||||||
|
- Der verwaltete Skill `athena-operator` wird aus dem Repository in das
|
||||||
|
Standardprofil und alle fünf benannten Profile synchronisiert. Er enthält
|
||||||
|
nur die verbindliche Arbeitslogik; Architektur und Ist-Zustand werden
|
||||||
|
bedarfsgerecht aus Platform-Context- und Operator-MCP gelesen.
|
||||||
- persistenter Zustand: `/data/hermes`
|
- persistenter Zustand: `/data/hermes`
|
||||||
- lokales Terminal: ausschließlich `/data/hermes/workspace` im Container
|
- lokales Terminal: ausschließlich `/data/hermes/workspace` im Container
|
||||||
- MCPs: Athena-Plattform, Athena-Operator, allgemeines Web, GitHub, Home
|
- MCPs: Athena-Plattform, Athena-Operator, allgemeines Web, GitHub, Home
|
||||||
@@ -109,6 +115,18 @@ Der Router übernimmt:
|
|||||||
Universitätsadresse
|
Universitätsadresse
|
||||||
- Start verweigert, wenn die verwaltete Konfiguration nicht lesbar ist oder
|
- Start verweigert, wenn die verwaltete Konfiguration nicht lesbar ist oder
|
||||||
nicht ausdrücklich `custom` und den lokalen Router als Provider nennt
|
nicht ausdrücklich `custom` und den lokalen Router als Provider nennt
|
||||||
|
- Optionale Community-WebUI 0.52.113 auf WireGuard-Port 8787: eigener
|
||||||
|
Container und eigener Zustand unter `/data/hermes-webui`; Chats laufen über
|
||||||
|
die vorhandene Hermes-Gateway-API. Der vom Upstream-Entrypoint benötigte
|
||||||
|
gemeinsame Hermes-Home-Mount ist beschreibbar; UI-eigener Zustand bleibt
|
||||||
|
davon getrennt. Änderungen in WebUI-Einstellungen wirken daher bewusst auf
|
||||||
|
die zentrale Hermes-Konfiguration. Eine schreibgeschützte Kopie des exakt
|
||||||
|
gepinnten Hermes-Agent-Codes liegt unter `/data/hermes-webui/hermes-agent`,
|
||||||
|
damit Modell-, Skill- und Sitzungsfunktionen nicht im reduzierten Modus
|
||||||
|
laufen; der Installer erneuert sie nur bei geändertem Hermes-Image. Der
|
||||||
|
kleine Container `mike-ai-hermes-webui-vpn-proxy` teilt ausschließlich den
|
||||||
|
Netzwerk-Namespace des WireGuard-Gateways und hält Port 8787 rebootfest,
|
||||||
|
ohne das Gateway für Installation oder Entfernung neu zu erstellen.
|
||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
|
|||||||
- [ ] Athena Operator und rootseitiger Executor gesund; Lesen und Preview
|
- [ ] Athena Operator und rootseitiger Executor gesund; Lesen und Preview
|
||||||
erfolgreich; falsches/abgelaufenes Ticket, Pfadausbruch, WireGuard-Stopp,
|
erfolgreich; falsches/abgelaufenes Ticket, Pfadausbruch, WireGuard-Stopp,
|
||||||
freie Befehle, SSH, Reboot und Shutdown in Negativtests verweigert
|
freie Befehle, SSH, Reboot und Shutdown in Negativtests verweigert
|
||||||
|
- [ ] Hermes-Profile Fast, Medium, Large, Ultra und Uncensored vorhanden;
|
||||||
|
`athena-operator` liegt im Standardprofil und in allen fünf Profilen
|
||||||
|
- [ ] falls `INSTALL_HERMES_WEBUI=true`: Community-WebUI auf VPN-Port 8787
|
||||||
|
gesund, Chat-Backend ist das bestehende Hermes-Gateway und weder Hermes
|
||||||
|
noch das aktive Qwen-Profil wurde dafür neu gestartet
|
||||||
- [ ] lokales Dokumentations-Overlay ist auch im privaten Git enthalten und
|
- [ ] lokales Dokumentations-Overlay ist auch im privaten Git enthalten und
|
||||||
der Recovery-Koffer wurde danach neu erzeugt
|
der Recovery-Koffer wurde danach neu erzeugt
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Aktuelle VPN-Adresse: `192.168.1.212`
|
|||||||
| 8092 | XTTS direkt | `http://192.168.1.212:8092` |
|
| 8092 | XTTS direkt | `http://192.168.1.212:8092` |
|
||||||
| 9119 | Hermes Dashboard | `http://192.168.1.212:9119` |
|
| 9119 | Hermes Dashboard | `http://192.168.1.212:9119` |
|
||||||
| 8642 | Hermes Agent API | `http://192.168.1.212:8642` |
|
| 8642 | Hermes Agent API | `http://192.168.1.212:8642` |
|
||||||
|
| 8787 | optionale Hermes Community-WebUI | `http://192.168.1.212:8787` |
|
||||||
| 8201 | Athena Platform Context MCP | `http://192.168.1.212:8201/mcp` |
|
| 8201 | Athena Platform Context MCP | `http://192.168.1.212:8201/mcp` |
|
||||||
| 8202 | Athena Operator MCP einschließlich Terminal | `http://192.168.1.212:8202/mcp` |
|
| 8202 | Athena Operator MCP einschließlich Terminal | `http://192.168.1.212:8202/mcp` |
|
||||||
| 8203 | Allgemeiner TinySearch-MCP | `http://192.168.1.212:8203/mcp` |
|
| 8203 | Allgemeiner TinySearch-MCP | `http://192.168.1.212:8203/mcp` |
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ OPENWEBUI_IMAGE=${OPENWEBUI_IMAGE:-mike-ai/openwebui:main-01f4282-tool-final-v3}
|
|||||||
OPENWEBUI_ENABLE_SIGNUP=${OPENWEBUI_ENABLE_SIGNUP:-false}
|
OPENWEBUI_ENABLE_SIGNUP=${OPENWEBUI_ENABLE_SIGNUP:-false}
|
||||||
OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION=${OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION:-false}
|
OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION=${OPENWEBUI_ENABLE_FOLLOW_UP_GENERATION:-false}
|
||||||
HERMES_IMAGE=${HERMES_IMAGE:-nousresearch/hermes-agent@sha256:143bdb9086bb2db645346179f11091e621ef6b7f4f9e5049ae7454bfeb3a0495}
|
HERMES_IMAGE=${HERMES_IMAGE:-nousresearch/hermes-agent@sha256:143bdb9086bb2db645346179f11091e621ef6b7f4f9e5049ae7454bfeb3a0495}
|
||||||
|
HERMES_WEBUI_IMAGE=${HERMES_WEBUI_IMAGE:-mike-ai/hermes-webui:0.52.113-hermes-source-v1}
|
||||||
PIPER_TTS_VERSION=${PIPER_TTS_VERSION:-1.6.0}
|
PIPER_TTS_VERSION=${PIPER_TTS_VERSION:-1.6.0}
|
||||||
PIPER_VOICE=${PIPER_VOICE:-de_DE-thorsten-high}
|
PIPER_VOICE=${PIPER_VOICE:-de_DE-thorsten-high}
|
||||||
XTTS_IMAGE=${XTTS_IMAGE:-ghcr.io/coqui-ai/xtts-streaming-server:latest-cuda121@sha256:f7fb3b1f9d4bc88af94da1b5959d8002f1e0b003c97557164034eb8a29f01b90}
|
XTTS_IMAGE=${XTTS_IMAGE:-ghcr.io/coqui-ai/xtts-streaming-server:latest-cuda121@sha256:f7fb3b1f9d4bc88af94da1b5959d8002f1e0b003c97557164034eb8a29f01b90}
|
||||||
@@ -531,6 +532,14 @@ PY
|
|||||||
printf '%s\n' "$activation_output"
|
printf '%s\n' "$activation_output"
|
||||||
grep -Fxq 'INSTALL_READINESS_OK' <<<"$activation_output" || \
|
grep -Fxq 'INSTALL_READINESS_OK' <<<"$activation_output" || \
|
||||||
die "Medium-Standardprofil lieferte keinen bestätigten Readiness-Marker"
|
die "Medium-Standardprofil lieferte keinen bestätigten Readiness-Marker"
|
||||||
|
|
||||||
|
log "Hermes-Profile aus der Standardmatrix anlegen"
|
||||||
|
"$STACK_DIR/platform/hermes/install-profiles.sh"
|
||||||
|
|
||||||
|
if [[ ${INSTALL_HERMES_WEBUI:-true} == true ]]; then
|
||||||
|
log "Entfernbare Hermes Community-WebUI installieren"
|
||||||
|
"$STACK_DIR/platform/hermes/install-webui.sh"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
hostnamectl set-hostname "$AI_HOSTNAME"
|
hostnamectl set-hostname "$AI_HOSTNAME"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FROM ghcr.io/nesquena/hermes-webui@sha256:48ba6ee4a837079955c00e997b751065cc0324ae6eaa0f2fec592c8f4b2a746e
|
||||||
|
|
||||||
|
COPY hermes_requirements.py /usr/local/bin/hermes_requirements.py
|
||||||
|
COPY patch_entrypoint.py /tmp/patch_entrypoint.py
|
||||||
|
|
||||||
|
RUN python3 /tmp/patch_entrypoint.py /hermeswebui_init.bash \
|
||||||
|
&& python3 /tmp/patch_entrypoint.py /apptoo/docker_init.bash \
|
||||||
|
&& rm /tmp/patch_entrypoint.py \
|
||||||
|
&& chmod 0555 /usr/local/bin/hermes_requirements.py
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Export Hermes dependencies without attempting to build Hermes itself."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
document = tomllib.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||||
|
project = document["project"]
|
||||||
|
optional = project.get("optional-dependencies", {})
|
||||||
|
pending = list(project.get("dependencies", [])) + list(optional.get("all", []))
|
||||||
|
emitted: set[str] = set()
|
||||||
|
|
||||||
|
while pending:
|
||||||
|
requirement = pending.pop(0)
|
||||||
|
match = re.fullmatch(r"hermes-agent(?:\[([^]]+)\])?", requirement.strip())
|
||||||
|
if match:
|
||||||
|
extras = [item.strip() for item in (match.group(1) or "").split(",") if item.strip()]
|
||||||
|
for extra in extras:
|
||||||
|
if extra not in optional:
|
||||||
|
raise SystemExit(f"Unknown Hermes extra: {extra}")
|
||||||
|
pending[:0] = optional[extra]
|
||||||
|
continue
|
||||||
|
if requirement not in emitted:
|
||||||
|
print(requirement)
|
||||||
|
emitted.add(requirement)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Patch WebUI dependency bootstrap for modern non-wheel Hermes releases."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
path = Path(sys.argv[1])
|
||||||
|
content = path.read_text(encoding="utf-8")
|
||||||
|
old = ''' uv pip install "$_stage_src[all]" --trusted-host pypi.org --trusted-host files.pythonhosted.org \\
|
||||||
|
|| error_exit "Failed to install hermes-agent's requirements"'''
|
||||||
|
new = ''' python3 /usr/local/bin/hermes_requirements.py "$_stage_src/pyproject.toml" \\
|
||||||
|
> /tmp/hermes-agent-requirements.txt \\
|
||||||
|
|| error_exit "Failed to export hermes-agent's requirements"
|
||||||
|
uv pip install -r /tmp/hermes-agent-requirements.txt \\
|
||||||
|
--trusted-host pypi.org --trusted-host files.pythonhosted.org \\
|
||||||
|
|| error_exit "Failed to install hermes-agent's requirements"'''
|
||||||
|
if content.count(old) != 1:
|
||||||
|
raise SystemExit(f"Expected dependency bootstrap exactly once in {path}")
|
||||||
|
path.write_text(content.replace(old, new), encoding="utf-8")
|
||||||
@@ -59,4 +59,5 @@ fi
|
|||||||
install -m 0600 "$STACK_DIR/platform/hermes/config.yaml" "$HERMES_DATA_DIR/config.yaml"
|
install -m 0600 "$STACK_DIR/platform/hermes/config.yaml" "$HERMES_DATA_DIR/config.yaml"
|
||||||
install -m 0600 "$STACK_DIR/platform/hermes/SOUL.md" "$HERMES_DATA_DIR/SOUL.md"
|
install -m 0600 "$STACK_DIR/platform/hermes/SOUL.md" "$HERMES_DATA_DIR/SOUL.md"
|
||||||
chown -R 10000:10000 "$HERMES_DATA_DIR"
|
chown -R 10000:10000 "$HERMES_DATA_DIR"
|
||||||
|
"$STACK_DIR/platform/hermes/install-skills.sh"
|
||||||
printf 'HERMES_CONFIG_OK %s\n' "$HERMES_DATA_DIR"
|
printf 'HERMES_CONFIG_OK %s\n' "$HERMES_DATA_DIR"
|
||||||
|
|||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
HERMES_CONTAINER=${HERMES_CONTAINER:-mike-ai-hermes}
|
||||||
|
|
||||||
|
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||||
|
docker inspect "$HERMES_CONTAINER" >/dev/null 2>&1 || \
|
||||||
|
die "Hermes-Container fehlt: $HERMES_CONTAINER"
|
||||||
|
|
||||||
|
deadline=$((SECONDS + 180))
|
||||||
|
until [[ $(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \
|
||||||
|
"$HERMES_CONTAINER" 2>/dev/null || true) == healthy ]]; do
|
||||||
|
(( SECONDS < deadline )) || die "Hermes wurde nicht rechtzeitig gesund."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
create_profile() {
|
||||||
|
local name=$1 model=$2 context=$3 description=$4
|
||||||
|
if ! docker exec "$HERMES_CONTAINER" hermes profile show "$name" >/dev/null 2>&1; then
|
||||||
|
docker exec "$HERMES_CONTAINER" hermes profile create "$name" \
|
||||||
|
--clone-from default --description "$description"
|
||||||
|
fi
|
||||||
|
docker exec "$HERMES_CONTAINER" hermes -p "$name" config set model.default "$model"
|
||||||
|
docker exec "$HERMES_CONTAINER" hermes -p "$name" config set model.context_length "$context"
|
||||||
|
[[ $(docker exec "$HERMES_CONTAINER" hermes -p "$name" config get model.default) == "$model" ]] || \
|
||||||
|
die "Modellalias von Profil $name konnte nicht verifiziert werden."
|
||||||
|
[[ $(docker exec "$HERMES_CONTAINER" hermes -p "$name" config get model.context_length) == "$context" ]] || \
|
||||||
|
die "Kontext von Profil $name konnte nicht verifiziert werden."
|
||||||
|
}
|
||||||
|
|
||||||
|
create_profile fast qwen-fast 76800 \
|
||||||
|
"Schnelles Qwen3.8-27B-Profil mit 76,8K Kontext fuer kurze Chats und schnelle Aufgaben."
|
||||||
|
create_profile medium qwen-medium 160000 \
|
||||||
|
"Ausgewogenes Qwen3.8-27B-Standardprofil mit 160K Kontext fuer Alltag und agentische Aufgaben."
|
||||||
|
create_profile large qwen-large 192000 \
|
||||||
|
"Grosses Qwen3.8-27B-Profil mit 192K Kontext fuer umfangreiche Dokumente und lange Aufgaben."
|
||||||
|
create_profile ultra qwen-ultra 262144 \
|
||||||
|
"Maximales Qwen3.8-27B-Profil mit 262K Kontext fuer sehr grosse Kontexte; langsamer als die Standardprofile."
|
||||||
|
create_profile uncensored qwen-uncensored 80000 \
|
||||||
|
"Unzensiertes Qwen3.8-27B-Profil mit 80K Kontext fuer spezielle Anfragen."
|
||||||
|
|
||||||
|
"${STACK_DIR:-/opt/mike-ai/stack}/platform/hermes/install-skills.sh"
|
||||||
|
docker exec "$HERMES_CONTAINER" hermes profile list
|
||||||
|
printf 'HERMES_PROFILES_OK\n'
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
STACK_DIR=${STACK_DIR:-/opt/mike-ai/stack}
|
||||||
|
HERMES_DATA_DIR=${HERMES_DATA_DIR:-/data/hermes}
|
||||||
|
SKILL_SOURCE=$STACK_DIR/platform/hermes/skills/athena-operator/SKILL.md
|
||||||
|
PROFILES=(fast medium large ultra uncensored)
|
||||||
|
|
||||||
|
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||||
|
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||||
|
[[ -s $SKILL_SOURCE ]] || die "Skill-Quelle fehlt: $SKILL_SOURCE"
|
||||||
|
grep -Fxq -- 'name: athena-operator' "$SKILL_SOURCE" || \
|
||||||
|
die "Skill-Quelle hat kein gültiges Athena-Operator-Frontmatter."
|
||||||
|
|
||||||
|
install_skill() {
|
||||||
|
local root=$1 target=$1/platform/athena-operator/SKILL.md
|
||||||
|
install -d -o 10000 -g 10000 -m 0750 "${target%/*}"
|
||||||
|
if [[ -s $target ]] && ! cmp -s "$SKILL_SOURCE" "$target"; then
|
||||||
|
cp -a "$target" "$target.before-managed-update-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
fi
|
||||||
|
install -o 10000 -g 10000 -m 0640 "$SKILL_SOURCE" "$target"
|
||||||
|
cmp -s "$SKILL_SOURCE" "$target" || die "Skill-Synchronisierung fehlgeschlagen: $target"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_skill "$HERMES_DATA_DIR/skills"
|
||||||
|
for profile in "${PROFILES[@]}"; do
|
||||||
|
[[ -d $HERMES_DATA_DIR/profiles/$profile ]] || continue
|
||||||
|
install_skill "$HERMES_DATA_DIR/profiles/$profile/skills"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf 'HERMES_ATHENA_OPERATOR_SKILL_OK\n'
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
STACK_DIR=${STACK_DIR:-/opt/mike-ai/stack}
|
||||||
|
DATA_DIR=${HERMES_WEBUI_DATA_DIR:-/data/hermes-webui}
|
||||||
|
HERMES_ENV=${HERMES_ENV:-/data/hermes/.env}
|
||||||
|
SECRETS_DIR=${SECRETS_DIR:-/etc/mike-ai}
|
||||||
|
STACK_ENV=$SECRETS_DIR/stack.env
|
||||||
|
WEBUI_IMAGE_DEFAULT=mike-ai/hermes-webui:0.52.113-hermes-source-v1
|
||||||
|
|
||||||
|
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||||
|
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||||
|
[[ -s $HERMES_ENV ]] || die "Hermes-Umgebung fehlt: $HERMES_ENV"
|
||||||
|
[[ -s $STACK_ENV ]] || die "Stack-Umgebung fehlt: $STACK_ENV"
|
||||||
|
[[ $(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \
|
||||||
|
mike-ai-hermes 2>/dev/null || true) == healthy ]] || \
|
||||||
|
die "Hermes-Gateway ist nicht gesund; WebUI wird nicht gestartet."
|
||||||
|
[[ $(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \
|
||||||
|
mike-ai-wireguard-gateway 2>/dev/null || true) == healthy ]] || \
|
||||||
|
die "WireGuard-Gateway ist nicht gesund; WebUI-VPN-Proxy wird nicht gestartet."
|
||||||
|
|
||||||
|
read_env() {
|
||||||
|
local key=$1
|
||||||
|
sed -n "s/^${key}=//p" "$HERMES_ENV" | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
gateway_key=$(read_env API_SERVER_KEY)
|
||||||
|
router_key=$(read_env ROUTER_API_KEY)
|
||||||
|
[[ ${#gateway_key} -ge 16 ]] || die "Hermes-Gateway-Key fehlt oder ist zu kurz."
|
||||||
|
[[ -n $router_key ]] || die "Router-Key fehlt in der Hermes-Umgebung."
|
||||||
|
|
||||||
|
install -d -m 0700 "$DATA_DIR" "$DATA_DIR/state"
|
||||||
|
|
||||||
|
hermes_image=$(sed -n 's/^HERMES_IMAGE=//p' "$STACK_ENV" | head -n 1)
|
||||||
|
if [[ -z $hermes_image ]]; then
|
||||||
|
hermes_image=$(docker inspect --format '{{.Config.Image}}' mike-ai-hermes 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
[[ -n $hermes_image ]] || die "Hermes-Image fehlt in $STACK_ENV und im laufenden Container."
|
||||||
|
image_id=$(docker image inspect --format '{{.Id}}' "$hermes_image" 2>/dev/null) || \
|
||||||
|
die "Hermes-Image ist lokal nicht verfügbar: $hermes_image"
|
||||||
|
webui_image=$(sed -n 's/^HERMES_WEBUI_IMAGE=//p' "$STACK_ENV" | head -n 1)
|
||||||
|
webui_image=${webui_image:-$WEBUI_IMAGE_DEFAULT}
|
||||||
|
docker build --pull=false --tag "$webui_image" "$STACK_DIR/platform/docker/hermes-webui"
|
||||||
|
|
||||||
|
agent_dir=$DATA_DIR/hermes-agent
|
||||||
|
agent_marker=$DATA_DIR/hermes-agent-image-id
|
||||||
|
if [[ ! -s $agent_marker || $(<"$agent_marker") != "$image_id" || ! -s $agent_dir/cli.py ]]; then
|
||||||
|
source_tmp=$(mktemp -d "$DATA_DIR/hermes-agent.new.XXXXXX")
|
||||||
|
source_container=$(docker create "$hermes_image")
|
||||||
|
cleanup_source_container() {
|
||||||
|
[[ -z ${source_container:-} ]] || docker rm -f "$source_container" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup_source_container EXIT
|
||||||
|
docker cp "$source_container:/opt/hermes/." "$source_tmp/"
|
||||||
|
docker rm "$source_container" >/dev/null
|
||||||
|
source_container=
|
||||||
|
chown -R 10000:10000 "$source_tmp"
|
||||||
|
if [[ -e $agent_dir ]]; then
|
||||||
|
mv "$agent_dir" "$DATA_DIR/hermes-agent.previous-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
fi
|
||||||
|
mv "$source_tmp" "$agent_dir"
|
||||||
|
printf '%s\n' "$image_id" >"$agent_marker"
|
||||||
|
chmod 0600 "$agent_marker"
|
||||||
|
trap - EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -s $SECRETS_DIR/hermes-webui-password ]] || \
|
||||||
|
openssl rand -base64 24 >"$SECRETS_DIR/hermes-webui-password"
|
||||||
|
chmod 0600 "$SECRETS_DIR/hermes-webui-password"
|
||||||
|
|
||||||
|
cat >"$DATA_DIR/.env" <<EOF
|
||||||
|
HERMES_WEBUI_PASSWORD=$(<"$SECRETS_DIR/hermes-webui-password")
|
||||||
|
HERMES_WEBUI_GATEWAY_API_KEY=$gateway_key
|
||||||
|
ROUTER_API_KEY=$router_key
|
||||||
|
EOF
|
||||||
|
chmod 0600 "$DATA_DIR/.env"
|
||||||
|
chown 10000:10000 "$DATA_DIR" "$DATA_DIR/.env" "$agent_marker"
|
||||||
|
chown -R 10000:10000 "$DATA_DIR/state"
|
||||||
|
|
||||||
|
cd "$STACK_DIR"
|
||||||
|
HERMES_WEBUI_IMAGE=$webui_image docker compose --env-file "$STACK_ENV" --profile hermes-webui \
|
||||||
|
up -d --no-deps hermes-webui hermes-webui-vpn-proxy
|
||||||
|
printf 'HERMES_WEBUI_OK http://192.168.1.212:8787\n'
|
||||||
|
printf 'Passwort lokal: %s\n' "$SECRETS_DIR/hermes-webui-password"
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
name: athena-operator
|
||||||
|
description: Operate and extend the Athena AI platform safely.
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
version: 0.1.0
|
||||||
|
author: Michael Roll, Hermes Agent
|
||||||
|
platforms: [linux, macos, windows]
|
||||||
|
tags: [athena, operations, docker, mcp, models, recovery]
|
||||||
|
related_skills: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# Athena Operator Skill
|
||||||
|
|
||||||
|
Operate, diagnose, extend, and recover the Athena AI platform through its
|
||||||
|
platform-context and operator MCPs. Keep durable truth in the versioned Athena
|
||||||
|
repository and use live measurements only as evidence of current state.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Use for Athena, MikeAI, Docker-stack, router, inference-profile, model,
|
||||||
|
benchmark, MCP, Hermes, Open WebUI, TTS, STT, image, Git-deploy, and recovery
|
||||||
|
work on the Athena host.
|
||||||
|
- Use when a user asks how Athena is built or whether an Athena component is
|
||||||
|
running, configured, documented, reproducible, or recoverable.
|
||||||
|
- Do not use as the primary tool for Home Assistant, Unraid, Sonarr, Radarr,
|
||||||
|
Navidrome, or GitHub data when their specialist MCP is available.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Require `Athena Plattformwissen` for architecture and versioned knowledge.
|
||||||
|
- Require `Athena Operator` for live host inspection and execution.
|
||||||
|
- Treat missing or failed tools as missing evidence. Never invent state,
|
||||||
|
output, files, logs, or completed actions.
|
||||||
|
- Never request, reveal, copy into chat, or commit secret values.
|
||||||
|
|
||||||
|
## Tool Selection
|
||||||
|
|
||||||
|
1. Identify the target system before calling a tool.
|
||||||
|
2. For Athena itself, begin with `athena_operator_inspect`. Use
|
||||||
|
`athena_get_overview` when architectural context is needed.
|
||||||
|
3. For external services, prefer the narrow specialist MCP. Use
|
||||||
|
`athena_get_external_services` before planning a duplicate service.
|
||||||
|
4. Use `athena_operator_search_source` and `athena_operator_read_source` for
|
||||||
|
deployed code. Use `athena_search_knowledge` and `athena_read_source` for
|
||||||
|
documentation. Do not guess paths or configuration.
|
||||||
|
5. Use `athena_operator_terminal` as the broad Athena escape hatch only when a
|
||||||
|
structured tool is too narrow. Keep commands focused and outputs bounded.
|
||||||
|
|
||||||
|
## Procedure
|
||||||
|
|
||||||
|
1. **Establish evidence.** Inspect only the relevant live subject and read the
|
||||||
|
smallest authoritative source section. Completion: runtime facts and source
|
||||||
|
facts are separately identified.
|
||||||
|
2. **Check drift.** Compare live state with versioned source and current
|
||||||
|
reference documentation. Completion: any mismatch is named before changes.
|
||||||
|
3. **Protect the active workload.** Check jobs and active containers. Do not
|
||||||
|
restart, recreate, switch profiles, alter shared configuration, or consume
|
||||||
|
required GPU capacity while an important request or benchmark is active.
|
||||||
|
Completion: work is either proven idle or the change is staged only.
|
||||||
|
4. **Plan rollback.** Name the files, services, validation, rollback artifact,
|
||||||
|
and expected user-visible effect. Completion: rollback is possible without
|
||||||
|
relying on chat history.
|
||||||
|
5. **Change the source of truth.** Modify repository sources, not only a live
|
||||||
|
container. Prefer `athena_operator_prepare`; show its full preview and stop
|
||||||
|
for the exact user confirmation before `athena_operator_execute`.
|
||||||
|
Completion: the approved ticket matches the intended content.
|
||||||
|
6. **Deploy narrowly.** Change only named services. Never restart the entire
|
||||||
|
stack merely to activate one component. Completion: unrelated containers
|
||||||
|
and the active inference request remain undisturbed.
|
||||||
|
7. **Verify behavior.** Run syntax/config checks, focused tests, service health,
|
||||||
|
and one bounded functional test. A running container alone is not proof.
|
||||||
|
Completion: expected behavior and rollback path are both verified.
|
||||||
|
8. **Close the maintenance loop.** Update relevant docs, publish the Git
|
||||||
|
change, create a newer recovery bundle, then check maintenance status.
|
||||||
|
Completion: source commit, deployed state, docs, and recovery agree.
|
||||||
|
|
||||||
|
## Persistent Versus Temporary Work
|
||||||
|
|
||||||
|
- "Use" a missing helper for one task: place it in a task-specific temporary
|
||||||
|
location or ephemeral container and remove it afterward.
|
||||||
|
- "Install", "add", "deploy", or "make permanent": implement it in repository
|
||||||
|
source, documentation, installation flow, and recovery.
|
||||||
|
- Do not create a second backend merely because an existing service is stopped,
|
||||||
|
inaccessible, or absent from one tool catalogue.
|
||||||
|
|
||||||
|
## Remote-Safety Boundary
|
||||||
|
|
||||||
|
- Athena has no physical console or KVM. Never attempt power control or changes
|
||||||
|
to Athena SSH, LAN, WireGuard, firewall, boot, kernel, drivers, mounts, or
|
||||||
|
partitions through this workflow.
|
||||||
|
- Do not stop or restart the WireGuard gateway as a side effect of ordinary
|
||||||
|
deployment. Bind user services to the VPN path; keep them unavailable from
|
||||||
|
the university LAN.
|
||||||
|
- Inside the trusted VPN, normal service communication and Internet access are
|
||||||
|
allowed. Do not add extra egress restrictions unless the user requests them.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Profile names are not simultaneous models; exactly one text profile is active.
|
||||||
|
- A profile switch can terminate active generation and invalidate prompt cache.
|
||||||
|
- A healthy container can still expose the wrong model, route, or tool set.
|
||||||
|
- `/opt/mike-ai/stack` is deployed source, not automatically the canonical Git
|
||||||
|
worktree. Complete durable changes through the documented Git workflow.
|
||||||
|
- New skills are loaded at the next Hermes session; absence in the current
|
||||||
|
session is expected.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- State the tools that supplied each important live claim.
|
||||||
|
- List every modified source file and every deployed service.
|
||||||
|
- Report focused test and health results, not vague success language.
|
||||||
|
- If Git publication or recovery creation is incomplete, call it unfinished
|
||||||
|
maintenance rather than declaring the task fully complete.
|
||||||
Reference in New Issue
Block a user