// Panduan Pengembang

Dokumen Pengembang Plugin

Bangun plugin untuk aplikasi desktop AnBoWork: tambahkan alat baru untuk AI, letakkan panel Anda sendiri di samping sesi, kirim pemberitahuan sistem, parsing format file baru. Satu manifest.json ditambah satu skrip entri sudah cukup untuk memulai.

01Ringkasan

Plugin AnBoWork adalah program Node.js biasa. Setelah instalasi, aplikasi membaginya sebagai proses anak yang berdiri sendiri (Node murni, tanpa API Elektron) dan berkomunikasi dengannya melalui pesan antar-proses. Tidak diperlukan paket SDK; yang Anda butuhkan hanyalah process.on('message') dan process.send().

Segala sesuatu yang dapat dilakukan plugin dinyatakan secara statis di manifest.json: halaman persetujuan pra-instal dan tinjauan pasar dapat mengetahui alat mana yang ditambahkan dan izin apa yang diperlukan tanpa menjalankan kode plugin apa pun. Semua UI (ikon toolbar, pengaturan, palet perintah, menu konteks) dirender oleh host dari deklarasi; kode plugin tidak pernah berjalan di dalam jendela utama. Hanya tampilan berlabuh dan panel mengambang yang merupakan HTML Anda sendiri, dan keduanya berjalan di halaman kotak pasir yang terisolasi.

Sekilas tentang kemampuan

Bidang manifesKemampuanTempat pengguna melihatnya
toolsTambahkan alat yang dapat dipanggil untuk AIAI memanggil mereka secara otomatis dalam percakapan; panggilan alat ditampilkan dalam sesi tersebut
viewsTampilan persisten ditempatkan di sidebar kanan sesiIkon tambahan di toolbar sesi membuka halaman Anda di sebelah kanan
uiJendela mengambang yang berdiri sendiriDibuka dari menu "⋯" di daftar plugin
sessionActionIkon tindakan persisten di toolbar sesiSatu klik menjalankan alat yang Anda tentukan
configurationEntri di halaman pengaturanPengaturan → Konfigurasi plugin; nilai diteruskan dengan setiap panggilan
commandsEntri palet perintah Ctrl/Cmd+KPalet perintah
keybindingsPintasan keyboard globalKombinasi tombol memicu perintah
menusItem menu konteks sesiMenu "⋯" / klik kanan pada baris sesi
documentParsersParsing file dimasukkan ke dalam kotak inputDipanggil secara otomatis ketika pengguna menjatuhkan file yang cocok
activationEventsMulailah dengan aplikasiTak terlihat; untuk plugin yang memerlukan pengatur waktu latar belakang
permissionsNyatakan izin yang diperlukanHalaman persetujuan pra-instal

02Mulai Cepat

Plugin minimal hanya membutuhkan dua file.

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. Tulis manifes.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. Tulis skrip entri

// 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. Instal ke dalam aplikasi

Buka AnBoWork → bilah atas "Alat" → "Plugin" → "Instal secara lokal" di pojok kanan atas, lalu pilih folder plugin (atau zipnya). Halaman persetujuan mencantumkan izin yang dinyatakan dan setiap alat sebelum instalasi; konfirmasi untuk mengaktifkan.

4. Cobalah

Di sesi mana pun, beri tahu AI "minta plugin untuk menyapa Alice". Berdasarkan deskripsi alat, AI memutuskan untuk memanggil say_hello, dan panggilan serta hasilnya muncul di sesi.

TIP
AI di dalam AnBoWork juga dapat menghasilkan plugin untuk Anda (misalnya "tuliskan saya plugin yang berfungsi X" dan ia akan memanggil create_local_plugin). Plugin yang dihasilkan dinonaktifkan secara default hingga Anda meninjau kode di halaman plugin dan mengaktifkannya secara manual. Panduan ini berlaku untuk membaca dan memodifikasi plugin yang dihasilkan AI.

03Bidang Manifes

BidangJenisDiperlukanKeterangan
idstringYaGaya domain terbalik, mis. com.example.my-plugin. Hanya huruf, angka, titik, dan tanda hubung. Tidak dapat diubah setelah dibuat; menginstal ulang id yang sama dianggap sebagai instalasi ulang.
namestringYaNama plugin. Bersama dengan description ini dimasukkan ke model sebagai konteks, jadi bahasa Inggris direkomendasikan.
versionstringYaVersi semantik; pasar menggunakannya untuk mendeteksi pembaruan.
descriptionstringYaSatu kalimat yang menjelaskan fungsi plugin (Bahasa Inggris).
authorstringTIDAKPengarang.
readmestringTIDAKDeskripsi bentuk panjang yang ditampilkan di halaman detail pasar, mendukung Penurunan Harga. Gambar di dalamnya harus berupa https URL — jalur relatif ke file yang dibundel dalam zip tidak didukung.
iconstringTIDAKIkon ditampilkan di daftar pasar dan halaman detail — jalur relatif ke file yang dibundel dalam zip (misalnya icon.png). Harus PNG, bukan SVG, minimal 128×128.
mainstringYaSkrip entri, relatif terhadap folder plugin. .. keluar dari folder tidak diperbolehkan.
permissionsstring[]Yanetwork / filesystem / notifications; mungkin array kosong. Lihat "Model Izin".
toolsobject[]YaDeklarasi alat; mungkin array kosong. Lihat "Alat & Protokol Panggilan".
documentParsersobject[]TIDAKKemampuan penguraian file.
uiobjectTIDAKPanel mengambang:{ entry, title }
viewsobject[]TIDAKTampilan berlabuh:{ id, title, icon, entry, width? }
sessionActionobjectTIDAKIkon toolbar, paling banyak satu per plugin.
configurationobject[]TIDAKDaftar entri pengaturan.
commandsobject[]TIDAKEntri palet perintah.
keybindingsobject[]TIDAKJalan pintas. command harus mengacu pada tool entri di commands[], jika tidak, pemasangan akan ditolak.
menusobjectTIDAKHanya lokasi session/context yang tersedia untuk saat ini.
activationEventsstring[]TIDAKHanya onStartupFinished untuk saat ini.

Konvensi bahasa untuk teks

Teks dalam manifes memiliki dua audiens. Untuk model: name, description, tools[].description, tools[].parameters.*.description. Tulis ini dalam bahasa Inggris; model memahami deskripsi alat bahasa Inggris dan parameter pengisian dengan lebih andal. Untuk orang: tools[].summary (ringkasan satu baris di halaman persetujuan), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title dan title di dalam menus. Tulis ini dalam bahasa pengguna target Anda.

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

permissions harus secara jujur ​​mencerminkan fungsi sebenarnya dari kode tersebut. Halaman persetujuan pra-pemasangan mencantumkan masing-masing sehingga pengguna dapat memutuskan apakah akan menginstal; tinjauan pasar juga memeriksa deklarasi terhadap kode.

IzinKapan harus diumumkanBagaimana tuan rumah memperlakukannya
filesystemMembaca atau menulis file pengguna (membaca kode proyek, membuat dokumen, menyimpan data di folder beranda)Persetujuan yang diinformasikan, ditunjukkan kepada pengguna.
networkMembuat permintaan jaringan apa pun (API pihak ketiga, unduhan)Persetujuan yang diinformasikan, ditunjukkan kepada pengguna.
notificationsMenampilkan notifikasi sistem melalui RPC terbalik show_notificationEnforced: memanggilnya tanpa deklarasi akan mengembalikan host_error dan tidak ada pemberitahuan yang ditampilkan.
CATATAN
Proses anak plugin adalah lingkungan Node.js lengkap. filesystem dan network saat ini merupakan deklarasi tingkat persetujuan, bukan runtime sandbox: host tidak mencegat fs atau fetch. Itulah sebabnya deklarasi harus jujur. Plugin yang kurang dideklarasikan akan ditolak dalam tinjauan pasar dan kehilangan kepercayaan pengguna saat ditemukan. Jangan mendeklarasikannya secara berlebihan (izin yang tidak digunakan membuat pengguna ragu).

Privasi dan data

context.cwd adalah folder proyek pengguna dan mungkin berisi kode sumber dan konfigurasi sensitif; argumen alat mungkin berisi percakapan pengguna. Jangan mengirim data ini melalui jaringan kecuali fitur tersebut memerlukannya, dan bila Anda melakukannya, sebutkan di description dan summary apa yang dikirim dan kepada siapa. Simpan data persisten plugin dalam file di bawah folder beranda pengguna yang diberi nama sesuai id plugin, sehingga mudah ditemukan saat menguninstall.

05Alat & Protokol Panggilan

Pernyataan

BidangKeterangan
nameNama alat dalam huruf kecil dan garis bawah, unik di seluruh aplikasi (terdaftar dengan model bersama alat bawaan dan alat plugin lainnya).
descriptionDeskripsi model (Bahasa Inggris). Nyatakan tiga hal: apa yang dilakukan alat tersebut, apa yang dikatakan pengguna kapan harus dipanggil, dan kapan tidak boleh dipanggil. Ini adalah satu-satunya bidang terpenting yang menentukan apakah AI menggunakan plugin Anda.
summaryRingkasan satu baris yang dapat dibaca manusia ditampilkan di halaman persetujuan. Opsional tetapi sangat disarankan; jika tidak, deskripsi terpotong akan ditampilkan.
parametersObjek Skema JSON standar dengan description di setiap properti. Model mengisi argumen darinya dan host meneruskannya tanpa validasi tambahan, jadi periksa bidang yang wajib diisi dalam kode Anda.
renderAsOpsional; saat ini hanya checklist: host merender array todos dari argumen panggilan sebagai kartu daftar periksa, lihat di bawah.
Catatan
Model hanya dapat memanggil nama alat yang dideklarasikan dalam tools[] di sini — sessionAction.tool, commands[].tool, dan bidang tool di dalam menus hanya diperiksa sebagai string yang tidak kosong pada waktu pemasangan; host tidak memverifikasi nama yang dideklarasikan di tools[], dan masih mengirimkan panggilan dengan nama tersebut ketika tombol, pintasan, atau item menu dipicu. Jadi tindakan yang murni dipicu oleh UI dapat ditangani sepenuhnya di index.js Anda sendiri tanpa pernah muncul di tools[] — tetapi AI tidak akan pernah bisa melihat atau memanggilnya. Jika Anda ingin tindakan yang sama dapat dipicu dengan mengklik dan oleh AI yang bertindak atas permintaan obrolan, nyatakan juga nama yang sama di tools[], sehingga kedua titik masuk berbagi satu penangan; jika tujuannya hanya untuk UI saja, biarkan tidak dideklarasikan — tidak perlu menambahkan entri hanya agar terlihat lengkap.

Protokol pesan

Tuan rumah mengirimkan call_tool; plugin harus membalas dengan tool_result atau tool_error dengan requestId yang sama. Panggilan tanpa balasan dalam waktu 30 detik akan habis dan model menerima kesalahan.

// 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 hanya menerima blok text dan image (gambar sebagai base64 data plus mimeType); tipe lain dihilangkan. details adalah data terstruktur arbitrer yang tidak pernah dimasukkan ke model; ini untuk rendering UI host dan tampilan dok Anda sendiri.

Hasil file: kartu file

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

Rendering daftar periksa: daftar periksa 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" }
  ]
}

Menulis deskripsi yang bagus

Jelaskan pemicu dalam perintah ("Panggil ini ketika pengguna meminta untuk...") dan nyatakan pengecualian ("Jangan panggil ini untuk..."). Jika hasilnya berupa file, mintalah model untuk mereferensikan link file yang dikembalikan daripada mengklaim bahwa file tersebut telah dibuat. Jika argumen diteruskan ke API generasi eksternal, sebutkan bahasa apa yang seharusnya digunakan. Hindari satu kalimat seperti "kelola tugas"; model tidak akan tahu kapan harus menggunakan alat tersebut.

06Tampilan berlabuh (tampilan)

Tampilan berlabuh adalah halaman tetap di sidebar kanan sesi: ikon tambahan muncul di toolbar sesi dan HTML Anda ditampilkan di sebelah kanan saat dibuka. Status halaman (posisi gulir, draf) bertahan dalam sesi peralihan dan diciutkan/diperluas, semantik yang sama seperti tampilan sidebar VS Code.

BidangKeterangan
idUnik dalam plugin.
titleTeks keterangan alat untuk ikon (dapat dibaca manusia).
iconPilih dari daftar host yang diizinkan: git-branch, checklist. Menjaga konsistensi ikon di seluruh aplikasi; gambar khusus tidak diterima.
entryFile HTML, relatif terhadap folder plugin.
widthLebar panel default opsional dalam piksel, 240 hingga 640, default 360. Pengguna dapat menyeret untuk mengubah ukuran dan ukurannya diingat.

Waktu proses halaman

Halaman berjalan di kotak pasir (sandbox + contextIsolation, tanpa Node, tidak ada akses ke data aplikasi host) dan hanya dapat berkomunikasi dengan proses pluginnya sendiri melalui beberapa metode di window.pluginHost. Bawalah gaya Anda sendiri; disarankan untuk mengikuti skema terang/gelap aplikasi seperti pada contoh di bawah ini.

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

Pola yang direkomendasikan: pada pemuatan pertama, panggil salah satu alat kueri Anda dengan invoke() dan minta alat tersebut mengembalikan data terstruktur di details; setelah itu dorong setiap perubahan dari proses plugin dengan ui_push alih-alih melakukan polling. Saat AI mengubah data dalam percakapan, panel segera disegarkan.

07Panel mengambang (ui)

ui: { entry, title } mendeklarasikan jendela mengambang mandiri (900×700) yang dibuka pengguna dari menu "⋯" di daftar plugin; itu hancur dalam waktu dekat. Lingkungan halaman dan window.pluginHost API identik dengan tampilan yang digandeng, dan ui_push dikirimkan ke panel mengambang dan tampilan yang digandeng. Cocok untuk panel besar yang dibuka sesekali; gunakan views untuk konten yang harus tetap ada di samping sesi.

08Ikon toolbar sesi (sessionAction)

Maksimal satu ikon persisten per plugin. Mengkliknya akan memanggil tool dengan argumen kosong, sehingga alat tersebut harus melakukan sesuatu yang berarti tanpa parameter.

BidangKeterangan
toolAlat untuk menelepon saat diklik.
iconNama ikon yang diizinkan.
tooltipArahkan teks.
visibleToolOpsional. Jika disetel, ikon akan dipanggil dengan argumen kosong sebelum dirender dan boolean details.visible pada hasilnya menentukan apakah ikon akan ditampilkan. Berguna untuk pemeriksaan runtime seperti "apakah folder saat ini merupakan repositori 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 },
});

09Pengaturan (konfigurasi)

Entri yang dideklarasikan muncul di bawah konfigurasi plugin pada halaman pengaturan. Host menyimpan nilai yang diubah oleh pengguna dan meneruskannya sebagai context.config pada setiap call_tool (dikunci oleh id, kembali ke defaultValue), baik panggilan tersebut berasal dari AI, ikon toolbar, atau panel Anda. Anda tidak perlu memintanya.

typeDiterjemahkan sebagaitipe nilai default
selectDaftar pilihan tunggal; opsi berasal dari optionsToolstring
booleanSakelar sakelarboolean
stringInput teks, dilakukan pada blurstring
numberMasukan angkanumber
// 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.

10Palet perintah/pintasan/menu konteks

Ketiganya merupakan titik masuk yang berbeda untuk "memanggil alat dengan argumen kosong"; sebuah plugin dapat mendeklarasikan beberapa.

BidangMembentukKeterangan
commands{ tool, icon, title }[]Ditampilkan di palet perintah Ctrl/Cmd+K dan dipicu setelah pengguna mencarinya.
keybindings{ command, key }[]command harus sama dengan tool entri di commands[]. key menggunakan notasi mod+shift+g, dengan mod adalah Cmd di macOS dan Ctrl di tempat lain. Konflik dengan pintasan lain tidak diperiksa secara statis, jadi pilihlah kombinasi yang tidak umum.
menus{ "session/context": { tool, icon, title }[] }Ditambahkan ke menu pada baris sesi. Hanya lokasi ini yang tersedia untuk saat ini.

11Pengurai dokumen (documentParsers)

Saat pengguna memasukkan file ke dalam kotak input, host mencari parser yang dideklarasikan oleh plugin yang diaktifkan berdasarkan tipe MIME dan meneruskan konten file sebagai base64 ke toolName yang cocok. Ini bukan alat yang dipanggil oleh AI, jadi jangan not cantumkan juga alat parser di bawah tools, atau alat tersebut akan ditampilkan sebagai alat yang dapat dipanggil oleh AI.

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

12Membalikkan RPC (host_request)

Proses plugin tidak memiliki Electron API. Ketika memerlukan kemampuan sistem nyata, ia mengirimkan host_request ke host, yang membalas dengan host_response atau host_error. Saluran ini sepenuhnya independen dari call_tool; Anda dapat menggunakannya saat menangani panggilan alat atau dari pengatur waktu.

capabilityparamsresultPrasyarat
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Membutuhkan izin notifications. Pengembalian dinonaktifkan alih-alih kesalahan saat pengguna mematikan notifikasi desktop di pengaturan. Saat viewId menunjuk ke salah satu tampilan yang digandeng, mengeklik notifikasi akan membawa aplikasi ke depan dan memperluas tampilan tersebut.
render_pdf{ outputPath, title, content }{}konten adalah penurunan harga; tuan rumah merender PDF ke outputPath dengan penyaji bawaannya.
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)',
});

13Siklus hidup

PanggungPerilaku
AwalMalas secara default: tidak dimulai dengan aplikasi, bercabang pada panggilan pertama ke salah satu alat Anda (AI, ikon toolbar, panel, perintah, penguraian dokumen). Plugin yang menyatakan activationEvents: ["onStartupFinished"] dimulai tepat setelah aplikasi selesai diluncurkan; gunakan ini hanya untuk plugin yang memerlukan pengatur waktu latar belakang (pengingat, polling), bukan untuk plugin alat biasa.
PendudukSetelah dimulai, prosesnya tetap berjalan dan digunakan kembali di seluruh panggilan alat. Anda dapat menyimpan status dalam cache di memori, tetapi menganggapnya dapat dimulai ulang kapan saja.
MenabrakJika proses keluar secara tiba-tiba, host akan memulai ulang: segera, kemudian setelah 5 dan 15 detik. Setelah 3 kegagalan berturut-turut, ini ditandai "gagal" dan tidak lagi dicoba ulang; pengguna melihat status di halaman plugin. Panggilan dalam penerbangan semua kesalahan pengembalian.
Nonaktifkan / uninstall / instal ulangProses dihentikan dan tampilan yang digandeng serta panel mengambang dihancurkan; instalasi ulang menggantikan seluruh folder.
Logstdout/stderr Anda ditulis ke file log aplikasi yang diawali dengan id plugin. Biasa console.log sudah cukup untuk debugging; lihat "Debug & FAQ".
ENV
Proses anak dimulai dengan ELECTRON_RUN_AS_NODE=1, cwd adalah folder pemasangan plugin, dan versi Node cocok dengan Electron yang dibundel dengan aplikasi. Jangan bergantung pada paket apa pun yang diinstal secara global dan jangan berasumsi npx ada.

14Pengemasan & Penerbitan

Kemasan

Zip folder plugin dengan manifest.json di root arsip. Jika Anda memiliki dependensi pihak ketiga, gabungkan dependensi tersebut ke dalam satu file dengan esbuild/webpack terlebih dahulu alih-alih mengirimkan keseluruhan 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)

Penerbitan ke pasar

AnBoWork → Alat → Plugin → tab "Marketplace" → "Publikasikan plugin" di pojok kanan atas, lalu unggah zipnya. Kiriman masuk ke tinjauan, yang memeriksa apakah deklarasi manifes cocok dengan kode (terutama izin). Setelah disetujui, pengguna lain dapat menemukan dan menginstalnya di pasar, dan AI dapat menginstal plugin terverifikasi untuk pengguna secara otomatis saat dianggap diperlukan.

Pembaruan

Bump version dan publikasikan lagi. Aplikasi ini membandingkan versi yang diinstal dengan versi pasar dan meminta untuk memperbarui; menginstal ulang mempertahankan status aktif dan nilai pengaturan pengguna.

15Contoh lengkap: Todo Quadrant

Plugin yang nyata dan berfungsi (dipangkas): AI dapat menambah dan menghapus tugas, sidebar kanan menampilkan papan yang disegarkan secara real time, dan dapat mengirimkan pengingat notifikasi sistem. Merangkai ketiganya mencakup protokol alat, dorongan tampilan dok, dan RPC terbalik.

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

16Proses Debug & FAQ

GejalaPenyebab dan perbaikan
Validasi manifes gagal saat instalasiPeriksa setiap bidang terhadap tabel "Bidang Manifes" seperti yang ditunjukkan oleh pesan: format id, utama/entri tidak boleh berisi .., ikon harus diizinkan, perintah pengikatan kunci harus menunjuk ke alat yang ada dalam perintah, lebar antara 240 dan 640.
AI tidak pernah memanggil alat sayaHampir selalu ada masalah description: pemicunya tidak jelas atau tidak ditulis dalam bahasa Inggris. Lihat "Menulis deskripsi yang baik". Periksa juga apakah nama alat tidak bertabrakan dengan alat lain.
Waktu panggilan habis (30 detik)Skrip entri tidak membalas dengan tool_result. Biasanya requestId tidak di-echo kembali, pesan type salah eja, atau skrip terlempar saat startup dan keluar. Cari baris yang diawali dengan [plugin:your-id] di log aplikasi.
Status plugin menunjukkan "rusak"3 start gagal berturut-turut. Biasanya require dari paket yang hilang; menjalankan node index.js di folder plugin akan mereproduksinya. Setelah diperbaiki, matikan dan hidupkan lagi plugin di halaman plugin.
Panel terbuka kosongJalur entry salah, atau kesalahan JS pada halaman. Halaman berjalan di kotak pasir tanpa require Node; hanya window.pluginHost yang tersedia.
Notifikasi tidak munculPeriksa secara berurutan: manifes menyatakan notifications; hasilnya bukan shown:false (pengguna mematikan notifikasi desktop); OS mengizinkan notifikasi dari AnBoWork.
Nilai pengaturan tidak dapat dibacaNilai ada di message.context.config[id], bukan di params.

Lokasi berkas log

sistem operasiJalur
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Lihat dokumen integrasi API