hermes-hub/launcher/hermes-hub-web.sh
Hermes Team e90f6331a2 fix(launcher): сообщение при запуске не совпадало с настройками; смена токена
Владелец привязал хаб на сервере к сети, а лаунчер всё равно напечатал
«Headless Mode», показал http://127.0.0.1:5800 и посоветовал пробросить порт
через ssh -L. Текст был зашит и настроек не читал: инструкция вела поднимать
туннель к серверу, который уже был виден напрямую. Инструкция, не
совпадающая с реальностью, хуже отсутствующей — тот же класс дефекта, что и
снятая заглушка про «вход через веб невозможен».

Теперь сообщение читает web_api_host из hub_settings.json: при 127.0.0.1
показывает проброс порта и подсказывает про enable_lan_access.py, при
сетевой привязке — реальный адрес из hostname -I и напоминание про токен.

Добавлен --rotate: смена токена понадобилась немедленно, потому что
выданный токен был вставлен в переписку и скомпрометирован. Без флага
поведение прежнее — повторный запуск токен не трогает.

Проверено: без --rotate токен сохраняется, с --rotate меняется; bash -n на
лаунчере проходит; переводы строк LF.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 10:47:29 +07:00

177 lines
7.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# Hermes Hub — Linux Web Application Launcher
# Starts the background web server if not already active, waits for /api/health,
# and opens the UI in application window mode (--app=URL) or handles headless mode.
# ==============================================================================
set -e
DEFAULT_HERMES_HOME="$HOME/.hermes"
HERMES_HOME="${HERMES_HOME:-$DEFAULT_HERMES_HOME}"
# 1. Discover Python Runtime
PYTHON_BIN=""
if [ -f "$HERMES_HOME/hermes-agent/venv/bin/python3" ]; then
PYTHON_BIN="$HERMES_HOME/hermes-agent/venv/bin/python3"
elif [ -f "$HERMES_HOME/hermes-agent/venv/bin/python" ]; then
PYTHON_BIN="$HERMES_HOME/hermes-agent/venv/bin/python"
elif command -v python3 >/dev/null 2>&1; then
PYTHON_BIN="$(command -v python3)"
elif command -v python >/dev/null 2>&1; then
PYTHON_BIN="$(command -v python)"
else
echo "❌ Error: Python 3 not found on system." >&2
exit 1
fi
# 2. Configure Environment and Read Settings (Host/Port)
export PYTHONPATH="$HERMES_HOME/plugins/antigravity-provider/src:$HERMES_HOME/hermes-agent:$PYTHONPATH"
HOST="127.0.0.1"
PORT=5800
SETTINGS_FILE="$HERMES_HOME/hub_settings.json"
if [ -f "$SETTINGS_FILE" ]; then
PARSED_PORT="$("$PYTHON_BIN" -c "import json; print(json.load(open('$SETTINGS_FILE')).get('web_api_port', 5800))" 2>/dev/null || echo 5800)"
if [ -n "$PARSED_PORT" ] && [ "$PARSED_PORT" -gt 0 ] 2>/dev/null; then
PORT="$PARSED_PORT"
fi
PARSED_HOST="$("$PYTHON_BIN" -c "import json; h = json.load(open('$SETTINGS_FILE')).get('web_api_host', '127.0.0.1'); print('127.0.0.1' if h in ('0.0.0.0', '') else h)" 2>/dev/null || echo '127.0.0.1')"
if [ -n "$PARSED_HOST" ]; then
HOST="$PARSED_HOST"
fi
fi
TARGET_URL="http://$HOST:$PORT/"
HEALTH_URL="http://$HOST:$PORT/api/health"
# Function to check server health
check_health() {
# sys импортируется здесь, а не внутри except: раньше на УСПЕШНОМ ответе
# возникал NameError, его ловил тот же except, и проверка всегда сообщала
# об отказе — сервер работал, а лаунчер писал «failed to respond».
"$PYTHON_BIN" -c "
import json
import sys
import urllib.request
try:
with urllib.request.urlopen('$HEALTH_URL', timeout=0.8) as resp:
data = json.loads(resp.read().decode('utf-8'))
sys.exit(0 if data.get('ok') is True else 1)
except Exception:
sys.exit(1)
" >/dev/null 2>&1
}
# 3. Start Web Server if not already active
SERVER_STARTED_BY_US=false
if ! check_health; then
echo "Starting Hermes Hub Web Server in background..."
LOG_FILE="$HERMES_HOME/hermes_web_server.log"
nohup "$PYTHON_BIN" -m antigravity_provider.router.web > "$LOG_FILE" 2>&1 &
SERVER_PID=$!
SERVER_STARTED_BY_US=true
# Wait for server readiness (polling /api/health)
READY=false
for i in $(seq 1 75); do
if check_health; then
READY=true
break
fi
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "❌ Error: Web server process terminated unexpectedly. Check $LOG_FILE" >&2
exit 1
fi
sleep 0.2
done
if [ "$READY" != "true" ]; then
echo "❌ Error: Web server failed to respond at $HEALTH_URL within 15 seconds." >&2
exit 1
fi
fi
# 4. Headless Server Check (SSH / No Graphical Display)
#
# Текст здесь раньше был зашит: он всегда советовал пробрасывать порт и
# показывал 127.0.0.1, даже когда хаб уже привязан к сети и доступен напрямую.
# Инструкция, не совпадающая с настройками, хуже отсутствующей — владелец
# поднимал туннель к серверу, который и так был виден.
if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then
SETTINGS_FILE="$HERMES_HOME/hub_settings.json"
BIND_HOST="127.0.0.1"
if [ -f "$SETTINGS_FILE" ]; then
BIND_HOST="$("$PYTHON_BIN" - "$SETTINGS_FILE" <<'PYEOF' 2>/dev/null || echo "127.0.0.1"
import json, sys
try:
print(json.load(open(sys.argv[1], encoding="utf-8")).get("web_api_host", "127.0.0.1"))
except Exception:
print("127.0.0.1")
PYEOF
)"
fi
echo "======================================================================"
echo " Hermes Hub Web Server is running (no graphical display)"
echo ""
if [ "$BIND_HOST" = "127.0.0.1" ] || [ -z "$BIND_HOST" ]; then
echo " Listening on 127.0.0.1:$PORT — reachable from this machine only."
echo ""
echo " To open it from another computer, forward the port:"
echo " ssh -L $PORT:127.0.0.1:$PORT <user>@<this-host>"
echo " then open http://127.0.0.1:$PORT/ there."
echo ""
echo " To expose it on the local network instead:"
echo " python3 $HERMES_HOME/scripts/enable_lan_access.py"
else
LAN_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
[ -n "$LAN_IP" ] || LAN_IP="<address of this host>"
echo " Listening on $BIND_HOST:$PORT — reachable over the network."
echo ""
echo " Open from another computer:"
echo " http://$LAN_IP:$PORT/"
echo ""
echo " A token is required: paste it in Settings on first open."
echo " No port forwarding needed."
fi
echo "======================================================================"
exit 0
fi
# 5. Graphical Environment: Search for Chromium-based browser in priority order
CHROMIUM_BIN=""
for b in google-chrome google-chrome-stable chromium chromium-browser microsoft-edge microsoft-edge-stable brave-browser; do
if command -v "$b" >/dev/null 2>&1; then
CHROMIUM_BIN="$(command -v "$b")"
break
fi
done
if [ -n "$CHROMIUM_BIN" ]; then
# Launch in Application Window mode without address bar, tabs, or menus
# Отдельный профиль: иначе запущенный браузер передаёт окно уже
# работающему экземпляру и сразу завершается — ожидание его закрытия
# срабатывает мгновенно. На Windows это приводило к тому, что сервер
# убивали, пока страница ещё грузилась.
BROWSER_PROFILE="$HERMES_HOME/web_browser_profile"
mkdir -p "$BROWSER_PROFILE"
"$CHROMIUM_BIN" --app="$TARGET_URL" --window-size=1400,900 --user-data-dir="$BROWSER_PROFILE" --no-first-run --no-default-browser-check "$@"
else
# Fallback to standard default browser
echo "Запуск в обычном браузере: режим приложения (без адресной строки) требует Google Chrome, Chromium или Microsoft Edge."
if command -v xdg-open >/dev/null 2>&1; then
xdg-open "$TARGET_URL"
elif command -v sensible-browser >/dev/null 2>&1; then
sensible-browser "$TARGET_URL"
elif command -v firefox >/dev/null 2>&1; then
firefox "$TARGET_URL"
else
echo "Please open $TARGET_URL in your web browser."
fi
fi
exit 0