Architecture
HostApi — 플러그인이 호스트와 대화하는 단일 통로
플러그인은 호스트 코드를 직접 import 하지 않고, context.hostApi.* 메서드만 호출합니다. SDK는 type-only로 lvis-plugin-sdk 에 정의되어 있고, 호스트 측 구현은 lvis-app/src/boot/steps/plugin-runtime.ts 에서 plugin별 인스턴스로 생성됩니다.
@lvis/plugin-sdk · runtime context
src/plugins/types.ts:750
static manifest, not runtime register
이 페이지의 사실 출처
모든 시그니처는
lvis-plugin-sdk/src/index.ts (정의) 와 lvis-app/src/plugins/types.ts (호스트 구현) 에서 확인된 것입니다. 도구 / Skill / UI 슬롯 / 이벤트는 runtime register API가 없습니다 — 모두 plugin.json manifest 에 정적으로 선언됩니다.PluginHostApi 표면 (verbatim)
storage
PluginStorage sandboxed FS — resolve / read / readText / readJson / write / writeJson / rm / list / exists / mkdir. 모든 경로는 plugin namespace 내부에서만.config
get<T>(key) / set<T>(key, value): Promise<void> / onChange<T>(key, cb) — plugin.json configSchema 로 선언된 키만.registerKeywords
채팅 입력 Skill 트리거 등록.
Array<{ keyword: string; skillId: string }>. 유일한 runtime register API.emitEvent / onEvent
이벤트 발사/구독. eventType은 opaque dot-delimited 문자열 (예:
meeting.ended, ms-graph.auth.changed).callTool / callLlm
callTool<T>(toolName, payload) 다른 플러그인 도구 호출. callLlm(prompt, { maxTokens?, systemPrompt? }) 호스트 LLM 호출.getSecret / resolveApiKey
getSecret(key) read-only secret 조회. resolveApiKey({ purpose: 'llm'|'stt'|'embedding'|'vision', vendor? }) 키 vendor 협상.openAuthWindow / openAuthPartitionViewer
OAuth/세션 추출용 Electron BrowserWindow. cookie harvester 포함. lge-api / ms-graph 가 사용.
triggerConversation / showOverlay
채팅 본문에 proactive conversation spawn / overlay UI. work-assistant 의 proactive trigger가 사용.
agentApproval
위험 액션 표준 경로.
request({ toolName, args, reason, scope }) → Promise<ApprovalChoice>. respond(requestId, choice, nonce?, hmac?).logEvent / onShutdown
logEvent(level, message, data?) 구조화 로그. onShutdown(handler) shutdown 훅.getInstalledPluginIds / onPluginsChanged
다른 plugin lifecycle 관찰. plugin 간 의존 (
pluginAccess) 시 사용.openExternalUrl? / getAppPreference?
optional. shell.openExternal / 호스트 prefs 읽기.
존재하지 않는 메서드
다음 메서드는 SDK 표면에 없습니다:
registerTool, registerSkill, registerCommand,addTask, saveNote, saveFile, setSecret, readFile/writeFile (storage.* 사용),enqueueMessage, openCard, registerPluginUI. 도구/UI/이벤트는 manifest 정적 선언.PluginRuntimeContext
// lvis-plugin-sdk/src/index.ts:1001
export interface PluginRuntimeContext {
pluginId: string;
pluginRoot: string; // dist root
hostRoot: string; // host app root
pluginDataDir: string; // ~/.lvis/plugins/<id>/
config?: Record<string, unknown>;
log: (message: string, meta?: unknown) => void;
hostApi: PluginHostApi;
}Lifecycle — start / stop 둘 뿐
// lvis-plugin-sdk/src/index.ts:1036
export interface RuntimePlugin {
start?: () => Promise<void> | void;
stop?: () => Promise<void> | void;
handlers: Record<string, PluginToolHandler>; // 도구명 -> 실행 함수
}
// Factory
export type RuntimePluginFactory =
(context: PluginRuntimeContext) => Promise<RuntimePlugin> | RuntimePlugin;onInstall/onActivate/onUninstall 같은 플러그인-측 hook은 없습니다. install/uninstall 은 hostApi.onPluginsChanged 로 관찰 만 가능합니다.
plugin.json 의 핵심 필드
- Required:
id,name,version,entry,tools,description. - tools:
string[]— 도구 이름. 정규식^[a-zA-Z_][a-zA-Z0-9_]*$, maxLength 64. 도트/하이픈 금지 (vendor 요구). - toolSchemas: 도구별
{ description, category: "read"|"write"|"shell"|"network", pathFields?, writesToOwnSandbox?, version?, inputSchema }. - capabilities: 닫힌 enum 12종 —
ms-graph-consumer,external-auth-consumer,mail-source,calendar-source,routine-provider,meeting-recorder,knowledge-index,background-watcher,worker-client,document-indexer,lifecycle-observer,host:overlay. - keywords, eventSubscriptions, emittedEvents, ui[], auth, pluginAccess, dependencies, configSchema, hostSecrets, llmKeySource, installPolicy, publisher.
실제 사용 예 — work-assistant 가 ms-graph 호출
// lvis-plugin-work-assistant/src/hostPlugin.ts:204
const today = await context.hostApi.callTool("msgraph_calendar_today", {});
// :753 — 미팅 종료 이벤트 구독
context.hostApi.onEvent("meeting.ended", (data) => { ... });
// :441 — 자기 알림 emit
context.hostApi.emitEvent(notificationEventForIntent(intent), payload);HostApi 외 import 금지
플러그인 코드가
lvis-app/* 의 호스트 내부 모듈을 직접 import 하는 것은 SDK type-only 강제 + CI 단계에서 차단됩니다. 통합은 항상 hostApi 호출 + manifest 선언으로만.