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
Архитектура
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.read | getAll, get, getActive, export |
collections.write | update, import, extension meta |
http.fetch | api.http.fetch (лимиты sandbox) |
secrets | api.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-api → LaitapExtensionAPI.
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, kindexecuteCommand(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.configurationonDidChange(listener)
laitap.workspace
- getId(), getLabel(), getKind() — local | personal | team |
|---|
- 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/editonWillSaveCollections — можно 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. Команда syncNow → exportNative → 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.