hermes-hub/agents/inbox/2026-08-23-A15-antigravity-pro-web-api.md
Hermes Team 06b55b49f4 docs(tasks): A15 и A16 — переход на веб-интерфейс
Решение владельца: у него сервер с Ubuntu Server и Xubuntu, Hermes там
будет линуксовый. Веб-интерфейс на сервере строго лучше десктопа.
Десктоп остаётся рабочим до паритета, router/ui/** не трогается.

A15 (Pro): веб-API поверх готового снапшота — GET /api/snapshot и
POST /api/action на семнадцать существующих действий; безопасность
(127.0.0.1 по умолчанию, отказ стартовать на внешнем адресе без токена,
тест на отсутствие секретов в ответе); порт на Linux — восемь мест,
читающих LOCALAPPDATA в обход paths.py; честное сообщение о том, какие
потоки авторизации на headless-сервере не работают. Плюс долг из
прошлого раунда: снапшот не различает «данных нет» и «данные грузятся».

A16 (Flash): клиент без сборки на обычном JS. Не заблокирован сервером —
разрабатывает против docs/web-api/snapshot.example.json. Экраны по
ценности: Аккаунты с видимыми квотами, затем Обзор и Маршрутизация.

Обе стороны пишутся против docs/web-api/CONTRACT.md и до слияния друг
друга не видят — отсюда требование не менять контракт односторонне.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 19:03:48 +07:00

15 KiB
Raw Permalink Blame History

Задание A15 (Antigravity Pro, этот ПК): веб-API и порт на Linux

Дата поступления

2026-08-23

База

Проверочный HEAD на момент выдачи: 7ae4a28.

Ветка

antigravity/web-api


Порядок работы с git

cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/web-api

После первого коммита — git push -u origin antigravity/web-api. В конце — push и проверка git log --oneline -1 origin/antigravity/web-api, git status должен быть чистым.


Что принято по A13

Проверено исполнением, работа сильная.

Устойчивость набора закрыта по-настоящему. Общий корень Tk на сессию вместо шести отдельных. Прогнал десять полных прогонов подряд, как требовало задание: 316 passed, ноль ошибок во всех десяти. До правки каждый прогон давал до семи ошибок, кочующих между файлами, и настоящую поломку приходилось искать перепроверкой в изоляции. Это чинилось не для галочки — оно мешало работе каждый раунд.

Раздел контракта написан по существу: все четыре вопроса раскрыты, явно сказано, что роль Hermes не передаёт. Варианты связывания профилей поданы таблицей с оценкой 34 / 812 / 45 дней, ни один не реализован без решения владельца — ровно как требовалось.

Одно исправлено при слиянии: в документе оказалось 11 символов BEL (0x07) на месте буквы «a» — последовательности вида \agy-05 были разобраны как escape. Пострадали 19 идентификаторов: antigravityntigravity, ag-w2g-w2. Контракт читают оба исполнителя, битые имена в нём недопустимы. Проверяйте документы после записи так же, как код.


Решение владельца: переходим на веб-интерфейс

У владельца сервер с Ubuntu Server и Xubuntu, Hermes там будет линуксовый. Веб-интерфейс на сервере строго лучше десктопа: X-forwarding CustomTkinter по сети — мучение, а интерфейс и так был единственным узким местом всех прошлых раундов.

Десктоп остаётся рабочим до достижения паритета. Ничего из router/ui/** не удаляется.

Читать перед началом: docs/web-api/CONTRACT.md. Это единственный источник истины для вас и для A16, который параллельно делает клиентскую часть. Отклонение от контракта — дефект, даже если ваш код работает: вторая сторона пишется против документа и вашего кода не видит.


P0-1. Веб-API поверх готового снапшота

Архитектура уже сделала бо́льшую часть работы, проверено исполнением: HubSnapshot — dataclass, сериализуется в JSON одним вызовом dataclasses.asdict, объём около 100 КБ, вся поверхность действий сведена в _handle_action и состоит из семнадцати имён.

Новый пакет src/antigravity_provider/router/web/ — структура задана контрактом.

Эндпоинты — по контракту, раздел 4:

  • GET /api/snapshot — весь снапшот, datetime в ISO-8601;
  • POST /api/action — тело {"action": "...", "data": {...}}, ответ {"ok": bool, "message": str, "data": {...}}. Отказ действия — это 200 с ok: false, а не 4xx; 4xx остаётся неизвестному действию и непройденной авторизации;
  • GET /api/health — без авторизации.

Стек: FastAPI и uvicorn. Обе зависимости уже объявлены в pyproject.toml в группе legacy — она осталась от удалённого gui_server.py и не используется ничем. Переименовать в web и подключить.

Действия исполнять через существующий путь, а не дублировать логику. _handle_action сейчас завязан на виджеты; вынести из него исполнительную часть так, чтобы её вызывали и десктоп, и веб. Второй реализации семнадцати действий в проекте быть не должно.

Долгие операции не должны держать запрос. refresh_all опрашивает провайдеров по сети, test дёргает подпроцесс, agy models в замерах то отвечает за 40 секунд, то висит больше двух минут. Такие действия возвращают ok: true с сообщением «запущено», а результат приходит следующим снапшотом.

P0-2. Безопасность — часть задания, не довесок

Сервер будет доступен по сети. Контракт, раздел 3:

  1. По умолчанию слушать только 127.0.0.1. Другой адрес — явным параметром, и тогда токен обязателен.
  2. Токен в заголовке X-Hub-Token, сравнение через secrets.compare_digest.
  3. При запуске на не-локальном адресе без токена сервер отказывается стартовать с внятным сообщением. Не поднимается открытым, не пишет предупреждение в лог и не продолжает.
  4. Тест, падающий при появлении секрета в ответе. Проверено на живых данных: сейчас в сериализованном снапшоте нет ни access_token, ни refresh_token, ни api_key, ни JWT, ни строк ya29./sk-. Это состояние надо удержать механически, а не обещанием.
  5. Никаких секретов в URL и параметрах запроса.

P0-3. Порт на Linux

Проверено по коду — ядро почти готово:

  • paths.py уже кроссплатформенный: при отсутствии LOCALAPPDATA уходит в ~/.hermes;
  • поиск CLI уже готов: shutil.which("agy") or shutil.which("agy.exe").

Чинить надо восемь мест, которые дублируют логику LOCALAPPDATA в обход paths.py и на Linux дадут неверные пути:

router/hermes_hub_app.py:37
router/router_config.py:319, 462
router/launcher_bootstrap.py:32
router/model_discovery_service.py:34
router/ui/assets.py:48
agy_subprocess.py:50

Все — через paths.get_hermes_home(). Единый источник истины уже есть, им просто не пользуются.

Тест: при заданном HERMES_HOME ни один модуль не обращается к LOCALAPPDATA напрямую; пути одинаковы во всех модулях.

Проверка на настоящем Linux обязательна — заявления «должно работать» не принимаются. Если под рукой нет машины, скажите об этом прямо в отчёте, и проверку сделает владелец.

P0-4. Авторизация на сервере без экрана — сказать правду

Разобрано по коду, выяснять заново не нужно:

Провайдер Поток На headless-сервере
OpenAI Codex device-code (12 упоминаний) работает
Grok device-code (13) работает
Antigravity redirect на localhost не работает
Claude redirect на localhost не работает

Для Antigravity и Claude редирект придёт на машину пользователя, а не сервера, — поток обрывается.

Требуется: серверная часть сообщает клиенту, какие потоки на этой машине доступны, а какие нет, и почему. Поле в ответе /api/health или отдельный эндпоинт — на ваше усмотрение, но зафиксируйте в контракте и предупредите A16.

Обходной путь предложить, а не изобретать молча: проброс порта по SSH либо авторизация на десктопе с переносом каталога профиля. Что из этого работает — проверить и написать.

Неработающую кнопку показывать нельзя. Это прямое продолжение правила честности: интерфейс, предлагающий подключить Antigravity на сервере, где это невозможно, — та же ложь, что выдуманная квота.

P1-5. Долг из прошлого раунда: квота не видна до фонового опроса

Найдено при проверке A14 и относится к вашей зоне.

state_store наполняет снапшот через quota_service.get_snapshot, который читает кэш и при промахе отдаёт пустую заглушку из двух корзин, живой опрос не запуская. Замер:

снапшот сразу после старта : 2 корзины, 0 измеренных
после прогрева кэша        : 4 корзины, все измерены, ag-w2 = 37.4%

В работающем приложении квота появляется только после фонового обновления, а до него карточки стоят пустыми без объяснения. Это и есть жалоба владельца «в аккаунтах квота так и не отображается».

Требуется различать в модели данных «данных нет» и «данные ещё грузятся». Сейчас оба состояния выглядят одинаково, и ни интерфейс десктопа, ни будущий веб отличить их не могут. Поле состояния — в снапшот и в контракт.


Ограничения

  • Параллельно идёт A16 (клиентская часть). Ваши файлы: router/web/** кроме static/, state_store.py, paths.py, router_config.py, agy_subprocess.py, launcher_bootstrap.py, model_discovery_service.py, pyproject.toml, docs/web-api/CONTRACT.md. Не ваши: router/web/static/**, router/ui/**.
  • Контракт менять можно, но только правкой документа с явным упоминанием в отчёте — вторая сторона пишется против него.
  • Десктоп не ломать: он остаётся рабочим до паритета.
  • Никаких чисел и идентификаторов без измерения.
  • Тег v0.1.1 не создавать.

Критерии приёмки

  1. Ветка в origin, финальный коммит виден, git status чист.
  2. GET /api/snapshot отдаёт снапшот, совпадающий по структуре с docs/web-api/snapshot.example.json; проверено тестом сравнения ключей.
  3. POST /api/action принимает все семнадцать действий; отказ возвращается как 200 с ok: false; неизвестное действие — 4xx.
  4. Действия исполняются через общий путь; второй реализации семнадцати действий в проекте нет.
  5. Долгие операции не держат запрос; результат приходит следующим снапшотом.
  6. Сервер по умолчанию слушает 127.0.0.1; на внешнем адресе без токена отказывается стартовать; проверено тестом.
  7. Есть тест, падающий при появлении в ответе access_token, refresh_token, api_key, JWT или строк ya29. / sk-.
  8. Ни один модуль не читает LOCALAPPDATA в обход paths.py; проверено тестом с заданным HERMES_HOME.
  9. Доступность потоков авторизации сообщается клиенту с причиной; зафиксировано в контракте.
  10. В снапшоте различаются «данных нет» и «данные грузятся»; поле описано в контракте.
  11. Прогон в обоих окружениях; ruff check . чисто; релизный гейт остаётся 7/7.
  12. Отчёт: START_HEAD, FINAL_HEAD, origin/main, git status, точный X passed / Y skipped / Z failed, и отдельно — проверялось ли на настоящем Linux.

Порядок сдачи

Передать точный FINAL_COMMIT_SHA. Сдано только после появления коммита в origin.