Skip to content

Начало работы ​

Впервые здесь? Начните с пошагового руководства

От нуля до вашего модуля в вашей песочнице проводит через все шаги по порядку — от доступа к реестру до вашего модуля в оболочке песочницы. Эта страница — справочник к нему.

Это руководство проведёт вас от нуля до модуля 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 и реестр, и токен:

ini
# .npmrc
@tv:registry=https://npm.k8s.tangovision.dev/
//npm.k8s.tangovision.dev/:_authToken=${TV_NPM_TOKEN}

Экспортируйте токен реестра и установите пакет. Токен приходит отдельным сообщением от вашего контакта в Tango Vision, а не в письме об учётной записи:

bash
export TV_NPM_TOKEN=...        # храните в профиле оболочки или менеджере секретов
pnpm add @tv/extension-sdk

Сборка в Docker

.npmrc с адресом реестра, но без токена — самая частая причина падения CI: реестр отвечает 401 на любой запрос без токена, включая метаданные. В Dockerfile монтируйте токен как build-секрет, а не «запекайте» его в слой:

dockerfile
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/i18nresolveLocale() и ключи хранилища локали, принятые в оболочке, — следуйте за языком хоста, не изобретая собственный детектор@stable
@tv/extension-sdk/nestjs@ModuleCapability(), @RequiresLicense(), McpSignatureGuard, TvHeartbeatModule@stable
@tv/extension-sdk/testingcreateMockPlatformContext()@stable
@tv/extension-sdk/permissionsextractCatalog(), PermissionCatalog, SubjectEntry@stable
@tv/extension-sdk/heartbeatОтчёт о живости в оболочку@experimental
@tv/extension-sdk/version-checkuseVersionCheck(), <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 ​

bash
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.json

init-module генерирует только фронтенд. Бэкенд необязателен: это сервис NestJS, который вы добавляете сами.

Манифест — это сердце модуля. Он объявляет идентификатор модуля, нужные ему разрешения, события, на которых он «говорит», инструменты Copilot и место монтирования его UI. Платформа читает его три раза:

  1. В вашем CI — npx @tv/extension-sdk validate module-manifest.json
  2. При публикации — реестр отклоняет недопустимый манифест
  3. Во время выполнения — оболочка Building OS собирает ваш модуль на его основе

Золотое правило ​

Ваш модуль обращается к состоянию платформы через SDK, а не в обход него.

Никакого ручного разбора ключей хранилища. Никаких собранных вручную URL Keycloak. Никаких жёстко прописанных https://tv-api…. Всем этим владеет SDK — поэтому один и тот же код без изменений работает и в вашей песочнице, и в продуктивной среде клиента.

Но какой именно вызов SDK нужен — зависит от того, где выполняется код, и на этом спотыкаются почти все.

tsx
// Внутри федеративного модуля. Вот этот вариант работает.
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 их не видит.

Создано на платформе Tango Vision. Вопросы? developers@tango.vision