@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.44 → 0.1.2-dev.45
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 +1 -1
- package/cli/README.md +184 -0
- package/cli/bin/muscat-pk.mjs +13 -0
- package/cli/prepack.mjs +10 -0
- package/cli/src/build.mjs +2 -29
- package/cli/src/dev.mjs +10 -2
- package/cli/src/devBack.mjs +72 -9
- package/cli/src/devUi.mjs +100 -34
- package/cli/src/manifest.mjs +15 -0
- package/cli/src/mcp.mjs +523 -0
- package/cli/src/pluginDevServer.mjs +113 -0
- package/cli/src/ports.mjs +160 -0
- package/cli/src/sdk.mjs +11 -9
- package/cli/src/storybook.mjs +168 -0
- package/cli/src/templates-embedded.mjs +1 -1
- package/cli/storybook-dist/_design-tokens.css +4957 -0
- package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -0
- package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -0
- package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -0
- package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -0
- package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -0
- package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -0
- package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -0
- package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -0
- package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -0
- package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -0
- package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -0
- package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -0
- package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +44 -0
- package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -0
- package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -0
- package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -0
- package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -0
- package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -0
- package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -0
- package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -0
- package/cli/storybook-dist/_stories-src/RBIcons/RBMenuTreeIcons.stories.tsx +108 -0
- package/cli/storybook-dist/_stories-src/RBIcons/RBProgramIcons.stories.tsx +116 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +119 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -0
- package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -0
- package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -0
- package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -0
- package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -0
- package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -0
- package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -0
- package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -0
- package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -0
- package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -0
- package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -0
- package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -0
- package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -0
- package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -0
- package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -0
- package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -0
- package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -0
- package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -0
- package/cli/storybook-dist/assets/Color-YHDXOIA2-BjuzF3Je.js +1 -0
- package/cli/storybook-dist/assets/Docs-BAFZYTMR.js +12 -0
- package/cli/storybook-dist/assets/Docs-BQesYW-Q.js +8 -0
- package/cli/storybook-dist/assets/Docs-BsMEe093.js +7 -0
- package/cli/storybook-dist/assets/Docs-By6k6vcM.js +20 -0
- package/cli/storybook-dist/assets/Docs-D1J63cJc.js +7 -0
- package/cli/storybook-dist/assets/Docs-GoNbntAU.js +8 -0
- package/cli/storybook-dist/assets/DocsRenderer-CFRXHY34-BwY_WiM7.js +2 -0
- package/cli/storybook-dist/assets/NxAvatar.stories-CvzBHfYa.js +13 -0
- package/cli/storybook-dist/assets/NxButton.stories-B83PesgN.js +20 -0
- package/cli/storybook-dist/assets/NxIcons.stories-Ca7qpAaO.js +3 -0
- package/cli/storybook-dist/assets/NxNumberInput.stories-DpNcCG6B.js +26 -0
- package/cli/storybook-dist/assets/NxPagination.stories-Xnvd3xhH.js +11 -0
- package/cli/storybook-dist/assets/NxPointerHoldButton.stories-C3YSIpM0.js +14 -0
- package/cli/storybook-dist/assets/NxRangeInput.stories-DIhaNZig.js +17 -0
- package/cli/storybook-dist/assets/NxSelectBox.stories-BGwO2mYJ.js +17 -0
- package/cli/storybook-dist/assets/NxSlideRange.stories-BXlt5Bgt.js +14 -0
- package/cli/storybook-dist/assets/NxSwitch.stories-C5ZqIRtw.js +11 -0
- package/cli/storybook-dist/assets/NxTextArea.stories-hm_ycALo.js +11 -0
- package/cli/storybook-dist/assets/NxTextInput.stories-DkZAySr_.js +11 -0
- package/cli/storybook-dist/assets/PretendardVariable-CJuje-Rk.woff2 +0 -0
- package/cli/storybook-dist/assets/RBAccordion.stories--bY36GZY.js +17 -0
- package/cli/storybook-dist/assets/RBAccordionGroup.stories-BVr1ccI4.js +33 -0
- package/cli/storybook-dist/assets/RBAlert.stories-CkjFBEkv.js +17 -0
- package/cli/storybook-dist/assets/RBBadge.stories-CNXILDPh.js +1 -0
- package/cli/storybook-dist/assets/RBBattery.stories-AokMv8ZY.js +10 -0
- package/cli/storybook-dist/assets/RBCard.stories-CX1sLKB2.js +45 -0
- package/cli/storybook-dist/assets/RBCheckBox.stories-By3QHBFm.js +1 -0
- package/cli/storybook-dist/assets/RBCheckIconButton.stories-Dud1uK3X.js +1 -0
- package/cli/storybook-dist/assets/RBCheckLabelButton.stories-C8iBkZxY.js +24 -0
- package/cli/storybook-dist/assets/RBChipTab.stories-DsgbiS2U.js +21 -0
- package/cli/storybook-dist/assets/RBChipsBoxTab.stories-C3DZGmq0.js +1 -0
- package/cli/storybook-dist/assets/RBColorIcons.stories-CusR20qk.js +3 -0
- package/cli/storybook-dist/assets/RBCommand.stories-DhJNgaLZ.js +73 -0
- package/cli/storybook-dist/assets/RBConfirm.stories-B6LKpCDa.js +20 -0
- package/cli/storybook-dist/assets/RBDateInput.stories-PEEhQ1wJ.js +15 -0
- package/cli/storybook-dist/assets/RBDialog.stories-CAtOdRen.js +38 -0
- package/cli/storybook-dist/assets/RBDivider.stories-0zpIUlXT.js +1 -0
- package/cli/storybook-dist/assets/RBDrawer.stories-CWMG4glY.js +14 -0
- package/cli/storybook-dist/assets/RBDropdownMenu.stories-6fbEYZfk.js +86 -0
- package/cli/storybook-dist/assets/RBEmptyView.stories-BRDZHjn4.js +12 -0
- package/cli/storybook-dist/assets/RBIconButton.stories-sYrRLYob.js +1 -0
- package/cli/storybook-dist/assets/RBIcons.stories-DFe8O1_g.js +3 -0
- package/cli/storybook-dist/assets/RBInlineLabelButton.stories-DX4VdUfk.js +1 -0
- package/cli/storybook-dist/assets/RBJoyStick.stories-SA2Foelf.js +11 -0
- package/cli/storybook-dist/assets/RBLabelButton.stories-CicBns_Y.js +1 -0
- package/cli/storybook-dist/assets/RBLight.stories-WlP3oEmY.js +1 -0
- package/cli/storybook-dist/assets/RBMenuTreeIcons.stories-CrWjPfxj.js +3 -0
- package/cli/storybook-dist/assets/RBMenubar.stories-Di4phWw7.js +3 -0
- package/cli/storybook-dist/assets/RBMultiRangeInput.stories-DS3wrR3l.js +14 -0
- package/cli/storybook-dist/assets/RBPagination.stories-BDVPAUVD.js +17 -0
- package/cli/storybook-dist/assets/RBPopover.stories-DL9b_iX9.js +11 -0
- package/cli/storybook-dist/assets/RBProgramIcons.stories-ByRVx-on.js +3 -0
- package/cli/storybook-dist/assets/RBPrompt.stories-vTmKWyA5.js +20 -0
- package/cli/storybook-dist/assets/RBRadioBox.stories-C9WHt4jb.js +1 -0
- package/cli/storybook-dist/assets/RBRadioButton.stories-D8KYq63j.js +11 -0
- package/cli/storybook-dist/assets/RBRangeDateInput.stories-Bvcf4Su7.js +15 -0
- package/cli/storybook-dist/assets/RBResizable.stories-3O2OMuH_.js +20 -0
- package/cli/storybook-dist/assets/RBSelectBox.stories-CKZLmPzX.js +6 -0
- package/cli/storybook-dist/assets/RBSheet.stories-Dko0It1W.js +39 -0
- package/cli/storybook-dist/assets/RBSideNumberInput.stories-CUvqVtId.js +5 -0
- package/cli/storybook-dist/assets/RBSlider.stories-CcbaKRYo.js +5 -0
- package/cli/storybook-dist/assets/RBSpinner.stories-D15e_JlE.js +14 -0
- package/cli/storybook-dist/assets/RBSwitch.stories-C4JRsYJ6.js +65 -0
- package/cli/storybook-dist/assets/RBTab.stories-BPvzQSM-.js +1 -0
- package/cli/storybook-dist/assets/RBTable.stories-nr8OS1mV.js +62 -0
- package/cli/storybook-dist/assets/RBTableRow.stories-rI0rK8Ci.js +11 -0
- package/cli/storybook-dist/assets/RBTabs.stories-CxMtHGWA.js +21 -0
- package/cli/storybook-dist/assets/RBTextArea.stories-Bru4AhJ3.js +5 -0
- package/cli/storybook-dist/assets/RBTextField.stories-Ah3D9lto.js +45 -0
- package/cli/storybook-dist/assets/RBTextInput.stories-C3qAVngJ.js +5 -0
- package/cli/storybook-dist/assets/RBToast.stories-C1occsvf.js +11 -0
- package/cli/storybook-dist/assets/RBToggle.stories-BNrNERc7.js +12 -0
- package/cli/storybook-dist/assets/RBToggleGroup.stories-D_RRBwxY.js +17 -0
- package/cli/storybook-dist/assets/RBTooltip.stories-DuyOQqrB.js +9 -0
- package/cli/storybook-dist/assets/RainbowUiProvider-BYlbsa69.css +1 -0
- package/cli/storybook-dist/assets/RainbowUiProvider-B_piZBLb.js +31 -0
- package/cli/storybook-dist/assets/browserUtils-DeopUpDy.js +1 -0
- package/cli/storybook-dist/assets/chunk-XP5HYGXS-BHXC9YeQ.js +1 -0
- package/cli/storybook-dist/assets/client-DTf7SId2.js +1 -0
- package/cli/storybook-dist/assets/createConveyorBeltLayer-Cn41pFRH.js +6210 -0
- package/cli/storybook-dist/assets/createConveyorBeltLayer-Ds5AOper.css +21 -0
- package/cli/storybook-dist/assets/entry-preview-D-mLV0cS.js +2 -0
- package/cli/storybook-dist/assets/entry-preview-docs-b8bpVBXy.js +46 -0
- package/cli/storybook-dist/assets/iframe-DNru2eN7.js +211 -0
- package/cli/storybook-dist/assets/index-B4FGfHer.js +1 -0
- package/cli/storybook-dist/assets/index-BRq5TIsn.js +24 -0
- package/cli/storybook-dist/assets/index-C1OzuK6-.js +240 -0
- package/cli/storybook-dist/assets/index-CT3U1fyk.js +1 -0
- package/cli/storybook-dist/assets/index-ChsGqxH_.js +9 -0
- package/cli/storybook-dist/assets/index-Cwr2bQOB.js +575 -0
- package/cli/storybook-dist/assets/index-DKFBREX_.js +1 -0
- package/cli/storybook-dist/assets/index-DgH-xKnr.js +11 -0
- package/cli/storybook-dist/assets/index-Dmy2JGbm.js +1 -0
- package/cli/storybook-dist/assets/index-DrFu-skq.js +6 -0
- package/cli/storybook-dist/assets/jsx-runtime-BTJTZTIL.js +9 -0
- package/cli/storybook-dist/assets/keymapTileIndex.worker-BsNYNbqs.js +1 -0
- package/cli/storybook-dist/assets/preview-B8lJiyuQ.js +34 -0
- package/cli/storybook-dist/assets/preview-BBWR9nbA.js +1 -0
- package/cli/storybook-dist/assets/preview-BWzBA1C2.js +396 -0
- package/cli/storybook-dist/assets/preview-CILRoSnB.js +1 -0
- package/cli/storybook-dist/assets/preview-CVgpLj3b.js +1 -0
- package/cli/storybook-dist/assets/preview-CvbIS5ZJ.js +1 -0
- package/cli/storybook-dist/assets/preview-DD_OYowb.js +1 -0
- package/cli/storybook-dist/assets/preview-DGUiP6tS.js +7 -0
- package/cli/storybook-dist/assets/preview-DHQbi4pV.js +1 -0
- package/cli/storybook-dist/assets/preview-DUTcCPBe.css +1 -0
- package/cli/storybook-dist/assets/preview-QAsoD6Sa.js +2 -0
- package/cli/storybook-dist/assets/preview-rcRMbKPr.js +3 -0
- package/cli/storybook-dist/assets/react-18-hB3smqWJ.js +1 -0
- package/cli/storybook-dist/assets/react-resizable-panels.browser-CvAJUcrm.js +72 -0
- package/cli/storybook-dist/assets/test-utils-BQudk2xt.js +9 -0
- package/cli/storybook-dist/favicon.svg +1 -0
- package/cli/storybook-dist/iframe.html +666 -0
- package/cli/storybook-dist/index.html +190 -0
- package/cli/storybook-dist/index.json +1 -0
- package/cli/storybook-dist/nunito-sans-bold-italic.woff2 +0 -0
- package/cli/storybook-dist/nunito-sans-bold.woff2 +0 -0
- package/cli/storybook-dist/nunito-sans-italic.woff2 +0 -0
- package/cli/storybook-dist/nunito-sans-regular.woff2 +0 -0
- package/cli/storybook-dist/project.json +1 -0
- package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js +331 -0
- package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js.LEGAL.txt +51 -0
- package/cli/storybook-dist/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
- package/cli/storybook-dist/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
- package/cli/storybook-dist/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
- package/cli/storybook-dist/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/links-1/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/storybook-13/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-addons/storybook-experimental-addon-test-12/manager-bundle.js +223 -0
- package/cli/storybook-dist/sb-addons/themes-11/manager-bundle.js +3 -0
- package/cli/storybook-dist/sb-common-assets/favicon.svg +1 -0
- package/cli/storybook-dist/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
- package/cli/storybook-dist/sb-common-assets/nunito-sans-bold.woff2 +0 -0
- package/cli/storybook-dist/sb-common-assets/nunito-sans-italic.woff2 +0 -0
- package/cli/storybook-dist/sb-common-assets/nunito-sans-regular.woff2 +0 -0
- package/cli/storybook-dist/sb-manager/globals-module-info.js +1052 -0
- package/cli/storybook-dist/sb-manager/globals-runtime.js +41775 -0
- package/cli/storybook-dist/sb-manager/globals.js +48 -0
- package/cli/storybook-dist/sb-manager/runtime.js +12048 -0
- package/cli/templates/common/.mcp.json +8 -0
- package/cli/templates/common/README.md +467 -53
- package/cli/templates/common/assets/icon.svg +44 -2
- package/cli/templates/common/package.json +2 -0
- package/cli/templates/common/ui/content.tsx +209 -26
- package/cli/templates/common/ui/style.css +6 -0
- package/cli/templates/common/vite.config.ts +6 -34
- package/cli/templates/cpp/cpp/main.cpp +1 -0
- package/cli/vite.d.mts +25 -0
- package/cli/vite.mjs +271 -0
- package/package.json +31 -4
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ muscat-pk pack # 배포용 ZIP 생성
|
|
|
121
121
|
|
|
122
122
|
| 패키지 | 역할 |
|
|
123
123
|
|---|---|
|
|
124
|
-
| `@rainbow-robotics/plugin-ui` | UI 런타임 계약:
|
|
124
|
+
| `@rainbow-robotics/plugin-ui` | UI 런타임 계약: React direct mount, `useRb()` context, host services |
|
|
125
125
|
| `@rainbow-robotics/muscat-plugin-kit` | CLI, 템플릿, 빌드/팩, 미리보기 호스트 |
|
|
126
126
|
|
|
127
127
|
플러그인 ZIP에는 플러그인 코드와 에셋만 포함됩니다. `@rainbow-robotics/plugin-ui`, `react`, `react-dom`은 호스트(Muscat/로봇)의 import map에서 제공됩니다.
|
package/cli/README.md
CHANGED
|
@@ -20,6 +20,190 @@ muscat-pk uninstall # remove muscat-pk itself (npm global or S3 binary)
|
|
|
20
20
|
muscat-pk sdk list | sdk update [version] # manage rainbow-rb-sdk (python)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Backend ports
|
|
24
|
+
|
|
25
|
+
If a plugin backend opens a TCP server, declare it in `manifest.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"ports": [28600, 28601]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`ports` is optional and accepts only `number[]`; ranges or objects are not
|
|
34
|
+
accepted. External plugin backend ports are reserved for `28600-28999`.
|
|
35
|
+
`muscat-pk dev` and `muscat-pk dev:back` validate the manifest before start and
|
|
36
|
+
stop the backend if it opens an undeclared managed TCP listen port.
|
|
37
|
+
|
|
38
|
+
Known robot/industrial/internal ports such as `502`, `4840`, `5170-5179`,
|
|
39
|
+
`8000-8999`, `10000`, `2222`, `30004`, `34964`, `44818`, `50000-50005`,
|
|
40
|
+
and `50051` are reserved and must not be used by external plugins.
|
|
41
|
+
|
|
42
|
+
## Backend runtimes
|
|
43
|
+
|
|
44
|
+
`manifest.json` selects one runtime:
|
|
45
|
+
|
|
46
|
+
| runtime | source | notes |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `{ "type": "none" }` | — | UI-only plugin; no backend container |
|
|
49
|
+
| `{ "type": "python", "entry": "main.py" }` | `py/` | Uses `rainbow.rb_sdk.RBPluginCall` for UI/backend messaging |
|
|
50
|
+
| `{ "type": "cpp", "entry": "<executable name>" }` | `cpp/` | Built with CMake in Docker; current template is a run skeleton |
|
|
51
|
+
| `{ "type": "ros2", "rosClient": "rclpy", ... }` | `ros2_ws/` | ROS2 Python node; may use Python SDK alongside ROS2 APIs |
|
|
52
|
+
| `{ "type": "ros2", "rosClient": "rclcpp", ... }` | `ros2_ws/` | ROS2 C++ node built with colcon/CMake |
|
|
53
|
+
|
|
54
|
+
All non-`none` runtimes run in Docker during `muscat-pk dev:back`, `muscat-pk
|
|
55
|
+
dev`, and build/export flows so development matches the robot environment.
|
|
56
|
+
|
|
57
|
+
## JavaScript dependencies
|
|
58
|
+
|
|
59
|
+
`muscat-pk build` runs the plugin project's own Vite build. Vite resolves imports
|
|
60
|
+
from installed `node_modules`; it does not care whether a package is listed under
|
|
61
|
+
`dependencies` or `devDependencies`, but the package must be installed.
|
|
62
|
+
|
|
63
|
+
Do not run a production-only install such as `npm install --omit=dev` before
|
|
64
|
+
building a plugin UI, because build tools like `vite`, `typescript`,
|
|
65
|
+
`tailwindcss`, `@tailwindcss/vite`, `sass`, or `less` are usually
|
|
66
|
+
`devDependencies`.
|
|
67
|
+
|
|
68
|
+
The scaffold keeps this split:
|
|
69
|
+
|
|
70
|
+
| package kind | examples | why |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `dependencies` | `@rainbow-robotics/plugin-ui`, UI libraries imported by plugin source | Author-facing APIs used by `ui/*.tsx` |
|
|
73
|
+
| `devDependencies` | `vite`, `typescript`, `tailwindcss`, `@tailwindcss/vite`, `sass`, `less` | Build/dev tools needed before `dist/*.js` is produced |
|
|
74
|
+
|
|
75
|
+
The built plugin ZIP does not include `package.json` or `node_modules`. For
|
|
76
|
+
production, Muscat provides `@rainbow-robotics/plugin-ui`, React, and ReactDOM
|
|
77
|
+
through the host import map; plugin UI build output imports those as host
|
|
78
|
+
externals.
|
|
79
|
+
|
|
80
|
+
## Plugin styles
|
|
81
|
+
|
|
82
|
+
`muscat()` auto-loads plugin styles from `ui/style.{css,scss,sass,less}` or
|
|
83
|
+
`ui/styles.{css,scss,sass,less}` for every UI entry, and from
|
|
84
|
+
`ui/{entry}.{css,scss,sass,less}` for a matching entry-specific file. Authors do
|
|
85
|
+
not need to import CSS from every `.tsx` entry.
|
|
86
|
+
|
|
87
|
+
The scaffold enables Tailwind in the plugin's own `vite.config.ts`; those
|
|
88
|
+
Tailwind dependencies live in the plugin package, while `muscat()` injects the
|
|
89
|
+
generated CSS into the plugin JS bundle so dynamic host imports do not need a
|
|
90
|
+
separate CSS file.
|
|
91
|
+
|
|
92
|
+
Built CSS selectors are scoped under the current plugin id
|
|
93
|
+
(`[data-muscat-plugin-id="..."]`), so Tailwind utilities or plugin styles do not
|
|
94
|
+
rewrite the host page or another plugin. CSS-in-JS libraries are allowed too,
|
|
95
|
+
but global styles (for example Emotion `Global`) remain the author's
|
|
96
|
+
responsibility to scope.
|
|
97
|
+
|
|
98
|
+
### Tailwind
|
|
99
|
+
|
|
100
|
+
New plugins include Tailwind by default:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { defineConfig } from "vite";
|
|
104
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
105
|
+
import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
|
|
106
|
+
|
|
107
|
+
export default defineConfig({
|
|
108
|
+
plugins: [tailwindcss(), muscat()],
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The default `ui/style.css` uses Tailwind theme/utilities only, not preflight:
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
@layer theme, utilities;
|
|
116
|
+
|
|
117
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
118
|
+
@import "tailwindcss/utilities.css" layer(utilities);
|
|
119
|
+
|
|
120
|
+
@source "./**/*.{ts,tsx}";
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### SCSS, Sass, and Less
|
|
124
|
+
|
|
125
|
+
Install the preprocessor package in the plugin project, then create one of the
|
|
126
|
+
auto-loaded style files.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm i -D sass
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```scss
|
|
133
|
+
/* ui/style.scss */
|
|
134
|
+
$accent: #3b82f6;
|
|
135
|
+
|
|
136
|
+
.panel {
|
|
137
|
+
border: 1px solid color-mix(in srgb, $accent 40%, transparent);
|
|
138
|
+
|
|
139
|
+
&__title {
|
|
140
|
+
color: $accent;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For Less, install `less` and use `ui/style.less`.
|
|
146
|
+
|
|
147
|
+
### Emotion
|
|
148
|
+
|
|
149
|
+
Emotion dependencies may be in `dependencies` or `devDependencies`; Vite can
|
|
150
|
+
bundle them when they are installed and imported by the plugin.
|
|
151
|
+
|
|
152
|
+
For `styled`, no Vite config change is needed:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm i @emotion/react @emotion/styled
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import styled from "@emotion/styled";
|
|
160
|
+
|
|
161
|
+
const Panel = styled.div({
|
|
162
|
+
display: "grid",
|
|
163
|
+
gap: 12,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For Emotion's `css` prop, pass React plugin options through `muscat()` and add
|
|
168
|
+
`jsxImportSource` to `tsconfig.json`:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm i @emotion/react @emotion/styled
|
|
172
|
+
npm i -D @emotion/babel-plugin
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
// vite.config.ts
|
|
177
|
+
import { defineConfig } from "vite";
|
|
178
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
179
|
+
import { muscat } from "@rainbow-robotics/muscat-plugin-kit/vite";
|
|
180
|
+
|
|
181
|
+
export default defineConfig({
|
|
182
|
+
plugins: [
|
|
183
|
+
tailwindcss(),
|
|
184
|
+
muscat({
|
|
185
|
+
react: {
|
|
186
|
+
jsxImportSource: "@emotion/react",
|
|
187
|
+
babel: {
|
|
188
|
+
plugins: ["@emotion/babel-plugin"],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
}),
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"compilerOptions": {
|
|
199
|
+
"jsxImportSource": "@emotion/react"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Emotion `Global` writes global CSS at runtime, so scope it manually or avoid it
|
|
205
|
+
inside plugins.
|
|
206
|
+
|
|
23
207
|
## How it fits together
|
|
24
208
|
|
|
25
209
|
- **Install/CLI package** is `@rainbow-robotics/muscat-plugin-kit`.
|
package/cli/bin/muscat-pk.mjs
CHANGED
|
@@ -11,6 +11,11 @@ import { runUpdate } from "../src/update.mjs";
|
|
|
11
11
|
import { runUninstall } from "../src/uninstall.mjs";
|
|
12
12
|
import { runSdk } from "../src/sdk.mjs";
|
|
13
13
|
import { runType } from "../src/type.mjs";
|
|
14
|
+
import { runStorybook } from "../src/storybook.mjs";
|
|
15
|
+
// mcp.mjs is dynamically imported below — it pulls in the MCP SDK + a full
|
|
16
|
+
// TypeScript program, and every other command would pay that cold-start cost
|
|
17
|
+
// on every invocation otherwise (see ros2.mjs/python.mjs/cpp.mjs for the same
|
|
18
|
+
// pattern in build.mjs).
|
|
14
19
|
|
|
15
20
|
const [, , cmd, ...args] = process.argv;
|
|
16
21
|
|
|
@@ -25,6 +30,8 @@ Commands:
|
|
|
25
30
|
dev Run Socket.IO bridge, dev:ui, and dev:back together
|
|
26
31
|
dev:ui Preview the plugin in a mini-Muscat (preview-host)
|
|
27
32
|
dev:back Run the plugin back end (python: watchfiles reload / ros2: Docker)
|
|
33
|
+
storybook Browse the bundled rb-components Storybook (--port <n>, --rebuild)
|
|
34
|
+
mcp Start an MCP server (stdio) exposing rb-components/rb SDK docs to AI agents
|
|
28
35
|
update Update @rainbow-robotics/muscat-plugin-kit and @rainbow-robotics/plugin-ui
|
|
29
36
|
uninstall Remove the installed muscat-pk CLI (npm global or S3 binary)
|
|
30
37
|
sdk <list|update [version]> Manage the python rainbow-rb-sdk version
|
|
@@ -46,6 +53,12 @@ async function main() {
|
|
|
46
53
|
return await runDevUi(args);
|
|
47
54
|
case "dev:back":
|
|
48
55
|
return await runDevBack(args);
|
|
56
|
+
case "storybook":
|
|
57
|
+
return await runStorybook(args);
|
|
58
|
+
case "mcp": {
|
|
59
|
+
const { runMcp } = await import("../src/mcp.mjs");
|
|
60
|
+
return await runMcp(args);
|
|
61
|
+
}
|
|
49
62
|
case "update":
|
|
50
63
|
return await runUpdate(args);
|
|
51
64
|
case "uninstall":
|
package/cli/prepack.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Runs on `npm publish` / `yarn npm publish` (and `npm pack`) via the "prepack"
|
|
2
|
+
// lifecycle hook. Builds the bundled Storybook (foundations + muscat) and stages
|
|
3
|
+
// it into cli/storybook-dist so it ships inside the published muscat-pk and
|
|
4
|
+
// `muscat-pk storybook` works from an external install.
|
|
5
|
+
//
|
|
6
|
+
// In the monorepo this runs the real build; outside it (re-packing an already
|
|
7
|
+
// published copy) buildStorybook() no-ops and the pre-staged dist is used.
|
|
8
|
+
import { buildStorybook } from "./src/storybook.mjs";
|
|
9
|
+
|
|
10
|
+
buildStorybook();
|
package/cli/src/build.mjs
CHANGED
|
@@ -223,32 +223,6 @@ export async function pinRequirements(root, { stableOnly = false } = {}) {
|
|
|
223
223
|
if (pinned > 0) writeFileSync(reqPath, out.join("\n"));
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
export const SANDBOX_HTML = `<!doctype html>
|
|
227
|
-
<html lang="en">
|
|
228
|
-
<head>
|
|
229
|
-
<meta charset="UTF-8" />
|
|
230
|
-
<title>plugin sandbox</title>
|
|
231
|
-
<script type="importmap">
|
|
232
|
-
{
|
|
233
|
-
"imports": {
|
|
234
|
-
"@rainbow-robotics/plugin-ui/connect": "/plugin-runtime/@rainbow-robotics/plugin-ui/connect.js",
|
|
235
|
-
"@rainbow-robotics/plugin-ui/react": "/plugin-runtime/@rainbow-robotics/plugin-ui/react.js",
|
|
236
|
-
"@rainbow-robotics/plugin-ui/sdk": "/plugin-runtime/@rainbow-robotics/plugin-ui/sdk.js",
|
|
237
|
-
"@rainbow-robotics/plugin-ui/elements": "/plugin-runtime/@rainbow-robotics/plugin-ui/elements.js",
|
|
238
|
-
"react": "/plugin-runtime/vendor/react.js",
|
|
239
|
-
"react/jsx-runtime": "/plugin-runtime/vendor/react-jsx-runtime.js",
|
|
240
|
-
"react/jsx-dev-runtime": "/plugin-runtime/vendor/react-jsx-dev-runtime.js",
|
|
241
|
-
"react-dom/client": "/plugin-runtime/vendor/react-dom-client.js"
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
</script>
|
|
245
|
-
</head>
|
|
246
|
-
<body>
|
|
247
|
-
<script type="module" src="./content.js"></script>
|
|
248
|
-
</body>
|
|
249
|
-
</html>
|
|
250
|
-
`;
|
|
251
|
-
|
|
252
226
|
export async function buildPlugin({ root, minify = false } = {}) {
|
|
253
227
|
const pluginRoot = root ?? findPluginRoot();
|
|
254
228
|
assertManifest(pluginRoot);
|
|
@@ -280,9 +254,8 @@ export async function buildPlugin({ root, minify = false } = {}) {
|
|
|
280
254
|
const manifestSrc = resolve(pluginRoot, "manifest.json");
|
|
281
255
|
if (existsSync(manifestSrc)) copyFileSync(manifestSrc, resolve(distDir, "manifest.json"));
|
|
282
256
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
log.step("generated dist/sandbox.html");
|
|
257
|
+
// No sandbox.html: the host dynamically imports dist/{ui}.js directly as a
|
|
258
|
+
// plain ESM module (see @rainbow-robotics/plugin-ui/host) — no iframe.
|
|
286
259
|
|
|
287
260
|
const assetsSrc = resolve(pluginRoot, "assets");
|
|
288
261
|
if (existsSync(assetsSrc)) {
|
package/cli/src/dev.mjs
CHANGED
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
import { runDevUi } from "./devUi.mjs";
|
|
3
3
|
import { runDevBack } from "./devBack.mjs";
|
|
4
4
|
import { startBridge, stopBridge, BRIDGE_CONTAINER, BRIDGE_PORT } from "./bridge.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { assertManifestPorts } from "./manifest.mjs";
|
|
6
|
+
import { formatPortRanges } from "./ports.mjs";
|
|
7
|
+
import { ensureDocker, findPluginRoot, log, prompt, promptRobotConnection } from "./util.mjs";
|
|
6
8
|
|
|
7
9
|
export async function runDev(args = []) {
|
|
10
|
+
const root = findPluginRoot();
|
|
11
|
+
const portRanges = assertManifestPorts(root);
|
|
12
|
+
if (portRanges.length > 0) {
|
|
13
|
+
log.step(`plugin backend ports declared: ${formatPortRanges(portRanges)}`);
|
|
14
|
+
process.env.MUSCAT_PK_PORTS_PRECHECKED = "1";
|
|
15
|
+
}
|
|
8
16
|
ensureDocker();
|
|
9
17
|
const { robotIp, robotToken } = await promptRobotConnection(args);
|
|
10
|
-
|
|
18
|
+
process.env.MUSCAT_ROBOT_HOST = robotIp;
|
|
11
19
|
if (robotToken) process.env.MUSCAT_ROBOT_TOKEN = robotToken;
|
|
12
20
|
log.step("starting Socket.IO bridge (Docker) + dev:ui + dev:back");
|
|
13
21
|
const bridgeStarted = await startBridge();
|
package/cli/src/devBack.mjs
CHANGED
|
@@ -10,8 +10,21 @@
|
|
|
10
10
|
import { spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, watch } from "node:fs";
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
detectPythonCmd,
|
|
15
|
+
ensureDocker,
|
|
16
|
+
findPluginRoot,
|
|
17
|
+
hasBin,
|
|
18
|
+
log,
|
|
19
|
+
promptRobotConnection,
|
|
20
|
+
venvPython,
|
|
21
|
+
} from "./util.mjs";
|
|
22
|
+
import { assertManifestPorts, manifestRuntime, readManifest, validateManifest } from "./manifest.mjs";
|
|
23
|
+
import {
|
|
24
|
+
formatPortRanges,
|
|
25
|
+
portEnvFlags,
|
|
26
|
+
startPortContractMonitor,
|
|
27
|
+
} from "./ports.mjs";
|
|
15
28
|
import {
|
|
16
29
|
PYTHON_DEV_IMAGE,
|
|
17
30
|
assertDockerReady as pyAssertDockerReady,
|
|
@@ -29,13 +42,49 @@ import {
|
|
|
29
42
|
ensureImage as cppEnsureImage,
|
|
30
43
|
} from "./cpp.mjs";
|
|
31
44
|
|
|
45
|
+
// ── IDE tooling venv (best-effort, non-blocking) ────────────────────────────
|
|
46
|
+
// Docker is the source of truth for actually RUNNING the backend — this just
|
|
47
|
+
// gives editors/linters (pylint, pyright, mypy, ...) a real interpreter with
|
|
48
|
+
// the plugin's deps installed, since they inspect the host filesystem, not the
|
|
49
|
+
// container. Runs in the background so it never delays `dev:back` starting,
|
|
50
|
+
// and any failure here (no local Python toolchain, offline, ...) is silent —
|
|
51
|
+
// Docker works fine with zero local Python.
|
|
52
|
+
function syncIdeVenv(root) {
|
|
53
|
+
const venvDir = resolve(root, ".venv");
|
|
54
|
+
const py = venvPython(venvDir);
|
|
55
|
+
|
|
56
|
+
setImmediate(() => {
|
|
57
|
+
try {
|
|
58
|
+
if (!existsSync(py)) {
|
|
59
|
+
let created;
|
|
60
|
+
if (hasBin("uv")) {
|
|
61
|
+
created = spawnSync("uv", ["venv", venvDir, "--python", "3.12", "--seed"], { cwd: root, stdio: "ignore" });
|
|
62
|
+
} else {
|
|
63
|
+
const { cmd } = detectPythonCmd();
|
|
64
|
+
created = spawnSync(cmd[0], [...cmd.slice(1), "-m", "venv", venvDir], { cwd: root, stdio: "ignore" });
|
|
65
|
+
}
|
|
66
|
+
if (created.status !== 0 || !existsSync(py)) return;
|
|
67
|
+
}
|
|
68
|
+
const reqs = resolve(root, "requirements.txt");
|
|
69
|
+
const args = ["-m", "pip", "install", "-q"];
|
|
70
|
+
if (existsSync(reqs)) args.push("-r", reqs);
|
|
71
|
+
if (spawnSync(py, args, { cwd: root, stdio: "ignore" }).status === 0) {
|
|
72
|
+
log.info(`IDE venv ready → .venv (point your editor's Python interpreter there for pylint/pyright)`);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
/* best-effort only */
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
32
80
|
// ── python ────────────────────────────────────────────────────────────────────
|
|
33
81
|
// Runs in a warm Ubuntu 24.04 + Python 3.12 container (matches the robot).
|
|
34
82
|
// Under `muscat-pk dev`, it shares the bridge container's netns, so the SDK
|
|
35
83
|
// reaches the Socket.IO bridge at the default localhost:10000.
|
|
36
|
-
async function runPython(root) {
|
|
84
|
+
async function runPython(root, portRanges) {
|
|
37
85
|
pyAssertDockerReady();
|
|
38
86
|
await pyEnsureImage();
|
|
87
|
+
syncIdeVenv(root);
|
|
39
88
|
|
|
40
89
|
const manifest = readManifest(root);
|
|
41
90
|
const pluginId = manifest.id ?? "";
|
|
@@ -46,6 +95,7 @@ async function runPython(root) {
|
|
|
46
95
|
...(pluginId ? ["-e", `RB_PLUGIN_ID=${pluginId}`] : []),
|
|
47
96
|
...(robotIp ? ["-e", `MUSCAT_ROBOT_HOST=${robotIp}`] : []),
|
|
48
97
|
...(robotToken ? ["-e", `MUSCAT_ROBOT_TOKEN=${robotToken}`] : []),
|
|
98
|
+
...portEnvFlags(portRanges),
|
|
49
99
|
];
|
|
50
100
|
// Share the bridge container's network namespace so localhost:10000 is the
|
|
51
101
|
// bridge (set by `muscat-pk dev`). Standalone dev:back has no bridge to join.
|
|
@@ -64,6 +114,7 @@ async function runPython(root) {
|
|
|
64
114
|
{ stdio: "inherit" },
|
|
65
115
|
);
|
|
66
116
|
if (up.status !== 0) throw new Error("failed to start dev container.");
|
|
117
|
+
const stopPortMonitor = startPortContractMonitor({ container, ranges: portRanges, log });
|
|
67
118
|
|
|
68
119
|
log.step("pip install requirements + watchfiles (in container)…");
|
|
69
120
|
docker(["exec", container, "bash", "-lc",
|
|
@@ -87,11 +138,13 @@ async function runPython(root) {
|
|
|
87
138
|
});
|
|
88
139
|
|
|
89
140
|
const cleanup = () => {
|
|
141
|
+
stopPortMonitor();
|
|
90
142
|
if (!child.killed) child.kill();
|
|
91
143
|
spawnSync("docker", ["rm", "-f", container], { stdio: "ignore" });
|
|
92
144
|
process.exit(0);
|
|
93
145
|
};
|
|
94
146
|
child.on("exit", (code) => {
|
|
147
|
+
stopPortMonitor();
|
|
95
148
|
spawnSync("docker", ["rm", "-f", container], { stdio: "ignore" });
|
|
96
149
|
process.exit(code ?? 0);
|
|
97
150
|
});
|
|
@@ -100,7 +153,7 @@ async function runPython(root) {
|
|
|
100
153
|
}
|
|
101
154
|
|
|
102
155
|
// ── ros2 ───────────────────────────────────────────────────────────────────────
|
|
103
|
-
async function runRos2(root) {
|
|
156
|
+
async function runRos2(root, portRanges) {
|
|
104
157
|
assertDockerReady();
|
|
105
158
|
const pkg = ros2PackageName(root);
|
|
106
159
|
if (!pkg) throw new Error("no ROS2 package found under ros2_ws/src.");
|
|
@@ -110,6 +163,7 @@ async function runRos2(root) {
|
|
|
110
163
|
const manifest = readManifest(root);
|
|
111
164
|
const pluginId = manifest.id ?? "";
|
|
112
165
|
const isRclcpp = manifest.runtime?.rosClient === "rclcpp";
|
|
166
|
+
if (!isRclcpp) syncIdeVenv(root); // rclpy is plain Python — rclcpp is C++, no venv needed
|
|
113
167
|
const container = `${ROS2_DEV_IMAGE}-run`;
|
|
114
168
|
const robotIp = process.env.MUSCAT_ROBOT_HOST ?? "";
|
|
115
169
|
const robotToken = process.env.MUSCAT_ROBOT_TOKEN ?? "";
|
|
@@ -117,6 +171,7 @@ async function runRos2(root) {
|
|
|
117
171
|
...(pluginId ? ["-e", `RB_PLUGIN_ID=${pluginId}`] : []),
|
|
118
172
|
...(robotIp ? ["-e", `MUSCAT_ROBOT_HOST=${robotIp}`] : []),
|
|
119
173
|
...(robotToken ? ["-e", `MUSCAT_ROBOT_TOKEN=${robotToken}`] : []),
|
|
174
|
+
...portEnvFlags(portRanges),
|
|
120
175
|
];
|
|
121
176
|
const docker = (args, opts = {}) =>
|
|
122
177
|
spawnSync("docker", args, { cwd: root, stdio: "inherit", ...opts });
|
|
@@ -129,6 +184,7 @@ async function runRos2(root) {
|
|
|
129
184
|
{ stdio: "inherit" },
|
|
130
185
|
);
|
|
131
186
|
if (up.status !== 0) throw new Error("failed to start dev container.");
|
|
187
|
+
const stopPortMonitor = startPortContractMonitor({ container, ranges: portRanges, log });
|
|
132
188
|
|
|
133
189
|
// one-time deps + symlink build (edits to .py reflect without rebuilding)
|
|
134
190
|
log.step("pip install + colcon build --symlink-install (once)…");
|
|
@@ -170,6 +226,7 @@ async function runRos2(root) {
|
|
|
170
226
|
}
|
|
171
227
|
|
|
172
228
|
const cleanup = () => {
|
|
229
|
+
stopPortMonitor();
|
|
173
230
|
if (child) child.kill();
|
|
174
231
|
spawnSync("docker", ["rm", "-f", container], { stdio: "ignore" });
|
|
175
232
|
process.exit(0);
|
|
@@ -179,7 +236,7 @@ async function runRos2(root) {
|
|
|
179
236
|
}
|
|
180
237
|
|
|
181
238
|
// ── cpp ──────────────────────────────────────────────────────────────────────
|
|
182
|
-
async function runCpp(root) {
|
|
239
|
+
async function runCpp(root, portRanges) {
|
|
183
240
|
cppAssertDockerReady();
|
|
184
241
|
const manifest = readManifest(root);
|
|
185
242
|
const entry = manifest.runtime?.entry;
|
|
@@ -195,6 +252,7 @@ async function runCpp(root) {
|
|
|
195
252
|
...(pluginId ? ["-e", `RB_PLUGIN_ID=${pluginId}`] : []),
|
|
196
253
|
...(robotIp ? ["-e", `MUSCAT_ROBOT_HOST=${robotIp}`] : []),
|
|
197
254
|
...(robotToken ? ["-e", `MUSCAT_ROBOT_TOKEN=${robotToken}`] : []),
|
|
255
|
+
...portEnvFlags(portRanges),
|
|
198
256
|
];
|
|
199
257
|
const docker = (args, opts = {}) =>
|
|
200
258
|
spawnSync("docker", args, { cwd: root, stdio: "inherit", ...opts });
|
|
@@ -207,6 +265,7 @@ async function runCpp(root) {
|
|
|
207
265
|
{ stdio: "inherit" },
|
|
208
266
|
);
|
|
209
267
|
if (up.status !== 0) throw new Error("failed to start dev container.");
|
|
268
|
+
const stopPortMonitor = startPortContractMonitor({ container, ranges: portRanges, log });
|
|
210
269
|
|
|
211
270
|
const build = () => {
|
|
212
271
|
log.step("cmake build…");
|
|
@@ -243,6 +302,7 @@ async function runCpp(root) {
|
|
|
243
302
|
if (existsSync(cppDir)) watch(cppDir, { recursive: true }, onChange);
|
|
244
303
|
|
|
245
304
|
const cleanup = () => {
|
|
305
|
+
stopPortMonitor();
|
|
246
306
|
if (child) child.kill();
|
|
247
307
|
spawnSync("docker", ["rm", "-f", container], { stdio: "ignore" });
|
|
248
308
|
process.exit(0);
|
|
@@ -259,14 +319,17 @@ function warnManifest(root) {
|
|
|
259
319
|
}
|
|
260
320
|
|
|
261
321
|
export async function runDevBack(args = []) {
|
|
262
|
-
ensureDocker();
|
|
263
322
|
const root = findPluginRoot();
|
|
264
323
|
warnManifest(root);
|
|
324
|
+
const portRanges = assertManifestPorts(root);
|
|
325
|
+
if (portRanges.length > 0 && process.env.MUSCAT_PK_PORTS_PRECHECKED !== "1")
|
|
326
|
+
log.step(`plugin backend ports declared: ${formatPortRanges(portRanges)}`);
|
|
327
|
+
ensureDocker();
|
|
265
328
|
const { robotIp, robotToken } = await promptRobotConnection(args);
|
|
266
329
|
if (robotIp) process.env.MUSCAT_ROBOT_HOST = robotIp;
|
|
267
330
|
if (robotToken) process.env.MUSCAT_ROBOT_TOKEN = robotToken;
|
|
268
331
|
const type = manifestRuntime(root);
|
|
269
|
-
if (type === "ros2") return await runRos2(root);
|
|
270
|
-
if (type === "cpp") return await runCpp(root);
|
|
271
|
-
return await runPython(root);
|
|
332
|
+
if (type === "ros2") return await runRos2(root, portRanges);
|
|
333
|
+
if (type === "cpp") return await runCpp(root, portRanges);
|
|
334
|
+
return await runPython(root, portRanges);
|
|
272
335
|
}
|