Spaces:
Running
Running
Z User commited on
Commit ·
f0b15e1
1
Parent(s): 7bfa1b4
feat: pin hermes-agent v0.12.0 + runtime auto-update
Browse filesDockerfile:
- Pin hermes-agent to v2026.4.30 (v0.12.0) release tag
- Write /app/hermes-agent.version for runtime tracking
start.sh: update_hermes_agent_background()
- Queries GitHub releases/latest for newest tag
- Date-style version comparison (2026.4.30 format)
- 5-phase safe update: fetch → checkout → pip install → re-patch → reload signal
- Rollback on failure: git checkout old tag + pip reinstall
- Re-applies Hermes Bot patches (prompt_builder, send_message_tool, etc.)
- Signals entry.py via SIGUSR1 for gateway restart
- Version tracked in /data/hermes/agent.version (persistent)
- Set AGENT_AUTO_UPDATE=false to disable
- Dockerfile +4 -2
- start.sh +126 -0
Dockerfile
CHANGED
|
@@ -11,8 +11,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 11 |
|
| 12 |
WORKDIR /app
|
| 13 |
|
| 14 |
-
# Clone hermes-agent
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# Build venv
|
| 18 |
RUN python3 -m venv /app/venv
|
|
|
|
| 11 |
|
| 12 |
WORKDIR /app
|
| 13 |
|
| 14 |
+
# Clone hermes-agent (pinned to v2026.4.30 = v0.12.0)
|
| 15 |
+
# Runtime auto-update will pull newer releases transparently
|
| 16 |
+
RUN git clone --depth 1 --branch v2026.4.30 https://github.com/NousResearch/hermes-agent.git /app/hermes-agent && \
|
| 17 |
+
echo "v2026.4.30" > /app/hermes-agent.version
|
| 18 |
|
| 19 |
# Build venv
|
| 20 |
RUN python3 -m venv /app/venv
|
start.sh
CHANGED
|
@@ -444,6 +444,129 @@ for i in $(seq 1 60); do
|
|
| 444 |
sleep 2
|
| 445 |
done
|
| 446 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
# ── Auto-update hermes-web-ui if newer release exists ──
|
| 448 |
# Runs asynchronously so it doesn't block startup.
|
| 449 |
# Check: GitHub releases/latest → compare with saved version → build if newer.
|
|
@@ -585,6 +708,9 @@ node index.js >> /data/hermes/logs/webui.log 2>&1 &
|
|
| 585 |
WEBUI_PID=$!
|
| 586 |
echo "[$(date)] WebUI BFF PID: $WEBUI_PID"
|
| 587 |
|
|
|
|
|
|
|
|
|
|
| 588 |
# Trigger WebUI auto-update in background (non-blocking)
|
| 589 |
# Will check GitHub, build if newer, and hot-swap
|
| 590 |
update_webui_background &
|
|
|
|
| 444 |
sleep 2
|
| 445 |
done
|
| 446 |
|
| 447 |
+
# ── Auto-update hermes-agent if newer release exists ──
|
| 448 |
+
# hermes-agent is pip install -e (editable), so git pull + pip upgrade = instant.
|
| 449 |
+
# Safety: update runs in background; if pip fails, old code stays intact.
|
| 450 |
+
# Set AGENT_AUTO_UPDATE=false to disable.
|
| 451 |
+
update_hermes_agent_background() {
|
| 452 |
+
[ "${AGENT_AUTO_UPDATE}" = "false" ] && return
|
| 453 |
+
|
| 454 |
+
AGENT_REPO="NousResearch/hermes-agent"
|
| 455 |
+
AGENT_DIR="/app/hermes-agent"
|
| 456 |
+
VERSION_FILE="/data/hermes/agent.version"
|
| 457 |
+
API_URL="https://api.github.com/repos/${AGENT_REPO}/releases/latest"
|
| 458 |
+
EXTRAS="feishu,mcp,cron,pty"
|
| 459 |
+
|
| 460 |
+
# Current version (from Dockerfile build or previous update)
|
| 461 |
+
CURRENT_VERSION="$(cat "$VERSION_FILE" 2>/dev/null | head -1)"
|
| 462 |
+
if [ -z "$CURRENT_VERSION" ]; then
|
| 463 |
+
# Try build-time version file
|
| 464 |
+
CURRENT_VERSION="$(cat /app/hermes-agent.version 2>/dev/null | head -1)"
|
| 465 |
+
[ -z "$CURRENT_VERSION" ] && CURRENT_VERSION="v2026.4.30"
|
| 466 |
+
echo "$CURRENT_VERSION" > "$VERSION_FILE"
|
| 467 |
+
fi
|
| 468 |
+
|
| 469 |
+
# Version comparison helper: strip leading 'v', compare date-style like 2026.4.30
|
| 470 |
+
compare_date_versions() {
|
| 471 |
+
local a="${1#v}" b="${2#v}"
|
| 472 |
+
IFS='.' read -ra A <<< "$a"
|
| 473 |
+
IFS='.' read -ra B <<< "$b"
|
| 474 |
+
for i in 0 1 2; do
|
| 475 |
+
local ai=${A[$i]:-0} bi=${B[$i]:-0}
|
| 476 |
+
if [ "$bi" -gt "$ai" ] 2>/dev/null; then return 0; fi
|
| 477 |
+
if [ "$bi" -lt "$ai" ] 2>/dev/null; then return 1; fi
|
| 478 |
+
done
|
| 479 |
+
return 1 # equal or older
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
echo "[$(date)] Agent auto-update: checking (current: $CURRENT_VERSION)..."
|
| 483 |
+
|
| 484 |
+
# Query GitHub API
|
| 485 |
+
LATEST_JSON=$(curl -sf --connect-timeout 10 --max-time 20 "$API_URL" 2>/dev/null)
|
| 486 |
+
if [ $? -ne 0 ] || [ -z "$LATEST_JSON" ]; then
|
| 487 |
+
echo "[$(date)] Agent auto-update: failed to reach GitHub API, skipping"
|
| 488 |
+
return
|
| 489 |
+
fi
|
| 490 |
+
|
| 491 |
+
LATEST_TAG=$(echo "$LATEST_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name',''))" 2>/dev/null)
|
| 492 |
+
if [ -z "$LATEST_TAG" ]; then
|
| 493 |
+
echo "[$(date)] Agent auto-update: could not parse latest tag, skipping"
|
| 494 |
+
return
|
| 495 |
+
fi
|
| 496 |
+
|
| 497 |
+
echo "[$(date)] Agent auto-update: latest release is $LATEST_TAG"
|
| 498 |
+
|
| 499 |
+
# Compare
|
| 500 |
+
if compare_date_versions "$CURRENT_VERSION" "$LATEST_TAG"; then
|
| 501 |
+
echo "[$(date)] Agent auto-update: upgrading $CURRENT_VERSION → $LATEST_TAG ..."
|
| 502 |
+
else
|
| 503 |
+
echo "[$(date)] Agent auto-update: $CURRENT_VERSION is up to date"
|
| 504 |
+
return
|
| 505 |
+
fi
|
| 506 |
+
|
| 507 |
+
# Phase 1: git fetch + checkout new tag (non-destructive)
|
| 508 |
+
cd "$AGENT_DIR"
|
| 509 |
+
if ! git fetch --tags origin 2>&1 | tail -3; then
|
| 510 |
+
echo "[$(date)] Agent auto-update: git fetch failed, aborting"
|
| 511 |
+
return
|
| 512 |
+
fi
|
| 513 |
+
|
| 514 |
+
# Verify tag exists
|
| 515 |
+
if ! git rev-parse "$LATEST_TAG" >/dev/null 2>&1; then
|
| 516 |
+
echo "[$(date)] Agent auto-update: tag $LATEST_TAG not found, aborting"
|
| 517 |
+
return
|
| 518 |
+
fi
|
| 519 |
+
|
| 520 |
+
# Phase 2: checkout new version
|
| 521 |
+
if ! git checkout "$LATEST_TAG" 2>&1 | tail -3; then
|
| 522 |
+
echo "[$(date)] Agent auto-update: git checkout failed, aborting"
|
| 523 |
+
# Try to recover to previous version
|
| 524 |
+
git checkout "$CURRENT_VERSION" 2>/dev/null
|
| 525 |
+
return
|
| 526 |
+
fi
|
| 527 |
+
|
| 528 |
+
# Phase 3: update pip dependencies (editable install)
|
| 529 |
+
echo "[$(date)] Agent auto-update: updating pip dependencies..."
|
| 530 |
+
if ! pip install --quiet -e "/app/hermes-agent[${EXTRAS}]" 2>&1 | tail -10; then
|
| 531 |
+
echo "[$(date)] Agent auto-update: pip install failed, rolling back"
|
| 532 |
+
git checkout "$CURRENT_VERSION" 2>/dev/null
|
| 533 |
+
pip install --quiet -e "/app/hermes-agent[${EXTRAS}]" 2>/dev/null
|
| 534 |
+
return
|
| 535 |
+
fi
|
| 536 |
+
|
| 537 |
+
# Phase 4: reinstall our patches on top of new version
|
| 538 |
+
echo "[$(date)] Agent auto-update: re-applying Hermes Bot patches..."
|
| 539 |
+
if [ -f "/app/scripts/patch_file_delivery.py" ]; then
|
| 540 |
+
python3 /app/scripts/patch_file_delivery.py 2>/dev/null
|
| 541 |
+
fi
|
| 542 |
+
if [ -f "/app/scripts/patch_auto_media.py" ]; then
|
| 543 |
+
python3 /app/scripts/patch_auto_media.py 2>/dev/null
|
| 544 |
+
fi
|
| 545 |
+
if [ -f "/app/scripts/patch_resolve_media_paths.py" ]; then
|
| 546 |
+
python3 /app/scripts/patch_resolve_media_paths.py 2>/dev/null
|
| 547 |
+
fi
|
| 548 |
+
# Copy patch files if they exist
|
| 549 |
+
for patch_file in prompt_builder.py send_message_tool.py; do
|
| 550 |
+
if [ -f "/app/patches/hermes-agent/agent/$patch_file" ] && [ -f "$AGENT_DIR/agent/$patch_file" ]; then
|
| 551 |
+
cp "/app/patches/hermes-agent/agent/$patch_file" "$AGENT_DIR/agent/$patch_file" 2>/dev/null
|
| 552 |
+
fi
|
| 553 |
+
done
|
| 554 |
+
|
| 555 |
+
# Save new version
|
| 556 |
+
echo "$LATEST_TAG" > "$VERSION_FILE"
|
| 557 |
+
echo "$(date '+%Y-%m-%d %H:%M:%S')" >> "$VERSION_FILE"
|
| 558 |
+
echo "[$(date)] Agent auto-update: upgraded to $LATEST_TAG ✓ (restart needed for full effect)"
|
| 559 |
+
|
| 560 |
+
# Phase 5: schedule gateway restart for clean reload
|
| 561 |
+
# Send SIGUSR1 to entry.py to trigger gateway restart cycle
|
| 562 |
+
ENTRY_PID=$(pgrep -f "python3 /app/entry.py" 2>/dev/null | head -1)
|
| 563 |
+
if [ -n "$ENTRY_PID" ]; then
|
| 564 |
+
kill -USR1 "$ENTRY_PID" 2>/dev/null && \
|
| 565 |
+
echo "[$(date)] Agent auto-update: sent reload signal to entry.py (PID: $ENTRY_PID)" || \
|
| 566 |
+
echo "[$(date)] Agent auto-update: gateway will use new code on next conversation"
|
| 567 |
+
fi
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
# ── Auto-update hermes-web-ui if newer release exists ──
|
| 571 |
# Runs asynchronously so it doesn't block startup.
|
| 572 |
# Check: GitHub releases/latest → compare with saved version → build if newer.
|
|
|
|
| 708 |
WEBUI_PID=$!
|
| 709 |
echo "[$(date)] WebUI BFF PID: $WEBUI_PID"
|
| 710 |
|
| 711 |
+
# Trigger hermes-agent auto-update in background (framework first, then UI)
|
| 712 |
+
update_hermes_agent_background &
|
| 713 |
+
|
| 714 |
# Trigger WebUI auto-update in background (non-blocking)
|
| 715 |
# Will check GitHub, build if newer, and hot-swap
|
| 716 |
update_webui_background &
|