@rainbow-robotics/muscat-plugin-kit 1.10.3-dev.3 → 1.10.3
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.ko.md +187 -60
- package/README.md +187 -60
- package/cli/README.md +3 -1
- package/cli/bin/muscat-pk.mjs +5 -1
- package/cli/src/completion.mjs +2 -0
- package/cli/src/dev.mjs +19 -2
- package/cli/src/devBack.mjs +1 -1
- package/cli/src/devUi.mjs +2 -1
- package/cli/src/muscatLocal.mjs +55 -19
- package/cli/src/sim.mjs +163 -0
- package/cli/src/util.mjs +2 -0
- package/cli/storybook-dist/assets/{Color-YHDXOIA2-Cp8LHsF-.js → Color-YHDXOIA2-DSB_yvex.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-Kg1Im4XT.js → Docs-C_90bDRy.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-UJwiVwO2.js → Docs-CbW9HZd4.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-LeW_BIsw.js → Docs-CoskoRMT.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-B2ox3o_G.js → Docs-Cy9XFeLJ.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-By11aG_M.js → Docs-De1oc64k.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-ik2AhpV0.js → Docs-klD3F_Ai.js} +1 -1
- package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-DPFneFUi.js → DocsRenderer-CFRXHY34-Bkm4uV_D.js} +1 -1
- package/cli/storybook-dist/assets/{NxAvatar.stories-CwnK8AUF.js → NxAvatar.stories-Ctf5G2FQ.js} +1 -1
- package/cli/storybook-dist/assets/{NxButton.stories-ueYdosfQ.js → NxButton.stories-DeLObVSj.js} +1 -1
- package/cli/storybook-dist/assets/{NxNumberInput.stories-M5ZCLbrK.js → NxNumberInput.stories-LqoY0ed1.js} +1 -1
- package/cli/storybook-dist/assets/{NxPagination.stories-bxxUnHEL.js → NxPagination.stories-DI6bXuJM.js} +1 -1
- package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-BHxB2kr5.js → NxPointerHoldButton.stories-3HSV1XmN.js} +1 -1
- package/cli/storybook-dist/assets/{NxRangeInput.stories-BKfr-fp4.js → NxRangeInput.stories-1pjgQVaG.js} +1 -1
- package/cli/storybook-dist/assets/{NxSelectBox.stories-CG8FuTBq.js → NxSelectBox.stories-bcQPZEuF.js} +1 -1
- package/cli/storybook-dist/assets/{NxSlideRange.stories-BRTFtMTL.js → NxSlideRange.stories-BosX4axk.js} +1 -1
- package/cli/storybook-dist/assets/{NxSwitch.stories-Bwuyzn-R.js → NxSwitch.stories-DxV6dS33.js} +1 -1
- package/cli/storybook-dist/assets/{NxTextArea.stories-COq2sGqu.js → NxTextArea.stories-CO545-LP.js} +1 -1
- package/cli/storybook-dist/assets/{NxTextInput.stories-CjsBLz05.js → NxTextInput.stories-BP0npai1.js} +1 -1
- package/cli/storybook-dist/assets/{RBAccordion.stories-BARwuZ0Q.js → RBAccordion.stories-fVR4Va2a.js} +1 -1
- package/cli/storybook-dist/assets/{RBAccordionGroup.stories-COrPxETW.js → RBAccordionGroup.stories-Du5bsckD.js} +1 -1
- package/cli/storybook-dist/assets/{RBAlert.stories-BQrkjZPt.js → RBAlert.stories-DNkMXUu6.js} +1 -1
- package/cli/storybook-dist/assets/{RBBadge.stories-BTsLL-lK.js → RBBadge.stories-C44pXt8D.js} +1 -1
- package/cli/storybook-dist/assets/{RBBattery.stories-DAeQdJbc.js → RBBattery.stories-DE2JHvfL.js} +1 -1
- package/cli/storybook-dist/assets/{RBCard.stories-HyhOE80R.js → RBCard.stories-CuIda7NI.js} +1 -1
- package/cli/storybook-dist/assets/{RBCheckBox.stories-KFk0WqX_.js → RBCheckBox.stories-D1qM1ayr.js} +1 -1
- package/cli/storybook-dist/assets/{RBCheckIconButton.stories-CXbbktF5.js → RBCheckIconButton.stories-e4gkliBO.js} +1 -1
- package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-DWfYz_9Z.js → RBCheckLabelButton.stories-CDzCtsMI.js} +1 -1
- package/cli/storybook-dist/assets/{RBChipTab.stories-JPEgVFAS.js → RBChipTab.stories-DBrc_3Mp.js} +1 -1
- package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-CTPuWXrQ.js → RBChipsBoxTab.stories-hlrsHjKY.js} +1 -1
- package/cli/storybook-dist/assets/{RBColorIcons.stories-DiFRZ5jk.js → RBColorIcons.stories-BHUvpkdu.js} +1 -1
- package/cli/storybook-dist/assets/{RBCommand.stories-Pf1HAVIR.js → RBCommand.stories-azAF41NP.js} +1 -1
- package/cli/storybook-dist/assets/{RBConfirm.stories-D1cEPUXk.js → RBConfirm.stories-BMNjwbmT.js} +1 -1
- package/cli/storybook-dist/assets/{RBDateInput.stories-RPYpCjig.js → RBDateInput.stories-D0VezDzv.js} +1 -1
- package/cli/storybook-dist/assets/{RBDialog.stories-DJH-0XSR.js → RBDialog.stories-AOJ975Fy.js} +1 -1
- package/cli/storybook-dist/assets/{RBDivider.stories-uEj1vOgK.js → RBDivider.stories-NSvnj43Z.js} +1 -1
- package/cli/storybook-dist/assets/{RBDrawer.stories-BiMzElb2.js → RBDrawer.stories-Du0oTvD1.js} +1 -1
- package/cli/storybook-dist/assets/{RBDropdownMenu.stories-Da_TBkla.js → RBDropdownMenu.stories-Hsxve4PW.js} +1 -1
- package/cli/storybook-dist/assets/{RBEmptyView.stories-BHeKGfPS.js → RBEmptyView.stories-BlwHx3zp.js} +1 -1
- package/cli/storybook-dist/assets/{RBIconButton.stories-NEsRFoYp.js → RBIconButton.stories-DAPDY63x.js} +1 -1
- package/cli/storybook-dist/assets/{RBIcons.stories-CcZ-TTLS.js → RBIcons.stories-W5ZUV2L1.js} +1 -1
- package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-B0wpdQyR.js → RBInlineLabelButton.stories-C99j0Y3g.js} +1 -1
- package/cli/storybook-dist/assets/{RBJoyStick.stories-DEgBou3K.js → RBJoyStick.stories-Du0H1iAS.js} +1 -1
- package/cli/storybook-dist/assets/{RBLabelButton.stories-DT8drIsF.js → RBLabelButton.stories-BlvHaU_Z.js} +1 -1
- package/cli/storybook-dist/assets/{RBLight.stories-Dh-aoeyG.js → RBLight.stories-BY6DKrre.js} +1 -1
- package/cli/storybook-dist/assets/{RBMenubar.stories-D-H7UqVh.js → RBMenubar.stories-D2PEGpM9.js} +1 -1
- package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-hlclv6jI.js → RBMultiRangeInput.stories-Bm2qSmjq.js} +1 -1
- package/cli/storybook-dist/assets/{RBPagination.stories-DWCeoZ_S.js → RBPagination.stories-DDleJa5N.js} +1 -1
- package/cli/storybook-dist/assets/{RBPopover.stories-C514M09_.js → RBPopover.stories-27YxPSFG.js} +1 -1
- package/cli/storybook-dist/assets/{RBPrompt.stories-BSItvfiT.js → RBPrompt.stories-BFaIOctr.js} +1 -1
- package/cli/storybook-dist/assets/{RBRadioBox.stories-BBb4YbFU.js → RBRadioBox.stories-BroDar0t.js} +1 -1
- package/cli/storybook-dist/assets/{RBRadioButton.stories-CXyOGJCv.js → RBRadioButton.stories-M3O-wX7V.js} +1 -1
- package/cli/storybook-dist/assets/{RBRangeDateInput.stories-qvhubu0e.js → RBRangeDateInput.stories-CWlM_D5X.js} +1 -1
- package/cli/storybook-dist/assets/{RBResizable.stories-CZKeyGjH.js → RBResizable.stories-ui8X1qnc.js} +1 -1
- package/cli/storybook-dist/assets/{RBSelectBox.stories-CRKP3ybf.js → RBSelectBox.stories-C8T6cfA0.js} +1 -1
- package/cli/storybook-dist/assets/{RBSheet.stories-sCM3ZJUa.js → RBSheet.stories-BCOo_Zcc.js} +1 -1
- package/cli/storybook-dist/assets/{RBSideNumberInput.stories-Bb7gw8RF.js → RBSideNumberInput.stories-CU2_MVlJ.js} +1 -1
- package/cli/storybook-dist/assets/{RBSlider.stories-23OAQU5z.js → RBSlider.stories-DgOp1TFc.js} +1 -1
- package/cli/storybook-dist/assets/{RBSpinner.stories-2xcQdcmt.js → RBSpinner.stories-qLScY3zZ.js} +1 -1
- package/cli/storybook-dist/assets/{RBSwitch.stories-BACUaJm_.js → RBSwitch.stories-SXSY1QMb.js} +1 -1
- package/cli/storybook-dist/assets/{RBTab.stories-CDHE3sVs.js → RBTab.stories-BzXXTsXP.js} +1 -1
- package/cli/storybook-dist/assets/{RBTable.stories-C0Cfhfl7.js → RBTable.stories-DHTeRNVk.js} +1 -1
- package/cli/storybook-dist/assets/{RBTableRow.stories-BF8kIkpp.js → RBTableRow.stories-DRNRsd15.js} +1 -1
- package/cli/storybook-dist/assets/{RBTabs.stories-BdJWRq1o.js → RBTabs.stories-DP0noBLp.js} +1 -1
- package/cli/storybook-dist/assets/{RBTextArea.stories-CGy2xeYH.js → RBTextArea.stories-TkaejfxJ.js} +1 -1
- package/cli/storybook-dist/assets/{RBTextField.stories-IfUOzxCM.js → RBTextField.stories-B6VpCGyt.js} +1 -1
- package/cli/storybook-dist/assets/{RBTextInput.stories-VjXNFo4R.js → RBTextInput.stories-D3hZ08e-.js} +1 -1
- package/cli/storybook-dist/assets/{RBToast.stories-BzcobblV.js → RBToast.stories-BQ2wS2U0.js} +1 -1
- package/cli/storybook-dist/assets/{RBToggle.stories-DuLE5mKv.js → RBToggle.stories-GVOjGqo3.js} +1 -1
- package/cli/storybook-dist/assets/{RBToggleGroup.stories-CuR0N0GJ.js → RBToggleGroup.stories-pzx0btOD.js} +1 -1
- package/cli/storybook-dist/assets/{RBTooltip.stories--aAgNC8_.js → RBTooltip.stories-jufB6fa1.js} +1 -1
- package/cli/storybook-dist/assets/{SideNumberInput-DyWQxYUj.js → SideNumberInput-BYlmNHEw.js} +1 -1
- package/cli/storybook-dist/assets/{entry-preview-BTw3Jh8E.js → entry-preview-DY0ZrYNE.js} +1 -1
- package/cli/storybook-dist/assets/{iframe-BqPQIkPQ.js → iframe-LwKb1w63.js} +2 -2
- package/cli/storybook-dist/assets/{index-k_VkBflK.js → index-BsAhjDKO.js} +3 -3
- package/cli/storybook-dist/assets/{preview-Cog7BnbD.js → preview-BJ0FvQZh.js} +2 -2
- package/cli/storybook-dist/assets/{preview-C3ctYQQj.js → preview-DiY2_30w.js} +2 -2
- package/cli/storybook-dist/assets/{preview-CigNHo-I.css → preview-kTZPgP6D.css} +1 -1
- package/cli/storybook-dist/assets/{react-resizable-panels-Dp9zi-o3.css → react-resizable-panels-HsFbxPbn.css} +1 -1
- package/cli/storybook-dist/iframe.html +1 -1
- package/cli/storybook-dist/project.json +1 -1
- package/package.json +3 -3
- /package/cli/storybook-dist/assets/{react-resizable-panels.browser-8hC8dr-6.js → react-resizable-panels.browser-DuxpZLkF.js} +0 -0
package/README.ko.md
CHANGED
|
@@ -111,6 +111,7 @@ muscat-pk build # dist/ + 배포용 ZIP
|
|
|
111
111
|
| `muscat-pk build` | 빌드, Python 의존성 lock, wheel 동봉, ZIP |
|
|
112
112
|
| `muscat-pk update` | kit, `plugin-ui`, `preview-host` 업데이트 |
|
|
113
113
|
| `muscat-pk sdk <list\|update [version]>` | `rainbow-rb-sdk` 버전 관리 |
|
|
114
|
+
| `muscat-pk sim list` | `dev --sim`에 쓸 수 있는 로봇 시뮬레이터 목록 |
|
|
114
115
|
| `muscat-pk storybook` | 번들된 rb-components Storybook 보기 |
|
|
115
116
|
| `muscat-pk mcp` | AI 코딩 에이전트용 MCP 서버 |
|
|
116
117
|
| `muscat-pk uninstall` | CLI 삭제 |
|
|
@@ -134,9 +135,15 @@ muscat-pk new [dir] [--type python|cpp|ros2|ros2-cpp] [--pm npm|yarn|pnpm|bun]
|
|
|
134
135
|
| `--pm` | 프론트 패키지 매니저. Yarn 워크스페이스 안이면 기본 `yarn`, 아니면 `npm` |
|
|
135
136
|
| `--author` / `--email` / `--description` | manifest 필드 (email은 manifest 필수값) |
|
|
136
137
|
| `--yes` | 질문 없이 진행. 지정 안 한 값은 기본값 |
|
|
137
|
-
| `--no-install` |
|
|
138
|
+
| `--no-install` | `<pm> install`, IDE `.venv`, C++ dev 이미지 준비를 생략 |
|
|
139
|
+
| `--help` | 사용법 출력 |
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
플러그인 id는 `com.example.<dir>`(소문자)로 만들어지며 `manifest.json`에서 바꿉니다. `new`는 `.gitignore`, `AGENTS.md`, `CLAUDE.md`, `.mcp.json`(11장 참고)도 만들고, `git init` 후 첫 커밋을 남깁니다 (원치 않으면 `.git`을 지우세요).
|
|
142
|
+
|
|
143
|
+
`--no-install`이 아니면:
|
|
144
|
+
|
|
145
|
+
- python / ros2 (rclpy): IDE 전용 `.venv`를 만듭니다 (9.6 참고).
|
|
146
|
+
- cpp / ros2-cpp: 에디터가 SDK 헤더를 찾도록 C++ dev 이미지를 빌드합니다 (Docker가 없으면 경고 후 건너뜀).
|
|
140
147
|
|
|
141
148
|
### 3.2 `type change`
|
|
142
149
|
|
|
@@ -144,25 +151,67 @@ Python 프로젝트는 IDE 전용 `.venv`도 만들어집니다 (9.6 참고).
|
|
|
144
151
|
muscat-pk type change [--to python|cpp|ros2|ros2-cpp] [--readme]
|
|
145
152
|
```
|
|
146
153
|
|
|
147
|
-
|
|
154
|
+
| 옵션 | 의미 |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `--to` | 새 타입. 생략하면 번호로 물어봄 |
|
|
157
|
+
| `--readme` | 묻지 않고 `README.md`를 새 타입용으로 교체 (기존 파일 → `README.md.bak`) |
|
|
158
|
+
|
|
159
|
+
`manifest.json`의 `runtime`을 바꾸고, 해당 디렉터리가 없으면 새 백엔드 파일(`cpp/` 또는 `ros2_ws/`)을 추가합니다. 이전 타입의 파일(`cpp/`, `ros2_ws/` 등)은 지우지 않습니다. Python ↔ C++ 백엔드를 오갈 때는 README 교체를 물어봅니다 (`--readme`면 예로 처리). C++ 타입으로 바꾸면 `new`처럼 C++ dev 이미지를 준비합니다.
|
|
148
160
|
|
|
149
161
|
### 3.3 `dev`, `dev:ui`, `dev:back`
|
|
150
162
|
|
|
151
163
|
```bash
|
|
152
|
-
muscat-pk dev [--robot-ip <ip>] [--keep-id]
|
|
153
|
-
|
|
154
|
-
muscat-pk dev:
|
|
164
|
+
muscat-pk dev [--robot-ip <ip>] [--robot-token <token>] [--keep-id] [--sim <model>]
|
|
165
|
+
[--port <n>] [--no-open]
|
|
166
|
+
muscat-pk dev:ui [--robot-ip <ip>] [--port <n>] [--keep-id] [--no-open]
|
|
167
|
+
muscat-pk dev:back [--robot-ip <ip>] [--robot-token <token>] [--keep-id]
|
|
155
168
|
```
|
|
156
169
|
|
|
157
170
|
| 명령 | 실행되는 것 |
|
|
158
171
|
|---|---|
|
|
159
|
-
| `dev` | 로봇이 없으면 로컬 Muscat 런타임, 그리고 `dev:ui` + `dev:back` |
|
|
160
|
-
| `dev:ui` | 플러그인 Vite 개발 서버 + preview-host `http://localhost:4180
|
|
172
|
+
| `dev` | 로봇이 없거나 로봇에 Muscat이 없으면 로컬 Muscat 런타임, 그리고 `dev:ui` + `dev:back` |
|
|
173
|
+
| `dev:ui` | 플러그인 Vite 개발 서버 + preview-host `http://localhost:4180` (사용 중이면 다음 빈 포트). 브라우저로 엶 |
|
|
161
174
|
| `dev:back` | 로봇 환경과 같은 Docker 컨테이너에서 백엔드 실행. Python은 `py/**/*.py`가 바뀌면 재시작 (watchfiles) |
|
|
162
175
|
|
|
163
|
-
|
|
176
|
+
| 옵션 | 의미 |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `--robot-ip` | 개발 대상 로봇. 없으면 `MUSCAT_ROBOT_HOST`, 그것도 없으면 프롬프트 (비우면 로봇 없음) |
|
|
179
|
+
| `--robot-token` | 로그인 정책이 켜진 로봇의 service token. 없으면 `MUSCAT_ROBOT_TOKEN`, 그것도 없으면 프롬프트 (비우면 없음) |
|
|
180
|
+
| `--keep-id` | dev id 대신 manifest id로 실행 (9.3 참고) |
|
|
181
|
+
| `--sim` | `dev` 전용, 로봇 없음: 로봇 시뮬레이터 실행 (아래). 로봇 IP 프롬프트를 건너뜀 |
|
|
182
|
+
| `--port` | preview-host 포트 (기본 `4180`, 사용 중이면 다음 빈 포트) |
|
|
183
|
+
| `--no-open` | 브라우저를 열지 않음 (CI이거나 TTY가 아니어도 열지 않음) |
|
|
184
|
+
|
|
185
|
+
- dev 환경은 PC당 하나입니다. 백엔드 컨테이너, 로컬 Muscat과 그 포트를 같이 쓰기 때문입니다. 두 번째 `dev` / `dev:ui` / `dev:back`은 실행 중인 것을 멈출지 묻습니다 (`Stop it and start this one? [y/N]`).
|
|
164
186
|
- 원격 로봇에 붙으면 플러그인이 dev id로 실행됩니다 (9.3 참고). `--keep-id`는 manifest id를 그대로 씁니다.
|
|
165
|
-
- 시작 전에 manifest
|
|
187
|
+
- 시작 전에 `dev`와 `dev:back`이 `manifest.json`의 `variables`, `ports`를 검사합니다 (오류면 중단). `dev:back`은 나머지 manifest 오류도 경고로 출력합니다. 전체 검증은 `build`에서 합니다. 선언하지 않은 포트를 열면 백엔드를 멈춥니다 (5.6 참고).
|
|
188
|
+
- Muscat이 도는 로봇에 붙으면 UI가 그 로봇의 Muscat 화면이 되고 백엔드는 그 gateway를 씁니다. 로컬에 `muscat-dev` 컨테이너가 있거나 `--sim`이면 로컬 실제 Muscat 화면, 아니면 mock 프리뷰입니다.
|
|
189
|
+
- `dev` / `dev:ui`는 새 kit이 배포돼 있으면 알려 줍니다 (`MUSCAT_PK_NO_UPDATE_CHECK=1`로 생략).
|
|
190
|
+
- `dev`를 멈추면(Ctrl+C) 백엔드 컨테이너와, 자신이 띄운 로컬 Muscat·시뮬레이터를 지웁니다.
|
|
191
|
+
|
|
192
|
+
#### 로봇 없이 개발 (`--sim`)
|
|
193
|
+
|
|
194
|
+
로봇 IP 없이 `dev --sim <기종>`을 실행하면 로컬 Muscat 옆에 로봇 시뮬레이터를 같이 띄웁니다. 플러그인 SDK의 로봇 호출이 실제 coreSW에 닿습니다.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
muscat-pk sim list # 한 줄에 "<name>\t<실행되는 것>"
|
|
198
|
+
muscat-pk dev --sim 500920
|
|
199
|
+
muscat-pk dev --sim rby1-m_v1.2
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| `--sim` | 함께 뜨는 Muscat 서비스 | 시뮬레이터 |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| `500880`, `500920` | common, log, manipulate | `rainbowroboticsofficial/cobot-sim` 1개 |
|
|
205
|
+
| `RBY2` | common, log, manipulate | cobot-sim 3개 (C500880, C501880, C502880) |
|
|
206
|
+
| `rby1-a`, `rby1-m`, `rby1-ub` (`rby1-m_v1.2`처럼 버전 지정 가능) | common, log, rby1 | `rainbowroboticsofficial/rby1-sim` + Xvfb (로컬 빌드) |
|
|
207
|
+
|
|
208
|
+
- 시뮬레이터 기종이 로컬 Muscat의 로봇 모델로 등록됩니다(`500880`은 C500880, `500920`은 C500920, `rby1-m`은 m). UI는 mock 프리뷰가 아니라 실제 Muscat 화면(대시보드, 컴포넌트, Move, Log, Settings)으로 뜹니다.
|
|
209
|
+
- 시뮬레이터는 `--sim`을 줄 때만 뜹니다. 그 밖의 값이면 `--sim takes one of: 500880, 500920, RBY2, rby1-a, rby1-m, rby1-ub (rby1 also takes rby1-m_v1.2)` 오류가 납니다. manifest의 `targetModels`에 없는 기종이면 경고만 합니다.
|
|
210
|
+
- 시뮬레이터 이미지는 Docker Hub의 최신 태그를 씁니다 (rby1에 버전을 주면 그 버전 태그). 오프라인이면 이미 받아 둔 이미지를 씁니다.
|
|
211
|
+
- rby1-sim은 화면을 계속 렌더링하므로 CPU를 1코어 정도 씁니다. 상한은 `MUSCAT_PK_RBY1_CPUS`(기본 1.5)입니다.
|
|
212
|
+
- `muscat-dev` 같은 다른 Muscat이 떠 있으면 시뮬레이터를 붙이지 않습니다.
|
|
213
|
+
- amr 시뮬레이터는 아직 없습니다.
|
|
214
|
+
- 로컬 Muscat, 시뮬레이터, 백엔드는 로봇처럼 모두 `--ipc host`로 돕니다. 따라서 zenoh SHM과 `shm_open`이 서로 통합니다.
|
|
166
215
|
|
|
167
216
|
### 3.4 `build`
|
|
168
217
|
|
|
@@ -172,18 +221,23 @@ muscat-pk build [--arch amd64|arm64|both]
|
|
|
172
221
|
|
|
173
222
|
순서:
|
|
174
223
|
|
|
175
|
-
1. `manifest.json`의 새 버전을 물어봅니다 (비우면
|
|
176
|
-
2.
|
|
177
|
-
3. `contributes[].ui`
|
|
178
|
-
4.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
- ros2:
|
|
182
|
-
|
|
224
|
+
1. `manifest.json`의 새 버전을 물어봅니다 (비우면 그대로, TTY일 때만).
|
|
225
|
+
2. 대상 아키텍처를 묻고(`--arch`가 없을 때) 같은 버전의 기존 ZIP을 지웁니다. 빌드가 실패해도 예전 ZIP이 남지 않습니다.
|
|
226
|
+
3. manifest를 검증하고(5장 전체), `contributes[].ui`마다 `ui/<entry>.tsx`(또는 `.ts` / `.jsx` / `.js`)가 있는지 확인합니다.
|
|
227
|
+
4. `contributes[].ui` 엔트리마다 `vite build` → `dist/<entry>.js`. `contributes`가 없는 플러그인은 UI를 빌드하지 않습니다. Node.js 20 이상이 없으면(바이너리 CLI) `Build the plugin without its UI? [y/N]`를 묻고, 예면 `dist/manifest.json`에서 `contributes`를 뺍니다.
|
|
228
|
+
5. `manifest.json`, `assets/`, `README.md`, `README.<lang>.md`, `CHANGELOG.md`, `py/`, `requirements.txt`를 `dist/`에 복사합니다.
|
|
229
|
+
6. 백엔드:
|
|
230
|
+
- python / ros2 (rclpy): `requirements.txt`를 `dist/requirements.lock`으로 해석하고(`uv pip compile`, Python 3.12, 고정된 줄 중 pre-release가 있으면 pre-release 허용), arch별 바이너리 wheel(cp312, `--only-binary=:all:`)을 `dist/wheels/`에 받습니다. 맞는 wheel이 없는 패키지가 있으면 빌드가 멈춥니다.
|
|
231
|
+
- cpp: Docker에서 CMake Release 빌드 → `dist/<arch>/<entry>` + `dist/<arch>/lib/` (`librb_sdk.so`). CMake 타깃 이름이 `runtime.entry`와 같아야 합니다.
|
|
232
|
+
- ros2: Docker에서 colcon 빌드 → `dist/ros2_ws/install/` (rclpy, 한 번) 또는 `dist/ros2_ws/install-<arch>/` (rclcpp, arch별).
|
|
233
|
+
7. `installedSize`(`wheels/`를 뺀 `dist/` 바이트 수)를 `manifest.json`과 `dist/manifest.json`에 씁니다.
|
|
234
|
+
8. `dist/`를 프로젝트 루트의 `<프로젝트 디렉터리>-<version>.zip`으로 묶습니다.
|
|
183
235
|
|
|
184
236
|
| 옵션 | 의미 |
|
|
185
237
|
|---|---|
|
|
186
|
-
| `--arch` |
|
|
238
|
+
| `--arch` | `amd64`(`x86_64`), `arm64`(`aarch64`), `both`. TTY면 물어보고(기본 both), 아니면 `both` |
|
|
239
|
+
|
|
240
|
+
`none`을 제외한 모든 런타임은 Docker가 필요합니다. 다른 arch를 빌드하려면 Docker 에뮬레이션이 필요하며, 네이티브 Linux에서는 `docker run --privileged --rm tonistiigi/binfmt --install <arch>`로 한 번 켜 둡니다.
|
|
187
241
|
|
|
188
242
|
### 3.5 `update`
|
|
189
243
|
|
|
@@ -191,25 +245,41 @@ muscat-pk build [--arch amd64|arm64|both]
|
|
|
191
245
|
muscat-pk update # 현재 프로젝트만 (로컬 설치가 있을 때)
|
|
192
246
|
muscat-pk update --global # 현재 프로젝트 + 전역 CLI
|
|
193
247
|
muscat-pk update dev # dev 채널 따라가기
|
|
248
|
+
muscat-pk update 1.2.3 # 특정 버전 (--tag <tag> / --version <v>도 가능)
|
|
194
249
|
```
|
|
195
250
|
|
|
251
|
+
| 옵션 | 의미 |
|
|
252
|
+
|---|---|
|
|
253
|
+
| `[tag]` | npm dist-tag 또는 버전 (`latest`, `dev`, `1.2.3`). 생략하면 `latest`. dev 빌드에서는 dev 유지(기본)와 stable 전환 중 물어봄 |
|
|
254
|
+
| `--global` | 프로젝트 로컬 설치에서 실행해도 전역 CLI까지 업데이트 |
|
|
255
|
+
|
|
196
256
|
| 실행 위치 | 업데이트 대상 |
|
|
197
257
|
|---|---|
|
|
198
|
-
| 로컬 설치가 있는 프로젝트 안 | 그 프로젝트의 `muscat-plugin-kit` + `plugin-ui` |
|
|
258
|
+
| 로컬 설치가 있는 프로젝트 안 | 그 프로젝트의 `muscat-plugin-kit` + `plugin-ui` (+ `package.json`에 있으면 `preview-host`) |
|
|
199
259
|
| 위와 같고 `--global` | 위 + 전역 CLI |
|
|
200
|
-
| 그 밖 | 전역 CLI (프로젝트 안이면 로컬 의존성도
|
|
260
|
+
| 그 밖 | 전역 CLI (npm `-g` 또는 S3 바이너리), 프로젝트 안이면 로컬 의존성도 |
|
|
261
|
+
|
|
262
|
+
- 프로젝트 로컬 설치가 실행한 CLI보다 오래됐으면, 실행한 CLI가 대신 프로젝트를 업데이트합니다 (프로젝트만).
|
|
263
|
+
- 패키지는 원래 있던 `dependencies` / `devDependencies` 구역에 그대로 둡니다. 패키지 매니저는 `packageManager` 필드나 lock 파일로 판단합니다.
|
|
264
|
+
- 예전 프로젝트 구조는 `[y/N]` 확인 후 옮깁니다: `src/` → `ui/` (+ `vite.config.ts`, `tsconfig.json`), `main.py` → `py/main.py`, `backend/` → `py/`.
|
|
265
|
+
- `.mcp.json`, `AGENTS.md`, `CLAUDE.md`가 없으면 추가하고, 있으면 그대로 둡니다.
|
|
266
|
+
- Windows에서는 새 바이너리가 다음 실행 때 적용됩니다.
|
|
201
267
|
|
|
202
|
-
업데이트 후
|
|
268
|
+
업데이트 후 새 `plugin-ui`에 맞춰 고쳐야 할 코드를 목록으로 보여 줍니다 (``This project needs changes for the updated plugin-ui. Fix these before `muscat-pk dev` / `build`:``). 목록대로 고치거나 `muscat-pk mcp`의 `check_project`로 확인하세요.
|
|
203
269
|
|
|
204
270
|
### 3.6 `sdk`
|
|
205
271
|
|
|
206
272
|
```bash
|
|
207
|
-
muscat-pk sdk list #
|
|
208
|
-
muscat-pk sdk update [version] # 생략하면 최신
|
|
273
|
+
muscat-pk sdk list # 최신 배포 버전 12개
|
|
274
|
+
muscat-pk sdk update [version] # 생략하면 최신 stable
|
|
209
275
|
```
|
|
210
276
|
|
|
211
|
-
|
|
212
|
-
|
|
277
|
+
| 백엔드 | `list` 출처 | `update`가 바꾸는 것 |
|
|
278
|
+
|---|---|---|
|
|
279
|
+
| python, ros2 (rclpy) | PyPI `rainbow-rb-sdk` | `requirements.txt`의 `rainbow-rb-*==` 핀을 모두 같은 버전으로 (서로 정확히 같은 버전을 요구함). 없으면 `rainbow-rb-sdk==<ver>` 추가. `.venv`가 있으면 갱신하고, dev 컨테이너는 다음 `dev` / `dev:back`에서 설치 |
|
|
280
|
+
| C++ (`cpp`, `ros2-cpp`) | apt `rainbow-rb-sdk-dev` (stable, 숫자 버전만) | `cpp/CMakeLists.txt` / `ros2_ws/src/*/CMakeLists.txt`의 `find_package(rb_sdk <ver> EXACT REQUIRED)`. dev 이미지는 다음 `build` / `dev:back`에서 그 버전으로 다시 빌드 |
|
|
281
|
+
|
|
282
|
+
C++ 버전은 숫자와 점만 됩니다 (`"<v>" is not a CMake version (digits and dots only)`).
|
|
213
283
|
|
|
214
284
|
### 3.7 `storybook`, `mcp`, `completion`, `version`
|
|
215
285
|
|
|
@@ -218,9 +288,19 @@ muscat-pk storybook [--port 6007] [--rebuild]
|
|
|
218
288
|
muscat-pk mcp [--install codex]
|
|
219
289
|
muscat-pk completion [bash|zsh|fish|powershell]
|
|
220
290
|
muscat-pk version
|
|
291
|
+
muscat-pk uninstall
|
|
221
292
|
```
|
|
222
293
|
|
|
223
|
-
|
|
294
|
+
| 명령 / 옵션 | 의미 |
|
|
295
|
+
|---|---|
|
|
296
|
+
| `storybook --port` | 서비스 포트 (기본 `6007`). 사용 중이면 `port <n> is in use — pass a free one with --port <n>.` |
|
|
297
|
+
| `storybook --rebuild` | Storybook을 다시 빌드 (repo 체크아웃에서만. 배포된 kit에는 빌드된 것이 들어 있음) |
|
|
298
|
+
| `mcp` | stdio MCP 서버 (11장). 실제 서버(`mcp --serve`)로 중계하고 kit이 업데이트되면 서버를 재시작하므로, 실행 중인 에이전트 세션도 새 도구를 씁니다 |
|
|
299
|
+
| `mcp --install codex` | `~/.codex/config.toml`에 서버 등록 (이미 있으면 그대로) |
|
|
300
|
+
| `version` | 실제로 실행된 쪽의 버전 (`-v`, `--version`도 같음) |
|
|
301
|
+
| `uninstall` | npm 전역: `npm uninstall -g`, 바이너리: 실행 파일 삭제. 프로젝트 로컬 설치는 프로젝트 패키지 매니저로 지웁니다 |
|
|
302
|
+
|
|
303
|
+
`completion`은 셸 Tab 자동완성 스크립트를 출력합니다. 한 번 등록하면 `muscat-pk d<Tab>`에서 `dev`, `dev:ui`, `dev:back`이 후보로 나옵니다. 스크립트를 출력만 하므로 아래처럼 셸 rc 파일에 붙여 넣어야 합니다. `[shell]`은 `bash`, `zsh`, `fish`, `powershell` 중 하나이고 생략하면 `$SHELL`을 따릅니다 (없으면 Windows는 `powershell`, 그 밖은 `bash`). Windows `cmd.exe`는 자동완성을 지원하지 않으니 PowerShell, Git Bash, WSL을 쓰세요. 편의 기능이라 쓰지 않아도 됩니다.
|
|
224
304
|
|
|
225
305
|
| OS / 셸 | 자동완성 설정 |
|
|
226
306
|
|---|---|
|
|
@@ -243,12 +323,18 @@ my-plugin/
|
|
|
243
323
|
├─ ros2_ws/ # colcon 워크스페이스 — ros2 / ros2-cpp 타입
|
|
244
324
|
├─ assets/icon.svg
|
|
245
325
|
├─ manifest.json
|
|
246
|
-
├─ requirements.txt # python 의존성 (빌드 시
|
|
326
|
+
├─ requirements.txt # python 의존성 (빌드 시 dist/requirements.lock으로 고정)
|
|
247
327
|
├─ package.json # UI 의존성 (배포물에 포함 안 됨)
|
|
248
328
|
├─ vite.config.ts # muscat() 프리셋 필수
|
|
249
|
-
|
|
329
|
+
├─ tsconfig.json
|
|
330
|
+
├─ AGENTS.md, CLAUDE.md # AI 코딩 에이전트 규칙 (CLAUDE.md → @AGENTS.md)
|
|
331
|
+
├─ .mcp.json # Claude Code용 `muscat-pk mcp` 등록
|
|
332
|
+
├─ .gitignore
|
|
333
|
+
└─ README.md # dist/에 복사됨 (README.<lang>.md, CHANGELOG.md도)
|
|
250
334
|
```
|
|
251
335
|
|
|
336
|
+
스캐폴드된 `requirements.txt`는 `rainbow-rb-sdk`, `rainbow-rb-flat-buffers`, `rainbow-rb-zenoh`를 같은 버전으로 고정합니다 (`muscat-pk sdk update`로 변경). 생성물이라 커밋하지 않는 것: `node_modules/`, `dist/`, `.muscat-dev/`, `.venv/`, `*.zip`, `cpp/build*`, `ros2_ws/{install,build*,log}`.
|
|
337
|
+
|
|
252
338
|
`vite.config.ts`:
|
|
253
339
|
|
|
254
340
|
```ts
|
|
@@ -265,7 +351,7 @@ ZIP에는 플러그인 자체 코드와 에셋만 들어갑니다. `@rainbow-rob
|
|
|
265
351
|
|
|
266
352
|
## 5. manifest.json
|
|
267
353
|
|
|
268
|
-
`muscat-pk dev
|
|
354
|
+
전체 검증은 `muscat-pk build`가 합니다 (오류: `manifest.json errors: …`). `dev` / `dev:back`은 시작 전에 `variables`와 `ports`를 검사하고, `dev:back`은 나머지를 경고로 출력합니다.
|
|
269
355
|
|
|
270
356
|
### 5.1 전체 예시
|
|
271
357
|
|
|
@@ -303,11 +389,12 @@ ZIP에는 플러그인 자체 코드와 에셋만 들어갑니다. `@rainbow-rob
|
|
|
303
389
|
| `company` | 아니오 | 문자열 |
|
|
304
390
|
| `description` | 예 | 문자열 또는 다국어 객체 |
|
|
305
391
|
| `icons` | 예 | SVG 경로 문자열, 또는 PNG/JPG면 `{ "16", "48", "128" }` (세 개 모두) |
|
|
306
|
-
| `targetModels` / `targetCategorys` | 둘 중 하나 이상 | `"all"` 또는 비어 있지 않은 배열. 카테고리: `manipulate`, `amr`, `rby1`, `vision` |
|
|
307
|
-
| `contributes` |
|
|
392
|
+
| `targetModels` / `targetCategorys` | 둘 중 하나 이상 | `"all"` 또는 비어 있지 않은 문자열 배열. 카테고리: `manipulate`, `amr`, `rby1`, `vision` |
|
|
393
|
+
| `contributes` | 아니오 | 5.3. 없거나 `[]`이면 백엔드 전용 플러그인 (화면 없음, UI 빌드 없음) |
|
|
308
394
|
| `variables` | 아니오 | 5.5 |
|
|
309
395
|
| `ports` | 아니오 | 5.6 |
|
|
310
396
|
| `runtime` | 예 | 5.7 |
|
|
397
|
+
| `installedSize` | — | `build`가 기록 (바이트) |
|
|
311
398
|
|
|
312
399
|
다국어 객체: `en` 필수, 허용 키 `ko`, `en`, `ja`, `zh`, `vi`, `th`, `es`.
|
|
313
400
|
|
|
@@ -322,7 +409,7 @@ ZIP에는 플러그인 자체 코드와 에셋만 들어갑니다. `@rainbow-rob
|
|
|
322
409
|
| `opens` | toolbar 전용: `popover`, `panel`, `page` |
|
|
323
410
|
| `commandInfo` | `program.panel`이면 필수 (5.4) |
|
|
324
411
|
|
|
325
|
-
`toolbar` 항목이 최소 하나 있어야
|
|
412
|
+
`contributes`가 비어 있지 않으면 `toolbar` 항목이 최소 하나 있어야 합니다 (호스트가 플러그인을 표시하는 곳). 각 `ui`에는 같은 이름의 `ui/<entry>.tsx`(또는 `.ts` / `.jsx` / `.js`)가 있어야 하며 `build`에서 확인합니다.
|
|
326
413
|
|
|
327
414
|
| point | 표시 위치 |
|
|
328
415
|
|---|---|
|
|
@@ -384,12 +471,14 @@ TCP 포트를 여는 백엔드는 모든 포트를 선언해야 합니다:
|
|
|
384
471
|
|
|
385
472
|
| 규칙 | 검사 주체 |
|
|
386
473
|
|---|---|
|
|
387
|
-
| `28600-28999`만, `number[]
|
|
474
|
+
| `28600-28999`만, `number[]`, 있으면 비어 있으면 안 됨, 중복 불가 | manifest 검증 |
|
|
388
475
|
| 예약 포트 금지: `502`, `2222`, `4840`, `5170-5179`, `8000-8999`, `10000`, `30004`, `34964`, `44818`, `50000-50005`, `50051` | manifest 검증 |
|
|
389
|
-
| 열린 포트는 전부 선언돼야 함 | `dev` / `dev:back` 런타임 모니터가 선언 안 된 포트 발견 즉시 백엔드 중지 |
|
|
476
|
+
| 열린 포트는 전부 선언돼야 함 | `dev` / `dev:back` 런타임 모니터가 선언 안 된 포트 발견 즉시 백엔드 중지 (`backend opened undeclared TCP port(s): …`) |
|
|
390
477
|
| 활성 플러그인끼리 포트 중복 불가 | 로봇, 설치 시 (HTTP 409) |
|
|
391
478
|
|
|
392
|
-
kit 자체 개발용 포트(gateway `10000`,
|
|
479
|
+
- kit 자체 개발용 포트(gateway `10000`, MediaMTX `8554` / `8889` / `8189`, Zenoh dev peer 포트)와 OS가 배정한 loopback 포트는 검사에서 제외됩니다.
|
|
480
|
+
- 백엔드가 다른 서비스와 network namespace를 같이 쓰면 플러그인 포트를 구분할 수 없어서, 네이티브 Linux(`--network host`)와 백엔드가 로컬 Muscat 컨테이너에 붙는 경우(로봇 없는 일반적인 `dev`)에는 모니터가 꺼집니다. 그래도 선언하세요 — 로봇이 설치 시 확인합니다.
|
|
481
|
+
- 선언한 포트는 `127.0.0.1`에 노출되며(`dev`에서는 로컬 Muscat에서), 호스트에서 이미 쓰는 포트는 경고 후 건너뜁니다. 백엔드는 이 목록을 `RB_PLUGIN_PORTS`로 받습니다.
|
|
393
482
|
|
|
394
483
|
### 5.7 `runtime`
|
|
395
484
|
|
|
@@ -409,8 +498,11 @@ kit 자체 개발용 포트(gateway `10000`, Zenoh dev peer 포트)는 검사에
|
|
|
409
498
|
|
|
410
499
|
| import 경로 | 내용 |
|
|
411
500
|
|---|---|
|
|
412
|
-
| `@rainbow-robotics/plugin-ui/react` | Muscat 컴포넌트(`RB*`), `useRb`, `useRbFormData`, `useRbSave`, `useRbStorage`, `createRbStore` |
|
|
413
|
-
| `@rainbow-robotics/plugin-ui/sdk` | 컴포넌트를 뺀 같은 훅들,
|
|
501
|
+
| `@rainbow-robotics/plugin-ui/react` | Muscat 컴포넌트(`RB*`), `useRb`, `useRbFormData`, `useRbSave`, `useRbStorage`, `createRbStore`, `RbSdk` / `RbStore` / `RbSetState` 타입 |
|
|
502
|
+
| `@rainbow-robotics/plugin-ui/sdk` | 컴포넌트를 뺀 같은 훅들, 그리고 `RbContext`와 `RbNotifyType` 타입 |
|
|
503
|
+
| `@rainbow-robotics/plugin-ui/manifest` | manifest 타입(`PluginManifest`, `PluginContribution`, `PluginVariable`, `PluginRuntime`), `CONTRIBUTION_POINTS`, `VARIABLE_TYPES`, `validateManifest` |
|
|
504
|
+
|
|
505
|
+
Muscat 호스트 밖(단위 테스트, Storybook)에서는 `useRb()`가 stub을 돌려줍니다: 호출하면 `[plugin-ui] rb.<name> called outside of a host context`를 로그로 남기고 빈 값으로 resolve합니다.
|
|
414
506
|
|
|
415
507
|
### 6.1 엔트리 컴포넌트
|
|
416
508
|
|
|
@@ -485,6 +577,7 @@ if (await rb.confirm({ title: "실행", message: "백엔드 동작을 시작할
|
|
|
485
577
|
| `rb.program.getSteps(scope?)` | Program 페이지에 열린 프로그램의 메인 스텝 트리 |
|
|
486
578
|
| `rb.program.setSteps(steps, scope?)` | 메인 스텝 트리 교체. 저장되지 않은 편집으로 들어가고 사용자가 저장 |
|
|
487
579
|
| `rb.program.getCatalog(scope?)` | 로드된 로봇의 명령 카탈로그 (이름, 기본 인자, 중첩 규칙) |
|
|
580
|
+
| `rb.program.getCommandDefaults?(command, scope?)` | 기본값이 실제 로봇 상태를 읽는 명령(카탈로그 `rules.hasFactoryDataFn`)의 기본 step. 선택적이며 호스트에 따라 없을 수 있음 |
|
|
488
581
|
|
|
489
582
|
```ts
|
|
490
583
|
type PluginProgramStep = {
|
|
@@ -561,7 +654,7 @@ await rb.storage.local.set("theme", "dark");
|
|
|
561
654
|
#### `useRbStorage` — 저장된 값 하나를 React state로
|
|
562
655
|
|
|
563
656
|
```tsx
|
|
564
|
-
import { useRbStorage } from "@rainbow-robotics/plugin-ui/react";
|
|
657
|
+
import { RBTextInput, useRbStorage } from "@rainbow-robotics/plugin-ui/react";
|
|
565
658
|
|
|
566
659
|
type Draft = { name: string };
|
|
567
660
|
|
|
@@ -581,7 +674,7 @@ function Editor() {
|
|
|
581
674
|
모듈 최상위에서 한 번 만들고 플러그인의 어느 화면에서든 씁니다:
|
|
582
675
|
|
|
583
676
|
```tsx
|
|
584
|
-
import { createRbStore } from "@rainbow-robotics/plugin-ui/react";
|
|
677
|
+
import { createRbStore, RBSlider } from "@rainbow-robotics/plugin-ui/react";
|
|
585
678
|
|
|
586
679
|
type Settings = {
|
|
587
680
|
speed: number;
|
|
@@ -635,7 +728,8 @@ persist(fn, { name: "settings", storage: createJSONStorage(() => rb.storage.loca
|
|
|
635
728
|
|
|
636
729
|
| API | 의미 |
|
|
637
730
|
|---|---|
|
|
638
|
-
| `rb.data` | 호스트가 마운트 시 넘기는 데이터 (`program.monitor` scope, 저장된 폼 값) |
|
|
731
|
+
| `rb.data` | 호스트가 마운트 시 넘기는 데이터 (`program.monitor` scope, 저장된 폼 값). 새로 마운트되면 `undefined` |
|
|
732
|
+
| `rb.onSave(getPayload)` | `useRbSave`의 저수준 형태. 등록 해제 함수 반환 |
|
|
639
733
|
| `rb.rosPeers.get()` | 이 플러그인의 ROS 2 노드에 접근을 허용한 PC 목록 |
|
|
640
734
|
| `rb.rosPeers.set(ips)` | 목록 저장 후 백엔드 재시작. 잘못된 IP면 reject |
|
|
641
735
|
|
|
@@ -646,11 +740,12 @@ persist(fn, { name: "settings", storage: createJSONStorage(() => rb.storage.loca
|
|
|
646
740
|
Python 백엔드(`runtime.type: "python"`, 엔트리 `py/main.py`)는 `rainbow-rb-sdk`를 씁니다. 버전은 `requirements.txt`에 고정하고 `muscat-pk sdk update`로 바꿉니다. 실행 시 SDK는 로봇의 플러그인 컨테이너가 제공합니다.
|
|
647
741
|
|
|
648
742
|
```python
|
|
649
|
-
from rainbow.rb_sdk import RBPluginCall, PluginProgramContext, RBManipulateSDK, RBAmrSDK
|
|
650
|
-
from rainbow.rb_sdk.
|
|
651
|
-
from rainbow.rb_sdk.rb_sdk_cpp import ConnectOptions
|
|
743
|
+
from rainbow.rb_sdk import RBPluginCall, PluginProgramContext, RBManipulateSDK, RBAmrSDK, RBRby1SDK
|
|
744
|
+
from rainbow.rb_sdk.rb_sdk_cpp import ConnectOptions, PluginRouteError, RBPluginGatewayError
|
|
652
745
|
```
|
|
653
746
|
|
|
747
|
+
`rainbow.rb_sdk`는 `RBPluginCall`, `PluginProgramContext`, `RBManipulateSDK`, `RBAmrSDK`, `RBRby1SDK`, `RBProgramSDK`, `RBBaseSDK`, `rby1`(벤더링된 rby1-sdk 타입)을 export합니다. 나머지(`ConnectOptions`, `PluginRouter`, `PluginRouteError`, `RBPluginGatewayError` 등)는 `rainbow.rb_sdk.rb_sdk_cpp`에 있습니다.
|
|
748
|
+
|
|
654
749
|
어떤 API를 쓸지는 contribution point로 정해집니다:
|
|
655
750
|
|
|
656
751
|
| contribution point | 백엔드 |
|
|
@@ -660,7 +755,7 @@ from rainbow.rb_sdk.rb_sdk_cpp import ConnectOptions
|
|
|
660
755
|
|
|
661
756
|
### 7.1 `RBPluginCall` — UI ↔ 백엔드
|
|
662
757
|
|
|
663
|
-
내부 전용입니다 (UI ↔ 백엔드, 플러그인 ↔ 플러그인). Muscat 밖에서는 접근할 수 없습니다.
|
|
758
|
+
내부 전용입니다 (UI ↔ 백엔드, 플러그인 ↔ 플러그인). Muscat 밖에서는 접근할 수 없습니다. `RBPluginCall(plugin_id=None)`: id는 인자, 없으면 환경 변수 `RB_PLUGIN_ID`, 그것도 없으면 `PLUGIN_ID`입니다 (앞뒤 `/` 제거). 프로세스·플러그인 id마다 인스턴스가 하나라서 `RBPluginCall()`을 다시 불러도 같은 객체가 나옵니다.
|
|
664
759
|
|
|
665
760
|
```python
|
|
666
761
|
plugin = RBPluginCall()
|
|
@@ -681,18 +776,18 @@ res = plugin.call("tool/ping", payload={"hi": 1}, timeout=3.0) # UI: rb.serve(
|
|
|
681
776
|
|
|
682
777
|
| 멤버 | 의미 |
|
|
683
778
|
|---|---|
|
|
684
|
-
| `serve(keyexpr, handler=None)` | 요청/응답 핸들러. 데코레이터 또는 직접 호출. 반환값이
|
|
779
|
+
| `serve(keyexpr, handler=None)` | 요청/응답 핸들러. 데코레이터 또는 직접 호출. 반환값이 응답. 키마다 핸들러 하나 — 다시 serve하면 이전 것을 교체 |
|
|
685
780
|
| `subscribe(topic, callback=None)` | publish 수신 |
|
|
686
|
-
| `publish(topic, payload=None)` | 구독자에게
|
|
687
|
-
| `call(keyexpr, *, payload, timeout=3.0)` | 상대편에 요청하고 응답
|
|
688
|
-
| `unserve(keyexpr=None)` | serve 핸들러 하나 / 전체 해제 |
|
|
689
|
-
| `unsubscribe(topic=None, callback=None)` | 구독 해제 |
|
|
781
|
+
| `publish(topic, payload=None)` | 구독자에게 발행. 연결이 끊긴 동안에는 조용히 버려짐 |
|
|
782
|
+
| `call(keyexpr, *, payload, timeout=3.0)` | 상대편에 요청하고 응답 대기. 연결이 없거나 시간 초과면 `RBPluginGatewayError` |
|
|
783
|
+
| `unserve(keyexpr=None)` / `undeclare_queryable(keyexpr=None)` | serve 핸들러 하나 / 전체 해제 |
|
|
784
|
+
| `unsubscribe(topic=None, callback=None)` / `unsubscribe_topic(topic=None, callback=None)` | 콜백 하나, 토픽의 구독 전체, 또는 모든 구독 해제 |
|
|
690
785
|
| `plugin_id` | 사용 중인 플러그인 id (`muscat-pk dev`에서는 dev id) |
|
|
691
786
|
| `connected` | 연결 여부 |
|
|
692
|
-
| `data_dir` | 영구 데이터 디렉터리 (접근 시 생성)
|
|
787
|
+
| `data_dir` | 영구 데이터 디렉터리 (접근 시 생성): `RB_PLUGIN_DATA_DIR`(Muscat이 설정), 없으면 `/app/data/runtime/plugins/data/{plugin_id}`. 업데이트·재시작에도 유지되고 플러그인 삭제 시 제거. 설치 디렉터리(cwd)는 업데이트마다 교체되므로 상태·캐시·설정은 여기에 저장 |
|
|
693
788
|
| `router` | 외부 엔드포인트 (7.2) |
|
|
694
789
|
|
|
695
|
-
payload는 JSON 직렬화 가능해야 합니다. 핸들러는 sync/async 모두 됩니다.
|
|
790
|
+
payload는 JSON 직렬화 가능해야 합니다. 핸들러는 sync/async 모두 됩니다. 토픽은 UI와 같은 규칙으로 격리됩니다: `"state"` → `{plugin_id}/state`, 앞에 `/`를 붙이면 절대 경로.
|
|
696
791
|
|
|
697
792
|
### 7.2 `plugin.router` — 외부 엔드포인트
|
|
698
793
|
|
|
@@ -818,6 +913,8 @@ asyncio.run(main())
|
|
|
818
913
|
| `executor_state(robot_model=None)` | 모델 하나의 상태 문자열, 또는 전체 `{model: state}` |
|
|
819
914
|
| `is_paused(robot_model=None)` / `is_stopped(robot_model=None)` | 상태 확인 |
|
|
820
915
|
| `on_executor_state_change(fn)` | 데코레이터. 상태가 바뀔 때마다 `(robot_model, state)`로 1회 |
|
|
916
|
+
| `set_execute_handler(fn)` / `set_state_change_handler(fn)` | 위 두 등록을 데코레이터 없이 |
|
|
917
|
+
| `begin_execution(robot_model=None)` / `end_execution()` | SDK가 `on_execute` 앞뒤로 부르는 실행 수명 주기 hook. 일반 플러그인에서는 필요 없음 |
|
|
821
918
|
| `start()` / `stop()` / `pause()` | 수명 주기 |
|
|
822
919
|
| `in_execute`, `exec_robot_model`, `plugin_id`, `started`, `control_topic` | 속성 |
|
|
823
920
|
|
|
@@ -928,16 +1025,21 @@ sudo apt update && sudo apt install rainbow-rb-sdk-dev
|
|
|
928
1025
|
|
|
929
1026
|
### 9.1 로컬 Muscat 런타임
|
|
930
1027
|
|
|
931
|
-
로봇 IP 없이 `dev` / `dev:ui`를 돌리면 `localhost:3000
|
|
1028
|
+
로봇 IP 없이(또는 Muscat이 없는 로봇에 붙어) `dev` / `dev:ui`를 돌리면 `localhost:3000`(API)과 `:10000`(플러그인 gateway)에 Muscat이 필요합니다:
|
|
932
1029
|
|
|
933
|
-
- 이미 `:10000`을
|
|
934
|
-
-
|
|
1030
|
+
- 이미 `:10000`을 노출하는 컨테이너(`muscat`, `muscat-dev` 등)가 있으면 재사용합니다. kit이 띄우지 않은 컨테이너는 플러그인 포트를 노출하지 않습니다.
|
|
1031
|
+
- 컨테이너 없이 그 포트를 이미 쓰는 Muscat(로봇 PC, host networking)이 있으면 그대로 씁니다.
|
|
1032
|
+
- 없으면 `muscat-pk-muscat`을 띄웁니다 — `common`(+ `--sim`에 필요한 서비스)과 MediaMTX(`8554`, `8889`, `8189`)를 돌리는 S3 런타임 이미지(`muscat-pk-runtime:<version>`, `version.json` 기준)이며 `~/.muscat-pk/muscat/`에 캐시됩니다. 노출할 플러그인 포트나 서비스가 바뀌면 다시 만들고, Muscat 새 버전이 나오면 이전 이미지를 교체합니다.
|
|
1033
|
+
- `dev`는 종료할 때 자신이 띄운 `muscat-pk-muscat`을 지웁니다. `dev:ui` 단독으로 띄운 것은 계속 떠 있습니다.
|
|
1034
|
+
- 백엔드는 로컬 Muscat의 network namespace에 붙으므로 `execute`와 SDK가 로봇에서처럼 닿습니다.
|
|
935
1035
|
|
|
936
1036
|
```bash
|
|
937
1037
|
docker rm -f muscat-pk-muscat # 중지
|
|
938
1038
|
MUSCAT_PK_NO_LOCAL_MUSCAT=1 muscat-pk dev # 띄우지 않기
|
|
939
1039
|
```
|
|
940
1040
|
|
|
1041
|
+
API 문서는 `http://localhost:3000/common/docs`에 있습니다.
|
|
1042
|
+
|
|
941
1043
|
### 9.2 로봇 PC에서 직접 개발
|
|
942
1044
|
|
|
943
1045
|
로봇 PC에서는 Muscat이 이미 `10000`에서 gateway를 열고 있으므로 CLI가 그것을 쓰고 로컬 Muscat을 띄우지 않습니다.
|
|
@@ -952,13 +1054,15 @@ MUSCAT_PK_NO_LOCAL_MUSCAT=1 muscat-pk dev # 띄우지 않기
|
|
|
952
1054
|
|
|
953
1055
|
### 9.5 오프라인 / 폐쇄망
|
|
954
1056
|
|
|
955
|
-
Docker 이미지는
|
|
1057
|
+
Docker 이미지는 없거나 kit의 Dockerfile / 빌드 인자가 바뀌었을 때만 빌드합니다 (재빌드는 캐시가 있어도 레지스트리에 접속함).
|
|
956
1058
|
|
|
957
1059
|
```bash
|
|
958
1060
|
docker save muscat-pk-python-dev muscat-pk-runtime | gzip > muscat-pk-images.tar.gz # 인터넷 되는 PC
|
|
959
1061
|
gunzip -c muscat-pk-images.tar.gz | docker load # 오프라인 PC
|
|
960
1062
|
```
|
|
961
1063
|
|
|
1064
|
+
C++ / ROS 2 프로젝트는 `muscat-pk-cpp-dev` / `muscat-pk-ros2-dev`(그리고 `build`가 쓰는 arch별 `-amd64` / `-arm64` 이미지)도 필요합니다. 오프라인에서 로컬 Muscat은 캐시된 `version.json`을, 시뮬레이터는 이미 받아 둔 이미지를 씁니다.
|
|
1065
|
+
|
|
962
1066
|
강제 재빌드: `MUSCAT_PK_REBUILD_IMAGE=1`.
|
|
963
1067
|
|
|
964
1068
|
### 9.6 IDE 지원 (Python)
|
|
@@ -1006,13 +1110,20 @@ muscat({ react: { jsxImportSource: "@emotion/react", babel: { plugins: ["@emotio
|
|
|
1006
1110
|
|
|
1007
1111
|
stdio MCP 서버입니다. rb-components(실제 props, Storybook 예시), 디자인 토큰, Python `rainbow-rb-sdk`(실제 시그니처/docstring)를 에이전트에 제공하고, 오래된 프로젝트 업그레이드용 `check_project`도 있습니다.
|
|
1008
1112
|
|
|
1009
|
-
|
|
1113
|
+
| 도구 | 반환 |
|
|
1114
|
+
|---|---|
|
|
1115
|
+
| `list_components`, `get_component_props`, `get_component_example` | plugin-ui 컴포넌트, prop 타입, 실제 Storybook 사용 예 |
|
|
1116
|
+
| `list_design_tokens`, `search_design_tokens` | 디자인 토큰 Tailwind class (`bg-bg-*`, `text-fg-*`, `typo-*`) |
|
|
1117
|
+
| `list_rb_sdk`, `search_rb_sdk`, `get_rb_sdk_api` | Python `rainbow-rb-sdk`와 C++ SDK의 클래스 / 시그니처 / docstring |
|
|
1118
|
+
| `check_project` | 오래된 프로젝트가 `muscat-pk update` 후 고쳐야 할 것 |
|
|
1119
|
+
|
|
1120
|
+
Claude Code — 플러그인 루트의 `.mcp.json` (`new`가 만들고, 없으면 `update`가 추가):
|
|
1010
1121
|
|
|
1011
1122
|
```json
|
|
1012
1123
|
{ "mcpServers": { "muscat": { "command": "muscat-pk", "args": ["mcp"] } } }
|
|
1013
1124
|
```
|
|
1014
1125
|
|
|
1015
|
-
Codex — `muscat-pk mcp --install codex
|
|
1126
|
+
Codex — `muscat-pk mcp --install codex` (Windows에서는 `muscat-pk.cmd`로 기록), 또는 `~/.codex/config.toml`:
|
|
1016
1127
|
|
|
1017
1128
|
```toml
|
|
1018
1129
|
[mcp_servers.muscat]
|
|
@@ -1033,12 +1144,20 @@ args = ["mcp"]
|
|
|
1033
1144
|
| `MUSCAT_PK_NO_LOCAL_MUSCAT=1` | 로컬 Muscat 런타임을 띄우지 않음 |
|
|
1034
1145
|
| `MUSCAT_PK_REBUILD_IMAGE=1` | Docker 이미지 재빌드 |
|
|
1035
1146
|
| `MUSCAT_PK_RB_SDK_VERSION` | C++ 이미지에 넣을 C++ SDK 버전 (`MUSCAT_PK_REBUILD_IMAGE=1`과 함께) |
|
|
1147
|
+
| `MUSCAT_PK_NO_BUILD_CACHE=1` | Docker 이미지를 `--no-cache`로 빌드 (CI) |
|
|
1148
|
+
| `MUSCAT_PK_HOST_NETWORK=0` | 네이티브 Linux에서도 `--network host` 대신 Docker Desktop(bridge) 경로 사용 (CI) |
|
|
1149
|
+
| `MUSCAT_PK_RBY1_CPUS` | rby1 시뮬레이터 CPU 상한 (기본 `1.5`) |
|
|
1150
|
+
| `MUSCAT_PK_MUSCAT_S3` | 로컬 Muscat 런타임 다운로드 위치 (기본 `https://rainbow-deploy.s3.ap-northeast-2.amazonaws.com/muscat/main`) |
|
|
1151
|
+
| `MUSCAT_HOST_AGENT_DISABLE=1` | 호스트 Zenoh byte-proxy를 띄우지 않음 (macOS / Windows / WSL에서 원격 로봇에 붙을 때) |
|
|
1152
|
+
| `MUSCAT_PROXY_PORT` | 그 proxy의 포트 (기본 `47100`) |
|
|
1153
|
+
| `MUSCAT_ZENOH_ADVERTISE_HOST` / `MUSCAT_ZENOH_PORT` | 원격 로봇이 되접속할 LAN IP / 포트 (기본: 로봇으로 가는 경로의 IP / `53917` 또는 다음 빈 포트) |
|
|
1036
1154
|
| `MUSCAT_PK_NO_UPDATE_CHECK=1` | `dev` / `dev:ui`의 업데이트 확인 생략 |
|
|
1037
1155
|
| `RB_REGISTRY` | 업데이트 확인용 npm 레지스트리 (기본 `https://registry.npmjs.org`) |
|
|
1038
1156
|
| `MUSCAT_PK_TAG` | 설치 스크립트 채널 / 버전 |
|
|
1039
|
-
| `
|
|
1157
|
+
| `CI` | 설정되면 `dev:ui`가 브라우저를 열지 않음 |
|
|
1158
|
+
| `NO_COLOR=1` / `FORCE_COLOR` | 출력 색상 |
|
|
1040
1159
|
|
|
1041
|
-
백엔드 컨테이너 안: `RB_PLUGIN_ID` (플러그인 id / dev id).
|
|
1160
|
+
백엔드 컨테이너 안: `RB_PLUGIN_ID` (플러그인 id / dev id), `RB_PLUGIN_PORTS` (선언한 `ports`, 쉼표 구분), `MUSCAT_ROBOT_HOST` / `MUSCAT_ROBOT_TOKEN` (원격 개발). 로봇에서는 Muscat이 `RB_PLUGIN_DATA_DIR`도 설정합니다 (`data_dir`, 7.1 참고).
|
|
1042
1161
|
|
|
1043
1162
|
---
|
|
1044
1163
|
|
|
@@ -1047,10 +1166,18 @@ args = ["mcp"]
|
|
|
1047
1166
|
| 증상 | 해결 |
|
|
1048
1167
|
|---|---|
|
|
1049
1168
|
| `manifest.json errors: …` | 나열된 필드를 고칩니다 (5장) |
|
|
1169
|
+
| `dev`에서 `manifest.json variable errors: …` / `manifest.json port errors: …` | `variables`(5.5) / `ports`(5.6) 수정 |
|
|
1170
|
+
| `A Muscat plugin dev environment is already running: …` | `y`로 멈추거나 다른 `dev`를 먼저 종료 |
|
|
1171
|
+
| `Docker is installed but the daemon is not running` | Docker Desktop 실행 (또는 `sudo systemctl start docker`) |
|
|
1172
|
+
| `contributes declares ui "<entry>" but ui/<entry>.tsx … does not exist.` | 파일을 추가하거나 `contributes[].ui` 수정 |
|
|
1173
|
+
| `wheel download failed for <arch>` | 그 arch용 cp312 바이너리 wheel이 없는 의존성 — 다른 버전이나 패키지 선택 |
|
|
1174
|
+
| `can't build <arch> on this <host>: Docker has no <arch> emulation.` | `docker run --privileged --rm tonistiigi/binfmt --install <arch>`, 또는 `--arch <호스트 arch>` |
|
|
1175
|
+
| `Permission denied when deleting dist/` | 예전 빌드가 root 소유 파일을 남김: `sudo rm -rf dist` 한 번 |
|
|
1050
1176
|
| 선언 안 된 포트로 백엔드가 멈춤 | 포트(28600-28999)를 `ports`에 추가 |
|
|
1051
1177
|
| UI 프리뷰에 "no default export" | 엔트리가 `export default App`인지, `vite.config.ts`가 `muscat()`을 쓰는지 확인. `muscat-pk mcp`의 `check_project` 실행 |
|
|
1052
1178
|
| `vite not found — UI will not load` | 프로젝트에서 `npm install` |
|
|
1053
1179
|
| `AttributeError: router` | 로봇 SDK가 0.0.9.dev5보다 낮음 |
|
|
1180
|
+
| `plugin.call`에서 `RBPluginGatewayError` | gateway 연결이 끊겼거나 `timeout` 안에 응답이 없음 (UI에 `rb.serve`가 등록돼 있는지 확인) |
|
|
1054
1181
|
| 스텝이 끝나지 않음 | `on_execute`에서 `await ctx.done()` 호출 |
|
|
1055
1182
|
| 정지를 눌러도 플러그인이 계속 돎 | 루프마다 `await ctx.check_stop()` 호출 |
|
|
1056
1183
|
| `set_variable`에서 `RuntimeError` | 새 변수 이름은 `RBP_`로 시작 |
|