// Guía para desarrolladores

Documentos para desarrolladores de complementos

Cree complementos para la aplicación de escritorio AnBoWork: agregue nuevas herramientas para la IA, conecte su propio panel junto a una sesión, envíe notificaciones del sistema, analice nuevos formatos de archivo. Un manifest.json más un script de entrada es suficiente para comenzar.

01Descripción general

Un complemento AnBoWork es un programa Node.js simple. Después de la instalación, la aplicación la bifurca como un proceso secundario independiente (Nodo puro, sin API de Electron) y se comunica con ella a través de mensajes entre procesos. No se requiere ningún paquete SDK; todo lo que necesitas es process.on('message') y process.send().

Todo lo que puede hacer un complemento se declara estáticamente en manifest.json: la página de consentimiento de preinstalación y la revisión del mercado pueden indicar qué herramientas agrega y qué permisos necesita sin ejecutar ningún código de complemento. El host representa toda la interfaz de usuario (iconos de la barra de herramientas, configuraciones, paleta de comandos, menús contextuales) a partir de la declaración; El código del complemento nunca se ejecuta dentro de la ventana principal. Sólo las vistas acopladas y los paneles flotantes son su propio HTML y se ejecutan en páginas aisladas en un espacio aislado.

Capacidades de un vistazo

campo manifiestoCapacidadDónde lo ven los usuarios
toolsAgregue herramientas invocables para la IALa IA los llama automáticamente durante una conversación; Las llamadas a herramientas se muestran en la sesión.
viewsVista persistente acoplada en la barra lateral derecha de la sesiónUn ícono adicional en la barra de herramientas de la sesión abre su página a la derecha
uiVentana flotante independienteAbierto desde el menú "⋯" en la lista de complementos
sessionActionIcono de acción persistente en la barra de herramientas de la sesión.Un clic ejecuta la herramienta que especifiques
configurationEntradas en la página de configuraciónConfiguración → Configuración del complemento; los valores se pasan con cada llamada
commandsEntradas de la paleta de comandos Ctrl/Cmd+Kpaleta de comandos
keybindingsAtajos de teclado globalesUna combinación de teclas activa un comando
menusElementos del menú contextual de sesiónEl menú "⋯"/clic derecho en una fila de sesión
documentParsersAnalizar archivos colocados en el cuadro de entradaSe llama automáticamente cuando el usuario suelta un archivo coincidente.
activationEventsComience junto con la aplicaciónInvisible; para complementos que necesitan temporizadores en segundo plano
permissionsDeclarar los permisos requeridosPágina de consentimiento de preinstalación

02Inicio rápido

Un complemento mínimo necesita sólo dos archivos.

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. Escribe manifest.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. Escribe el guión 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. Instalar en la aplicación

Abra AnBoWork → barra superior "Herramientas" → "Complementos" → "Instalar localmente" en la esquina superior derecha, luego seleccione la carpeta del complemento (o un archivo zip). Una página de consentimiento enumera los permisos declarados y todas las herramientas antes de la instalación; confirme para habilitar.

4. Pruébalo

En cualquier sesión, dígale a la IA "pídale al complemento que salude a Alice". Según la descripción de la herramienta, la IA decide llamar a say_hello y la llamada y su resultado aparecen en la sesión.

CONSEJO
La IA dentro de AnBoWork también puede generar complementos para usted (diga "escríbame un complemento que haga X" y llamará a create_local_plugin). Los complementos generados están deshabilitados de forma predeterminada hasta que revise el código en la página del complemento y los habilite manualmente. Esta guía se aplica igualmente a la lectura y modificación de complementos generados por IA.

03Campos manifiestos

CampoTipoRequeridoDescripción
idstringSíEstilo de dominio inverso, p. com.example.my-plugin. Sólo letras, dígitos, puntos y guiones. Inmutable una vez creado; instalar la misma identificación nuevamente se trata como una reinstalación.
namestringSíNombre del complemento. Junto con description se introduce en el modelo como contexto, por lo que se recomienda el inglés.
versionstringSíVersión semántica; el mercado lo utiliza para detectar actualizaciones.
descriptionstringSíUna frase que describe lo que hace el complemento (inglés).
authorstringNoAutor.
readmestringNoLa descripción detallada que se muestra en la página de detalles del mercado admite Markdown. Las imágenes que contiene deben ser https URL; no se admiten rutas relativas a archivos empaquetados en el zip.
iconstringNoIcono que se muestra en la lista del mercado y en la página de detalles: una ruta relativa a un archivo incluido en el zip (por ejemplo, icon.png). Debe ser PNG, no SVG, al menos 128×128.
mainstringSíScript de entrada, relativo a la carpeta del complemento. .. no se permite escapar de la carpeta.
permissionsstring[]Sínetwork / filesystem / notifications; puede ser una matriz vacía. Consulte "Modelo de permisos".
toolsobject[]SíDeclaraciones de herramientas; puede ser una matriz vacía. Consulte "Herramientas y protocolo de llamadas".
documentParsersobject[]NoCapacidad de análisis de archivos.
uiobjectNoPanel flotante:{ entry, title }
viewsobject[]NoVistas acopladas:{ id, title, icon, entry, width? }
sessionActionobjectNoÍcono de la barra de herramientas, como máximo uno por complemento.
configurationobject[]NoLista de entradas de configuración.
commandsobject[]NoEntradas de la paleta de comandos.
keybindingsobject[]NoAtajos. command debe hacer referencia al tool de una entrada en commands[]; de lo contrario, se rechaza la instalación.
menusobjectNoPor ahora solo está disponible la ubicación session/context.
activationEventsstring[]NoSólo onStartupFinished por ahora.

Convenciones de idioma para texto

El texto del manifiesto tiene dos audiencias. Para el modelo: name, description, tools[].description, tools[].parameters.*.description. Escríbalos en inglés; Los modelos entienden las descripciones de herramientas en inglés y completan los parámetros de manera más confiable. Para personas: tools[].summary (resumen de una línea en la página de consentimiento), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title y title dentro de menus. Escríbalos en el idioma de sus usuarios objetivo.

{
  "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 permiso

permissions debe reflejar honestamente lo que realmente hace el código. La página de consentimiento de preinstalación enumera cada uno para que los usuarios puedan decidir si realizar la instalación; La revisión del mercado también verifica la declaración con el código.

PermisoCuando se debe declararCómo lo trata el anfitrión
filesystemLeer o escribir archivos de usuario (leer código de proyecto, generar documentos, almacenar datos en la carpeta de inicio)Consentimiento informado, mostrado al usuario.
networkRealizar cualquier solicitud de red (API de terceros, descargas)Consentimiento informado, mostrado al usuario.
notificationsMostrar notificaciones del sistema a través del show_notification RPC inversoEnforced: llamarlo sin la declaración devuelve host_error y no se muestra ninguna notificación.
NOTA
El proceso secundario del complemento es un entorno Node.js completo. filesystem y network son actualmente declaraciones de nivel de consentimiento, no un entorno limitado de ejecución: el host no intercepta fs o fetch. Precisamente por eso las declaraciones deben ser honestas. Los complementos que no declaran lo suficiente se rechazan en la revisión del mercado y pierden la confianza del usuario cuando se descubren. Tampoco declare demasiado (los permisos no utilizados hacen que los usuarios duden).

Privacidad y datos

context.cwd es la carpeta del proyecto del usuario y puede contener código fuente y configuración confidencial; Los argumentos de la herramienta pueden contener la conversación del usuario. No envíe estos datos a través de la red a menos que la función lo requiera, y cuando lo haga, indique en description y summary qué se envía y a quién. Mantenga los datos persistentes del complemento en un archivo en la carpeta de inicio del usuario con el nombre de la identificación del complemento, para que sea fácil de encontrar al desinstalarlo.

05Herramientas y protocolo de llamadas

Declaración

CampoDescripción
nameNombre de la herramienta en letras minúsculas y guiones bajos, único en toda la aplicación (está registrado con el modelo junto con las herramientas integradas y las herramientas de otros complementos).
descriptionDescripción del modelo (inglés). Indique tres cosas: qué hace la herramienta, qué dice el usuario, cuándo se debe llamar y cuándo no se debe llamar. Este es el campo más importante para determinar si la IA utiliza su complemento.
summaryResumen de una línea legible por humanos que se muestra en la página de consentimiento. Opcional pero muy recomendable; de lo contrario, se muestra una descripción truncada.
parametersObjeto de esquema JSON estándar con un description en cada propiedad. El modelo completa los argumentos y el host los pasa sin validación adicional, así que verifique los campos obligatorios en su código.
renderAsOpcional; Actualmente solo checklist: el host representa la matriz todos de los argumentos de llamada como una tarjeta de lista de verificación, consulte a continuación.
Nota
El modelo solo puede llamar a los nombres de herramientas declarados en tools[] aquí: sessionAction.tool, commands[].tool y el campo tool dentro de menus solo se verifica que sean cadenas no vacías en el momento de la instalación; el anfitrión no verifica que el nombre esté declarado en tools[] y aun así envía una llamada con ese nombre cuando se activa el botón, acceso directo o elemento de menú. Por lo tanto, una acción puramente activada por la interfaz de usuario se puede manejar completamente en tu propio index.js sin aparecer en tools[], pero entonces la IA nunca podrá verla ni llamarla. Si desea que se pueda activar la misma acción al hacer clic y que la IA actúe en una solicitud de chat, declare ese mismo nombre en tools[] también, de modo que ambos puntos de entrada compartan un controlador; si está destinado a permanecer solo en la interfaz de usuario, déjelo sin declarar; no es necesario agregar una entrada solo para que parezca completo.

Protocolo de mensajes

El anfitrión envía call_tool; el complemento debe responder con un tool_result o tool_error llevando el mismo requestId. Una llamada sin respuesta dentro de los 30 segundos caduca y el modelo recibe un error.

// 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 acepta solo los bloques text y image (imagen como base64 data más mimeType); otros tipos se eliminan. details son datos estructurados arbitrarios que nunca se introducen en el modelo; es para la representación de la interfaz de usuario del host y sus propias vistas acopladas.

Resultados del archivo: tarjetas de archivo

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

Representación de lista de verificación: lista de verificación 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" }
  ]
}

Escribir una buena descripción

Describa el desencadenante en imperativo ("Llame a esto cuando el usuario lo solicite...") y indique las exclusiones ("No llame a esto para..."). Si el resultado es un archivo, solicite al modelo que haga referencia al enlace del archivo devuelto en lugar de afirmar que se generó. Si los argumentos se reenvían a una API de generación externa, indique en qué idioma deberían estar. Evite frases ingeniosas como "administrar todos"; el modelo no sabrá cuándo utilizar la herramienta.

06Vistas acopladas (vistas)

Una vista acoplada es una página persistente en la barra lateral derecha de la sesión: aparece un ícono adicional en la barra de herramientas de la sesión y su HTML se muestra a la derecha cuando se abre. El estado de la página (posición de desplazamiento, borradores) sobrevive al cambio de sesiones y al colapso/expansión, la misma semántica que una vista de barra lateral de VS Code.

CampoDescripción
idÚnico dentro del complemento.
titleTexto de información sobre herramientas para el icono (legible por humanos).
iconElija de la lista de hosts permitidos: git-branch, checklist. Mantiene los íconos consistentes en toda la aplicación; No se aceptan imágenes personalizadas.
entryArchivo HTML, relativo a la carpeta del complemento.
widthAncho del panel predeterminado opcional en píxeles, de 240 a 640, 360 predeterminado. Los usuarios pueden arrastrar para cambiar el tamaño y se recuerda el tamaño.

Tiempo de ejecución de la página

La página se ejecuta en una zona de pruebas (sandbox + contextIsolation, sin nodo, sin acceso a los datos de la aplicación host) y solo puede comunicarse con su propio proceso de complemento a través de los pocos métodos en window.pluginHost. Trae tus propios estilos; Se recomienda seguir el esquema claro/oscuro de la aplicación como en el siguiente ejemplo.

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

Patrón recomendado: en la primera carga llame a una de sus propias herramientas de consulta con invoke() y haga que devuelva datos estructurados en details; luego presione cada cambio del proceso del complemento con ui_push en lugar de sondear. Cuando la IA cambia los datos de la conversación, el panel se actualiza inmediatamente.

07Panel flotante (ui)

ui: { entry, title } declara una ventana flotante independiente (900×700) que los usuarios abren desde el menú "⋯" en la lista de complementos; se destruye al cerrar. El entorno de la página y la API window.pluginHost son idénticos a las vistas acopladas, y ui_push se entrega tanto al panel flotante como a las vistas acopladas. Adecuado para grandes paneles abiertos ocasionalmente; use views para el contenido que debe permanecer al lado de la sesión.

08Icono de la barra de herramientas de sesión (sessionAction)

Como máximo un icono persistente por complemento. Al hacer clic en él, se llama tool con argumentos vacíos, por lo que esa herramienta debe hacer algo significativo sin parámetros.

CampoDescripción
toolHerramienta para llamar al hacer clic.
iconNombre del icono incluido en la lista de permitidos.
tooltipColoca el cursor sobre el texto.
visibleToolOpcional. Cuando se establece, se llama con argumentos vacíos antes de renderizar y el booleano details.visible en el resultado decide si se muestra el icono. Útil para comprobaciones en tiempo de ejecución como "¿la carpeta actual es un repositorio 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 },
});

09Ajustes (configuración)

Las entradas declaradas aparecen en la configuración del complemento en la página de configuración. El host almacena los valores modificados por el usuario y los pasa como context.config en cada call_tool (tecleado por id, volviendo a defaultValue), ya sea que la llamada provenga de la IA, el ícono de la barra de herramientas o su panel. Nunca necesitas pedirlos.

typeRepresentado comotipo de valor predeterminado
selectLista de opción única; las opciones vienen de optionsToolstring
booleaninterruptor de palancaboolean
stringEntrada de texto, comprometida en desenfoquestring
numberEntrada de númerosnumber
// 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/atajos/menú contextual

Los tres son puntos de entrada diferentes para "llamar a una herramienta con argumentos vacíos"; un complemento puede declarar varios.

CampoFormaDescripción
commands{ tool, icon, title }[]Se muestra en la paleta de comandos Ctrl/Cmd+K y se activa después de que el usuario lo busca.
keybindings{ command, key }[]command debe ser igual al tool de una entrada en commands[]. key usa la notación mod+shift+g, donde mod es Cmd en macOS y Ctrl en otros lugares. Los conflictos con otros atajos no se verifican estáticamente, así que elija combinaciones poco comunes.
menus{ "session/context": { tool, icon, title }[] }Agregado al menú en una fila de sesión. Sólo esta ubicación está disponible por ahora.

11Analizadores de documentos (documentParsers)

Cuando el usuario coloca un archivo en el cuadro de entrada, el host busca analizadores declarados por complementos habilitados por tipo MIME y pasa el contenido del archivo como base64 al toolName correspondiente. Esta no es una herramienta a la que la IA decida llamar, así que no también incluya la herramienta de análisis en tools, o quedará expuesta como una herramienta a la que la IA puede llamar.

// 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 inverso (host_request)

El proceso del complemento no tiene API de Electron. Cuando necesita una capacidad real del sistema, envía host_request al host, que responde con host_response o host_error. Este canal es totalmente independiente de call_tool; puede usarlo mientras maneja una llamada de herramienta o desde un temporizador.

capabilityparamsresultCondición previa
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Requiere el permiso notifications. Devuelve deshabilitado en lugar de un error cuando el usuario desactivó las notificaciones de escritorio en la configuración. Cuando viewId apunta a una de sus vistas acopladas, al hacer clic en la notificación, la aplicación pasa al frente y expande esa vista.
render_pdf{ outputPath, title, content }{}el contenido es Markdown; el host procesa un PDF en OutputPath con su procesador incorporado.
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)',
});

13Ciclo vital

EscenarioComportamiento
ComenzarPerezoso por defecto: no se inicia con la aplicación, se bifurca en la primera llamada a una de sus herramientas (IA, ícono de la barra de herramientas, panel, comando, análisis de documentos). Los complementos que declaran activationEvents: ["onStartupFinished"] se inician inmediatamente después de que la aplicación termina de iniciarse; Úselo solo para complementos que necesiten temporizadores en segundo plano (recordatorios, sondeos), no para complementos de herramientas comunes.
ResidenteUna vez iniciado, el proceso permanece activo y se reutiliza en todas las llamadas a herramientas. Puede almacenar en caché el estado en la memoria, pero asume que puede reiniciarse en cualquier momento.
ChocarSi el proceso sale inesperadamente, el host lo reinicia: inmediatamente, luego después de 5 y 15 segundos. Después de 3 fallos consecutivos, se marca como "fallado" y ya no se vuelve a intentar; los usuarios ven el estado en la página del complemento. Llamadas en vuelo todos los errores de devolución.
Desactivar/desinstalar/reinstalarSe da por terminado el proceso y se destruyen las vistas acopladas y los paneles flotantes; una reinstalación reemplaza toda la carpeta.
RegistrosSu stdout/stderr se escribe en el archivo de registro de la aplicación con el prefijo ID del complemento. Simple console.log es suficiente para depurar; consulte "Depuración y preguntas frecuentes".
ENV
El proceso secundario comienza con ELECTRON_RUN_AS_NODE=1, cwd es la carpeta de instalación del complemento y la versión de Node coincide con la de Electron incluida con la aplicación. No dependa de ningún paquete instalado globalmente y no asuma que npx existe.

14Embalaje y publicación

Embalaje

Comprima la carpeta del complemento con manifest.json en la raíz del archivo. Si tiene dependencias de terceros, agrúpelas primero en un solo archivo con esbuild/webpack en lugar de enviar el node_modules completo.

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)

Publicar en el mercado

AnBoWork → Herramientas → Complementos → pestaña "Mercado" → "Publicar complemento" en la esquina superior derecha, luego sube el archivo zip. Los envíos ingresan a revisión, que verifica que la declaración del manifiesto coincida con el código (especialmente los permisos). Una vez aprobado, otros usuarios pueden encontrarlo e instalarlo en el mercado, y la IA puede instalar complementos verificados para los usuarios automáticamente cuando lo considere necesario.

Actualizaciones

Suba version y publique nuevamente. La aplicación compara la versión instalada con la versión del mercado y solicita una actualización; la reinstalación mantiene el estado habilitado del usuario y los valores de configuración.

15Ejemplo completo: Cuadrante Todo

Un complemento real y funcional (recortado): la IA puede agregar y eliminar todos, la barra lateral derecha muestra un tablero que se actualiza en tiempo real y puede enviar recordatorios de notificaciones del sistema. Encadenar estos tres cubre el protocolo de la herramienta, los push de vista acoplada y el RPC inverso.

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

16Depuración y preguntas frecuentes

SíntomaCausa y solución
La validación del manifiesto falla durante la instalaciónVerifique cada campo con la tabla "Campos de manifiesto", ya que el mensaje indica: formato de identificación, principal/entrada no debe contener .., el icono debe estar en la lista de permitidos, el comando de combinaciones de teclas debe apuntar a una herramienta existente en los comandos, ancho entre 240 y 640.
La IA nunca llama a mi herramientaCasi siempre es un problema description: el disparador no está claro o no está escrito en inglés. Consulte "Escribir una buena descripción". También verifique que el nombre de la herramienta no colisione con otra herramienta.
Tiempo de espera de llamada (30 s)El script de entrada no respondió con tool_result. Por lo general, requestId no se repite, el mensaje type está mal escrito o el script se inicia al iniciar y sale. Busque líneas con el prefijo [plugin:your-id] en el registro de la aplicación.
El estado del complemento muestra "fallado"3 salidas fallidas consecutivas. Comúnmente un require de un paquete faltante; ejecutar node index.js en la carpeta del complemento lo reproduce. Después de solucionarlo, desactive y vuelva a activar el complemento en la página del complemento.
El panel se abre en blancoRuta entry incorrecta o un error JS en la página. La página se ejecuta en una zona de pruebas sin require de Node; solo window.pluginHost está disponible.
No aparece la notificacionVerifique en orden: el manifiesto declara notifications; el resultado no es shown:false (el usuario desactivó las notificaciones del escritorio); el sistema operativo permite notificaciones desde AnBoWork.
Los valores de configuración no son legiblesLos valores están en message.context.config[id], no en params.

Ubicación del archivo de registro

SOCamino
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Consulte los documentos de integración de API