// Руководство разработчика

Документация разработчика плагинов

Создавайте плагины для настольного приложения AnBoWork: добавляйте новые инструменты для ИИ, закрепляйте собственную панель рядом с сеансом, отправляйте системные уведомления, анализируйте новые форматы файлов. Для начала достаточно одного файла Manifest.json и одного входного скрипта.

01Обзор

Плагин AnBoWork — это простая программа Node.js. После установки приложение разветвляет его как автономный дочерний процесс (чистый Node, без Electron API) и взаимодействует с ним посредством межпроцессных сообщений. Пакет SDK не требуется; все, что вам нужно, это process.on('message') и process.send().

Все, что может делать плагин, объявлено статически в manifest.json: страница согласия перед установкой и обзор рынка могут указать, какие инструменты он добавляет и какие разрешения ему необходимы, без запуска какого-либо кода плагина. Весь пользовательский интерфейс (значки панели инструментов, настройки, палитра команд, контекстные меню) отображается хостом из объявления; Код плагина никогда не запускается внутри главного окна. Только закрепленные представления и плавающие панели представляют собой ваш собственный HTML, и они выполняются на изолированных изолированных страницах.

Возможности с первого взгляда

Поле манифестаВозможностьГде пользователи это видят
toolsДобавьте вызываемые инструменты для ИИИИ автоматически вызывает их в разговоре; вызовы инструментов отображаются в сеансе
viewsПостоянное представление, закрепленное на правой боковой панели сеанса.Дополнительный значок на панели инструментов сеанса открывает вашу страницу справа.
uiОтдельное плавающее окноОткрывается из меню «⋯» в списке плагинов.
sessionActionЗначок постоянного действия на панели инструментов сеансаОдин клик запускает указанный вами инструмент
configurationЗаписи на странице настроекНастройки → Настройка плагина; значения передаются при каждом вызове
commandsЭлементы палитры команд Ctrl/Cmd+KПалитра команд
keybindingsГлобальные сочетания клавишКомбинация клавиш запускает команду
menusЭлементы контекстного меню сеансаМеню «⋯» / контекстное меню в строке сеанса
documentParsersАнализ файлов, помещенных в поле вводаВызывается автоматически, когда пользователь удаляет соответствующий файл.
activationEventsНачни вместе с приложениемНевидимый; для плагинов, которым нужны фоновые таймеры
permissionsОбъявить необходимые разрешенияСтраница согласия на предустановку

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

Минимальному плагину требуется всего два файла.

my-plugin/
├── manifest.json   # plugin manifest (required)
├── index.js        # entry script, referenced by manifest.main (required)
├── view.html       # docked view page (optional, manifest.views[].entry)
└── ui.html         # floating panel page (optional, manifest.ui.entry)

1. Напишите манифест.json

{
  "id": "com.example.hello",
  "name": "Hello Plugin",
  "version": "1.0.0",
  "description": "Say hello to the user.",
  "main": "index.js",
  "permissions": [],
  "tools": [
    {
      "name": "say_hello",
      "summary": "Greet the user by name",
      "description": "Return a greeting for the given name. Call this when the user explicitly asks the plugin to say hello.",
      "parameters": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "description": "Who to greet." }
        },
        "required": ["name"]
      }
    }
  ]
}

2. Напишите сценарий входа

// The entry script runs in a standalone Node.js child process (ELECTRON_RUN_AS_NODE=1).
// Only Node built-ins and dependencies shipped inside the plugin folder are available;
// there is no Electron API.
process.on('message', (msg) => {
  if (!msg || msg.type !== 'call_tool') return;
  const { requestId, toolName, params } = msg;
  try {
    if (toolName !== 'say_hello') throw new Error(`unknown tool: ${toolName}`);
    process.send({
      type: 'tool_result',
      requestId,
      content: [{ type: 'text', text: `Hello, ${params.name}!` }],
    });
  } catch (err) {
    process.send({ type: 'tool_error', requestId, message: err.message });
  }
});

3. Установите в приложение.

Откройте AnBoWork → верхняя панель «Инструменты» → «Плагины» → «Установить локально» в правом верхнем углу, затем выберите папку плагина (или ее zip-архив). На странице согласия перечислены заявленные разрешения и каждый инструмент перед установкой; подтвердите включение.

4. Попробуйте

В любом сеансе скажите ИИ «попросите плагин передать привет Алисе». На основании описания инструмента ИИ решает вызвать say_hello, и вызов и его результат появляются в сеансе.

КОНЧИК
ИИ внутри AnBoWork также может генерировать для вас плагины (скажем, «напишите мне плагин, который делает X», и он вызовет create_local_plugin). Созданные плагины по умолчанию отключены, пока вы не просмотрите код на странице плагина и не включите их вручную. Это руководство в равной степени применимо к чтению и изменению плагинов, созданных искусственным интеллектом.

03Поля манифеста

ПолеТипНеобходимыйОписание
idstringДаСтиль обратного домена, например. com.example.my-plugin. Только буквы, цифры, точки и дефисы. Неизменяемый после создания; повторная установка того же идентификатора рассматривается как переустановка.
namestringДаНазвание плагина. Вместе с description он передается в модель как контекст, поэтому рекомендуется использовать английский язык.
versionstringДаСемантическая версия; рынок использует его для обнаружения обновлений.
descriptionstringДаОдно предложение, описывающее, что делает плагин (на английском языке).
authorstringНетАвтор.
readmestringНетПолное описание, показанное на странице сведений о торговой площадке, поддерживает Markdown. Изображения внутри него должны иметь URL-адреса https — относительные пути к файлам, упакованным в zip-архив, не поддерживаются.
iconstringНетЗначок, отображаемый в списке торговой площадки и на странице сведений — относительный путь к файлу, упакованному в zip-архив (например, icon.png). Должен быть PNG, а не SVG, не менее 128×128.
mainstringДаСкрипт входа относительно папки плагина. .. выход из папки не разрешен.
permissionsstring[]Даnetwork/filesystem/notifications; может быть пустой массив. См. «Модель разрешений».
toolsobject[]ДаОбъявления инструментов; может быть пустой массив. См. «Инструменты и протокол вызовов».
documentParsersobject[]НетВозможность разбора файлов.
uiobjectНетПлавающая панель:{ entry, title }
viewsobject[]НетЗакрепленные представления:{ id, title, icon, entry, width? }
sessionActionobjectНетЗначок на панели инструментов, не более одного на каждый плагин.
configurationobject[]НетСписок записей настроек.
commandsobject[]НетЗаписи палитры команд.
keybindingsobject[]НетЯрлыки. command должен ссылаться на tool записи в commands[], в противном случае установка будет отклонена.
menusobjectНетНа данный момент доступна только локация session/context.
activationEventsstring[]НетПока только onStartupFinished.

Языковые соглашения для текста

Текст в манифесте предназначен для двух аудиторий. Для модели : name, description, tools[].description, tools[].parameters.*.description. Напишите это на английском языке; модели понимают описания инструментов на английском языке и более надежно заполняют параметры. Для людей: tools[].summary (однострочное резюме на странице согласия), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title и title внутри menus. Напишите их на языке ваших целевых пользователей.

{
  "id": "com.example.full",
  "name": "Full Example",
  "version": "1.2.0",
  "description": "Every contribution point in one manifest.",
  "author": "Example Inc.",
  "readme": "## Full Example\n\nLonger Markdown shown on the marketplace detail page. Images must be https URLs.",
  "icon": "icon.png",
  "main": "index.js",
  "permissions": ["filesystem", "network", "notifications"],
  "activationEvents": ["onStartupFinished"],
  "tools": [
    {
      "name": "do_something",
      "summary": "Do one thing to the current project",
      "description": "…English. State clearly when to call this and when not to…",
      "parameters": { "type": "object", "properties": {}, "required": [] },
      "renderAs": "checklist"
    },
    { "name": "is_available", "summary": "Whether the current folder applies", "description": "…", "parameters": { "type": "object", "properties": {} } },
    { "name": "list_modes", "summary": "List selectable modes", "description": "…", "parameters": { "type": "object", "properties": {} } }
  ],
  "documentParsers": [
    { "mimeType": "text/markdown", "toolName": "parse_markdown" }
  ],
  "ui": { "entry": "ui.html", "title": "Example Panel" },
  "views": [
    { "id": "board", "title": "Example Board", "icon": "checklist", "entry": "view.html", "width": 380 }
  ],
  "sessionAction": {
    "tool": "do_something",
    "icon": "git-branch",
    "tooltip": "Do one thing to the current project",
    "visibleTool": "is_available"
  },
  "configuration": [
    { "id": "mode", "label": "Mode", "type": "select", "optionsTool": "list_modes", "defaultValue": "auto" },
    { "id": "verbose", "label": "Verbose output", "type": "boolean", "defaultValue": false },
    { "id": "prefix", "label": "Prefix", "type": "string", "defaultValue": "" },
    { "id": "limit", "label": "Max items", "type": "number", "defaultValue": 20 }
  ],
  "commands": [
    { "tool": "do_something", "icon": "git-branch", "title": "Do one thing to the current project" }
  ],
  "keybindings": [
    { "command": "do_something", "key": "mod+shift+d" }
  ],
  "menus": {
    "session/context": [
      { "tool": "do_something", "icon": "git-branch", "title": "Do one thing to this session" }
    ]
  }
}

04Модель разрешения

permissions должен честно отражать то, что на самом деле делает код. На странице согласия перед установкой перечислены все из них, чтобы пользователи могли решить, устанавливать ли их; Обзор рынка также проверяет декларацию на соответствие коду.

РазрешениеКогда его необходимо объявитьКак к этому относится хозяин
filesystemЧтение или запись пользовательских файлов (чтение кода проекта, генерация документов, сохранение данных в домашней папке)Информированное согласие, показываемое пользователю.
networkВыполнение любого сетевого запроса (сторонние API, загрузки)Информированное согласие, показываемое пользователю.
notificationsОтображение системных уведомлений через обратный RPC show_notificationEnforced: вызов без объявления возвращает host_error и уведомление не отображается.
ПРИМЕЧАНИЕ
Дочерний процесс плагина представляет собой полную среду Node.js. filesystem и network в настоящее время являются декларациями уровня согласия, а не изолированной программной средой времени выполнения: хост не перехватывает fs или fetch. Именно поэтому декларации должны быть честными. Плагины с недостаточной декларацией отклоняются при проверке на рынке и теряют доверие пользователей при обнаружении. Не переусердствуйте (неиспользуемые разрешения заставляют пользователей колебаться).

Конфиденциальность и данные

context.cwd — это папка проекта пользователя, которая может содержать исходный код и конфиденциальную конфигурацию; Аргументы инструмента могут содержать разговор пользователя. Не отправляйте эти данные по сети, если этого не требует функция. Когда вы это сделаете, укажите в description и summary, что и кому отправляется. Храните собственные постоянные данные плагина в файле в домашней папке пользователя, названном в честь идентификатора плагина, чтобы их можно было легко найти при удалении.

05Инструменты и протокол вызова

Декларация

ПолеОписание
nameИмя инструмента строчными буквами и подчеркиванием, уникальное для всего приложения (оно регистрируется в модели наряду со встроенными инструментами и инструментами других плагинов).
descriptionОписание к модели (англ.). Укажите три вещи: что делает инструмент, что говорит пользователь, когда его следует вызывать и когда его нельзя вызывать. Это самое важное поле для определения того, использует ли ИИ ваш плагин.
summaryОднострочное, понятное человеку резюме, отображаемое на странице согласия. Необязательно, но настоятельно рекомендуется; в противном случае вместо этого отображается усеченное описание.
parametersСтандартный объект схемы JSON с description для каждого свойства. Модель заполняет из нее аргументы, а хост передает их без дополнительной проверки, поэтому проверяйте обязательные поля в своем коде.
renderAsНеобязательный; в настоящее время только checklist: хост отображает массив todos из аргументов вызова как карточку контрольного списка, см. ниже.
Примечание
Модель может вызывать только имена инструментов, объявленные здесь в tools[] — sessionAction.tool, commands[].tool, а поле tool внутри menus проверяется только на то, чтобы быть непустой строкой во время установки; хост не проверяет, что имя объявлено в tools[], и по-прежнему отправляет вызов под этим именем при срабатывании кнопки, ярлыка или пункта меню. Таким образом, действие, запускаемое исключительно пользовательским интерфейсом, может быть полностью обработано в вашем собственном index.js, даже не появляясь в tools[] — но тогда ИИ никогда не сможет его увидеть или вызвать. Если вы хотите, чтобы одно и то же действие запускалось как щелчком мыши, так и действием ИИ по запросу чата, объявите это же имя и в tools[], чтобы обе точки входа использовали один обработчик; если он предназначен только для пользовательского интерфейса, оставьте его необъявленным — нет необходимости добавлять запись только для того, чтобы она выглядела завершенной.

Протокол сообщений

Хост отправляет call_tool; плагин должен ответить tool_result или tool_error, содержащим тот же requestId. Вызов без ответа в течение 30 секунд истекает, и модель получает сообщение об ошибке.

// host → plugin
{
  "type": "call_tool",
  "requestId": "0b2d…",          // unique per call, echo it back unchanged
  "toolName": "do_something",
  "params": { "name": "…" },     // arguments validated against tools[].parameters
  "context": {
    "cwd": "/Users/me/project",  // project folder of the calling session; a temp folder for chat sessions; empty string for document parsing
    "config": { "mode": "auto", "verbose": false }   // present only when configuration is declared; keys are configuration[].id
  }
}

content принимает только блоки text и image (изображение в формате base64 data плюс mimeType); другие типы отбрасываются. details — произвольные структурированные данные, которые никогда не передаются в модель; он предназначен для рендеринга пользовательского интерфейса хоста и ваших собственных закрепленных представлений.

Результаты файла: карточки файлов

// When the result is a file: include the absolute path and file name. The session
// renders a clickable file card that previews in-app (docx/xlsx/pdf/markdown/code…).
process.send({
  type: 'tool_result',
  requestId,
  content: [{ type: 'text', text: `Generated [${fileName}](${filePath})` }],
  details: { filePath, fileName },
});

Контрольный список рендеринга: контрольный список renderAs

// For tools with renderAs: "checklist", the host renders the todos array from the call arguments:
{
  "todos": [
    { "id": "1", "content": "Initialize repository", "status": "completed" },
    { "id": "2", "content": "Write unit tests", "status": "in_progress" },
    { "id": "3", "content": "Publish", "status": "pending" }
  ]
}

Написание хорошего описания

Опишите триггер в императиве («Вызовите это, когда пользователь просит…») и укажите исключения («Не вызывайте это для…»). Если результатом является файл, попросите модель сослаться на возвращенную ссылку на файл, а не заявлять, что она была сгенерирована. Если аргументы пересылаются во внешний API-интерфейс генерации, укажите, на каком языке они должны быть. Избегайте однострочных слов, таких как «управление задачами»; модель не будет знать, когда использовать инструмент.

06Закрепленные виды (представления)

Закрепленное представление — это постоянная страница на правой боковой панели сеанса: на панели инструментов сеанса появляется дополнительный значок, а при открытии справа отображается HTML-код. Состояние страницы (положение прокрутки, черновики) сохраняется при переключении сеансов и сворачивании/развертывании, та же семантика, что и представление боковой панели VS Code.

ПолеОписание
idУникальный в рамках плагина.
titleТекст подсказки для значка (читабельный для человека).
iconВыберите из белого списка хостов: git-branch, checklist. Сохраняет единообразие значков во всем приложении; пользовательские изображения не принимаются.
entryHTML-файл относительно папки плагина.
widthНеобязательная ширина панели по умолчанию в пикселях: от 240 до 640, по умолчанию 360. Пользователи могут перетаскивать, чтобы изменить размер, и размер запоминается.

Время выполнения страницы

Страница работает в «песочнице» (sandbox + contextIsolation, без Node, без доступа к данным хост-приложения) и может взаимодействовать со своим собственным процессом плагина только с помощью нескольких методов из window.pluginHost. Принесите свои собственные стили; рекомендуется следовать светлой/темной схеме приложения, как показано в примере ниже.

interface PluginHost {
  // Call a tool declared in this plugin's manifest.tools; same call_tool channel the AI uses
  invoke(toolName: string, params: unknown): Promise<
    { content: unknown[]; details?: unknown } | { error: string }
  >;
  // Subscribe to ui_push messages from the plugin process; returns an unsubscribe function
  onPush(callback: (channel: string, payload: unknown) => void): () => void;
  // Current light/dark scheme and subsequent changes
  getColorScheme(): Promise<'light' | 'dark'>;
  onColorSchemeChange(callback: (scheme: 'light' | 'dark') => void): () => void;
}
declare const pluginHost: PluginHost; // exposed as window.pluginHost

Рекомендуемый шаблон: при первой загрузке вызовите один из ваших собственных инструментов запросов с помощью invoke() и он вернет структурированные данные в details; после этого отправляйте каждое изменение из процесса плагина с помощью ui_push вместо опроса. Когда ИИ меняет данные в разговоре, панель немедленно обновляется.

07Плавающая панель (UI)

ui: { entry, title } объявляет отдельное плавающее окно (900×700), которое пользователи открывают из меню «⋯» в списке плагинов; он уничтожается при закрытии. Среда страницы и API window.pluginHost идентичны закрепленным представлениям, а ui_push доставляется как на плавающую панель, так и на закрепленные представления. Подходит для больших панелей, открываемых время от времени; используйте views для контента, который должен оставаться рядом с сеансом.

08Значок панели инструментов сеанса (sessionAction)

Не более одного постоянного значка для каждого плагина. Щелчок по нему вызывает tool с пустыми аргументами, так что инструмент должен сделать что-то значимое без параметров.

ПолеОписание
toolИнструмент для вызова по клику.
iconИмя значка из белого списка.
tooltipНаведите текст.
visibleToolНеобязательный. Если этот параметр установлен, он вызывается с пустыми аргументами перед рендерингом, и логическое значение details.visible в результате определяет, будет ли отображаться значок. Полезно для проверок во время выполнения, например «является ли текущая папка репозиторием git».
// The host calls visibleTool with empty params and shows the icon based on details.visible
process.send({
  type: 'tool_result',
  requestId,
  content: [{ type: 'text', text: isGitRepo ? 'git repo' : 'not a git repo' }],
  details: { visible: isGitRepo },
});

09Настройки (конфигурация)

Объявленные записи отображаются в разделе конфигурации плагина на странице настроек. Хост сохраняет значения, измененные пользователем, и передает их как context.config при каждом call_tool (с помощью id, возвращаясь к defaultValue), независимо от того, исходит ли вызов от AI, значка панели инструментов или вашей панели. Вам никогда не придется их просить.

typeОтображается какТип значения по умолчанию
selectСписок с одним выбором; варианты приходят из optionsToolstring
booleanТумблерboolean
stringВвод текста, зафиксированный при размытииstring
numberВвод номераnumber
// For type: "select" settings, the host calls optionsTool with empty params to fetch options
process.send({
  type: 'tool_result',
  requestId,
  content: [{ type: 'text', text: '2 options' }],
  details: {
    options: [
      { value: 'fast', label: 'Fast' },
      { value: 'accurate', label: 'Accurate' },
    ],
  },
});
// The host automatically prepends an "Auto" option whose value equals this
// configuration entry's defaultValue; the plugin must not return it again.

10Палитра команд/ярлыки/контекстное меню

Все три являются разными точками входа для «вызова инструмента с пустыми аргументами»; плагин может объявить несколько.

ПолеФормаОписание
commands{ tool, icon, title }[]Отображается в палитре команд Ctrl/Cmd+K и активируется после того, как пользователь выполняет его поиск.
keybindings{ command, key }[]command должно равняться tool записи в commands[]. key использует обозначение mod+shift+g, где mod — это Cmd на macOS и Ctrl в другом месте. Конфликты с другими ярлыками не проверяются статически, поэтому выбирайте необычные комбинации.
menus{ "session/context": { tool, icon, title }[] }Добавлено в меню в строке сеанса. Пока доступна только эта локация.

11Парсеры документов (documentParsers)

Когда пользователь помещает файл в поле ввода, хост ищет синтаксические анализаторы, объявленные включенными плагинами, по типу MIME и передает содержимое файла в формате base64 соответствующему toolName. Это не тот инструмент, который ИИ решает вызвать, поэтому не также укажите инструмент синтаксического анализатора в tools, иначе он будет представлен как инструмент, вызываемый ИИ.

// The toolName declared in documentParsers is called when the user drops a file into the input:
// params = { base64: "<file content as base64>" }
// Return: details must be { success: true, text } or { success: false, reason }
process.on('message', (msg) => {
  if (msg.type !== 'call_tool' || msg.toolName !== 'parse_markdown') return;
  const text = Buffer.from(msg.params.base64, 'base64').toString('utf8');
  process.send({
    type: 'tool_result',
    requestId: msg.requestId,
    content: [{ type: 'text', text: 'parsed' }],
    details: { success: true, text },
  });
});

12Обратный RPC (host_request)

Процесс плагина не имеет Electron API. Когда ему нужны реальные возможности системы, он отправляет host_request хосту, который отвечает host_response или host_error. Этот канал полностью независим от call_tool; вы можете использовать его при обработке вызова инструмента или по таймеру.

capabilityparamsresultПредварительное условие
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Требуется разрешение notifications. Возвращает значение «disabled» вместо ошибки, когда пользователь отключил уведомления на рабочем столе в настройках. Когда viewId указывает на одно из закрепленных представлений, щелчок по уведомлению выводит приложение на передний план и расширяет это представление.
render_pdf{ outputPath, title, content }{}контент — Markdown; хост визуализирует PDF-файл в выходной путь с помощью встроенного средства визуализации.
const crypto = require('crypto');
const pending = new Map();

function requestHost(capability, params) {
  const requestId = crypto.randomUUID();
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      pending.delete(requestId);
      reject(new Error(`host capability "${capability}" timed out`));
    }, 30_000);
    pending.set(requestId, { resolve, reject, timer });
    process.send({ type: 'host_request', requestId, capability, params });
  });
}

process.on('message', (msg) => {
  if (msg.type === 'host_response' || msg.type === 'host_error') {
    const p = pending.get(msg.requestId);
    if (!p) return;
    pending.delete(msg.requestId);
    clearTimeout(p.timer);
    if (msg.type === 'host_error') p.reject(new Error(msg.message));
    else p.resolve(msg.result);
  }
});

// usage
await requestHost('show_notification', {
  title: '3 todos for today',
  body: '• Write weekly report\n• Reply to emails',
  viewId: 'board',      // optional: expand this docked view when the notification is clicked
});
// => { shown: true } or { shown: false, reason: 'disabled' | 'unsupported' }

await requestHost('render_pdf', {
  outputPath: '/abs/path/report.pdf',
  title: 'Report',
  content: '# Title\n\nBody (Markdown)',
});

13Жизненный цикл

ЭтапПоведение
НачинатьЛенивый по умолчанию: не запускается с приложением, разветвляется при первом вызове одного из ваших инструментов (ИИ, значок на панели инструментов, панель, команда, анализ документа). Плагины, объявляющие activationEvents: ["onStartupFinished"], запускаются сразу после завершения запуска приложения; используйте это только для плагинов, которым нужны фоновые таймеры (напоминания, опрос), а не для обычных плагинов инструментов.
РезидентПосле запуска процесс остается активным и повторно используется при вызовах инструментов. Вы можете кэшировать состояние в памяти, но предполагайте, что его можно перезапустить в любое время.
КрушениеЕсли процесс неожиданно завершается, хост перезапускает его: сразу, затем через 5 и 15 секунд. После 3 неудач подряд он помечается как «сбой» и больше не повторяется; пользователи видят статус на странице плагина. Во время полета вызывает все возвращаемые ошибки.
Отключить/удалить/переустановитьПроцесс завершается, а закрепленные виды и плавающие панели уничтожаются; при переустановке заменяется вся папка.
ЖурналыВаши stdout/stderr записываются в файл журнала приложения с префиксом идентификатора плагина. Для отладки достаточно обычного console.log; см. «Отладка и часто задаваемые вопросы».
ЕНВ
Дочерний процесс начинается с ELECTRON_RUN_AS_NODE=1, cwd — это папка установки плагина, а версия Node соответствует версии Electron, входящей в комплект приложения. Не зависьте от какого-либо глобально установленного пакета и не предполагайте, что npx существует.

14Упаковка и издательское дело

Упаковка

Заархивируйте папку плагина с помощью manifest.json в корне архива. Если у вас есть сторонние зависимости, сначала объедините их в один файл с помощью esbuild/webpack вместо того, чтобы отправлять весь node_modules.

my-plugin.zip
├── manifest.json     # must be at the archive root
├── icon.png          # optional, referenced by manifest.icon
├── index.js
├── view.html
└── node_modules/…    # include dependencies if any (bundling into a single file is recommended)

Публикация на торговой площадке

AnBoWork → Инструменты → Плагины → вкладка «Торговая площадка» → «Опубликовать плагин» в правом верхнем углу, затем загрузите zip-архив. Отправленные материалы проходят проверку, которая проверяет соответствие декларации манифеста коду (особенно разрешениям). После одобрения другие пользователи смогут найти и установить его на рынке, а ИИ может автоматически устанавливать проверенные плагины для пользователей, когда сочтет это необходимым.

Обновления

Нажмите version и опубликуйте снова. Приложение сравнивает установленную версию с версией из магазина и предлагает обновиться; при переустановке сохраняется включенное состояние пользователя и значения настроек.

15Полный пример: Todo Quadrant

Настоящий работающий плагин (обрезанный): ИИ может добавлять и удалять задачи, на правой боковой панели отображается доска, которая обновляется в реальном времени, и он может отправлять напоминания о системных уведомлениях. Объединение этих трех элементов охватывает протокол инструмента, отправку закрепленного представления и обратный RPC.

{
  "id": "local.todo-quadrant",
  "name": "Todo Quadrant",
  "version": "1.2.0",
  "description": "Daily todo list organized by the Eisenhower matrix. Install this whenever the user wants to track tasks or asks to be reminded about something…",
  "main": "index.js",
  "permissions": ["filesystem", "notifications"],
  "activationEvents": ["onStartupFinished"],
  "tools": [{
    "name": "manage_todo_quadrant",
    "summary": "Add, list, complete, delete and update todos in an urgent/important matrix",
    "description": "Manage the user's daily todo list… Actions: add / list / complete / delete / update / set_reminder / remind_now. add/update also accept remind_at to schedule a one-time reminder for that specific todo. Call this whenever the user talks about their todos…",
    "parameters": {
      "type": "object",
      "properties": {
        "action": { "type": "string", "enum": ["add", "list", "complete", "delete", "update", "set_reminder", "remind_now"] },
        "id": { "type": "string" },
        "content": { "type": "string" },
        "quadrant": { "type": "string", "enum": ["urgent_important", "important_not_urgent", "urgent_not_important", "not_urgent_not_important"] },
        "due_date": { "type": "string", "description": "YYYY-MM-DD" },
        "remind_at": { "type": "string", "description": "YYYY-MM-DD HH:MM — a one-time reminder for this todo, independent of the daily digest" }
      },
      "required": ["action"]
    }
  }],
  "views": [{ "id": "quadrant", "title": "Todo Quadrant", "icon": "checklist", "entry": "view.html", "width": 380 }]
}

16Отладка и часто задаваемые вопросы

СимптомПричина и исправление
Проверка манифеста не удалась при установкеПроверьте каждое поле по таблице «Поля манифеста», поскольку в сообщении указано: формат идентификатора, основная/запись не должна содержать .., значок должен быть внесен в список разрешенных, команда привязок клавиш должна указывать на существующий инструмент в командах, ширина от 240 до 640.
ИИ никогда не вызывает мой инструментПрактически всегда проблема description: триггер непонятен или написан не по-английски. См. «Написание хорошего описания». Также убедитесь, что имя инструмента не конфликтует с именем другого инструмента.
Тайм-аут звонка (30 с)Сценарий входа не ответил tool_result. Обычно requestId не было возвращено, сообщение type написано с ошибкой или сценарий выдал ошибку при запуске и завершил работу. Найдите строки с префиксом [plugin:your-id] в журнале приложения.
Статус плагина показывает «разбился».3 неудачных старта подряд. Обычно require об отсутствующем пакете; запуск node index.js в папке плагина воспроизводит его. После исправления выключите и снова включите плагин на странице плагина.
Панель открывается пустаяНеправильный путь entry или ошибка JS на странице. Страница работает в песочнице без Node require; доступен только window.pluginHost.
Уведомление не появляетсяПроверьте по порядку: в манифесте указано notifications; результат не shown:false (пользователь отключил уведомления на рабочем столе); ОС позволяет получать уведомления от AnBoWork.
Значения настроек не читаютсяЗначения указаны в message.context.config[id], а не в params.

Расположение файла журнала

ОСПуть
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
См. документацию по интеграции API.