Ваш модуль в оболочке песочницы
Ваш модуль — это remote федерации. По-настоящему он работает только тогда, когда его загружает оболочка, и для вашего модуля это оболочка песочницы: тот же образ Building OS, развёрнутый отдельно и привязанный к вашей песочнице. В продуктивную оболочку ваш модуль не попадает никогда: код модуля выполняется с полным доверием той оболочки, которая его загрузила, поэтому стороннему коду выделяется собственная.
Начиная с 1.14.0
tv-sdk sandbox install и скрипты dev:remote / serve:remote появились в @tv/extension-sdk 1.14.0. Используйте 1.15.1 или новее: в более ранних версиях есть ошибки в командах с этой страницы, а модуль, созданный версией 1.14.0, не собирается. У модуля, созданного версией 1.13.0 или более ранней, этих скриптов нет — пересоздайте каркас или добавьте их вручную, как показано в шаге 1. Что не так в каждой старой версии: Известные проблемы по версиям SDK.
1. Отдавайте remote со своей машины
@originjs/vite-plugin-federation создаёт remoteEntry.js при сборке, а не в dev-сервере. Поэтому pnpm dev отдаёт приложение вообще без remote: на запрос /assets/remoteEntry.js dev-порт отвечает HTML-страницей приложения, и оболочка, нацеленная на него, показывает Module failed to load, а консоль браузера сообщает о скрипте-модуле с MIME-типом "text/html". Два терминала, две команды, запускайте в таком порядке:
# терминал 1 — пересборка при каждом изменении
pnpm dev:remote # vite build --watch
# терминал 2 — отдача собранного
pnpm serve:remote # vite preview --port 6001 --strictPortПока первая сборка не закончилась, serve:remote останавливается с The directory "dist" does not exist. Did you build your project?; запустите его снова, когда dev:remote соберёт проект. После этого ваш remote будет здесь:
http://localhost:6001/assets/remoteEntry.jsРегистрируйте его с localhost, а не 127.0.0.1: в macOS сервер preview отвечает только на localhost.
6001 — закреплённый в каркасе порт preview (порт dev плюс 1000). strictPort задан намеренно: без него vite перескочит на следующий свободный порт и уведёт remote из-под записи реестра, которая на него ссылается.
Если в вашем каркасе нет dev:remote / serve:remote, их эквиваленты — vite build --watch и vite preview --port 6001 --strictPort плюс preview: { port: 6001, strictPort: true, cors: true } в vite.config.ts.
2. Установите модуль в свою песочницу
npx @tv/extension-sdk sandbox install module-manifest.json \
--sandbox=sbx_... \
--remote-entry=http://localhost:6001/assets/remoteEntry.jsДва вызова к tv-api вашей собственной песочницы, по порядку:
POST /api/v1/registry/modules/ingestс вашим манифестом и URL remote — это та запись реестра, из которой рисует оболочка.POST /api/v1/licensesна наполненное здание: оболочка рисует из состояния лицензий, поэтому зарегистрированный, но нелицензированный модуль просто отсутствует. Уже существующая лицензия отвечает409, и команда считает это успехом — её безопасно запускать повторно.
--sandbox=<id> за один запрос находит URL API, токен, наполненное здание и console URL. Здесь нужен идентификатор песочницы вида sbx_…: его печатает sandbox create, а npx @tv/extension-sdk sandbox list показывает идентификаторы всех ваших песочниц. Это не slug из URL песочницы.
С --sandbox держите в TV_API_TOKEN ключ с портала (или полагайтесь на login) и не передавайте --token. Поиск песочницы идёт в сервис, который управляет песочницами, с этими учётными данными, а два вызова выше используют собственный токен песочницы из её записи. Не выполняйте в том же терминале строки export, которые печатает sandbox connect: тогда TV_API_URL отправит поиск в вашу песочницу, и она ответит 404.
Без --sandbox используется пара TV_API_URL / TV_API_TOKEN, которую печатает tv-sdk sandbox connect.
Обоим эндпоинтам нужны права администратора платформы — и внутри своей песочницы они у вас есть: tv-api песочницы принимает собственный токен как администратора этого одного API и этой одной базы, и ничего больше (tv-platform#795). Поэтому 401 или 403 от этой команды означают неверные учётные данные: проверьте их по таблице Если что-то не так. SDK 1.15.0 и более ранние печатают после любого из них старое пояснение (This is expected today: … ask for the module to be registered for you), написанное до этого изменения; не обращайте на него внимания.
Смена адреса, с которого вы отдаёте remote, — это повторный запуск команды, а не пересборка оболочки.
3. Откройте
<хост вашей песочницы>/sandbox/<slug вашего модуля>Хост песочницы — это consoleUrl в записи песочницы (тот же хост, что и apiUrl: оболочка отдаётся на /, API остаётся на /api и /socket.io, поэтому UI и бэкенд у них общий origin). consoleUrl равен null, если для вашей песочницы оболочка не развёрнута.
Оболочка песочницы монтирует один общий маршрут /sandbox/:slug, который ведут поля remoteEntryUrl, federationName и exposeName из вашей записи реестра; маршруты /<slug> продуктивной оболочки тут ни при чём.
4. Войдите
Оболочка песочницы аутентифицируется в реалме Keycloak sandboxes. Самостоятельной регистрации там нет. Войти можно двумя способами.
Приглашённые разработчики. Учётную запись в этом реалме создаёт для вас команда Tango Vision, проставляя в клейм tenant_id значение organizationId вашей песочницы. Пришлите slug песочницы (поддомен из её apiUrl) контакту, указанному в приветственном сообщении, на почту или в Telegram; адрес developers@tango.vision доходит до тех же людей. Отвечаем в тот же рабочий день по московскому времени. Учётная запись создаётся с временным паролем, и при первом входе вас попросят задать собственный.
Забыли пароль от песочницы? На странице входа в песочницу нет ссылки для сброса. Напишите своему контакту — мы зададим новый временный пароль.
Сотрудники Tango Vision. Сотрудники входят под корпоративной учётной записью кнопкой Tango Vision staff на странице входа. Это работает только для участника группы /sandbox-developers, чью учётную запись в песочнице оператор связал с его корпоративной учётной записью. Автоматически ничего не создаётся и не связывается, а собственного пароля у такой учётной записи нет.
Чего ожидать, а чего нет
- Chrome или Firefox, не Safari. Safari блокирует загрузку скрипта с
http://localhostна страницу с публичного HTTPS-origin. Chrome при первом запуске может спросить разрешение на доступ к локальной сети — разрешите. - Берите токен через
getAccessToken()илиauthorizationHeader()из@tv/extension-sdk/context, а не изlocalStorage['access_token']. В оболочке песочницы мост к старому ключу выключен: оболочка, зеркалящая живой access-токен в ключ, читаемый любым скриптом, отдавала бы его чужому JavaScript. Модули, читающие этот ключ напрямую, в оболочке песочницы не аутентифицируются; модули, использующие функции токена из SDK, работают как обычно. См. Вызов API из модуля. - Remote загрузится, только если его пропустят два независимых барьера — CSP браузера и собственный список разрешённых origin у оболочки. В оболочке песочницы оба настроены пропускать
localhost. Remote с другого адреса требует изменения обоих, и это запрос к нам, а не настройка на вашей стороне. - Лицензионной проверки на этом маршруте нет, но вход всё равно нужен: маршрут находится внутри защищённой области оболочки.
- Бэкенд вашего модуля всё это не разворачивает.
installрегистрирует и лицензирует фронтенд-remote. Бэкенд работает там, где вы его запустили, и обращается к tv-api песочницы по сети, как любой другой клиент.
Правила загрузки модуля с localhost
Всё сказанное выше в виде списка. Если страница пустая, одно из этих условий не выполнено.
- С localhost загружает только оболочка песочницы. Content-Security-Policy продуктивной оболочки не допускает
http://localhost, а универсальный маршрут модулей в ней выключен. Оболочка песочницы допускает loopback-адреса и открывает любой модуль из реестра вашей песочницы по адресу/sandbox/<slug>. - Только loopback. Допускаются
http://localhost:<порт>иhttp://127.0.0.1:<порт>. Адрес в локальной сети, туннель или любой другой хост не допускаются. Регистрируйтеlocalhost: в macOSserve:remoteне отвечает на127.0.0.1. - Отдавайте сборку, а не dev-сервер.
vite devне создаётassets/remoteEntry.js: federation-remote существует только в сборке. Запуститеpnpm dev:remoteиpnpm serve:remoteодновременно. - Адрес фиксирован:
http://localhost:6001/assets/remoteEntry.js. Preview работает соstrictPort, поэтому занятый порт даёт ошибку, а не тихий переход на другой, и с включённым CORS. - Запись в реестре должна указывать на него.
tv-sdk sandbox installс параметром--remote-entry=http://localhost:6001/assets/remoteEntry.jsрегистрирует манифест в вашей песочнице и выдаёт лицензию на созданное здание. Slug в нижнем регистре через дефис, не длиннее 64 символов; remote экспортирует./Shell. - Chrome или Firefox, не Safari. Оболочка открыта по HTTPS и загружает скрипт с HTTP loopback; Safari это блокирует. Chrome может один раз запросить доступ к устройствам в локальной сети: разрешите.
- Модуль загружается только в вашем браузере.
localhostэто ваш компьютер. Коллега, открыв тот же адрес, получит ошибку загрузки. Коллега со своей учётной записью в вашей песочнице может вместо этого отдавать модуль со своей машины (см. Два разработчика, одна песочница). Чтобы показать модуль кому-то ещё, разместите собранный бандл на хосте, который допускает оболочка песочницы, и попросите нас разрешить этот хост. - Авторизация только через SDK, не через
localStorage['access_token']. Оболочка песочницы не записывает туда токен, аPlatformContext.apiнедоступен, потому что React-контекст не пересекает границу federation. На каждый запрос к относительному пути/api/…вызывайтеauthorizationHeader()из@tv/extension-sdk/context. - Ваш бэкенд работает на вашем компьютере. Политика оболочки позволяет фронтенду обращаться к
http://localhost:<порт>, поэтому локальный бэкенд доступен из браузера. Платформа до него не дотянется: у песочницы нет исходящего доступа в интернет и маршрута к вашему компьютеру, поэтому Copilot не сможет вызвать MCP-эндпоинт на localhost. Про проверку вызывающего и настройку CORS для этого origin — см. Добавьте бэкенд. i18nextиreact-i18nextв shared либо оба, либо ни одного, и сохраняйте aliasuse-sync-external-store/shimиз шаблона. Нарушение проявляется только внутри оболочки, где ваш CI его не видит.
Если страница пустая
| Что вы видите | Обычная причина |
|---|---|
Module failed to load, а в консоли видно, что запрос за remoteEntry.js не прошёл | Не запущен pnpm serve:remote, либо запись реестра указывает на 127.0.0.1 (в macOS сервер отвечает только на localhost) |
| Module failed to load, а консоль сообщает о скрипте-модуле с MIME-типом "text/html" | Запись реестра указывает на порт pnpm dev, а dev-сервер remote не создаёт. Переустановите модуль с --remote-entry=http://localhost:6001/assets/remoteEntry.js |
| Remote грузится с другого порта | Нет strictPort: vite сменил порт, а запись реестра по-прежнему указывает на 6001 |
| Module not in the registry | Модуль не установлен, slug в URL не совпадает с частью id манифеста после module-, либо у вашей учётной записи в песочнице ещё не задан tenant_id (тогда в боковом меню ещё и «Реестр модулей недоступен — показан кэшированный список.»). Запустите sandbox install заново и перезагрузите страницу; если надпись в меню не пропала, напишите своему контакту |
| Вход выполнен, но все вызовы API дают 401 | Модуль читает localStorage['access_token']; вместо этого передавайте authorizationHeader() из @tv/extension-sdk/context в каждом запросе |
| Падение из-за второй копии React при монтировании | Нет алиаса use-sync-external-store/shim; см. Федерация и оболочка |