hermes-hub/installer/install-linux.sh
Hermes Team 9641957d5b fix(antigravity): вход завершался успешно, а agy требовал войти снова
agy 2.0 читает учётные данные не из .gemini/oauth_creds.json — это формат
Gemini CLI. Свой токен он берёт из .gemini/antigravity-cli/antigravity-oauth-token
в виде {"auth_method": ..., "token": {...}}. Хаб этот файл никогда не создавал,
поэтому «Авторизация успешно завершена» соседствовала с ответом
«Please sign in to view available models».

Установлено сравнением рабочего профиля владельца с неработающим: оба имели
oauth_creds.json одинакового размера, различие было только в этом файле.
Значение auth_method («consumer») взято из рабочего профиля, а не выведено
из общих соображений.

Срок годности пишется в обоих видах: expiry_date в миллисекундах для формата
Node и expiry строкой RFC3339 для Go-шного oauth2.Token.

Заодно устранён рост номеров профилей: слот выбирался до входа, когда почта
ещё неизвестна, и повторный вход тем же аккаунтом занимал очередной свободный
слот — один аккаунт владельца расползся на ag-2, ag-3, ag-4. Теперь после
опознания почты учётные данные возвращаются в слот, который этот аккаунт уже
занимает. Функция проверки двойников в проекте была, но её никто не вызывал.

Версия поднята до 0.1.3, чтобы отличать сборки на глаз. Проверка версии в
тестах сверяется с исходником, а не с записанным числом.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 10:37:27 +07:00

268 lines
10 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 Installation Script
# Mirrors plugin files, registers .desktop entry, creates application launcher,
# and supports both desktop and headless server environments.
# ==============================================================================
set -e
HUB_VERSION="0.1.3"
DEFAULT_HERMES_HOME="$HOME/.hermes"
HERMES_HOME="${HERMES_HOME:-$DEFAULT_HERMES_HOME}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
echo "======================================================================"
echo " HERMES HUB INSTALLER (Linux / POSIX) "
echo "======================================================================"
echo "Version : $HUB_VERSION"
echo "Hermes Home : $HERMES_HOME"
echo "Source Root : $REPO_ROOT"
echo ""
# 0. Остановка работающего хаба.
#
# Установщик копировал файлы, но работающий сервер не трогал: он продолжал
# крутить старый код в памяти, и владелец видел прежний интерфейс при новом
# номере сборки. Три сборки подряд ставились в файлы, но не в работу.
echo "[0/6] Остановка работающего Hermes Hub..."
stop_running_hub() {
local pattern="antigravity_provider.router.web|hermes_hub_web_entry"
local pids
# Только процессы ЭТОГО пользователя и только те, что относятся к хабу.
pids="$(pgrep -u "$(id -u)" -f "$pattern" 2>/dev/null | tr '
' ' ')"
if [ -z "$pids" ]; then
echo " Работающий хаб не найден — останавливать нечего."
return 0
fi
echo " Найдены процессы хаба: $pids"
# shellcheck disable=SC2086
kill $pids 2>/dev/null || true
local waited=0
while [ "$waited" -lt 10 ]; do
sleep 1
waited=$((waited + 1))
pids="$(pgrep -u "$(id -u)" -f "$pattern" 2>/dev/null | tr '
' ' ')"
[ -z "$pids" ] && break
done
if [ -n "$pids" ]; then
echo " Не завершились за 10 секунд, снимаю принудительно: $pids"
# shellcheck disable=SC2086
kill -9 $pids 2>/dev/null || true
sleep 1
pids="$(pgrep -u "$(id -u)" -f "$pattern" 2>/dev/null | tr '
' ' ')"
fi
if [ -n "$pids" ]; then
# Не прерываем установку: файлы обновятся, а владельцу скажем правду.
echo " ⚠ Остались процессы: $pids. Снимите их вручную, иначе будет работать старый код."
return 1
fi
echo " Хаб остановлен."
return 0
}
stop_running_hub || true
echo ""
# 1. Check / Discover Python Runtime and Hermes Environment
echo "[1/6] Checking Python and Hermes environment..."
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)"
fi
if [ -z "$PYTHON_BIN" ]; then
echo "❌ Error: Python 3 not found on system. Please install Python 3.9+." >&2
exit 10
fi
PY_VER="$("$PYTHON_BIN" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
echo " Using Python: $PYTHON_BIN (v$PY_VER)"
# Ensure Hermes Home Directory Structure
mkdir -p "$HERMES_HOME"
mkdir -p "$HERMES_HOME/config"
mkdir -p "$HERMES_HOME/plugins/antigravity-provider/src"
mkdir -p "$HERMES_HOME/plugins/antigravity-provider/assets"
mkdir -p "$HOME/.local/bin"
mkdir -p "$HOME/.local/share/applications"
# 2. Check and Install Dependencies
echo "[2/6] Verifying Python dependencies..."
DEPS_OK=true
"$PYTHON_BIN" -c "import fastapi, uvicorn, pydantic, psutil, yaml; print('DEPS_OK')" >/dev/null 2>&1 || DEPS_OK=false
if [ "$DEPS_OK" != "true" ]; then
echo " Installing required packages (fastapi, uvicorn, pydantic, psutil, pyyaml)..."
"$PYTHON_BIN" -m pip install --no-warn-script-location -q fastapi uvicorn pydantic psutil pyyaml || {
echo "⚠️ Warning: pip install returned non-zero code. Trying with --user..."
"$PYTHON_BIN" -m pip install --user --no-warn-script-location -q fastapi uvicorn pydantic psutil pyyaml || true
}
fi
# 3. Mirror Plugin Files to ~/.hermes/plugins/antigravity-provider (with cleanup of stale files)
echo "[3/6] Deploying plugin source files (mirrored)..."
PLUGIN_SRC="$REPO_ROOT/src/antigravity_provider"
PLUGIN_DST="$HERMES_HOME/plugins/antigravity-provider/src/antigravity_provider"
if [ -d "$PLUGIN_SRC" ]; then
mkdir -p "$PLUGIN_DST"
# Use rsync with --delete if available, or python mirroring
if command -v rsync >/dev/null 2>&1; then
rsync -a --delete --exclude='__pycache__' --exclude='*.pyc' "$PLUGIN_SRC/" "$PLUGIN_DST/"
else
"$PYTHON_BIN" -c "
import os, shutil
src = r'$PLUGIN_SRC'
dst = r'$PLUGIN_DST'
if os.path.exists(dst):
shutil.rmtree(dst)
shutil.copytree(src, dst, ignore=shutil.ignore_patterns('__pycache__', '*.pyc'))
"
fi
else
echo "❌ Error: Source directory $PLUGIN_SRC not found!" >&2
exit 12
fi
# Deploy Assets
if [ -d "$REPO_ROOT/assets" ]; then
mkdir -p "$HERMES_HOME/plugins/antigravity-provider/assets"
cp -r "$REPO_ROOT/assets/"* "$HERMES_HOME/plugins/antigravity-provider/assets/" 2>/dev/null || true
fi
# Deploy helper scripts. Они лежат в поставке, но раньше не разворачивались,
# поэтому вспомогательных инструментов (например открытия хаба в домашнюю сеть)
# на установленной машине просто не оказывалось.
if [ -d "$REPO_ROOT/scripts" ]; then
mkdir -p "$HERMES_HOME/scripts"
cp -r "$REPO_ROOT/scripts/"*.py "$HERMES_HOME/scripts/" 2>/dev/null || true
cp -r "$REPO_ROOT/scripts/"*.sh "$HERMES_HOME/scripts/" 2>/dev/null || true
fi
# 4. Write Deployment Manifest
echo "[4/6] Writing deployment manifest..."
MANIFEST_FILE="$HERMES_HOME/plugins/antigravity-provider/deployment_manifest.json"
# Коммит берётся из BUILD_COMMIT (кладёт сборщик самораспаковывающегося
# установщика, где git недоступен), иначе — из репозитория. Запасного
# литерала нет намеренно: неизвестный коммит должен читаться как неизвестный,
# а не как чужой чужой номер сборки.
if [ -f "$REPO_ROOT/BUILD_COMMIT" ]; then
GIT_COMMIT="$(tr -d '
' < "$REPO_ROOT/BUILD_COMMIT")"
else
GIT_COMMIT="$(cd "$REPO_ROOT" 2>/dev/null && git rev-parse --short HEAD 2>/dev/null || echo 'не определён')"
fi
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u)"
cat <<EOF > "$MANIFEST_FILE"
{
"version": "$HUB_VERSION",
"deployed_at": "$TIMESTAMP",
"git_commit": "$GIT_COMMIT",
"platform": "linux"
}
EOF
# Deploy Template Config only if not existing
if [ ! -f "$HERMES_HOME/config/router_profiles.yaml" ] && [ -f "$REPO_ROOT/config/router_profiles.example.yaml" ]; then
echo " Installing default router_profiles.yaml from template..."
cp "$REPO_ROOT/config/router_profiles.example.yaml" "$HERMES_HOME/config/router_profiles.yaml"
else
echo " Preserving existing user router_profiles.yaml."
fi
# 5. Deploy Launcher Executable and .desktop Entry
echo "[5/6] Creating application launcher and .desktop entry..."
LAUNCHER_SRC="$REPO_ROOT/launcher/hermes-hub-web.sh"
if [ ! -f "$LAUNCHER_SRC" ]; then
LAUNCHER_SRC="$SCRIPT_DIR/hermes-hub-web.sh"
fi
LAUNCHER_BIN="$HOME/.local/bin/hermes-hub-web"
cp "$LAUNCHER_SRC" "$LAUNCHER_BIN"
chmod +x "$LAUNCHER_BIN"
# Also place in ~/.hermes/bin for convenience
mkdir -p "$HERMES_HOME/bin"
cp "$LAUNCHER_SRC" "$HERMES_HOME/bin/hermes-hub-web"
chmod +x "$HERMES_HOME/bin/hermes-hub-web"
# Create .desktop file
DESKTOP_FILE="$HOME/.local/share/applications/hermes-hub-web.desktop"
ICON_PATH="$HERMES_HOME/plugins/antigravity-provider/assets/branding/app/HermesHub.ico"
if [ ! -f "$ICON_PATH" ]; then
ICON_PATH="utilities-terminal"
fi
cat <<EOF > "$DESKTOP_FILE"
[Desktop Entry]
Version=1.0
Type=Application
Name=Hermes Hub Web
GenericName=Multi-Agent & Multi-Provider Control Hub
Comment=Multi-Agent & Multi-Provider Control Hub for Hermes Agent
Exec=$LAUNCHER_BIN
Icon=$ICON_PATH
Terminal=false
Categories=Development;Utility;
StartupNotify=true
StartupWMClass=hermes-hub-web
EOF
chmod +x "$DESKTOP_FILE"
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
fi
# 6. Post-install Smoke Test Verification
echo "[6/6] Running post-install verification smoke test..."
"$PYTHON_BIN" -c "
import sys
sys.path.insert(0, '$HERMES_HOME/plugins/antigravity-provider/src')
import antigravity_provider.router.web.server
print('HERMES_HUB_LINUX_VERIFY_OK')
" || {
echo "❌ Error: Post-install verification failed!" >&2
exit 14
}
echo ""
echo "======================================================================"
echo " HERMES HUB SUCCESSFULLY INSTALLED ON LINUX! "
echo "======================================================================"
echo "Application Launcher : $LAUNCHER_BIN"
echo "Desktop Shortcut : $DESKTOP_FILE"
echo ""
echo "To launch the Web Application window:"
echo " $LAUNCHER_BIN"
echo ""
echo "Or open 'Hermes Hub Web' from your Applications menu."
echo ""
echo "ВАЖНО: работавший хаб был остановлен перед установкой, иначе он"
echo "продолжал бы выполнять прежний код из памяти. Запустите его заново"
echo "командой выше — только тогда новая сборка начнёт работать."
echo ""
echo "Проверить, что поднялся новый код:"
echo " curl -s -D - -o /dev/null http://127.0.0.1:5800/workspace.js | grep -i cache-control"
echo "======================================================================"
exit 0