docs(tasks): A17, A18, A19 — честный статус, выбор модели, два установщика
A17 (Pro): «Работает» — ветка else в определении здоровья, она означает «мы не знаем о проблемах», а подана как утверждение. Отказы попадают в статус только после боевого сбоя, поэтому непроверенный профиль автоматически зелёный. Плюс «Проверить подключение» не вызывает модель вовсе — Grok её проходит и не работает. A18 (Flash): действия смены модели не существует ни среди семнадцати, ни в клиенте; десктоп это умеет, но логика заперта в методе интерфейса. И выбирать не из чего: кэш моделей пуст по всем провайдерам, потому что agy models нестабильна — в одном прогоне 40 секунд, в следующем висит больше двух минут. A19: два установщика. Windows — ярлык, открывающий веб окном приложения через --app без адресной строки (проверено на машине владельца: Edge и Chrome есть, окно открывается). Linux — скрипт установки, .desktop и удаление с сохранением данных, плюс честная подсказка про проброс порта при пустом DISPLAY. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fb23bff0b0
commit
18695a3f6e
3 changed files with 414 additions and 0 deletions
137
agents/inbox/2026-08-23-A17-antigravity-pro-honest-status.md
Normal file
137
agents/inbox/2026-08-23-A17-antigravity-pro-honest-status.md
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
# Задание A17 (Antigravity Pro): честный статус аккаунта и настоящая проверка
|
||||||
|
|
||||||
|
## Дата поступления
|
||||||
|
2026-08-23
|
||||||
|
|
||||||
|
## База
|
||||||
|
Проверочный HEAD на момент выдачи: **`fb23bff`**.
|
||||||
|
|
||||||
|
## Ветка
|
||||||
|
`antigravity/honest-status`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок работы с git
|
||||||
|
|
||||||
|
```
|
||||||
|
cd <каталог репозитория>; git fetch origin --prune; git status
|
||||||
|
git checkout main; git pull --ff-only origin main
|
||||||
|
git checkout -b antigravity/honest-status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Сначала коммит, потом push.** В прошлый раз работа A15 была выполнена целиком, но осталась незакоммиченной в рабочем каталоге — git в вашем окружении был недоступен, и ветка в `origin` оказалась пустой. Её нашли случайно.
|
||||||
|
|
||||||
|
Если git снова недоступен — **скажите об этом первой строкой отчёта**, а не в предупреждении под ним. Это меняет весь порядок приёмки.
|
||||||
|
|
||||||
|
В конце:
|
||||||
|
|
||||||
|
```
|
||||||
|
git status <- дерево чистое
|
||||||
|
git log --oneline -1 origin/antigravity/honest-status <- ваш коммит
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что принято по A15
|
||||||
|
|
||||||
|
Веб-API и вынесение действий в общий `ActionExecutor` — правильная архитектура, и она работает. Порт путей на Linux почти закрыт: осталось одно место, `hermes_hub_app.py:37`.
|
||||||
|
|
||||||
|
Но в сданном виде **не работало ни то, ни другое**, и это важнее похвалы:
|
||||||
|
|
||||||
|
1. **Десктоп был уничтожен.** При выносе действий пропало объявление `class HermesHubApp` вместе с 13 методами каркаса — `__init__`, `_build_layout`, `_create_view`, `_show_view`, `_refresh_data`. Оставшиеся 14 методов оказались вложены **внутрь функции `_load_saved_theme` после её `return`** — синтаксически валидный недостижимый код. Поэтому модуль импортировался, и дефект выглядел безобидным, а `launch_hub()` упал бы с `NameError`.
|
||||||
|
|
||||||
|
2. **Веб-API падал с 500 на обоих значимых эндпоинтах**: `get_auth_token` и `run_server` читали `config.hub`, которого у `RouterConfig` нет. Работал только `/api/health` — у него нет проверки авторизации, из-за чего сервер и выглядел поднявшимся.
|
||||||
|
|
||||||
|
3. **`do_save_settings` при переносе потеряла** атомарную запись через `os.replace`, `ensure_ascii=False` и вызов `set_refresh_interval` — интервал обновления квот из настроек перестал применяться.
|
||||||
|
|
||||||
|
Всё восстановлено ревьюером. Урок один и он общий для проекта: **крупное перемещение кода проверяется запуском того, что перемещали.** Импорт модуля ничего не доказывает — Python примет и недостижимый код.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Главное: «Работает» — вымышленный статус
|
||||||
|
|
||||||
|
Владелец сообщил про два аккаунта: «стоит опенкод аккаунт, который не подключён… аккаунт не работает» и «и грок не работает». Оба показаны зелёным **«Работает»**.
|
||||||
|
|
||||||
|
По одному из них причина найдена и уже исправлена: адаптер OpenCode не читал сохранённый ключ (`fb23bff`). Но осталась причина, общая для обоих и более глубокая.
|
||||||
|
|
||||||
|
`unified_health.py:429` — `STATUS_HEALTHY` с подписью «Работает» назначается в **ветке `else`**, когда ни одно условие отказа не совпало:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. не enabled -> Отключён
|
||||||
|
2. нет учётных данных -> Аккаунт не добавлен / Требуется вход / Холодный резерв
|
||||||
|
3. cooldown или квота -> Квота исчерпана
|
||||||
|
4. rate limit -> Лимит запросов
|
||||||
|
5. precord.overall_state -> Ошибка
|
||||||
|
6. иначе -> «Работает» <- сюда попадает всё непроверенное
|
||||||
|
```
|
||||||
|
|
||||||
|
Состояния отказа берутся из `precord` — записей health tracker, которые появляются **только после настоящего сбоя в бою**. Профиль, который ни разу не вызывали, автоматически получает зелёное «Работает».
|
||||||
|
|
||||||
|
**То есть надпись означает «мы не знаем о проблемах», а подана как утверждение, что аккаунт работает.** Это тот же класс дефекта, из-за которого в первом аудите проекта были удалены выдуманные проценты квот: отсутствие данных выдаётся за положительный результат.
|
||||||
|
|
||||||
|
### Что требуется
|
||||||
|
|
||||||
|
1. **Различать «проверено и работает» и «не проверялось».** Профиль без подтверждения не должен выглядеть так же, как подтверждённо рабочий. Формулировку выберите сами, но она обязана быть честной: «Не проверялся» — правда, «Работает» — нет.
|
||||||
|
|
||||||
|
2. **Хранить результат и время последней успешной проверки** рядом с профилем и отдавать их в снапшоте. Интерфейсу нужно показать «проверено 12:05», а не только цвет.
|
||||||
|
|
||||||
|
3. Состояние отказа по-прежнему приходит из боевых сбоев — это правильно и ломать не нужно.
|
||||||
|
|
||||||
|
**Тест:** профиль со свежесохранёнными учётными данными и без единой проверки не получает статус, утверждающий работоспособность.
|
||||||
|
|
||||||
|
## P0-2. «Проверить подключение» ничего не проверяет
|
||||||
|
|
||||||
|
`do_test_profile` проверяет наличие учётных данных и доступность локального runtime — и **никогда не вызывает модель**. Поэтому Grok эту проверку проходит и всё равно не работает.
|
||||||
|
|
||||||
|
Так сложилось не случайно: требование «тест не должен запускать OAuth и открывать браузер» стоит в проекте с первого аудита, и ради него вызов модели убрали целиком. Требование верное, но реализация выплеснула вместе с ним смысл проверки.
|
||||||
|
|
||||||
|
**Требуется настоящая проверка**, не нарушающая прежнего запрета:
|
||||||
|
|
||||||
|
- минимальный реальный вызов к провайдеру — самый дешёвый из возможных, с жёстким таймаутом;
|
||||||
|
- **интерактивный вход не запускается ни при каких условиях**: просроченные учётные данные дают ошибку «Авторизация истекла», а не окно браузера. Для Antigravity это уже обеспечено флагами `BROWSER=none` и `CI=1` в окружении подпроцесса и проверкой срока токена до вызова;
|
||||||
|
- результат сохраняется как последняя проверка (P0-1) с временем;
|
||||||
|
- по каждому провайдеру в отчёте: что именно вызывается и сколько это стоит владельцу. Если у провайдера нет дешёвого способа — **сказать об этом прямо**, а не имитировать проверку.
|
||||||
|
|
||||||
|
Осторожно с ценой: у владельца шесть аккаунтов Antigravity, три Codex, три OpenCode. Проверка всех подряд не должна съедать квоту. Массовую проверку делать по явной команде, а не автоматически при каждом обновлении.
|
||||||
|
|
||||||
|
**Тест:** просроченные учётные данные дают ошибку авторизации без попытки интерактивного входа; успешная проверка фиксируется с временем.
|
||||||
|
|
||||||
|
## P1-3. Остаток порта на Linux
|
||||||
|
|
||||||
|
`hermes_hub_app.py:37` по-прежнему читает `LOCALAPPDATA` напрямую:
|
||||||
|
|
||||||
|
```python
|
||||||
|
_LOCAL = Path(os.environ.get("LOCALAPPDATA", ""))
|
||||||
|
```
|
||||||
|
|
||||||
|
На Linux это даст пустой путь. Провести через `paths.get_hermes_home()`, как остальные семь мест.
|
||||||
|
|
||||||
|
В `agy_subprocess.py` два оставшихся упоминания трогать не нужно: строка 41 — комментарий, строка 414 — список переменных окружения, пробрасываемых подпроцессу на Windows, и он там уместен.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
- Параллельно идут **A18** (смена модели) и **A19** (установщики). Ваши файлы: `unified_health.py`, `health_tracker.py`, `action_handler.py`, `adapters/**`, `state_store.py`, `hermes_hub_app.py`, `docs/UI_STATE_CONTRACT.md`, `docs/web-api/CONTRACT.md`. **Не ваши:** `router/web/**`, `router/ui/**`, `model_discovery*`, `installer/**`, `launcher/**`.
|
||||||
|
- Меняете снапшот — правьте `docs/web-api/CONTRACT.md` и скажите об этом в отчёте: против него пишется клиент.
|
||||||
|
- Никаких статусов без основания. Нет проверки — так и написать.
|
||||||
|
- Тег `v0.1.1` не создавать.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
1. Ветка в `origin`, `git status` чист. Если git был недоступен — сказано первой строкой отчёта.
|
||||||
|
2. Непроверенный профиль не показывается как работающий; проверено тестом.
|
||||||
|
3. Результат и время последней проверки хранятся и приходят в снапшоте; контракт обновлён.
|
||||||
|
4. «Проверить подключение» делает реальный вызов с таймаутом и **не запускает интерактивный вход ни при каких условиях**; проверено тестом на просроченных данных.
|
||||||
|
5. В отчёте по каждому провайдеру сказано, что вызывается при проверке и во что это обходится владельцу.
|
||||||
|
6. Массовая проверка не запускается автоматически при обновлении данных.
|
||||||
|
7. `hermes_hub_app.py:37` больше не читает `LOCALAPPDATA` напрямую.
|
||||||
|
8. Прогон в обоих окружениях; `ruff check .` чисто; релизный гейт не ухудшен (сейчас 7/7).
|
||||||
|
9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. На `main` сейчас 331 passed, 2 skipped.
|
||||||
|
|
||||||
|
## Главное
|
||||||
|
|
||||||
|
Зелёная галочка на нерабочем аккаунте — худший вид лжи в этом продукте: она не просто бесполезна, она уводит от поиска настоящей причины. Владелец потратил на это два обращения. Лучше честное «не проверялся», чем уверенное «работает».
|
||||||
|
|
||||||
|
## Порядок сдачи
|
||||||
|
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.
|
||||||
130
agents/inbox/2026-08-23-A18-antigravity-flash-model-choice.md
Normal file
130
agents/inbox/2026-08-23-A18-antigravity-flash-model-choice.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Задание A18 (Antigravity Flash): выбор модели и надёжное обнаружение
|
||||||
|
|
||||||
|
## Дата поступления
|
||||||
|
2026-08-23
|
||||||
|
|
||||||
|
## База
|
||||||
|
Проверочный HEAD на момент выдачи: **`fb23bff`**.
|
||||||
|
|
||||||
|
## Ветка
|
||||||
|
`antigravity/model-choice`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок работы с git
|
||||||
|
|
||||||
|
```
|
||||||
|
cd <каталог репозитория>; git fetch origin --prune; git status
|
||||||
|
git checkout main; git pull --ff-only origin main
|
||||||
|
git checkout -b antigravity/model-choice
|
||||||
|
git commit -m "..." <- сначала коммит
|
||||||
|
git push -u origin antigravity/model-choice
|
||||||
|
```
|
||||||
|
|
||||||
|
В конце — push и проверка:
|
||||||
|
|
||||||
|
```
|
||||||
|
git status
|
||||||
|
git log --oneline -1 origin/antigravity/model-choice
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что принято по A16
|
||||||
|
|
||||||
|
**Лучшая работа за все раунды.** Веб-клиент собран без сборки, экран «Аккаунты» показывает 22 из 22 аккаунтов с квотами, указанием пула и периода, временем сброса. Неподключённые честно помечены. Внизу индикатор источника данных — интерфейс сам сообщает, живые данные он показывает или фикстуру. Все восемь скриншотов содержательные, пустых нет. Границу зоны соблюли.
|
||||||
|
|
||||||
|
Три вещи доделал ревьюер, знать полезно:
|
||||||
|
|
||||||
|
1. **Сервер не отдавал статику вообще** — в браузере был 404. Обе стороны выполнили контракт, но он не назвал, кто монтирует `static/`. Это пропуск автора контракта, не ваш; исправлено, контракт поднят до 1.1.
|
||||||
|
2. **Квоты не подтягивались**: кэш никто не грел, а `HubStateStore.get_snapshot()` возвращает кэшированный снапшот и пересобирает его только при первом вызове. Добавлен фоновый цикл.
|
||||||
|
3. **Загрузка выглядела как отсутствие данных.** Сервер отдавал `is_loading`, клиент его игнорировал и рисовал «Н/Д» — тот же текст, что у аккаунта без лимитов. Владелец увидел это и решил, что лимиты не работают. Теперь показывается «Загрузка…».
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0-1. Смены модели не существует
|
||||||
|
|
||||||
|
Владелец: **«не дает поменять модель. хочу выбрать 2 кодера гемини про, а не дает»**.
|
||||||
|
|
||||||
|
Проверено: **действия смены модели нет ни среди семнадцати, ни в веб-клиенте.** Десктоп это умеет — `_open_agent_settings_modal._save_agent` пишет `profile.preferred_models` и вызывает `save_router_config`, — но логика заперта внутри метода интерфейса и наружу не вынесена.
|
||||||
|
|
||||||
|
**Требуется:**
|
||||||
|
|
||||||
|
1. **Действие `set_model`** в `action_handler.ActionExecutor` — там, где живут остальные. Принимает профиль (или роль) и идентификатор модели, ставит её первой в `preferred_models`, сохраняет конфигурацию.
|
||||||
|
2. **Отказ, если модели нет у провайдера.** Не подставлять «похожую», не сохранять молча. У владельца в конфигурации уже стоит `gemini-3.7-flash`, которой у провайдера **не существует**, — она попала туда именно так, через литерал в коде.
|
||||||
|
3. **Выбор в веб-клиенте**: на карточке роли и в окне деталей аккаунта. После сохранения новая модель видна без перезагрузки страницы.
|
||||||
|
4. Добавить `set_model` в `docs/web-api/CONTRACT.md` — список действий там перечислен поимённо, и клиент пишется против него.
|
||||||
|
|
||||||
|
Десктопную модалку не ломать: она должна вызывать то же действие, а не свою копию. Второй реализации в проекте быть не должно — ради этого действия и выносили в общий слой.
|
||||||
|
|
||||||
|
**Тест:** смена модели сохраняется в конфигурацию и переживает перезапуск; несуществующая модель отклоняется с внятной причиной.
|
||||||
|
|
||||||
|
## P0-2. Обнаружение моделей не даёт ничего
|
||||||
|
|
||||||
|
Даже когда выбор появится, он будет пустым. Проверено:
|
||||||
|
|
||||||
|
```
|
||||||
|
antigravity моделей в кэше: 0
|
||||||
|
opencode-go моделей в кэше: 0
|
||||||
|
grok моделей в кэше: 0
|
||||||
|
файла models_cache.json на диске нет
|
||||||
|
```
|
||||||
|
|
||||||
|
Причина не в вашем коде: зонд вызывает `agy models`, а эта команда нестабильна. Замерено на живой машине — **в одном прогоне отвечает за 40 секунд, в следующем висит больше двух минут и убивается по таймауту** (`rc=124`). Причём виснет она и при прямом вызове из консоли, без всякого Hub.
|
||||||
|
|
||||||
|
Когда она отвечает, список настоящий и там есть то, что просит владелец:
|
||||||
|
|
||||||
|
```
|
||||||
|
gemini-3.7-flash-high / -medium / -low
|
||||||
|
gemini-3.6-flash-high / -medium / -low
|
||||||
|
gemini-3.5-flash-high / -medium / -low
|
||||||
|
gemini-3.1-pro-high / -low <- «Gemini Pro», которого он хочет
|
||||||
|
claude-sonnet-4-6, claude-opus-4-6-thinking, gpt-oss-120b-medium
|
||||||
|
```
|
||||||
|
|
||||||
|
**Требуется сделать обнаружение полезным вопреки нестабильности CLI:**
|
||||||
|
|
||||||
|
- результат **сохраняется на диск** и переживает перезапуск. Один удачный опрос за сутки должен закрывать вопрос;
|
||||||
|
- обновление в фоне, с таймаутом; срабатывание таймаута **не затирает прежний кэш**;
|
||||||
|
- **ручная кнопка «Обновить список моделей»** — владелец должен иметь возможность попробовать ещё раз, а не ждать интервала;
|
||||||
|
- при пустом кэше интерфейс говорит «список моделей ещё не получен» и предлагает обновить. Литеральный список **не подставлять** ни при каких условиях;
|
||||||
|
- в отчёте написать, сколько попыток из десяти `agy models` завершились успешно на вашей машине. Это цифра, которая определит, годен ли зонд вообще.
|
||||||
|
|
||||||
|
Если окажется, что команда безнадёжна — предложить альтернативу и обосновать: разбор конфигурации `agy`, отдельный эндпоинт, ручной ввод модели владельцем. Молча оставлять пустой список нельзя, это второе задание подряд с этим пунктом.
|
||||||
|
|
||||||
|
**Тест:** кэш переживает перезапуск; таймаут не затирает прежние данные; пустой кэш даёт понятное сообщение, а не пустой выпадающий список.
|
||||||
|
|
||||||
|
## P1-3. Проверить остальные экраны на данных
|
||||||
|
|
||||||
|
Экран «Команда агентов» на скриншоте владельца показывает роли `research` и `fast` с профилем `opengo-1` и моделью `deepseek-r1`. Убедиться, что после появления выбора модель на этом экране меняется вместе с конфигурацией, а не остаётся прежней до перезапуска.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
- Параллельно идут **A17** (честный статус) и **A19** (установщики). Ваши файлы: `router/web/static/**`, `model_discovery.py`, `model_discovery_service.py`, `router/ui/**`, `tests/test_ui_*.py`, `tests/test_web_client_*.py`. По `action_handler.py` — **только добавление `set_model`**, остального там не касаться: A17 работает в этом же файле.
|
||||||
|
- Контракт правьте в части списка действий, остальное — зона A17. Скажите в отчёте, что изменили.
|
||||||
|
- Не выдумывать названия моделей. Нет обнаруженного списка — пустой список и объяснение.
|
||||||
|
- Тег `v0.1.1` не создавать.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
1. Ветка в `origin`, `git status` чист.
|
||||||
|
2. Действие `set_model` живёт в `action_handler`; десктоп и веб вызывают его, второй реализации нет.
|
||||||
|
3. Модель меняется из веб-интерфейса, сохраняется и переживает перезапуск; проверено тестом.
|
||||||
|
4. Несуществующая модель отклоняется с внятной причиной; проверено тестом.
|
||||||
|
5. Кэш моделей сохраняется на диск и переживает перезапуск; таймаут не затирает прежние данные.
|
||||||
|
6. Есть ручное обновление списка моделей.
|
||||||
|
7. Пустой кэш даёт объяснение, а не пустой список; литералов нет.
|
||||||
|
8. В отчёте: сколько попыток из десяти `agy models` завершились успехом.
|
||||||
|
9. `set_model` добавлено в контракт.
|
||||||
|
10. Прогон в обоих окружениях; `ruff check .` чисто; гейт не ухудшен.
|
||||||
|
11. **Скриншот смены модели с настоящими данными**: до, выбор, после.
|
||||||
|
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На `main` сейчас 331 passed.
|
||||||
|
|
||||||
|
## Главное
|
||||||
|
|
||||||
|
Владелец хочет поставить двум кодерам Gemini Pro. Сейчас это невозможно тремя способами сразу: действия нет, элемента управления нет, списка моделей нет. Задание закрывает все три.
|
||||||
|
|
||||||
|
## Порядок сдачи
|
||||||
|
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.
|
||||||
147
agents/inbox/2026-08-23-A19-installers-windows-linux.md
Normal file
147
agents/inbox/2026-08-23-A19-installers-windows-linux.md
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
# Задание A19: два установщика — Windows и Linux, запуск веб-интерфейса окном приложения
|
||||||
|
|
||||||
|
## Дата поступления
|
||||||
|
2026-08-23
|
||||||
|
|
||||||
|
## База
|
||||||
|
Проверочный HEAD на момент выдачи: **`fb23bff`**.
|
||||||
|
|
||||||
|
## Ветка
|
||||||
|
`antigravity/installers`
|
||||||
|
|
||||||
|
## Кому
|
||||||
|
Отдельная задача, не пересекается по файлам ни с A17, ни с A18. Брать тому, кто освободится первым.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок работы с git
|
||||||
|
|
||||||
|
```
|
||||||
|
cd <каталог репозитория>; git fetch origin --prune; git status
|
||||||
|
git checkout main; git pull --ff-only origin main
|
||||||
|
git checkout -b antigravity/installers
|
||||||
|
git commit -m "..." <- сначала коммит
|
||||||
|
git push -u origin antigravity/installers
|
||||||
|
```
|
||||||
|
|
||||||
|
В конце — push и проверка `git log --oneline -1 origin/antigravity/installers`, `git status` чистый.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Чего хочет владелец
|
||||||
|
|
||||||
|
Дословно: «сделай задание на 2 установочника. линукс и виндовс. в винде будет так же создаваться ярлык. при запуске будет открываться окно браузерное но без настроек браузера а как наша десктоп программа. а у линукс ссылка на открытие окна в браузере».
|
||||||
|
|
||||||
|
**Так можно, и это стандартный приём.** Chromium-браузеры умеют режим приложения: `--app=URL` открывает окно без адресной строки, вкладок и меню — визуально обычное окно программы.
|
||||||
|
|
||||||
|
Проверено ревьюером на машине владельца:
|
||||||
|
|
||||||
|
```
|
||||||
|
Edge : C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe — есть
|
||||||
|
Chrome : C:\Program Files\Google\Chrome\Application\chrome.exe — есть
|
||||||
|
|
||||||
|
msedge.exe --app=http://127.0.0.1:5800/ --window-size=1400,900
|
||||||
|
-> окно приложения открылось, адресной строки нет
|
||||||
|
```
|
||||||
|
|
||||||
|
## Как это должно работать целиком
|
||||||
|
|
||||||
|
Один ярлык делает три вещи по порядку:
|
||||||
|
|
||||||
|
1. поднимает веб-сервер, если он ещё не запущен;
|
||||||
|
2. дожидается готовности — опрашивает `GET /api/health`, а не спит фиксированное время;
|
||||||
|
3. открывает окно приложения на `http://127.0.0.1:<порт>/`.
|
||||||
|
|
||||||
|
Закрытие окна **не должно оставлять висящий сервер**. Решите, как: сервер завершается вместе с окном, либо живёт как фоновая служба и ярлык к нему просто подключается. Второе удобнее для сервера владельца, первое — для десктопа. Выберите и обоснуйте в отчёте.
|
||||||
|
|
||||||
|
Команда запуска сервера сейчас:
|
||||||
|
|
||||||
|
```
|
||||||
|
python -m antigravity_provider.router.web
|
||||||
|
```
|
||||||
|
|
||||||
|
Порт по умолчанию 5800, слушает `127.0.0.1`. Настройки читаются из `hub_settings.json` (`web_api_host`, `web_api_port`, `web_api_token`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0-1. Windows
|
||||||
|
|
||||||
|
**Опираться на существующий установщик**, а не писать новый: `installer/HermesHubSetup.cs` — 1043 строки, в нём уже есть обнаружение Hermes, экран переустановки с показом версий, зеркальное развёртывание `MirrorDirectoryRecursive`, флаги `/silent`, `/uninstall`, `/repair`, `/reinstall` и запись `deployment_manifest.json`. Всё это переиспользуется.
|
||||||
|
|
||||||
|
Что добавить:
|
||||||
|
|
||||||
|
1. **Ярлык, открывающий веб-интерфейс окном приложения.** Существующий ярлык на десктопное приложение сохранить: десктоп остаётся рабочим и удалять его никто не просил. Итого два ярлыка с понятными именами, либо один на веб и один на десктоп в подпапке меню — на ваше усмотрение, но владелец должен понимать, что чем открывается.
|
||||||
|
|
||||||
|
2. **Поиск браузера в порядке**: Edge, Chrome, затем любой Chromium из реестра. **Если ни одного нет — не молчать**: показать понятное сообщение и предложить открыть обычный браузер по адресу. Неработающий ярлык хуже отсутствующего.
|
||||||
|
|
||||||
|
3. **Иконка окна.** В режиме `--app` окно берёт иконку из профиля браузера. Если удастся задать свою через `--user-data-dir` с отдельным профилем — хорошо, но **это не обязательное требование**: не тратьте на него больше часа и напишите в отчёте, чем кончилось.
|
||||||
|
|
||||||
|
4. Флаг тихой установки должен ставить и веб-ярлык тоже.
|
||||||
|
|
||||||
|
## P0-2. Linux
|
||||||
|
|
||||||
|
Здесь всё пишется с нуля, но проще: ни реестра, ни `pythonw`, ни возни с venv чужого приложения.
|
||||||
|
|
||||||
|
1. **Скрипт установки** `installer/install-linux.sh`: проверяет Python и Hermes, ставит зависимости, разворачивает плагин зеркалом (удаляя устаревшие файлы, как это делает Windows-версия), пишет `deployment_manifest.json`.
|
||||||
|
|
||||||
|
2. **`.desktop`-файл** в `~/.local/share/applications/` — это и есть «ссылка на открытие окна в браузере», о которой просит владелец. Запускает тот же скрипт: сервер, ожидание готовности, затем браузер.
|
||||||
|
|
||||||
|
3. **Порядок поиска браузера на Linux**: `google-chrome`, `chromium`, `chromium-browser`, `microsoft-edge`. Ни одного не нашлось — открывать `xdg-open` обычным браузером и **сказать пользователю, что окно будет с адресной строкой**, а не притворяться, что всё как задумано.
|
||||||
|
|
||||||
|
4. **Учесть headless.** У владельца Ubuntu Server с Xubuntu: он может работать и через SSH без экрана. Если `DISPLAY` и `WAYLAND_DISPLAY` пусты — браузер не запускать, а **напечатать адрес и подсказать проброс порта**:
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh -L 5800:127.0.0.1:5800 user@server
|
||||||
|
```
|
||||||
|
|
||||||
|
Это честный путь, и он единственный рабочий для аккаунтов Antigravity и Claude: их авторизация требует redirect на localhost и на сервере без экрана не работает в принципе. Codex, Grok и OpenCode подключаются без проброса.
|
||||||
|
|
||||||
|
5. **Удаление** `installer/uninstall-linux.sh`: убирает программу и `.desktop`, но **не трогает** `~/.hermes/config/router_profiles.yaml`, каталоги профилей, `hub_settings.json`, журналы и телеметрию. То же правило, что и в Windows-версии.
|
||||||
|
|
||||||
|
## P0-3. Порт путей уже почти сделан
|
||||||
|
|
||||||
|
`paths.py` кроссплатформенный: при отсутствии `LOCALAPPDATA` уходит в `~/.hermes`. A15 провёл через него семь из восьми мест. Осталось `hermes_hub_app.py:37` — **это чинит A17, не трогайте**.
|
||||||
|
|
||||||
|
В `agy_subprocess.py` два упоминания `LOCALAPPDATA` уместны: строка 41 — комментарий, строка 414 — список переменных окружения для подпроцесса на Windows.
|
||||||
|
|
||||||
|
## P0-4. Проверка на настоящем Linux обязательна
|
||||||
|
|
||||||
|
Заявления «должно работать» не принимаются. Если под рукой нет машины — **сказать об этом прямо в отчёте**, и проверку сделает владелец на своём сервере. Это нормальный исход, а вот необоснованное «проверено» — нет.
|
||||||
|
|
||||||
|
Минимум, что должно быть проверено вживую или явно отмечено как непроверенное:
|
||||||
|
|
||||||
|
- установка проходит на чистой Ubuntu;
|
||||||
|
- `.desktop` появляется в меню и запускает окно;
|
||||||
|
- при пустом `DISPLAY` печатается подсказка про проброс порта, а не падает;
|
||||||
|
- удаление сохраняет данные пользователя.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
- Ваши файлы: `installer/**`, `launcher/**`, `scripts/install*.ps1`, `scripts/uninstall*.ps1`, новые `installer/*-linux.sh`, `docs/` в части установки. **Не ваши:** `src/**` целиком — там работают A17 и A18.
|
||||||
|
- Понадобилась правка в `src/` — скажите, будет заказана отдельно.
|
||||||
|
- Данные пользователя при переустановке и удалении не трогать.
|
||||||
|
- Тег `v0.1.1` не создавать.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
1. Ветка в `origin`, `git status` чист.
|
||||||
|
2. Windows: установщик создаёт ярлык, открывающий веб-интерфейс окном приложения без адресной строки; ярлык десктопа сохранён.
|
||||||
|
3. Ярлык поднимает сервер, **дожидается `/api/health`** и только потом открывает окно.
|
||||||
|
4. Закрытие окна не оставляет висящий процесс сервера; выбранное поведение обосновано в отчёте.
|
||||||
|
5. Браузер не найден — понятное сообщение и запасной путь, а не тишина.
|
||||||
|
6. Linux: скрипт установки, `.desktop`, скрипт удаления; зеркальное развёртывание удаляет устаревшие файлы.
|
||||||
|
7. При пустом `DISPLAY` печатается адрес и команда проброса порта.
|
||||||
|
8. Удаление сохраняет `router_profiles.yaml`, профили, `hub_settings.json`, журналы.
|
||||||
|
9. Тихая установка ставит веб-ярлык.
|
||||||
|
10. В отчёте отдельно сказано, что проверено на настоящем Linux, а что нет.
|
||||||
|
11. Сборка Windows-установщика без предупреждений компилятора; `ruff check .` чисто.
|
||||||
|
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`.
|
||||||
|
|
||||||
|
## Главное
|
||||||
|
|
||||||
|
Владелец должен запустить один ярлык и увидеть окно программы — без консоли, без адресной строки, без чтения инструкций. На сервере — открыть адрес и получить то же самое. Всё остальное в этом задании обслуживает эти два сценария.
|
||||||
|
|
||||||
|
## Порядок сдачи
|
||||||
|
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.
|
||||||
Loading…
Reference in a new issue