enВойти в Senler

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

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

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

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

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

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

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

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

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

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

Максимальная ширина iframe

В поле «Максимальная ширина страницы» укажите целое число от 1 до 100000. Пустое поле использует стандартные 1024 px. Это начальный максимум для окон приложения; Bridge может временно заменить его. Сохраните настройку той же кнопкой, что и URL страницы.

Встроенная страница. 1. «Максимальная ширина страницы»
1. «Максимальная ширина страницы»

«Протестировать» использует текущую ширину из поля, поэтому её можно проверить до сохранения. Для остальных запусков применяется сохранённая настройка.

Значение ограничивает ширину самой страницы внутри iframe; рамка и отступы кабинета добавляются отдельно. На узком экране iframe сжимается до доступной ширины. Оболочка окна сохраняет место для заголовка и кнопок кабинета, поэтому при маленьком лимите она может быть шире iframe. Настройка общая для главной страницы, тестового запуска, конфигураторов инструментов агента, шагов автоматизации и источников воронки во всплывающих окнах. Она действует и при выключенном показе главной страницы.

Для постоянной ширины достаточно настройки в кабинете. В Bridge 2.1.2 приложение может временно изменить максимум открытого окна:

await bridge.connect();
await bridge.setFrameMaxWidth(1200);
await bridge.setFrameMaxWidth(640);
await bridge.setFrameMaxWidth(null); // Вернуть сохранённую настройку или 1024 px.

Promise подтверждает, что кабинет принял новый лимит. Пересчёт размеров браузером может завершиться позже; для получения фактической ширины реагируйте на событие resize окна iframe. Сам вызов не перезагружает страницу и сохраняет заполненную форму.

Значение из Bridge имеет приоритет над настройкой приложения и действует до закрытия окна или запуска другого приложения либо конфигуратора. Перезагрузка и переходы внутри того же iframe сохраняют лимит. Он не записывается в настройки приложения. Обновление сохранённой настройки не отменяет активное значение из Bridge; вызов с null применяет актуальную настройку.

Проверьте bridge.getContext()?.frame_max_width === true перед вызовом. Встроенная страница в боковой панели автоматизации сохраняет ширину панели; в ней и в хосте без поддержки этой возможности метод отклоняет Promise с code: 'unsupported'. Для изменения ширины используйте собранный пакет Bridge 2.1.2; метод доступен начиная с версии клиента 2.1.0.

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

Нажмите «Протестировать» и выберите доступный проект. Если режим разработчика включён, тест откроет 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 или funnel_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 "@senlerio/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.