//Guia do desenvolvedor

Documentos para desenvolvedores de plug-ins

Crie plug-ins para o aplicativo de desktop AnBoWork: adicione novas ferramentas para a IA, encaixe seu próprio painel próximo a uma sessão, envie notificações do sistema, analise novos formatos de arquivo. Um manifest.json mais um script de entrada são suficientes para começar.

01Visão geral

Um plugin AnBoWork é um programa Node.js simples. Após a instalação, o aplicativo o bifurca como um processo filho autônomo (Nó puro, sem API Electron) e se comunica com ele por meio de mensagens entre processos. Nenhum pacote SDK é necessário; tudo que você precisa é process.on('message') e process.send().

Tudo o que um plugin pode fazer é declarado estaticamente em manifest.json: a página de consentimento de pré-instalação e a revisão do mercado podem informar quais ferramentas ele adiciona e quais permissões são necessárias sem executar nenhum código de plugin. Toda a UI (ícones da barra de ferramentas, configurações, paleta de comandos, menus de contexto) é renderizada pelo host a partir da declaração; o código do plugin nunca é executado dentro da janela principal. Somente visualizações encaixadas e painéis flutuantes são seu próprio HTML e são executados em páginas isoladas em área restrita.

Visão geral das capacidades

Campo de manifestoCapacidadeOnde os usuários veem
toolsAdicione ferramentas que podem ser chamadas para a IAA IA os chama automaticamente durante uma conversa; chamadas de ferramenta são mostradas na sessão
viewsVisualização persistente ancorada na barra lateral direita da sessãoUm ícone extra na barra de ferramentas da sessão abre sua página à direita
uiJanela flutuante independenteAberto no menu "⋯" na lista de plugins
sessionActionÍcone de ação persistente na barra de ferramentas da sessãoUm clique executa a ferramenta que você especifica
configurationEntradas na página de configuraçõesConfigurações → Configuração do plugin; valores são passados ​​com cada chamada
commandsEntradas da paleta de comandos Ctrl/Cmd+KPaleta de comandos
keybindingsAtalhos de teclado globaisUma combinação de teclas aciona um comando
menusItens do menu de contexto da sessãoO menu "⋯"/clique com o botão direito em uma linha da sessão
documentParsersAnalisar arquivos colocados na caixa de entradaChamado automaticamente quando o usuário descarta um arquivo correspondente
activationEventsComece junto com o aplicativoInvisível; para plug-ins que precisam de temporizadores em segundo plano
permissionsDeclarar as permissões necessáriasPágina de consentimento de pré-instalação

02Início rápido

Um plugin mínimo precisa de apenas dois arquivos.

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. Escreva manifesto.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. Escreva o script de entrada

// 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. Instale no aplicativo

Abra AnBoWork → barra superior "Ferramentas" → "Plugins" → "Instalar localmente" no canto superior direito e escolha a pasta do plugin (ou um zip dela). Uma página de consentimento lista as permissões declaradas e todas as ferramentas antes da instalação; confirme para ativar.

4. Experimente

Em qualquer sessão, diga à IA "peça ao plugin para dizer olá para Alice". Com base na descrição da ferramenta a IA decide chamar say_hello, e a chamada e seu resultado aparecem na sessão.

DICA
A IA dentro de AnBoWork também pode gerar plugins para você (diga "escreva-me um plugin que faça X" e ele chama create_local_plugin). Os plug-ins gerados ficam desabilitados por padrão até que você revise o código na página do plug-in e os habilite manualmente. Este guia se aplica igualmente à leitura e modificação de plug-ins gerados por IA.

03Campos de manifesto

CampoTipoObrigatórioDescrição
idstringSimEstilo de domínio reverso, por ex. com.example.my-plugin. Somente letras, dígitos, pontos e hifens. Imutável uma vez criado; instalar o mesmo ID novamente é tratado como uma reinstalação.
namestringSimNome do plug-in. Junto com description ele é inserido no modelo como contexto, portanto o inglês é recomendado.
versionstringSimVersão semântica; o mercado o utiliza para detectar atualizações.
descriptionstringSimUma frase descrevendo o que o plugin faz (Inglês).
authorstringNãoAutor.
readmestringNãoA descrição longa mostrada na página de detalhes do mercado suporta Markdown. As imagens dentro dele devem ser https URLs — caminhos relativos para arquivos agrupados no zip não são suportados.
iconstringNãoÍcone mostrado na lista do mercado e na página de detalhes — um caminho relativo para um arquivo compactado no zip (por exemplo, icon.png). Deve ser PNG, não SVG, pelo menos 128×128.
mainstringSimScript de entrada, relativo à pasta do plugin. .. escapar da pasta não é permitido.
permissionsstring[]Simnetwork / filesystem / notifications; pode ser uma matriz vazia. Consulte "Modelo de permissão".
toolsobject[]SimDeclarações de ferramentas; pode ser uma matriz vazia. Consulte "Ferramentas e protocolo de chamada".
documentParsersobject[]NãoCapacidade de análise de arquivos.
uiobjectNãoPainel flutuante:{ entry, title }
viewsobject[]NãoVisualizações encaixadas:{ id, title, icon, entry, width? }
sessionActionobjectNãoÍcone da barra de ferramentas, no máximo um por plugin.
configurationobject[]NãoLista de entradas de configurações.
commandsobject[]NãoEntradas da paleta de comandos.
keybindingsobject[]NãoAtalhos. command deve fazer referência ao tool de uma entrada em commands[], caso contrário a instalação será rejeitada.
menusobjectNãoApenas a localização session/context está disponível por enquanto.
activationEventsstring[]NãoApenas onStartupFinished por enquanto.

Convenções de idioma para texto

O texto do manifesto tem dois públicos. Para o modelo: name, description, tools[].description, tools[].parameters.*.description. Escreva-os em inglês; os modelos entendem as descrições das ferramentas em inglês e preenchem os parâmetros de maneira mais confiável. Para pessoas: tools[].summary (resumo de uma linha na página de consentimento), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title e title dentro de menus. Escreva-os no idioma dos usuários-alvo.

{
  "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" }
    ]
  }
}

04Modelo de permissão

permissions deve refletir honestamente o que o código realmente faz. A página de consentimento de pré-instalação lista cada um para que os usuários possam decidir se desejam instalar; a revisão do mercado também verifica a declaração em relação ao código.

PermissãoQuando deve ser declaradoComo o anfitrião trata isso
filesystemLer ou gravar arquivos do usuário (ler código do projeto, gerar documentos, armazenar dados na pasta inicial)Consentimento informado, mostrado ao usuário.
networkFazendo qualquer solicitação de rede (APIs de terceiros, downloads)Consentimento informado, mostrado ao usuário.
notificationsMostrando notificações do sistema por meio do RPC reverso show_notificationEnforced: chamá-lo sem a declaração retorna host_error e nenhuma notificação é mostrada.
OBSERVAÇÃO
O processo filho do plugin é um ambiente Node.js completo. filesystem e network são atualmente declarações de nível de consentimento, não uma sandbox de tempo de execução: o host não intercepta fs ou fetch. É exactamente por isso que as declarações devem ser honestas. Plugins que não são declarados são rejeitados na análise do mercado e perdem a confiança do usuário quando descobertos. Não declare demais (permissões não utilizadas fazem os usuários hesitarem).

Privacidade e dados

context.cwd é a pasta do projeto do usuário e pode conter código-fonte e configurações confidenciais; os argumentos da ferramenta podem conter a conversa do usuário. Não envie esses dados pela rede, a menos que o recurso exija e, quando o fizer, indique em description e summary o que é enviado e para quem. Mantenha os próprios dados persistentes do plug-in em um arquivo na pasta inicial do usuário com o nome do ID do plug-in, para que seja fácil encontrá-los durante a desinstalação.

05Ferramentas e protocolo de chamada

Declaração

CampoDescrição
nameNome da ferramenta em letras minúsculas e sublinhados, exclusivo em todo o aplicativo (é registrado com o modelo junto com ferramentas integradas e ferramentas de outros plug-ins).
descriptionDescrição do modelo (inglês). Indique três coisas: o que a ferramenta faz, o que o usuário diz quando deve ser chamada e quando não deve ser chamada. Este é o campo mais importante para saber se a IA usa seu plugin.
summaryResumo legível de uma linha mostrado na página de consentimento. Opcional, mas fortemente recomendado; caso contrário, uma descrição truncada será mostrada.
parametersObjeto JSON Schema padrão com description em cada propriedade. O modelo preenche os argumentos dele e o host os transmite sem validação extra, portanto, verifique os campos obrigatórios em seu código.
renderAsOpcional; atualmente apenas checklist: o host renderiza o array todos dos argumentos de chamada como um cartão de lista de verificação, veja abaixo.
Observação
O modelo só pode chamar nomes de ferramentas declarados em tools[] aqui — sessionAction.tool, commands[].tool e o campo tool dentro de menus são verificados apenas como strings não vazias no momento da instalação; o host não verifica se o nome foi declarado em tools[] e ainda despacha uma chamada com esse nome quando o botão, atalho ou item de menu é acionado. Portanto, uma ação puramente acionada pela IU pode ser realizada inteiramente em seu próprio index.js sem nunca aparecer em tools[] - mas a IA nunca poderá vê-la ou chamá-la. Se você deseja que a mesma ação seja acionada tanto pelo clique quanto pela IA agindo em uma solicitação de chat, declare o mesmo nome em tools[] também, para que ambos os pontos de entrada compartilhem um manipulador; se for para permanecer apenas na interface do usuário, deixe-o não declarado - não há necessidade de adicionar uma entrada apenas para parecer completo.

Protocolo de mensagem

O host envia call_tool; o plugin deve responder com tool_result ou tool_error carregando o mesmo requestId. Uma chamada sem resposta em 30 segundos expira e o modelo recebe um erro.

// 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 aceita apenas blocos text e image (imagem como base64 data mais mimeType); outros tipos são descartados. details são dados estruturados arbitrários que nunca são alimentados no modelo; é para renderização da interface do usuário do host e suas próprias visualizações encaixadas.

Resultados do arquivo: cartões de arquivo

// 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 },
});

Renderização da lista de verificação: lista de verificação 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" }
  ]
}

Escrevendo uma boa descrição

Descreva o gatilho no imperativo ("Chame isso quando o usuário pedir…") e exclusões de estado ("Não chame isso para…"). Se o resultado for um arquivo, peça ao modelo para fazer referência ao link do arquivo retornado em vez de afirmar que ele foi gerado. Se os argumentos forem encaminhados para uma API de geração externa, diga em qual idioma eles devem estar. Evite frases simples como "gerenciar todos"; o modelo não saberá quando usar a ferramenta.

06Visualizações ancoradas (visualizações)

Uma visualização encaixada é uma página persistente na barra lateral direita da sessão: um ícone extra aparece na barra de ferramentas da sessão e seu HTML aparece à direita quando aberto. O estado da página (posição de rolagem, rascunhos) sobrevive à troca de sessões e ao colapso/expansão, a mesma semântica de uma visualização da barra lateral do VS Code.

CampoDescrição
idÚnico dentro do plugin.
titleTexto da dica de ferramenta para o ícone (legível por humanos).
iconEscolha na lista de permissões de hosts: git-branch, checklist. Mantém os ícones consistentes em todo o aplicativo; imagens personalizadas não são aceitas.
entryArquivo HTML, relativo à pasta do plugin.
widthLargura do painel padrão opcional em pixels, 240 a 640, padrão 360. Os usuários podem arrastar para redimensionar e o tamanho é lembrado.

Tempo de execução da página

A página é executada em uma sandbox (sandbox + contextIsolation, sem Node, sem acesso aos dados do aplicativo host) e só pode se comunicar com seu próprio processo de plugin através dos poucos métodos em window.pluginHost. Traga seus próprios estilos; é recomendado seguir o esquema claro/escuro do aplicativo, como no exemplo abaixo.

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

Padrão recomendado: no primeiro carregamento, chame uma de suas próprias ferramentas de consulta com invoke() e faça com que ela retorne dados estruturados em details; depois, envie todas as alterações do processo do plugin com ui_push em vez de polling. Quando a IA altera os dados da conversa, o painel é atualizado imediatamente.

07Painel flutuante (ui)

ui: { entry, title } declara uma janela flutuante independente (900×700) que os usuários abrem no menu “⋯” na lista de plugins; ele é destruído de perto. O ambiente da página e a API window.pluginHost são idênticos às visualizações encaixadas, e ui_push é entregue tanto ao painel flutuante quanto às visualizações encaixadas. Adequado para grandes painéis abertos ocasionalmente; use views para conteúdo que deve ficar próximo à sessão.

08Ícone da barra de ferramentas da sessão (sessionAction)

No máximo um ícone persistente por plugin. Clicar nele chama tool com argumentos vazios, então essa ferramenta deve fazer algo significativo sem parâmetros.

CampoDescrição
toolFerramenta para ligar ao clicar.
iconNome do ícone permitido.
tooltipPasse o texto.
visibleToolOpcional. Quando definido, ele é chamado com argumentos vazios antes da renderização e o booleano details.visible no resultado decide se o ícone será exibido. Útil para verificações de tempo de execução como "a pasta atual é um repositório 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 },
});

09Configurações (configuração)

As entradas declaradas aparecem na configuração do plugin na página de configurações. O host armazena valores alterados pelo usuário e os passa como context.config em cada call_tool (digitado por id, voltando para defaultValue), quer a chamada venha da IA, do ícone da barra de ferramentas ou do seu painel. Você nunca precisa pedir por eles.

typeRenderizado comotipo de valor padrão
selectLista de escolha única; as opções vêm de optionsToolstring
booleanAlternar interruptorboolean
stringEntrada de texto, confirmada em desfoquestring
numberEntrada de númeronumber
// 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.

10Paleta de comandos/atalhos/menu de contexto

Todos os três são pontos de entrada diferentes para "chamar uma ferramenta com argumentos vazios"; um plugin pode declarar vários.

CampoFormaDescrição
commands{ tool, icon, title }[]Mostrado na paleta de comandos Ctrl/Cmd+K e acionado após o usuário pesquisá-lo.
keybindings{ command, key }[]command deve ser igual a tool de uma entrada em commands[]. key usa a notação mod+shift+g, onde mod é Cmd em macOS e Ctrl em outro lugar. Os conflitos com outros atalhos não são verificados estaticamente, portanto escolha combinações incomuns.
menus{ "session/context": { tool, icon, title }[] }Adicionado ao menu em uma linha da sessão. Apenas este local está disponível por enquanto.

11Analisadores de documentos (documentParsers)

Quando o usuário coloca um arquivo na caixa de entrada, o host procura analisadores declarados por plug-ins habilitados por tipo MIME e passa o conteúdo do arquivo como base64 para o toolName correspondente. Esta não é uma ferramenta que a IA decide chamar, então não liste também a ferramenta do analisador em tools, ou ela será exposta como uma ferramenta que pode ser chamada pela IA.

// 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 },
  });
});

12RPC reverso (host_request)

O processo do plugin não possui API Electron. Quando precisa de uma capacidade real do sistema, ele envia host_request para o host, que responde com host_response ou host_error. Este canal é totalmente independente de call_tool; você pode usá-lo ao lidar com uma chamada de ferramenta ou com um temporizador.

capabilityparamsresultCondição prévia
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Requer a permissão notifications. Retorna desativado em vez de um erro quando o usuário desativou as notificações da área de trabalho nas configurações. Quando viewId aponta para uma de suas visualizações encaixadas, clicar na notificação traz o aplicativo para frente e expande essa visualização.
render_pdf{ outputPath, title, content }{}o conteúdo é Markdown; o host renderiza um PDF em outputPath com seu renderizador integrado.
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)',
});

13Vida útil

EstágioComportamento
ComeçarPreguiçoso por padrão: não iniciado com o aplicativo, bifurcado na primeira chamada para uma de suas ferramentas (IA, ícone da barra de ferramentas, painel, comando, análise de documento). Os plug-ins que declaram activationEvents: ["onStartupFinished"] iniciam logo após a inicialização do aplicativo; use isso apenas para plug-ins que precisam de temporizadores de fundo (lembretes, pesquisas), não para plug-ins de ferramentas comuns.
ResidenteUma vez iniciado, o processo permanece ativo e é reutilizado nas chamadas de ferramenta. Você pode armazenar em cache o estado na memória, mas suponha que ele possa ser reiniciado a qualquer momento.
ColidirSe o processo for encerrado inesperadamente, o host o reinicia: imediatamente e depois de 5 e 15 segundos. Após 3 falhas consecutivas, ele será marcado como "travado" e não será mais tentado; os usuários veem o status na página do plugin. Todas as chamadas em voo retornam erros.
Desativar/desinstalar/reinstalarO processo é encerrado e as visualizações encaixadas e os painéis flutuantes são destruídos; uma reinstalação substitui a pasta inteira.
RegistrosSeus stdout/stderr são gravados no arquivo de log do aplicativo prefixado com o ID do plugin. Simples console.log é suficiente para depuração; consulte "Depuração e perguntas frequentes".
ENV
O processo filho começa com ELECTRON_RUN_AS_NODE=1, cwd é a pasta de instalação do plugin e a versão do Node corresponde ao Electron fornecido com o aplicativo. Não dependa de nenhum pacote instalado globalmente e não presuma que npx existe.

14Embalagem e publicação

Embalagem

Compacte a pasta do plugin com manifest.json na raiz do arquivo. Se você tiver dependências de terceiros, agrupe-as primeiro em um único arquivo com esbuild/webpack, em vez de enviar o node_modules inteiro.

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)

Publicação no mercado

AnBoWork → Ferramentas → Plugins → guia "Marketplace" → "Publicar plugin" no canto superior direito e, em seguida, carregue o zip. Os envios passam por revisão, que verifica se a declaração do manifesto corresponde ao código (especialmente as permissões). Uma vez aprovado, outros usuários podem encontrá-lo e instalá-lo no mercado, e a IA pode instalar plug-ins verificados para usuários automaticamente quando considerar necessário.

Atualizações

Clique em version e publique novamente. O aplicativo compara a versão instalada com a versão do marketplace e solicita a atualização; a reinstalação mantém o estado ativado e os valores de configurações do usuário.

15Exemplo completo: Todo Quadrante

Um plugin real e funcional (cortado): a IA pode adicionar e remover todos, a barra lateral direita mostra um quadro que é atualizado em tempo real e pode enviar lembretes de notificação do sistema. O encadeamento desses três abrange o protocolo da ferramenta, pushes de visualização encaixada e RPC reverso.

{
  "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 }]
}

16Depuração e perguntas frequentes

SintomaCausa e correção
A validação do manifesto falha na instalaçãoVerifique cada campo na tabela "Campos de manifesto", conforme a mensagem indica: formato de id, principal/entrada não deve conter .., o ícone deve estar na lista de permissões, o comando de atalhos de teclado deve apontar para uma ferramenta existente nos comandos, largura entre 240 e 640.
A IA nunca liga para minha ferramentaQuase sempre um problema description: o gatilho não está claro ou não está escrito em inglês. Consulte "Escrevendo uma boa descrição". Verifique também se o nome da ferramenta não colide com outra ferramenta.
A chamada expira (30 s)O script de entrada não respondeu com tool_result. Normalmente requestId não foi ecoado de volta, a mensagem type está escrita incorretamente ou o script foi lançado na inicialização e encerrado. Procure linhas prefixadas com [plugin:your-id] no log do aplicativo.
O status do plug-in mostra "travado"3 partidas consecutivas com falha. Geralmente um require de um pacote ausente; executar node index.js na pasta do plugin o reproduz. Após a correção, desative e ligue o plugin novamente na página do plugin.
Painel abre em brancoCaminho entry errado ou um erro JS na página. A página roda em um sandbox sem o require do Node; apenas window.pluginHost está disponível.
A notificação não apareceVerifique em ordem: o manifesto declara notifications; o resultado não é shown:false (o usuário desativou as notificações da área de trabalho); o sistema operacional permite notificações de AnBoWork.
Os valores das configurações não são legíveisOs valores estão em message.context.config[id], não em params.

Localização do arquivo de log

SOCaminho
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Consulte a documentação de integração de API