Установка на сервере владельца прервалась на проверке: «No module named
'fastapi'». Причин две, и обе в установщике.
Первая: запуск через sudo. Установка пользовательская — всё ложится в
$HOME/.hermes и $HOME/.local/bin, root не нужен. Под sudo домашним каталогом
становится /root, венв Hermes там не находится, и установщик уходит на
системный python. Копия при этом ложится в /root/.hermes, где владелец её не
видит. Теперь запуск через sudo распознаётся и отклоняется с объяснением;
осознанный обход остаётся через HERMES_ALLOW_ROOT=1.
Вторая: системный python в Ubuntu 24.04 помечен EXTERNALLY-MANAGED (PEP 668) и
отклоняет pip install — и обычный, и с --user. Установщик обе неудачи проглатывал
(|| true) и продолжал работу до отказа на проверке. Теперь при защищённом
системном python создаётся собственное окружение $HERMES_HOME/venv, а неудача
установки зависимостей прекращает установку с внятным кодом возврата.
--break-system-packages не применяется: имя флага не преувеличивает.
Пусковик научен той же ветке — иначе запуск уходил бы на системный python,
где зависимостей нет и быть не может.
Проверено исполнением на сервере под непривилегированным пользователем:
окружение создано, зависимости установлены, HERMES_HUB_LINUX_VERIFY_OK,
запуск через sudo отклонён с кодом 3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец привязал хаб на сервере к сети, а лаунчер всё равно напечатал
«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>
1. Главное: ERR_CONNECTION_REFUSED в окне приложения.
browserProc.WaitForExit() возвращался МГНОВЕННО, когда Edge уже был
запущен: новый msedge.exe передаёт окно работающему экземпляру и сразу
завершается. Лаунчер считал, что окно закрыли, и убивал сервер, пока
страница ещё грузилась.
Браузер теперь запускается с отдельным профилем (--user-data-dir), то
есть процесс живёт столько же, сколько окно. Плюс подстраховка: выход
браузера быстрее пяти секунд не считается закрытием окна.
Та же ловушка была в Linux-лаунчере — исправлена там же.
Проверено вживую: сервер отвечает 200 И окно приложения живо
(«Hermes Hub — Панель управления»).
2. Установщик по галочке «Запустить сейчас» открывал ДЕСКТОП. Владелец
получал десктопное окно и принимал его за старую версию — внешне оно
и правда другое. Теперь запускается веб-интерфейс, подпись галочки
уточнена. Десктоп остаётся доступен своим ярлыком.
Тесты: 373 passed, ruff чисто. Установщик пересобран.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Три дефекта, найденные при установке владельцем на две машины.
1. Linux: лаунчер писал «Web server failed to respond», хотя сервер
поднимался нормально — в логе старт API, планировщик квот и прогрев
кэша без ошибок. Причина в самой проверке: sys импортировался ТОЛЬКО
внутри except, а использовался в успешной ветке. На здоровом ответе
возникал NameError, его ловил тот же except, и проверка всегда
возвращала отказ.
Доказано исполнением на живом сервере: старая логика -> код 1,
новая -> код 0.
2. Windows: при нечитаемом манифесте мастер показывал зашитые
InstalledVersion = "0.1.0" и InstalledDate = "19.08.2026" как факт.
Владелец видел «старую версию» на свежей установке, хотя проверка
показала правильный путь и версию 0.1.1. Заглушки заменены на
«не определена» — выдуманный факт хуже отсутствующего.
3. Манифест писался одним File.WriteAllText: прерванная запись оставляла
пустой файл, и разбор версии падал на первом символе — ровно это и
случилось у владельца (JSONDecodeError, char 0). Запись переведена на
временный файл с переносом.
Попутно: git_commit в манифесте был зашит как "8cddc9f", то есть манифест
сообщал неправду о происхождении сборки. Теперь сборщик проставляет
фактический коммит.
Тесты: 373 passed, ruff чисто. Установщик пересобран.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>