Extension Guides

Разработка расширений Laitap — манифест, API, упаковка и публикация в каталоге. SDK: @laitap/extension-api · Каталог расширений

Laitap Extensions — система плагинов по модели VS Code: манифест laitap-extension.json, Extension Host (Electron Utility Process), declared capabilities и узкий Extension API.

Что можно делать

  • Команды в контекстном меню коллекции и в sidebar
  • Чтение и изменение коллекций (локальный workspace)
  • HTTP через прокси main process (audit log)
  • Секреты в OS keychain
  • Периодические задачи (scheduler)
  • Реакция на сохранение коллекций (onDidChangeCollections)

Быстрый старт

npm install --save-dev @laitap/extension-api
npx create-laitap-extension ./my-ext acme.my-ext
cd my-ext && npm install && npm run pack

Установка: Настройки → Расширения — перетащите .laitap-ext или «Установить из файла».

SDK

РесурсURL
npm@laitap/extension-api
Каталогlaitap.pro/extensions
ПубликацияPUBLISHING.md

Архитектура

Renderer (Vue)  ←→  Extension Utility Host  ←→  Main IPC (http, secrets, install)
       ↑                      ↑
  Extension API         extension.js (activate)

Код расширения не имеет доступа к window.electronAPI и Node integration в renderer.

Минимальный пример

{
  "id": "acme.hello",
  "name": "Hello",
  "version": "0.1.0",
  "publisher": "acme",
  "description": "Demo extension",
  "main": "./extension.js",
  "engines": { "laitap": "^26.6.0" },
  "activationEvents": ["onStartup"],
  "capabilities": ["collections.read"],
  "contributes": {
    "commands": [{ "id": "hello", "title": "Say hello" }],
    "menus": {
      "collection/context": [{ "command": "hello", "when": "collection" }]
    }
  }
}

Поля

ПолеОбязательноОписание
idдаУникальный id: publisher.short-name
nameдаОтображаемое имя
versionдаSemver
publisherдаДолжен совпадать с id подписи для каталога
mainдаEntry: activate / deactivate
engines.laitapрекомендуетсяСовместимая версия приложения
activationEventsнетonStartup, onCommand:cmdId
capabilitiesда*Права — пользователь подтверждает при установке

Capabilities

CapabilityДоступ
collections.readgetAll, get, getActive, export
collections.writeupdate, import, extension meta
http.fetchapi.http.fetch (лимиты sandbox)
secretsapi.secrets.* (keychain)

Menus

Menu idГде
collection/contextКонтекстное меню дерева коллекций
sidebar/actionsКнопки в шапке sidebar

when: collection | folder | request | smartFolder

Configuration & secrets

contributes.configuration — настройки в UI расширения.

contributes.secrets — чувствительные значения (webhook, token) через api.secrets, не plain text.

Типы: @laitap/extension-apiLaitapExtensionAPI.

activate / deactivate

exports.activate = async function (api) {
  api.commands.registerCommand('my.cmd', async (ctx) => {
    const col = api.collections.getActive()
    await api.window.showInfo(col ? col.name : 'No collection')
  })
}

exports.deactivate = function () {}

laitap.commands

  • registerCommand(id, handler)handler(ctx) с collectionId, nodeId, kind
  • executeCommand(id, ctx?)

laitap.collections

  • getAll(), get(id), getActive()
  • exportPostman(id), exportNative(id), importPostman(json)
  • update(id, patch)
  • getExtensionMeta(collectionId), setExtensionMeta(collectionId, patch)

laitap.http

const res = await api.http.fetch({
  url: 'https://example.com/api',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ ok: true })
})

Лимиты: rate limit, max body — см. sandbox в приложении. Все запросы в audit log.

laitap.secrets

await api.secrets.set('webhookUrl', url)
const url = await api.secrets.get('webhookUrl')
await api.secrets.delete('webhookUrl')

laitap.configuration

  • get(key) — из contributes.configuration
  • onDidChange(listener)

laitap.workspace

- getId(), getLabel(), getKind()localpersonalteam
- onDidChange(listener)

laitap.window

  • showInfo, showError, showInput, showConfirm, showChoice

laitap.scheduler

api.scheduler.setInterval(async () => { /* sync */ }, 60_000)

Лимит числа таймеров и минимальный интервал enforced sandbox.

laitap.events

  • onDidChangeCollections — после load/save/edit
  • onWillSaveCollections — можно cancel()
  • onWillSendRequest / onDidReceiveResponse — хуки HTTP pipeline приложения

Wave 2 (план)

laitap.auth (OAuth), laitap.ui (WebView panels) — по запросу авторов.

Из проекта расширения

npm run pack
# → acme-my-ext-0.1.0.laitap-ext

Использует laitap-pack-extension из @laitap/extension-api.

Из репозитория Laitap

npm run pack:extension -- path/to/extension

Подпись Ed25519

openssl genpkey -algorithm ED25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

export LAITAP_EXT_SIGN_KEY_PATH=./private.pem
export LAITAP_EXT_PUBLIC_KEY_ID=acme
npm run pack

В пакете появится laitap-extension.sig.json.

Формат .laitap-ext

ZIP-архив:

  • laitap-extension.json
  • entry (extension.js, …)
  • опционально laitap-extension.sig.json

Локальная установка

  • Drag-and-drop на окно Laitap
  • Настройки → Расширения → Установить из файла / папки

Unsigned-пакеты устанавливаются с предупреждением.

Проверка подписи

Приложение сверяет хеши файлов и Ed25519 подпись с доверенным .pub ключом (resources/extension-trust/ + userData).

Каталог

  • Web: laitap.pro/extensions
  • JSON: https://laitap.pro/data/extensions-catalog.json
  • In-app: Настройки → Расширения → Каталог laitap.pro

Записи с signatureRequired: true требуют valid подпись verified publisher.

Публикация для авторов

1. Подпишите пакет ключом Ed25519
2. Отправьте на support@laitap.pro:
- .laitap-ext (signed)
- public.pem + publisher id
- описание, теги, иконка (SVG 44×44 или emoji)
3. После review — запись в каталог и publishers.json

Verified registry

Bundled: resources/extension-trust/publishers.json

Пользовательский overlay: %APPDATA%/Laitap/extension-trust/publishers.json (Windows).

Review checklist (maintainers)

  • [ ] Publisher id уникален
  • [ ] Подпись valid, publisher совпадает с manifest
  • [ ] Capabilities минимально необходимы
  • [ ] Нет obfuscated / eval в extension.js
  • [ ] HTTP только к заявленным доменам (audit при тесте)
  • [ ] Версия semver, changelog в описании

Обновления

In-app проверка обновлений сравнивает semver установленной версии с каталогом.

Сборка каталога (maintainers):

npm run website:extensions:signed

Встроенное расширение laitap.bitrix24-sync — эталон для sync-кейса.

Возможности

  • Выгрузка коллекции JSON на диск Bitrix24
  • Загрузка с диска
  • Three-way merge при конфликте
  • Autosync по onDidChangeCollections + scheduler

Манифест (фрагмент)

{
  "id": "laitap.bitrix24-sync",
  "capabilities": ["collections.read", "collections.write", "http.fetch", "secrets"],
  "contributes": {
    "commands": [
      { "id": "syncNow", "title": "Bitrix24 — синхронизировать" }
    ],
    "menus": {
      "collection/context": [{ "command": "syncNow", "when": "collection" }]
    },
    "secrets": {
      "webhookUrl": { "title": "Incoming webhook" }
    }
  }
}

Сценарий sync

1. Пользователь выбирает коллекцию
2. Команда syncNowexportNative → POST на REST webhook
3. setExtensionMeta сохраняет bitrix24FileId, timestamp
4. При pull — merge local/remote/base

Настройка

1. Bitrix24 → Разработчикам → Входящий webhook
2. В расширении: URL портала + webhook в secrets
3. Контекстное меню коллекции → синхронизация

Исходники: resources/extensions/bitrix24-sync/ в репозитории Laitap.