Раздел: App
Идентификатор:tools· Экран:ui/tools/ToolsScreen.kt
Снимок: 2026-10-11
1. Зачем и что это
| Блок | Зачем агенту | Плейсхолдер | |
|---|---|---|---|
| GitHub | поиск кода (webget_github_search) | ghp_… | |
| AgentSearch | основной веб-поиск (backend + MCP) | — | |
| Генерация изображений | image_generate / image_edit (Krea 2 Turbo и Flux Klein 4B через y83.media) | `1 | …` |
| Context7 | живая документация библиотек по MCP | ctx7sk… | |
| GitLab | публикация репозиториев (адрес и токен — на своём экране) | — |
Это единственный экран хаба, который пишет в backend и перезапускает сервисы: после сохранения нового GitHub-токена backend сам перезапускает прокси и codex app-server, чтобы MCP подхватил конфигурацию. Пользователю не нужно ничего перезапускать руками.
2. Как попасть
Хаб инструментов → секция App → карточка «Интеграции» (hub_tools_title, подпись «Интеграции GitHub и AgentSearch», тайл на главной — «Интегр.»).
| Откуда ещё | Путь |
|---|---|
Параметр embedded | Отключает терминальную шапку, если экран куда-то встраивают. Сейчас его никто не передаёт: вход всегда из хаба |
| Мост | app_navigate с screen: "tools_settings" |
Маршрут: Screen.kt:154 ("tools" -> Screen.ToolsSettings) → MainRouter.kt:295 (ToolsScreen). Обратите внимание: id инструмента tools, а ключ экрана в мосте — tools_settings (BridgeScreenCatalog.kt:211, MainScreen.kt:192).
3. Экран: элементы и действия
| Элемент | Что делает | Где в коде |
|---|---|---|
Шапка y83@tools | Терминальная: промпт :~$, мета «интеграций 5 · токенов N», loading…/saving…; при embedded = true не рисуется | TerminalHeader |
| Строка статуса | Закреплена под шапкой и не уезжает со списком: итог сохранения и ошибки (вместо Snackbar) | ToggleStatusStrip |
| Спиннер | Пока идёт загрузка настроек | if (loading) |
| Карточка ошибки | Текст ошибки + кнопка «Повторить» | loadError?.let |
| Секции | «поиск и код», «медиа», «документация», «git» | SectionHeader |
| Блок GitHub | Поле токена, «глаз», Test, Remove, кнопка «i» | ToolTokenCard |
| Блок AgentSearch | URL сервера + bearer-токен, Test, Remove, «i» | ToolTokenCard c showUrl = true |
| Блок Image generation | URL эндпоинта + токен, Test, Remove, «i» | ToolTokenCard c showUrl = true |
| Блок Context7 | Ключ, Test, Remove, «i» (инлайн-инструкция стала окном) | ToolTokenCard |
| Карточка GitLab | Состояние подключения (адрес, токен) и переход на экран GitLab, «i» | GitLabShortcutCard |
| Подвал Save | Закреплён внизу, активен только при несохранённых правках | ToolsSaveBar |
Строка состояния токена (TokenStatusRow → StatusDot) показывает три состояния: «Токена нет» (приглушённый маркер), «Токен сохранён» (primary, рядом маска с сервера) и «новый, не сохранён» (primary, пока черновик не записан). Маска приходит с сервера — сам токен приложение никогда не читает.
Черновик считает toolsHasDraft (ui/tools/ToolsHelp.kt): изменённые адреса, непустые поля токенов и флаги удаления. Пока черновик не пуст, подвал показывает «есть несохранённые правки», а выход (стрелка «назад» или системная кнопка) спрашивает «сохранить или выйти» — введённый токен не теряется молча.
Кнопка Test у каждого блока отправляет POST /api/v1/tools/test с несохранёнными значениями полей, поэтому токен можно проверить до сохранения.
4. Возможности подробно
Семантика пустых значений
Ключевая тонкость запроса — image_url, image_token, context7_token приходят указателями (*string), а остальные поля — строками:
| Поле | Семантика |
|---|---|
github_token, agentsearch_url, agentsearch_token | обычная строка: пустая строка = записать пустое |
image_url, image_token, context7_token | null = не трогать; "" = очистить; значение = записать |
На экране это выражено флагом imageTokenClear / context7TokenClear: пока поле не меняли и флаг не выставлен, в запрос уходит null, и сохранённый токен на сервере остаётся целым. Remove выставляет флаг и сразу сохраняет, то есть удаление — это сохранение пустой строки, а не отдельный вызов.
Валидация на клиенте
Перед сохранением URL AgentSearch обрезается пробелами и проверяется на ://: непустой URL без схемы блокирует сохранение, и в строке статуса появляется «Адрес должен быть полным — со схемой https://» — типичная ошибка ввода «localhost:8080» без префикса. Ключ Context7 проверяется на сервере (ctx7sk): ответ с ошибкой приходит из PUT /api/v1/tools/settings.
Проверка токенов
POST /api/v1/tools/test делает реальный сетевой запрос с таймаутом 20 секунд (context.WithTimeout) и клиентом на 15 секунд:
| tool | Запрос | Успех | |
|---|---|---|---|
github | GET https://api.github.com/user | 200 → «token works» | |
agentsearch | GET <url>/search?q=test&max_results=1 | 200 → «server responds» | |
image | GET <url>/tokens | 200 → «token works» | |
context7 | context7.Probe | сообщение + `mode: token | anonymous` |
Пустые значения в запросе не мешают проверке: код берёт сохранённые настройки как fallback (githubTokenFull(), toolscfg.Get(), toolscfg.ImageConfig()), так что кнопка Test работает и для давно сохранённого токена. 401/403 превращаются в «token rejected (Unauthorized)» через 502 — чтобы отличать от сетевой ошибки. Что-то кроме четырёх известных значений → 400 «tool must be github, agentsearch, image, or context7».
Сохранение и перезапуск
SaveToolsSettings на сервере делает по шагам:
applyGitHubToken— пишет токен и перезапускает сервисы (вrestartedпопадает имя перезапущенного компонента);toolscfg.Set(agentsearch_url, agentsearch_token)— пишетtools.json;toolscfg.SetImage(image_url, image_token)— только если указатели неnull;- Context7: проверка префикса
ctx7sk,toolscfg.SetContext7Token, затемcontext7.ApplyToConfig(codexConfigPath())— секция конфига переписывается сразу, потому что ключ читает прокси, а в файл он не попадает; - если менялся Context7 —
h.restartCodex()добавляет «codex» вrestarted.
Приложение показывает строкой статуса «Настройки сохранены» и, если что-то перезапускалось, дописывает « перезапущено: <компоненты>». Ошибка рестарта codex не проваливает сохранение — она пишется в лог как Warn.
Инструкции по токенам (кнопка «i»)
У каждого блока есть кнопка «i» — она открывает окно HelpWindow (ui/components/HelpWindow.kt) с терминальной шапкой y83@tools/help: нумерованные шаги, блок «Важно» и кнопка перехода на страницу выдачи токена (через onOpenUrl — встроенный браузер, не внешний). Спецификации инструкций — GitHubHelp/AgentSearchHelp/ImageHelp/Context7Help в ui/tools/ToolsHelp.kt; тексты — в strings_tools.xml. Инструкция по токену GitLab общая (ui/gitlab/GitLabHelp.kt): её открывают и экран GitLab, и карточка GitLab на этом экране, ссылка собирается из адреса сервера.
Локализация
Все подписи экрана живут в res/values/strings_tools.xml (RU — дефолт, values-en — английский), в коде — stringResource/ResText.get. До 0.8.136 подписи были английским текстом прямо в коде, поэтому экран оставался английским и в русской локали.
GitHub: миграция из легаси
GitHub-токен живёт в tools.json, но есть legacyProxyConfigPath() (~/y83-proxy/config.json, переопределяется Y83_PROXY_CONFIG) — источник одноразовой миграции, потому что сам y83-proxy выведен из эксплуатации. Клиенту наружу отдаётся только github_has и маска.
5. Данные: БД, файлы, настройки
| Что | Где лежит | Как меняется | Кто читает |
|---|---|---|---|
| GitHub-токен | ~/.y83-ws/tools.json | PUT /api/v1/tools/settings | прокси, MCP webget |
| AgentSearch URL + bearer | тот же tools.json | там же | webget, MCP web_search/web_fetch |
| Image URL + токен | тот же tools.json | там же | MCP image_generate / image_edit / image_status / image_jobs / image_cancel / image_quota и окно правки в приложении (POST /api/v1/image-jobs) |
| Context7 ключ | тот же tools.json + секция в ~/.codex/config.toml | там же + context7.ApplyToConfig | MCP mcp__context7__* |
Файл: backend/internal/toolscfg (const fileName = "tools.json", функция Path()). Значения из файла имеют приоритет над переменными окружения — это отражено в KDoc модуля. Маскирование — maskToken.
6. Backend: REST/роуты
| Метод и путь | Назначение | Обработчик |
|---|---|---|
GET /api/v1/tools/settings | текущее состояние (флаги has, маски, URL) | webget.ToolsSettings, backend/internal/handlers/webget/settings.go:85 |
PUT /api/v1/tools/settings | сохранить токены/URL, перезапустить сервисы | webget.SaveToolsSettings, там же:107 |
POST /api/v1/tools/test | проверить токен реальным запросом | webget.ToolsTest, там же:183 |
GET /api/v1/webget/settings | legacy: только GitHub-токен и путь к конфигу | webget.Settings, там же:37 |
PUT /api/v1/webget/settings | legacy: сохранить только GitHub-токен | webget.SaveSettings, там же:48 |
Роуты объявлены в backend/internal/api/v1/routes.go:102–104.
7. Мост и MCP
| Что | Как | Примечание |
|---|---|---|
| Открыть экран | app_navigate с screen: "tools_settings" | BridgeScreenCatalog.kt:211 |
| Проверить GitHub-поиск | MCP web_search (движок agentsearch) | требует AgentSearch |
| Картинки | MCP image_generate / image_edit (маска mask_path/mask_url, прямоугольники, outpaint, style_refs, LoRA) + image_status/image_jobs/image_cancel/image_quota | требует image-токен; тот же токен использует окно правки в просмотрщике |
| Документация библиотек | MCP mcp__context7__resolve_library_id, mcp__context7__query_docs | работает и анонимно |
Отдельной bridge-команды для чтения/записи токенов нет — только открытие экрана.
8. Настройки, конфиг, квоты и лимиты
| Параметр | Значение | Где задаётся |
|---|---|---|
| Файл конфигурации | ~/.y83-ws/tools.json | toolscfg.Path() |
| Легаси-источник GitHub-токена | ~/y83-proxy/config.json или $Y83_PROXY_CONFIG | legacyProxyConfigPath() |
| Таймаут проверки | 20 с контекст, 15 с HTTP-клиент | ToolsTest |
| Ограничение ответа | 1 МиБ (1<<20) | bearerGet |
| Формат Context7-ключа | префикс ctx7sk | context7.IsKey |
| Дашборд Context7 | https://context7.com/dashboard | CONTEXT7_KEYS_URL |
9. Ловушки и ограничения
- Сохраняются все четыре блока сразу. Кнопка одна на весь экран, поэтому несохранённый текст в поле другого блока уедет вместе с остальным — сбой в одной строке (например, битый URL AgentSearch) отменяет сохранение целиком.
- Токен не читается обратно. После сохранения поле пустое, видно только маску; чтобы «проверить, что сохранено», нужно нажать
Test. - Пустая строка в AgentSearch-токене означает «стереть», в отличие от
image_token/context7_token, где пустая строка значит то же, но null — «не трогать». Смешение этих двух семантик легко приводит к потере ключа Context7 при невинном сохранении. - Контекст7 без ключа работает анонимно — с ограничением частоты. То есть пустой результат
Testсmode: "anonymous"это не ошибка конфигурации. - Экран полностью на английском, строки захардкожены — локализации нет.
- URL AgentSearch обязан быть абсолютным; относительный путь отклоняется ещё до сети.
- Рестарт codex после смены Context7 логируется как Warn, а не падает: можно получить «Tools saved.» и при этом неработающую MCP-секцию Context7 до ручного рестарта app-server.
10. Источники правды
app/src/main/java/com/y83/ws/ui/tools/ToolsScreen.kt— экран.app/src/main/java/com/y83/ws/data/repo/ToolsRepository.kt,data/api/BackendApiSettings.kt— клиент REST.backend/internal/handlers/webget/settings.go— все три обработчика и легаси-миграция.backend/internal/toolscfg/— чтение и записьtools.json.backend/internal/context7/—IsKey,Probe,ApplyToConfig.backend/internal/api/v1/routes.go:102–104— роуты.