// Entwicklerhandbuch

Plugin-Entwicklerdokumente

Erstellen Sie Plugins für die AnBoWork Desktop-App: Fügen Sie neue Tools für die KI hinzu, docken Sie Ihr eigenes Panel neben einer Sitzung an, senden Sie Systembenachrichtigungen, analysieren Sie neue Dateiformate. Eine manifest.json plus ein Eingabeskript reichen aus, um loszulegen.

01Überblick

Ein AnBoWork-Plugin ist ein einfaches Node.js-Programm. Nach der Installation verzweigt die App ihn als eigenständigen untergeordneten Prozess (reiner Knoten, keine Electron-API) und kommuniziert mit ihm über prozessübergreifende Nachrichten. Es ist kein SDK-Paket erforderlich; Alles, was Sie brauchen, ist process.on('message') und process.send().

Alles, was ein Plugin tun kann, wird statisch in manifest.json deklariert: Die Zustimmungsseite vor der Installation und die Marktplatzüberprüfung können erkennen, welche Tools es hinzufügt und welche Berechtigungen es benötigt, ohne dass Plugin-Code ausgeführt werden muss. Die gesamte Benutzeroberfläche (Symbolleistensymbole, Einstellungen, Befehlspalette, Kontextmenüs) wird vom Host aus der Deklaration gerendert; Der Plugin-Code wird niemals im Hauptfenster ausgeführt. Nur angedockte Ansichten und schwebende Bedienfelder sind Ihr eigenes HTML und werden auf isolierten Sandbox-Seiten ausgeführt.

Fähigkeiten auf einen Blick

ManifestfeldFähigkeitWo Benutzer es sehen
toolsFügen Sie aufrufbare Tools für die KI hinzuDie KI ruft sie automatisch im Gespräch auf; Werkzeugaufrufe werden in der Sitzung angezeigt
viewsPermanente Ansicht, angedockt in der rechten Seitenleiste der SitzungEin zusätzliches Symbol in der Sitzungssymbolleiste öffnet Ihre Seite auf der rechten Seite
uiEigenständiges schwebendes FensterWird über das Menü „⋯“ in der Plugin-Liste geöffnet
sessionActionSymbol für dauerhafte Aktion in der SitzungssymbolleisteMit einem Klick wird das von Ihnen angegebene Tool ausgeführt
configurationEinträge auf der EinstellungsseiteEinstellungen → Plugin-Konfiguration; Bei jedem Aufruf werden Werte übergeben
commandsEinträge in der Befehlspalette „Strg/Befehl+K“.Befehlspalette
keybindingsGlobale TastaturkürzelEine Tastenkombination löst einen Befehl aus
menusElemente des SitzungskontextmenüsDas „⋯“ / Rechtsklick-Menü in einer Sitzungszeile
documentParsersParse-Dateien werden im Eingabefeld abgelegtWird automatisch aufgerufen, wenn der Benutzer eine passende Datei ablegt
activationEventsStarten Sie gemeinsam mit der AppUnsichtbar; für Plugins, die Hintergrund-Timer benötigen
permissionsErklären Sie die erforderlichen BerechtigungenZustimmungsseite vor der Installation

02Schnellstart

Ein minimales Plugin benötigt nur zwei Dateien.

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. Schreiben Sie 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. Schreiben Sie das Eingabeskript

// 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. In die App installieren

Öffnen Sie AnBoWork → obere Leiste „Extras“ → „Plugins“ → „Lokal installieren“ in der oberen rechten Ecke und wählen Sie dann den Plugin-Ordner (oder eine ZIP-Datei davon) aus. Auf einer Zustimmungsseite werden die deklarierten Berechtigungen und jedes Tool vor der Installation aufgelistet. Zum Aktivieren bestätigen.

4. Probieren Sie es aus

Sagen Sie der KI in jeder Sitzung: „Bitten Sie das Plugin, Alice Hallo zu sagen“. Basierend auf der Toolbeschreibung entscheidet die KI, say_hello aufzurufen, und der Aufruf und sein Ergebnis erscheinen in der Sitzung.

TIPP
Die KI in AnBoWork kann auch Plugins für Sie generieren (sagen Sie „Schreiben Sie mir ein Plugin, das X macht“ und es ruft create_local_plugin auf). Generierte Plugins sind standardmäßig deaktiviert, bis Sie den Code auf der Plugin-Seite überprüfen und sie manuell aktivieren. Diese Anleitung gilt gleichermaßen für das Lesen und Ändern von KI-generierten Plugins.

03Manifestfelder

FeldTypErforderlichBeschreibung
idstringJaReverse-Domain-Stil, z.B. com.example.my-plugin. Nur Buchstaben, Ziffern, Punkte und Bindestriche. Unveränderlich, sobald es erstellt wurde; Eine erneute Installation derselben ID wird als Neuinstallation behandelt.
namestringJaPlugin-Name. Zusammen mit description wird es dem Modell als Kontext zugeführt, daher wird Englisch empfohlen.
versionstringJaSemantische Version; Der Marktplatz nutzt es, um Updates zu erkennen.
descriptionstringJaEin Satz, der beschreibt, was das Plugin tut (Englisch).
authorstringNEINAutor.
readmestringNEINDie ausführliche Beschreibung wird auf der Detailseite des Marktplatzes angezeigt und unterstützt Markdown. Darin enthaltene Bilder müssen https URLs sein – relative Pfade zu in der ZIP-Datei gebündelten Dateien werden nicht unterstützt.
iconstringNEINIn der Marktplatzliste und auf der Detailseite angezeigtes Symbol – ein relativer Pfad zu einer in der ZIP-Datei gebündelten Datei (z. B. icon.png). Muss PNG sein, nicht SVG, mindestens 128×128.
mainstringJaEingabeskript, relativ zum Plugin-Ordner. .. Das Escapen des Ordners ist nicht zulässig.
permissionsstring[]Janetwork / filesystem / notifications; kann ein leeres Array sein. Siehe „Berechtigungsmodell“.
toolsobject[]JaWerkzeugdeklarationen; kann ein leeres Array sein. Siehe „Tools & Anrufprotokoll“.
documentParsersobject[]NEINFunktion zum Parsen von Dateien.
uiobjectNEINSchwebendes Panel:{ entry, title }
viewsobject[]NEINAngedockte Ansichten:{ id, title, icon, entry, width? }
sessionActionobjectNEINSymbolleistensymbol, höchstens eines pro Plugin.
configurationobject[]NEINListe der Einstellungseinträge.
commandsobject[]NEINEinträge in der Befehlspalette.
keybindingsobject[]NEINVerknüpfungen. command muss auf die tool eines Eintrags in commands[] verweisen, andernfalls wird die Installation abgelehnt.
menusobjectNEINDerzeit ist nur der Standort session/context verfügbar.
activationEventsstring[]NEINVorerst nur onStartupFinished.

Sprachkonventionen für Texte

Der Text im Manifest hat zwei Zielgruppen. Für das Modell: name, description, tools[].description, tools[].parameters.*.description. Schreiben Sie diese auf Englisch. Modelle verstehen englische Werkzeugbeschreibungen und füllen Parameter zuverlässiger aus. Für Personen: tools[].summary (einzeilige Zusammenfassung auf der Einwilligungsseite), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title und title innerhalb von menus. Schreiben Sie diese in der Sprache Ihrer Zielbenutzer.

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

04Berechtigungsmodell

permissions muss ehrlich widerspiegeln, was der Code tatsächlich tut. Auf der Zustimmungsseite vor der Installation werden alle einzelnen Elemente aufgeführt, sodass Benutzer entscheiden können, ob sie installieren möchten. Die Marktplatzüberprüfung prüft außerdem die Deklaration anhand des Codes.

ErlaubnisWann es deklariert werden mussWie der Gastgeber damit umgeht
filesystemBenutzerdateien lesen oder schreiben (Projektcode lesen, Dokumente erstellen, Daten im Home-Ordner speichern)Einverständniserklärung, die dem Benutzer angezeigt wird.
networkStellen Sie beliebige Netzwerkanfragen (APIs von Drittanbietern, Downloads)Einverständniserklärung, die dem Benutzer angezeigt wird.
notificationsSystembenachrichtigungen über den show_notification Reverse RPC anzeigenErzwungen: Ein Aufruf ohne die Deklaration gibt host_error zurück und es wird keine Benachrichtigung angezeigt.
NOTIZ
Der untergeordnete Plugin-Prozess ist eine vollständige Node.js-Umgebung. filesystem und network sind derzeit Deklarationen auf Zustimmungsebene, keine Laufzeit-Sandbox: Der Host fängt fs oder fetch nicht ab. Genau deshalb müssen Erklärungen ehrlich sein. Plugins, die zu wenig deklariert sind, werden bei der Marktüberprüfung abgelehnt und verlieren das Vertrauen der Benutzer, wenn sie entdeckt werden. Deklarieren Sie auch nicht zu viel (nicht genutzte Berechtigungen lassen Benutzer zögern).

Privatsphäre und Daten

context.cwd ist der Projektordner des Benutzers und kann Quellcode und vertrauliche Konfigurationen enthalten; Tool-Argumente können die Konversation des Benutzers enthalten. Senden Sie diese Daten nicht über das Netzwerk, es sei denn, die Funktion erfordert dies. Wenn Sie dies tun, geben Sie in description und summary an, was gesendet wird und an wen. Bewahren Sie die eigenen persistenten Daten des Plugins in einer Datei im Home-Ordner des Benutzers auf, die nach der Plugin-ID benannt ist, damit sie bei der Deinstallation leicht gefunden werden kann.

05Tools und Anrufprotokoll

Erklärung

FeldBeschreibung
nameWerkzeugname in Kleinbuchstaben und Unterstrichen, eindeutig in der gesamten App (er wird zusammen mit integrierten Tools und anderen Plugin-Tools beim Modell registriert).
descriptionBeschreibung für das Modell (Englisch). Geben Sie drei Dinge an: Was das Tool tut, was der Benutzer sagt, wann es aufgerufen werden soll und wann es nicht aufgerufen werden darf. Dies ist das wichtigste Feld dafür, ob die KI Ihr Plugin verwendet.
summaryEinzeilige, für Menschen lesbare Zusammenfassung, die auf der Einwilligungsseite angezeigt wird. Optional, aber dringend empfohlen; andernfalls wird stattdessen eine verkürzte Beschreibung angezeigt.
parametersStandard-JSON-Schemaobjekt mit einer description für jede Eigenschaft. Das Modell füllt daraus Argumente und der Host leitet sie ohne zusätzliche Validierung weiter. Überprüfen Sie daher die erforderlichen Felder in Ihrem Code.
renderAsOptional; derzeit nur checklist: Der Host rendert das todos-Array aus den Aufrufargumenten als Checklistenkarte, siehe unten.
Notiz
Das Modell kann hier nur in tools[] deklarierte Werkzeugnamen aufrufen – sessionAction.tool, commands[].tool und das tool-Feld in menus werden bei der Installation nur darauf überprüft, dass es sich um nicht leere Zeichenfolgen handelt. Der Host überprüft nicht, ob der Name in tools[] deklariert ist, und sendet dennoch einen Anruf unter diesem Namen, wenn die Schaltfläche, die Verknüpfung oder das Menüelement ausgelöst wird. Eine rein UI-ausgelöste Aktion kann also vollständig in Ihrem eigenen index.js gehandhabt werden, ohne jemals in tools[] aufzutauchen – aber dann kann die KI sie nie sehen oder aufrufen. Wenn Sie möchten, dass dieselbe Aktion sowohl durch Klicken als auch durch die Reaktion der KI auf eine Chat-Anfrage ausgelöst werden kann, deklarieren Sie denselben Namen auch in tools[], sodass beide Einstiegspunkte einen Handler teilen; Wenn es nur für die Benutzeroberfläche gedacht ist, lassen Sie es nicht deklariert – es ist nicht nötig, einen Eintrag hinzuzufügen, nur um vollständig auszusehen.

Nachrichtenprotokoll

Der Host sendet call_tool; Das Plugin muss mit einem tool_result oder tool_error antworten, das dasselbe requestId trägt. Bei einem Anruf ohne Antwort innerhalb von 30 Sekunden tritt eine Zeitüberschreitung auf und das Modell erhält einen Fehler.

// 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 akzeptiert nur text- und image-Blöcke (Bild als Base64 data plus mimeType); andere Typen werden gelöscht. details sind willkürlich strukturierte Daten, die niemals dem Modell zugeführt werden; Es dient zum Rendern der Host-Benutzeroberfläche und Ihrer eigenen angedockten Ansichten.

Dateiergebnisse: Karteikarten

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

Checklisten-Rendering: renderAs-Checkliste

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

Eine gute Beschreibung schreiben

Beschreiben Sie den Auslöser im Imperativ („Rufen Sie dies auf, wenn der Benutzer darum bittet…“) und geben Sie Ausschlüsse an („Rufen Sie dies nicht auf für…“). Wenn das Ergebnis eine Datei ist, bitten Sie das Modell, auf den zurückgegebenen Dateilink zu verweisen, anstatt zu behaupten, dass er generiert wurde. Wenn Argumente an eine externe Generierungs-API weitergeleitet werden, geben Sie an, in welcher Sprache sie vorliegen sollen. Vermeiden Sie Einzeiler wie „Aufgaben verwalten“; Das Modell weiß nicht, wann es das Werkzeug verwenden soll.

06Angedockte Ansichten (Ansichten)

Eine angedockte Ansicht ist eine dauerhafte Seite in der rechten Seitenleiste der Sitzung: Ein zusätzliches Symbol erscheint in der Sitzungssymbolleiste und Ihr HTML wird beim Öffnen rechts angezeigt. Der Seitenstatus (Bildlaufposition, Entwürfe) überlebt den Sitzungswechsel und das Reduzieren/Erweitern, die gleiche Semantik wie eine VS Code-Seitenleistenansicht.

FeldBeschreibung
idEinzigartig innerhalb des Plugins.
titleTooltip-Text für das Symbol (für Menschen lesbar).
iconWählen Sie aus der Host-Zulassungsliste: git-branch, checklist. Hält die Symbole in der gesamten App konsistent; Benutzerdefinierte Bilder werden nicht akzeptiert.
entryHTML-Datei, relativ zum Plugin-Ordner.
widthOptionale Standardbreite des Panels in Pixeln, 240 bis 640, Standard 360. Benutzer können die Größe durch Ziehen ändern und die Größe wird gespeichert.

Seitenlaufzeit

Die Seite läuft in einer Sandbox (sandbox + contextIsolation, kein Knoten, kein Zugriff auf Host-App-Daten) und kann nur über die wenigen Methoden auf window.pluginHost mit ihrem eigenen Plugin-Prozess kommunizieren. Bringen Sie Ihre eigenen Stile mit; Es wird empfohlen, dem Hell/Dunkel-Schema der App wie im folgenden Beispiel zu folgen.

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

Empfohlenes Muster: Rufen Sie beim ersten Laden eines Ihrer eigenen Abfragetools mit invoke() auf und lassen Sie es strukturierte Daten in details zurückgeben. Anschließend jede Änderung aus dem Plugin-Prozess mit ui_push pushen, statt abzufragen. Wenn die KI Daten in der Konversation ändert, wird das Panel sofort aktualisiert.

07Schwebendes Panel (UI)

ui: { entry, title } deklariert ein eigenständiges schwebendes Fenster (900×700), das Benutzer über das „⋯“-Menü in der Plugin-Liste öffnen; es wird beim Schließen zerstört. Die Seitenumgebung und die window.pluginHost-API sind identisch mit angedockten Ansichten, und ui_push wird sowohl für das schwebende Bedienfeld als auch für die angedockten Ansichten bereitgestellt. Geeignet für große Paneele, die gelegentlich geöffnet werden; Verwenden Sie views für Inhalte, die neben der Sitzung bleiben sollen.

08Sitzungssymbolleistensymbol (sessionAction)

Höchstens ein dauerhaftes Symbol pro Plugin. Wenn Sie darauf klicken, wird tool mit leeren Argumenten aufgerufen, sodass das Tool ohne Parameter etwas Sinnvolles tun muss.

FeldBeschreibung
toolTool zum Aufrufen per Klick.
iconSymbolname auf der Zulassungsliste.
tooltipHover-Text.
visibleToolOptional. Wenn es festgelegt ist, wird es vor dem Rendern mit leeren Argumenten aufgerufen und der boolesche Wert details.visible im Ergebnis entscheidet, ob das Symbol angezeigt wird. Nützlich für Laufzeitprüfungen wie „Ist der aktuelle Ordner ein Git-Repository“.
// 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 },
});

09Einstellungen (Konfiguration)

Deklarierte Einträge erscheinen unter der Plugin-Konfiguration auf der Einstellungsseite. Der Host speichert vom Benutzer geänderte Werte und übergibt sie bei jedem call_tool als context.config (geschlüsselt durch id, Rückfall auf defaultValue), unabhängig davon, ob der Anruf von der KI, dem Symbolleistensymbol oder Ihrem Panel kommt. Sie müssen nie danach fragen.

typeGerendert alsdefaultValue-Typ
selectSingle-Choice-Liste; Optionen kommen von optionsToolstring
booleanKippschalterboolean
stringTexteingabe, auf Unschärfe festgelegtstring
numberZahleneingabenumber
// 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.

10Befehlspalette / Verknüpfungen / Kontextmenü

Alle drei sind unterschiedliche Einstiegspunkte für „ein Tool mit leeren Argumenten aufrufen“; Ein Plugin kann mehrere deklarieren.

FeldFormBeschreibung
commands{ tool, icon, title }[]Wird in der Befehlspalette Strg/Befehl+K angezeigt und ausgelöst, nachdem der Benutzer danach gesucht hat.
keybindings{ command, key }[]command muss gleich tool eines Eintrags in commands[] sein. key verwendet die mod+shift+g-Notation, wobei mod Cmd auf macOS und Strg an anderer Stelle ist. Konflikte mit anderen Verknüpfungen werden nicht statisch überprüft. Wählen Sie daher ungewöhnliche Kombinationen aus.
menus{ "session/context": { tool, icon, title }[] }Zum Menü in einer Sitzungszeile hinzugefügt. Derzeit ist nur dieser Standort verfügbar.

11Dokumentparser (documentParsers)

Wenn der Benutzer eine Datei in das Eingabefeld einfügt, sucht der Host nach Parsern, die von aktivierten Plugins deklariert wurden, nach MIME-Typ und übergibt den Dateiinhalt als Base64 an die entsprechende toolName. Dies ist kein Tool, das die KI aufrufen möchte. Listen Sie daher nicht auch das Parser-Tool unter tools auf, da es sonst als von der KI aufrufbares Tool angezeigt wird.

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

Der Plugin-Prozess verfügt über keine Electron-API. Wenn es eine echte Systemfähigkeit benötigt, sendet es host_request an den Host, der mit host_response oder host_error antwortet. Dieser Kanal ist völlig unabhängig von call_tool; Sie können es während der Bearbeitung eines Werkzeugaufrufs oder über einen Timer verwenden.

capabilityparamsresultVoraussetzung
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Erfordert die Berechtigung notifications. Gibt „deaktiviert“ anstelle eines Fehlers zurück, wenn der Benutzer Desktop-Benachrichtigungen in den Einstellungen deaktiviert hat. Wenn viewId auf eine Ihrer angedockten Ansichten zeigt, wird durch Klicken auf die Benachrichtigung die App in den Vordergrund gebracht und diese Ansicht erweitert.
render_pdf{ outputPath, title, content }{}Inhalt ist Markdown; Der Host rendert mit seinem integrierten Renderer eine PDF-Datei in „outputPath“.
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)',
});

13Lebenszyklus

BühneVerhalten
StartStandardmäßig faul: nicht mit der App gestartet, beim ersten Aufruf eines Ihrer Tools (KI, Symbolleistensymbol, Bedienfeld, Befehl, Dokumentanalyse) gegabelt. Plugins, die activationEvents: ["onStartupFinished"] deklarieren, starten direkt nach dem Start der App; Verwenden Sie dies nur für Plugins, die Hintergrund-Timer (Erinnerungen, Umfragen) benötigen, nicht für gewöhnliche Tool-Plugins.
EinwohnerNach dem Start bleibt der Prozess aktiv und wird bei allen Toolaufrufen wiederverwendet. Sie können den Status im Speicher zwischenspeichern, gehen aber davon aus, dass er jederzeit neu gestartet werden kann.
AbsturzWenn der Prozess unerwartet beendet wird, startet ihn der Host neu: sofort, dann nach 5 und 15 Sekunden. Nach drei aufeinanderfolgenden Fehlern wird der Vorgang als „abgestürzt“ markiert und es wird kein neuer Versuch mehr unternommen. Benutzer sehen den Status auf der Plugin-Seite. Alle Anrufe während des Flugs geben Fehler zurück.
Deaktivieren / deinstallieren / neu installierenDer Prozess wird beendet und angedockte Ansichten und schwebende Panels werden zerstört; Bei einer Neuinstallation wird der gesamte Ordner ersetzt.
ProtokolleIhre stdout/stderr werden mit dem Präfix der Plugin-ID in die App-Protokolldatei geschrieben. Zum Debuggen reicht einfaches console.log; siehe „Debugging & FAQ“.
ENV
Der untergeordnete Prozess beginnt mit ELECTRON_RUN_AS_NODE=1, cwd ist der Plugin-Installationsordner und die Node-Version entspricht dem mit der App gebündelten Electron. Verlassen Sie sich nicht auf ein global installiertes Paket und gehen Sie nicht davon aus, dass npx existiert.

14Verpackung und Verlagswesen

Verpackung

Zippen Sie den Plugin-Ordner mit manifest.json im Archivstammverzeichnis. Wenn Sie Abhängigkeiten von Drittanbietern haben, bündeln Sie diese zuerst mit esbuild/webpack in einer einzigen Datei, anstatt das Ganze zu versenden 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)

Veröffentlichung auf dem Marktplatz

AnBoWork → Extras → Plugins → Registerkarte „Marktplatz“ → „Plugin veröffentlichen“ in der oberen rechten Ecke, dann laden Sie die ZIP-Datei hoch. Einreichungen durchlaufen eine Überprüfung, bei der überprüft wird, ob die Manifestdeklaration mit dem Code (insbesondere den Berechtigungen) übereinstimmt. Nach der Genehmigung können andere Benutzer es auf dem Marktplatz finden und installieren, und die KI kann verifizierte Plugins für Benutzer automatisch installieren, wenn sie diese für erforderlich hält.

Aktualisierungen

Bump version und erneut veröffentlichen. Die App vergleicht die installierte Version mit der Marketplace-Version und fordert zur Aktualisierung auf; Bei einer Neuinstallation bleiben der aktivierte Status und die Einstellungswerte des Benutzers erhalten.

15Vollständiges Beispiel: Todo Quadrant

Ein echtes, funktionierendes Plugin (getrimmt): Die KI kann Aufgaben hinzufügen und entfernen, die rechte Seitenleiste zeigt ein Board, das in Echtzeit aktualisiert wird, und es kann Erinnerungen an Systembenachrichtigungen senden. Die Verkettung dieser drei umfasst das Tool-Protokoll, Push-Vorgänge in der angedockten Ansicht und Reverse 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 }]
}

16Debugging und FAQ

SymptomUrsache und Lösung
Die Manifestvalidierung schlägt bei der Installation fehlVergleichen Sie jedes Feld mit der Tabelle „Manifestfelder“, da die Meldung Folgendes anzeigt: ID-Format, Haupt-/Eintrag darf nicht .. enthalten, Symbol muss auf der Zulassungsliste stehen, Tastenkombinationsbefehl muss auf ein vorhandenes Werkzeug in Befehlen verweisen, Breite zwischen 240 und 640.
Die KI ruft mein Werkzeug nie aufFast immer ein description Problem: Der Auslöser ist unklar oder nicht auf Englisch geschrieben. Siehe „Eine gute Beschreibung verfassen“. Stellen Sie außerdem sicher, dass der Werkzeugname nicht mit einem anderen Werkzeug kollidiert.
Anrufzeitüberschreitung (30 s)Das Eingabeskript hat nicht mit tool_result geantwortet. Normalerweise wurde requestId nicht zurückgegeben, die Nachricht type ist falsch geschrieben oder das Skript hat beim Start einen Fehler gemacht und beendet. Suchen Sie im App-Protokoll nach Zeilen mit dem Präfix [plugin:your-id].
Plugin-Status zeigt „abgestürzt“3 aufeinanderfolgende Fehlstarts. Im Allgemeinen ein require eines fehlenden Pakets; Wenn Sie node index.js im Plugin-Ordner ausführen, wird es reproduziert. Schalten Sie das Plugin nach der Reparatur auf der Plugin-Seite aus und wieder ein.
Das Panel wird leer geöffnetFalscher entry-Pfad oder ein JS-Fehler auf der Seite. Die Seite läuft in einer Sandbox ohne Node's require; Es ist nur window.pluginHost verfügbar.
Benachrichtigung erscheint nichtIn der Reihenfolge prüfen: Das Manifest deklariert notifications; das Ergebnis ist nicht shown:false (der Benutzer hat Desktop-Benachrichtigungen deaktiviert); Das Betriebssystem erlaubt Benachrichtigungen von AnBoWork.
Einstellungswerte sind nicht lesbarWerte sind in message.context.config[id], nicht in params.

Speicherort der Protokolldatei

BetriebssystemWeg
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Weitere Informationen finden Sie in den Dokumenten zur API-Integration