Начало работы
Впервые здесь? Начните с пошагового руководства
От нуля до вашего модуля в вашей песочнице проводит через все шаги по порядку — от доступа к реестру до вашего модуля в оболочке песочницы. Эта страница — справочник к нему.
Это руководство проведёт вас от нуля до модуля Tango Vision, работающего в вашей собственной песочнице. Регистрация модуля в реестре платформы — отдельный шаг, доступный только администратору платформы; см. ingest ниже и Ваш модуль в оболочке песочницы — путь, который открыт вам сегодня.
Что вам понадобится
- Учётная запись разработчика — подайте заявку. Пока платформа для разработчиков в превью, регистрация только по приглашению.
- Node 24 (подойдёт и 22) и pnpm 10.18.2 — pnpm является стандартом платформы, а 10.18.2 — ровно та версия, которую ставит её CI. Все шаблоны и генераторы рассчитаны на неё. Её ставит corepack, который входит в Node 24:
corepack enable && corepack prepare pnpm@10.18.2 --activate. - Доступ к приватному реестру
https://npm.k8s.tangovision.dev/ - Учётные данные для API песочницы: либо
npx @tv/extension-sdk login(вход через CLI) в терминале, либо выпущенный в портале API-ключ для CI
Установка SDK
Пакеты области @tv/* живут в приватном реестре, который не отдаёт анонимные загрузки — запрос без токена возвращает 401. Укажите в .npmrc и реестр, и токен:
# .npmrc
@tv:registry=https://npm.k8s.tangovision.dev/
//npm.k8s.tangovision.dev/:_authToken=${TV_NPM_TOKEN}Экспортируйте токен реестра и установите пакет. Токен приходит отдельным сообщением от вашего контакта в Tango Vision, а не в письме об учётной записи:
export TV_NPM_TOKEN=... # храните в профиле оболочки или менеджере секретов
pnpm add @tv/extension-sdkСборка в Docker
.npmrc с адресом реестра, но без токена — самая частая причина падения CI: реестр отвечает 401 на любой запрос без токена, включая метаданные. В Dockerfile монтируйте токен как build-секрет, а не «запекайте» его в слой:
RUN --mount=type=secret,id=npm_token \
echo "@tv:registry=https://npm.k8s.tangovision.dev/" > .npmrc && \
if [ -f /run/secrets/npm_token ]; then \
echo "//npm.k8s.tangovision.dev/:_authToken=$(cat /run/secrets/npm_token)" >> .npmrc; \
fi && \
pnpm install --frozen-lockfileЧто внутри пакета
SDK поставляет всё одним пакетом, доступным через подпути:
| Подпуть | Что он даёт | Уровень |
|---|---|---|
@tv/extension-sdk | Корневой barrel — реэкспортирует manifest, context, events, api, heartbeat и i18n. Это не объединение всего: react, nestjs, testing, permissions, graph и version-check доступны только по своим подпутям. | @stable |
@tv/extension-sdk/manifest | Схема манифеста, валидатор, генераторы, checkExposes | @stable |
@tv/extension-sdk/context | Типы PlatformContext времени выполнения, а также readBuildingSelection(), subscribeBuildingSelection(), isSameBuildingSelection() и getAccessToken(), authorizationHeader(), resolveAccessToken() для аутентификации собственных запросов модуля к API (1.15.0+) | @stable |
@tv/extension-sdk/events | Каталог событий, конверт, субъекты | @stable |
@tv/extension-sdk/api | Типы PlatformApiClient | @stable |
@tv/extension-sdk/graph | Типизированный клиент графа здания — createBuildingGraphClient(), buildingGraphKeys, platformApiFromAxios(). Чтение и запись этажей, помещений, элементов и жизненного цикла changeset. | @stable |
@tv/extension-sdk/react | <PlatformProvider>, usePlatformContext(), useBuilding(), useOptionalBuilding(), useCurrentUser(), useSelectedBuildingId(), useBuildingSelection(), <LocationPicker> | @stable |
@tv/extension-sdk/i18n | resolveLocale() и ключи хранилища локали, принятые в оболочке, — следуйте за языком хоста, не изобретая собственный детектор | @stable |
@tv/extension-sdk/nestjs | @ModuleCapability(), @RequiresLicense(), McpSignatureGuard, TvHeartbeatModule | @stable |
@tv/extension-sdk/testing | createMockPlatformContext() | @stable |
@tv/extension-sdk/permissions | extractCatalog(), PermissionCatalog, SubjectEntry | @stable |
@tv/extension-sdk/heartbeat | Отчёт о живости в оболочку | @experimental |
@tv/extension-sdk/version-check | useVersionCheck(), <UpdatePrompt> — предложение обновиться развёрнутым клиентам | @experimental |
tv-sdk (bin) | CLI — см. ниже | смешанный |
В пакете едут четыре JSON-артефакта; их можно импортировать как отдельные подпути (import schema from '@tv/extension-sdk/manifest.schema.json') или читать прямо с диска: manifest.schema.json, permissions.snapshot.json, tv-events.snapshot.json, openapi.snapshot.json.
Каждый подпуть грузится и из CommonJS, и из ESM
Начиная с 1.3.1 каждый подпуть объявляет require рядом с import. До этого CJS-require большинства подпутей проходил проверку типов, а во время выполнения падал с ERR_PACKAGE_PATH_NOT_EXPORTED: зелено при компиляции, мертво в рантайме. Бэкенды модулей — CommonJS, так что если вы зафиксированы ниже 1.3.1, обновитесь.
Уровень символа определяет, может ли он измениться под вами — см. Уровни стабильности.
CLI
npx @tv/extension-sdk --helpВызывайте CLI по полному имени пакета
Бинарь CLI называется tv-sdk, и внутри проекта с установленным SDK npx tv-sdk тоже сработает. Не привыкайте к этой форме: вне такого проекта npx tv-sdk запросит имя tv-sdk у публичного npm, а это не наш SDK. Организация зарезервировала это имя в публичном npm 29.08.2026 (версия 0.0.1, «Reserved name»), так что прежний риск подмены закрыт — но резервация пустая, и SDK такая команда всё равно не запустит. Всегда пишите npx @tv/extension-sdk <команда>: эта форма резолвится через аутентифицированный @tv:registry, настроенный выше, где бы вы её ни запускали.
| Команда | Что делает |
|---|---|
login / logout / whoami | Вход в realm разработчиков прямо из терминала — см. Вход через CLI |
init-module <slug> --category=<c> | Генерирует модуль целиком — полный список файлов в Ваш первый модуль. Вне Tango Vision добавьте --external --scope=<ваша-область> --author="<Ваша компания>", как в пошаговом руководстве |
init-manifest <slug> --category=<c> | Генерирует только module-manifest.json |
validate <manifest> | Проверяет манифест по схеме |
check-exposes <manifest> | Проверяет канонический экспорт ./Shell в vite.config.ts |
check-pact <manifest> | Проверяет, что контракты events.subscribes совместимы со схемами издателей. Для каждого издающего модуля повторите --producer=<manifest>. |
check-events <snapshot.json> | Падает на ломающих изменениях относительно зафиксированного снимка схем событий |
snapshot-events <out.json> | Пишет снимок схем событий, с которым сравнивает check-events |
check-api <snapshot.json> | Падает на ломающих изменениях относительно снимка OpenAPI |
snapshot-api <out.json> | Пишет снимок OpenAPI, с которым сравнивает check-api |
snapshot-permissions <src> <out> | Перегенерирует снимок каталога разрешений |
write-version | Пишет version.json (версия + коммит + время сборки) как postbuild-шаг |
ingest <manifest> | Загружает манифест в реестр модулей. Для реестра платформы нужно разрешение platform.registry:write, которое есть только у администраторов платформы, — внешний разработчик получит 403. Для tv-api вашей собственной песочницы (--api=<apiUrl песочницы> и токен, который печатает sandbox connect) команда работает: там вы администратор этого одного API и этой одной базы. |
sandbox <subcommand> | Управление песочницами — create, list, connect, extend, reset, delete, а также install (требуется @tv/extension-sdk 1.14.0 или новее) |
init-module, init-manifest, validate, check-exposes, write-version и snapshot-permissions имеют уровень @stable. Остальные — включая login и семейство sandbox — @experimental: полезны, но набор флагов может измениться.
Анатомия модуля
tv-module-hello/
├── module-manifest.json ← контракт
├── src/Shell.tsx ← React, оболочка получает его как "./Shell"
├── src/locales/ ← en.json, ru.json
├── vite.config.ts ← remote федерации
└── package.jsoninit-module генерирует только фронтенд. Бэкенд необязателен: это сервис NestJS, который вы добавляете сами.
Манифест — это сердце модуля. Он объявляет идентификатор модуля, нужные ему разрешения, события, на которых он «говорит», инструменты Copilot и место монтирования его UI. Платформа читает его три раза:
- В вашем CI —
npx @tv/extension-sdk validate module-manifest.json - При публикации — реестр отклоняет недопустимый манифест
- Во время выполнения — оболочка Building OS собирает ваш модуль на его основе
Золотое правило
Ваш модуль обращается к состоянию платформы через SDK, а не в обход него.
Никакого ручного разбора ключей хранилища. Никаких собранных вручную URL Keycloak. Никаких жёстко прописанных https://tv-api…. Всем этим владеет SDK — поэтому один и тот же код без изменений работает и в вашей песочнице, и в продуктивной среде клиента.
Но какой именно вызов SDK нужен — зависит от того, где выполняется код, и на этом спотыкаются почти все.
// Внутри федеративного модуля. Вот этот вариант работает.
import { useSelectedBuildingId } from '@tv/extension-sdk/react';
import { useQuery } from '@tanstack/react-query';
export function WorkOrderList() {
const buildingId = useSelectedBuildingId(); // следует за селектором в шапке оболочки
return useQuery({
queryKey: ['work-orders', buildingId],
enabled: buildingId !== null,
queryFn: () => fetchWorkOrders(buildingId!),
});
}PlatformContext — usePlatformContext(), useBuilding() и его предварительно аутентифицированный клиент api — передаётся через React Context, а React Context не пересекает границу Module Federation. Внутри федеративного remote эти хуки бросают исключение, как бы корректно оболочка ни отрисовала вокруг него <PlatformProvider>. Они предназначены для кода, выполняющегося внутри бандла оболочки, и для ваших тестов, где провайдер вы отрисовываете сами.
Полная карта границы — что её пересекает, что нет и к чему обращаться с каждой стороны — на странице PlatformContext.
Далее
→ Ваш первый модуль собирает рабочий hello-world от начала до конца.
→ Федерация и оболочка — страница, которую стоит прочитать до того, как вы откроете vite.config.ts. Два правила оттуда нарушаются только внутри оболочки Building OS, где ваш собственный CI их не видит.