DAP Plugin Platform

DAP Plugin Docs

DAP 플러그인은 데스크톱 위의 DAP 경험에 명령, 액션, 설정, 트레이 패널, 단축키, 팔레트 UI와 로컬 서비스를 추가하는 확장 모듈입니다. 이 문서는 실제 플러그인을 만들고 배포하는 데 필요한 최소 경로와 API 레퍼런스를 제공합니다.

문서/버전 기준

현재 Plugin Platform 버전은 0.3.0, manifest 기준은 manifest_version: 2, 전체 API 기준 최소 앱 버전은 DAP 1.4.1입니다. 0.3은 데일리 브리핑과 Host 관리 OAuth·커넥터 상태 API를 추가한 하위 호환 릴리스입니다.

AI로 플러그인 만들기

AI 에이전트에게 긴 문서를 직접 설명하지 않아도 됩니다. 아래 프롬프트에 만들고 싶은 기능만 적어 전달하면, AI가 llms.txt에서 필요한 문서와 규칙을 따라가도록 설계했습니다.

Read https://project-undonghae.github.io/desk-ai-pet/llms.txt
and create a DAP plugin that [원하는 기능을 적어주세요].

Return:
- plugin.yaml
- a self-contained ESM entry file
- optional palette/tray static files
- required permissions and why they are needed
- local install steps
- known limitations

Follow manifest_version: 2 and Plugin Platform 0.3.0.
Do not invent Host APIs.
추천 사용법

사람은 원하는 기능만 구체적으로 적고, AI에게는 llms.txt URL을 함께 전달하세요. 예: “선택한 한국어 문장을 공손하게 다듬는 DAP 플러그인을 만들어줘.”

Overview

DAP 플러그인은 plugin.yaml과 자기완결 ESM .mjs 진입 파일로 구성됩니다. manifest에는 메타데이터와 권한만 선언하고, 기능 등록은 activate(ctx)에서 수행합니다.

현재 런타임

현행 DAP 플러그인 문서는 Electron/TypeScript 기반 Host API를 기준으로 합니다. 예전 Python/PyQt 플러그인 문서는 historical 문서로만 취급합니다.

신뢰 경계

플러그인 진입 파일은 main process에서 실행되는 신뢰 기반 코드입니다. permissions[]는 사용자 동의와 Host API 노출 경계이지 프로세스 sandbox가 아닙니다. DAP은 공식 카탈로그의 공개 GitHub 저장소만 설치합니다.

Quickstart

1. 폴더 만들기

my-plugin/
  plugin.yaml
  dap_my_plugin/
    plugin.mjs
  README.md

2. manifest 작성

id: com.example.my_plugin
name: My Plugin
version: 1.0.0
manifest_version: 2
entry: dap_my_plugin.plugin:activate
description: DAP에서 실행되는 첫 플러그인
author: Your Name
min_app_version: "1.4.1"
surface: user
permissions: []
execution_modes:
  - user

3. 액션 등록

// dap_my_plugin/plugin.mjs
export function activate(ctx) {
  ctx.actions.registerAction({
    id: "hello",
    callback: () => {
      ctx.host.bubble.speak("Hello from plugin");
      return "Hello from plugin";
    },
  });

  ctx.radialMenu.addItem({
    itemId: "hello",
    label: "Hello",
    actionId: "hello",
    priority: 50,
  });
}

4. 로컬 설치

Windows:
%APPDATA%\dap\plugins\com.example.my_plugin\

macOS:
~/Library/Application Support/dap/plugins/com.example.my_plugin/

진입 파일은 bare import가 없는 자기완결 .mjs여야 합니다. 로컬 폴더를 설치 경로에 둔 뒤 DAP을 재시작하거나 설정의 플러그인 토글을 다시 켜서 로드합니다.

Core concepts

개념설명
Manifestplugin.yaml. id, 이름, 버전, entry, 권한을 선언합니다.
Entrymodule.path:activate. 플러그인의 ESM 진입점입니다.
ActivationDAP가 플러그인을 로드할 때 activate(ctx)를 호출합니다.
Registration액션, 메뉴, 단축키, 설정 섹션은 등록 핸들로 추적되고 비활성화 시 정리됩니다.
Host services클립보드, 저장소, 팔레트, 회의 캡처, 계정, 이미지 생성 같은 기능은 ctx.host.*로만 접근합니다.
Permissions권한이 필요한 host service는 permissions[]에 최소 범위로 선언합니다. 미선언 서비스는 undefined입니다.
Surface slotssurface_slots: [tray_panel]처럼 DAP가 제공하는 시각 표면을 manifest에 선언합니다.
AI contextcontext_contributors에 선언한 id만 ctx.aiContext.contribute()로 등록할 수 있습니다.

Guides

명령 등록하기

채팅 또는 스팟라이트 입력을 자연어로 라우팅해야 할 때 ctx.commands를 사용합니다.

ctx.commands.addCommand({
  id: "weather",
  title: "날씨",
  matchers: [
    { type: "keyword", patterns: ["날씨", "weather"], priority: 40 },
  ],
  backend: { type: "builtin", handler: "dap.weather.current" },
});

설정 섹션 추가하기

ctx.settings.registerSettingsSection({
  sectionId: "general",
  title: "My Plugin",
  spec: {
    fields: [
      { key: "compact", label: "간단히 보기", type: "toggle", default: false },
      { key: "mode", label: "모드", type: "select", default: "fast",
        options: [
          { value: "fast", label: "빠르게" },
          { value: "careful", label: "정교하게" },
        ] },
      { key: "note", label: "메모", type: "text" },
    ],
  },
});

const values = ctx.host.settings.values("general");
ctx.host.settings.set("general", "compact", true);

트레이 허브 패널 등록하기

surface_slots: [tray_panel]window.palette 권한을 manifest에 선언한 뒤 등록합니다. 패널은 샌드박스 iframe이며 최대 64KB의 JSON형 메시지만 주고받습니다.

# plugin.yaml
permissions:
  - window.palette
surface_slots:
  - tray_panel
const panel = ctx.trayPanel.register({
  id: "status",
  page: "tray/index.html",
  height: 210,
  priority: 10,
});

panel.onMessage((message) => {
  if (message?.type === "refresh") refresh();
});
panel.postMessage({ type: "status", items });

컴팩트 패널 권장 높이는 120–240px입니다. 상세 화면은 ctx.host.windows.openPalette()로 분리하고, 일반 트레이 액션은 ctx.trayMenu.addItem({ showInContextMenu: true })로 명시합니다.

팔레트 UI 열기

팔레트는 플러그인 전용 HTML UI입니다. 페이지는 권한 API를 직접 받지 않고 window.dapPalette 메시지 브리지만 사용합니다.

export function activate(ctx) {
  let palette = null;

  const open = () => {
    if (palette && !palette.isDestroyed()) {
      palette.toggle();
      return;
    }

    palette = ctx.host.windows.openPalette({
      page: "palette/index.html",
      width: 360,
      height: 520,
      frame: false,
    });

    palette.onMessage((msg) => {
      if (msg.type === "ready") {
        palette.postMessage({ type: "items", items: ["A", "B"] });
      }
    });

    palette.show();
  };

  ctx.actions.registerAction({ id: "openPalette", callback: open });
  ctx.radialMenu.addItem({ itemId: "palette", label: "Palette", actionId: "openPalette" });

  return () => palette?.close();
}

API reference

Manifest fields

필드필수설명
id플러그인 고유 id. 카탈로그 id와 같아야 합니다.
name사용자에게 표시할 이름입니다.
version플러그인 버전입니다.
entrymodule.path:callable 형식의 진입점입니다.
manifest_version권장현행 문서는 2를 기준으로 합니다.
description / author아니요사용자 설명과 작성자 메타데이터입니다.
min_app_version아니요필요한 최소 DAP 버전입니다. 충족하지 못하면 설치 전에 중단합니다.
surface아니요user(기본) 또는 dev 빌드 전용 dev입니다.
permissions아니요사용할 권한 토큰 목록입니다.
execution_modes아니요user, builtin, scheduled 등 실행 형태 메타데이터입니다.
context_contributors아니요ctx.aiContext로 등록할 기여 id 목록입니다.
surface_slots아니요tray_panel 또는 briefing.daily 기여를 사용하기 전에 선언합니다.

추가 필드는 거절됩니다. manifest에는 명령·matcher·backend를 넣지 말고 activate(ctx)에서 등록하세요.

ctx namespaces

API용도
ctx.actions.registerAction호출 가능한 액션을 등록합니다.
ctx.commands.addCommand자연어/라벨 기반 명령을 등록합니다.
ctx.settings.registerSettingsSection설정 UI 섹션을 등록합니다.
ctx.trayMenu.addItem트레이 메뉴 항목을 추가합니다.
ctx.radialMenu.addItem펫 래디얼 메뉴 항목을 추가합니다.
ctx.shortcuts.registerShortcut재바인딩 가능한 전역 단축키를 등록합니다.
ctx.trayPanel.register트레이 허브의 샌드박스 시각 패널을 등록합니다.
ctx.aiContext.contributemanifest에 선언한 짧은 AI 컨텍스트를 기여합니다.
ctx.briefing.contributebriefing.daily에 오늘의 짧은 사용자용 요약을 기여합니다.

Host services

권한서비스설명
권한 불필요ctx.host.bubble펫 말풍선을 출력합니다.
권한 불필요ctx.host.llm활성 LLM provider로 텍스트를 생성합니다.
권한 불필요ctx.host.clipboard현재 클립보드 텍스트를 읽고 씁니다. manifest 토큰은 설치 고지용입니다.
권한 불필요ctx.host.settings이 플러그인의 선언형 설정값을 읽고 씁니다.
storage.privatectx.host.storage플러그인 전용 격리 저장소입니다.
window.palettectx.host.windows플러그인 팔레트 창을 엽니다.
clipboard.historyctx.host.clipboardHistory클립보드 기록을 읽습니다. 사용자 opt-in이 필요합니다.
input.synthesizectx.host.paste이전 foreground 앱에 붙여넣습니다.
presentation.overlayctx.host.presentation발표용 투명 오버레이를 열고 제어합니다.
meeting.capturectx.host.meeting지원 여부 확인 후 회의 캡처·상태·전사 이벤트를 사용합니다.
ai.accountsctx.host.aiAccounts로그인된 Claude/Codex 계정과 공개된 사용량 메타데이터를 조회합니다.
image.generatectx.host.imageGenCodex provider로 PNG를 생성해 bytes로 받습니다.
oauth.connectctx.host.oauthHost가 관리하는 OAuth 연결·상태·access token·해제 흐름을 사용합니다.
connectors.readctx.host.connectors현재 Gmail 커넥터의 상태와 Host가 만든 사용자 안내를 읽습니다.

Permissions

요청 동작에 꼭 필요한 토큰만 선언하세요. 설치 화면은 선언 권한을 고지하며, 미선언 권한 서비스는 플러그인 컨텍스트에 제공하지 않습니다.

토큰용도추가 조건
storage.privateJSON·blob 격리 저장소플러그인별 기본 500MB cap
window.palette팔레트와 tray_panel UI페이지는 플러그인 루트 안, 외부 네트워크 차단
clipboard.history클립보드 기록민감·기본 OFF, 사용자가 설정에서 별도 opt-in
input.synthesize이전 앱에 붙여넣기macOS Accessibility 권한 필요
dragdrop.export팔레트에서 이미지·파일 드래그텍스트 드래그는 HTML5 native drag 사용
presentation.overlay발표 오버레이투명·클릭스루 상태를 명시적으로 제어
meeting.capture마이크·시스템 오디오 전사OS 권한과 capability gate, 실행 중 표시
ai.accounts계정·구독 한도 메타데이터token/cookie/keychain 값은 전달되지 않음
image.generateAI 이미지 생성Codex provider 전용, 사용자 quota 소모
oauth.connectOAuth 서비스 연결브라우저 흐름·refresh token 저장은 DAP이 관리
connectors.readGmail 커넥터 상태읽기 전용 상태와 Host가 만든 사용자 안내만 제공

현재 entry module은 신뢰된 in-process 코드입니다. 권한 브로커는 Host API 노출을 제한하지만 악성 플러그인을 프로세스 수준에서 격리하지는 않습니다.

Examples

Daily briefing and OAuth

// plugin.yaml: surface_slots: [briefing.daily]
ctx.briefing.contribute({
  id: "today",
  provider: () => "오늘 일정 2개 · 마감 할 일 1개",
});

// plugin.yaml: permissions: [oauth.connect]
const status = await ctx.host.oauth.status("google-calendar");
if (!status.connected) {
  await ctx.host.oauth.connect({
    connectionId: "google-calendar",
    provider: "google",
    scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
  });
}

OAuth 브라우저 흐름과 refresh token 보관은 DAP Host가 맡습니다. 플러그인은 refresh token을 직접 받거나 저장하지 않습니다. connectors.read를 선언한 플러그인은 현재 ctx.host.connectors.status("gmail"){ state, via, hint }를 받습니다. stateconnected, unavailable, unknown 중 하나이며, 판별 자체가 실패한 unknown에서는 기능을 차단하지 말고 명시적인 unavailable에서만 Host의 hint를 안내하세요.

Storage

await ctx.host.storage.setJson("pins", ["a", "b"]);
const pins = await ctx.host.storage.getJson("pins");
await ctx.host.storage.delete("pins");

LLM

const answer = await ctx.host.llm.generate(
  "다음 문장을 더 자연스럽게 다듬어줘: " + text,
  30
);

Meeting capability gate

const capability = ctx.host.meeting.capabilities();
if (!capability.available) {
  ctx.host.bubble.speak(capability.reason ?? "회의 캡처를 사용할 수 없어요.");
  return;
}

const offTranscript = ctx.host.meeting.onTranscript((event) => {
  palette.postMessage({ type: "meeting.transcript", event });
});
await ctx.host.meeting.start({ source: "both", sourceLanguage: "en", targetLanguage: "ko" });
// cleanup에서 await ctx.host.meeting.stop(); offTranscript();

Image generation

const image = await ctx.host.imageGen.generate("고양이 밈");
const blobId = await ctx.host.storage.putBlob(image.bytes, {
  mime: image.mime,
  name: image.name,
});

Palette page

<!doctype html>
<meta charset="utf-8" />
<button id="close">close</button>
<ul id="list"></ul>
<script>
  const list = document.getElementById("list");

  window.dapPalette.onMessage((msg) => {
    if (msg.type !== "items") return;
    list.innerHTML = "";
    for (const item of msg.items) {
      const li = document.createElement("li");
      li.textContent = item;
      list.append(li);
    }
  });

  window.dapPalette.postMessage({ type: "ready" });
  document.getElementById("close").onclick = () => window.dapPalette.close();
</script>

Distribution

Catalog entry

플러그인은 공개 GitHub 저장소에 두고, Project-Undonghae/dap-pluginsplugin_catalog.json에 PR을 보냅니다.

{
  "plugins": [
    {
      "id": "com.example.my_plugin",
      "name": "My Plugin",
      "description": "한 줄 설명",
      "repo": "https://github.com/example/dap-my-plugin.git",
      "ref": "v1.0.0"
    }
  ]
}
  • repoowner/name 또는 GitHub HTTPS URL을 사용합니다. 저장소는 public이어야 합니다.
  • ref는 브랜치·태그·SHA이며 재현 가능한 설치를 위해 버전 태그를 권장합니다.
  • plugin.yamlid와 카탈로그 id가 다르면 설치를 거절합니다.
  • DAP은 GitHub tree/raw API로 파일을 받아 설치합니다. 사용자 PC에서 git, zip 해제, npm install, 빌드를 실행하지 않습니다.
  • 설치는 명시적인 신뢰 동작이며 새 플러그인은 즉시 활성화됩니다. 이전에 비활성화한 id는 그 상태를 유지합니다.
  • 최대 200파일·20MB이며 node_modules, .git, .github는 제외됩니다.
  • 업데이트마다 plugin.yamlversion을 올리고 카탈로그 ref를 새 태그로 갱신하세요. 권한이 추가되면 사용자가 다시 동의합니다.

Versioning

DAP 앱 릴리스 버전, Plugin Platform 버전, manifest 버전, 개별 플러그인 버전은 서로 다른 책임을 가집니다. 플러그인 개발자에게 영향을 주는 변경은 개발문서와 docs/plugin-platform 아래의 관리 파일을 함께 갱신합니다.

변경필수 갱신버전 처리
새 host API, permission, manifest 선택 필드API reference, changelog, version.jsonPlugin Platform minor
API 설명 보강, 예시 수정, 비호환 없는 오류 수정관련 문서, changelogPlugin Platform patch
API 제거, 인자 변경, 권한 필수화, manifest 필수 필드 변경문서 전체, migration note, changelogPlugin Platform major 또는 manifest_version 증가
카탈로그 필드 추가/변경Distribution, plugins.html 등록 안내, fallback dataPlugin Platform minor 또는 major
개별 플러그인 등록/업데이트카탈로그 ref, 필요 시 plugins.html fallback개별 플러그인 버전
  • docs/plugin-platform/version.json은 Plugin Platform의 현재 버전과 호환성 기준입니다.
  • docs/plugin-platform/changelog.md는 앱 릴리스 노트와 별도로 플러그인 개발자용 변경사항을 기록합니다.
  • manifest_version은 manifest 해석 방식이 호환되지 않을 때만 올립니다.

Troubleshooting

증상확인할 것
플러그인이 보이지 않음설치 폴더명, plugin.yaml 위치, id 일치 여부를 확인합니다.
활성화 실패entry가 실제 .mjs named export를 가리키는지 확인합니다.
host service가 undefined필요한 권한을 permissions[]에 선언했는지 확인합니다.
팔레트가 열리지 않음window.palette 권한과 page 경로가 플러그인 루트 안인지 확인합니다.

Changelog

2026-08-05: Plugin Platform 0.3.0. briefing.daily, ctx.briefing, oauth.connect/ctx.host.oauth, connectors.read/ctx.host.connectors를 추가했습니다. 최소 앱은 DAP 1.4.1이며 manifest v2는 그대로 호환됩니다.

2026-07-29: Plugin Platform 0.2.0. tray_panel, 동적 tray submenu, ctx.aiContext, 설정 쓰기, presentation/meeting/AI accounts/image generation Host API와 최신 설치·업데이트 계약을 반영했습니다. manifest v2는 그대로 호환됩니다.

2026-07-05: Plugin Platform 0.1.0 기준 문서 구조를 정리했습니다. Electron/TypeScript Host API, manifest v2, palette, distribution, versioning 구조를 반영했습니다.