@morit/cli 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +53 -18
  2. package/assets/docs/README.md +105 -0
  3. package/assets/docs/ai-response-and-timeline.md +125 -0
  4. package/assets/docs/ai-skill-and-docx-workflow.md +83 -0
  5. package/assets/docs/app-builder.md +56 -0
  6. package/assets/docs/authentication.md +140 -0
  7. package/assets/docs/components.md +159 -0
  8. package/assets/docs/design-tokens-responsive.md +148 -0
  9. package/assets/docs/docs-index.json +93 -0
  10. package/assets/docs/examples-notion.md +83 -0
  11. package/assets/docs/examples-school-life.md +74 -0
  12. package/assets/docs/getting-started.md +132 -0
  13. package/assets/docs/information-hierarchy.md +81 -0
  14. package/assets/docs/instances-and-connectors.md +93 -0
  15. package/assets/docs/lifecycle-and-api.md +158 -0
  16. package/assets/docs/local-cli.md +124 -0
  17. package/assets/docs/manifest.md +208 -0
  18. package/assets/docs/packaging-and-testing.md +115 -0
  19. package/assets/docs/permissions-and-data.md +131 -0
  20. package/assets/docs/platform-compatibility.md +62 -0
  21. package/assets/docs/project-structure.md +102 -0
  22. package/assets/docs/remote-mcp.md +152 -0
  23. package/assets/docs/school-life-privacy.md +49 -0
  24. package/assets/docs/screens-layout-navigation.md +95 -0
  25. package/assets/docs/sdk-and-mcp.md +182 -0
  26. package/assets/docs/tool-and-skill.md +163 -0
  27. package/assets/docs/troubleshooting.md +117 -0
  28. package/assets/docs/ui-extensions.md +70 -0
  29. package/assets/docs/ui-runtime-v2.md +273 -0
  30. package/assets/docs/verification.md +128 -0
  31. package/assets/plugin_contract.json +222 -1
  32. package/package.json +1 -1
  33. package/src/cli.js +27 -3
  34. package/src/secure-store.js +113 -43
  35. package/src/workspace.js +297 -86
package/README.md CHANGED
@@ -1,26 +1,61 @@
1
1
  # @morit/cli
2
2
 
3
- Official Morit Developer CLI. It owns the shared Plugin SDK, local/Cloud project
4
- linking, authenticated synchronization, builds, and deployments. The separate
5
- `@morit/plugin-mcp` package exposes these capabilities to AI clients over MCP.
3
+ Morit Plugin의 공식 개발 CLI입니다. 프로젝트 scaffold, Cloud 연결·동기화, 계약 검증, Ed25519
4
+ 서명 package build, immutable Deployment 배포를 같은 SDK로 처리합니다.
5
+
6
+ ## 빠른 시작
7
+
8
+ Node.js 20.12 이상에서 실행합니다.
6
9
 
7
10
  ```powershell
8
- npx @morit/cli login
9
- npx @morit/cli plugin setup . --id com.example.plugin --name Example --publisher example
10
- npx @morit/cli plugin add .
11
- npx @morit/cli plugin validate .
12
- npx @morit/cli plugin build .
13
- npx @morit/cli plugin deploy . --visibility private
11
+ npx -y @morit/cli login
12
+ npx -y @morit/cli plugin setup . --id com.example.study --name "Study" --publisher example
13
+ npx -y @morit/cli plugin add .
14
+ npx -y @morit/cli plugin validate .
15
+ npx -y @morit/cli plugin build .
16
+ npx -y @morit/cli plugin deploy . --visibility private
14
17
  ```
15
18
 
16
- `morit login` uses a browser device flow. Windows credentials are protected with
17
- DPAPI, macOS uses Keychain, and Linux uses Secret Service through `secret-tool`.
18
- CI can supply `MORIT_ACCESS_TOKEN` without writing it to disk.
19
+ CLI에 별도 `plugin preview` 명령은 없습니다. UI preview는 `@morit/cli/sdk`의
20
+ `previewProjectDirectory`, `@morit/plugin-mcp`의 `morit_project_preview`, 또는 저장소 도구인
21
+ `python tools/morit_plugin.py preview <directory>`를 사용합니다.
22
+
23
+ 전체 명령은 `npx -y @morit/cli --help`에서 확인하고 자동화에서는 `--json`을 사용합니다.
24
+
25
+ ## 프로젝트와 인증
26
+
27
+ `morit login`은 브라우저 device flow를 사용합니다. Windows는 DPAPI, macOS는 Keychain,
28
+ Linux는 Secret Service를 통해 credential을 보호합니다. CI는 파일에 저장하지 않고
29
+ `MORIT_ACCESS_TOKEN`을 전달할 수 있습니다.
30
+
31
+ Cloud 연결 정보는 프로젝트 root의 `morit-plugin.json`에 기록됩니다. 이 파일에는 Project UUID와
32
+ revision만 들어가며 credential이나 Secret 평문은 포함되지 않습니다. Project source와 `.mplg`도
33
+ Secret identifier만 선언하고 실제 값은 Morit Cloud Project Secrets에 저장합니다.
34
+
35
+ 동기화는 revision 충돌을 검사합니다.
36
+
37
+ ```powershell
38
+ npx -y @morit/cli plugin sync . # local → Cloud
39
+ npx -y @morit/cli plugin sync . --pull # Cloud → local
40
+ ```
41
+
42
+ `--force` pull은 로컬 변경을 교체하므로 변경 내용을 확인한 뒤 명시적으로 사용합니다.
43
+
44
+ ## SDK와 문서
45
+
46
+ Node SDK는 `@morit/cli/sdk` export에서 같은 contract, validator, preview, build 기능을 제공합니다.
47
+ 배포 package의 `assets/plugin_contract.json`과 `assets/docs/`에는 빌드 시점의 공통 계약과 공식
48
+ 개발 문서가 함께 들어갑니다. 문서는 `assets/docs/README.md`의 실제 개발 순서를 따라 읽습니다.
49
+
50
+ Plugin MCP 설정과 Local/Remote Tool 흐름은 bundled `assets/docs/sdk-and-mcp.md`, manifest와 UI
51
+ 계약은 `assets/docs/manifest.md`, `assets/docs/ui-runtime-v2.md`를 참고하세요.
52
+
53
+ ## 파일과 보안 경계
19
54
 
20
- The local link is stored in `morit-plugin.json`; it contains project identifiers
21
- and a revision only, never credentials. Project source and `.mplg` files also
22
- contain secret identifiers only. Values belong in Morit Cloud Project Secrets.
55
+ PNG/JPEG/WebP/GIF asset과 직접 `children/*.mplg` dependency는 JSON Cloud 경계를 건널 때만
56
+ 검증된 binary envelope로 변환되며 pull, source ZIP, 최종 `.mplg`에서는 원본 바이트가 유지됩니다.
57
+ 밖의 source는 UTF-8 text입니다.
23
58
 
24
- Binary PNG/JPEG/WebP/GIF assets and direct `children/*.mplg` dependencies are
25
- encoded only while crossing the JSON Cloud boundary and are restored byte for
26
- byte on pull, source ZIP, and build. Other source remains strict UTF-8.
59
+ build는 manifest와 source 구조를 검증하고 package에 검증용 public key와 signature를 포함합니다.
60
+ publisher private key, OS credential, Secret 평문은 source, source ZIP, package, CLI JSON 응답에
61
+ 포함하지 않습니다.
@@ -0,0 +1,105 @@
1
+ # Morit Plugin 개발 문서
2
+
3
+ 이 문서는 아이디어를 실제 설치 가능한 `.mplg`로 만드는 순서대로 구성되어 있습니다. 플러그인은
4
+ 실행 코드를 앱에 직접 주입하지 않습니다. Manifest와 JSON fragment로 기능·화면·권한을 선언하고,
5
+ Morit Host가 서명과 계약을 확인한 뒤 공용 런타임으로 실행합니다.
6
+
7
+ ## 가장 짧은 개발 경로
8
+
9
+ ```text
10
+ 요구사항 정리
11
+ → 프로젝트 생성
12
+ → manifest와 기능 fragment 작성
13
+ → 화면·상태·내비게이션 작성
14
+ → 권한·설정·알림 연결
15
+ → validate
16
+ → preview
17
+ → build
18
+ → verify
19
+ → 실제 앱 설치·실사용 테스트
20
+ → deploy
21
+ ```
22
+
23
+ 공식 CLI를 사용하는 기본 명령은 다음과 같습니다.
24
+
25
+ ```bash
26
+ npx -y @morit/cli plugin setup . \
27
+ --id com.example.study \
28
+ --name "Study" \
29
+ --publisher example
30
+ npx -y @morit/cli plugin validate .
31
+ npx -y @morit/cli plugin build .
32
+ npx -y @morit/cli login
33
+ npx -y @morit/cli plugin add .
34
+ npx -y @morit/cli plugin deploy . --visibility private
35
+ ```
36
+
37
+ 화면 preview는 Local/Remote Plugin MCP의 `morit_project_preview` 또는 저장소 도구
38
+ `python tools/morit_plugin.py preview <project>`를 사용합니다. preview HTML은 구조를 점검하는
39
+ 보조 수단이며, 실제 Host 렌더링과 사용자 흐름은 앱에서 따로 확인해야 합니다.
40
+
41
+ ## 개발 순서별 인덱스
42
+
43
+ ### 1. 시작과 개발 흐름
44
+
45
+ 1. [시작하기와 개발 흐름](getting-started.md) — 개발 방식 선택, 첫 프로젝트, 완료 조건
46
+ 2. [앱에서 플러그인 만들기](app-builder.md) — 단순 Tool을 앱 Builder로 만드는 범위
47
+
48
+ ### 2. 프로젝트와 계약
49
+
50
+ 3. [프로젝트 구조와 fragment](project-structure.md) — 디렉터리, 병합 규칙, 패키지 포함 파일
51
+ 4. [Manifest 레퍼런스](manifest.md) — ID, 버전, capability, connector, dependency
52
+ 5. [Instance, Connector, 복합 패키지](instances-and-connectors.md) — 사용자별 실행 단위와 계정 연결
53
+
54
+ ### 3. 화면과 사용자 경험
55
+
56
+ 6. [화면, 레이아웃, 내비게이션](screens-layout-navigation.md) — 화면 역할과 이동 구조
57
+ 7. [기본·커스텀 컴포넌트](components.md) — 각 node의 목적, 속성, 제약
58
+ 8. [토큰, 크기, 색, 여백, 반응형](design-tokens-responsive.md) — Material 3 기반 시각 규칙
59
+ 9. [화면 분리와 정보 계층](information-hierarchy.md) — 한 화면에 정보를 몰지 않는 설계
60
+ 10. [UI extension point](ui-extensions.md) — Host의 어느 위치에 UI를 노출할지 선택
61
+ 11. [UI Runtime v2 레퍼런스](ui-runtime-v2.md) — data, state, binding, event 계약
62
+ 12. [Response UI와 Agent Timeline](ai-response-and-timeline.md) — AI 메시지 안의 결과 UI
63
+
64
+ ### 4. 기능, 데이터, 사용자 제어
65
+
66
+ 13. [Tool, Skill, Search, Slash Command](tool-and-skill.md) — capability와 runtime adapter
67
+ 14. [권한, 설정, 저장소, 알림](permissions-and-data.md) — 최소 권한과 Host action
68
+ 15. [외부 서비스 인증과 Cloud Secrets](authentication.md) — OAuth, API key, 비밀 값 경계
69
+ 16. [AI Skill과 파일 산출물](ai-skill-and-docx-workflow.md) — 실제 파일을 반환하는 완료 흐름
70
+
71
+ ### 5. 플랫폼
72
+
73
+ 17. [Android, iOS, Desktop 호환](platform-compatibility.md) — 현재 지원 범위와 이식 원칙
74
+
75
+ ### 6. 검증과 배포
76
+
77
+ 18. [공식 CLI 개발 흐름](local-cli.md) — setup, sync, build, deploy
78
+ 19. [패키징과 테스트](packaging-and-testing.md) — 서명, 무결성, 테스트 층
79
+ 20. [오류 해결](troubleshooting.md) — validate, preview, build, 설치, 실행 오류 구분
80
+ 21. [예제 검증 방법과 확인 경계](verification.md) — 자동·live·실기기 검증의 차이
81
+
82
+ ### 7. SDK, MCP, API
83
+
84
+ 22. [CLI와 AI 에이전트 MCP](sdk-and-mcp.md) — Codex·Claude 등 로컬/원격 연결
85
+ 23. [원격 Plugin MCP](remote-mcp.md) — Cloud Project를 다루는 Tool 계약
86
+ 24. [수명주기와 HTTP API](lifecycle-and-api.md) — Host API와 설치·실행 상태
87
+
88
+ ### 8. 실제 예제
89
+
90
+ 25. [학교 생활 플러그인](examples-school-life.md) — 공개 NEIS를 사용하는 학생용 경험
91
+ 26. [학교 생활 개인정보 처리](school-life-privacy.md) — 저장·전송·삭제 범위
92
+ 27. [Notion 플러그인](examples-notion.md) — OAuth Connection과 실제 문서 검색
93
+
94
+ ## 문서가 배포되는 위치
95
+
96
+ `docs/plugin_docs`가 문서 콘텐츠의 공통 원본입니다. 같은 파일이 다음 위치에 복사되거나 빌드 시
97
+ 포함됩니다.
98
+
99
+ - `@morit/cli`: npm 패키지의 `assets/docs`
100
+ - Local MCP `@morit/plugin-mcp`: npm 패키지의 `assets/docs`
101
+ - Remote MCP: 서비스 이미지의 `/app/docs/plugin_docs`
102
+ - `developers.moring.co`: `docs-index.json`의 순서와 경로로 생성한 MDX
103
+
104
+ 네 배포 위치는 같은 계약과 예제를 제공하며, 문서의 JSON은 공식 CLI와 Host 계약으로 지속
105
+ 검증합니다. 각 경로에서 별도 규격을 정의하지 않습니다.
@@ -0,0 +1,125 @@
1
+ # Response UI와 Agent Timeline
2
+
3
+ Response UI는 capability 결과를 AI 메시지 안에서 시각화합니다. 대화 아래의 별도 카드 목록이나
4
+ overflow 메뉴 밑에 떨어뜨리지 않고, 해당 AI 메시지에 속한 하나의 response container 안에
5
+ 제목·내용·행동을 함께 렌더링합니다.
6
+
7
+ ## Response extension
8
+
9
+ ```json
10
+ {
11
+ "id": "school_life.day_response",
12
+ "point": "response",
13
+ "title": "오늘 학교 생활",
14
+ "order": 10,
15
+ "permissions": [],
16
+ "config": {
17
+ "ui_schema": 2,
18
+ "theme": {"density": "compact"},
19
+ "data_sources": [
20
+ {
21
+ "id": "result",
22
+ "capability": "school_life.schedule.lookup",
23
+ "trigger": "manual",
24
+ "query": "",
25
+ "arguments": {}
26
+ }
27
+ ],
28
+ "view": {
29
+ "type": "column",
30
+ "props": {"spacing": 10},
31
+ "children": [
32
+ {
33
+ "type": "text",
34
+ "props": {"text": "{{data.result.summary}}", "style": "heading"}
35
+ },
36
+ {
37
+ "type": "timeline",
38
+ "props": {"source": "data.result.data.timetable", "empty_text": "수업 정보가 없습니다."},
39
+ "children": [
40
+ {
41
+ "type": "text",
42
+ "props": {"text": "{{item.period}}교시 · {{item.subject}}"}
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ Response point도 일반 Runtime v2와 같은 node, theme, app bar, navigation, binding, action 계약을
53
+ 사용합니다. AI가 실행한 capability 결과는 `data` namespace에 주입되므로 같은 결과를 얻기 위해
54
+ 화면 mount 시 외부 요청을 반복하지 않습니다. 후속 refresh나 사용자가 누른 action에만 추가
55
+ capability를 호출합니다.
56
+
57
+ ## 한 container의 정보 순서
58
+
59
+ 1. 결과를 설명하는 짧은 제목 또는 summary
60
+ 2. 사용자가 요청한 핵심 데이터
61
+ 3. 출처·기간·갱신 시점 같은 보조 정보
62
+ 4. 필요한 후속 행동 1~2개
63
+
64
+ 복사, 다시 시도, 다운로드 같은 메뉴도 같은 container의 Host chrome에 속합니다. 각 section을
65
+ 별도 떠 있는 card로 만들지 않습니다. 긴 결과는 list limit과 상세 화면 이동을 사용합니다.
66
+
67
+ ## 대화 UI를 깨뜨리지 않는 제약
68
+
69
+ - 메시지 폭을 넘는 고정 width를 사용하지 않습니다.
70
+ - Response 내부에 자체 채팅 입력창을 만들지 않습니다.
71
+ - 무한 높이 목록 대신 요약과 상세 화면 이동을 제공합니다.
72
+ - background refresh가 대화 scroll 위치를 바꾸지 않게 기존 높이와 데이터를 가능한 유지합니다.
73
+ - 렌더링 오류는 해당 Response만 텍스트 fallback으로 바꾸고 대화 전체를 종료하지 않습니다.
74
+ - accessibility 순서는 AI 본문 다음, Response 제목, 내용, 행동 순으로 유지합니다.
75
+
76
+ ## Text fallback
77
+
78
+ Response UI를 표시할 수 없더라도 AI의 원래 텍스트 답변은 남아야 합니다. capability는 UI 전용
79
+ 원시 JSON만 반환하지 말고 사람이 읽을 수 있는 `summary`를 함께 제공합니다. 알 수 없는 node,
80
+ 손상된 binding, 이미지 로드 실패는 전체 답변 성공을 숨기지 않습니다.
81
+
82
+ ```json
83
+ {
84
+ "completed": true,
85
+ "summary": "오늘은 6교시이며 점심은 카레라이스입니다.",
86
+ "data": {
87
+ "timetable": [],
88
+ "meal": {}
89
+ },
90
+ "evidence": []
91
+ }
92
+ ```
93
+
94
+ ## Agent Timeline
95
+
96
+ Agent Timeline은 모델의 숨겨진 추론이 아니라 사용자가 이해할 수 있는 실행 상태만 보여줍니다.
97
+
98
+ ```text
99
+ 요청을 확인하고 계획했어요
100
+ ├─ 학교 정보를 확인했어요
101
+ ├─ 오늘 시간표를 불러왔어요
102
+ └─ 결과를 정리했어요
103
+ ```
104
+
105
+ Timeline event에는 도구 이름이나 내부 stack trace 대신 작업 이름, 상태, 필요한 사용자 행동을
106
+ 기록합니다. 상태는 대체로 다음과 같습니다.
107
+
108
+ - 진행 중
109
+ - 완료
110
+ - 재시도 중
111
+ - 사용자 입력 필요
112
+ - 일부 결과로 완료
113
+ - 실패
114
+
115
+ Tool 하나가 실패했지만 대체 경로로 결과를 만들었다면 전체 timeline을 실패로 표시하지 않습니다.
116
+ 실패한 단계와 복구 결과를 함께 설명합니다.
117
+
118
+ ## Code Interpreter 파일
119
+
120
+ Code Interpreter가 파일을 만들었다면 sandbox 내부 경로만 답변에 남기지 않습니다. 파일은 Host가
121
+ 접근 가능한 artifact로 전달되고, AI 답변에는 실제 파일 이름·형식·크기와 다운로드 가능한 링크가
122
+ 있어야 합니다. Response UI의 다운로드 action은 동일 artifact를 가리키며 존재하지 않는 경로나
123
+ 가짜 링크를 생성하지 않습니다.
124
+
125
+ 파일 생성 완료 조건은 [AI Skill과 파일 산출물](ai-skill-and-docx-workflow.md)에 정리되어 있습니다.
@@ -0,0 +1,83 @@
1
+ # AI Skill과 파일 산출물
2
+
3
+ Morit Plugin의 `kind: "skill"`과 Codex·Claude 같은 AI 에이전트의 Skill 파일은 이름이 같지만
4
+ 역할이 다릅니다.
5
+
6
+ - Morit Plugin Skill: Host가 설치·권한·timeout 경계에서 실행하는 capability
7
+ - AI 에이전트 Skill: 에이전트가 작업 순서와 도구 사용법을 따르는 지침
8
+
9
+ 에이전트 Skill을 사용해 플러그인을 만들더라도 결과 `.mplg`는 일반 SDK 계약과 동일하게 검증·서명됩니다.
10
+
11
+ ## 파일 작업 완료 순서
12
+
13
+ 문서, 표, 이미지, archive 등 파일을 만드는 capability나 AI 작업은 다음 순서를 지킵니다.
14
+
15
+ ```text
16
+ 요청과 입력 조사
17
+ → 파일 생성·편집
18
+ → 구조 검증
19
+ → 가능하면 시각 검증
20
+ → 원래 parser로 다시 열고 내용 확인
21
+ → 최종 artifact 저장
22
+ → 사용자에게 실제 링크와 요약 반환
23
+ ```
24
+
25
+ “작업을 마쳤어요”만 답하거나 sandbox 내부 경로만 남기면 완료가 아닙니다. 존재하지 않는 `.docx`
26
+ 경로나 아직 생성하지 않은 artifact를 최종 링크처럼 반환하지 않습니다.
27
+
28
+ ## DOCX 예
29
+
30
+ DOCX는 Microsoft Open XML ZIP 구조입니다. 단순히 `.docx` 확장자를 붙이지 않습니다.
31
+
32
+ 1. 요청한 목차, 표, 이미지, 스타일을 실제로 작성합니다.
33
+ 2. ZIP entry와 `[Content_Types].xml`, document relationship을 확인합니다.
34
+ 3. DOCX parser로 다시 열어 문단·표·이미지 수를 확인합니다.
35
+ 4. 모든 페이지를 렌더링해 잘림, 빈 페이지, 겹침, 깨진 한글을 확인합니다.
36
+ 5. 수정 후 parser와 렌더링을 다시 실행합니다.
37
+ 6. 존재하고 0바이트가 아닌 최종 `.docx`만 artifact로 반환합니다.
38
+
39
+ 다른 형식도 동일합니다. PDF는 모든 페이지, spreadsheet는 수식과 셀 type, image는 실제 크기와
40
+ 디코딩, ZIP은 entry 경로와 traversal 안전성을 확인합니다.
41
+
42
+ ## Plugin이 파일을 만드는 경우
43
+
44
+ Plugin은 `file_write` 권한을 요청하고 Host가 제공하는 artifact 경계로 결과를 전달합니다.
45
+ `sandbox_python` 응답의 `summary`, `data`, `evidence`에 존재하지 않는 `sandbox:/...` 링크를 만들지
46
+ 않습니다. Host로 복사된 artifact ID나 내부 파일 링크 문법을 사용하고, AI 최종 답변에는 다음을
47
+ 포함합니다.
48
+
49
+ - 파일 이름과 형식
50
+ - 사용자가 요청한 결과 요약
51
+ - 다운로드 또는 앱 내부 미리보기 링크
52
+ - 검증한 항목과 남은 제한
53
+
54
+ ## `.mplg` 산출물
55
+
56
+ 플러그인 개발도 파일 작업입니다.
57
+
58
+ ```text
59
+ source 작성
60
+ → validate
61
+ → UI가 있으면 preview
62
+ → signed build
63
+ → package reopen·signature verify
64
+ → 앱 설치·실행
65
+ → 요청한 경로에 artifact 반환
66
+ ```
67
+
68
+ source ZIP과 `.mplg`는 목적이 다릅니다. source ZIP은 편집용이고 `.mplg`는 설치용입니다. 둘을 같은
69
+ 다운로드 이름이나 MIME으로 반환하지 않습니다.
70
+
71
+ 플러그인의 기본 품질 게이트는 `validate → preview → build → verify`입니다. UI가 없는 package는
72
+ preview를 생략할 수 있지만 생략 이유를 결과에 기록합니다.
73
+
74
+ ## 완료 체크
75
+
76
+ - 요청한 모든 파일이 실제로 존재하는가
77
+ - 파일 크기가 0보다 큰가
78
+ - 확장자와 내부 형식이 일치하는가
79
+ - 원래 parser로 다시 열리는가
80
+ - 모든 페이지/시트/entry를 확인했는가
81
+ - 미리보기와 다운로드가 같은 최종 artifact를 가리키는가
82
+ - sandbox 경로나 secret이 사용자 응답에 남지 않았는가
83
+ - 실패한 Tool이 있다면 복구·대체 결과와 제한을 설명했는가
@@ -0,0 +1,56 @@
1
+ # 앱에서 플러그인 만들기
2
+
3
+ Plugin Builder는 개발 환경 없이 작은 Tool 또는 Skill을 만드는 경로입니다. Builder도 일반
4
+ `.mplg`를 생성하고 같은 설치·권한·삭제 흐름을 사용하지만, 안전하게 표현할 수 있는 범위를
5
+ 의도적으로 제한합니다.
6
+
7
+ ## 지원 범위
8
+
9
+ | 항목 | 지원 값 |
10
+ |---|---|
11
+ | capability kind | `tool`, `skill` |
12
+ | runtime | `text_stats`, `text_template` |
13
+ | UI 위치 | `card`, `screen`, `settings` 중 최대 3개 |
14
+ | 선택 기능 | Search Provider, Slash Command, background, notification |
15
+ | 권한 | `background`, `notifications` |
16
+ | credential | 최대 4개의 `api_token` 선언(현재 앱 화면은 고급 source 흐름에서 설정) |
17
+ | background 주기 | 15~10,080분 |
18
+
19
+ Builder는 ID, 이름, 설명, version, capability 제목과 runtime 설정을 입력받아 최소 Manifest와
20
+ Runtime v1 UI를 만듭니다. UI 코드를 생성하거나 외부 코드를 실행하지 않습니다.
21
+
22
+ ## Builder가 적합한 예
23
+
24
+ - 입력 텍스트의 길이와 단어 수를 계산하는 Tool
25
+ - 정해진 템플릿으로 회의 메모를 정리하는 Skill
26
+ - settings의 작은 정적 목록을 Search Provider로 노출
27
+ - 정해진 문구를 일정 주기로 알리는 개인 알림
28
+
29
+ ## 소스 프로젝트로 전환할 때
30
+
31
+ 다음 중 하나가 필요하면 공식 CLI 프로젝트를 사용합니다.
32
+
33
+ - Runtime v2의 화면·내비게이션·theme·Response UI
34
+ - 외부 HTTPS API, OAuth, Remote MCP
35
+ - 여러 데이터 source와 동적 목록
36
+ - `sandbox_python` 데이터 처리
37
+ - child package와 dependency
38
+ - Cloud Secret, Connector retry/rate-limit
39
+ - package asset, 이미지, 복수 source file
40
+
41
+ [시작하기](getting-started.md)의 `@morit/cli plugin setup`으로 프로젝트를 만들고 Builder에서 정한
42
+ ID와 capability 이름을 유지하면 사용자가 기능을 다시 익힐 필요가 없습니다. 기존 설치를 업데이트할
43
+ 때는 publisher와 서명 identity, plugin ID, semantic version 순서를 유지해야 합니다.
44
+
45
+ ## 실제 확인
46
+
47
+ Builder 생성 성공만 확인하지 말고 다음을 앱에서 실행합니다.
48
+
49
+ 1. 생성 직후 설치 목록에 나타나는지
50
+ 2. 필요한 권한을 거부했을 때 기능이 실행되지 않는지
51
+ 3. Tool/Skill과 Slash Command가 정확한 capability를 호출하는지
52
+ 4. background/notification을 켜고 끌 수 있는지
53
+ 5. 설정 변경 후 다시 열었을 때 유지되는지
54
+ 6. 삭제 후 pending 알림과 credential이 정리되는지
55
+
56
+ 고급 화면 개발은 [화면, 레이아웃, 내비게이션](screens-layout-navigation.md)부터 시작하세요.
@@ -0,0 +1,140 @@
1
+ # 외부 서비스 인증과 Cloud Secrets
2
+
3
+ 플러그인 source와 `.mplg`에는 비밀 값이 들어가지 않습니다. package는 필요한 Credential·Connector·
4
+ Secret ID만 선언하고 실제 값은 사용자 Connection 또는 조직의 Cloud Secret에 저장합니다.
5
+
6
+ ## 선택 기준
7
+
8
+ | 값의 소유자 | 사용 모델 | 예 |
9
+ |---|---|---|
10
+ | 각 사용자 | Credential + Connection | 개인 Notion OAuth, 개인 API token |
11
+ | Developer 조직 | Cloud Secret | 서버 간 API key, OAuth client secret |
12
+ | 공개 설정 | Connector 또는 manifest | 공개 HTTPS endpoint, provider ID |
13
+
14
+ 사용자 token을 Cloud Secret으로 공유하거나 조직 secret을 Instance settings에 복사하지 않습니다.
15
+
16
+ ## Manifest 선언
17
+
18
+ OAuth 계정:
19
+
20
+ ```json
21
+ {
22
+ "credentials": [
23
+ {
24
+ "id": "notion_account",
25
+ "label": "Notion 계정",
26
+ "description": "공유한 페이지를 읽습니다.",
27
+ "kind": "oauth_access_token",
28
+ "required": true,
29
+ "oauth_provider": "notion",
30
+ "allow_multiple": true
31
+ }
32
+ ],
33
+ "connectors": [
34
+ {
35
+ "id": "notion_cloud",
36
+ "kind": "oauth",
37
+ "label": "Notion Cloud",
38
+ "description": "Notion 공식 OAuth 연결",
39
+ "credential_id": "notion_account",
40
+ "cloud_connection_id": "notion"
41
+ }
42
+ ]
43
+ }
44
+ ```
45
+
46
+ 조직 Secret:
47
+
48
+ ```json
49
+ {
50
+ "cloud_project_id": "00000000-0000-4000-8000-000000000000",
51
+ "required_secrets": [
52
+ {
53
+ "id": "EXAMPLE_API_KEY",
54
+ "label": "Example API key",
55
+ "description": "서버 간 API 호출에 사용합니다.",
56
+ "required": true
57
+ }
58
+ ],
59
+ "connectors": [
60
+ {
61
+ "id": "example_api",
62
+ "kind": "api_key",
63
+ "label": "Example API",
64
+ "description": "조직 API 연결",
65
+ "cloud_secret_id": "EXAMPLE_API_KEY"
66
+ }
67
+ ]
68
+ }
69
+ ```
70
+
71
+ `cloud_project_id`는 Developer Project에 연결할 때 발급된 실제 UUID를 사용합니다.
72
+
73
+ ## Developer Platform 설정
74
+
75
+ 1. 조직을 선택하고 Plugin Project를 엽니다.
76
+ 2. Secrets에서 manifest의 `required_secrets.id`와 같은 ID를 등록합니다.
77
+ 3. Connections에서 `cloud_connection_id`와 같은 provider 설정을 만듭니다.
78
+ 4. OAuth authorize URL, token URL, client ID, client secret, redirect URI를 공급자 문서와 맞춥니다.
79
+ 5. 필요한 header/content type과 PKCE 지원 여부를 명시합니다.
80
+ 6. source를 sync하고 새 Deployment를 build합니다.
81
+
82
+ Secret 값은 저장 후 다시 평문으로 표시되거나 source ZIP, MCP 응답, build log에 포함되면 안 됩니다.
83
+ 변경은 새 값으로 덮어쓰고 필요한 경우 provider에서 기존 값을 폐기합니다.
84
+
85
+ ## 사용자 연결 흐름
86
+
87
+ ```text
88
+ 설치·활성화
89
+ → 필요한 Connection 없음
90
+ → 설정에서 “계정 연결”
91
+ → Host가 state와 redirect를 생성
92
+ → provider 승인
93
+ → Host callback에서 state 검증과 token 교환
94
+ → 암호화 저장
95
+ → default Instance에 Connection 연결
96
+ ```
97
+
98
+ `allow_multiple: true`이면 같은 default Instance 안에 여러 Connection을 만들고 사용자가 표시 이름으로
99
+ 선택합니다. 같은 package root Instance를 복제해 계정을 추가하지 않습니다.
100
+
101
+ OAuth state와 redirect URI는 필수입니다. PKCE는 공급자 호환 설정에 따라 사용하지만 state 검증을
102
+ 대체하지 않습니다. callback이 성공하기 전에 연결 완료로 표시하지 않습니다.
103
+
104
+ ## 실행과 갱신
105
+
106
+ Capability는 `connector_id`만 참조합니다. Host는 선택된 Connection의 token을 요청 직전에 주입하고
107
+ 응답과 로그에서 제거합니다. credential 거부가 명확하고 provider가 refresh를 지원하면 한 번 갱신한
108
+ 뒤 전체 요청을 재시도합니다. 반복 실패는 “다시 연결” 상태로 전환합니다.
109
+
110
+ Connection 상태를 구분합니다.
111
+
112
+ - `ready`: 필요한 credential과 runtime이 준비됨
113
+ - `not_configured`: 필수 Connection/Secret이 없음
114
+ - `unavailable`: provider, 암호화, dependency, network runtime을 사용할 수 없음
115
+ - 만료·거부: 사용자 재연결 필요
116
+
117
+ ## 연결 해제와 삭제
118
+
119
+ Instance Connection 삭제는 해당 연결 row의 encrypted credential, 진행 중 OAuth state, 관련 pending
120
+ Host action을 로컬에서 정리하고 generation을 갱신합니다. 이 API를 provider token revoke가
121
+ 완료됐다는 증거로 사용하지 않습니다.
122
+
123
+ 기존 default credential 해제 API는 provider에 revocation endpoint가 있으면 먼저 revoke하고 성공한
124
+ 경우에만 local credential을 삭제합니다. provider 오류가 나면 암호화된 credential을 유지하고 오류를
125
+ 반환하므로 사용자가 다시 시도할 수 있습니다. Plugin 전체 삭제는 remote revoke를 가능한 범위에서
126
+ 시도한 뒤 `data_policy: retain`이어도 local credential과 OAuth state를 보존하지 않습니다.
127
+
128
+ ## 운영 점검
129
+
130
+ - authorize 요청마다 새로운 state가 생성되는가
131
+ - redirect URI가 등록값과 정확히 일치하는가
132
+ - token URL과 content type이 provider 요구와 맞는가
133
+ - client secret과 access token이 로그·MCP·artifact에 없는가
134
+ - 여러 Connection의 token과 표시 이름이 섞이지 않는가
135
+ - token 만료 후 한 번 갱신하고, 실패 시 재연결을 안내하는가
136
+ - Connection 삭제 후 해당 연결의 실행이 즉시 차단되고 local credential이 제거되는가
137
+ - provider revoke가 제품 요구사항이면 공급자 console에서도 token 폐기를 별도로 확인했는가
138
+ - 플러그인 삭제 후 local credential과 OAuth state가 남지 않는가
139
+
140
+ Notion의 실제 구성은 [Notion 플러그인](examples-notion.md)을 참고하세요.