hermes-hub/agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md
Hermes Team 1b03f03ea5 docs(agents): задания A32 и A34 — восстановление подключения аккаунтов, OpenRouter и NVIDIA, удаление десктопа
Проверка кандидата A28-A31 в браузере вскрыла блокирующую регрессию: при
переписывании клиента в A29 функции удалили, а вызовы в разметке оставили.
Консоль на живой сборке:

    openAddAccountWizard is not defined     «+ Добавить аккаунт» не работает
    checkUpdates is not defined             падает при каждой загрузке
    handleNodeAccountChange                 не сменить аккаунт у агента
    handleNodeModelChange                   не сменить модель
    handleRefreshProviderModels             не обновить список моделей

Подключить аккаунт в новой сборке невозможно, назначить агенту тоже. При этом
startDeviceAuth и startRedirectAuth в коде остались, но не вызываются ниоткуда.

A34 собирает в один порядок: восстановление подключения (блокирует остальное),
адаптеры OpenRouter и NVIDIA с несколькими аккаунтами, интерфейс автопоиска
локальных серверов поверх готовой серверной части, и удаление десктопа по A32.

Задание опирается на ветку review/a28-a31-fixes, где лежат исправления
ревьюера, и перечисляет их отдельным разделом, чтобы не переделывались.

A32 добавлен в репозиторий: он был написан ранее, но остался только локально.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 20:48:11 +07:00

177 lines
17 KiB
Markdown
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.

# Задание A34: восстановить подключение аккаунтов, добавить OpenRouter и NVIDIA, удалить десктоп
## Дата поступления
2026-08-30
## База
Работать **поверх ветки ревьюера** `review/a28-a31-fixes` (`529192b`), а не поверх `main` и не поверх своей прошлой ветки. В ней уже лежат A28A31, слитые с `main`, плюс исправления ревьюера.
```
git fetch origin --prune
git checkout -b antigravity/a34-restore-and-providers origin/review/a28-a31-fixes
```
В `main` напрямую не пушить. В конце — push, `git log --oneline -1`, `git status` чистый.
## Порядок исполнения
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора.
Пункты выполняются **по порядку**. P0-1 блокирует всё остальное: пока нельзя подключить аккаунт, ни новых провайдеров, ни автопоиска проверить не на чем.
---
## Что уже сделано ревьюером — не переделывать
Снято исполнением на кандидате A28A31 и исправлено в `529192b`:
1. **Дублирование ролей.** `RoleRegistry.migrate_legacy_roles` существовала, но не вызывалась ниоткуда; интерфейс показывал 19 агентов вместо 13, шесть пар неотличимы по названию. Миграция подключена, порядок обработки исправлен, цепочки владельца сохраняются. Проверено на его живой конфигурации: 19 → 13, цепочки совпадают.
2. **Сохранённый workflow** мигрируется вместе с ролями, иначе рёбра ссылались на исчезнувших агентов.
3. **Поиск локальных серверов** — новый модуль `router/local_discovery.py` и действие `discover_local_models`. Серверная часть готова и проверена; **не хватает только интерфейса** (P0-3).
4. **Глобальный мьютекс Antigravity** снят ранее (`7e83c38`): три параллельных вызова занимали 3.01 с, стали 1.00 с. Возвращать нельзя, есть тест.
5. **CORS** закрыт по умолчанию (`c35bc48`). Список источников — настройка `web_api_allowed_origins`.
---
## P0-1. Восстановить подключение аккаунтов — блокирующее
При переписывании клиента в A29 **функции удалили, а вызовы оставили**. Проверено в браузере на живом кандидате, консоль:
```
openAddAccountWizard is not defined ← «+ Добавить аккаунт» ничего не делает
checkUpdates is not defined ← падает при каждой загрузке страницы
```
Полный список повисших обработчиков — определений 0, вызовы есть:
```
openAddAccountWizard нельзя подключить аккаунт
handleNodeAccountChange нельзя сменить аккаунт у агента в «Обзоре»
handleNodeModelChange нельзя сменить модель
handleRefreshProviderModels нельзя обновить список моделей
checkUpdates проверка обновлений падает на загрузке
```
При этом `startDeviceAuth` и `startRedirectAuth` **в коде остались и работают**, но не вызываются ниоткуда — стали мёртвыми. Их надо не писать заново, а связать с восстановленным мастером.
Требуется вернуть работоспособность каждому пункту списка. Действия на сервере существуют и менять их не нужно:
```
add_account, start_device_auth, poll_device_auth,
start_redirect_auth, submit_redirect_callback, poll_redirect_auth,
assign_role, set_model, refresh_models, check_updates
```
Что мастер обязан уметь, по провайдерам:
- **Grok, OpenAI Codex** — код устройства. Адрес и код приходят **от провайдера**, подставлять свои нельзя.
- **Antigravity, Claude** — вход по ссылке с возвратом. Ссылку можно открыть **на любой машине**. Принимается и полный адрес возврата, и один только код. При работе с другой машины показывается готовая команда проброса порта возврата.
- **Локальный сервер** — адрес и необязательный ключ, плюс автопоиск из P0-3.
- **Выбор слота обязателен и делается владельцем.** Автоподбор ошибается: `find_free_slot` определяет занятость по файлу учётных данных, а `agy` на Windows держит их в keyring, поэтому все слоты выглядят свободными и всегда возвращается первый. Вход затирал бы работающий аккаунт. В списке слотов видно, какие заняты и кем, и участвует ли слот в маршрутизации.
**Проверять исполнением, а не глазами.** Откройте страницу, нажмите каждую кнопку, посмотрите консоль. Ни одного `ReferenceError` при загрузке и при работе.
## P0-2. OpenRouter и NVIDIA, аккаунтов по несколько
Владелец подключил их в Hermes напрямую, мимо хаба: «главный кодекс стоит, подключил себе нвидеа и опенроутер и грок». Хаб их не видит — адаптеров нет, упоминаний в коде нет вовсе.
Оба **OpenAI-совместимы**, поэтому образец есть: `adapters/local_adapter.py` и `adapters/deepseek_adapter.py` работают ровно так же — `POST {base_url}/chat/completions`, `GET {base_url}/models`.
1. **Адаптеры** `openrouter` и `nvidia`. Базовый адрес — **настройка, не константа**. Для OpenRouter это `https://openrouter.ai/api/v1`, у NVIDIA свой; но зашивать нельзя, владелец может использовать прокси.
2. **Несколько аккаунтов на провайдера**, без потолка. Потолок в A26 уже снят: `find_free_slot` выдаёт идентификаторы сама, когда предопределённые кончились (`codex-4`, `codex-5`). Сделать так же.
3. **Ключ вводится в мастере** и хранится там же, где ключи прочих провайдеров. В снапшот, в журнал и в `/api/settings` он попадать не должен — тест на это уже есть.
4. **Обнаружение моделей** через `GET /models`. У OpenRouter список большой; показывать надо тот, что вернул провайдер, а не подмножество из головы.
5. **Квоты.** OpenRouter отдаёт остаток кредитов, у NVIDIA свои лимиты. Отдаёт — показывать; не отдаёт — **«Н/Д» с причиной**, а не ноль и не пустая полоса, которую можно принять за исчерпание.
6. **Логотипы** провайдеров у владельца есть в каталоге с макетами. Файла нет — нейтральная заглушка, а не чужой знак.
## P0-3. Автопоиск локальных моделей в интерфейсе
Серверная часть готова ревьюером, писать её заново не нужно.
```
router/local_discovery.py discover_local_servers()
action_handler действие discover_local_models
```
Опрашивает Ollama 11434, LM Studio 1234, llama.cpp 80808082, vLLM 8000, Jan, GPT4All, Text Generation WebUI — параллельно, девять портов за 1.5 с. Возвращает только ответившие, со списком моделей от самого сервера.
Требуется кнопка **«Найти на этом компьютере»** в шаге подключения локального провайдера:
1. Нажатие — опрос, показ найденного: имя сервера, адрес, список моделей.
2. Выбор найденного заполняет адрес; вводить руками по-прежнему можно.
3. **Ничего не найдено — так и сказать**, с подсказкой запустить Ollama, LM Studio или llama.cpp либо ввести адрес вручную. Пустой список это результат, а не ошибка.
4. Порт, занятый чужим сервисом, показывается **с причиной** — иначе владелец будет гадать, почему заведомо работающий сервер не виден.
5. Опрос идёт в фоне, интерфейс не блокируется.
Учесть: хаб может работать на сервере, а браузер у владельца на другой машине. Поиск идёт **там, где работает хаб**, и это надо сказать в интерфейсе прямо, иначе результат будет непонятен.
## P0-4. Удаление десктопа (задание A32 целиком)
Выполняется **после** P0-1: пока веб не подключает аккаунты, удалять десктоп нельзя.
Полный текст — `agents/inbox/2026-08-25-A32-remove-desktop.md`, здесь коротко:
```
router/ui/** 20 файлов, ~6 910 строк
hermes_hub_app.py ~1 345 строк, CustomTkinter
cli_commands.py:481 команда запуска десктопа
HermesHubSetup.cs:575 ярлык «Hermes Hub (Desktop).lnk»
HermesHubSetup.cs:140 проверка зависимостей ТРЕБУЕТ customtkinter
pyproject.toml:40 customtkinter>=6.0.0
```
Сначала **таблица паритета** «умение десктопа → где в вебе → проверено», и только потом удаление. Непокрытое умение не удалять, а назвать в отчёте.
Отдельно: в `b4ae08e` появился обход «GUI helpers importable without customtkinter». После удаления десктопа эта прослойка не нужна — снять её, а не оставлять.
Обновление пойдёт **поверх установок с десктопом** на трёх машинах владельца: старый ярлык обязан убираться, учётные данные, настройки и цепочки ролей — уцелеть.
## P0-5. Проверка на живой установке
1. Собрать оба установщика, поставить на Windows и на Linux.
2. Подключить хотя бы по одному аккаунту каждого потока: код устройства, вход по ссылке, локальный сервер.
3. Разложить аккаунты по ролям и убедиться, что порядок переживает перезапуск.
4. Проверить обновление поверх старой установки.
## P0-6. Аудит вторым проходом
1. **Повисшие вызовы.** Собрать все `onclick`/`onchange` и убедиться, что каждая функция определена. Именно этот класс дефекта и пропустили в A29: разметка звала пять функций, которых нет.
2. **Консоль браузера чистая** при загрузке и при работе.
3. **Выдуманные значения.** Особый риск в P0-2: квоты и списки моделей новых провайдеров. Ни одного числа, которого не дал провайдер.
4. **Ключи не утекают** в снапшот, журнал и `/api/settings`.
5. **Мьютекс Antigravity не вернулся** — есть тест, он должен проходить.
6. **Побочные изменения** объяснить.
7. **Пропущенный пункт назвать пропущенным.**
---
## Ограничения
- Не переделывать то, что перечислено в разделе «сделано ревьюером».
- Без сборки, без npm, без фреймворка — решение обосновано в контракте.
- Действия только через `action_handler`; новые — с правкой `docs/web-api/CONTRACT.md`.
- Правило честности без исключений: нет данных — «Н/Д» и причина.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист.
2. В консоли браузера нет `ReferenceError` ни при загрузке, ни при работе; все обработчики определены — проверено списком.
3. Аккаунт подключается всеми тремя потоками; слот выбирает владелец, занятые видны.
4. Аккаунт назначается агенту и меняется модель — из «Обзора» и из «Маршрутизации»; переживает перезапуск.
5. OpenRouter и NVIDIA подключаются, аккаунтов больше трёх на провайдера; ключи не утекают; модели берутся у провайдера.
6. Квоты новых провайдеров показаны настоящие либо «Н/Д» с причиной.
7. Кнопка автопоиска находит запущенные локальные серверы; пустой результат объяснён; чужой сервис на порту назван с причиной.
8. `router/ui/**` и `hermes_hub_app.py` удалены, `customtkinter` из зависимостей убран, ярлык один; обновление поверх старой установки сохраняет данные.
9. Таблица паритета приложена.
10. `ruff check .` чисто; релизный гейт не ухудшен.
11. **Скриншоты:** мастер на каждом из трёх потоков, автопоиск локальных, «Обзор» с назначением аккаунта, «Маршрутизация».
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**.
## Главное
Сейчас в новой сборке нельзя подключить ни одного аккаунта и нельзя назначить его агенту — разметка зовёт пять функций, которых в коде нет. Всё остальное в задании бессмысленно, пока это не восстановлено.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.