enВойти в Senler

Встроенная страница

Настройка страницы

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

Настройка страницы. 1. раздел «Встроенная страница»
1. раздел «Встроенная страница»

Переключатель «Показывать главную встроенную страницу приложения» добавляет в кабинет проекта отдельный пункт со страницей плагина. Если его выключить, этот пункт исчезнет, но основной URL продолжит автоматически использоваться в конфигураторах инструментов агента и шагов автоматизаций.

Основной URL и режим разработчика

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

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

После изменения URL или режима разработчика нажмите кнопку сохранения.

Настройка страницы. Отмеченные элементы: 2. «Показывать главную встроенную страницу приложения»; 3. «Основной URL»; 4. «Режим разработчика»; 5. URL для разработчиков; 6. кнопку сохранения
2. «Показывать главную встроенную страницу приложения» · 3. «Основной URL» · 4. «Режим разработчика» · 5. URL для разработчиков · 6. кнопку сохранения

Ниже расположена отдельная настройка действий приложения. Она публикует выбранные методы backend в MCP и сохраняется своей кнопкой; переключатель и URL встроенной страницы на неё не влияют.

Тестирование и режимы запуска

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

Тестирование и режимы запуска. 2. доступный проект
2 / 2
2. доступный проект

Все способы открытия используют единый 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.