Создание плагина
Плагин представляет собой отдельную программу. QueryLane запускает команду из plugin.json плагина и общается с ней по JSON-RPC 2.0 через stdin и stdout, по одному JSON-сообщению в строке. Писать плагин можно на любом языке. Официальные плагины и инструменты, которые их собирают, подписывают и публикуют, находятся в репозитории querylane-plugins.
QueryLane устанавливает только пакеты, подписанные официальным ключом QueryLane. Пакет, подписанный другим ключом, можно собрать и проверить, но приложение откажется его устанавливать.
Подготовка репозитория
- Node 24, затем
npm install. - Копия приложения QueryLane в
../dataflow3/roadmapили путь к ней вQUERYLANE_APP_DIR. Тесты запускают плагины через собственный хост плагинов приложения. - Для
npm run buildзакрытый ключ Ed25519 в формате PEM в~/.querylane/plugin-signing/querylane-official.pemили путь к нему вQUERYLANE_PLUGIN_SIGNING_KEY.
Создание плагина
- Создайте папку
plugins/<name>/сplugin.jsonи кодом, при желании добавьте иконку и руководство для ИИ-ассистента. - Добавьте папку в
PLUGIN_SOURCESвtools/lib/config.mjsвместе с платформами, например{ dir: 'plugins/hello', platforms: ['darwin-aarch64'] }. Для папки, которой нет в списке, тест манифестов упадёт. - Дайте плагину английские и русские тексты с одинаковыми ключами, это тоже проверяет тест манифестов.
plugin.json
| Поле | Значение |
|---|---|
schemaVersion, apiVersion | Оба равны 1 |
id | Идентификатор из строчных частей через точку, например com.example.hello |
version, minAppVersion | 1.2.3; самая старая версия QueryLane, которая нужна плагину |
author | { "name": "…", "url": "…" } |
icon | Квадратная SVG или PNG в пакете, до 128 КиБ |
permissions | Любые из network, download, spawnProcesses, userFiles |
platforms | Ключи вроде darwin-aarch64, у каждого command, необязательные команды setup и env |
contributes.steps | id, language кода шага, starterCode и template: true, чтобы значения ${...} подставлялись до того, как плагин получит код |
contributes.settings | key, type, default, group, scope и другие поля |
contributes.tools | name, description, inputSchema (JSON Schema) |
contributes.runEvents | ["finished"], чтобы получать уведомления о запусках |
contributes.guide | Markdown-файл с инструкциями по написанию кода шагов |
locales, defaultLocale | Тексты по языкам и резервный язык |
В командах и значениях env можно использовать ${pluginDir} (распакованный пакет), ${dataDir} (папка, которая переживает обновления) и ${node} (Node.js, на котором работает QueryLane). Команды setup выполняются один раз перед первым запуском и после обновлений.
Типы настроек: string, number, boolean, enum, stringList, secret и proxyProfile. scope принимает значения plugin (по умолчанию), step или notification.
Каждому языку нужны name и description, а также плоские ключи: steps.<id>.title, settings.<key>.label, settings.<key>.description, settings.<key>.options.<option>, groups.<group> и errors.<reasonCode>.
Протокол
QueryLane отправляет такие запросы:
| Метод | Ответ |
|---|---|
initialize | { "apiVersion": 1 } |
prepare | Выполняется один раз после установки и обновления, например чтобы скачать браузер |
step/run | { "summary": {...}, "rows": [...] } для code, settings, inputs, variables, timeoutMs, artifactsDir и других параметров |
tool/call, tool/close | { "data": ..., "images": [...] } на вызов инструмента; tool/close завершает сессию инструментов |
run/finished | Сообщает о полном запуске задачи, у которой плагин указан в уведомлениях |
shutdown | Приходит перед тем, как QueryLane остановит плагин |
Во время обработки запроса плагин может отправлять уведомления rows, progress и artifact (kind равен screenshot, html или file), указав в requestId идентификатор запроса. step/cancel приходит уведомлением с runId запуска, который нужно остановить.
Об ошибках сообщайте ошибками JSON-RPC с data.reasonCode. QueryLane покажет текст errors.<reasonCode> из плагина. Оставьте stdout для сообщений протокола, а логи пишите в stderr. Завершайте работу, когда закрывается stdin.
Минимальный пример
plugins/hello/plugin.json:
{
"schemaVersion": 1,
"id": "com.example.hello",
"version": "1.0.0",
"apiVersion": 1,
"author": { "name": "Example" },
"permissions": [],
"platforms": {
"darwin-aarch64": { "command": ["${node}", "${pluginDir}/main.mjs"] }
},
"contributes": {
"steps": [{ "id": "greet", "language": "plaintext", "template": true, "starterCode": "Hello, ${name}" }]
},
"locales": {
"en": { "name": "Hello", "description": "Returns a greeting as a row", "steps.greet.title": "Greeting" },
"ru": { "name": "Привет", "description": "Возвращает приветствие строкой", "steps.greet.title": "Приветствие" }
}
}plugins/hello/main.mjs:
import readline from 'node:readline';
const send = message => process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', ...message })}\n`);
readline.createInterface({ input: process.stdin }).on('line', (line) => {
const { id, method, params = {} } = JSON.parse(line);
if (id === undefined)
return;
if (method === 'initialize')
send({ id, result: { apiVersion: 1 } });
else if (method === 'step/run')
send({ id, result: { rows: [{ greeting: params.code.trim() }] } });
else if (method === 'prepare' || method === 'shutdown')
send({ id, result: {} });
else
send({ id, error: { code: -32601, message: `unknown method ${method}`, data: { reasonCode: 'HELLO_METHOD_UNKNOWN' } } });
});Сборка и проверка
npm run buildупаковывает каждый плагин из списка вdist/<id>-<version>-<platform>.qlplugin, архив gzip tar сplugin.jsonв корне, подписывает его и записываетdist/catalog.json. Если ключ не официальный, сборка предупреждает, что приложение откажется от этих пакетов.npm run checkзапускает линтеры, проверку манифестов валидатором приложения и тесты пакетов и плагинов.
Чтобы проверить плагин целиком, напишите тест в tests/ по образцу существующих: соберите пакет одноразовым ключом Ed25519, установите его во временную папку функцией приложения installPluginPackage, передав этот ключ в trustedKeys, затем вызовите preparePlugin и runPluginStep из plugins/host.ts приложения.
Если приложение запущено из исходников, его npm run dev показывает пакеты из dist/catalog.json в списке Маркетплейс. Официальная подпись им всё равно нужна.
Другие среды выполнения
- Bun:
package.jsonсquerylanePlugin.bun(version,entry,binary) компилируется в один исполняемый файл, поэтому пользователям ничего не нужно устанавливать. Для сборки нужен Bun этойversion. - Python:
bundleUv: trueвPLUGIN_SOURCESдобавляет в пакетbin/uv, а командыsetupс его помощью ставят Python и зависимости в${dataDir}, как это делает плагин Браузер.