Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,33 @@ jobs:

- name: Test Docker image
run: |
CONTAINER_NAME="hermes-webtop-test"

# Start container with shm-size (required by webtop) and all service ports mapped
docker run -d --name "$CONTAINER_NAME" \
--shm-size=1gb \
-p 3000:3000 \
-p 8888:8888 \
-p 7352:7352 \
-p 20128:20128 \
-p 9119:9119 \
hermes-webtop:test

# Wait for Hermes Dashboard to be ready (init scripts run in background)
echo "=== Waiting for Hermes Dashboard to become healthy ==="
CONTAINER_NAME="hermes-webtop"

# Start container like how it is used - DevProd Parity
NO_LOGS=1 DOCKER_URI=hermes-webtop:test make start

# Wait for init scripts to finish (port 9119 responding = start-hermes.sh done)
echo "=== Waiting for Hermes to become healthy ==="
for i in $(seq 1 30); do
if docker exec "$CONTAINER_NAME" curl -s -o /dev/null -w "%{http_code}" http://localhost:9119 2>/dev/null | grep -q "200\|302\|401"; then
echo "Dashboard ready after $((i * 5)) seconds."
echo "Hermes ready after $((i * 5)) seconds."
break
fi
if [ "$i" -eq 30 ]; then
echo "WARNING: Dashboard did not respond within 150 seconds."
echo "WARNING: Hermes did not respond within 150 seconds."
fi
sleep 5
done

# Run self-check (it has its own 120s polling loop for all ports)
echo "=== Running boot-time self-check validation ==="
# Execute the self-check script inside the container.
# We check the exit status. In headless CI, some warnings might be present,
# but critical errors (exit code 2) must trigger build failure.

# Disable exit-on-error and capture the exit status of the self-check script
set +e
docker exec "$CONTAINER_NAME" self-check
docker exec -u abc "$CONTAINER_NAME" self-check
SELF_CHECK_STATUS=$?
set -e

Expand All @@ -97,6 +91,9 @@ jobs:
if [ "${SELF_CHECK_STATUS}" -eq 2 ]; then
echo "FAIL: Docker image smoke test failed; Collecting logs."
mkdir -p /tmp/failure-logs

# Capture docker-compose startup logs without color codes
docker compose logs --no-color > /tmp/failure-logs/docker-compose.log

# Service logs (written to /tmp by start scripts)
docker exec "$CONTAINER_NAME" sh -c 'cd /tmp && tar cf - *.log' | tar xf - -C /tmp/failure-logs/
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ start:
PUID=$(shell id -u) \
PGID=$(shell id -g) \
docker compose up -d
docker compose logs -f
if [ -z "$$NO_LOGS" ]; then \
docker compose logs -f; \
fi

stop:
docker compose down
Expand Down
15 changes: 0 additions & 15 deletions docker/01-copy-web-ui-stamp.sh

This file was deleted.

15 changes: 2 additions & 13 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ARG HERMES_VERSION="v2026.8.13"
ARG OMNIROUTE_VERSION=3.8.49
ARG MODELRELAY_VERSION=1.18.0
ARG OLLAMA_VERSION=0.32.6
ARG NODE_VERSION=24.18.0
ARG NODE_VERSION=26.7.0
ARG CODE_SERVER_VERSION=4.131.0
ARG MNEMON_VERSION=0.1.17
ARG TARGETARCH
Expand Down Expand Up @@ -53,22 +53,11 @@ ARG HERMES_VERSION
ENV HERMES_HOME=/config/.hermes
RUN curl -fsSL "https://raw.githubusercontent.com/NousResearch/hermes-agent/${HERMES_VERSION}/scripts/install.sh" | bash -s -- --skip-setup && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /opt/hermes-prebuilt && \
cd /usr/local/lib/hermes-agent && \
CI=1 npm ci --include=dev --workspace web --silent 2>&1 || \
CI=1 npm install --no-save --include=dev --workspace web --silent 2>&1 && \
CI=1 npm run build -w web --silent 2>&1 && \
npm cache clean --force && \
HERMES_HOME=/opt/hermes-prebuilt python3 <<'PYEOF'
import sys
sys.path.insert(0, '/usr/local/lib/hermes-agent')
from hermes_cli.main import _write_web_ui_build_stamp, _workspace_root
from pathlib import Path
web_dir = Path('/usr/local/lib/hermes-agent/web')
project_root = _workspace_root(web_dir)
_write_web_ui_build_stamp(project_root, web_dir)
print('Build-time stamp written to /opt/hermes-prebuilt')
PYEOF
npm cache clean --force

# Install ModelRelay and start automatically in the background
ARG MODELRELAY_VERSION
Expand Down
62 changes: 61 additions & 1 deletion docker/self-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,65 @@ else
echo " (skipped)"
fi

# ── 8. Ollama ───────────────────────────────────────────────────────────────
section "Ollama"

if ! should_skip "ollama"; then
# 1. Binary
if command -v ollama >/dev/null 2>&1; then
_ok "Binary" "$(ollama --version 2>/dev/null || echo 'installed')"
else
_fail "Binary" "ollama not in PATH"
fi

# 2. API responds
OLLAMA_API=$(curl -s --max-time 5 http://localhost:11434/api/tags 2>/dev/null || echo "")
if [ -n "$OLLAMA_API" ]; then
_ok "API" "responding on :11434"
else
_fail "API" "no response from :11434"
fi

# 3. Model listed — retry up to 15s (server may still be scanning models)
MODEL_LISTED=0
for _attempt in 1 2 3 4 5; do
MODEL_LISTED=$(ollama list 2>/dev/null | grep -c "nomic-embed-text" || true)
[ -z "$MODEL_LISTED" ] && MODEL_LISTED=0
[ "$MODEL_LISTED" -gt 0 ] && break
sleep 3
done
if [ "$MODEL_LISTED" -gt 0 ]; then
_ok "Model" "nomic-embed-text available"
else
# Filesystem fallback: check if model files exist on disk (server may be slow to load)
MODEL_MANIFEST="$HOME/.ollama/models/manifests/registry.ollama.ai/library/nomic-embed-text/latest"
if [ -f "$MODEL_MANIFEST" ]; then
_warn "Model" "nomic-embed-text on disk but not listed by server (loading slow)"
else
_warn "Model" "nomic-embed-text not found on disk"
fi
fi

# 4. Embedding generation — retry after model check (model may have loaded during retries above)
EMBED_RESULT=""
for _attempt in 1 2 3; do
EMBED_RESULT=$(curl -s --max-time 30 -X POST http://localhost:11434/api/embed \
-d '{"model":"nomic-embed-text","input":"hello world"}' 2>/dev/null || echo "")
if echo "$EMBED_RESULT" | grep -q '"embeddings"'; then
break
fi
sleep 3
done
if echo "$EMBED_RESULT" | grep -q '"embeddings"'; then
EMBED_DIM=$(echo "$EMBED_RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['embeddings'][0]))" 2>/dev/null || echo "?")
_ok "Embedding" "generated (dim=${EMBED_DIM})"
else
_warn "Embedding" "failed to generate embedding (non-critical)"
fi
else
echo " (skipped)"
fi

# ── Summary ──────────────────────────────────────────────────────────────────
section "Summary"
echo ""
Expand All @@ -335,7 +394,8 @@ fi
echo ""

# ── Write JSON report ────────────────────────────────────────────────────────
cat > "$REPORT_FILE" <<EOF
rm -f "$REPORT_FILE" 2>/dev/null || true
cat > "$REPORT_FILE" <<EOF || true
{
"timestamp": "$TIMESTAMP",
"exit_code": $EXIT_CODE,
Expand Down
3 changes: 2 additions & 1 deletion docker/start-hermes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ runuser -l abc <<'EOF'
if [ -d "$HOME/.hermes/logs" ] && [ -z "$(ls -A "$HOME/.hermes/logs")" ]; then
echo "[start-hermes] No logs found in $HOME/.hermes/logs, setting up default configuration for custom provider"
echo "[start-hermes] Initializing hermes config..."
hermes config set terminal.cwd $HOME
hermes config set model.default auto-fastest
hermes config set model.provider omniroute
hermes config set providers.omniroute.base_url http://localhost:20128/v1
Expand Down Expand Up @@ -86,7 +87,7 @@ runuser -l abc <<'EOF'

# Start Hermes Dashboard in background and expose it on port 9119 via socat
nohup socat TCP4-LISTEN:9119,fork,reuseaddr TCP4:127.0.0.1:9009 > ~/.hermes/logs/socat-9119.log 2>&1 &
nohup hermes dashboard --port 9009 --no-open > ~/.hermes/logs/dashboard.log 2>&1 &
nohup hermes dashboard --port 9009 --no-open --skip-build > ~/.hermes/logs/dashboard.log 2>&1 &

# Remind Hermes on Mnemon setup if needed
if [ ! -f "$HOME/.hermes/memories/USER.md" ]; then
Expand Down
2 changes: 1 addition & 1 deletion docker/start-omniroute.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ if [ "$INIT_OMNIROUTE" -eq "1" ]; then
curl -s -X PUT "http://localhost:20128/api/combos/$COMBO_ID" \
-H "Content-Type: application/json" \
-d '{
"models": ["oc/deepseek-v4-flash-free","mimocode/mimo-auto","oc/big-pickle"],
"models": ["oc/deepseek-v4-flash-free","oc/big-pickle","opencode-zen/deepseek-v4-flash-free","opencode-zen/hy3-free","opencode-zen/mimo-v2.5-free","opencode-zen/north-mini-code-free","opencode-zen/nemotron-3-ultra-free","opencode-zen/big-pickle"],
"strategy": "auto",
"config": {
"maxRetries": 2,
Expand Down