// Hướng dẫn dành cho nhà phát triển

Tài liệu dành cho nhà phát triển plugin

Xây dựng plugin cho ứng dụng máy tính để bàn AnBoWork: thêm công cụ mới cho AI, gắn bảng điều khiển của riêng bạn bên cạnh phiên, gửi thông báo hệ thống, phân tích cú pháp các định dạng tệp mới. Một tệp kê khai.json cộng với một tập lệnh nhập là đủ để bắt đầu.

01Tổng quan

Plugin AnBoWork là một chương trình Node.js đơn giản. Sau khi cài đặt, ứng dụng sẽ phân tách nó thành một tiến trình con độc lập (Node thuần túy, không có API Electron) và trao đổi với nó qua các thông báo giữa các tiến trình. Không cần gói SDK; tất cả những gì bạn cần là process.on('message') và process.send().

Mọi thứ mà plugin có thể thực hiện đều được khai báo tĩnh trong manifest.json: trang chấp thuận cài đặt sẵn và đánh giá thị trường có thể cho biết plugin nào bổ sung và quyền nào cần mà không cần chạy bất kỳ mã plugin nào. Tất cả giao diện người dùng (biểu tượng thanh công cụ, cài đặt, bảng lệnh, menu ngữ cảnh) được máy chủ hiển thị từ phần khai báo; mã plugin không bao giờ chạy bên trong cửa sổ chính. Chỉ các chế độ xem được gắn cố định và bảng điều khiển nổi mới là HTML của riêng bạn và chúng chạy trong các trang có hộp cát riêng biệt.

Khả năng trong nháy mắt

Trường kê khaiKhả năngNơi người dùng nhìn thấy nó
toolsThêm các công cụ có thể gọi được cho AIAI tự động gọi họ trong cuộc trò chuyện; cuộc gọi công cụ được hiển thị trong phiên
viewsChế độ xem liên tục được gắn vào thanh bên phải của phiênMột biểu tượng bổ sung trên thanh công cụ phiên sẽ mở trang của bạn ở bên phải
uiCửa sổ nổi độc lậpĐã mở từ menu "⋯" trong danh sách plugin
sessionActionBiểu tượng hành động liên tục trên thanh công cụ phiênMột cú nhấp chuột sẽ chạy công cụ bạn chỉ định
configurationCác mục trên trang cài đặtCài đặt → Cấu hình plugin; các giá trị được chuyển với mỗi cuộc gọi
commandsCác mục trong bảng lệnh Ctrl/Cmd+KBảng lệnh
keybindingsPhím tắt chungMột tổ hợp phím sẽ kích hoạt một lệnh
menusCác mục menu ngữ cảnh của phiênMenu "⋯" / nhấp chuột phải vào hàng phiên
documentParsersPhân tích các tập tin được thả vào hộp đầu vàoĐược gọi tự động khi người dùng đánh rơi một tệp phù hợp
activationEventsBắt đầu cùng với ứng dụngVô hình; dành cho các plugin cần bộ hẹn giờ trong nền
permissionsKhai báo các quyền cần thiếtTrang đồng ý cài đặt sẵn

02Bắt đầu nhanh

Một plugin tối thiểu chỉ cần hai tệp.

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. Viết bảng kê khai.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. Viết kịch bản đầu vào

// 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. Cài đặt vào ứng dụng

Mở AnBoWork → thanh trên cùng "Công cụ" → "Plugins" → "Cài đặt cục bộ" ở góc trên bên phải, sau đó chọn thư mục plugin (hoặc một tệp zip của nó). Trang đồng ý liệt kê các quyền đã khai báo và mọi công cụ trước khi cài đặt; xác nhận để kích hoạt.

4. Hãy thử nó

Trong bất kỳ phiên nào, hãy nói với AI "yêu cầu plugin gửi lời chào đến Alice". Dựa trên mô tả công cụ, AI quyết định gọi say_hello và lệnh gọi cũng như kết quả của nó sẽ xuất hiện trong phiên.

MẸO
AI bên trong AnBoWork cũng có thể tạo plugin cho bạn (giả sử "viết cho tôi một plugin thực hiện X" và nó gọi create_local_plugin). Các plugin đã tạo bị tắt theo mặc định cho đến khi bạn xem lại mã trên trang plugin và bật chúng theo cách thủ công. Hướng dẫn này áp dụng như nhau cho việc đọc và sửa đổi các plugin do AI tạo.

03Trường kê khai

Cánh đồngKiểuYêu cầuSự miêu tả
idstringĐúngKiểu tên miền đảo ngược, ví dụ: com.example.my-plugin. Chỉ chữ cái, chữ số, dấu chấm và dấu gạch nối. Bất biến một khi được tạo; cài đặt lại cùng một id được coi là cài đặt lại.
namestringĐúngTên plugin. Cùng với description, nó được đưa vào mô hình dưới dạng ngữ cảnh, vì vậy nên sử dụng tiếng Anh.
versionstringĐúngPhiên bản ngữ nghĩa; thị trường sử dụng nó để phát hiện các bản cập nhật.
descriptionstringĐúngMột câu mô tả chức năng của plugin (tiếng Anh).
authorstringKHÔNGTác giả.
readmestringKHÔNGMô tả dạng dài hiển thị trên trang chi tiết thị trường, hỗ trợ Markdown. Hình ảnh bên trong phải là https URL — đường dẫn tương đối đến các tệp được gói trong zip không được hỗ trợ.
iconstringKHÔNGBiểu tượng hiển thị trong danh sách thị trường và trang chi tiết — đường dẫn tương đối đến tệp được gói trong zip (ví dụ: icon.png). Phải là PNG, không phải SVG, ít nhất là 128×128.
mainstringĐúngTập lệnh nhập, liên quan đến thư mục plugin. .. không được phép thoát khỏi thư mục.
permissionsstring[]Đúngnetwork / filesystem / notifications; có thể là một mảng trống. Xem "Mô hình cấp phép".
toolsobject[]ĐúngKhai báo công cụ; có thể là một mảng trống. Xem "Công cụ & Giao thức cuộc gọi".
documentParsersobject[]KHÔNGKhả năng phân tích tập tin.
uiobjectKHÔNGBảng nổi:{ entry, title }
viewsobject[]KHÔNGChế độ xem được cố định:{ id, title, icon, entry, width? }
sessionActionobjectKHÔNGBiểu tượng thanh công cụ, tối đa một biểu tượng cho mỗi plugin.
configurationobject[]KHÔNGDanh sách các mục cài đặt.
commandsobject[]KHÔNGCác mục trong bảng lệnh.
keybindingsobject[]KHÔNGPhím tắt. command phải tham chiếu tool của một mục trong commands[], nếu không quá trình cài đặt sẽ bị từ chối.
menusobjectKHÔNGHiện chỉ có vị trí session/context khả dụng.
activationEventsstring[]KHÔNGHiện tại chỉ có onStartupFinished.

Quy ước ngôn ngữ cho văn bản

Văn bản trong tệp kê khai có hai đối tượng. Đối với mẫu: name, description, tools[].description, tools[].parameters.*.description. Viết chúng bằng tiếng Anh; các mô hình hiểu mô tả công cụ bằng tiếng Anh và điền các tham số một cách đáng tin cậy hơn. Dành cho mọi người: tools[].summary (tóm tắt một dòng trên trang chấp thuận), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title và title bên trong menus. Viết những điều này bằng ngôn ngữ của người dùng mục tiêu của bạn.

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

04Mô hình quyền

permissions phải phản ánh trung thực những gì mã thực sự làm. Trang đồng ý cài đặt sẵn liệt kê từng trang để người dùng có thể quyết định có cài đặt hay không; đánh giá thị trường cũng kiểm tra tuyên bố dựa trên mã.

Sự cho phépKhi nào nó phải được khai báoChủ nhà đối xử với nó như thế nào
filesystemĐọc hoặc ghi tệp người dùng (đọc mã dự án, tạo tài liệu, lưu trữ dữ liệu trong thư mục chính)Sự đồng ý có hiểu biết, được hiển thị cho người dùng.
networkThực hiện bất kỳ yêu cầu mạng nào (API của bên thứ ba, tải xuống)Sự đồng ý có hiểu biết, được hiển thị cho người dùng.
notificationsHiển thị thông báo hệ thống qua RPC đảo ngược show_notificationEnforced: gọi nó mà không khai báo trả về host_error và không hiển thị thông báo.
GHI CHÚ
Tiến trình con của plugin là một môi trường Node.js đầy đủ. filesystem và network hiện là các khai báo ở cấp độ đồng ý, không phải là hộp cát thời gian chạy: máy chủ không chặn fs hoặc fetch. Đó chính là lý do tại sao lời khai phải trung thực. Các plugin không được khai báo đầy đủ sẽ bị từ chối trong quá trình đánh giá trên thị trường và đánh mất niềm tin của người dùng khi bị phát hiện. Cũng đừng khai báo quá mức (các quyền không sử dụng khiến người dùng ngần ngại).

Quyền riêng tư và dữ liệu

context.cwd là thư mục dự án của người dùng và có thể chứa mã nguồn và cấu hình nhạy cảm; đối số công cụ có thể chứa cuộc trò chuyện của người dùng. Không gửi dữ liệu này qua mạng trừ khi tính năng này yêu cầu điều đó và khi bạn làm như vậy, hãy nêu rõ description và summary nội dung được gửi và gửi cho ai. Giữ dữ liệu liên tục của plugin trong một tệp trong thư mục chính của người dùng được đặt tên theo id plugin để dễ dàng tìm thấy khi gỡ cài đặt.

05Công cụ & Giao thức cuộc gọi

Tuyên ngôn

Cánh đồngSự miêu tả
nameTên công cụ bằng chữ thường và dấu gạch dưới, duy nhất trên toàn bộ ứng dụng (nó được đăng ký với mô hình cùng với các công cụ tích hợp sẵn và các công cụ của plugin khác).
descriptionMô tả cho mô hình (tiếng Anh). Nêu ba điều: công cụ này làm gì, người dùng nói gì khi nào nên gọi nó và khi nào không được gọi nó. Đây là trường quan trọng nhất để xác định liệu AI có sử dụng plugin của bạn hay không.
summaryBản tóm tắt một dòng mà con người có thể đọc được hiển thị trên trang chấp thuận. Tùy chọn nhưng được khuyến khích mạnh mẽ; nếu không thì mô tả bị cắt ngắn sẽ được hiển thị thay thế.
parametersĐối tượng Lược đồ JSON tiêu chuẩn có description trên mọi thuộc tính. Mô hình sẽ điền các đối số từ nó và máy chủ sẽ chuyển chúng qua mà không cần xác thực thêm, vì vậy hãy kiểm tra các trường bắt buộc trong mã của bạn.
renderAsKhông bắt buộc; hiện chỉ có checklist: máy chủ hiển thị mảng todos từ đối số cuộc gọi dưới dạng thẻ danh sách kiểm tra, xem bên dưới.
Ghi chú
Mô hình chỉ có thể gọi tên công cụ được khai báo trong tools[] tại đây — sessionAction.tool, commands[].tool và trường tool bên trong menus chỉ được chọn là chuỗi không trống khi cài đặt; máy chủ thực hiện not xác minh tên được khai báo trong tools[] và vẫn gửi cuộc gọi dưới tên đó khi nút, phím tắt hoặc mục menu được kích hoạt. Vì vậy, một hành động hoàn toàn do giao diện người dùng kích hoạt có thể được xử lý hoàn toàn trong index.js của riêng bạn mà không bao giờ xuất hiện trong tools[] — nhưng khi đó AI không bao giờ có thể nhìn thấy hoặc gọi nó. Nếu bạn muốn kích hoạt cùng một hành động bằng cả cách nhấp vào và AI thực hiện yêu cầu trò chuyện, hãy khai báo cùng tên đó trong tools[] để cả hai điểm nhập đều có chung một trình xử lý; nếu mục đích của nó chỉ là giao diện người dùng, hãy để nó không được khai báo — không cần thêm mục nhập chỉ để trông hoàn chỉnh.

Giao thức tin nhắn

Máy chủ gửi call_tool; plugin phải trả lời bằng tool_result hoặc tool_error kèm theo requestId tương tự. Một cuộc gọi không trả lời trong vòng 30 giây và mô hình nhận được lỗi.

// 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 chỉ chấp nhận các khối text và image (hình ảnh dưới dạng base64 data cộng với mimeType); các loại khác bị loại bỏ. details là dữ liệu có cấu trúc tùy ý không bao giờ được đưa vào mô hình; nó dùng để hiển thị giao diện người dùng máy chủ và các chế độ xem được gắn đế của riêng bạn.

Kết quả tập tin: thẻ tập tin

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

Kết xuất danh sách kiểm tra: danh sách kiểm tra 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" }
  ]
}

Viết một mô tả tốt

Mô tả yếu tố kích hoạt trong câu mệnh lệnh ("Gọi cái này khi người dùng yêu cầu...") và nêu các loại trừ ("Không gọi cái này cho..."). Nếu kết quả là một tệp, hãy yêu cầu mô hình tham chiếu liên kết tệp được trả về thay vì tuyên bố rằng nó đã được tạo. Nếu các đối số được chuyển tiếp đến API thế hệ bên ngoài, hãy cho biết chúng nên sử dụng ngôn ngữ nào. Tránh viết một dòng như "quản lý việc cần làm"; người mẫu sẽ không biết khi nào nên sử dụng công cụ này.

06Lượt xem cố định (lượt xem)

Chế độ xem được gắn cố định là một trang cố định trong thanh bên phải của phiên: một biểu tượng bổ sung xuất hiện trên thanh công cụ phiên và HTML của bạn hiển thị ở bên phải khi mở. Trạng thái trang (vị trí cuộn, bản nháp) vẫn tồn tại trong các phiên chuyển đổi và thu gọn/mở rộng, ngữ nghĩa tương tự như chế độ xem thanh bên của VS Code.

Cánh đồngSự miêu tả
idDuy nhất trong plugin.
titleVăn bản chú giải công cụ cho biểu tượng (người có thể đọc được).
iconChọn từ danh sách cho phép của máy chủ: git-branch, checklist. Giữ các biểu tượng nhất quán trên ứng dụng; hình ảnh tùy chỉnh không được chấp nhận.
entryTệp HTML, liên quan đến thư mục plugin.
widthTùy chọn chiều rộng bảng mặc định tính bằng pixel, 240 đến 640, mặc định 360. Người dùng có thể kéo để thay đổi kích thước và ghi nhớ kích thước.

Thời gian chạy trang

Trang chạy trong hộp cát (sandbox + contextIsolation, không có Nút, không có quyền truy cập vào dữ liệu ứng dụng máy chủ) và chỉ có thể giao tiếp với quy trình plugin của chính nó thông qua một số phương pháp trên window.pluginHost. Mang phong cách riêng của bạn; Bạn nên tuân theo sơ đồ sáng/tối của ứng dụng như trong ví dụ bên dưới.

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

Mẫu được đề xuất: trong lần tải đầu tiên, hãy gọi một trong các công cụ truy vấn của riêng bạn bằng invoke() và yêu cầu nó trả về dữ liệu có cấu trúc trong details; sau đó đẩy mọi thay đổi từ quy trình plugin bằng ui_push thay vì bỏ phiếu. Khi AI thay đổi dữ liệu trong cuộc trò chuyện, bảng điều khiển sẽ làm mới ngay lập tức.

07Bảng điều khiển nổi (ui)

ui: { entry, title } khai báo một cửa sổ nổi độc lập (900×700) mà người dùng mở từ menu "⋯" trong danh sách plugin; nó bị phá hủy khi đóng. Môi trường trang và API window.pluginHost giống hệt với chế độ xem được gắn và ui_push được phân phối tới cả bảng điều khiển nổi và chế độ xem được gắn. Phù hợp với các tấm lớn thỉnh thoảng mở ra; sử dụng views cho nội dung sẽ ở bên cạnh phiên.

08Biểu tượng thanh công cụ phiên (sessionAction)

Tối đa một biểu tượng cố định cho mỗi plugin. Nhấp vào nó sẽ gọi tool với các đối số trống, do đó công cụ đó phải thực hiện điều gì đó có ý nghĩa mà không có tham số.

Cánh đồngSự miêu tả
toolCông cụ gọi khi nhấp chuột.
iconTên biểu tượng được cho phép.
tooltipDi chuột văn bản.
visibleToolKhông bắt buộc. Khi được đặt, nó sẽ được gọi với các đối số trống trước khi hiển thị và boolean details.visible trong kết quả sẽ quyết định xem biểu tượng có hiển thị hay không. Hữu ích cho việc kiểm tra thời gian chạy chẳng hạn như "thư mục hiện tại có phải là kho lưu trữ git không".
// 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 },
});

09Cài đặt (cấu hình)

Các mục được khai báo xuất hiện dưới cấu hình plugin trên trang cài đặt. Máy chủ lưu trữ các giá trị do người dùng thay đổi và chuyển chúng dưới dạng context.config trên mỗi call_tool (được khóa bởi id, quay lại defaultValue), cho dù cuộc gọi đến từ AI, biểu tượng thanh công cụ hay bảng điều khiển của bạn. Bạn không bao giờ cần phải yêu cầu họ.

typeHiển thị dưới dạngloại giá trị mặc định
selectDanh sách một lựa chọn; các tùy chọn đến từ optionsToolstring
booleanCông tắc chuyển đổiboolean
stringNhập văn bản, cam kết làm mờstring
numberNhập số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.

10Bảng lệnh/phím tắt/menu ngữ cảnh

Cả ba đều là điểm vào khác nhau để "gọi một công cụ có đối số trống"; một plugin có thể khai báo một số.

Cánh đồngHình dạngSự miêu tả
commands{ tool, icon, title }[]Hiển thị trong bảng lệnh Ctrl/Cmd+K và được kích hoạt sau khi người dùng tìm kiếm nó.
keybindings{ command, key }[]command phải bằng tool của một mục trong commands[]. key sử dụng ký hiệu mod+shift+g, trong đó mod là Cmd trên macOS và Ctrl ở nơi khác. Xung đột với các phím tắt khác không được kiểm tra tĩnh, vì vậy hãy chọn các kết hợp không phổ biến.
menus{ "session/context": { tool, icon, title }[] }Đã thêm vào menu trên một hàng phiên. Hiện tại chỉ có vị trí này có sẵn.

11Trình phân tích cú pháp tài liệu (documentParsers)

Khi người dùng thả tệp vào hộp nhập liệu, máy chủ sẽ tra cứu các trình phân tích cú pháp được các plugin đã bật theo loại MIME khai báo và chuyển nội dung tệp dưới dạng base64 tới toolName phù hợp. Đây không phải là công cụ mà AI quyết định gọi, do đó, not cũng liệt kê công cụ phân tích cú pháp trong tools, nếu không nó sẽ được hiển thị dưới dạng công cụ có thể gọi 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 },
  });
});

12RPC đảo ngược (host_request)

Quá trình plugin không có API Electron. Khi cần khả năng hệ thống thực, nó sẽ gửi host_request đến máy chủ và máy chủ sẽ trả lời bằng host_response hoặc host_error. Kênh này hoàn toàn độc lập với call_tool; bạn có thể sử dụng nó trong khi xử lý lệnh gọi công cụ hoặc từ bộ hẹn giờ.

capabilityparamsresultđiều kiện tiên quyết
show_notification{ title, body?, viewId? }{ shown: true } / { shown: false, reason: "disabled" | "unsupported" }Yêu cầu sự cho phép notifications. Trả về bị tắt thay vì lỗi khi người dùng tắt thông báo trên màn hình trong cài đặt. Khi viewId trỏ đến một trong các chế độ xem được gắn vào vị trí của bạn, việc nhấp vào thông báo sẽ đưa ứng dụng lên phía trước và mở rộng chế độ xem đó.
render_pdf{ outputPath, title, content }{}nội dung là Markdown; máy chủ hiển thị tệp PDF thành outPath bằng trình kết xuất tích hợp sẵn.
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)',
});

13Vòng đời

Sân khấuHành vi
Bắt đầuLười theo mặc định: không khởi động với ứng dụng, được phân nhánh trong lần gọi đầu tiên tới một trong các công cụ của bạn (AI, biểu tượng thanh công cụ, bảng điều khiển, lệnh, phân tích cú pháp tài liệu). Các plugin khai báo activationEvents: ["onStartupFinished"] bắt đầu ngay sau khi ứng dụng khởi chạy xong; chỉ sử dụng tính năng này cho các plugin cần bộ hẹn giờ nền (lời nhắc, bỏ phiếu), không sử dụng cho các plugin công cụ thông thường.
Người dânSau khi bắt đầu, quy trình vẫn tồn tại và được sử dụng lại trong các lệnh gọi công cụ. Bạn có thể lưu trạng thái vào bộ nhớ đệm trong bộ nhớ nhưng giả sử nó có thể được khởi động lại bất kỳ lúc nào.
Tai nạnNếu quá trình thoát đột ngột, máy chủ sẽ khởi động lại nó: ngay lập tức, sau đó sau 5 và 15 giây. Sau 3 lần thất bại liên tiếp nó bị đánh dấu là "bị hỏng" và không thử lại nữa; người dùng nhìn thấy trạng thái trên trang plugin. Các cuộc gọi trên chuyến bay đều có lỗi trả về.
Tắt/gỡ cài đặt/cài đặt lạiQuá trình này bị chấm dứt và các chế độ xem được gắn đế cũng như bảng điều khiển nổi bị hủy; cài đặt lại sẽ thay thế toàn bộ thư mục.
Nhật kýstdout/stderr của bạn được ghi vào tệp nhật ký ứng dụng có tiền tố id plugin. Plain console.log là đủ để gỡ lỗi; xem "Gỡ lỗi & Câu hỏi thường gặp".
ENV
Quá trình con bắt đầu bằng ELECTRON_RUN_AS_NODE=1, cwd là thư mục cài đặt plugin và phiên bản Node khớp với Electron đi kèm với ứng dụng. Không phụ thuộc vào bất kỳ gói nào được cài đặt trên toàn cầu và không cho rằng npx tồn tại.

14Đóng gói & Xuất bản

Bao bì

Nén thư mục plugin bằng manifest.json ở thư mục gốc của kho lưu trữ. Nếu bạn có phần phụ thuộc của bên thứ ba, trước tiên hãy nhóm chúng thành một tệp duy nhất bằng esbuild/webpack thay vì gửi toàn bộ 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)

Xuất bản ra thị trường

AnBoWork → Công cụ → Plugin → tab "Thị trường" → "Xuất bản plugin" ở góc trên bên phải, sau đó tải tệp zip lên. Các bài gửi sẽ được xem xét để kiểm tra xem phần khai báo trong bảng kê khai có khớp với mã hay không (đặc biệt là các quyền). Sau khi được phê duyệt, những người dùng khác có thể tìm và cài đặt nó trên thị trường và AI có thể tự động cài đặt các plugin đã được xác minh cho người dùng khi thấy cần thiết.

Cập nhật

Bump version và xuất bản lại. Ứng dụng so sánh phiên bản đã cài đặt với phiên bản trên thị trường và nhắc cập nhật; cài đặt lại sẽ giữ các giá trị cài đặt và trạng thái kích hoạt của người dùng.

15Ví dụ đầy đủ: Todo Quadrant

Một plugin thực sự, đang hoạt động (đã được cắt bớt): AI có thể thêm và xóa việc cần làm, thanh bên phải hiển thị một bảng được làm mới theo thời gian thực và có thể gửi lời nhắc thông báo hệ thống. Xâu chuỗi ba cái này bao gồm giao thức công cụ, đẩy chế độ xem gắn đế và RPC đảo ngược.

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

16Gỡ lỗi & Câu hỏi thường gặp

Triệu chứngNguyên nhân và cách khắc phục
Xác thực tệp kê khai không thành công khi cài đặtKiểm tra từng trường dựa trên bảng "Trường kê khai" vì thông báo cho biết: định dạng id, mục chính/mục nhập không được chứa .., biểu tượng phải nằm trong danh sách cho phép, lệnh liên kết phím phải trỏ đến công cụ hiện có trong lệnh, chiều rộng từ 240 đến 640.
AI không bao giờ gọi công cụ của tôiHầu như luôn luôn là vấn đề description: trình kích hoạt không rõ ràng hoặc không được viết bằng tiếng Anh. Xem phần "Viết mô tả hay". Đồng thời kiểm tra xem tên công cụ có xung đột với một công cụ khác không.
Hết thời gian gọi (30 giây)Tập lệnh nhập không trả lời với tool_result. Thông thường, requestId không được phản hồi lại, thông báo type bị sai chính tả hoặc tập lệnh bị lỗi khi khởi động và thoát. Tìm các dòng có tiền tố [plugin:your-id] trong nhật ký ứng dụng.
Trạng thái plugin hiển thị "bị lỗi"3 lần khởi động thất bại liên tiếp. Thường là require của gói bị thiếu; đang chạy node index.js trong thư mục plugin sẽ tái tạo nó. Sau khi sửa xong, hãy tắt và bật lại plugin trên trang plugin.
Bảng điều khiển mở trốngĐường dẫn entry sai hoặc lỗi JS trên trang. Trang chạy trong hộp cát không có require của Node; chỉ có window.pluginHost.
Thông báo không xuất hiệnKiểm tra theo thứ tự: bảng kê khai khai báo notifications; kết quả không phải là shown:false (người dùng đã tắt thông báo trên màn hình); hệ điều hành cho phép thông báo từ AnBoWork.
Giá trị cài đặt không thể đọc đượcCác giá trị nằm trong message.context.config[id], không phải trong params.

Vị trí tệp nhật ký

hệ điều hànhCon đường
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
Xem tài liệu tích hợp API