Get started

Canvas Plugin 리팩터링

Canvas Plugin 리팩터링

Canvas는 사용 빈도가 낮고 실험적입니다. 핵심 기능이 아니라 번들 Plugin으로 취급하세요. 코어는 일반 Gateway, Node, HTTP, 인증, config, 네이티브 클라이언트 배관을 유지할 수 있지만, Canvas 전용 동작은 extensions/canvas 아래에 있어야 합니다.

목표

현재의 페어링된 Node 동작을 유지하면서 Canvas 소유권을 extensions/canvas로 옮깁니다.

  • 에이전트 대상 canvas 도구는 Canvas Plugin이 등록합니다
  • Canvas Node 명령은 Canvas Plugin이 등록할 때만 허용됩니다
  • A2UI 호스트/소스 파일은 Canvas Plugin 아래에 있습니다
  • Canvas 문서 구체화는 Canvas Plugin 아래에 있습니다
  • CLI 명령 구현은 Canvas Plugin 아래에 있거나, Plugin 소유 런타임 배럴을 통해 위임합니다
  • 문서와 Plugin 인벤토리는 Canvas를 실험적이며 Plugin 기반이라고 설명합니다

비목표

  • 이 리팩터링에서 네이티브 앱 Canvas UI를 다시 설계하지 않습니다.
  • 별도 제품 결정에서 Canvas를 삭제해야 한다고 하지 않는 한 iOS, Android, macOS에서 Canvas 프로토콜/클라이언트 지원을 제거하지 않습니다.
  • 같은 seam이 필요한 다른 번들 Plugin이 하나 이상 있지 않은 한, Canvas만을 위해 광범위한 Plugin 서비스 프레임워크를 만들지 않습니다.

현재 브랜치 상태

완료됨:

  • extensions/canvas에 번들 Plugin 패키지를 추가했습니다.
  • extensions/canvas/openclaw.plugin.json을 추가했습니다.
  • 에이전트 canvas 도구를 src/agents/tools/canvas-tool.ts에서 extensions/canvas/src/tool.ts로 옮겼습니다.
  • src/agents/openclaw-tools.ts에서 createCanvasTool의 코어 등록을 제거했습니다.
  • Canvas 호스트 구현을 src/canvas-host에서 extensions/canvas/src/host로 옮겼습니다.
  • 테스트, 패키징, 외부 공개 Canvas 헬퍼를 위한 Plugin 소유 호환성 배럴로 extensions/canvas/runtime-api.ts를 유지했습니다.
  • Canvas 문서 구체화를 src/gateway/canvas-documents.ts에서 extensions/canvas/src/documents.ts로 옮겼습니다.
  • Canvas CLI 구현과 A2UI JSONL 헬퍼를 extensions/canvas/src/cli.ts로 옮겼습니다.
  • Canvas 호스트 URL과 범위 지정 capability 헬퍼를 extensions/canvas/src로 옮겼습니다.
  • Canvas Node 명령 기본값을 하드코딩된 코어 목록에서 Plugin nodeInvokePolicies로 옮겼습니다.
  • plugins.entries.canvas.config.host에 Plugin 소유 Canvas 호스트 config를 추가했습니다.
  • Canvas 및 A2UI HTTP 서빙을 Canvas Plugin HTTP 라우트 등록 뒤로 옮겼습니다.
  • Plugin 소유 HTTP 라우트를 위한 일반 Plugin WebSocket 업그레이드 디스패치를 추가했습니다.
  • Canvas 전용 Gateway 호스트 URL과 Node capability 인증을 일반 호스팅 Plugin 표면 및 Node capability 헬퍼로 대체했습니다.
  • Plugin 소유 호스팅 미디어 리졸버를 추가해, 코어가 Canvas 문서 내부 구현을 가져오는 대신 Canvas 문서 URL이 Canvas Plugin을 통해 해석되도록 했습니다.
  • Canvas가 부모 명령 경로를 수동으로 쓰지 않고 openclaw nodes canvas를 Plugin 소유 Node 기능으로 선언할 수 있도록 api.registerNodeCliFeature(...)를 추가했습니다.
  • extensions/canvas/runtime-api.js의 프로덕션 src/** import를 제거했습니다.
  • A2UI 번들 소스를 apps/shared/OpenClawKit/Tools/CanvasA2UI에서 extensions/canvas/src/host/a2ui-app로 옮겼습니다.
  • A2UI 빌드/복사 구현을 extensions/canvas/scripts 아래로 옮기고 루트 빌드 배선을 일반 번들 Plugin 애셋 훅으로 대체했습니다.
  • 런타임 레거시 최상위 canvasHost config 별칭을 제거했습니다.
  • openclaw doctor --fix가 이전 canvasHost config를 plugins.entries.canvas.config.host로 다시 쓰도록 Canvas doctor 마이그레이션은 유지했습니다.
  • Gateway 프로토콜 v4 뒤의 이전 에이전트 Canvas 프로토콜 호환성을 제거했습니다. 네이티브 클라이언트와 Gateway는 이제 pluginSurfaceUrls.canvasnode.pluginSurface.refresh만 사용합니다. 더 이상 사용되지 않는 canvasHostUrl, canvasCapability, node.canvas.capability.refresh 경로는 이 실험적 리팩터링에서 의도적으로 지원되지 않습니다.
  • 생성된 Plugin 인벤토리를 업데이트해 Canvas를 포함했습니다.
  • docs/plugins/reference/canvas.md에 Plugin 참조 문서를 추가했습니다.

남아 있는 것으로 알려진 코어 소유 Canvas 표면:

  • apps/ 아래의 네이티브 앱 Canvas 핸들러는 여전히 의도적으로 Canvas Plugin 표면을 소비합니다
  • apps/ 아래의 네이티브 앱 Canvas 프로토콜/클라이언트 핸들러
  • 게시된 아티팩트 출력은 이전 버전과 호환되는 런타임 조회를 위해 여전히 dist/canvas-host/a2ui를 사용하지만, 복사 단계는 이제 Plugin 소유입니다

목표 형태

extensions/canvas가 소유해야 하는 항목:

  • Plugin 매니페스트와 패키지 메타데이터
  • 에이전트 도구 등록
  • Node invoke 명령 정책
  • Canvas 호스트와 A2UI 런타임
  • Canvas A2UI 번들 소스와 애셋 빌드/복사 스크립트
  • Canvas 문서 생성과 애셋 해석
  • Canvas CLI 구현
  • Canvas 문서 페이지와 Plugin 인벤토리 항목

코어는 일반 seam만 소유해야 합니다.

  • Plugin 발견 및 등록
  • 일반 에이전트 도구 레지스트리
  • 일반 Node invoke 정책 레지스트리
  • 일반 Gateway HTTP/인증 및 WebSocket 업그레이드 디스패치
  • 일반 호스팅 Plugin 표면 URL 해석
  • 일반 호스팅 미디어 리졸버 등록
  • 일반 Node capability 전송
  • 일반 config 배관
  • 일반 번들 Plugin 애셋 훅 발견

네이티브 앱은 프로토콜의 클라이언트로서 Canvas 명령 핸들러를 유지할 수 있습니다. 네이티브 앱은 Plugin 런타임 소유자가 아닙니다.

마이그레이션 단계

  1. plugins.entries.canvas.config.host를 Plugin 소유 config 표면으로 취급합니다.
  2. 문서를 업데이트해 Canvas를 실험적 번들 Plugin으로 설명합니다.
  3. 집중 Canvas 테스트, Plugin 인벤토리 검사, Plugin SDK API 검사, 런타임 경계의 영향을 받는 빌드/타입 게이트를 실행합니다.

감사 체크리스트

리팩터링이 완료되었다고 하기 전에:

  • rg "src/canvas-host|../canvas-host"가 살아 있는 소스 import를 반환하지 않습니다.
  • rg "canvas-tool|createCanvasTool" src가 코어 소유 Canvas 도구 구현을 찾지 않습니다.
  • rg "canvas.present|canvas.snapshot|canvas.a2ui" src/gateway가 일반 Plugin 정책 테스트 밖에서 하드코딩된 허용 목록 기본값을 찾지 않습니다.
  • rg "extensions/canvas/runtime-api" src --glob '!**/*.test.ts'가 비어 있습니다.
  • rg "canvas-documents" src가 비어 있습니다.
  • rg "registerNodesCanvasCommands|nodes-canvas" src가 비어 있습니다. Canvas Plugin은 중첩 Plugin CLI 메타데이터를 통해 openclaw nodes canvas를 등록합니다.
  • rg "createCanvasHostHandler|handleA2uiHttpRequest" src/gateway가 Gateway 런타임 소유권을 반환하지 않습니다.
  • rg "apps/shared/OpenClawKit/Tools/CanvasA2UI|canvas-a2ui-copy|extensions/canvas/src/host/a2ui" scripts .github package.json가 호환성 래퍼 또는 Plugin 소유 경로만 찾습니다.
  • pnpm plugins:inventory:check가 통과합니다.
  • pnpm plugin-sdk:api:check가 통과하거나, 생성된 API 기준선이 의도적으로 업데이트되고 검토됩니다.
  • 대상 Canvas 테스트가 통과합니다.
  • Canvas 호스트/A2UI 경로에 대한 변경 레인 테스트가 통과합니다.
  • PR 본문에 Canvas가 실험적이며 Plugin 기반이라고 명시적으로 적혀 있습니다.

검증 명령

반복 작업 중에는 대상 로컬 검사를 사용하세요.

pnpm test extensions/canvas/src/host/server.test.ts extensions/canvas/src/host/server.state-dir.test.ts extensions/canvas/src/host/file-resolver.test.ts
pnpm test src/gateway/server.plugin-node-capability-auth.test.ts src/gateway/server-import-boundary.test.ts
pnpm test extensions/canvas/src/config-migration.test.ts src/commands/doctor-legacy-config.migrations.test.ts
pnpm test test/scripts/changed-lanes.test.ts test/scripts/build-all.test.ts extensions/canvas/scripts/bundle-a2ui.test.ts test/scripts/bundled-plugin-assets.test.ts extensions/canvas/scripts/copy-a2ui.test.ts src/infra/run-node.test.ts
pnpm tsgo:extensions
pnpm plugins:inventory:check
pnpm plugin-sdk:api:check

런타임 배럴, lazy import, 패키징, 게시된 Plugin 표면이 변경되면 push 전에 pnpm build를 실행하세요.