跳到主要内容

dsh-qa-integrations

已验证

@yadsh/dsh-qa-integrations · v0.10.3 · MIT · Web 界面

Principal-scoped, encrypted user integrations for DSH QA Surface

安装

dsh plugin add @yadsh/dsh-qa-integrations

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

发布到 npm 但没有公开仓库。安装前请检查包内容。

标签

说明文档

dsh-qa-integrations

Персональные интеграции для @yadsh/dsh-qa-surface. Плагин подключает Bitrix24 через URL входящего вебхука, GitLab через personal access token, TeamCity через access token, Jira и Confluence — через API-токен Atlassian, Test IT — через API-токен системы управления тестированием, а Weblate — через API-токен платформы локализации, после чего даёт агенту набор read-only инструментов, ограниченный и правами подключения, и персональной политикой пользователя; единственное исключение — комментарий в таймлайн Bitrix24, который монтируется только явным флагом оператора. У всех семи интеграций подключение может работать не только от личного токена, но и от сервисного токена развёртывания — общего read-only аккаунта с жёстким потолком режима (раздел «Сервисные токены»).

Плагин показывает подключения двумя монтированиями одного и того же набора карточек: отдельным разделом «Интеграции» в пользовательских настройках QA и секцией конфигурации самого плагина на панели «Плагины» оригинального DSH (слот plugins.bundle.config, ключ — имя пакета). Панель рисуется клиентом и не читает loopback-only каталог Host settings: в установленном @deepseek-ai/dsh-client-ui-plugin-manager 0.1.7-rc.2 ветки isLoopback нет вовсе, а единственный loopback-выбор на стороне настроек выбирает режим записи — host или memory — и не зависит от того, на каком слоте висит карточка. Поэтому секция доступна и в LAN-браузере, где тот каталог намеренно отключён. Слот выбран по AGENTS.md («Choosing the registration point»): карточка конфигурации плагина сидит на панели «Плагины», а не в диалоге настроек. Оболочку карточка не рисует: на ряду панели «Плагины» страницу, заголовок строки, id строки и описание выдаёт хост, а карточка монтирует только тело — own shell остаётся за settings.section и settings.plugins.tab (решение владельца от 01.10, влитое в контракт #684). Кольцо фокуса строится из хозяйских токенов --dsw-focus-ring-width / --dsw-focus-ring-color с fallback у каждой половины. Клик-обход живого не-loopback стенда — пункт приёмки переноса. Она читает аккаунт через клиентский сервис qaUserSession плагина @yadsh/dsh-qa-surface: без входа в QA ничего не показывает и не делает запросов. Токен вводится один раз, шифруется на Host и никогда не возвращается браузеру. Выбор пользователя, integration id, secret id или токена отсутствует в model-visible схемах: principal берётся из DSH-сессии, а Bitrix user id — из сохранённой записи интеграции.

Операторская карточка настроек

Кроме двух пользовательских монтирований, плагин рисует операторскую карточку по своей конфигурации — секцией конфигурации своей строки профиля на панели «Плагины» (слот plugins.row.config, ключ @yadsh/dsh-qa-integrations#qa-integrations), под секцией аккаунта. На хосте 0.1.7 роль settings namespace играет id строки профиля (qa-integrations), а доступными для браузера становятся те поля, чей узел схемы помечен .volatile(); карточка берёт форму этого входа через ctx.configForms и появляется вместе с рядом, не дожидаясь Remote-описания развёртывания. Ключ нового места собран из того же id строки и имени пакета, поэтому переехало только место рендера: значение, сохранённое до переноса, читается после. Обе карточки живут на панели «Плагины», куда не нужен loopback-only каталог Host settings, и каждая монтирует только тело — рамку, заголовок и раскрытие рядом рисует сама страница плагина. Операторская карточка — третья поверхность того же бандла, и она не требует ни входа в QA, ни Remote-описания развёртывания: оператор включается и настраивает плагин из этой карточки, даже когда плагин ещё выключен.

Карточка читается сверху вниз: у каждой секции в свёрнутом виде строка состояния (включён ли провайдер, сколько подключений настроено, сколько возможностей открыто), а внутри — четыре подписанных блока: «Провайдер» (выключатели провайдера), «Подключение» (инстансы, сайты, адреса и сетевые политики во всю ширину), «Что доступно агенту» (чек-лист возможностей, флажок перед подписью) и свёрнутые «Ограничения и повторы» (потолки, таймауты, повторы при ошибках). Подпись поля связана с самим полем: клик по названию ставит курсор в него. Переключатель, до которого сервисный токен развёртывания не дотягивается целиком или частично, говорит об этом прямо в чек-листе («с сервисным токеном недоступно: требуется личный аккаунт»): сам по себе включённый флажок ничего оператору не объясняет, а пользователь с отказом читает его как сломанную интеграцию. Заметка появляется только при включённых сервисных доступах и снимается самим переключателем по его пути в конфигурации: копии имени флага в карточке нет, опечатать в ней нечего. tests/client/operator-service-reach.test.ts сверяет таблицу заметок с классификацией каждого каталога, а tests/client/operator-card.test.tsx считает заметки в отрисованной карточке — карточка не сможет разойтись с тем, что провайдер на самом деле разрешает общему аккаунту.

Карточка покрывает всю разрешённую резолверами конфигурацию: общие ручки (enabled, таймаут, потолки ответа и аудита, пути хранилища и мастер-ключа, суффиксы порталов Bitrix24), у каждого провайдера — выключатель, возможности, инстансы/сайты (строки id/название/адрес) и лимиты, у TeamCity — адрес сервера и сетевую политику, у Jira — псевдонимы полей, плюс сервисные доступы (профили с ресурсными границами и deny-политикой) и замены встроенных подсказок получения токена (credentialHelp). Половины GitLab CI — ciMetadataRead и ciLogsRead — это в карточке два переключателя, а не один ciRead: резолвер отвечает половинами отдельно и складывает в них старое ciRead, только пока ни одна из них не названа явно. Единая ручка этого состояния выразить не могла: половина, выключенная отдельно от другой, читалась «включено», а правка по старому имени переставала действовать, как только вторая половина названа. Поэтому карточка читает каждую половину в том же порядке, что и резолвер, — половина ?? ciRead ?? значение по умолчанию. Каждое поле помечает, что пользовательский слой namespace переопределяет композиционную строку профиля, одна кнопка очищает слой целиком. Хост проверяет правку по схеме и отвергает то, что схема не позволяет; кросс-полевые ограничения (политика хостов TeamCity, дубликат id в списке инстансов) схема выразить не может, и их держат резолверы: значение, которое они не принимают, остаётся в документе профиля, а запущенный сервис сохраняет прежнее состояние и пишет config.rejected в лог.

Правка применяется к запущенному сервису сразу: Loader коммитит её в volatile-ссылки самой строки и сообщает об этом её фибре (loader/volatile-update), брокер перепривязывается к свежему набору провайдеров, а монтирование инструментов следует за выключателем плагина и единственным флагом записи. Хранилище подключений и мастер-ключ — сознательное исключение: подключения и зашифрованные секреты живут по путям загрузки, поэтому их правка предупреждает в логе и вступает в силу при следующем рестарте Host. Без отдаваемого namespace карточка объясняет это одной строкой вместо пустой секции, а плагин грузится от своей композиционной строки — настройки профиля ему не нужны, чтобы работать.

Структура

Один каталог — одна интеграция. Всё, что знает про Bitrix24, GitLab, TeamCity, Jira, Confluence, Test IT и Weblate, лежит в своём каталоге src/providers/<id>/: каталог возможностей и операций, построение запросов, HTTP-граница, срез конфига и тулы. У TeamCity отдельно лежат locator-синтаксис, обработка логов, политика артефактов и сеть; у Jira — JQL и ADF; у Confluence — CQL и ADF; у Test IT — политика видов вложений и бюджет их чтения; у Weblate — сборка поискового синтаксиса q. Общий слой — брокер, репозиторий, секреты, реестр сервисных токенов (src/service-credentials/), обвязка тулов — не знает ни одной интеграции по имени: capability это свободная строка, а providers/contract.ts описывает, что должен уметь провайдер. Порядок добавления следующей интеграции и правила гейта пакета — в src/providers/README.md.

Инструменты

CRM:

Tool Что делает
bitrix_search_crm Поиск лидов, сделок, контактов, компаний, счетов и смарт-процессов: по названию, стадии, воронке, свежести, открытости (только сделки), ответственный; порядок сортировки зафиксирован, чтобы страницы не дублировались
bitrix_get_crm_item Полная карточка элемента CRM, включая пользовательские поля и коммуникации
bitrix_get_crm_fields Схема полей типа сущности, включая UF_CRM_* портала
bitrix_get_crm_funnels Воронки (категории) типа сущности
bitrix_get_crm_statuses Расшифровка стадий и справочников (STATUS, DEAL_STAGE, DEAL_STAGE_<id>, SOURCE, …)
bitrix_get_crm_activities Дела CRM: звонки, встречи, письма, задачи; просрочки и незавершённые
bitrix_get_crm_activity Одно дело целиком, включая описание
bitrix_get_crm_timeline Комментарии таймлайна лида, сделки, контакта, компании
bitrix_get_crm_stage_history История движения по стадиям: сколько висело, куда возвращали
bitrix_get_crm_product_rows Товарные позиции: что продаём, количество, цена, скидки
bitrix_find_crm_duplicates Поиск дублей клиента по телефону или e-mail
bitrix_get_crm_requisites Реквизиты контакта или компании: ИНН, КПП, адрес, банк
bitrix_get_call_transcript Готовая AI-расшифровка звонка по делу CRM

Сотрудники и структура:

Tool Что делает
bitrix_get_current_user Чей вебхук подключён
bitrix_search_users Поиск сотрудников по имени, e-mail, подразделению
bitrix_get_departments Подразделения, родительский отдел, руководитель
bitrix_get_user_fields Какие поля сотрудника доступны с этим вебхуком

Чаты и открытые линии:

Tool Что делает
bitrix_search_chats Поиск чатов по названию и участникам
bitrix_get_chat_messages Последние сообщения чата
bitrix_search_chat_messages Поиск по тексту и датам внутри одного чата
bitrix_get_recent_chats Последние диалоги пользователя, счётчики непрочитанного
bitrix_search_chat_users Поиск сотрудника как контакта чата: статус, телефоны
bitrix_find_chat Чат, привязанный к объекту: обсуждение сделки, чат задачи, событие календаря, чат группы
bitrix_get_chat_participants Кто участвует в чате
bitrix_get_chat_user_data Профили участников: имя, должность, телефоны, присутствие
bitrix_get_openline_dialog Диалог открытой линии: участники, связь с CRM
bitrix_get_openline_history История переписки с клиентом в открытой линии

Задачи, календарь, Диск:

Tool Что делает
bitrix_search_tasks Задачи по названию, ответственному, группе, дедлайну
bitrix_get_task Карточка задачи целиком, включая ufCrmTask
bitrix_get_task_history История изменений задачи: что, когда и кем
bitrix_get_task_results Результаты работы по задаче
bitrix_get_task_elapsed_time Затраченное время: кто сколько залогировал
bitrix_get_calendar_events События календаря сотрудника, группы или компании
bitrix_get_calendar_accessibility Занятость сотрудников, чтобы предложить время встречи
bitrix_search_files Поиск по Диску, включая текст внутри документов
bitrix_get_file Метаданные и ссылка на файл Диска
bitrix_get_drives Доступные диски и их id
bitrix_get_storage_items Содержимое корня диска
bitrix_get_folder_items Содержимое папки Диска

Запись (по умолчанию выключена оператором bitrix24.crmCommentWrite):

Tool Что делает
bitrix_add_crm_timeline_comment Один комментарий в таймлайн лида, сделки, контакта или компании; единственная операция записи во всём плагине

Списочные инструменты отвечают единым конвертом { items, pagination }: у модели одна форма ответа вместо шести разных у REST, а курсор следующей страницы не теряется. Методы, возвращающие словари по id (история открытой линии, занятость), спроецированы в упорядоченные массивы.

Инструменты GitLab

Tool Что делает
gitlab_connection_get К какому инстансу и каким пользователем подключён аккаунт
gitlab_projects_list Поиск проектов: путь, ветка по умолчанию, видимость
gitlab_project_get Карточка проекта: описание, namespace, даты
gitlab_repository_tree Содержимое каталогов на выбранном ref, постранично
gitlab_repository_file_get Текст файла с метаданными; бинарное и слишком большое — только метаданные
gitlab_commits_list Коммиты проекта, ветки или одного файла
gitlab_commit_get Один коммит: сообщение, автор, родители, объём изменений
gitlab_compare Сравнение двух refs: коммиты и изменённые файлы
gitlab_search Поиск по проектам, задачам, MR, коммитам, коду и комментариям
gitlab_issues_list Задачи по проекту, состоянию, автору, исполнителю, меткам, датам
gitlab_issue_get Карточка задачи: описание, веха, срок
gitlab_issue_notes_list Комментарии задачи, системные помечены
gitlab_merge_requests_list Merge requests по проекту, веткам, автору, ревьюеру, черновику
gitlab_merge_request_get Карточка MR: описание, ветки, статус слияния, конфликты
gitlab_merge_request_changes_get Изменённые файлы MR с диффами
gitlab_merge_request_discussions_list Обсуждения ревью и их разрешение
gitlab_merge_request_approvals_get Сколько одобрений нужно, сколько есть, кто одобрил
gitlab_merge_request_pipelines_list Пайплайны, привязанные к MR
gitlab_pipelines_list Пайплайны проекта по ветке, статусу, источнику, автору
gitlab_pipeline_get Один пайплайн: статус, длительности, покрытие
gitlab_pipeline_jobs_list Джобы пайплайна: стадия, статус, длительность
gitlab_job_get Одна джоба: причина падения, теги, список артефактов
gitlab_job_log_get Лог джобы: ограничен по размеру и очищен от токенов

Списочные инструменты GitLab отвечают тем же конвертом { items, pagination }; pagination.nextPage — это номер следующей страницы, а не подписанный курсор: состояние между вызовами не хранится, principal и права перепроверяются на каждом вызове.

Диффы (gitlab_compare, gitlab_merge_request_changes_get) отдают список файлов целиком, а текст диффов — в пределах общего бюджета символов; при обрезке ответ помечается diffTruncated. Логи CI обрезаются по лимиту стенда и дополнительно проходят через редакцию секретов: встроенная маскировка GitLab — фильтр, а не гарантия.

Инструменты TeamCity

Tool Что делает
teamcity_connection_get К какому серверу TeamCity и каким пользователем подключён аккаунт, версия сервера
teamcity_projects Проекты: поиск по id и названию, дочерние проекты, архивные
teamcity_build_configs Конфигурации сборки: id, название, проект, пауза
teamcity_builds Поиск сборок по проекту, конфигурации, ветке, статусу, состоянию и датам
teamcity_build Одна сборка: состояние, статус и его текст, агент, кто запустил, времена
teamcity_build_changes Изменения в VCS, попавшие в сборку: ревизия, автор, дата, комментарий
teamcity_build_failures Почему упало: упавшие тесты и проблемы сборки одним ответом
teamcity_build_log Фрагмент лога сборки: конец, начало или строки по поиску
teamcity_queue Очередь сборки: что ждёт агента и на какой ветке
teamcity_investigations Расследования падений: состояние, ответственный, разрешение
teamcity_agents Агенты сборки: подключён, включён, авторизован
teamcity_artifacts Артефакты сборки: имя, путь, размер, время изменения
teamcity_artifact_text Текст небольшого артефакта: отчёты тестов, лог-файлы

Списочные инструменты TeamCity отвечают конвертом { items, pagination }, где pagination.returned — сколько строк вернулось, а pagination.hasMore — что сервер отдал не всё: инструмент никогда не ходит по nextHref сам, поэтому продолжение получается сужением фильтра, а не вторым запросом за спиной у модели. Лимит каждого списка задан спецификацией (сборки и очередь — 50, проекты, конфигурации, изменения, тесты, проблемы, расследования и агенты — 100, артефакты — 200) и не поднимается выше, сколько бы модель ни попросила.

Лог сборки скачивается не больше, чем разрешает стенд (maxLogBytes, по умолчанию 256 КиБ), чистится от управляющих последовательностей терминала, проходит через редакцию секретов и режется до запрошенного числа строк (maxLines, максимум 1000). Ответ различает два случая: truncated — окно не покрыло запрошенные строки, logTruncated — сам лог длиннее скачанного куска, и тогда в режиме tail это конец скачанного, а не конец сборки. Артефакты читаются только текстовые: архивы, образы, бинарники, документы и ключи (zip, jar, png, pdf, pem, …) отказываются по имени до запроса, а бинарное тело отвечает метаданными с binary: true вместо содержимого. Логи и артефакты описаны модели как недоверенные данные: это текст из внешней системы, а не инструкция.

Инструменты Jira

Tool Что делает
jira_get_current_user К какому сайту Jira и каким пользователем Atlassian подключён аккаунт
jira_search_issues Поиск задач: текст, проекты, типы, статусы и их категория, приоритеты, резолюции, компоненты, метки, версии (fix и affected), люди, даты, история поля, кастомные поля
jira_get_issue Карточка задачи: ключ, ссылка, проект, тип, статус, приоритет, люди, метки, компоненты, версии, резолюция, даты и описание
jira_get_issue_comments Комментарии задачи, от новых к старым; ограниченный комментарий помечен видимостью
jira_get_issue_attachments Метаданные вложений: имя файла, тип, размер, автор; файлы не скачиваются
jira_get_available_transitions Доступные переходы статуса и их обязательные поля; переход не выполняется
jira_get_project Карточка проекта: ключ, название, тип, руководитель, описание, ссылка
jira_get_fields Поля сайта: id, имя, признак кастомного поля, тип и JQL-имена

Модель не получает JQL: фильтры в jira_search_issues типизированные, а строку запроса собирает и экранирует провайдер — значение с кавычкой или обратным слэшем остаётся значением и не может добавить условие. Поиск без единого фильтра отказывается: «все задачи сайта» — не вопрос, на который этот инструмент отвечает. Ответы идут по последнему обновлению (сначала свежие) и несут pagination.nextCursor — это continuation-токен самого Jira, состояние между вызовами не хранится, а страница не выходит за потолок стенда (defaultSearchLimit, maxSearchLimit) и за 100 строк, которые Jira отдаёт с полями. Комментарии Jira по-прежнему листаются позицией, поэтому их pagination — { startAt, returned, total, hasMore }.

Точное число результатов ищет не провайдер, а продукт: Server / Data Center считает поиск по позиции и отдаёт размер ответа, поэтому его pagination несёт total (и startAt, и следующий offset в nextCursor), а Atlassian Cloud на /search/jql числа не сообщает вовсе — там остаётся только nextCursor и isLast, и «сколько всего» превращается в вопрос к самому Jira, а не к этому пакету. Провайдер не выдумывает оценку: счётчика нет в ответе — нет и в конверте.

Словарь фильтров покрывает то, что спрашивают у корпоративной Jira, и каждый фильтр — это клауза, которую собирает jql.ts: проект, тип задачи, статус и категория статуса (To Do / In Progress / Done — «закрытые» одним фильтром, чем бы ни называл их workflow), приоритет, резолюция, компонент, метки (все перечисленные, а не «любая из»), версии — fixVersions и affectedVersions, включая «не проставлена» (fixVersionEmpty: true) и «проставлена» (false), исполнитель и автор, даты создания и обновления в обе стороны, история поля (history, см. ниже) и кастомные поля. Даты принимают и абсолютное значение (2026-08-01, ISO-таймстемп), и родной относительный токен Jira (-3w, -2d, -4h, -30m), а пара «с/по» — это createdAfter/createdBefore (границы включительные). Текст ищется двумя способами: match: "all" (по умолчанию) требует все слова, match: "phrase" — точную фразу; оба варианта бьются на отдельные клаузы text ~ "…", поэтому OR из фразы остаётся словом, а не оператором.

history — фильтр не по тому, что задача несёт сейчас, а по тому, что с ней делали: JQL-операторы WAS и CHANGED собираются из типизированной записи, { field: "status", op: "was", value: "In Progress" } отвечает «побывали в In Progress», { field: "status", op: "changed", after: "-2w" } — «двигали статус за две недели», { field: "assignee", op: "changed", value: "5b10…" } — «переназначали на вот этого человека». Поле ограничено списком status, assignee, reporter, priority, resolution, fixVersion — тем, у чего Jira ищет историю; кастомное поле здесь отказывается, для него есть customFields. Ключевые слова читаются по строгому набору: was требует значение и не принимает from, changed принимает from (откуда ушло) и value (куда пришло), by — кто двигал, дата — один день (on) или край окна (after/before), а день вместе с окном — противоречие. Значения экранируются как любое другое, поэтому история не распахивает запрос: клауза OR project = SECRET из value остаётся текстом внутри кавычек. Человек здесь — me или идентификатор, по которому фильтрует этот продукт, но не имя: справочник пользователей за этим фильтром не читается (в отличие от assignee/reporter, где имя разрешается провайдером), потому что идентификатор уже лежит в истории самой задачи — в changelog_summary. Неразборчивая запись отказывается целиком, а не собирается «из того, что удалось распарсить»: молча потерянная граница отвечала бы на другой вопрос, а её ответ выглядел бы как факт о задачах. Клауз — не больше трёх, и они, как остальные фильтры, соединяются через AND.

Кастомные поля фильтруются по id, который вернул jira_get_fields, или по алиасу, который объявил оператор стенда: customFields: [{ field: "product", value: "…" }], с match: "contains" для текстовых полей и empty: true/false для пустого/заполненного. Имя поля Jira не принимается намеренно — на одном сайте легко живут несколько полей с одинаковым именем, а какой из них «продукт», знает только развёртывание: соответствие «алиас → customfield_…» задаётся в конфиге оператора (jira.fieldAliases), поэтому в репозитории нет ни одного id конкретного инстанса, а каталог полей (jira_get_fields) отдаёт объявленные алиасы вместе со схемой. Неизвестное имя — отказ со списком алиасов, а не пустая страница. Люди принимаются как me, как идентификатор, по которому фильтрует эта Jira (accountId у Cloud, логин у Server / Data Center), или как имя: имя провайдер разрешает через справочник сайта (/rest/api/3/user/search или /rest/api/2/user/search, не больше десяти совпадений) и подставляет этот идентификатор в запрос. Имя, которое никого не нашло, и имя, которое нашли несколько человек, — это отказ с подсказкой, а не пустая страница: «нет задач» и «фильтр не понят» — разные ответы. Ответ справочника модели не показывается, он только определяет id, по которому построен запрос.

Описания и комментарии приходят в Atlassian Document Format и рендерятся в текст с сохранением структуры: заголовки, списки, код, ссылки, упоминания, таблицы и вложения-маркеры. Сырой JSON документа модели не отдаётся, а встроенные ссылки и медиа-узлы не загружаются. История изменений задачи читается отдельной группой include: ["changelog_summary"] (Jira отдаёт её только через expand=changelog): ответ несёт до 20 изменённых полей с автором и значениями «было/стало» и честно различает две обрезки — свою (truncated, упёрлись в 20 записей) и джировскую (groupsTruncated, Jira посчитала групп изменений больше, чем прислала). Кастомные поля называются по схеме сайта (jira_get_fields показывает её модель целиком): схема читается только после того, как задача ответила, и не кэшируется — один и тот же сайт отдаёт разный список полей двум пользователям с разными правами, а кэш на двоих был бы утечкой ради одного запроса. Тело задачи, комментария или кастомного поля ограничено maxTextChars (по умолчанию 20 000 символов); при обрезке ответ помечается descriptionTruncated или bodyTruncated. Текст из Jira — данные, а не инструкция.

Инструменты Confluence

Tool Что делает
confluence_connection_get К какому сайту Confluence и каким аккаунтом Atlassian подключён пользователь
confluence_search Поиск страниц по тексту, пространствам, меткам, автору и дате изменения
confluence_get_page Страница целиком: заголовок, пространство, родитель, версия, метки и текст
confluence_get_page_comments Комментарии страницы: подвал, inline или оба, с ответами и статусом резолва
confluence_get_page_attachments Метаданные вложений: имя, тип, размер, версия, автор, ссылки
confluence_get_page_versions История версий: номер, автор, дата и комментарий к версии
confluence_get_space Пространство: ключ, имя, тип, статус, описание
confluence_list_spaces Пространства, доступные аккаунту, с ключами для поиска и чтения

Списочные инструменты Confluence отвечают конвертом { items, nextCursor }: nextCursor — это значение, которое нужно вернуть в аргументе cursor, а не URL и не подписанный токен. У поиска это смещение строк (эндпоинт считает строки), у остальных чтений — курсор самого Confluence, из которого провайдер берёт только сам токен: путь и параметры следующего запроса он собирает сам, поэтому курсор не может увести запрос на чужой адрес. Комментарии с kind: "all" отвечают двумя курсорами (cursors.footer и cursors.inline), потому что продолжать их придётся по отдельности. totalSize у поиска говорит, сколько всего совпадений нашлось, — по нему модель понимает, стоит ли листать дальше.

Дату изменения можно задать абсолютным днём (YYYY-MM-DD) или окном от сегодняшнего дня (-7d, -2w, -1m, -1y): у агента в промпте часов нет, поэтому окно считает провайдер.

Поиск идёт через CQL, но CQL собирает провайдер: модель передаёт типизированные фильтры (текст, пространства, типы, метки, автор, дата), а cql.ts экранирует литералы и подставляет space in (…), currentUser() и ORDER BY. Инструмента с произвольным CQL в surface нет. Текст страницы и комментариев приходит не сырым payload, а markdown-подобной разметкой в поле untrustedContent: adf.ts разбирает Atlassian Document Format в заголовки, списки, таблицы, блоки кода, ссылки, упоминания, панели и статусы, а макросы, медиа и встроенные представления заменяет плейсхолдерами ([Confluence macro: jira], [media: chart]) — провайдер ничего не отрисовывает и никуда не ходит по ссылкам из страницы. Тело режется бюджетом (maxChars в аргументе, потолок — maxBodyChars стенда) и при обрезке отвечает truncated: true и totalChars. Подсветка совпадений в выдержках поиска снимается вместе с разметкой.

Инструменты Weblate

Tool Что делает
weblate_connection_get К какому инстансу Weblate и каким аккаунтом подключён токен и личный он или проектный; сам токен не возвращается
weblate_projects_list Проекты, видимые аккаунту, постранично
weblate_project_get Один проект: слаг, имя, ссылка
weblate_project_statistics_get Готовность проекта: строки и слова переведены, требуют правки, не проходят проверки
weblate_components_list Компоненты проекта постранично
weblate_component_get Один компонент: слаг, имя, система контроля версий, ветка, формат файлов, исходный язык
weblate_component_statistics_get Готовность компонента по языкам
weblate_translations_list Языки компонента с их состоянием перевода
weblate_translation_get Один язык одного компонента целиком: переведено, требует правки, проверки, комментарии, предложения, автор последней правки
weblate_translation_statistics_get Статистика одного языка одного компонента
weblate_units_search Строки одного языка одного компонента с типизированными фильтрами
weblate_units_find Строка по всему, что видит аккаунт, с сужением по проекту, компоненту и языку
weblate_unit_get Одна строка целиком: все формы множественного числа исходника и перевода, состояние, контекст, заметка, метки, флаги, проверки
weblate_unit_comments_list Комментарии к строке: автор, время, текст
weblate_unit_suggestions_list Предложенные переводы строки: автор, голоса, время
weblate_failing_units_list Строки, у которых не проходит хотя бы одна проверка, с сужением по проекту, компоненту, языку, тексту и состоянию
weblate_changes_list Последние изменения проекта: какая строка, какой язык, что сделано, кем и когда
weblate_screenshots_list Скриншоты Weblate и строки, к которым они привязаны
weblate_screenshot_get Метаданные одного скриншота: имя, файл в репозитории, язык, id связанных строк

Списочные инструменты Weblate отвечают конвертом { items, pagination }: pagination.nextPage — номер следующей страницы, а не подписанный курсор. По ссылке next провайдер не ходит сам: Weblate кладёт в тело абсолютный адрес, из которого читается только номер страницы, и только когда он ведёт на настроенный инстанс — ссылка с чужого адреса отбрасывается, а список честно считается законченным. Страница — 20 строк по умолчанию, а perPage сверх потолка стенда (maxPageSize, по умолчанию 100) обрезается, а не отвергается: «дай больше строк» — это запрос, на который стенд отвечает своим потолком, а не ошибка, которую модель выясняет перебором. Состояние между вызовами не хранится, principal и права перепроверяются на каждом вызове.

Модель не получает q: фильтры типизированные (source, target, context, state, failingChecks, suggestions, comments, а у поиска по всему инстансу ещё project, component и language), а строку запроса собирает query.ts — значения всегда в двойных кавычках с экранированием \ и ", поэтому кавычка или обратный слэш внутри запроса остаются значением и не становятся условием. Словарь состояний — собственный словарь Weblate is:: untranslated (по определению самого Weblate — всё, что ниже translated, то есть вместе со строками, помеченными needs-editing), needs-editing, translated, approved, read-only. Поиск без единого фильтра уходит без параметра q, а не с пустым. weblate_failing_units_list — тот же /units/, что и weblate_units_find, с жёсткой клаузой has:check; Weblate сообщает, что строка не проходит проверку, но не то, какая именно проверка упала, поэтому ответ несёт hasFailingCheck, а не список имён проверок.

Строки нормализованы: числовые состояния Weblate (0/10/20/30/100) отображаются в untranslated/needs-editing/translated/approved/read-only, формы множественного числа остаются массивом (перевод, молча короче настоящего, был бы прочитан как правда), текст режется — 200 символов в списке, maxChars у отдельной строки, потолок — maxTextChars стенда — и помечается textTruncated, метки приходят именами, а проект, компонент и язык читаются из вложенной ссылки translation, и только когда она ведёт на настроенный инстанс. Каждый ответ с текстом, написанным на стороне Weblate, помечен untrustedExternalContent: true: это данные, а не инструкция. Скриншоты — только метаданные (имя, файл в репозитории, язык, id строк, адрес картинки на настроенном инстансе): байты провайдер не скачивает. История изменений у Weblate есть у проекта, компонента и перевода, но не у строки, поэтому инструмент проектный, а строка в каждой записи ищется по её id.

weblate_connection_get называет инстанс, аккаунт и вид токена: у Weblate нет эндпоинта «кто я», и подключённый аккаунт определяется по GET /api/users/, который непривилегированный токен видит одной своей строкой. Токен, который умеет перечислять пользователей, отвечает полным списком — тогда аккаунт честно не определяется (пустой внешний id, в карточке адрес инстанса и вид токена), а не угадывается по первой строке.

Возможности и скопы Confluence

Подключение — Atlassian API token вместе с почтой аккаунта Atlassian (Basic-пара). Возможность появляется у пользователя, если её включил оператор (confluence.*Read).

Возможность Что открывает
identity.read Сайт Confluence и подключённый аккаунт Atlassian
spaces.read Список пространств и карточка пространства
search.read Поиск страниц по тексту, пространствам, меткам, автору и дате
content.read Страница целиком с текстом, метками и версией
comments.read Комментарии страницы: подвал, inline и ответы
attachments.read Метаданные вложений страницы
versions.read История версий страницы

Confluence, как и TeamCity, не сообщает через API, какие права выданы конкретному токену: у OAuth-приложений для этого есть /oauth/token/accessible-resources, а у API token аналога нет. Поэтому сужение идёт только по деплой-флагам оператора, а права аккаунта применяет сам Confluence — отказ приходит как ProviderPermissionDenied, и провайдер никогда не пробует другой credential.

Оператор дополнительно может сузить пространства: confluence.allowedSpaces — это список ключей, и он применяется одинаково к поиску, к списку пространств и к прямому чтению страницы, комментариев, вложений и версий. Страница в пространстве вне списка отвечает отказом OperationDeniedByPolicy, а не тихо исчезает из выдачи; в поиск добавляется space in (…), и строки, пространство которых по ответу не подтверждается, отбрасываются. Страница не несёт ключ пространства, только spaceId, поэтому каждое чтение страницы делает дополнительный запрос самого пространства — он же и наполняет ответ ключом и именем, и по нему принимается решение политики; чтения комментариев, вложений и версий с включённой политикой стоят ещё одного чтения страницы, чтобы узнать её пространство.

Скопы, которые нужны токену: классические read:confluence-content.all, read:confluence-space.summary, search:confluence, read:confluence-user (или гранулярные read:page:confluence, read:comment:confluence, read:attachment:confluence, read:space:confluence, read:content.metadata:confluence, read:user:confluence). Классический токен работает по адресу сайта (https://company.atlassian.net); токен со скоупами Atlassian принимает только через шлюз https://api.atlassian.com/ex/confluence/{cloudId} — такой адрес оператор может задать как instances[].baseUrl, и провайдер будет ходить по тому же относительному пути /wiki/.... Сам cloudId провайдер не ищет. Для Server / Data Center скоупов и почты нет: там нужен личный токен доступа, а адрес инстанса задаётся вместе с его контекстным путём (https://wiki.example.corp/confluence).

Запись в surface отсутствует: создание и правка страниц, комментарии, резолв inline-комментариев, вложения, перемещение и администрирование пространств ждут confirmation-фреймворка. Произвольного REST (confluence_rest_call), произвольного CQL и скачивания вложений тоже нет — только метаданные и ссылки, которые отдал сам Confluence.

Сервисный токен Confluence

Подключение может работать от сервисного токена развёртывания: профиль называет сайт, секрет — та же Basic-пара email:token, а граница ресурсов — spaces (ключи пространств). Чувствительных чтений в каталоге нет: поиск, пространства, страницы, комментарии, метаданные вложений и версии остаются сервисно-безопасными, но каталог не умеет отдавать байты вложений, и такая операция, когда появится, обязана прийти с классификацией не ниже чувствительной. Страница, названная по id, сначала раскрывается в своё пространство, поиск сужается до space in (…) по границе ещё на входе, а строки, пространство которых по ответу не подтверждается, отбрасываются вторым замком. Граница сервиса и операторский confluence.allowedSpaces — два независимых сужения: сервисный вызов живёт в их пересечении.

Возможности и скопы Weblate

Подключение — API-токен Weblate вместе с выбором инстанса: адреса задаёт только оператор (weblate.instances), пользователь выбирает инстанс из списка и вставляет токен. Возможность появляется у пользователя, если её включил оператор (weblate.*Read).

Возможность Что открывает
identity.read Инстанс Weblate, подключённый аккаунт и вид токена
projects.read Список проектов и карточка проекта
components.read Компоненты проекта и карточка компонента
translations.read Языки компонента и один язык целиком
units.read Поиск строк по тексту, контексту и состоянию, чтение одной строки
checks.read Строки, у которых не проходит хотя бы одна проверка
comments.read Комментарии к строке
suggestions.read Предложенные переводы строки
changes.read История изменений проекта
statistics.read Готовность проекта, компонента и языка
screenshots.read Метаданные скриншотов

Weblate, как и TeamCity с Confluence, не сообщает через API, какие права выданы конкретному токену: токен действует от имени своего пользователя или своего проекта, а «проверить» чтением чужой области провайдер не имеет права. Поэтому сужение идёт только по деплой-флагам оператора (weblate.*Read), а права применяет сам Weblate — отказ приходит как ProviderPermissionDenied, и провайдер никогда не пробует другой credential. Каждая возможность при этом достижима хотя бы одной операцией, поэтому карточка не может показать переключатель, который ничего не делает.

Префиксы токенов Weblate (wlu_ — личный, wlp_ — проектный) доходят до пользователя подписью в карточке (· личный токен, · токен проекта, · токен) и никогда не являются проверкой прав: токен с незнакомым префиксом принимается наравне с остальными, а его права определяет сам Weblate. Проектный токен карточка рекомендует как меньший радиус поражения — это подсказка, а не запрет.

Оператор стенда дополнительно решает, какие из инструментов видит модель: имена weblate_* нужно добавить в tool allow-list пресета QA. Инструменты не принимают ни пользователя, ни credential, ни инстанс, поэтому допуск к ним — решение оператора, а не модели.

Запись в surface отсутствует: предложения перевода, комментарии, правка и утверждение перевода, файлы перевода, autotranslate и операции с репозиторием ждут confirmation-фреймворка. Инструмента с произвольным q и произвольным REST тоже нет: поиск принимает только типизированные фильтры.

Возможности и скопы

Одна возможность = один scope Bitrix24. Возможность появляется у пользователя только если её включил оператор (bitrix24.*Read, у записи — bitrix24.crmCommentWrite) и подключённый вебхук реально получил соответствующий scope — плагин спрашивает это у портала методом scope при подключении и при нажатии «Проверить».

Возможность Scope вебхука Что открывает
crm.read crm CRM, дела, таймлайн, товарные строки, дубли
crm.comment.write crm Единственная операция записи: комментарий в таймлайн лида, сделки, контакта или компании
chat.read im Чаты, сообщения, поиск по переписке
openlines.read imopenlines Диалоги открытых линий и их история
user.read user_brief / user_basic / user Свой профиль и поиск сотрудников
department.read department Структура компании
tasks.read task Задачи
calendar.read calendar Календарь и занятость
disk.read disk Файлы Диска

У Bitrix24 нет read-only scope вебхука: crm покрывает и чтение, и запись, поэтому гранулярность «запись отдельно» даёт только конфиг-флаг. crm.comment.write выключен по умолчанию; даже после включения флага операция стартует с политикой deny, пока пользователь (или оператор) явно её не разрешит, — проба scope сама по себе запись не открывает. Остальная запись (*.add, *.update, *.delete), бизнес-процессы, управление пользователями и generic REST/MCP в surface отсутствуют.

Выданные в Bitrix24 права не отменяются: портал всё равно применяет права того пользователя, чей вебхук используется. Возможность, обнаруженная после выдачи нового scope, появляется в карточке выключенной — пользователь включает её сам.

Сервисный токен Bitrix24

Подключение может работать от сервисного вебхука развёртывания: профиль managedServiceCredentials называет портал (хост, в любом регистре — он сравнивается с хостом, который брокер выводит из URL вебхука), секретом служит полный URL сервисного входящего вебхука, а граница ресурсов — единственный вид portals: сервисный вызов обязан отвечать на портале из списка, других идентификаторов у границы нет. Проба (validateServiceCredential) читает только profile и scope; подтвердить read-only она не может — scope crm и читает, и пишет одним битом, — поэтому вердикт всегда «здоров» с предупреждением, а потолок режима обеспечивают классификация операций и граница. Сервисному токену недоступны по классификации: запись в таймлайн, справочник сотрудников (телефоны и почта), дубликаты по телефону и почте, расшифровки звонков, все чтения чатов и открытых линий, календарь занятости и Диск — это чтения, ответы которых принадлежат конкретным людям, а не порталу; CRM-записи, задачи и структура отделов остаются сервисно-безопасными.

Возможности и скопы GitLab

Подключение — personal access token. Возможность появляется у пользователя только если её включил оператор (gitlab.*Read) и токен при подключении реально предъявил соответствующий scope: плагин спрашивает scopes у самого токена (GET /api/v4/personal_access_tokens/self) при сохранении и при нажатии «Проверить». Токен, который не может прочитать себя (старый GitLab, group/project access token), только снижает точность — срез остаётся на деплой-флагах оператора.

Возможность Scope токена Что открывает
identity.read read_user / read_api / api Свой профиль: логин, имя, инстанс
projects.read read_api / api Проекты, видимость, ветка по умолчанию
repository.read read_repository / read_api / api Дерево, файлы, коммиты, сравнение веток
search.read read_api / api Поиск по проектам, задачам, MR, коммитам, коду
issues.read read_api / api Задачи и комментарии к ним
merge_requests.read read_api / api MR, диффы, обсуждения, одобрения, пайплайны MR
ci.metadata.read read_api / api Пайплайны, джобы, их статусы и метаданные
ci.logs.read read_api / api Содержимое лога джоба

CI разделён надвое намеренно: лог джоба — это другая утечка, чем список джоб, а раньше обе половины закрывала одна возможность ci.read. Оператор стенда задаёт половины отдельно (ciMetadataRead и ciLogsRead), а старый флаг ciRead продолжает работать и теперь управляет обеими; сохранённая политика ci.read при первом запуске переносится на оба новых id, поэтому выключенный CI не включается сам. Под сервисным токеном доступна только первая половина: ci.metadata.read отвечает, какие джобы были и чем кончились, а ci.logs.read показывает, что они напечатали, и остаётся личным чтением. Любая операция, читающая проект, в сервисном режиме обязана назвать проект или группу внутри границы профиля, а листинг без проекта (gitlab_issues_list, gitlab_merge_requests_list) отказывается — вместо ответа всей картиной, доступной общему аккаунту.

У GitLab нет отдельного read-scope на каждую область, поэтому read_api покрывает почти всё, а read_repository и read_user дают узкие наборы. Права токена не отменяются: GitLab всё равно применяет права своего пользователя. Возможность, появившаяся после выдачи нового scope, приходит в карточку выключенной. Запись (POST/PUT/DELETE), GraphQL, произвольный REST, admin-API, управление токенами, участниками и CI-переменными в surface отсутствуют; merge и запись в репозиторий — тоже. Сервисный токен не превращает эти запреты в разрешения: он только сужает — и дополнительно прячет от общего аккаунта лог джоба.

Оператор стенда дополнительно решает, какие из инструментов видит модель: имена нужно добавить в tool allow-list пресета QA. Инструменты не принимают ни пользователя, ни credential, поэтому допуск к ним — решение оператора, а не модели.

Возможности и скопы TeamCity

Подключение — персональный access token TeamCity. Адрес сервера задаёт оператор в конфигурации развёртывания (teamcity.serverUrl) — он один на всех, поэтому форма подключения спрашивает только токен, а карточка показывает адрес как справочную строку. Если адрес не задан, карточка говорит об этом вместо формы, которая всё равно не сохранится. Возможность появляется у пользователя, если её включил оператор (teamcity.*Read).

Возможность Что открывает
identity.read Сервер TeamCity, его версия и подключённый пользователь
projects.read Проекты
buildConfigs.read Конфигурации сборки
builds.read Сборки, карточка сборки, изменения в VCS
failures.read Упавшие тесты и проблемы сборки
logs.read Фрагмент лога сборки
queue.read Очередь сборки
investigations.read Расследования падений
agents.read Агенты сборки
artifacts.read Список артефактов и текст небольших текстовых файлов

Лог сборки и текст артефакта остаются личными под сервисным токеном: это текст, который напечатала сборка, и общий read-only аккаунт его не читает. Список артефактов — обычное чтение метаданных, но возможность у него одна с текстом, поэтому в сервисном режиме artifacts.read целиком помечена «Требуется личный аккаунт», как и logs.read. В сервисном режиме листинг обязан назвать проект или конфигурацию: teamcity_builds, teamcity_build_configs, teamcity_queue и teamcity_investigations без projectId или buildTypeId отказываются, потому что иначе ответили бы всей картиной сервера, а teamcity_projects собирается из allowlist профиля. Сборка, названная по id, сначала разрешается в свой проект, поэтому прямой teamcity_build проходит ту же проверку, что и листинг. Причины падений (teamcity_build_failures) остаются сервисными: инструмент отвечает упавшими тестами и проблемами сборки, а не логом.

TeamCity, в отличие от GitLab и Bitrix24, не сообщает через API, какие именно права выданы конкретному токену: админ-скоупы невидимы снаружи, а «проверить» записью провайдер не имеет права. Поэтому сужение идёт только по деплой-флагам оператора, а права токена применяет сам TeamCity — отказ приходит как ProviderPermissionDenied, и провайдер никогда не пробует другой credential. Рекомендация пользователю в карточке прямая: создать токен с «Limit per project» и только теми правами чтения, которые нужны.

Запись в surface отсутствует: запуск, перезапуск, отмена сборки, комментарии и теги ждут confirmation-фреймворка; редактирование конфигураций, управление агентами, расследованиями и mute-ами — тоже. Параметры сборки (/parameters, resulting-properties) провайдер не запрашивает вообще, поэтому секретных значений в ответах нет по построению.

Возможности и скопы Jira

Подключение зависит от того, где живёт Jira, и это объявляет оператор в конфигурации развёртывания (jira.sites[].deploymentType). Atlassian Cloud аутентифицирует API-токен Atlassian вместе с e-mail аккаунта через HTTP Basic (email:token); Jira Server / Data Center — личный токен доступа (Personal Access Token) через Bearer, без почты. Провайдер ходит по API того продукта, который объявлен: /rest/api/3 у Cloud, /rest/api/2 у Server / Data Center. Сайт, который отвечает serverInfo.deploymentType другого продукта, отклоняется на подключении с подсказкой, какое значение поставить.

Список сайтов задаёт только оператор — пользователь выбирает сайт из списка и вставляет токен (и почту, если сайт облачный); произвольный хост ввести нельзя. Возможность появляется у пользователя, если её включил оператор (jira.*Read).

Возможность Что открывает
identity.read Подключённый пользователь Atlassian и сайт, на котором он работает
issues.read Поиск задач со всем словарём фильтров (включая фильтр по истории поля — WAS/CHANGED — и разрешение имени в идентификатор пользователя) и карточка задачи с описанием, связями и кастомными полями
comments.read Комментарии задачи, включая ограниченные (с пометкой видимости)
attachments.read Метаданные вложений задачи: имя, тип, размер, автор
transitions.read Доступные переходы статуса и их обязательные поля
projects.read Карточка проекта
fields.read Схема полей сайта, включая кастомные, — она же источник id для фильтра по кастомному полю

Jira, как и TeamCity, не сообщает через API, какие права выданы конкретному API-токену: токен действует от имени пользователя и наследует его права целиком, а «проверить» записью провайдер не имеет права. Поэтому сужение идёт только по деплой-флагам оператора, а права аккаунта применяет сама Jira — отказ приходит как ProviderPermissionDenied (задача, проект или поле закрыты схемой прав, issue security level или схемой ролей), и провайдер никогда не пробует другой credential. Единственная проверка, которую провайдер делает сам при подключении, — тип развёртывания: сайт, который отвечает deploymentType не тем продуктом, который объявил оператор, отклоняется с подсказкой, какое значение поставить, потому что Cloud и Server / Data Center — разные API, и молча считать их совместимыми спецификация запрещает.

Запись в surface отсутствует: создание, правка, назначение, комментарии и переходы ждут pending actions и карточки подтверждения; удаление, администрирование проектов и workflow, произвольный REST и скачивание содержимого вложений — тоже. Провайдер читает только объявленный allow-list эндпоинтов /rest/api/3 (гейт пакета падает на любом пути вне него), а легаси-эндпоинт /rest/api/3/search, удалённый Atlassian, не используется: поиск идёт через /rest/api/3/search/jql.

Сервисный токен Jira

Подключение может работать от сервисного токена развёртывания: профиль называет сайт, секрет — обычный API-токен Atlassian в Basic-паре, а граница ресурсов — projects (ключи проектов, сравнение без учёта регистра; числовой id засчитывается, если Jira отдала его в выдаче). Сервисному токену недоступны чтения вложений — даже список метаданий помечен чувствительным, потому что следующий естественный шаг агента — скачать файл. Фильтры JQL по имени человека в сервисном режиме отказывают целиком — это чтение корпоративного справочника, у которого нет проекта в границе, — а фильтры по accountId и me остаются. Задача, названная по ключу, сначала раскрывается в свой проект, и чтение с уровнем безопасности, который сервисный токен не проходит, отвечает ResourceNotFound, как для чужой задачи.

Адрес TeamCity задаёт оператор: сервер один на весь стенд, поэтому teamcity.serverUrl — обычная настройка развёртывания, а не поле формы. Пользователь вводит только токен, и токен тратится ровно по этому адресу. Рядом оператор задаёт политику адресов: какие адреса этот стенд готов набирать вообще (политика осталась от времён, когда адрес вводил пользователь, и закрывает переезд конфига на чужой хост).

teamcity:
  enabled: true
  serverUrl: https://teamcity.example.internal
  network:
    mode: allowlist # allowlist | trusted-private
    allowedHosts:
      - teamcity.example.internal
      - "*.corp.example"
    allowedCidrs:
      - 10.20.0.0/16 # только для адресов, записанных цифрами
    allowedPorts:
      - 443
      - 8111
    allowHttp: false

Правила простые и проверяются дважды — при сохранении подключения и на каждом вызове инструмента, поэтому ужесточение политики закрывает и ранее сохранённые подключения:

  • только http/https, HTTPS обязателен вне явного allowHttp; в URL не может быть credentials, query и fragment;
  • имя хоста должно попасть в allowedHosts (точное имя или *.суффикс), а адрес, записанный цифрами, — в allowedCidrs;
  • порт должен быть в allowedPorts: по умолчанию разрешён только порт схемы, поэтому TeamCity на своём стандартном :8111 требует явного allowedPorts: [8111];
  • редиректы не отслеживаются (redirect: "error"), токен уходит только в заголовке Authorization: Bearer, в URL и теле его нет;
  • mode: trusted-private — для доверенной корпоративной сети: имена хостов разрешены любые, а закрытые диапазоны (10/8, 172.16/12, 192.168/16, 127/8) подставляются как allowedCidrs по умолчанию. Он предполагает, что стенд доверяет своему DNS; если это не так, нужен allowlist.

Пустая политика (allowedHosts и allowedCidrs пусты) — это не ошибка загрузки, а «ничего не подключить»: плагин пишет об этом предупреждение в лог при старте, а карточка отвечает пользователю отказом. Опечатки в самой политике — наоборот, падение на старте: молча выброшенный шаблон хоста оставил бы стенд без объяснения.

Если для этого сервера заведён сервисный токен, форма вместо поля с токеном показывает чекбокс «Использовать сервисный токен»: при defaultForNewConnections: true он отмечен, кнопка называется «Подключить сервисный токен», а адрес сервера остаётся той же спра