@morit/cli 1.4.0 → 1.5.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 +3 -5
- package/assets/plugin_contract.json +4 -2
- package/bin/morit.js +0 -0
- package/package.json +1 -1
- package/src/cli.js +5 -0
- package/src/workspace.js +83 -12
- package/assets/docs/README.md +0 -107
- package/assets/docs/ai-response-and-timeline.md +0 -211
- package/assets/docs/ai-skill-and-docx-workflow.md +0 -83
- package/assets/docs/app-builder.md +0 -56
- package/assets/docs/authentication.md +0 -140
- package/assets/docs/components.md +0 -225
- package/assets/docs/design-tokens-responsive.md +0 -171
- package/assets/docs/docs-index.json +0 -94
- package/assets/docs/examples-notion.md +0 -83
- package/assets/docs/examples-school-life.md +0 -79
- package/assets/docs/getting-started.md +0 -132
- package/assets/docs/information-hierarchy.md +0 -81
- package/assets/docs/instances-and-connectors.md +0 -93
- package/assets/docs/lifecycle-and-api.md +0 -169
- package/assets/docs/local-cli.md +0 -125
- package/assets/docs/manifest.md +0 -247
- package/assets/docs/packaging-and-testing.md +0 -131
- package/assets/docs/permissions-and-data.md +0 -149
- package/assets/docs/platform-compatibility.md +0 -62
- package/assets/docs/plugin-storage.md +0 -175
- package/assets/docs/project-structure.md +0 -102
- package/assets/docs/remote-mcp.md +0 -158
- package/assets/docs/school-life-privacy.md +0 -55
- package/assets/docs/screens-layout-navigation.md +0 -95
- package/assets/docs/sdk-and-mcp.md +0 -199
- package/assets/docs/tool-and-skill.md +0 -188
- package/assets/docs/troubleshooting.md +0 -121
- package/assets/docs/ui-extensions.md +0 -75
- package/assets/docs/ui-runtime-v2.md +0 -406
- package/assets/docs/verification.md +0 -133
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# 권한, 설정, 저장소, 알림
|
|
2
|
-
|
|
3
|
-
## 권한 목록
|
|
4
|
-
|
|
5
|
-
| permission | 허용하는 작업 |
|
|
6
|
-
|---|---|
|
|
7
|
-
| `network` | Host가 보호하는 외부 HTTPS/MCP 요청 |
|
|
8
|
-
| `user_data_read` | Morit 사용자 데이터 읽기 |
|
|
9
|
-
| `user_data_write` | Morit 사용자 데이터 변경 |
|
|
10
|
-
| `attachments_read` | 사용자가 선택한 첨부 파일 읽기 |
|
|
11
|
-
| `file_write` | 사용자에게 반환할 파일 artifact 작성 |
|
|
12
|
-
| `ai_tool_call` | AI Tool 실행 경계 사용 |
|
|
13
|
-
| `notifications` | 표시·예약·취소 알림 요청 |
|
|
14
|
-
| `background` | Host scheduler에서 자동 실행 |
|
|
15
|
-
| `account_read` | 필요한 계정 기본 정보 읽기 |
|
|
16
|
-
| `storage` | 플러그인 전용 데이터 저장 |
|
|
17
|
-
| `ai_storage` | AI가 선언된 namespace를 표준 CRUD Tool로 읽거나 변경 |
|
|
18
|
-
| `credentials` | Credential/Connection 사용 |
|
|
19
|
-
|
|
20
|
-
Manifest는 요청 가능한 최대 권한, capability와 UI extension은 실제 필요한 부분집합을 선언합니다.
|
|
21
|
-
사용자가 허용하지 않은 권한은 runtime에서 다시 차단됩니다. 화면을 볼 수 있다는 사실과 작업 권한은
|
|
22
|
-
동일하지 않습니다.
|
|
23
|
-
|
|
24
|
-
## Settings
|
|
25
|
-
|
|
26
|
-
Settings는 default Instance에 저장되는 작은 사용자 환경설정입니다. UI Runtime state를 유지하려면
|
|
27
|
-
`initial_state`에 key를 선언하고 해당 control 또는 `set_state`에 `persist: true`를 사용합니다.
|
|
28
|
-
|
|
29
|
-
적합한 값:
|
|
30
|
-
|
|
31
|
-
- 기본 기간, 정렬 방식, 표시 옵션
|
|
32
|
-
- 알림 on/off와 사용자 선택 시간
|
|
33
|
-
- 공개 school code처럼 민감하지 않은 외부 식별자
|
|
34
|
-
|
|
35
|
-
저장하면 안 되는 값:
|
|
36
|
-
|
|
37
|
-
- OAuth access/refresh token, API key
|
|
38
|
-
- 파일 원문이나 큰 목록
|
|
39
|
-
- 다른 플러그인의 설정
|
|
40
|
-
- 서버가 검증하지 않은 실행 코드
|
|
41
|
-
|
|
42
|
-
Settings 전체는 32 KiB 이하입니다. UI state의 client 저장 경계는 더 작을 수 있으므로 화면에 필요한
|
|
43
|
-
최소 key만 유지합니다.
|
|
44
|
-
|
|
45
|
-
## Plugin Local Storage
|
|
46
|
-
|
|
47
|
-
`storage`는 사용자·Installation·Instance·namespace로 격리됩니다. 문자열, 숫자, boolean, JSON과
|
|
48
|
-
목록을 저장하며 package가 업데이트되어도 정책에 따라 유지됩니다. generation이 바뀐 오래된 실행은
|
|
49
|
-
새 상태를 쓸 수 없습니다.
|
|
50
|
-
|
|
51
|
-
삭제 시 `data_policy`가 일반 storage의 `purge` 또는 `retain`을 정합니다. credential, OAuth state,
|
|
52
|
-
pending Host action은 별도 보안 수명주기를 따르며 일반 storage retention으로 보존하지 않습니다.
|
|
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
|
-
|
|
59
|
-
## Network
|
|
60
|
-
|
|
61
|
-
외부 통신은 `http_json`, `mcp_http`, 지원되는 Host adapter를 통과합니다.
|
|
62
|
-
|
|
63
|
-
- 공개 HTTPS만 허용
|
|
64
|
-
- redirect 비허용
|
|
65
|
-
- DNS/IP와 SSRF 정책 적용
|
|
66
|
-
- Connector timeout 최대 30초
|
|
67
|
-
- retry 최대 4회
|
|
68
|
-
- Instance별 rate limit
|
|
69
|
-
- credential은 Host가 요청 직전에 주입
|
|
70
|
-
|
|
71
|
-
endpoint, Authorization header, secret을 UI state나 capability 결과에 복사하지 않습니다.
|
|
72
|
-
|
|
73
|
-
Capability 결과 URL을 `image` 또는 `avatar`에 표시하는 경우에도 같은 권한 경계를 사용합니다.
|
|
74
|
-
Manifest의 `network` 요청과 활성 Instance의 `network` grant가 모두 필요합니다. Flutter 앱은 외부
|
|
75
|
-
이미지를 직접 요청하지 않고 인증된 Host proxy를 호출하며, Host가 public HTTPS·SSRF·redirect·이미지
|
|
76
|
-
형식·512 KiB·4096 px·16 MP 제한을 적용합니다. 선언과 상세 제한은
|
|
77
|
-
[UI Runtime v2](ui-runtime-v2.md), endpoint는 [수명주기와 HTTP API](lifecycle-and-api.md)를
|
|
78
|
-
참고하세요.
|
|
79
|
-
|
|
80
|
-
## Notification Host action
|
|
81
|
-
|
|
82
|
-
Capability 결과의 `data.host_actions`는 Host가 서버에서 다시 확인하고 durable outbox에 기록합니다.
|
|
83
|
-
알림을 직접 OS API로 호출하지 않습니다.
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"type": "schedule_notification",
|
|
88
|
-
"id": 41001,
|
|
89
|
-
"key": "school_life:morning:2026-08-14",
|
|
90
|
-
"title": "내일 학교 일정",
|
|
91
|
-
"body": "1교시는 수학입니다.",
|
|
92
|
-
"at_millis": 1786640400000,
|
|
93
|
-
"category": "reminder",
|
|
94
|
-
"visibility": "private",
|
|
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
|
-
]
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
지원 type:
|
|
106
|
-
|
|
107
|
-
- `show_notification`: 즉시 표시
|
|
108
|
-
- `schedule_notification`: `at_millis`에 예약
|
|
109
|
-
- `cancel_notification`: 같은 `id`와 `key` 취소
|
|
110
|
-
|
|
111
|
-
공통 한도는 `id` 0~2,147,483,647, `key` 최대 240자, `title` 160자, `body` 500자,
|
|
112
|
-
`big_text` 2,000자입니다. `category`는 `general`, `reminder`, `progress`, `status`,
|
|
113
|
-
`visibility`는 `private`, `public`, `secret`입니다. 예약은 현재부터 최대 366일 안입니다.
|
|
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
|
-
|
|
121
|
-
progress:
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"current": 3,
|
|
126
|
-
"max": 10,
|
|
127
|
-
"indeterminate": false
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
`max`는 1~1,000,000이고 `current`는 0~max입니다. 진행 알림은 실제 작업 상태와 일치해야 하며 완료된
|
|
132
|
-
뒤 ongoing 상태로 남기지 않습니다.
|
|
133
|
-
|
|
134
|
-
## Durable delivery
|
|
135
|
-
|
|
136
|
-
Host는 pending action을 lease한 뒤 플랫폼 gateway에 전달하고 결과를 ACK합니다. 앱이 중단되어도
|
|
137
|
-
lease가 만료되면 재전달할 수 있으므로 `key`를 안정적인 중복 제거 키로 만듭니다. 권한 철회,
|
|
138
|
-
Instance disable/delete, package generation 변경은 해당 owner의 pending action을 폐기합니다.
|
|
139
|
-
|
|
140
|
-
현재 알림 gateway 실사용 검증은 Android 기준입니다. iOS/Desktop은 같은 Host action 계약 위에
|
|
141
|
-
플랫폼 adapter를 구현해야 합니다. 지원되지 않는 플랫폼에서 알림 실패를 원본 데이터 저장 성공으로
|
|
142
|
-
위장하지 않되, 알림 하나 때문에 읽기 화면 전체를 깨뜨리지 않습니다.
|
|
143
|
-
|
|
144
|
-
## Background
|
|
145
|
-
|
|
146
|
-
Background capability는 `background` 권한과 15~10,080분의 `interval_minutes`가 필요합니다.
|
|
147
|
-
Host는 enabled, granted permission, generation, 연결 상태를 확인한 뒤 실행합니다. 앱이 꺼져 있을 때
|
|
148
|
-
정확한 시각 실행을 보장하는 cron으로 설명하지 않습니다. 사용자에게 주기, 마지막 실행, 오류,
|
|
149
|
-
끄기 동작을 settings에서 제공합니다.
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Android, iOS, Desktop 호환
|
|
2
|
-
|
|
3
|
-
Morit 앱은 현재 Android-first입니다. Plugin UI와 capability 계약은 플랫폼 중립 JSON이지만, 모든
|
|
4
|
-
Host 기능이 모든 플랫폼에서 구현되었다는 뜻은 아닙니다.
|
|
5
|
-
|
|
6
|
-
## 현재 지원 범위
|
|
7
|
-
|
|
8
|
-
| 영역 | Android | iOS | Desktop |
|
|
9
|
-
|---|---|---|---|
|
|
10
|
-
| Manifest·서명·설치 계약 | 지원 | Host 구현 필요 | Host 구현 필요 |
|
|
11
|
-
| Material 3 Runtime tree | 지원 | 공용 Flutter renderer 재사용 목표 | 공용 Flutter renderer 재사용 목표 |
|
|
12
|
-
| capability와 Connector 호출 | 지원 | Host 실행 경계 구현 필요 | Host 실행 경계 구현 필요 |
|
|
13
|
-
| 일반 Plugin storage/settings | 지원 | Host 저장 adapter 필요 | Host 저장 adapter 필요 |
|
|
14
|
-
| 알림 예약·표시 | Android gateway 지원 | iOS gateway 필요 | OS별 gateway 필요 |
|
|
15
|
-
| Android 공유·다운로드 네이티브 기능 | 지원 | 동일 동작 아님 | 동일 동작 아님 |
|
|
16
|
-
|
|
17
|
-
문서나 배포 설명에서 iOS·Desktop을 현재 검증 완료로 표현하지 않습니다. 플랫폼 지원은 실제 앱
|
|
18
|
-
빌드와 기기 테스트 결과로 표시합니다.
|
|
19
|
-
|
|
20
|
-
## 플랫폼 중립으로 작성하기
|
|
21
|
-
|
|
22
|
-
- Dart, JavaScript, HTML, WebView, native view를 UI fragment에 넣지 않습니다.
|
|
23
|
-
- 색상 코드와 픽셀 고정 화면 대신 Host token과 반응형 속성을 사용합니다.
|
|
24
|
-
- 파일 경로는 POSIX package 상대 경로만 사용합니다.
|
|
25
|
-
- 외부 앱 실행이나 OS 설정 화면 이동을 capability 성공의 필수 조건으로 만들지 않습니다.
|
|
26
|
-
- notification은 Host action으로 요청하고 플랫폼 채널 이름을 manifest에 넣지 않습니다.
|
|
27
|
-
- touch 전용 표현 대신 “선택”, “열기”, “실행”처럼 입력 장치와 무관한 문구를 씁니다.
|
|
28
|
-
|
|
29
|
-
## 반응형 입력과 접근성
|
|
30
|
-
|
|
31
|
-
Android의 좁은 세로 화면을 최소 기준으로 설계하되, 넓은 창에서는 무조건 빈 공간을 채우지
|
|
32
|
-
않습니다. `stack_at`과 `min_item_width`로 구조만 바꾸고 읽는 순서는 유지합니다. 키보드 focus와
|
|
33
|
-
스크린 리더 순서는 JSON children 순서를 따르므로 시각적 배치와 의미 순서를 다르게 만들지 않습니다.
|
|
34
|
-
|
|
35
|
-
`icon`만 있는 action은 의미를 전달하기 어렵습니다. 주요 행동에는 텍스트 `label`을 제공하고,
|
|
36
|
-
색만으로 상태를 구분하지 않습니다. 시스템 글자 크기가 커져도 핵심 문구와 행동이 잘리지 않도록
|
|
37
|
-
중요 텍스트에 과도한 `max_lines`를 쓰지 않습니다.
|
|
38
|
-
|
|
39
|
-
## 기능 저하 원칙
|
|
40
|
-
|
|
41
|
-
특정 플랫폼 adapter가 없을 때 전체 플러그인을 깨뜨리지 않습니다.
|
|
42
|
-
|
|
43
|
-
- UI는 읽을 수 있는 상태와 지원 범위 안내를 유지합니다.
|
|
44
|
-
- 해당 action만 비활성화하거나 명확한 오류로 종료합니다.
|
|
45
|
-
- capability는 성공으로 위장하지 않습니다.
|
|
46
|
-
- 알림을 만들지 못해도 원본 일정·데이터 저장 결과는 별도로 판단합니다.
|
|
47
|
-
- 재시도가 의미 있는 경우에만 재시도 행동을 제공합니다.
|
|
48
|
-
|
|
49
|
-
## 출시 전 플랫폼 체크
|
|
50
|
-
|
|
51
|
-
각 지원 플랫폼에서 다음을 실제로 확인합니다.
|
|
52
|
-
|
|
53
|
-
1. 설치·업데이트·삭제
|
|
54
|
-
2. 첫 화면, 빈 데이터, 긴 텍스트, 큰 글자 크기
|
|
55
|
-
3. 화면 이동과 뒤로가기/창 닫기
|
|
56
|
-
4. 권한 허용·거부·철회
|
|
57
|
-
5. OAuth 연결·만료·재연결
|
|
58
|
-
6. background와 notification의 실제 전달
|
|
59
|
-
7. 오프라인·timeout·재시도
|
|
60
|
-
8. 키보드와 스크린 리더 순서
|
|
61
|
-
|
|
62
|
-
실행하지 않은 항목은 “미검증”으로 기록합니다.
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# Plugin Local Storage와 AI 접근
|
|
2
|
-
|
|
3
|
-
Plugin Local Storage는 Host가 관리하는 영구 JSON 저장소입니다. 데이터는 `user_id + installation_id +
|
|
4
|
-
instance_id + namespace + key`로 분리되며 플러그인 코드가 파일 경로나 DB에 직접 접근하지 않습니다.
|
|
5
|
-
UI, Tool, Skill, Background는 같은 Instance 저장소를 사용합니다.
|
|
6
|
-
|
|
7
|
-
## Manifest 선언
|
|
8
|
-
|
|
9
|
-
```json
|
|
10
|
-
{
|
|
11
|
-
"schema_version": 2,
|
|
12
|
-
"permissions": ["storage", "ai_storage"],
|
|
13
|
-
"data_policy": "retain",
|
|
14
|
-
"storage": {
|
|
15
|
-
"version": 2,
|
|
16
|
-
"namespaces": [
|
|
17
|
-
{"id": "default", "max_bytes": 131072, "ai_access": "read"},
|
|
18
|
-
{"id": "tasks", "max_bytes": 262144, "ai_access": "read_write"}
|
|
19
|
-
],
|
|
20
|
-
"migrations": [
|
|
21
|
-
{
|
|
22
|
-
"from": 1,
|
|
23
|
-
"to": 2,
|
|
24
|
-
"namespace": "tasks",
|
|
25
|
-
"rename": {"today": "today_tasks"},
|
|
26
|
-
"delete": ["legacy_filter"]
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
- `storage` 선언에는 schema 2와 `storage` 권한이 필요합니다.
|
|
34
|
-
- namespace는 1~16개, ID는 안전한 plugin identifier입니다.
|
|
35
|
-
- `max_bytes`는 1~256 KiB이고 전체 Installation은 512 KiB 이하입니다.
|
|
36
|
-
- namespace마다 최대 256개 key, key는 최대 80자, 값 하나는 UTF-8 JSON 32 KiB 이하입니다.
|
|
37
|
-
- migration 선언은 최대 64개이며 version은 1부터 1000까지 한 단계씩 증가합니다.
|
|
38
|
-
- 값은 문자열, 유한한 숫자, boolean, `null`, JSON 객체·목록을 사용할 수 있습니다.
|
|
39
|
-
- `ai_access`는 `none`, `read`, `read_write`입니다. 하나라도 AI에 공개하면 Manifest에
|
|
40
|
-
`ai_storage` 권한도 선언해야 합니다.
|
|
41
|
-
|
|
42
|
-
schema 1 플러그인이 기존 `storage` 권한만 요청한 경우 Host는 호환용 `default` namespace(version 1,
|
|
43
|
-
128 KiB)를 제공합니다. 새 플러그인은 명시적 schema 2 선언을 사용하세요.
|
|
44
|
-
|
|
45
|
-
## 표준 Storage Tool
|
|
46
|
-
|
|
47
|
-
Host는 저장소를 선언한 각 Instance에 다음 capability를 제공합니다.
|
|
48
|
-
|
|
49
|
-
| Tool | arguments | 동작 |
|
|
50
|
-
|---|---|---|
|
|
51
|
-
| `morit.storage.list` | `namespace`, 선택 `query`, 선택 `limit` | key와 값 검색·목록 |
|
|
52
|
-
| `morit.storage.get` | `namespace`, `key` | 단일 항목 조회 |
|
|
53
|
-
| `morit.storage.create` | `namespace`, `key`, `value` | 없는 key 생성 |
|
|
54
|
-
| `morit.storage.update` | `namespace`, `key`, `value`, `revision` | revision 일치 시 수정 |
|
|
55
|
-
| `morit.storage.delete` | `namespace`, `key`, `revision` | revision 일치 시 삭제 |
|
|
56
|
-
|
|
57
|
-
`namespace`를 생략하면 `default`, 없으면 첫 namespace를 사용합니다. `list.limit`은 1~100이고
|
|
58
|
-
응답 크기 때문에 일부만 반환하면 `data.has_more`가 `true`입니다. create는 같은 key를 덮어쓰지
|
|
59
|
-
않으며 update/delete는 직전에 읽은 양의 정수 `revision`을 요구합니다.
|
|
60
|
-
|
|
61
|
-
모든 결과는 공통 `ExtensionResult` 형태입니다.
|
|
62
|
-
|
|
63
|
-
```json
|
|
64
|
-
{
|
|
65
|
-
"summary": "task-42 항목을 수정했어요.",
|
|
66
|
-
"data": {
|
|
67
|
-
"namespace": "tasks",
|
|
68
|
-
"item": {
|
|
69
|
-
"key": "task-42",
|
|
70
|
-
"value": {"title": "과학 보고서", "done": true},
|
|
71
|
-
"revision": 3,
|
|
72
|
-
"created_at": "2026-08-17T01:00:00+00:00",
|
|
73
|
-
"updated_at": "2026-08-17T02:00:00+00:00"
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"evidence": [
|
|
77
|
-
{"type": "plugin_storage", "instance_id": "...", "namespace": "tasks", "key": "task-42", "revision": 3}
|
|
78
|
-
]
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Saved Entity
|
|
83
|
-
|
|
84
|
-
AI에서 저장·즐겨찾기한 Plugin 항목은 별도 DB 사본을 만들지 않고 같은 Local Storage의
|
|
85
|
-
`saved_entities` namespace를 source of truth로 사용합니다. UI, Tool, Skill과 AI 표준 Storage Tool이
|
|
86
|
-
같은 key·revision을 읽고 쓰므로 한 경로의 변경이 다른 경로에도 즉시 반영됩니다.
|
|
87
|
-
|
|
88
|
-
```json
|
|
89
|
-
{
|
|
90
|
-
"type": "school.assignment",
|
|
91
|
-
"title": "과학 보고서",
|
|
92
|
-
"subtitle": "금요일까지",
|
|
93
|
-
"description": "실험 결과 정리",
|
|
94
|
-
"url": "https://school.example/assignments/42",
|
|
95
|
-
"image_url": "https://school.example/images/42.png",
|
|
96
|
-
"favorite": true,
|
|
97
|
-
"data": {"due": "2026-08-21"},
|
|
98
|
-
"source_capability": "school_life.assignments"
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
`type`과 `title`은 필수입니다. 선택 필드는 `subtitle`, `description`, HTTPS `url`/`image_url`,
|
|
103
|
-
boolean `favorite`, bounded JSON `data`, 안전한 identifier `source_capability`뿐입니다. CRUD는
|
|
104
|
-
`morit.storage.list/get/create/update/delete`를 그대로 사용하며 기존 namespace 권한, quota,
|
|
105
|
-
revision, generation, credential 차단 검사가 동일하게 적용됩니다.
|
|
106
|
-
|
|
107
|
-
## UI binding과 갱신
|
|
108
|
-
|
|
109
|
-
UI Runtime v2 data source에서 표준 Tool을 그대로 사용합니다.
|
|
110
|
-
|
|
111
|
-
```json
|
|
112
|
-
{
|
|
113
|
-
"id": "saved_tasks",
|
|
114
|
-
"capability": "morit.storage.list",
|
|
115
|
-
"trigger": "load",
|
|
116
|
-
"query": "",
|
|
117
|
-
"arguments": {"namespace": "tasks", "limit": 20}
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
표시는 `data.saved_tasks.data.items`를 source로 사용하는 `list`, 단일 값은
|
|
122
|
-
`{{data.saved_task.data.item.value.title}}`처럼 binding합니다. UI가 Storage create/update/delete를
|
|
123
|
-
완료하거나 AI 응답이 끝나면 Host가 storage revision을 갱신해 Storage data source만 다시 읽습니다.
|
|
124
|
-
앱 재개 시에도 갱신합니다. 일반 Tool 결과 data source는 불필요하게 재실행하지 않습니다.
|
|
125
|
-
서버 Background가 화면과 무관하게 값을 바꾸는 namespace는 source에 `refresh_seconds: 30` 이상을
|
|
126
|
-
선언해 화면이 열린 동안에도 주기적으로 맞춥니다.
|
|
127
|
-
|
|
128
|
-
Tool·Skill·Background capability는 `storage` 권한을 선언하면 동일 Instance의 `plugin_storage`
|
|
129
|
-
snapshot을 읽고, 결과의 `data.host_actions`에 `plugin_storage_set` 또는
|
|
130
|
-
`plugin_storage_delete`를 반환해 같은 canonical 저장소를 원자적으로 변경할 수 있습니다.
|
|
131
|
-
`namespace`를 명시하면 해당 namespace를 사용합니다.
|
|
132
|
-
|
|
133
|
-
## AI 권한과 보안 경계
|
|
134
|
-
|
|
135
|
-
설치 또는 설정에서 사용자가 `ai_storage`를 한 번 허용하면 Morit AI는 매 CRUD마다 다시 승인받지
|
|
136
|
-
않습니다. 하지만 다음 검사는 항상 적용됩니다.
|
|
137
|
-
|
|
138
|
-
- 현재 사용자에게 속하고 활성화된 Instance만 선택
|
|
139
|
-
- 해당 Installation·Instance·namespace와 package generation 일치
|
|
140
|
-
- `ai_access: read`는 list/get만, `read_write`만 mutation 허용
|
|
141
|
-
- 다른 사용자, 다른 Plugin/Instance, 선언하지 않은 namespace 접근 차단
|
|
142
|
-
- key 이름에 credential, password, secret, token, access token, refresh token, API key,
|
|
143
|
-
private key 계열을 저장하지 못하도록 차단
|
|
144
|
-
- 실행 코드·파일 경로·DB 접근은 제공하지 않음
|
|
145
|
-
|
|
146
|
-
Host가 AI에 노출하는 tool handle은 Instance와 capability를 해시한 불투명 ID입니다. AI는 임의
|
|
147
|
-
`instance_id`를 arguments로 넘겨 scope를 바꿀 수 없습니다.
|
|
148
|
-
|
|
149
|
-
## 오류 구분
|
|
150
|
-
|
|
151
|
-
| code | 의미 | 처리 |
|
|
152
|
-
|---|---|---|
|
|
153
|
-
| `PLUGIN_STORAGE_PERMISSION_DENIED` | 권한·namespace·AI access 부족 | 설정과 manifest 확인 |
|
|
154
|
-
| `PLUGIN_STORAGE_NOT_FOUND` | key 없음 | create 여부를 사용자 의도에 따라 결정 |
|
|
155
|
-
| `PLUGIN_STORAGE_QUOTA_EXCEEDED` | 값·namespace·Installation 한도 초과 | 데이터를 줄이거나 정리 |
|
|
156
|
-
| `PLUGIN_STORAGE_CONFLICT` | 중복 create, stale revision/generation | list/get 후 최신 revision으로 재시도 |
|
|
157
|
-
| `PLUGIN_STORAGE_INVALID` | key, JSON, query, arguments 형식 오류 | 입력 수정 |
|
|
158
|
-
|
|
159
|
-
오류를 not-found나 빈 목록으로 바꾸지 마세요. 특히 conflict에서 revision 검사를 제거하면 동시에
|
|
160
|
-
수정한 사용자 데이터를 잃을 수 있습니다.
|
|
161
|
-
|
|
162
|
-
## 업데이트, migration, 삭제, 초기화
|
|
163
|
-
|
|
164
|
-
- 업데이트: 같은 Installation/Instance의 데이터는 유지되고 설치 transaction 안에서 version을
|
|
165
|
-
한 단계씩 올립니다. 각 `(from, namespace)` migration은 한 번만 선언하며 rename 대상 충돌 시
|
|
166
|
-
업데이트 전체가 실패해 이전 package와 데이터가 유지됩니다.
|
|
167
|
-
- `data_policy: purge`: uninstall 때 Plugin Local Storage를 삭제합니다.
|
|
168
|
-
- `data_policy: retain`: uninstall 뒤 같은 사용자·Plugin Installation ID로 재설치할 때 복구합니다.
|
|
169
|
-
- credential, OAuth state, pending action은 retain과 무관하게 별도 보안 수명주기로 정리됩니다.
|
|
170
|
-
- 사용자가 설정의 **저장 데이터 초기화**를 누르면 해당 Instance 데이터만 삭제합니다. HTTP API는
|
|
171
|
-
`DELETE /v1/plugin-instances/{instance_id}/storage`이며 다른 Instance에는 영향이 없습니다.
|
|
172
|
-
- disable은 데이터를 삭제하지 않지만 읽기·쓰기를 모두 막습니다.
|
|
173
|
-
|
|
174
|
-
배포 전에는 두 사용자와 둘 이상의 Instance로 격리, read-only AI mutation 차단, quota,
|
|
175
|
-
stale revision/generation, purge/retain, migration 충돌과 재시작 복원을 검증하세요.
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# 프로젝트 구조와 fragment
|
|
2
|
-
|
|
3
|
-
## 권장 디렉터리
|
|
4
|
-
|
|
5
|
-
```text
|
|
6
|
-
my-plugin/
|
|
7
|
-
├─ manifest.json
|
|
8
|
-
├─ README.md
|
|
9
|
-
├─ tools/*.json
|
|
10
|
-
├─ skills/*.json
|
|
11
|
-
├─ search/*.json
|
|
12
|
-
├─ notifications/*.json
|
|
13
|
-
├─ background/*.json
|
|
14
|
-
├─ ui/*.json
|
|
15
|
-
├─ credentials/*.json
|
|
16
|
-
├─ slash_commands/*.json
|
|
17
|
-
├─ src/**/*.py
|
|
18
|
-
├─ assets/*.{json,md,txt,png,jpg,jpeg,gif,webp}
|
|
19
|
-
├─ children/*.mplg
|
|
20
|
-
├─ morit-plugin.json
|
|
21
|
-
└─ dist/
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
`morit-plugin.json`과 `dist/`는 개발 상태와 산출물이므로 설치 패키지의 기능 계약이 아닙니다.
|
|
25
|
-
비밀 값도 두 위치에 저장하지 않습니다.
|
|
26
|
-
|
|
27
|
-
## fragment 병합 규칙
|
|
28
|
-
|
|
29
|
-
각 fragment 파일은 UTF-8 JSON 객체 하나입니다. 빌드할 때 파일명을 정렬한 순서로 다음 manifest
|
|
30
|
-
배열에 추가합니다.
|
|
31
|
-
|
|
32
|
-
| 디렉터리 | 대상 배열 | 자동으로 채우는 값 |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| `capabilities/` | `capabilities` | 없음 |
|
|
35
|
-
| `tools/` | `capabilities` | `kind: "tool"` |
|
|
36
|
-
| `skills/` | `capabilities` | `kind: "skill"` |
|
|
37
|
-
| `search/` | `capabilities` | `kind: "provider"`, `runtime.role: "search"` |
|
|
38
|
-
| `notifications/` | `capabilities` | `kind: "notification"` |
|
|
39
|
-
| `background/` | `capabilities` | `kind: "background"` |
|
|
40
|
-
| `ui/` | `ui_extensions` | 없음 |
|
|
41
|
-
| `credentials/` | `credentials` | 없음 |
|
|
42
|
-
| `slash_commands/` | `slash_commands` | 없음 |
|
|
43
|
-
|
|
44
|
-
같은 값이 fragment에 이미 있으면서 자동 값과 다르면 빌드가 실패합니다. manifest 배열과 fragment
|
|
45
|
-
배열은 합쳐지므로 ID는 전체에서 유일해야 합니다.
|
|
46
|
-
|
|
47
|
-
## source와 패키지에 들어가는 파일
|
|
48
|
-
|
|
49
|
-
검증은 프로젝트의 안전한 상대 POSIX 경로만 읽습니다. 절대 경로, `..`, 역슬래시, Windows 예약명,
|
|
50
|
-
심볼릭 링크는 사용할 수 없습니다. 프로젝트 전체 한도는 1 MiB, 파일 수는 64개, 파일 하나는
|
|
51
|
-
512 KiB입니다. 최종 `.mplg`는 2 MiB 이하입니다.
|
|
52
|
-
|
|
53
|
-
빌드가 설치 패키지에 포함하는 항목은 다음뿐입니다.
|
|
54
|
-
|
|
55
|
-
- fragment를 병합해 만든 `manifest.json`
|
|
56
|
-
- 선택적 `README.md`
|
|
57
|
-
- 선언된 `sandbox_python` entrypoint와 연결된 `src/**/*.py`
|
|
58
|
-
- 허용 확장자의 `assets/`
|
|
59
|
-
- dependency와 정확히 일치하는 `children/*.mplg`
|
|
60
|
-
- `signature.json`
|
|
61
|
-
|
|
62
|
-
fragment 원본은 compiled manifest에 병합되므로 `.mplg` 안에 중복해 넣지 않습니다. `src/*.py`는
|
|
63
|
-
각각 정확히 한 `sandbox_python` capability가 entrypoint로 선언해야 하며, 선언되지 않은 Python
|
|
64
|
-
파일이나 여러 capability가 공유하는 entrypoint는 거부됩니다.
|
|
65
|
-
|
|
66
|
-
## 텍스트와 바이너리
|
|
67
|
-
|
|
68
|
-
JSON, Markdown, 텍스트, Python은 엄격한 UTF-8입니다. PNG/JPEG/WebP/GIF와 child `.mplg`는
|
|
69
|
-
바이너리 그대로 보존됩니다. Cloud sync의 JSON 경계를 지날 때만 바이너리 envelope로 변환되며,
|
|
70
|
-
pull·source ZIP·build 결과에서는 원래 바이트로 복원됩니다.
|
|
71
|
-
|
|
72
|
-
UI에서 참조하는 package 이미지는 최대 512 KiB이고 가로·세로가 각각 4096 px 이하여야 합니다.
|
|
73
|
-
정적 이미지는 1 frame, GIF/WebP 애니메이션은 최대 128 frame이며 `width × height × frame 수`로
|
|
74
|
-
계산한 frame 합산 pixel은 최대 16,000,000입니다. 확장자·MIME에 대응하는 magic byte와 실제 이미지
|
|
75
|
-
포맷도 일치해야 합니다.
|
|
76
|
-
|
|
77
|
-
### 웹 프로젝트에서 아이콘 올리기
|
|
78
|
-
|
|
79
|
-
developers.moring.co의 **플러그인 프로젝트 관리 → 정보 → 앱 아이콘**에서는 PNG, JPEG, WebP
|
|
80
|
-
파일을 직접 선택하거나 끌어 놓을 수 있습니다. 업로드가 완료되면 이미지는
|
|
81
|
-
`assets/plugin-icon.*`에 저장되고 `manifest.icon`도 같은 revision에서 함께 갱신됩니다. 기존에 이
|
|
82
|
-
화면에서 올린 아이콘을 교체하거나 제거할 때도 파일과 manifest가 원자적으로 함께 반영됩니다.
|
|
83
|
-
|
|
84
|
-
아이콘은 512 KiB 이하, 가로·세로 각각 4096 px 이하, 전체 16,000,000 pixel 이하만 허용됩니다.
|
|
85
|
-
확장자만 바꾼 파일이나 실제 포맷이 다른 이미지는 서버 검증에서 거부됩니다. MCP가 바이너리 파일을
|
|
86
|
-
직접 전송하지 못하더라도 이 화면에서 한 번 올린 아이콘은 프로젝트 source에 포함되므로 이후 MCP,
|
|
87
|
-
CLI pull·build에서도 원래 이미지 바이트와 `manifest.icon` 경로가 그대로 유지됩니다.
|
|
88
|
-
|
|
89
|
-
## 파일별 책임
|
|
90
|
-
|
|
91
|
-
- `manifest.json`: 패키지 신원, 버전, 전역 권한, 연결과 데이터 정책
|
|
92
|
-
- capability fragment: AI나 UI가 실행할 한 가지 작업
|
|
93
|
-
- UI fragment: Host가 표시할 화면과 capability 참조
|
|
94
|
-
- credential/connector: 사용자 계정 또는 Cloud 비밀 값과 외부 endpoint의 연결
|
|
95
|
-
- `src/`: 검증되고 격리된 Python 작업만 포함
|
|
96
|
-
- `assets/`: 아이콘·정적 데이터·설명 자료
|
|
97
|
-
- `children/`: 함께 설치할 별도 서명 플러그인
|
|
98
|
-
|
|
99
|
-
화면 문구, 외부 endpoint, 비밀 값, 사용자 설정을 한 JSON에 섞지 마세요. 바뀌는 주기와 접근 권한이
|
|
100
|
-
다른 정보는 각각 UI, Connector, Secret, settings에 둡니다.
|
|
101
|
-
|
|
102
|
-
다음은 [Manifest 레퍼런스](manifest.md)입니다.
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# 원격 Plugin MCP
|
|
2
|
-
|
|
3
|
-
Remote Plugin MCP는 파일시스템이 없는 AI client가 Morit 조직의 Cloud Project를 만들고 검증·배포할
|
|
4
|
-
수 있게 하는 OAuth 보호 Streamable HTTP 서비스입니다.
|
|
5
|
-
|
|
6
|
-
```text
|
|
7
|
-
https://morit-api.moring.co/mcp
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
인증 전 응답은 `401`과 RFC 9728 protected-resource metadata 위치를 반환합니다. Morit SSO의
|
|
11
|
-
authorization code + PKCE 흐름으로 로그인하며 승인된 scope와 사용자 RLS를 모든 Tool에 적용합니다.
|
|
12
|
-
|
|
13
|
-
## 연결
|
|
14
|
-
|
|
15
|
-
Codex:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
codex mcp add morit-plugin-remote --url https://morit-api.moring.co/mcp
|
|
19
|
-
codex mcp login morit-plugin-remote
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Claude Code:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
claude mcp add --transport http --scope user \
|
|
26
|
-
morit-plugin-remote https://morit-api.moring.co/mcp
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Tool 목록
|
|
30
|
-
|
|
31
|
-
### 계약과 문서
|
|
32
|
-
|
|
33
|
-
| Tool | 목적 |
|
|
34
|
-
|---|---|
|
|
35
|
-
| `morit_sdk_contract` | SDK 1.7.12 Theme·Storage·UI/Host capability contract 전체 반환 |
|
|
36
|
-
| `morit_docs_search` | bundled 공식 문서 검색 |
|
|
37
|
-
| `morit_docs_get` | 검색 결과의 Markdown 한 파일 읽기 |
|
|
38
|
-
|
|
39
|
-
### 조직과 Project
|
|
40
|
-
|
|
41
|
-
| Tool | 목적 |
|
|
42
|
-
|---|---|
|
|
43
|
-
| `morit_organization_list` | 접근 가능한 조직과 role 조회 |
|
|
44
|
-
| `morit_project_list` | 조직의 Cloud Project 목록 |
|
|
45
|
-
| `morit_project_create` | 표준 source를 가진 Project 생성 |
|
|
46
|
-
| `morit_project_files_get` | 최신 revision과 source snapshot 읽기 |
|
|
47
|
-
| `morit_project_files_put` | revision 보호를 적용한 파일 생성·수정·삭제 |
|
|
48
|
-
| `morit_project_metadata_update` | Marketplace 표시 metadata 변경 |
|
|
49
|
-
| `morit_project_delete` | 활성 Deployment가 없는 Project 삭제 |
|
|
50
|
-
|
|
51
|
-
### 검증과 artifact
|
|
52
|
-
|
|
53
|
-
| Tool | 목적 |
|
|
54
|
-
|---|---|
|
|
55
|
-
| `morit_project_validate` | 현재 revision의 source 계약 검사 |
|
|
56
|
-
| `morit_project_preview` | light/dark 토글을 포함한 script-free private HTML preview 생성 |
|
|
57
|
-
| `morit_project_source_download` | 편집 가능한 source ZIP 생성 |
|
|
58
|
-
| `morit_build_start` | immutable build/Deployment 시작 |
|
|
59
|
-
| `morit_build_status` | build state와 package artifact 조회 |
|
|
60
|
-
| `morit_artifact_download` | owned artifact의 짧은 수명 download URL 생성 |
|
|
61
|
-
|
|
62
|
-
### Deployment
|
|
63
|
-
|
|
64
|
-
| Tool | 목적 |
|
|
65
|
-
|---|---|
|
|
66
|
-
| `morit_deployment_list` | Project의 immutable Deployment history |
|
|
67
|
-
| `morit_deployment_get` | build 결과, hash, logs, visibility 조회 |
|
|
68
|
-
| `morit_deployment_publish` | completed Deployment를 private/public current로 설정 |
|
|
69
|
-
|
|
70
|
-
### Secret과 Connection
|
|
71
|
-
|
|
72
|
-
| Tool | 목적 |
|
|
73
|
-
|---|---|
|
|
74
|
-
| `morit_project_secret_list` | Secret ID와 masked metadata 조회 |
|
|
75
|
-
| `morit_project_secret_put` | encrypted Secret 생성·rotation |
|
|
76
|
-
| `morit_project_secret_delete` | 참조되지 않는 Secret 삭제 |
|
|
77
|
-
| `morit_project_connection_list` | OAuth/API Connection 선언 조회 |
|
|
78
|
-
| `morit_project_connection_put` | Connection 생성·수정 |
|
|
79
|
-
| `morit_project_connection_delete` | Connection 선언 삭제 |
|
|
80
|
-
|
|
81
|
-
Secret Tool은 평문을 반환하지 않습니다. source, log, Tool 응답에 Secret 값을 복사하지 마세요.
|
|
82
|
-
|
|
83
|
-
## ID와 경로
|
|
84
|
-
|
|
85
|
-
- organization, Project, Deployment, artifact ID는 `format: uuid`입니다.
|
|
86
|
-
- Manifest plugin ID는 소문자 reverse-domain ID입니다.
|
|
87
|
-
- source path는 상대 POSIX 형식입니다.
|
|
88
|
-
- `..`, 역슬래시, 절대 경로, Windows 예약명, symlink는 거부합니다.
|
|
89
|
-
- `manifest.json`은 삭제할 수 없습니다.
|
|
90
|
-
- 한 파일 512 KiB, source 최대 64개 파일·1 MiB입니다.
|
|
91
|
-
- 한 `files_put` 변경은 최대 64개입니다.
|
|
92
|
-
|
|
93
|
-
AI가 UUID나 경로를 추측하지 않고 list/create/get 결과를 그대로 다음 Tool에 전달해야 합니다.
|
|
94
|
-
|
|
95
|
-
## Revision과 파일 변경
|
|
96
|
-
|
|
97
|
-
```json
|
|
98
|
-
{
|
|
99
|
-
"project_id": "00000000-0000-4000-8000-000000000000",
|
|
100
|
-
"revision": 3,
|
|
101
|
-
"files": {
|
|
102
|
-
"ui/home.json": "{...}\n",
|
|
103
|
-
"ui/old.json": null
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
`null`은 파일 삭제입니다. revision이 오래되면 전체 요청을 거부하고 최신 source를 반환하므로
|
|
109
|
-
`files_get` → 변경 병합 → `files_put`을 반복합니다. conflict를 `force`로 숨기지 않습니다.
|
|
110
|
-
|
|
111
|
-
이미지 asset과 child `.mplg`는 `morit-base64-v1:<canonical-base64>` binary envelope로만 JSON
|
|
112
|
-
경계를 지나며 MIME magic과 경로를 검증합니다. 최종 source ZIP과
|
|
113
|
-
package에서는 원래 바이트로 복원됩니다.
|
|
114
|
-
|
|
115
|
-
Theme/Storage를 생성·수정할 때는 contract의 `ui_runtime.theme_*`, `storage_tools`,
|
|
116
|
-
`storage_ai_access`, `storage_limits`를 기준으로 Manifest와 UI fragment를 함께 변경합니다. Remote
|
|
117
|
-
MCP validation은 Host와 같은 잘못된 token, 확정적으로 보이지 않는 색 조합, namespace/permission,
|
|
118
|
-
migration과 binding 오류를 거부하고 정적으로 확정하기 어려운 대비는 warning으로 반환합니다.
|
|
119
|
-
|
|
120
|
-
## Build와 다운로드
|
|
121
|
-
|
|
122
|
-
```text
|
|
123
|
-
morit_build_start
|
|
124
|
-
→ job_id와 deployment_id
|
|
125
|
-
morit_build_status 반복
|
|
126
|
-
→ completed + artifact_id + size + sha256
|
|
127
|
-
morit_deployment_get
|
|
128
|
-
→ exact artifact hash와 visibility
|
|
129
|
-
morit_artifact_download
|
|
130
|
-
→ 짧은 수명 URL
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
같은 요청을 재시도할 때 새 build가 생성될 수 있으므로 반환된 ID를 섞지 않습니다. status가
|
|
134
|
-
`failed`이면 logs의 공개 진단을 source에 반영하고 새 build를 시작합니다. `processing`을 완료로
|
|
135
|
-
간주하지 않습니다.
|
|
136
|
-
|
|
137
|
-
## Protocol compatibility
|
|
138
|
-
|
|
139
|
-
서비스는 직접 `tools/list`/`tools/call`을 보내는 최신 handshake-free client와
|
|
140
|
-
initialize → initialized → Tool 호출 방식의 client를 모두 지원합니다. `MCP-Protocol-Version`과
|
|
141
|
-
`Mcp-Method`/`Mcp-Name` 같은 routing header는 서버가 협상한 값과 맞춰야 합니다. 지원 protocol
|
|
142
|
-
date는 서비스 응답을 사용하며 client가 임의로 고정하지 않습니다.
|
|
143
|
-
|
|
144
|
-
## 복구
|
|
145
|
-
|
|
146
|
-
| 오류 | 대응 |
|
|
147
|
-
|---|---|
|
|
148
|
-
| `401` | OAuth metadata를 따라 로그인 |
|
|
149
|
-
| scope 부족 | 필요한 scope를 사용자에게 명시하고 재승인 |
|
|
150
|
-
| Project 없음 | 현재 조직과 Project ID 확인 |
|
|
151
|
-
| revision conflict | 최신 source를 읽고 병합 |
|
|
152
|
-
| validation 오류 | 반환된 정확한 field/reference 진단 수정 |
|
|
153
|
-
| build timeout | status로 기존 build 확인 후 필요한 경우 새 build |
|
|
154
|
-
| artifact 만료 | 같은 artifact ID로 새 download URL 발급 |
|
|
155
|
-
| Secret 참조 중 | Connection 참조를 먼저 정리 |
|
|
156
|
-
|
|
157
|
-
로컬 파일을 직접 편집해야 하면 [Local MCP](sdk-and-mcp.md)를 사용합니다. Remote 서비스에 local
|
|
158
|
-
absolute path나 publisher private key를 전달하지 않습니다.
|