// 개발자 가이드

플러그인 개발자 문서

AnBoWork 데스크톱 앱용 플러그인 구축: AI용 새 도구 추가, 세션 옆에 자체 패널 고정, 시스템 알림 보내기, 새 파일 형식 구문 분석 등이 가능합니다. 하나의 Manifest.json과 하나의 입력 스크립트만 있으면 시작하기에 충분합니다.

01개요

AnBoWork 플러그인은 일반 Node.js 프로그램입니다. 설치 후 앱은 이를 독립형 하위 프로세스(Electron API가 아닌 순수 노드)로 분기하고 프로세스 간 메시지를 통해 통신합니다. SDK 패키지가 필요하지 않습니다. 필요한 것은 process.on('message') 및 process.send()뿐입니다.

플러그인이 수행할 수 있는 모든 작업은 manifest.json에 정적으로 선언되어 있습니다. 사전 설치 동의 페이지와 마켓플레이스 검토를 통해 플러그인 코드를 실행하지 않고도 추가되는 도구와 필요한 권한을 알 수 있습니다. 모든 UI(도구 모음 아이콘, 설정, 명령 팔레트, 상황에 맞는 메뉴)는 선언에서 호스트에 의해 렌더링됩니다. 플러그인 코드는 기본 창 내에서 실행되지 않습니다. 도킹된 보기와 부동 패널만 고유한 HTML이며 격리된 샌드박스 페이지에서 실행됩니다.

기능 개요

매니페스트 필드능력사용자가 볼 수 있는 위치
toolsAI용 호출 가능 도구 추가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. 매니페스트.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)를 선택하세요. 동의 페이지에는 설치 전 선언된 권한과 모든 도구가 나열되어 있습니다. 활성화하려면 확인하세요.

4. 시도해 보세요

어떤 세션에서든 AI에게 "플러그인에게 Alice에게 인사하라고 요청하세요"라고 말합니다. 도구 설명에 따라 AI는 say_hello를 호출하기로 결정하고 호출과 그 결과가 세션에 나타납니다.

팁
AnBoWork 내부의 AI는 플러그인을 생성할 수도 있습니다("X를 수행하는 플러그인을 작성해 주세요"라고 말하면 create_local_plugin을 호출합니다). 생성된 플러그인은 플러그인 페이지에서 코드를 검토하고 수동으로 활성화할 때까지 기본적으로 비활성화되어 있습니다. 이 가이드는 AI 생성 플러그인을 읽고 수정하는 데 동일하게 적용됩니다.

03매니페스트 필드

필드유형필수의설명
idstring예역방향 도메인 스타일(예: com.example.my-plugin. 문자, 숫자, 점, 하이픈만 사용할 수 있습니다. 일단 생성되면 변경할 수 없습니다. 동일한 ID를 다시 설치하는 것은 재설치로 간주됩니다.
namestring예플러그인 이름. description와 함께 모델에 컨텍스트로 제공되므로 영어가 권장됩니다.
versionstring예의미론적 버전; 마켓플레이스는 이를 사용하여 업데이트를 감지합니다.
descriptionstring예플러그인의 기능을 설명하는 한 문장(영어).
authorstring아니요작가.
readmestring아니요Marketplace 세부 정보 페이지에 표시되는 긴 형식 설명은 Markdown을 지원합니다. 그 안에 있는 이미지는 https URL이어야 합니다. zip에 번들로 포함된 파일의 상대 경로는 지원되지 않습니다.
iconstring아니요마켓플레이스 목록 및 세부정보 페이지에 표시되는 아이콘 - zip에 포함된 파일의 상대 경로(예: icon.png)입니다. SVG가 아닌 PNG여야 하며 최소 128×128이어야 합니다.
mainstring예플러그인 폴더에 상대적인 항목 스크립트입니다. .. 폴더 이스케이프는 허용되지 않습니다.
permissionsstring[]예network / filesystem / notifications; 빈 배열일 수 있습니다. "권한 모델"을 참조하세요.
toolsobject[]예도구 선언 빈 배열일 수 있습니다. "도구 및 통화 프로토콜"을 참조하세요.
documentParsersobject[]아니요파일 구문 분석 기능.
uiobject아니요플로팅 패널:{ entry, title }
viewsobject[]아니요고정된 뷰:{ id, title, icon, entry, width? }
sessionActionobject아니요툴바 아이콘은 플러그인당 최대 1개입니다.
configurationobject[]아니요설정 항목 목록입니다.
commandsobject[]아니요명령 팔레트 항목.
keybindingsobject[]아니요바로가기. command는 commands[] 항목의 tool를 참조해야 하며, 그렇지 않으면 설치가 거부됩니다.
menusobject아니요현재는 session/context 위치만 사용할 수 있습니다.
activationEventsstring[]아니요현재는 onStartupFinished만 가능합니다.

텍스트의 언어 규칙

매니페스트의 텍스트에는 두 가지 대상이 있습니다. 모델의 경우: name, description, tools[].description, tools[].parameters.*.description. 영어로 작성하세요. 모델은 영어 도구 설명을 이해하고 매개변수를 보다 안정적으로 입력합니다. 사람의 경우: tools[].summary(동의 페이지의 한 줄 요약), ui.title, views[].title, sessionAction.tooltip, configuration[].label, commands[].title 및 title는 menus 내부에 있습니다. 대상 사용자의 언어로 작성하세요.

{
  "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, 다운로드)사전 동의가 사용자에게 표시됩니다.
notificationsshow_notification 역방향 RPC를 통해 시스템 알림 표시Enforced: 선언 없이 호출하면 host_error가 반환되고 알림이 표시되지 않습니다.
메모
플러그인 하위 프로세스는 전체 Node.js 환경입니다. filesystem 및 network는 현재 런타임 샌드박스가 아닌 동의 수준 선언입니다. 호스트는 fs 또는 fetch를 가로채지 않습니다. 이것이 바로 선언이 정직해야 하는 이유입니다. 과소 선언된 플러그인은 마켓플레이스 검토에서 거부되며 발견되면 사용자 신뢰를 잃습니다. 둘 중 하나를 과도하게 선언하지 마십시오(사용하지 않는 권한은 사용자를 주저하게 만듭니다).

개인정보 보호 및 데이터

context.cwd은 사용자의 프로젝트 폴더이며 소스 코드와 민감한 구성이 포함될 수 있습니다. 도구 인수에는 사용자의 대화가 포함될 수 있습니다. 기능에서 요구하지 않는 한 네트워크를 통해 이 데이터를 전송하지 마십시오. 전송하는 경우 description 및 summary에 무엇이 전송되고 누구에게 전송되는지 명시하십시오. 플러그인 ID의 이름을 딴 사용자 홈 폴더 아래 파일에 플러그인 자체의 영구 데이터를 보관하면 제거 시 쉽게 찾을 수 있습니다.

05도구 및 통화 프로토콜

선언

필드설명
name전체 앱에서 고유한 소문자 및 밑줄로 된 도구 이름(내장 도구 및 기타 플러그인 도구와 함께 모델에 등록됨)
description모델에 대한 설명(영어)입니다. 세 가지를 기술하십시오. 도구가 수행하는 작업, 도구를 호출해야 할 때 사용자가 말하는 내용, 호출해서는 안 되는 경우입니다. 이는 AI가 플러그인을 사용하는지 여부에 대한 가장 중요한 단일 필드입니다.
summary사람이 읽을 수 있는 한 줄 요약이 동의 페이지에 표시됩니다. 선택사항이지만 적극 권장됩니다. 그렇지 않으면 잘린 설명이 대신 표시됩니다.
parameters모든 속성에 description가 있는 표준 JSON 스키마 개체입니다. 모델은 인수를 채우고 호스트는 추가 검증 없이 이를 전달하므로 코드에서 필수 필드를 확인하세요.
renderAs선택 과목; 현재 checklist만: 호스트는 호출 인수의 todos 배열을 체크리스트 카드로 렌더링합니다. 아래를 참조하세요.
메모
모델은 여기 tools[]에 선언된 도구 이름만 호출할 수 있습니다. — sessionAction.tool, commands[].tool 및 menus 내부의 tool 필드는 설치 시 비어 있지 않은 문자열인지 확인만 됩니다. 호스트는 않고 이름이 tools[]에 선언되었는지 확인하지 않고 버튼, 바로가기 또는 메뉴 항목이 실행될 때 여전히 해당 이름으로 호출을 전달합니다. 따라서 순전히 UI로 실행되는 작업은 tools[]에 표시되지 않고 index.js에서 완전히 처리될 수 있지만 AI는 이를 보거나 호출할 수 없습니다. 클릭과 AI의 채팅 요청에 의해 동일한 작업이 실행되도록 하려면 tools[]에도 동일한 이름을 선언하여 두 진입점이 하나의 핸들러를 공유하도록 합니다. UI 전용으로 유지하려는 경우 선언하지 않은 상태로 두십시오. 완성된 것처럼 보이도록 항목을 추가할 필요가 없습니다.

메시지 프로토콜

호스트는 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 블록만 허용합니다(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 체크리스트

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

좋은 설명 작성하기

명령형("사용자가 요청하면 이것을 호출하세요...") 및 상태 제외("...를 위해 호출하지 마세요")의 트리거를 설명합니다. 결과가 파일인 경우 생성된 파일 링크를 주장하는 대신 반환된 파일 링크를 참조하도록 모델에 요청하세요. 인수가 외부 생성 API로 전달되는 경우 어떤 언어로 작성해야 하는지 알려주세요. "할 일 관리"와 같은 한 줄짜리 명령은 피하세요. 모델은 언제 도구를 사용해야 할지 알 수 없습니다.

06고정된 뷰(뷰)

고정된 보기는 세션의 오른쪽 사이드바에 있는 영구 페이지입니다. 추가 아이콘이 세션 도구 모음에 나타나고 HTML이 열릴 때 오른쪽에 표시됩니다. 페이지 상태(스크롤 위치, 초안)는 VS Code 사이드바 보기와 동일한 의미로 세션 전환 및 축소/확장 후에도 유지됩니다.

필드설명
id플러그인 내에서 고유합니다.
title아이콘에 대한 도구 설명 텍스트(사람이 읽을 수 있음)
icon호스트 허용 목록에서 선택하세요: git-branch, checklist. 앱 전체에서 아이콘을 일관되게 유지합니다. 사용자 정의 이미지는 허용되지 않습니다.
entry플러그인 폴더에 상대적인 HTML 파일입니다.
width선택적 기본 패널 너비(픽셀), 240~640, 기본 360. 사용자는 드래그하여 크기를 조정할 수 있으며 크기는 기억됩니다.

페이지 런타임

페이지는 샌드박스(sandbox + contextIsolation, 노드 없음, 호스트 앱 데이터에 대한 액세스 없음)에서 실행되며 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설정(구성)

선언된 항목은 설정 페이지의 플러그인 구성 아래에 표시됩니다. 호스트는 사용자가 변경한 값을 저장하고 호출이 AI, 도구 모음 아이콘 또는 패널에서 오는지 여부에 관계없이 모든 call_tool(id로 입력, defaultValue로 대체)마다 context.config로 전달합니다. 당신은 그들을 요구할 필요가 없습니다.

type다음으로 렌더링됨defaultValue 유형
select단일 선택 목록; 옵션은 optionsTool에서 옵니다.string
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 유형별로 조회하고 파일 콘텐츠를 일치하는 toolName에 base64로 전달합니다. 이는 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 권한이 필요합니다. 사용자가 설정에서 데스크톱 알림을 끄면 오류 대신 비활성화된 값이 반환됩니다. viewId가 고정된 뷰 중 하나를 가리키면 알림을 클릭하면 앱이 맨 앞으로 이동하고 해당 뷰가 확장됩니다.
render_pdf{ outputPath, title, content }{}콘텐츠는 마크다운입니다. 호스트는 내장 렌더러를 사용하여 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, 도구 모음 아이콘, 패널, 명령, 문서 구문 분석) 중 하나에 대한 첫 번째 호출에서 분기됩니다. activationEvents: ["onStartupFinished"]를 선언하는 플러그인은 앱 실행이 완료된 직후에 시작됩니다. 일반 도구 플러그인이 아닌 백그라운드 타이머(알림, 폴링)가 필요한 플러그인에만 이 기능을 사용하세요.
거주자일단 시작된 프로세스는 활성 상태로 유지되며 도구 호출 전반에 걸쳐 재사용됩니다. 상태를 메모리에 캐시할 수 있지만 언제든지 다시 시작할 수 있다고 가정합니다.
충돌프로세스가 예기치 않게 종료되면 호스트는 프로세스를 즉시 다시 시작하고 5초 및 15초 후에 다시 시작합니다. 3번 연속 실패하면 "충돌"로 표시되고 더 이상 재시도되지 않습니다. 사용자는 플러그인 페이지에서 상태를 볼 수 있습니다. 기내 통화는 모두 오류를 반환합니다.
비활성화 / 제거 / 재설치프로세스가 종료되고 도킹된 뷰와 부동 패널이 삭제됩니다. 다시 설치하면 전체 폴더가 대체됩니다.
로그stdout/stderr는 플러그인 ID 접두사가 붙은 앱 로그 파일에 기록됩니다. 일반 console.log는 디버깅에 충분합니다. "디버깅 및 FAQ"를 참조하세요.
환경
하위 프로세스는 ELECTRON_RUN_AS_NODE=1로 시작하고, cwd은 플러그인 설치 폴더이며, 노드 버전은 앱에 번들로 제공되는 Electron과 일치합니다. 전체적으로 설치된 패키지에 의존하지 말고 npx가 존재한다고 가정하지 마십시오.

14포장 및 출판

포장

아카이브 루트에 manifest.json로 플러그인 폴더를 압축합니다. 타사 종속성이 있는 경우 전체 node_modules를 배송하는 대신 먼저 esbuild/webpack을 사용하여 단일 파일로 묶습니다.

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을 업로드하세요. 제출물은 매니페스트 선언이 코드(특히 권한)와 일치하는지 확인하는 검토 단계로 들어갑니다. 승인되면 다른 사용자가 마켓플레이스에서 해당 플러그인을 찾아서 설치할 수 있으며, AI는 필요하다고 판단될 때 자동으로 사용자를 위해 검증된 플러그인을 설치할 수 있습니다.

업데이트

version를 범하고 다시 게시하세요. 앱은 설치된 버전을 마켓플레이스 버전과 비교하고 업데이트하라는 메시지를 표시합니다. 다시 설치하면 사용자의 활성화된 상태와 설정 값이 유지됩니다.

15전체 예: Todo Quadrant

실제 작동하는 플러그인(손질됨): 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디버깅 및 FAQ

징후원인 및 수정
설치 시 매니페스트 검증 실패메시지에 표시된 대로 "매니페스트 필드" 표와 비교하여 각 필드를 확인하세요. id 형식, 기본/항목에는 ..가 포함되어서는 안 되며, 아이콘은 허용 목록에 있어야 하고, 키 바인딩 명령은 명령의 기존 도구를 가리켜야 하며, 너비는 240~640 사이입니다.
AI는 내 도구를 호출하지 않습니다.거의 항상 description 문제가 발생합니다. 트리거가 불분명하거나 영어로 작성되지 않았습니다. "좋은 설명 작성"을 참조하세요. 또한 도구 이름이 다른 도구와 충돌하지 않는지 확인하십시오.
통화 시간 초과(30초)항목 스크립트가 tool_result로 응답하지 않았습니다. 일반적으로 requestId가 반향되지 않거나, type 메시지의 철자가 틀리거나, 스크립트가 시작 시 던져지고 종료되었습니다. 앱 로그에서 [plugin:your-id] 접두사가 붙은 줄을 찾아보세요.
플러그인 상태가 "충돌됨"으로 표시됩니다.3연속 선발 실패. 일반적으로 누락된 패키지의 require; 플러그인 폴더에서 node index.js를 실행하면 재현됩니다. 수정 후 플러그인 페이지에서 플러그인을 껐다가 다시 켜세요.
패널이 공백으로 열립니다.entry 경로가 잘못되었거나 페이지에 JS 오류가 있습니다. 페이지는 노드의 require 없이 샌드박스에서 실행됩니다. window.pluginHost만 사용할 수 있습니다.
알림이 표시되지 않습니다순서대로 확인: 매니페스트가 notifications를 선언합니다. 결과는 shown:false(사용자가 데스크톱 알림을 껐음)이 아닙니다. OS는 AnBoWork의 알림을 허용합니다.
설정 값을 읽을 수 없습니다.값은 params이 아닌 message.context.config[id]에 있습니다.

로그 파일 위치

운영체제길
macOS~/Library/Logs/AnBoWork/main.log
Windows%USERPROFILE%\AppData\Roaming\AnBoWork\logs\main.log
API 통합 문서 보기