// Przewodnik programisty

Dokumentacja programisty wtyczek

Twórz wtyczki do aplikacji komputerowej AnBoWork: dodawaj nowe narzędzia dla sztucznej inteligencji, dokuj własny panel obok sesji, wysyłaj powiadomienia systemowe, analizuj nowe formaty plików. Na początek wystarczy jeden manifest.json i jeden skrypt wejściowy.

01Przegląd

Wtyczka AnBoWork to zwykły program Node.js. Po instalacji aplikacja przekształca ją w samodzielny proces potomny (czysty węzeł, bez interfejsu Electron API) i komunikuje się z nim za pośrednictwem komunikatów między procesami. Nie jest wymagany żaden pakiet SDK; wszystko czego potrzebujesz to process.on('message') i process.send().

Wszystko, co może zrobić wtyczka, jest zadeklarowane statycznie w manifest.json: strona zgody przed instalacją i przegląd na rynku mogą określić, jakie narzędzia dodaje i jakich uprawnień potrzebuje, bez uruchamiania żadnego kodu wtyczki. Cały interfejs użytkownika (ikony na pasku narzędzi, ustawienia, paleta poleceń, menu kontekstowe) jest renderowany przez hosta na podstawie deklaracji; kod wtyczki nigdy nie działa w głównym oknie. Tylko zadokowane widoki i pływające panele są Twoim własnym kodem HTML i działają na izolowanych stronach w trybie piaskownicy.

Możliwości w skrócie

Pole manifestuZdolnośćGdzie użytkownicy to widzą
toolsDodaj wywoływalne narzędzia dla sztucznej inteligencjiAI wywołuje je automatycznie w trakcie rozmowy; wywołania narzędzi są wyświetlane w sesji
viewsWidok stały zadokowany na prawym pasku bocznym sesjiDodatkowa ikona na pasku narzędzi sesji otwiera Twoją stronę po prawej stronie
uiSamodzielne pływające oknoOtwierane z menu „⋯” na liście wtyczek
sessionActionIkona trwałej akcji na pasku narzędzi sesjiJedno kliknięcie uruchamia określone narzędzie
configurationWpisy na stronie ustawieńUstawienia → Konfiguracja wtyczki; wartości są przekazywane przy każdym wywołaniu
commandsWpisy palety poleceń Ctrl/Cmd+KPaleta poleceń
keybindingsGlobalne skróty klawiaturoweKombinacja klawiszy uruchamia polecenie
menusElementy menu kontekstowego sesjiMenu „⋯” / kliknięcie prawym przyciskiem myszy w wierszu sesji
documentParsersAnalizuj pliki upuszczone do pola wejściowegoWywoływane automatycznie, gdy użytkownik upuści pasujący plik
activationEventsZacznij razem z aplikacjąNiewidzialny; dla wtyczek, które wymagają liczników czasu w tle
permissionsZadeklaruj wymagane uprawnieniaStrona zgody przed instalacją

02Szybki start

Minimalna wtyczka potrzebuje tylko dwóch plików.

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. Napisz 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. Napisz skrypt wejścia

// 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. Zainstaluj w aplikacji

Otwórz AnBoWork → górny pasek „Narzędzia” → „Wtyczki” → „Zainstaluj lokalnie” w prawym górnym rogu, a następnie wybierz folder wtyczek (lub jego plik zip). Strona zgody zawiera listę zadeklarowanych uprawnień i każdego narzędzia przed instalacją; potwierdź, aby włączyć.

4. Spróbuj

W dowolnej sesji powiedz AI „poproś wtyczkę, aby przywitała się z Alicją”. Na podstawie opisu narzędzia AI decyduje się wywołać say_hello, a wywołanie i jego wynik pojawiają się w sesji.

WSKAZÓWKA
Sztuczna inteligencja wewnątrz AnBoWork może również generować dla Ciebie wtyczki (powiedz „napisz mi wtyczkę, która wykonuje X”, a wywoła ona funkcję create_local_plugin). Wygenerowane wtyczki są domyślnie wyłączone, dopóki nie przejrzysz kodu na stronie wtyczki i nie włączysz ich ręcznie. Ten przewodnik dotyczy w równym stopniu odczytywania i modyfikowania wtyczek generowanych przez sztuczną inteligencję.

03Pola manifestu

PoleTypWymaganyOpis
idstringTakStyl domeny odwrotnej, np. com.example.my-plugin. Tylko litery, cyfry, kropki i łączniki. Niezmienny po utworzeniu; ponowna instalacja tego samego identyfikatora jest traktowana jako ponowna instalacja.
namestringTakNazwa wtyczki. Razem z description jest on podawany do modelu jako kontekst, dlatego zalecany jest język angielski.
versionstringTakWersja semantyczna; rynek używa go do wykrywania aktualizacji.
descriptionstringTakJedno zdanie opisujące działanie wtyczki (w języku angielskim).
authorstringNIEAutor.
readmestringNIEDługi opis wyświetlany na stronie szczegółów rynku obsługuje Markdown. Obrazy w nim muszą mieć adresy URL https — ścieżki względne do plików spakowanych w pliku zip nie są obsługiwane.
iconstringNIEIkona wyświetlana na liście rynku i stronie szczegółów — ścieżka względna do pliku spakowanego w pliku ZIP (np. icon.png). Musi być w formacie PNG, a nie SVG, co najmniej 128 × 128.
mainstringTakSkrypt wejściowy względem folderu wtyczki. .. ucieczka z folderu jest niedozwolona.
permissionsstring[]Taknetwork / filesystem / notifications; może być pustą tablicą. Zobacz „Model uprawnień”.
toolsobject[]TakDeklaracje narzędzi; może być pustą tablicą. Patrz „Narzędzia i protokół połączeń”.
documentParsersobject[]NIEMożliwość analizowania plików.
uiobjectNIEPływający panel:{ entry, title }
viewsobject[]NIEZadokowane widoki:{ id, title, icon, entry, width? }
sessionActionobjectNIEIkona paska narzędzi, maksymalnie jedna na wtyczkę.
configurationobject[]NIELista wpisów ustawień.
commandsobject[]NIEWpisy palety poleceń.
keybindingsobject[]NIESkróty. command musi odwoływać się do tool wpisu w commands[], w przeciwnym razie instalacja zostanie odrzucona.
menusobjectNIENa razie dostępna jest tylko lokalizacja session/context.
activationEventsstring[]NIENa razie tylko onStartupFinished.

Konwencje językowe tekstu

Tekst w manifeście ma dwóch odbiorców. Dla modelu: name, description, tools[].description, tools[].parameters.*.description. Napisz je po angielsku; modele lepiej rozumieją angielskie opisy narzędzi i parametry wypełnienia. Dla osób: tools[].summary (jednowierszowe podsumowanie na stronie zgody), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title i ​​title wewnątrz menus. Zapisz je w języku docelowych użytkowników.

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

04Model uprawnień

permissions musi szczerze odzwierciedlać to, co faktycznie robi kod. Strona zgody na instalację przed instalacją zawiera listę poszczególnych z nich, dzięki czemu użytkownicy mogą zdecydować, czy dokonać instalacji; Przegląd rynku sprawdza również deklarację pod kątem zgodności z kodem.

PozwolenieKiedy trzeba to zgłosićJak gospodarz to traktuje
filesystemOdczyt lub zapis plików użytkownika (odczyt kodu projektu, generowanie dokumentów, przechowywanie danych w folderze domowym)Świadoma zgoda pokazana użytkownikowi.
networkWykonywanie dowolnego żądania sieciowego (interfejsy API stron trzecich, pliki do pobrania)Świadoma zgoda pokazana użytkownikowi.
notificationsWyświetlanie powiadomień systemowych poprzez odwrotne wywołanie RPC show_notificationEnforced: wywołanie bez deklaracji zwraca host_error i nie jest wyświetlane żadne powiadomienie.
NOTATKA
Proces potomny wtyczki jest pełnym środowiskiem Node.js. filesystem i network są obecnie deklaracjami na poziomie zgody, a nie piaskownicą środowiska wykonawczego: host nie przechwytuje fs ani fetch. Właśnie dlatego deklaracje muszą być szczere. Wtyczki, które nie deklarują, są odrzucane podczas przeglądu na rynku i tracą zaufanie użytkowników po wykryciu. Nie deklaruj też przesadnie (niewykorzystane uprawnienia powodują, że użytkownicy się wahają).

Prywatność i dane

context.cwd to folder projektu użytkownika i może zawierać kod źródłowy i poufną konfigurację; Argumenty narzędzia mogą zawierać rozmowę użytkownika. Nie wysyłaj tych danych przez sieć, chyba że funkcja tego wymaga, a kiedy to zrobisz, wpisz w description i summary, co jest wysyłane i do kogo. Przechowuj trwałe dane wtyczki w pliku w folderze domowym użytkownika, którego nazwa pochodzi od identyfikatora wtyczki, aby można je było łatwo znaleźć podczas odinstalowywania.

05Narzędzia i protokół połączeń

Deklaracja

PoleOpis
nameNazwa narzędzia pisana małymi literami i podkreśleniami, unikalna w całej aplikacji (jest zarejestrowana w modelu wraz z narzędziami wbudowanymi i narzędziami innych wtyczek).
descriptionOpis modelu (w języku angielskim). Podaj trzy rzeczy: co robi narzędzie, co mówi użytkownik, kiedy należy je wywołać i kiedy nie należy go wywoływać. Jest to najważniejsze pole określające, czy sztuczna inteligencja korzysta z Twojej wtyczki.
summaryJednowierszowe podsumowanie czytelne dla człowieka wyświetlane na stronie zgody. Opcjonalne, ale zdecydowanie zalecane; w przeciwnym razie zamiast tego wyświetlany jest skrócony opis.
parametersStandardowy obiekt schematu JSON z wartością description w każdej właściwości. Model wypełnia z niego argumenty, a host przekazuje je bez dodatkowej weryfikacji, więc sprawdź wymagane pola w swoim kodzie.
renderAsFakultatywny; obecnie tylko checklist: host renderuje tablicę todos z argumentów wywołania jako kartę listy kontrolnej, patrz poniżej.
Notatka
Model może wywoływać tylko nazwy narzędzi zadeklarowane w tools[] tutaj — sessionAction.tool, commands[].tool, a pole tool wewnątrz menus są sprawdzane tylko pod kątem niepustych ciągów znaków podczas instalacji; host nie sprawdza, czy nazwa została zadeklarowana w tools[] i nadal wywołuje wywołanie pod tą nazwą, gdy zostanie uruchomiony przycisk, skrót lub element menu. Zatem czynność uruchamianą wyłącznie przez interfejs użytkownika można wykonać w całości w index.js, nie pojawiając się w tools[] — ale wtedy sztuczna inteligencja nigdy jej nie zobaczy ani nie wywoła. Jeśli chcesz, aby ta sama akcja była uruchamiana zarówno przez kliknięcie, jak i przez sztuczną inteligencję działającą na żądanie czatu, zadeklaruj tę samą nazwę również w tools[], aby oba punkty wejścia miały ten sam moduł obsługi; jeśli ma on pozostać wyłącznie interfejsem użytkownika, pozostaw go niezadeklarowanym — nie ma potrzeby dodawania wpisu, aby wyglądał na kompletny.

Protokół wiadomości

Host wysyła call_tool; wtyczka musi odpowiedzieć tool_result lub tool_error niosąc to samo requestId. Połączenie bez odpowiedzi w ciągu 30 sekund upłynie i model otrzyma błąd.

// 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 akceptuje tylko bloki text i image (obraz jako base64 data plus mimeType); inne typy są odrzucane. details to dane o dowolnej strukturze, które nigdy nie są wprowadzane do modelu; służy do renderowania interfejsu użytkownika hosta i własnych zadokowanych widoków.

Wyniki plików: karty plików

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

Renderowanie listy kontrolnej: lista kontrolna 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" }
  ]
}

Napisanie dobrego opisu

Opisz wyzwalacz w trybie rozkazującym („Wywołaj to, gdy użytkownik poprosi o…”) i określ wykluczenia („Nie wywoływaj tego dla…”). Jeśli wynikiem jest plik, poproś model, aby odwołał się do zwróconego łącza do pliku, zamiast twierdzić, że zostało ono wygenerowane. Jeśli argumenty są przekazywane do zewnętrznego interfejsu API generacji, powiedz, w jakim języku powinny być zapisane. Unikaj jednowierszowych sformułowań, takich jak „zarządzaj zadaniami”; model nie będzie wiedział, kiedy użyć narzędzia.

06Widoki zadokowane (widoki)

Widok zadokowany to trwała strona na prawym pasku bocznym sesji: na pasku narzędzi sesji pojawia się dodatkowa ikona, a po otwarciu kod HTML jest wyświetlany po prawej stronie. Stan strony (pozycja przewijania, wersje robocze) przetrwa sesje przełączania i zwijanie/rozwijanie, taka sama semantyka jak widok paska bocznego VS Code.

PoleOpis
idUnikalny w wtyczce.
titleTekst podpowiedzi ikony (czytelny dla człowieka).
iconWybierz z listy dozwolonych gospodarzy: git-branch, checklist. Zapewnia spójność ikon w całej aplikacji; niestandardowe obrazy nie są akceptowane.
entryPlik HTML względem folderu wtyczki.
widthOpcjonalna domyślna szerokość panelu w pikselach, od 240 do 640, domyślna 360. Użytkownicy mogą przeciągać, aby zmienić rozmiar, a rozmiar zostanie zapamiętany.

Czas działania strony

Strona działa w piaskownicy (sandbox + contextIsolation, brak węzła, brak dostępu do danych aplikacji hosta) i może komunikować się z własnym procesem wtyczki tylko za pomocą kilku metod na window.pluginHost. Przynieś swój własny styl; Zalecane jest przestrzeganie schematu jasny/ciemny aplikacji, jak w przykładzie poniżej.

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

Zalecany wzorzec: przy pierwszym ładowaniu wywołaj jedno z własnych narzędzi zapytań za pomocą invoke() i poproś o zwrócenie uporządkowanych danych w details; następnie wypchnij każdą zmianę z procesu wtyczki za pomocą ui_push zamiast odpytywania. Gdy AI zmieni dane w rozmowie, panel natychmiast się odświeża.

07Pływający panel (UI)

ui: { entry, title } deklaruje samodzielne pływające okno (900×700), które użytkownicy otwierają z menu „⋯” na liście wtyczek; zostaje zniszczony po zamknięciu. Środowisko strony i interfejs API window.pluginHost są identyczne z widokami zadokowanymi, a ui_push jest dostarczane zarówno do panelu pływającego, jak i do widoków zadokowanych. Nadaje się do dużych paneli otwieranych sporadycznie; użyj views dla treści, które powinny pozostać obok sesji.

08Ikona paska narzędzi sesji (sessionAction)

Maksymalnie jedna trwała ikona na wtyczkę. Kliknięcie wywołuje tool z pustymi argumentami, więc narzędzie musi zrobić coś znaczącego bez parametrów.

PoleOpis
toolNarzędzie do wywoływania po kliknięciu.
iconNazwa ikony na liście dozwolonych.
tooltipNajedź kursorem na tekst.
visibleToolFakultatywny. Gdy jest ustawiona, jest wywoływana z pustymi argumentami przed renderowaniem, a wartość logiczna details.visible w wyniku decyduje, czy ikona się pojawi. Przydatne do sprawdzania w czasie wykonywania, np. „czy bieżący folder jest repozytorium 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 },
});

09Ustawienia (konfiguracja)

Zadeklarowane wpisy pojawiają się w konfiguracji wtyczki na stronie ustawień. Host przechowuje wartości zmienione przez użytkownika i przekazuje je jako context.config co call_tool (z kluczem id, z powrotem do defaultValue), niezależnie od tego, czy połączenie pochodzi z AI, ikony na pasku narzędzi czy z panelu. Nigdy nie musisz o nie prosić.

typeRenderowane jakotyp wartości domyślnej
selectLista jednokrotnego wyboru; opcje pochodzą z optionsToolstring
booleanPrzełącznikboolean
stringWprowadzanie tekstu, popełnione przy rozmyciustring
numberWprowadzanie numerunumber
// 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 poleceń / skróty / menu kontekstowe

Wszystkie trzy to różne punkty wejścia dla „wywołania narzędzia z pustymi argumentami”; wtyczka może zadeklarować kilka.

PoleKształtOpis
commands{ tool, icon, title }[]Wyświetlany na palecie poleceń Ctrl/Cmd+K i wywoływany po wyszukaniu go przez użytkownika.
keybindings{ command, key }[]command musi być równe tool wpisu w commands[]. key używa notacji mod+shift+g, gdzie mod to Cmd na macOS i Ctrl gdzie indziej. Konflikty z innymi skrótami nie są sprawdzane statycznie, więc wybieraj nietypowe kombinacje.
menus{ "session/context": { tool, icon, title }[] }Dodano do menu w wierszu sesji. Na razie dostępna jest tylko ta lokalizacja.

11Parsery dokumentów (documentParsers)

Gdy użytkownik upuści plik w polu wejściowym, host wyszukuje parsery zadeklarowane przez włączone wtyczki według typu MIME i przekazuje zawartość pliku jako base64 do pasującego toolName. Nie jest to narzędzie, które sztuczna inteligencja zdecyduje się wywołać, dlatego nie wypisz także narzędzie parsera pod tools, w przeciwnym razie zostanie ono ujawnione jako narzędzie wywoływane przez sztuczną inteligencję.

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

12Odwrotne wywołanie RPC (host_request)

Proces wtyczki nie ma interfejsu API Electron. Kiedy potrzebuje rzeczywistych możliwości systemu, wysyła host_request do hosta, który odpowiada host_response lub host_error. Kanał ten jest w pełni niezależny od call_tool; można go używać podczas obsługi wywołania narzędzia lub z poziomu licznika czasu.

capabilityparamsresultWarunek wstępny
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Wymaga pozwolenia notifications. Zwraca wyłączone zamiast błędu, gdy użytkownik wyłączył powiadomienia na pulpicie w ustawieniach. Kiedy viewId wskazuje jeden z zadokowanych widoków, kliknięcie powiadomienia przenosi aplikację na pierwszy plan i rozwija ten widok.
render_pdf{ outputPath, title, content }{}treść to Markdown; host renderuje plik PDF do OutputPath za pomocą wbudowanego modułu renderującego.
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)',
});

13Cykl życia

ScenaZachowanie
StartDomyślnie leniwy: nie uruchomiony z aplikacją, rozwidlony przy pierwszym wywołaniu jednego z narzędzi (AI, ikona paska narzędzi, panel, polecenie, analiza dokumentu). Wtyczki deklarujące activationEvents: ["onStartupFinished"] uruchamiają się zaraz po zakończeniu uruchamiania aplikacji; używaj tego tylko do wtyczek, które wymagają liczników czasu w tle (przypomnienia, odpytywanie), a nie do zwykłych wtyczek narzędziowych.
RezydentRaz uruchomiony proces pozostaje aktywny i jest ponownie wykorzystywany w wywołaniach narzędzi. Możesz buforować stan w pamięci, ale załóż, że można go zrestartować w dowolnym momencie.
Rozbić sięJeśli proces zakończy się nieoczekiwanie, host uruchomi go ponownie: natychmiast, a następnie po 5 i 15 sekundach. Po 3 kolejnych niepowodzeniach zostaje oznaczony jako „awaria” i nie jest już ponawiany; użytkownicy widzą status na stronie wtyczki. Wszystkie wywołania podczas lotu zwracają błędy.
Wyłącz / odinstaluj / zainstaluj ponownieProces zostaje zakończony, a zadokowane widoki i pływające panele zostają zniszczone; ponowna instalacja zastępuje cały folder.
DziennikiTwoje stdout/stderr są zapisywane w pliku dziennika aplikacji poprzedzonym identyfikatorem wtyczki. Do debugowania wystarczy zwykły console.log; zobacz „Debugowanie i często zadawane pytania”.
ENV
Proces potomny rozpoczyna się od ELECTRON_RUN_AS_NODE=1, cwd to folder instalacyjny wtyczki, a wersja Node odpowiada wersji Electron dołączonej do aplikacji. Nie polegaj na żadnym globalnie zainstalowanym pakiecie i nie zakładaj, że npx istnieje.

14Pakowanie i wydawnictwo

Opakowanie

Spakuj folder wtyczki z manifest.json w katalogu głównym archiwum. Jeśli masz zależności od stron trzecich, spakuj je w jeden plik za pomocą najpierw esbuild/webpack, zamiast wysyłać cały 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)

Publikowanie na rynku

AnBoWork → Narzędzia → Wtyczki → zakładka „Rynek” → „Opublikuj wtyczkę” w prawym górnym rogu, a następnie prześlij plik ZIP. Zgłoszenia podlegają przeglądowi, który sprawdza, czy deklaracja manifestu jest zgodna z kodem (szczególnie uprawnienia). Po zatwierdzeniu inni użytkownicy mogą znaleźć i zainstalować ją na rynku, a sztuczna inteligencja może automatycznie instalować zweryfikowane wtyczki dla użytkowników, gdy uzna je za potrzebne.

Aktualizacje

Podbij version i opublikuj ponownie. Aplikacja porównuje zainstalowaną wersję z wersją z marketu i wyświetla monit o aktualizację; ponowna instalacja powoduje zachowanie stanu włączenia i wartości ustawień użytkownika.

15Pełny przykład: Kwadrant zadań do wykonania

Prawdziwa, działająca wtyczka (przycięta): sztuczna inteligencja może dodawać i usuwać zadania, na prawym pasku bocznym wyświetla się tablica, która odświeża się w czasie rzeczywistym i może wysyłać przypomnienia o powiadomieniach systemowych. Łączenie tych trzech elementów obejmuje protokół narzędzia, wypychanie widoku zadokowanego i odwrotne wywołanie 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 }]
}

16Debugowanie i często zadawane pytania

ObjawPrzyczyna i naprawa
Podczas instalacji nie udaje się sprawdzić poprawności manifestuSprawdź każde pole w tabeli „Pola manifestu”, zgodnie z komunikatem: format identyfikatora, element główny/wpis nie może zawierać .., ikona musi znajdować się na liście dozwolonych, polecenie skrótów klawiszowych musi wskazywać istniejące narzędzie w poleceniach, szerokość między 240 a 640.
AI nigdy nie wywołuje mojego narzędziaPrawie zawsze problem description: wyzwalacz jest niejasny lub nie jest napisany w języku angielskim. Zobacz „Pisanie dobrego opisu”. Sprawdź także, czy nazwa narzędzia nie koliduje z nazwą innego narzędzia.
Limit czasu połączenia (30 s)Skrypt wejściowy nie odpowiedział tool_result. Zwykle requestId nie zostało powtórzone, komunikat type jest błędnie napisany lub skrypt rzucił się przy uruchomieniu i zakończył działanie. Poszukaj wierszy z przedrostkiem [plugin:your-id] w dzienniku aplikacji.
Stan wtyczki pokazuje „awaria”3 kolejne nieudane starty. Zwykle require brakującego pakietu; uruchomienie node index.js w folderze wtyczki odtwarza to. Po naprawieniu wyłącz i włącz wtyczkę na stronie wtyczki.
Panel otwiera się pustyZła ścieżka entry lub błąd JS na stronie. Strona działa w piaskownicy bez węzłów require; dostępne jest tylko window.pluginHost.
Powiadomienie nie pojawia sięSprawdź w kolejności: manifest deklaruje notifications; wynikiem nie jest shown:false (użytkownik wyłączył powiadomienia na pulpicie); system operacyjny umożliwia powiadomienia z AnBoWork.
Wartości ustawień nie są czytelneWartości znajdują się w message.context.config[id], a nie w params.

Lokalizacja pliku dziennika

system operacyjnyŚcieżka
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Zobacz dokumentację integracji API