Встроенная страница
Настройка страницы
Встроенная страница служит пользовательским интерфейсом плагина внутри кабинета: на ней пользователь может настраивать подключения и аккаунты, просматривать данные и работать с другими функциями плагина. Состав страницы определяет разработчик. У приложения типа «Плагин» откройте раздел «Встроенная страница».

Переключатель «Показывать главную встроенную страницу приложения» добавляет в кабинет проекта отдельный пункт со страницей плагина. Если его выключить, этот пункт исчезнет, но основной URL продолжит автоматически использоваться в конфигураторах инструментов агента и шагов автоматизаций.
Основной URL и режим разработчика
В поле «Основной URL» укажите единый адрес для главной страницы и конфигураторов инструментов и шагов. Если протокол пропущен, кабинет сохранит адрес с https://; явно указанный http:// допустим для локальной разработки. Для обычной публикации используйте HTTPS. Страница должна разрешать открытие во фрейме и не должна рассчитывать на переход всего верхнего окна.
Включите «Режим разработчика», чтобы участники команды приложения открывали страницу с отдельного URL для разработчиков. Здесь можно указать http://localhost или отдельное тестовое окружение; для домена без протокола также добавится https://. Обычные пользователи установленного приложения продолжают открывать основной URL.
После изменения URL или режима разработчика нажмите кнопку сохранения.

Ниже расположена отдельная настройка действий приложения. Она публикует выбранные методы backend в MCP и сохраняется своей кнопкой; переключатель и URL встроенной страницы на неё не влияют.
Тестирование и режимы запуска
Нажмите «Протестировать» и выберите доступный проект. Если режим разработчика включён, тест откроет developer URL, иначе основной. Тестирование доступно и тогда, когда показ главной страницы в меню проекта выключен.

Все способы открытия используют единый bootstrap-контракт URL версии 2: одноразовый launch_code, senler_context_version=2, senler_mode, senler_theme=light|dark и senler_language=ru|en. Режим принимает значение installed, test, tool_configurator или automation_step_configurator.
Параметры senler_* нужны только для первоначального отображения до подключения Bridge. Идентификаторы приложения, проекта, установки, агента, автоматизации и узла передаются только в проверенном context.launch через Senler Bridge. Сервер приложения должен доверять только проверенному launch_code. Для чтения и изменения данных проекта нужна обычная OAuth/API-авторизация с выданными правами: launch_code её не заменяет.
Проверка launch_code
launch_code имеет вид <payload>.<signature>. Обе части используют base64url. В payload находятся version: 1, project_id, время окончания expires_at в Unix-секундах и случайный nonce; код действует 2 минуты. Подпись — HMAC-SHA256 от закодированной части payload с Client Secret приложения.
Проверяйте код на сервере приложения до показа данных: сравните подпись без утечки времени, проверьте версию и срок, запретите повторное использование nonce, затем создайте собственную короткую сессию приложения. Не записывайте полный URL или launch_code в открытые логи. Если код истёк, пользователь должен закрыть страницу или конфигуратор и открыть их снова, чтобы получить новый код.
Пример проверки в Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyLaunchCode(code, clientSecret) {
const parts = code.split(".");
if (parts.length !== 2 || !parts[0] || !parts[1]) {
throw new Error("Invalid launch_code format");
}
const [encodedPayload, encodedSignature] = parts;
const actual = Buffer.from(encodedSignature, "base64url");
const expected = createHmac("sha256", clientSecret)
.update(encodedPayload)
.digest();
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
throw new Error("Invalid launch_code signature");
}
const payload = JSON.parse(
Buffer.from(encodedPayload, "base64url").toString("utf8"),
);
const now = Math.floor(Date.now() / 1000);
if (
payload.version !== 1 ||
typeof payload.project_id !== "string" ||
typeof payload.expires_at !== "number" ||
typeof payload.nonce !== "string" ||
payload.expires_at < now
) {
throw new Error("Expired or invalid launch_code payload");
}
return payload;
}
Хранение использованных nonce и создание сессии остаются на стороне приложения. Отдельного обмена launch_code на токен Senler нет, и этот код не заменяет OAuth.
Контекст и элементы встроенного приложения
Подключите Senler Bridge так же, как в конфигураторе инструмента. Для обычной страницы context.launch.type равен embedded_page; контекст содержит app_id, project_id, необязательный installation_id и режим installed или test. Конфигураторы используют типы tool_configurator и automation_step_configurator; контракт второго описан в «Шагах автоматизаций». Это позволяет использовать один интерфейс для разных сценариев без чтения идентификаторов из непроверенных query-параметров.
Чтобы агент кабинета мог не только объяснить страницу, но и подсветить, открыть или изменить её элементы, добавьте разметку с ID в пространстве app.* и подключите стандартный обработчик:
import {
clearSenlerBridgeElementHighlight,
createSenlerBridgeClient,
executeSenlerBridgeElementAction,
} from "@senler/ui/bridge";
const bridge = createSenlerBridgeClient({ parentOrigin });
await bridge.connect();
const unsubscribeAction = bridge.onElementAction((request) =>
executeSenlerBridgeElementAction(request),
);
const unsubscribeClear = bridge.onElementHighlightClear(() =>
clearSenlerBridgeElementHighlight(),
);
<button
data-ai-reveals-context-id="app.orders.create.form"
data-ai-reveal-action="click"
>
Создать заказ
</button>
<form data-ai-context-id="app.orders.create.form">
<input data-ai-context-id="app.orders.create.customer" />
<button data-ai-context-id="app.orders.create.submit">Сохранить</button>
</form>
Поддерживаются highlight, scroll_to, focus, click, fill, clear, select и toggle. Цель должна быть видимой и единственной с таким data-ai-context-id; fill, clear и select работают только с обычными input, textarea или select, а toggle — с checkbox, radio или элементом role="switch". Если поле находится во вкладке, dropdown, accordion или диалоге, пометьте открывающий элемент через data-ai-reveals-context-id; Bridge может последовательно выполнить до четырёх таких шагов.
Текущий Senler Bridge передаёт context_id, action и необязательное value. Уточнения entity_type, entity_id и role он не передаёт: кабинет отклоняет такие команды с кодом embedded_target_qualifiers_unsupported. Поэтому одинаковый ID у нескольких строк нельзя уточнить ID сущности через этот обработчик. Это ограничение Bridge, а не разметки основной страницы сайта, где виджет поддерживает выбор конкретной сущности. Поддержка нестандартных выпадающих списков в основном виджете также не распространяется автоматически на встроенное приложение.
Кабинет отправляет команду только одному видимому приложению, которое отвечает за указанный контекст. Если подходящих приложений несколько, команда отклоняется с ambiguous_embedded_target; если подходящее приложение скрыто или его окно недоступно, возвращается embedded_frame_unavailable. Порядок открытия окон не используется для выбора цели. Закройте лишнее окно или откройте нужное приложение перед новой попыткой.
Используйте стабильные смысловые ID, например app.orders.filter.status, и теми же ID помечайте соответствующие объяснения в документации приложения. Не включайте в ID проект, пользователя, перевод подписи или случайный DOM-идентификатор. Тогда агент сможет найти справку и выполнить одинаковый сценарий на русском, английском, узком и широком экране.
При размонтировании вызовите unsubscribeAction(), unsubscribeClear() и bridge.destroy().
Встроенный фрейм разрешает скрипты, формы, модальные окна, загрузки, всплывающие окна, буфер обмена, полноэкранный режим и микрофон; возможности браузера, которых нет в этом списке, следует считать недоступными до отдельной проверки.
Сохраните изменения через кнопку сохранения. Если тест не открывается, проверьте URL, заголовки Content-Security-Policy/X-Frame-Options, доступность локального сервера из браузера и ошибки внутри iframe.