Плагины

Разработка плагина (SDK)

Контракт TypeScript-плагина, правила манифеста, чеклист артефактов и локальная проверка публикации.

Плагин Conveyor — отдельный Node-процесс: он публикует манифест в plugin-manager и принимает задачи исполнителей по TCP. На TypeScript удобнее через npm-пакет @kosolapus/plugin-ts-sdk.

На какие вопросы отвечает раздел: Как устроен процесс плагина? Какие поля manifest допустимы? Как собрать свой пакет и проверить публикацию?

Перед обновлением SDK сверьте матрицу совместимости и npm view @kosolapus/plugin-ts-sdk version.

Как плагин стыкуется с платформой

При старте процесс отправляет manifest (список nodeType, адрес pull для batch). Plugin-manager сохраняет каталог. После включения плагина в редакторе узлы попадают в библиотеку. При Запуске процесса runtime передаёт задачу в control plane, control plane маршрутизирует её на TCP-порт исполнителя плагина.

sequenceDiagram
  participant P as Процесс плагина
  participant PM as Plugin-manager
  participant UI as Редактор
  participant RT as Runtime
  participant CP as Control plane

  P->>PM: TCP manifest и pull
  PM->>PM: Каталог и staticAssets по URL если есть
  UI->>PM: Список плагинов enable disable
  RT->>CP: Задача на nodeType плагина
  CP->>P: executor.task
  P->>CP: Результат шага
  CP->>RT: Продолжение процесса

Подробнее о роли plugin-manager: сервис plugin-manager. Модель в целом: Плагины и интеграции.

Можно и нельзя

Контракт публикации задаёт тип PluginManifestRequestV2 в SDK. Ниже правила для собственного пакета.

Можно

  • поля manifest только из типов SDK;
  • в массиве executors[] только { nodeType }. Transport, DTO и help задаются в @Executor и ответе batch pull;
  • несколько исполнителей в одном процессе: реестр классов и executorsFromClasses;
  • секреты на узле через FieldDecorator с type: 'ref' и secretKind. Массив variables в manifest оставьте пустым;
  • отдельный output-порт на каждое поле OutputDto;
  • файл help.md на каждый исполнитель. Сборка: tsc, затем copy:help, bundle, prune:dist;
  • npm-зависимости домена (HTTP-клиенты и т.п.) внутри пакета плагина;
  • переменные окружения для pull, RPC, control plane и для домена (например base URL внешнего API).

Нельзя

  • произвольные поля manifest, которых нет в SDK;
  • секреты в variables manifest;
  • presets внутри PluginManifestRequestV2 (presets регистрируются отдельно через preset-service);
  • один blob-output (result, data) вместо структурированных портов, если ответ API структурирован;
  • адрес 127.0.0.1 в pull.host, если plugin-manager работает в другом контейнере;
  • разные значения у PLUGIN_TCP_PORT, EXECUTOR_TCP_PORT и PORT в одном процессе.

Пример skeleton manifest (без секретов):

{
  "kind": "plugin_manifest_request_v2",
  "pluginId": "my-integration",
  "publicationVersion": "0.0.1",
  "label": "My integration",
  "description": "Краткое описание для каталога",
  "variables": [],
  "staticAssets": [],
  "executors": [{ "nodeType": "plugin.my-integration.action" }],
  "pull": { "host": "my-integration", "port": 9409 }
}

Чеклист: от идеи до готового плагина

Минимальный путь предполагает один исполнитель. Расширения (несколько узлов, секреты, внешние API) см. раздел Паттерны на учебном примере.

Работайте в checkout репозитория plugins или в своём fork с тем же layout workspace.

#ШагАртефактКритерий готовности
1IntakeЗапись: pluginId, список nodeType, контур (sidecar / host / external)Решение до кода
2Пакет<id>/package.json: имя @conveyor/plugin-<id>, conveyorPluginBuild, scripts build/start, зависимость SDK по матрицеtsc проходит
3TypeScript<id>/tsconfig.json: decorators, strict, outDir: dist
4WorkspaceКаталог <id> в workspaces корневого package.jsonnpm install из корня checkout
5Точка входаsrc/main.ts: bootstrapPluginExecutorMicroservice(AppModule)процесс стартует
6Pull из envsrc/getUrlFromEnv.ts: host и port для manifest и transportсовпадает с compose или .env
7Manifestsrc/<id>-manifest.builder.ts: buildManifestRequest(), pull, executors[]JSON manifest в логах при старте
8Batch pullsrc/<id>-publication-batch.source.ts: buildExecutorBatchPullResponseFromEntriesplugin-manager получает transport
9Реестрsrc/executors.index.ts: список классов исполнителей
10Nest modulesrc/app.module.ts: PluginPublicationTcpHostModule.forRoot, BulkExecutorRouterService, pluginId в optionswiring полный
11Исполнительsrc/.../executor.ts: @Executor, InputDto/OutputDto, transport: { type: 'tcp', params: publicationPullEndpointFromEnv() }узел в палитре после enable
12Справка узлаsrc/.../help.md, loadHelpFromFile, script copy:helphelp.md в dist/
13README пакета<id>/README.md: назначение, таблица узлов, конфиг в редакторе, блок для разработчика
14Env<id>/env.example или комментарии в README пакеталокальный npm start с demo
15Сборкаnpm run build -w @conveyor/plugin-<id> из корня checkoutесть dist/run.cjs
16Composesidecar-сервис по образцу в README repo (файлы compose.demo.yml, compose.env.example)контейнер healthy, publication ok
17Проверкараздел нижеЗапуск с узлом плагина успешен

Сборка на хосте из корня checkout:

npm install
npm run build -w @conveyor/plugin-<id>
cd <id>
npm start

Переменные RPC, control plane, pull и порты executor должны совпадать с контуром, в котором вы проверяете плагин. Шаблоны env и compose: репозиторий plugins.

Паттерны на учебном примере

В checkout репозитория plugins откройте пакет llm/ (только как учебный образец, не как фиксированный список интеграций платформы). Там показаны типовые усложнения.

Несколько исполнителей. Один AppModule, реестр в executors.index.ts, разные nodeType (например llm.ollama.generate и plugin.huggingface.inference). Manifest перечисляет все nodeType, batch pull отдаёт transport для каждого.

Секрет на узле, не в manifest. В OpenAI-исполнителе поле API key оформлено как type: 'ref', secretKind: 'OPENAI_API_KEY', static: true. Значение берётся из «Хранилища» редактора при Запуске.

Конфиг домена. У Ollama-исполнителя base URL можно задать static-полем узла. В Docker sidecar часто добавляют fallback через переменную окружения (например OLLAMA_BASE_URL в compose repo).

Сервисный слой. HTTP-логика вынесена в ollama.service.ts, executor остаётся тонким адаптером между SDK и доменом.

Несколько файлов help. В package.json поле conveyorPluginBuild.helpMarkdown может указывать { "distRelativePath": "…/help.md" }, если primary help не первый в dist/.

Расширенные возможности (staticAssets, формы воркспейса, widgets, presets) описаны в SDK и в исходниках repo. На landing пошаговый tutorial для них не приводится. Presets регистрируются через preset-service, не через manifest v2.

Проверка публикации

  1. Исполнитель запущен в контуре, токены совпадают с ядром (см. Подключение).
  2. В редакторе на вкладке «Плагины» включите переключатель. Статус «онлайн» означает, что plugin-manager видит pull-endpoint.
  3. На вкладке «Палитра» появляются узлы с вашими nodeType.
  4. В логах процесса плагина: plugin_wire_outbound_manifest_ok. В логах plugin-manager: plugin_publication_committed.
  5. Маркер plugin_static_cached появляется только если в manifest непустой массив staticAssets.
  6. Соберите процесс с одним узлом плагина и выполните тестовый Запуск.

Секреты и параметры узлов настраиваются в полях узла на канвасе (тип ref → «Хранилище»), а не на карточке плагина, если variables в manifest пусты.

Диагностика

СимптомЧто проверить
unauthorizedPLUGIN_MANAGER_INGRESS_TOKEN, PLUGIN_CONTROL_PLANE_KEY совпадают с ядром
batch pull не доходитpull.host и порт достижимы из сети plugin-manager (DNS sidecar или IP хоста)
узлов нет в палитрелоги manifest, плагин включён в UI

Operational-детали demo и external stack: Подключение и интеграции и README репозитория plugins.

Docker-образ одного плагина

В checkout repo используется multi-stage Dockerfile.plugin. Сборка из корня checkout (подставьте <id>):

docker build -f Dockerfile.plugin --build-arg PLUGIN_DIR=<id> -t plugin-<id>:local .

SDK резолвится из npm lockfile, каталог dist/ на хосте перед сборкой образа не обязателен. Sidecar-сервисы в compose repo описаны в README.

Дальше