開發者指南

插件開發文件

為 AnBoWork 桌面客戶端開發插件:幫 AI 增加新工具、喺會話旁邊掛一塊自己嘅面板、發系統通知、解析新嘅檔案格式。一個 manifest.json 加一個入口腳本就可以跑起來。

01概覽

AnBoWork 插件係一個普通嘅 Node.js 程式。安裝後,客戶端會將佢 fork 成一個獨立嘅子進程運行(純 Node 環境,冇 Electron API),透過進程間訊息同宿主通訊。插件唔需要依賴任何 SDK 套件,只要識 process.on('message') 同 process.send()。

插件「有邊啲能力」全部寫喺 manifest.json 入面,係靜態聲明:安裝前嘅知情同意頁、市場審核,都唔需要運行插件代碼就知道佢會裝邊啲工具、要咩權限。所有 UI(頂欄圖示、設定項、命令面板、右鍵選單)都由宿主按聲明統一渲染,插件代碼唔會走入主視窗。只有停靠視圖同浮動面板係插件自己嘅 HTML,都運行喺獨立沙箱頁面入面。

能力一覽

manifest 欄位能力用戶喺邊度見到
tools幫 AI 增加可調用嘅工具AI 喺對話中自動調用;工具調用記錄顯示喺會話入面
views停靠喺會話右側欄嘅常駐視圖會話頂欄多一個圖示,撳開喺右側展示你嘅頁面
ui獨立浮動視窗插件列表嘅「⋯」選單入面打開
sessionAction會話頂欄常駐操作圖示撳一下調用你指定嘅工具
configuration設定頁嘅配置項設定 → 插件配置,值隨每次調用傳畀你
commandsCtrl/Cmd+K 命令面板條目命令面板
keybindings全域快捷鍵按鍵觸發某條命令
menus會話右鍵選單項會話列表行嘅「⋯」/右鍵選單
documentParsers解析拖入輸入框嘅檔案用戶拖入對應格式檔案時自動調用
activationEvents隨應用啟動拉起無感知;用於需要背景計時器嘅插件
permissions聲明需要嘅權限安裝前嘅知情同意頁

02快速開始

一個最小插件只需要兩個檔案。

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. 寫 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. 寫入口腳本

// 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. 安裝到客戶端

打開 AnBoWork → 頂部「工具」→「插件」→ 右上角「本地安裝」,揀插件目錄(或者打成 zip 後揀 zip 檔)。安裝前會彈出知情同意頁,列出呢個插件聲明嘅權限同全部工具,確認後啟用。

4. 試一下

喺任意會話入面同 AI 講「叫插件同小明打個招呼」,AI 會根據工具描述決定調用 say_hello,會話入面會顯示呢次調用同返回結果。

提示
AnBoWork 入面嘅 AI 本身都可以替你生成插件(同佢講「幫我寫一個 xxx 插件」,佢會調用 create_local_plugin),生成嘅插件預設停用,需要你喺插件頁睇過代碼後手動啟用。呢份文件同樣適用於閱讀同修改 AI 生成嘅插件。

03manifest 欄位

欄位類型必填說明
idstring是反向域名風格,例如 com.example.my-plugin。只允許字母、數字、點、連字號。建立後不可改,安裝同 id 視為覆蓋重裝。
namestring是插件名。呢個欄位同 description 會作為上下文餵畀模型,建議用英文。
versionstring是語義化版本,市場按佢判斷更新。
descriptionstring是一句話說明插件做咩(英文)。
authorstring否作者。
readmestring否市場詳情頁展示嘅長文說明,支援 Markdown。圖片淨係可以用 https 連結,唔支援引用 zip 入面嘅相對路徑圖片。
iconstring否市場列表/詳情頁展示嘅圖示,填 zip 入面一個相對路徑(例如 icon.png)。必須係 PNG,唔支援 SVG,尺寸至少 128×128。
mainstring是入口腳本,相對插件目錄嘅路徑,唔允許 .. 跳出目錄。
permissionsstring[]是network / filesystem / notifications,可為空陣列。見「權限模型」。
toolsobject[]是工具聲明,可為空陣列。見「工具與調用協議」。
documentParsersobject[]否檔案解析能力。
uiobject否浮動面板:{ entry, title }
viewsobject[]否停靠視圖:{ id, title, icon, entry, width? }
sessionActionobject否頂欄圖示,每個插件最多一個。
configurationobject[]否設定項列表。
commandsobject[]否命令面板條目。
keybindingsobject[]否快捷鍵,command 必須引用 commands[] 入面某條嘅 tool,否則安裝被拒絕。
menusobject否目前只開放 session/context 一個位置。
activationEventsstring[]否目前只有 onStartupFinished。

文案語言約定

manifest 入面嘅文字分兩類受眾。畀模型睇嘅:name、description、tools[].description、tools[].parameters.*.description,請用英文,模型對英文工具描述嘅理解同參數填寫更穩定。畀人睇嘅:tools[].summary(知情同意頁嘅一句話摘要)、ui.title、views[].title、sessionAction.tooltip、configuration[].label、commands[].title、menus 入面嘅 title,用你目標用戶嘅語言。

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

04權限模型

permissions 必須如實反映代碼實際做嘅嘢。安裝前嘅知情同意頁會逐條展示,用戶據此決定裝唔裝;市場審核都會對照代碼檢查聲明係咪屬實。

權限幾時必須聲明宿主點樣對待
filesystem讀寫用戶檔案(讀項目代碼、生成文件、喺用戶目錄存資料)知情同意,向用戶展示。
network發起任何網絡請求(調第三方 API、下載資源)知情同意,向用戶展示。
notifications透過反向 RPC show_notification 彈系統通知硬校驗:冇聲明就調用,宿主直接返回 host_error,通知唔會彈出。
注意
插件子進程係完整嘅 Node.js 環境,filesystem 同 network 目前係知情同意層面嘅聲明,唔係運行時沙箱:宿主唔會攔截 fs 或 fetch。呢個正正係點解聲明必須誠實——漏報權限嘅插件喺市場審核時會被拒絕,被用戶發現後會失去信任。唔好多報(用唔到嘅權限會令用戶猶豫),都唔好少報。

私隱與資料

context.cwd 係用戶嘅項目目錄,入面可能有源碼同敏感配置;工具參數入面可能有用戶嘅對話內容。除非功能必需,唔好將呢啲資料發到網絡;需要發送時喺 description 同 summary 入面講清楚發畀邊個、發咩。插件自己嘅持久化資料建議放喺用戶主目錄下一個以插件 id 命名嘅檔案入面,卸載時用戶一眼搵到。

05工具與調用協議

聲明

欄位說明
name工具名,細楷字母加底線,喺整個客戶端入面要唯一(會同內置工具、其它插件嘅工具一齊註冊畀模型)。
description畀模型嘅說明(英文)。寫清楚三件事:呢個工具做咩、用戶講咩話時應該調用、咩情況下唔好調用。呢個係決定 AI 會唔會用你插件嘅最關鍵欄位。
summary畀人睇嘅一句話摘要,知情同意頁展示。可選但強烈建議提供,否則會退回展示裁剪過嘅 description。
parameters標準 JSON Schema 物件,每個屬性都寫 description。模型按佢填參數,宿主原樣傳畀你,唔做額外校驗,代碼入面要自己檢查必填項。
renderAs可選,目前只有 checklist:宿主將呢次調用參數入面嘅 todos 陣列渲染成清單卡片,見下方。
注意
模型能夠調用嘅工具淨係嚟自呢度聲明嘅 tools[]——sessionAction.tool、commands[].tool、menus 入面嘅 tool 字段淨係喺安裝時校驗係咪非空字串,唔會校驗呢個名有冇喺 tools[] 度聲明過,撳/觸發嗰陣宿主照樣用呢個名發起調用。所以一個純 UI 觸發嘅按鈕可以淨係喺你自己嘅 index.js 度實現處理邏輯、完全唔出現喺 tools[] 度——但係咁樣 AI 永遠睇唔到都調用唔到佢。想要同一個功能既可以撳嚟觸發、又可以俾 AI 喺對話入面直接調用,就將呢個名都聲明入 tools[],兩條入口共用同一份處理邏輯;如果肯定淨係要 UI 觸發、唔想 AI 插手,唔聲明就得,唔使為咗“睇落齊全”而勉強加一條。

訊息協議

宿主發 call_tool,插件必須用同一個 requestId 回一條 tool_result 或 tool_error。單次調用 30 秒內冇回應會被判為超時,模型收到錯誤。

// 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 只認 text 同 image 兩種區塊(image 用 base64 嘅 data 加 mimeType),其它類型會被丟棄。details 係任意結構化資料,唔會餵畀模型,用於宿主 UI 渲染同你自己嘅停靠視圖。

產物係檔案:檔案卡片

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

清單渲染:renderAs checklist

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

寫好 description 嘅要點

用祈使句描述觸發條件(「Call this when the user asks to…」),明確排除項(「Do not call this for…」)。如果產物係檔案,要求模型喺回覆入面引用返回嘅檔案連結、唔好憑空聲稱已生成。如果參數會被轉發畀外部生成類 API,說明參數要用咩語言。避免只寫「manage todos」呢類一句話,模型會唔知幾時該用。

06停靠視圖 views

停靠視圖係一張掛喺會話右側欄嘅常駐頁面:會話頂欄多出一個圖示,撳開後你嘅 HTML 顯示喺右側,切換會話、收起再展開都唔會丟失頁面狀態(滾動位置、草稿),同 VS Code 側欄視圖同一個語義。

欄位說明
id插件內唯一。
title圖示嘅提示文字(人類可讀)。
icon從宿主白名單入面揀:git-branch、checklist。保持全應用圖示風格統一,唔接受自帶圖片。
entryHTML 檔案,相對插件目錄。
width可選,面板預設闊度(像素),240 到 640,預設 360。用戶之後可以拖動調整並被記住。

頁面運行環境

頁面喺沙箱入面運行(sandbox + contextIsolation,冇 Node、冇宿主應用嘅任何資料),只能透過 window.pluginHost 呢幾個方法同自己嘅插件進程通訊。樣式自己寫,建議按下面示例跟隨應用嘅亮/暗色。

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

推薦模式:頁面首次載入用 invoke() 調自己嘅一個查詢工具,並讓工具喺 details 入面返回結構化資料;之後每次資料變化由插件進程 ui_push 推送,頁面唔輪詢。AI 喺對話入面改咗資料,面板會即時刷新。

07浮動面板 ui

ui: { entry, title } 聲明一個獨立嘅浮動視窗(900×700),用戶喺插件列表嘅「⋯」選單入面打開,閂咗即銷毀。頁面環境同 window.pluginHost API 同停靠視圖完全一樣,ui_push 都會同時送到浮動面板同停靠視圖。適合「偶爾打開睇一眼」嘅大面板;需要常駐喺會話旁邊嘅內容用 views。

08會話頂欄圖示 sessionAction

每個插件最多一個常駐圖示,撳嘅時候用空參數調用 tool,所以嗰個工具必須喺冇參數時都能有意義咁執行。

欄位說明
tool撳一下調用嘅工具名。
icon白名單圖示名。
tooltip懸停提示。
visibleTool可選。畀咗就喺渲染前先用空參數調用佢,按返回嘅 details.visible(布林)決定顯唔顯示圖示。適合「當前目錄係咪 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 },
});

09設定項 configuration

聲明後出現喺設定頁嘅插件配置入面。用戶改嘅值由宿主保存,每次 call_tool 都會以 context.config 傳畀你(鍵係 id,冇改過嘅用 defaultValue),唔理呢次調用來自 AI、頂欄圖示定係你嘅面板,你唔需要自己去問。

type渲染成defaultValue 類型
select單選列表,候選項來自 optionsToolstring
boolean開關boolean
string文字輸入框,失焦提交string
number數字輸入框number
// 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.

10命令面板 / 快捷鍵 / 右鍵選單

三者都係「用空參數調用某個工具」嘅唔同入口,一個插件可以聲明多條。

欄位形狀說明
commands{ tool, icon, title }[]出現喺 Ctrl/Cmd+K 命令面板,用戶搜尋後觸發。
keybindings{ command, key }[]command 必須等於 commands[] 入面某條嘅 tool。key 用 mod+shift+g 呢種寫法,mod 喺 macOS 係 Cmd、其它平台係 Ctrl。同其它快捷鍵衝突唔做靜態檢查,揀唔常用嘅組合。
menus{ "session/context": { tool, icon, title }[] }掛入會話列表行嘅選單。目前只開放呢一個位置。

11文件解析 documentParsers

用戶將檔案拖入輸入框時,宿主按 MIME 類型搵已啟用插件入面聲明嘅解析器,將檔案內容 base64 傳畀對應嘅 toolName。呢個唔係 AI 決定調用嘅工具,所以唔好將解析工具都寫入 tools,否則會被當成 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 },
  });
});

12反向 RPC host_request

插件進程冇 Electron API,需要「真實系統能力」時向宿主發 host_request,宿主回 host_response 或 host_error。呢條通道同 call_tool 完全獨立,可以喺處理一次工具調用嘅過程中發起,都可以喺計時器入面發起。

capabilityparamsresult前提
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }聲明 notifications 權限;用戶喺設定入面關閉桌面通知時返回 disabled 而唔係報錯。viewId 指向自己嘅停靠視圖時,撳通知會將應用拉到前台並展開該視圖。
render_pdf{ outputPath, title, content }{}content 係 Markdown,宿主用內置渲染器生成 PDF 到 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)',
});

13生命週期

階段行為
啟動預設懶啟動:應用啟動時唔拉起,第一次有人調用你嘅工具(AI、頂欄圖示、面板、命令、文件解析)時先至 fork。聲明 activationEvents: ["onStartupFinished"] 嘅插件喺應用啟動完成後即刻拉起,畀需要背景計時器(到點提醒、輪詢)嘅插件用;普通工具型插件唔好聲明。
常駐進程啟動後一直存活,跨多次工具調用復用,可以喺記憶體入面快取狀態,但要假設隨時可能被重啟。
崩潰進程意外退出時宿主自動重啟:即刻一次,再等 5 秒、15 秒,連續 3 次失敗後標記為「崩潰」唔再重試,用戶喺插件頁見到狀態。進行中嘅調用全部以錯誤返回。
停用 / 卸載 / 重裝進程被終止,停靠視圖同浮動面板銷毀;重裝係整個目錄推倒重來。
日誌你嘅 stdout/stderr 會帶住插件 id 前綴寫入應用日誌檔。用 console.log 除錯即可,見「除錯與常見問題」。
環境
子進程以 ELECTRON_RUN_AS_NODE=1 啟動,cwd 係插件安裝目錄,Node 版本同客戶端內置嘅 Electron 一致。唔好依賴全域安裝嘅任何套件,都唔好假設有 npx。

14打包與發佈

打包

將插件目錄打成 zip,manifest.json 必須喺壓縮檔根目錄。有第三方依賴時建議先用 esbuild/webpack 打成單一檔案,唔好將整個 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)

發佈到市場

AnBoWork → 工具 → 插件 → 「市場」標籤 → 右上角「發佈插件」,揀 zip 上傳。提交後進入待審核狀態,審核會核對 manifest 聲明同代碼係咪一致(尤其係權限)。通過後其他用戶可以喺市場入面搜尋安裝,AI 都可以喺判斷需要時自動為用戶安裝已認證嘅插件。

更新

改 version 後重新發佈。客戶端按已安裝版本同市場版本比對提示更新;重裝會保留用戶嘅啟用狀態同設定項嘅值。

15完整示例:四象限待辦

一個真實可用嘅插件(精簡版):AI 可以增刪待辦,右側欄有即時刷新嘅看板,可以彈系統通知提醒。將三個能力串起來啱啱好覆蓋咗工具協議、停靠視圖推送同反向 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 }]
}

16除錯與常見問題

現象原因與解法
安裝時提示 manifest 校驗失敗按提示逐欄位對照「manifest 欄位」表:id 格式、main/entry 唔可以帶 ..、icon 必須喺白名單、keybindings 嘅 command 必須指向 commands 入面存在嘅 tool、width 喺 240 到 640 之間。
AI 從來唔調用我嘅工具幾乎都係 description 嘅問題:冇寫清楚觸發條件,或者用咗非英文。參考「寫好 description 嘅要點」。都要檢查工具名係咪同其他工具重名。
調用超時(30 秒)入口腳本冇回 tool_result,通常係 requestId 冇原樣帶返、訊息 type 串錯,或者腳本啟動時就拋異常退出咗。睇應用日誌入面帶 [plugin:你嘅id] 前綴嘅行。
插件狀態顯示「崩潰」連續 3 次啟動失敗。常見於 require 咗唔存在嘅套件;喺插件目錄入面直接 node index.js 可以重現。整好後喺插件頁閂咗再打開即可。
面板打開係空白entry 路徑唔啱,或者頁面 JS 報錯。頁面運行喺沙箱入面,冇 Node 嘅 require,只能用 window.pluginHost。
通知冇彈出依次檢查:manifest 聲明咗 notifications;返回值唔係 shown:false(用戶閂咗桌面通知);系統設定入面允許咗 AnBoWork 嘅通知。
設定項嘅值讀唔到值喺 message.context.config[id],唔喺 params 入面。

日誌檔位置

系統路徑
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
睇下 API 接入文件