@morit/cli 1.1.0 → 1.2.0
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.
- package/README.md +5 -3
- package/assets/docs/README.md +18 -16
- package/assets/docs/ai-response-and-timeline.md +25 -0
- package/assets/docs/components.md +48 -1
- package/assets/docs/design-tokens-responsive.md +33 -10
- package/assets/docs/docs-index.json +1 -0
- package/assets/docs/examples-school-life.md +12 -7
- package/assets/docs/getting-started.md +1 -1
- package/assets/docs/lifecycle-and-api.md +11 -0
- package/assets/docs/local-cli.md +4 -3
- package/assets/docs/manifest.md +23 -1
- package/assets/docs/packaging-and-testing.md +8 -2
- package/assets/docs/permissions-and-data.md +23 -5
- package/assets/docs/plugin-storage.md +175 -0
- package/assets/docs/project-structure.md +12 -0
- package/assets/docs/remote-mcp.md +9 -3
- package/assets/docs/school-life-privacy.md +7 -1
- package/assets/docs/sdk-and-mcp.md +13 -2
- package/assets/docs/tool-and-skill.md +10 -1
- package/assets/docs/ui-runtime-v2.md +67 -0
- package/assets/docs/verification.md +4 -4
- package/assets/plugin_contract.json +71 -4
- package/package.json +1 -1
- package/src/cli.js +36 -3
- package/src/preview.js +132 -0
- package/src/secure-store.js +113 -43
- package/src/workspace.js +170 -16
package/README.md
CHANGED
|
@@ -12,13 +12,15 @@ npx -y @morit/cli login
|
|
|
12
12
|
npx -y @morit/cli plugin setup . --id com.example.study --name "Study" --publisher example
|
|
13
13
|
npx -y @morit/cli plugin add .
|
|
14
14
|
npx -y @morit/cli plugin validate .
|
|
15
|
+
npx -y @morit/cli plugin preview . --output ./dist/preview.html
|
|
15
16
|
npx -y @morit/cli plugin build .
|
|
16
17
|
npx -y @morit/cli plugin deploy . --visibility private
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
`previewProjectDirectory`, `@morit/plugin-mcp`의
|
|
21
|
-
`python tools/morit_plugin.py preview <directory
|
|
20
|
+
`plugin preview`는 validate된 Runtime v2 화면을 script-free HTML로 렌더링하고 light/dark 토글을
|
|
21
|
+
제공합니다. Node SDK의 `previewProjectDirectory`, `@morit/plugin-mcp`의
|
|
22
|
+
`morit_project_preview`, 저장소 도구 `python tools/morit_plugin.py preview <directory>`도 같은
|
|
23
|
+
계약을 사용합니다.
|
|
22
24
|
|
|
23
25
|
전체 명령은 `npx -y @morit/cli --help`에서 확인하고 자동화에서는 `--json`을 사용합니다.
|
|
24
26
|
|
package/assets/docs/README.md
CHANGED
|
@@ -28,15 +28,16 @@ npx -y @morit/cli plugin setup . \
|
|
|
28
28
|
--name "Study" \
|
|
29
29
|
--publisher example
|
|
30
30
|
npx -y @morit/cli plugin validate .
|
|
31
|
+
npx -y @morit/cli plugin preview . --output ./dist/preview.html
|
|
31
32
|
npx -y @morit/cli plugin build .
|
|
32
33
|
npx -y @morit/cli login
|
|
33
34
|
npx -y @morit/cli plugin add .
|
|
34
35
|
npx -y @morit/cli plugin deploy . --visibility private
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
`python tools/morit_plugin.py preview <project
|
|
39
|
-
|
|
38
|
+
같은 safe renderer는 공식 CLI, Local/Remote Plugin MCP의 `morit_project_preview`, 저장소 도구
|
|
39
|
+
`python tools/morit_plugin.py preview <project>`에서 사용합니다. preview의 라이트·다크 전환으로
|
|
40
|
+
계약과 배치를 확인하고, 실제 Flutter focus·navigation·Tool 흐름은 앱에서도 확인합니다.
|
|
40
41
|
|
|
41
42
|
## 개발 순서별 인덱스
|
|
42
43
|
|
|
@@ -65,31 +66,32 @@ npx -y @morit/cli plugin deploy . --visibility private
|
|
|
65
66
|
|
|
66
67
|
13. [Tool, Skill, Search, Slash Command](tool-and-skill.md) — capability와 runtime adapter
|
|
67
68
|
14. [권한, 설정, 저장소, 알림](permissions-and-data.md) — 최소 권한과 Host action
|
|
68
|
-
15. [
|
|
69
|
-
16. [
|
|
69
|
+
15. [Plugin Local Storage와 AI 접근](plugin-storage.md) — namespace, CRUD, migration, 격리, AI Tool
|
|
70
|
+
16. [외부 서비스 인증과 Cloud Secrets](authentication.md) — OAuth, API key, 비밀 값 경계
|
|
71
|
+
17. [AI Skill과 파일 산출물](ai-skill-and-docx-workflow.md) — 실제 파일을 반환하는 완료 흐름
|
|
70
72
|
|
|
71
73
|
### 5. 플랫폼
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
18. [Android, iOS, Desktop 호환](platform-compatibility.md) — 현재 지원 범위와 이식 원칙
|
|
74
76
|
|
|
75
77
|
### 6. 검증과 배포
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
19. [공식 CLI 개발 흐름](local-cli.md) — setup, sync, preview, build, deploy
|
|
80
|
+
20. [패키징과 테스트](packaging-and-testing.md) — 서명, 무결성, 테스트 층
|
|
81
|
+
21. [오류 해결](troubleshooting.md) — validate, preview, build, 설치, 실행 오류 구분
|
|
82
|
+
22. [예제 검증 방법과 확인 경계](verification.md) — 자동·live·실기기 검증의 차이
|
|
81
83
|
|
|
82
84
|
### 7. SDK, MCP, API
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
23. [CLI와 AI 에이전트 MCP](sdk-and-mcp.md) — Codex·Claude 등 로컬/원격 연결
|
|
87
|
+
24. [원격 Plugin MCP](remote-mcp.md) — Cloud Project를 다루는 Tool 계약
|
|
88
|
+
25. [수명주기와 HTTP API](lifecycle-and-api.md) — Host API와 설치·실행 상태
|
|
87
89
|
|
|
88
90
|
### 8. 실제 예제
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
26. [학교 생활 플러그인](examples-school-life.md) — 공개 NEIS와 AI 할 일을 사용하는 학생용 경험
|
|
93
|
+
27. [학교 생활 개인정보 처리](school-life-privacy.md) — 저장·전송·삭제 범위
|
|
94
|
+
28. [Notion 플러그인](examples-notion.md) — OAuth Connection과 실제 문서 검색
|
|
93
95
|
|
|
94
96
|
## 문서가 배포되는 위치
|
|
95
97
|
|
|
@@ -54,6 +54,18 @@ Response point도 일반 Runtime v2와 같은 node, theme, app bar, navigation,
|
|
|
54
54
|
화면 mount 시 외부 요청을 반복하지 않습니다. 후속 refresh나 사용자가 누른 action에만 추가
|
|
55
55
|
capability를 호출합니다.
|
|
56
56
|
|
|
57
|
+
## 실시간 구조화 응답
|
|
58
|
+
|
|
59
|
+
Morit AI 오케스트레이터는 Tool·Skill 실행 전후 같은 `(message, plugin, instance, capability)` 응답을
|
|
60
|
+
upsert합니다. `data.__morit_response.state`는 `loading`, `partial`, `completed`, `failed` 중 하나이며
|
|
61
|
+
`retryable`이 true인 실패는 해당 메시지의 재생성으로 복구할 수 있습니다. 텍스트 delta는 별도로
|
|
62
|
+
계속 스트리밍되므로 UI가 실패해도 텍스트 답변은 유지됩니다.
|
|
63
|
+
|
|
64
|
+
Plugin이 `point: "response"` UI를 선언하면 그 Runtime v2를 사용합니다. 선언하지 않은 Tool·Skill
|
|
65
|
+
결과도 Host가 같은 검증기를 통해 `metric`, `progress`, `list`, `timeline`, `calendar`, `chart`,
|
|
66
|
+
`table`, `empty` 조합으로 안전하게 표시합니다. 차트는 `bar`, `line`, `donut`, `scatter`를 지원합니다.
|
|
67
|
+
알 수 없는 shape은 실행하거나 추측하지 않고 summary와 텍스트 fallback을 남깁니다.
|
|
68
|
+
|
|
57
69
|
## 한 container의 정보 순서
|
|
58
70
|
|
|
59
71
|
1. 결과를 설명하는 짧은 제목 또는 summary
|
|
@@ -123,3 +135,16 @@ Code Interpreter가 파일을 만들었다면 sandbox 내부 경로만 답변에
|
|
|
123
135
|
가짜 링크를 생성하지 않습니다.
|
|
124
136
|
|
|
125
137
|
파일 생성 완료 조건은 [AI Skill과 파일 산출물](ai-skill-and-docx-workflow.md)에 정리되어 있습니다.
|
|
138
|
+
|
|
139
|
+
## 이미지 artifact
|
|
140
|
+
|
|
141
|
+
`image_generation`·`image_edit`의 PNG/JPEG/GIF/WebP artifact는 생성 중 placeholder에서 완료 후 답변
|
|
142
|
+
내 inline 이미지로 바뀝니다. 여러 장은 gallery로 넘기며 원본 비율을 유지한 contain 렌더링,
|
|
143
|
+
전체 화면 `InteractiveViewer`, 다운로드, Android 공유를 제공합니다. 대화 재진입 때도 저장된
|
|
144
|
+
`ai_tool_executions.result_summary.artifacts`를 다시 사용합니다.
|
|
145
|
+
|
|
146
|
+
Artifact URL은 conversation·execution·attachment 소유권을 확인한 뒤 5분 signed URL로 만들며 만료나
|
|
147
|
+
일시 실패 시 새 URL을 받아 두 번 시도합니다. 앱은 redirect를 따르지 않고 HTTPS(개발 loopback 제외),
|
|
148
|
+
MIME, magic byte, 실제 decode, 24 MiB, 4096 px, 16 MP를 확인합니다. SVG와 외부 실행 형식은 inline으로
|
|
149
|
+
열지 않고 기존 파일 카드로 fallback합니다. Plugin response의 URL 이미지는 앱이 직접 요청하지 않고
|
|
150
|
+
기존 인증 Host proxy와 `network` grant를 거치며 실패 시 이미지 단위 재시도를 제공합니다.
|
|
@@ -137,11 +137,58 @@ action에는 반드시 `label`, 일반 `icon` node에는 `semantic_label`을 함
|
|
|
137
137
|
| `list` | 일반 항목 반복 | `source`, `empty_text`, `limit`, `dense` | item template child 정확히 하나 |
|
|
138
138
|
| `timeline` | 시간 순서 사건 | `source`, `empty_text`, `limit` | item template child 정확히 하나 |
|
|
139
139
|
| `calendar` | 날짜별 데이터 | `source`, `date_key`, `title_key`, `state_key` | source 필수 |
|
|
140
|
-
| `chart` | 수치
|
|
140
|
+
| `chart` | 수치 비교·추세·분포 | `source`, `chart_type`, `x_key`, `y_key`, `show_legend` | `bar`, `line`, `donut`, `scatter` |
|
|
141
|
+
| `table` | 행·열 구조 데이터 | `source`, `empty_text`, `limit`, `dense` | 최대 6열, 가로 스크롤 |
|
|
141
142
|
|
|
142
143
|
반복 template 안에서는 `{{item.title}}`처럼 `item` binding을 사용합니다. 큰 목록을 한 번에 렌더링하지
|
|
143
144
|
말고 capability에서 페이지나 기간을 나눕니다.
|
|
144
145
|
|
|
146
|
+
## 공통 UX 프리셋
|
|
147
|
+
|
|
148
|
+
프리셋은 새 실행 타입이 아니라 검증된 primitive 조합입니다. 그래서 Preview와 Flutter Host가 같은
|
|
149
|
+
node를 렌더링하고 기존 플러그인도 별도 migration 없이 사용합니다.
|
|
150
|
+
|
|
151
|
+
| 프리셋 | 조합 | Host가 맡는 상태 |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| 목록·검색 | `field` + `button` + `list`/`timeline` | source별 loading/error/retry, 빈 목록 |
|
|
154
|
+
| 상세 | `app_bar` + `section` + `card`/`metric` | route back, stale response 차단 |
|
|
155
|
+
| 폼 | `form` + `field`/`select`/`switch` + submit `button` | focus, keyboard, disabled feedback |
|
|
156
|
+
| 설정 | `section` + persisted controls | state 복원, permission/connection 관리 링크 |
|
|
157
|
+
| 빈 상태 | `empty` + 다음 행동 `button` | 의미 있는 title/supporting/icon |
|
|
158
|
+
| 반응형 dashboard | `grid.min_item_width` + `row.stack_at` + adaptive navigation | bar/rail/drawer 전환 |
|
|
159
|
+
|
|
160
|
+
목록·검색의 최소 tree:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"type": "column",
|
|
165
|
+
"props": {"spacing": 12},
|
|
166
|
+
"children": [
|
|
167
|
+
{"type": "field", "props": {"state_key": "query", "label": "검색", "placeholder": "검색어"}},
|
|
168
|
+
{
|
|
169
|
+
"type": "button",
|
|
170
|
+
"props": {"label": "찾기", "icon": "search"},
|
|
171
|
+
"action": {"type": "invoke", "capability": "com.example.search", "query": "{{state.query}}", "arguments": {}, "store": "results"}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"type": "list",
|
|
175
|
+
"props": {"source": "data.results.data.items", "empty_text": "검색 결과가 없어요.", "limit": 50},
|
|
176
|
+
"children": [
|
|
177
|
+
{"type": "card", "props": {"title": "{{item.title}}", "subtitle": "{{item.summary}}"}}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
권한 요청, 연결 필요, 첫 loading, capability error와 retry는 플러그인이 비슷한 경고 카드를 다시
|
|
185
|
+
만들지 않고 Host 공통 상태를 사용합니다. Plugin 상세의 권한/Connection 화면으로 이동한 뒤 같은
|
|
186
|
+
route state와 persisted control을 복원합니다. 데이터가 정상적으로 비었을 때만 `empty` 또는
|
|
187
|
+
`empty_text`를 사용하며 오류를 빈 목록으로 숨기지 않습니다.
|
|
188
|
+
|
|
189
|
+
모든 프리셋은 light/dark, 320px 폭, tablet/desktop 폭, 큰 글자에서 확인합니다. node별 tooltip,
|
|
190
|
+
semantic label, 최소 탭 영역과 색 이외의 상태 표시는 Host Material 3 규칙을 따릅니다.
|
|
191
|
+
|
|
145
192
|
## 공통 크기·정렬·접근성 props
|
|
146
193
|
|
|
147
194
|
- 크기: `width`, `height`, `min_width`, `max_width`, `min_height`, `max_height`
|
|
@@ -8,15 +8,30 @@ Runtime v2는 기본적으로 Morit의 Material 3 theme를 사용합니다. 플
|
|
|
8
8
|
```json
|
|
9
9
|
{
|
|
10
10
|
"theme": {
|
|
11
|
-
"color_scheme": {
|
|
12
|
-
"primary": "#315DA8",
|
|
13
|
-
"on_primary": "#FFFFFF",
|
|
14
|
-
"primary_container": "#D9E2FF",
|
|
15
|
-
"on_primary_container": "#001A41"
|
|
16
|
-
},
|
|
17
11
|
"radius": 18,
|
|
18
12
|
"spacing": 12,
|
|
19
|
-
"density": "standard"
|
|
13
|
+
"density": "standard",
|
|
14
|
+
"surface": {"elevation": 1},
|
|
15
|
+
"border": {"width": 1},
|
|
16
|
+
"icon": {"size": 22},
|
|
17
|
+
"typography": {"scale": 1.0, "body_weight": 400, "title_weight": 700},
|
|
18
|
+
"states": {"disabled_opacity": 0.38},
|
|
19
|
+
"light": {
|
|
20
|
+
"color_scheme": {
|
|
21
|
+
"primary": "#315DA8",
|
|
22
|
+
"on_primary": "#FFFFFF",
|
|
23
|
+
"surface": "#F9F9FF",
|
|
24
|
+
"on_surface": "#1A1B20"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"dark": {
|
|
28
|
+
"color_scheme": {
|
|
29
|
+
"primary": "#AFC6FF",
|
|
30
|
+
"on_primary": "#002F66",
|
|
31
|
+
"surface": "#111318",
|
|
32
|
+
"on_surface": "#E2E2E9"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
36
|
}
|
|
22
37
|
```
|
|
@@ -25,9 +40,17 @@ Runtime v2는 기본적으로 Morit의 Material 3 theme를 사용합니다. 플
|
|
|
25
40
|
- `spacing`: 0~32
|
|
26
41
|
- `density`: `compact`, `standard`, `comfortable`
|
|
27
42
|
- `color_scheme`: Material role과 `#RRGGBB` 또는 `#AARRGGBB`
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
- `typography`: `scale` 0.8~1.4, body/title weight 100~900
|
|
44
|
+
- `surface`: `color`, `container_color`, `elevation` 0~24
|
|
45
|
+
- `border`: `color`, `width` 0~8, `radius` 0~64
|
|
46
|
+
- `icon`: `color`, `size` 12~64
|
|
47
|
+
- `states`: `disabled_opacity` 0.2~0.8, `selected_color`, `focus_color`
|
|
48
|
+
- `light`, `dark`: 위 필드의 mode별 partial override
|
|
49
|
+
|
|
50
|
+
최상위 base → 현재 mode variant → Host Material 3 theme 순서로 병합됩니다. 누락한 role은 Host가
|
|
51
|
+
채우므로 기존 플러그인은 선언을 추가하지 않아도 앱의 라이트·다크 모드를 따릅니다. 명백히 같은
|
|
52
|
+
literal 전경/배경은 validate 오류이며 계산 가능한 4.5:1 미만 조합은 경고입니다. 대비가 확인된 작은
|
|
53
|
+
palette만 override하고 모든 role을 임의로 복제하지 않습니다.
|
|
31
54
|
|
|
32
55
|
## 색상 token
|
|
33
56
|
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"documents": [
|
|
47
47
|
{ "file": "tool-and-skill.md", "slug": "tool-and-skill", "title": "Tool, Skill, Search, Slash Command" },
|
|
48
48
|
{ "file": "permissions-and-data.md", "slug": "permissions-settings-notifications", "title": "권한, 설정, 저장소, 알림" },
|
|
49
|
+
{ "file": "plugin-storage.md", "slug": "plugin-storage", "title": "Plugin Local Storage와 AI 접근" },
|
|
49
50
|
{ "file": "authentication.md", "slug": "authentication", "title": "외부 서비스 인증과 Cloud Secrets" },
|
|
50
51
|
{ "file": "ai-skill-and-docx-workflow.md", "slug": "ai-skill-artifacts", "title": "AI Skill과 파일 산출물" }
|
|
51
52
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
위치: `examples/plugins/school-life/`
|
|
4
4
|
|
|
5
|
-
학교 생활 1.
|
|
5
|
+
학교 생활 1.5.0은 하드코딩 시간표나 테스트용 JSON을 사용하지 않습니다. 학교명을
|
|
6
6
|
[NEIS 공개 API](https://open.neis.go.kr/)의 `schoolInfo`에서 찾고, 학교 종류에 맞는
|
|
7
7
|
시간표 API와 `SchoolSchedule`, `mealServiceDietInfo`를 호출합니다. 선택한 학교·학년·반과
|
|
8
8
|
알림 설정은 사용자와 Plugin Instance 범위의 저장소에 격리합니다.
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
| 다음 학교일 알림 | `school_life.schedule.reminder` |
|
|
22
22
|
| 6시간 자동 확인·중복 방지 브리핑 | background refresh/briefing |
|
|
23
23
|
| 빠른 명령 | `/school`, `/meal`, `/timetable` |
|
|
24
|
+
| AI와 공유하는 학교 할 일 | `tasks` namespace와 표준 Storage CRUD Tool |
|
|
25
|
+
| 라이트·다크 화면 | Host theme 상속 + mode별 최소 색 override |
|
|
24
26
|
|
|
25
27
|
기간 조회는 시간표·일정·급식 API를 각각 한 번씩 병렬 호출하고 날짜별로 묶으므로 7일
|
|
26
28
|
화면이 날짜마다 네트워크를 반복하지 않습니다. 급식의 숫자 알레르기 코드는 19개 표준
|
|
@@ -29,13 +31,13 @@
|
|
|
29
31
|
## 설치와 첫 설정
|
|
30
32
|
|
|
31
33
|
```text
|
|
32
|
-
examples/plugins/school-life/dist/school-life-1.
|
|
33
|
-
크기
|
|
34
|
-
SHA-256
|
|
34
|
+
examples/plugins/school-life/dist/school-life-1.5.0.mplg
|
|
35
|
+
크기 263184 bytes
|
|
36
|
+
SHA-256 3b6397ca1ef26833fe842087b0ecf5f05621bd45f9d80b8a422b6547c060f603
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
1. Morit 플러그인 관리에서 위 `.mplg`를 선택합니다.
|
|
38
|
-
2. `network`, `storage`, `notifications`, `background` 권한을 허용합니다.
|
|
40
|
+
2. `network`, `storage`, `ai_storage`, `notifications`, `background` 권한을 허용합니다.
|
|
39
41
|
3. 학교 이름을 검색하고 후보에서 정확한 학교를 고릅니다.
|
|
40
42
|
4. 학년·반, 알림 시각(기본 07:30), 아침 브리핑 사용 여부를 저장합니다.
|
|
41
43
|
5. 홈의 `학교 생활`, `오늘`, `이번 주`, `급식` 화면을 사용합니다.
|
|
@@ -49,12 +51,14 @@ AI 예:
|
|
|
49
51
|
- `오늘 수업 순서와 급식 알려줘`
|
|
50
52
|
- `내일 알레르기 5번 메뉴가 있어?`
|
|
51
53
|
- `이번 주 학사일정과 수업량을 정리해줘`
|
|
54
|
+
- `이번 주 학교 할 일에 과학 보고서 추가해줘`
|
|
55
|
+
- `학교 할 일 중 끝낸 과제를 완료 처리해줘`
|
|
52
56
|
- `/timetable 내일`
|
|
53
57
|
- `/meal 앞으로 7일`
|
|
54
58
|
|
|
55
59
|
## 개인정보와 실패 처리
|
|
56
60
|
|
|
57
|
-
- 저장: 공개 학교 식별자, 학년·반, 알림 시각/사용
|
|
61
|
+
- 저장: 공개 학교 식별자, 학년·반, 알림 시각/사용 여부, 사용자가 AI에 요청한 학교 할 일
|
|
58
62
|
- 미수집: 학생 이름, 학번, 성적, NEIS 로그인 정보
|
|
59
63
|
- 외부 전송: 공개 조회 조건만 NEIS에 전달
|
|
60
64
|
- 삭제: profile, background lease/state, host-action outbox를 사용자 범위에서 purge
|
|
@@ -65,7 +69,8 @@ AI 예:
|
|
|
65
69
|
|
|
66
70
|
`backend/archive_processing/test_plugin_examples.py`는 서명 package 설치, 권한 전 상태,
|
|
67
71
|
학교 연결, 단일 날짜와 7일 조회, 알레르기/영양 변환, 검색, rich 알림, 자동 브리핑 중복
|
|
68
|
-
방지, 재시작 복원, actor 격리, disable/delete purge를
|
|
72
|
+
방지, Storage CRUD/AI 권한, 재시작 복원, actor·Instance 격리, migration, disable/delete purge를
|
|
73
|
+
고정된 공식 response contract로
|
|
69
74
|
검증합니다.
|
|
70
75
|
|
|
71
76
|
배포 전에는 [예제 검증 방법](verification.md)에 따라 실제 NEIS 네트워크로 공개 시간표·학사일정·
|
|
@@ -47,7 +47,7 @@ revision만 있으며 인증 정보는 들어가지 않습니다.
|
|
|
47
47
|
"description": "오늘 할 일을 정리합니다.",
|
|
48
48
|
"publisher": "example",
|
|
49
49
|
"version": "1.0.0",
|
|
50
|
-
"min_morit_version": "1.7.
|
|
50
|
+
"min_morit_version": "1.7.8",
|
|
51
51
|
"max_morit_version": "1.999.999",
|
|
52
52
|
"permissions": [],
|
|
53
53
|
"capabilities": [
|
|
@@ -47,6 +47,7 @@ Raw install content type은 `application/vnd.morit.plugin+zip`, 최대 2 MiB입
|
|
|
47
47
|
| GET | `/v1/plugin-instances` | default/child Instance 목록 |
|
|
48
48
|
| GET | `/v1/plugin-instances/{instance_id}` | Instance 상태·Connection·dependency |
|
|
49
49
|
| PATCH | `/v1/plugin-instances/{instance_id}` | 이름, enabled, permission, settings 변경 |
|
|
50
|
+
| DELETE | `/v1/plugin-instances/{instance_id}/storage` | 해당 Instance의 Local Storage 초기화 |
|
|
50
51
|
| DELETE | `/v1/plugin-instances/{instance_id}` | Instance와 descendant 정리용 API |
|
|
51
52
|
| GET | `/v1/plugins/{plugin_id}` | plugin ID 기준 설치 상세 |
|
|
52
53
|
| PATCH | `/v1/plugins/{plugin_id}` | 호환용 default 설치 설정 |
|
|
@@ -123,6 +124,11 @@ notification key와 storage action은 idempotent하게 처리합니다.
|
|
|
123
124
|
package 응답은 attachment `Content-Disposition`을 사용합니다. 삭제 시 `purge_data`와 manifest
|
|
124
125
|
`data_policy`를 적용하되 credential, OAuth state, pending Host action은 항상 별도 보안 정리를 거칩니다.
|
|
125
126
|
|
|
127
|
+
Storage 초기화는 actor가 소유한 정확한 Instance만 대상으로 하며 같은 Installation의 다른 Instance와
|
|
128
|
+
다른 사용자 데이터는 건드리지 않습니다. `data_policy: retain`은 uninstall/reinstall에만 적용되고
|
|
129
|
+
사용자가 명시적으로 초기화하면 즉시 삭제됩니다. 자세한 수명주기는
|
|
130
|
+
[Plugin Local Storage와 AI 접근](plugin-storage.md)을 참고하세요.
|
|
131
|
+
|
|
126
132
|
Asset API는 활성화된 사용자 소유 설치가 UI Runtime v2에서 실제 참조한 이미지에만 접근할 수
|
|
127
133
|
있습니다. Host는 저장 package의 hash·signature·manifest를 다시 확인하고 확장자와 PNG/JPEG/GIF/WebP
|
|
128
134
|
magic byte, 실제 이미지 포맷을 검사합니다. package asset과 원격 이미지에 공통으로 가로·세로 각각
|
|
@@ -144,6 +150,11 @@ Host는 공개 HTTPS만 허용하고 DNS/IP SSRF 검사를 거치며 redirect를
|
|
|
144
150
|
`413`, 지원하지 않거나 손상된 이미지는 `415`, upstream 실패는 `502`, Host network 경계 미설정은
|
|
145
151
|
`503`으로 구분합니다. 소유하지 않은 Instance는 상세 정보를 노출하지 않습니다.
|
|
146
152
|
|
|
153
|
+
AI 생성/분석 artifact는 별도 소유권 경계인
|
|
154
|
+
`GET /v1/ai/conversations/{conversation_id}/code-executions/{execution_id}/artifacts/{attachment_id}/access`
|
|
155
|
+
에서만 signed URL을 발급합니다. 완료된 `python_code`, `image_generation`, `image_edit` 실행의 저장된
|
|
156
|
+
artifact manifest와 attachment 행이 모두 일치해야 하며 다른 대화·실행의 attachment ID는 404입니다.
|
|
157
|
+
|
|
147
158
|
## 앱 시작 복구
|
|
148
159
|
|
|
149
160
|
시작할 때 DB Installation과 content-addressed package store를 함께 점검합니다.
|
package/assets/docs/local-cli.md
CHANGED
|
@@ -35,13 +35,14 @@ Ed25519 key로 자동 서명합니다. key는 기본적으로 사용자 홈의 `
|
|
|
35
35
|
보호해 저장하고 source나 `.mplg`에 private key를 넣지 않습니다. build 결과를 같은 과정에서 다시
|
|
36
36
|
열어 signature와 package 구조를 확인합니다.
|
|
37
37
|
|
|
38
|
-
UI preview는 현재 CLI subcommand가 아니라 SDK/MCP 기능입니다.
|
|
39
|
-
|
|
40
38
|
```bash
|
|
39
|
+
npx -y @morit/cli plugin preview . --output ./dist/preview.html
|
|
41
40
|
python tools/morit_plugin.py preview . --output ./dist/preview.html
|
|
42
41
|
```
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
두 명령과 Local/Remote MCP의 `morit_project_preview`는 같은 검증 계약을 사용합니다. 결과는 script를
|
|
44
|
+
실행하지 않는 safe HTML이며 light/dark 토글을 포함합니다. 실제 Flutter route·focus·Tool 실행은
|
|
45
|
+
서명 package를 앱에 설치해 확인합니다.
|
|
45
46
|
|
|
46
47
|
## 3. 로그인
|
|
47
48
|
|
package/assets/docs/manifest.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"privacy_policy_url": "https://example.com/privacy",
|
|
21
21
|
"publisher": "example",
|
|
22
22
|
"version": "1.0.0",
|
|
23
|
-
"min_morit_version": "1.7.
|
|
23
|
+
"min_morit_version": "1.7.8",
|
|
24
24
|
"max_morit_version": "1.999.999",
|
|
25
25
|
"permissions": ["network", "credentials"],
|
|
26
26
|
"capabilities": [
|
|
@@ -78,6 +78,28 @@ Manifest `permissions`는 플러그인이 요청할 수 있는 최대 범위입
|
|
|
78
78
|
|
|
79
79
|
credential, OAuth state, pending notification은 두 정책과 무관하게 보안 수명주기에 따라 정리됩니다.
|
|
80
80
|
|
|
81
|
+
### Local Storage
|
|
82
|
+
|
|
83
|
+
새 저장소는 schema 2에서 명시적으로 선언합니다.
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"permissions": ["storage", "ai_storage"],
|
|
88
|
+
"storage": {
|
|
89
|
+
"version": 1,
|
|
90
|
+
"namespaces": [
|
|
91
|
+
{"id": "default", "max_bytes": 131072, "ai_access": "read"},
|
|
92
|
+
{"id": "tasks", "max_bytes": 262144, "ai_access": "read_write"}
|
|
93
|
+
],
|
|
94
|
+
"migrations": []
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`ai_access`가 `read` 또는 `read_write`인 namespace가 있으면 `ai_storage` 권한이 필수입니다.
|
|
100
|
+
namespace, quota, migration과 표준 CRUD Tool은 [Plugin Local Storage와 AI 접근](plugin-storage.md)을
|
|
101
|
+
참고하세요.
|
|
102
|
+
|
|
81
103
|
## Capability
|
|
82
104
|
|
|
83
105
|
```json
|
|
@@ -71,10 +71,12 @@ Connector, source entrypoint, dependency를 검사합니다.
|
|
|
71
71
|
### 2. Preview
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
+
npx -y @morit/cli plugin preview . --output ./dist/preview.html
|
|
74
75
|
python tools/morit_plugin.py preview . --output ./dist/preview.html
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
정보 구조와 compiled manifest를 점검합니다. 실제 Flutter renderer 성공을
|
|
78
|
+
정보 구조와 compiled manifest, light/dark partial theme를 점검합니다. 실제 Flutter renderer 성공을
|
|
79
|
+
대신하지 않습니다.
|
|
78
80
|
|
|
79
81
|
### 3. Package reopen
|
|
80
82
|
|
|
@@ -98,6 +100,10 @@ python tools/morit_plugin.py verify ./dist/plugin.mplg
|
|
|
98
100
|
- local file 설치
|
|
99
101
|
- 활성화와 권한 허용·거부
|
|
100
102
|
- 화면 loading·empty·error·retry
|
|
103
|
+
- 시스템/라이트/다크 전환과 기존 theme 없는 package 호환
|
|
104
|
+
- Storage CRUD·검색·즉시 UI 갱신, AI read/read_write 권한
|
|
105
|
+
- 사용자·Installation·Instance·namespace 격리와 quota/revision/generation 충돌
|
|
106
|
+
- Storage migration, reset, purge/retain uninstall/reinstall
|
|
101
107
|
- 동일 package 재설치와 version update
|
|
102
108
|
- OAuth 여러 Connection과 재연결
|
|
103
109
|
- notification 실제 표시
|
|
@@ -105,7 +111,7 @@ python tools/morit_plugin.py verify ./dist/plugin.mplg
|
|
|
105
111
|
|
|
106
112
|
### 6. 회귀 검사
|
|
107
113
|
|
|
108
|
-
SDK, Python Host, Local MCP, Remote MCP가 같은 contract를 반환하고 같은 fixture를 허용·거부하는지
|
|
114
|
+
SDK, Python Host, Flutter schema, Local MCP, Remote MCP가 같은 contract를 반환하고 같은 fixture를 허용·거부하는지
|
|
109
115
|
확인합니다. 문서 예제 JSON도 source 검증 과정에서 실행해 구현과 함께 변경합니다.
|
|
110
116
|
|
|
111
117
|
## 실패 시 원칙
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
| `background` | Host scheduler에서 자동 실행 |
|
|
15
15
|
| `account_read` | 필요한 계정 기본 정보 읽기 |
|
|
16
16
|
| `storage` | 플러그인 전용 데이터 저장 |
|
|
17
|
+
| `ai_storage` | AI가 선언된 namespace를 표준 CRUD Tool로 읽거나 변경 |
|
|
17
18
|
| `credentials` | Credential/Connection 사용 |
|
|
18
19
|
|
|
19
20
|
Manifest는 요청 가능한 최대 권한, capability와 UI extension은 실제 필요한 부분집합을 선언합니다.
|
|
@@ -41,15 +42,20 @@ Settings는 default Instance에 저장되는 작은 사용자 환경설정입니
|
|
|
41
42
|
Settings 전체는 32 KiB 이하입니다. UI state의 client 저장 경계는 더 작을 수 있으므로 화면에 필요한
|
|
42
43
|
최소 key만 유지합니다.
|
|
43
44
|
|
|
44
|
-
## Plugin
|
|
45
|
+
## Plugin Local Storage
|
|
45
46
|
|
|
46
|
-
`storage`는 사용자·Installation
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
`storage`는 사용자·Installation·Instance·namespace로 격리됩니다. 문자열, 숫자, boolean, JSON과
|
|
48
|
+
목록을 저장하며 package가 업데이트되어도 정책에 따라 유지됩니다. generation이 바뀐 오래된 실행은
|
|
49
|
+
새 상태를 쓸 수 없습니다.
|
|
49
50
|
|
|
50
51
|
삭제 시 `data_policy`가 일반 storage의 `purge` 또는 `retain`을 정합니다. credential, OAuth state,
|
|
51
52
|
pending Host action은 별도 보안 수명주기를 따르며 일반 storage retention으로 보존하지 않습니다.
|
|
52
53
|
|
|
54
|
+
`ai_storage`는 설치/설정에서 한 번 허용한 뒤 매 CRUD 승인을 반복하지 않습니다. 그래도 Host는
|
|
55
|
+
현재 사용자·활성 Instance·허용 namespace와 `read`/`read_write` 범위를 매 호출 확인합니다.
|
|
56
|
+
Manifest, CRUD 인자, quota, migration, 오류와 초기화는
|
|
57
|
+
[Plugin Local Storage와 AI 접근](plugin-storage.md)을 참고하세요.
|
|
58
|
+
|
|
53
59
|
## Network
|
|
54
60
|
|
|
55
61
|
외부 통신은 `http_json`, `mcp_http`, 지원되는 Host adapter를 통과합니다.
|
|
@@ -86,7 +92,13 @@ Capability 결과의 `data.host_actions`는 Host가 서버에서 다시 확인
|
|
|
86
92
|
"at_millis": 1786640400000,
|
|
87
93
|
"category": "reminder",
|
|
88
94
|
"visibility": "private",
|
|
89
|
-
"silent": false
|
|
95
|
+
"silent": false,
|
|
96
|
+
"actions": [
|
|
97
|
+
{"id": "done", "type": "check", "label": "완료", "capability": "school_life.task.complete", "arguments": {"task_id": "42"}},
|
|
98
|
+
{"id": "later", "type": "snooze", "label": "10분 후", "minutes": 10},
|
|
99
|
+
{"id": "open", "type": "open_plugin", "label": "학교 생활", "target": "school_life.dashboard"},
|
|
100
|
+
{"id": "reply", "type": "reply", "label": "답장", "capability": "school_life.task.reply", "arguments": {"task_id": "42"}}
|
|
101
|
+
]
|
|
90
102
|
}
|
|
91
103
|
```
|
|
92
104
|
|
|
@@ -100,6 +112,12 @@ Capability 결과의 `data.host_actions`는 Host가 서버에서 다시 확인
|
|
|
100
112
|
`big_text` 2,000자입니다. `category`는 `general`, `reminder`, `progress`, `status`,
|
|
101
113
|
`visibility`는 `private`, `public`, `secret`입니다. 예약은 현재부터 최대 366일 안입니다.
|
|
102
114
|
|
|
115
|
+
알림당 action은 최대 4개이며 `check`, `capability`, `snooze`, `open_plugin`, `reply`를 지원합니다.
|
|
116
|
+
check/custom/reply는 현재 Instance에 선언된 Tool·Skill capability만 실행하고, 클릭 순간 enabled,
|
|
117
|
+
generation, permission, Connection을 다시 검사합니다. reply text는 Host가 제한된 argument로만 넣습니다.
|
|
118
|
+
event token과 action ID로 중복 클릭을 제거하고 성공한 event만 ACK합니다. capability가 Storage를
|
|
119
|
+
변경하면 같은 storage revision 신호로 열린 UI를 갱신합니다.
|
|
120
|
+
|
|
103
121
|
progress:
|
|
104
122
|
|
|
105
123
|
```json
|