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>
This commit is contained in:
Hermes Team 2026-08-25 10:47:29 +07:00
parent 2ca42a39c4
commit e90f6331a2
2 changed files with 51 additions and 11 deletions

View file

@ -95,16 +95,48 @@ if ! check_health; then
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 (Headless Mode)"
echo " Web Interface URL: $TARGET_URL"
echo " Hermes Hub Web Server is running (no graphical display)"
echo ""
echo " To access the interface from your local computer, forward the port:"
echo " ssh -L $PORT:127.0.0.1:$PORT user@server"
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 " Then open in your browser:"
echo " $TARGET_URL"
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

View file

@ -2,6 +2,7 @@
python3 scripts/enable_lan_access.py # показать, что будет сделано
python3 scripts/enable_lan_access.py --yes # применить
python3 scripts/enable_lan_access.py --yes --rotate # сменить токен
Почему подтверждение обязательно. Каталог берётся из HERMES_HOME, а эта
переменная часто задана в окружении незаметно для запускающего. При отладке
@ -20,6 +21,7 @@ from pathlib import Path
def main() -> int:
apply = "--yes" in sys.argv
rotate = "--rotate" in sys.argv
env_home = os.environ.get("HERMES_HOME")
home = Path(env_home) if env_home else (Path.home() / ".hermes")
target = home / "hub_settings.json"
@ -37,10 +39,16 @@ def main() -> int:
print(f"\nОТКАЗ: {target} повреждён. Разберитесь вручную — перезаписывать не буду.")
return 1
existing = data.get("web_api_token")
existing = None if rotate else data.get("web_api_token")
keep = sorted(k for k in data if not k.startswith("web_api"))
print(f"Будут сохранены: {', '.join(keep) if keep else '(файл пуст)'}")
print(f"Токен : {'уже задан, оставляю прежний' if existing else 'будет создан новый'}")
if rotate:
note = "БУДЕТ ЗАМЕНЁН — прежний перестанет действовать"
elif existing:
note = "уже задан, оставляю прежний"
else:
note = "будет создан новый"
print(f"Токен : {note}")
if not apply:
print("\nПоказан предпросмотр. Чтобы применить, повторите с --yes")