@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.8 → 0.10.1-dev.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.ko.md +1186 -0
- package/README.md +1137 -57
- package/cli/README.md +239 -7
- package/cli/bin/create-muscat-plugin.mjs +1 -1
- package/cli/bin/muscat-pk.mjs +123 -27
- package/cli/bundle-templates.mjs +31 -0
- package/cli/docker/Dockerfile.cpp +28 -0
- package/cli/docker/Dockerfile.dev +17 -0
- package/cli/docker/Dockerfile.python +21 -0
- package/cli/prepack.mjs +10 -0
- package/cli/src/__pycache__/host_agent.cpython-37.pyc +0 -0
- package/cli/src/ai-rules.mjs +107 -0
- package/cli/src/build.mjs +333 -93
- package/cli/src/check-package.mjs +8 -23
- package/cli/src/completion.mjs +168 -0
- package/cli/src/cpp.mjs +78 -0
- package/cli/src/cppIde.mjs +76 -0
- package/cli/src/dev.mjs +195 -6
- package/cli/src/devBack.mjs +671 -60
- package/cli/src/devId.mjs +54 -0
- package/cli/src/devSession.mjs +67 -0
- package/cli/src/devUi.mjs +214 -25
- package/cli/src/esbuild.mjs +27 -10
- package/cli/src/host_agent.py +223 -0
- package/cli/src/manifest.mjs +288 -0
- package/cli/src/mcp.mjs +1153 -0
- package/cli/src/mcpProxy.mjs +110 -0
- package/cli/src/muscatLocal.mjs +292 -0
- package/cli/src/new.mjs +205 -68
- package/cli/src/pluginDevServer.mjs +111 -0
- package/cli/src/ports.mjs +193 -0
- package/cli/src/projectCheck.mjs +102 -0
- package/cli/src/python.mjs +112 -0
- package/cli/src/ros2.mjs +62 -18
- package/cli/src/sdk.mjs +74 -24
- package/cli/src/sim.mjs +177 -0
- package/cli/src/storybook.mjs +150 -0
- package/cli/src/templates-embedded.mjs +4 -0
- package/cli/src/type.mjs +93 -34
- package/cli/src/uninstall.mjs +81 -0
- package/cli/src/update.mjs +364 -35
- package/cli/src/util.mjs +341 -11
- package/cli/src/zenohDev.mjs +83 -0
- package/cli/src/zip.mjs +121 -0
- 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 +56 -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/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 +216 -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-pCKrjUal.js +1 -0
- package/cli/storybook-dist/assets/Docs-B-Y7I1GA.js +7 -0
- package/cli/storybook-dist/assets/Docs-BFJcE_VS.js +8 -0
- package/cli/storybook-dist/assets/Docs-B_QaTtz8.js +8 -0
- package/cli/storybook-dist/assets/Docs-CryDGmfh.js +20 -0
- package/cli/storybook-dist/assets/Docs-Cw2AVNKx.js +7 -0
- package/cli/storybook-dist/assets/Docs-Q6r1fBeL.js +12 -0
- package/cli/storybook-dist/assets/DocsRenderer-CFRXHY34-_OmZBCn8.js +2 -0
- package/cli/storybook-dist/assets/NxAvatar.stories-Ctf5G2FQ.js +13 -0
- package/cli/storybook-dist/assets/NxButton.stories-DeLObVSj.js +20 -0
- package/cli/storybook-dist/assets/NxIcons.stories-DeqrVzVs.js +3 -0
- package/cli/storybook-dist/assets/NxNumberInput.stories-LqoY0ed1.js +26 -0
- package/cli/storybook-dist/assets/NxPagination.stories-DI6bXuJM.js +11 -0
- package/cli/storybook-dist/assets/NxPointerHoldButton.stories-3HSV1XmN.js +14 -0
- package/cli/storybook-dist/assets/NxRangeInput.stories-1pjgQVaG.js +17 -0
- package/cli/storybook-dist/assets/NxSelectBox.stories-bcQPZEuF.js +17 -0
- package/cli/storybook-dist/assets/NxSlideRange.stories-BosX4axk.js +14 -0
- package/cli/storybook-dist/assets/NxSwitch.stories-DxV6dS33.js +11 -0
- package/cli/storybook-dist/assets/NxTextArea.stories-CO545-LP.js +11 -0
- package/cli/storybook-dist/assets/NxTextInput.stories-BP0npai1.js +11 -0
- package/cli/storybook-dist/assets/PretendardVariable-CJuje-Rk.woff2 +0 -0
- package/cli/storybook-dist/assets/RBAccordion.stories-fVR4Va2a.js +17 -0
- package/cli/storybook-dist/assets/RBAccordionGroup.stories-Du5bsckD.js +33 -0
- package/cli/storybook-dist/assets/RBAlert.stories-DNkMXUu6.js +17 -0
- package/cli/storybook-dist/assets/RBBadge.stories-C44pXt8D.js +1 -0
- package/cli/storybook-dist/assets/RBBattery.stories-DE2JHvfL.js +10 -0
- package/cli/storybook-dist/assets/RBCard.stories-CuIda7NI.js +45 -0
- package/cli/storybook-dist/assets/RBCheckBox.stories-D1qM1ayr.js +1 -0
- package/cli/storybook-dist/assets/RBCheckIconButton.stories-e4gkliBO.js +1 -0
- package/cli/storybook-dist/assets/RBCheckLabelButton.stories-CDzCtsMI.js +24 -0
- package/cli/storybook-dist/assets/RBChipTab.stories-DBrc_3Mp.js +21 -0
- package/cli/storybook-dist/assets/RBChipsBoxTab.stories-hlrsHjKY.js +1 -0
- package/cli/storybook-dist/assets/RBColorIcons.stories-BHUvpkdu.js +3 -0
- package/cli/storybook-dist/assets/RBCommand.stories-azAF41NP.js +73 -0
- package/cli/storybook-dist/assets/RBConfirm.stories-BMNjwbmT.js +20 -0
- package/cli/storybook-dist/assets/RBDateInput.stories-D0VezDzv.js +15 -0
- package/cli/storybook-dist/assets/RBDialog.stories-AOJ975Fy.js +38 -0
- package/cli/storybook-dist/assets/RBDivider.stories-NSvnj43Z.js +1 -0
- package/cli/storybook-dist/assets/RBDrawer.stories-Du0oTvD1.js +14 -0
- package/cli/storybook-dist/assets/RBDropdownMenu.stories-Hsxve4PW.js +86 -0
- package/cli/storybook-dist/assets/RBEmptyView.stories-BlwHx3zp.js +12 -0
- package/cli/storybook-dist/assets/RBIconButton.stories-DAPDY63x.js +1 -0
- package/cli/storybook-dist/assets/RBIcons.stories-W5ZUV2L1.js +3 -0
- package/cli/storybook-dist/assets/RBInlineLabelButton.stories-C99j0Y3g.js +1 -0
- package/cli/storybook-dist/assets/RBJoyStick.stories-Du0H1iAS.js +11 -0
- package/cli/storybook-dist/assets/RBLabelButton.stories-BlvHaU_Z.js +1 -0
- package/cli/storybook-dist/assets/RBLight.stories-BY6DKrre.js +1 -0
- package/cli/storybook-dist/assets/RBMenubar.stories-D2PEGpM9.js +3 -0
- package/cli/storybook-dist/assets/RBMultiRangeInput.stories-Bm2qSmjq.js +14 -0
- package/cli/storybook-dist/assets/RBPagination.stories-DDleJa5N.js +17 -0
- package/cli/storybook-dist/assets/RBPopover.stories-27YxPSFG.js +11 -0
- package/cli/storybook-dist/assets/RBPrompt.stories-BFaIOctr.js +20 -0
- package/cli/storybook-dist/assets/RBRadioBox.stories-BroDar0t.js +1 -0
- package/cli/storybook-dist/assets/RBRadioButton.stories-M3O-wX7V.js +11 -0
- package/cli/storybook-dist/assets/RBRangeDateInput.stories-CWlM_D5X.js +15 -0
- package/cli/storybook-dist/assets/RBResizable.stories-ui8X1qnc.js +20 -0
- package/cli/storybook-dist/assets/RBSelectBox.stories-C8T6cfA0.js +74 -0
- package/cli/storybook-dist/assets/RBSheet.stories-BCOo_Zcc.js +39 -0
- package/cli/storybook-dist/assets/RBSideNumberInput.stories-CU2_MVlJ.js +5 -0
- package/cli/storybook-dist/assets/RBSlider.stories-DgOp1TFc.js +5 -0
- package/cli/storybook-dist/assets/RBSpinner.stories-qLScY3zZ.js +14 -0
- package/cli/storybook-dist/assets/RBSwitch.stories-SXSY1QMb.js +65 -0
- package/cli/storybook-dist/assets/RBTab.stories-BzXXTsXP.js +1 -0
- package/cli/storybook-dist/assets/RBTable.stories-DHTeRNVk.js +62 -0
- package/cli/storybook-dist/assets/RBTableRow.stories-DRNRsd15.js +11 -0
- package/cli/storybook-dist/assets/RBTabs.stories-DP0noBLp.js +21 -0
- package/cli/storybook-dist/assets/RBTextArea.stories-TkaejfxJ.js +5 -0
- package/cli/storybook-dist/assets/RBTextField.stories-B6VpCGyt.js +45 -0
- package/cli/storybook-dist/assets/RBTextInput.stories-D3hZ08e-.js +5 -0
- package/cli/storybook-dist/assets/RBToast.stories-BQ2wS2U0.js +11 -0
- package/cli/storybook-dist/assets/RBToggle.stories-GVOjGqo3.js +12 -0
- package/cli/storybook-dist/assets/RBToggleGroup.stories-pzx0btOD.js +17 -0
- package/cli/storybook-dist/assets/RBTooltip.stories-jufB6fa1.js +9 -0
- package/cli/storybook-dist/assets/SideNumberInput-BYlmNHEw.js +1109 -0
- package/cli/storybook-dist/assets/SideNumberInput-SAkhydsn.css +21 -0
- package/cli/storybook-dist/assets/browserUtils-CQM9Za7e.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/entry-preview-CAkU-e0D.js +2 -0
- package/cli/storybook-dist/assets/entry-preview-docs-b8bpVBXy.js +46 -0
- package/cli/storybook-dist/assets/iframe-B-BmTsxV.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-CT3U1fyk.js +1 -0
- package/cli/storybook-dist/assets/index-ChsGqxH_.js +9 -0
- package/cli/storybook-dist/assets/index-CkB0GfaI.js +575 -0
- package/cli/storybook-dist/assets/index-ClAc6FBW.js +240 -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/preview-B8lJiyuQ.js +34 -0
- package/cli/storybook-dist/assets/preview-BBWR9nbA.js +1 -0
- package/cli/storybook-dist/assets/preview-BVJwlGUF.js +1 -0
- package/cli/storybook-dist/assets/preview-BWzBA1C2.js +396 -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-CznQwwbo.js +3 -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-M77JRg1A.js +2 -0
- package/cli/storybook-dist/assets/preview-kTZPgP6D.css +1 -0
- package/cli/storybook-dist/assets/react-18-hB3smqWJ.js +1 -0
- package/cli/storybook-dist/assets/react-resizable-panels-HsFbxPbn.css +1 -0
- package/cli/storybook-dist/assets/react-resizable-panels.browser-DuxpZLkF.js +120 -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/README.md +1070 -83
- package/cli/templates/common/assets/icon.svg +44 -2
- package/cli/templates/common/manifest.json +14 -13
- package/cli/templates/common/package.json +11 -4
- package/cli/templates/common/py/main.py +125 -0
- package/cli/templates/common/requirements.txt +4 -3
- package/cli/templates/common/tsconfig.json +1 -1
- package/cli/templates/common/ui/content.tsx +216 -0
- package/cli/templates/common/ui/style.css +6 -0
- package/cli/templates/common/vite.config.ts +6 -25
- package/cli/templates/cpp/cpp/CMakeLists.txt +14 -0
- package/cli/templates/cpp/cpp/main.cpp +73 -0
- package/cli/templates/readme-cpp/README.md +1012 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +12 -1
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +22 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +41 -0
- package/cli/vite.d.mts +25 -0
- package/cli/vite.mjs +292 -0
- package/package.json +34 -7
- package/cli/src/pack.mjs +0 -61
- package/cli/src/preview.mjs +0 -91
- package/cli/templates/common/main.py +0 -114
- package/cli/templates/common/src/content.tsx +0 -33
package/cli/src/new.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// muscat-pk new — scaffold a new plugin project from templates.
|
|
2
|
-
import {
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
3
|
import {
|
|
4
4
|
cpSync,
|
|
5
5
|
existsSync,
|
|
@@ -7,26 +7,102 @@ import {
|
|
|
7
7
|
readFileSync,
|
|
8
8
|
readdirSync,
|
|
9
9
|
renameSync,
|
|
10
|
+
rmSync,
|
|
10
11
|
statSync,
|
|
11
12
|
writeFileSync,
|
|
12
13
|
} from "node:fs";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { basename, dirname, resolve } from "node:path";
|
|
15
|
+
import { color, ensureIdeVenv, hasBin, kitVersion, parseArgs, prompt, templatesDir, log } from "./util.mjs";
|
|
16
|
+
import { agentsMd, CLAUDE_MD, MCP_JSON } from "./ai-rules.mjs";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
// Like cpSync(src, dst, {recursive:true}) but reads from the embedded snapshot
|
|
19
|
+
// when the templates dir doesn't exist on disk (compiled binary mode).
|
|
20
|
+
// ponytail: import() is module-cached by the runtime — no manual cache needed.
|
|
21
|
+
export async function copyTemplate(srcDir, dst) {
|
|
22
|
+
if (existsSync(templatesDir)) { cpSync(srcDir, dst, { recursive: true }); return; }
|
|
23
|
+
const { TEMPLATES } = await import("./templates-embedded.mjs");
|
|
24
|
+
const prefix = srcDir.slice(templatesDir.length + 1).replace(/\\/g, "/") + "/";
|
|
25
|
+
for (const [key, b64] of Object.entries(TEMPLATES)) {
|
|
26
|
+
if (!key.startsWith(prefix)) continue;
|
|
27
|
+
const dest = resolve(dst, key.slice(prefix.length));
|
|
28
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
29
|
+
writeFileSync(dest, Buffer.from(b64, "base64"));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// `--type` values map to a runtime descriptor. ros2 has a python (rclpy) and a
|
|
34
|
+
// C++ (rclcpp) flavor; we expose them as distinct type choices for clarity.
|
|
35
|
+
export const TYPE_CHOICES = {
|
|
36
|
+
python: { type: "python" },
|
|
37
|
+
cpp: { type: "cpp" },
|
|
38
|
+
ros2: { type: "ros2", rosClient: "rclpy" },
|
|
39
|
+
"ros2-cpp":{ type: "ros2", rosClient: "rclcpp" },
|
|
40
|
+
};
|
|
41
|
+
const TYPES = Object.keys(TYPE_CHOICES);
|
|
18
42
|
const PMS = ["npm", "yarn", "pnpm", "bun"];
|
|
19
43
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
function printNewSuccess({ targetName, type, pm }) {
|
|
45
|
+
const mark = [
|
|
46
|
+
" /\\",
|
|
47
|
+
" / \\_",
|
|
48
|
+
" ____||__",
|
|
49
|
+
" / \\__/ \\",
|
|
50
|
+
" __/\\__/ \\__/\\",
|
|
51
|
+
" / \\__/\\__/\\__/\\",
|
|
52
|
+
" \\__/ \\__/ \\__/",
|
|
53
|
+
" \\__/ \\__/",
|
|
54
|
+
" \\__/",
|
|
55
|
+
].map((line) => color.green(line)).join("\n");
|
|
56
|
+
|
|
57
|
+
log.info("");
|
|
58
|
+
log.info(mark);
|
|
59
|
+
log.info(color.bold(color.green("Welcome to Muscat Plugin Kit!")));
|
|
60
|
+
log.info(color.dim("A fresh plugin workspace is ready."));
|
|
61
|
+
log.info("");
|
|
62
|
+
log.ok(`created ${color.bold(targetName)} ${color.dim(`(type: ${type}, pm: ${pm})`)}`);
|
|
63
|
+
log.info("");
|
|
64
|
+
log.info(color.bold("Next steps"));
|
|
65
|
+
log.info(` ${color.cyan("cd")} ${targetName}`);
|
|
66
|
+
log.info(` ${color.cyan("muscat-pk dev")} ${color.dim("# preview in mini-Muscat + back end")}`);
|
|
67
|
+
log.info(` ${color.cyan("muscat-pk build")} ${color.dim("# build + distributable ZIP")}`);
|
|
68
|
+
log.info("");
|
|
69
|
+
log.info(color.dim("AI assistant? Claude Code reads the scaffolded .mcp.json and CLAUDE.md (→ AGENTS.md);"));
|
|
70
|
+
log.info(color.dim(`Codex users run ${color.cyan("muscat-pk mcp --install codex")} once.`));
|
|
71
|
+
log.info("");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// C++ backends: the editor needs the SDK headers, which only exist in the dev image. Building the image
|
|
75
|
+
// now (dev/build reuse it) lets VS Code resolve rb_sdk right away. Best-effort, like the .venv.
|
|
76
|
+
export async function prepareCppIde(root, choice) {
|
|
77
|
+
const later = "they come with the first `muscat-pk dev` / `build`";
|
|
78
|
+
if (!hasBin("docker")) return log.warn(`Docker not found — SDK headers for the editor: ${later}.`);
|
|
79
|
+
try {
|
|
80
|
+
log.step("preparing the C++ dev image + SDK headers for your editor (first run is slow)…");
|
|
81
|
+
const { ensureImage } = await import(choice.type === "cpp" ? "./cpp.mjs" : "./ros2.mjs");
|
|
82
|
+
await ensureImage(undefined, { root });
|
|
83
|
+
} catch (err) {
|
|
84
|
+
log.warn(`Could not prepare SDK headers for the editor (${err.message}) — ${later}.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Build the manifest runtime object for a given type choice + plugin slug.
|
|
89
|
+
export function runtimeForChoice(choice, slug) {
|
|
90
|
+
switch (choice.type) {
|
|
91
|
+
case "cpp":
|
|
92
|
+
// entry = built executable name (matches add_executable in cpp/CMakeLists.txt)
|
|
93
|
+
return { type: "cpp", entry: slug };
|
|
94
|
+
case "ros2":
|
|
95
|
+
// package = colcon package name; launch = node executable run on the robot
|
|
96
|
+
return { type: "ros2", rosClient: choice.rosClient, package: slug, launch: `${slug}_node` };
|
|
97
|
+
case "python":
|
|
98
|
+
default:
|
|
99
|
+
return { type: "python", entry: "main.py" };
|
|
100
|
+
}
|
|
26
101
|
}
|
|
27
102
|
|
|
28
103
|
export function applyTokens(dir, tokens) {
|
|
29
104
|
for (const entry of readdirSync(dir)) {
|
|
105
|
+
if (entry === "node_modules" || entry === ".git" || entry === ".venv") continue;
|
|
30
106
|
const full = resolve(dir, entry);
|
|
31
107
|
if (statSync(full).isDirectory()) {
|
|
32
108
|
applyTokens(full, tokens);
|
|
@@ -46,6 +122,12 @@ export function applyTokens(dir, tokens) {
|
|
|
46
122
|
}
|
|
47
123
|
|
|
48
124
|
// Derive naming tokens from a plugin directory name.
|
|
125
|
+
// One id part (see PLUGIN_ID_RE): lowercase, single "-", starts with a letter.
|
|
126
|
+
function idPart(name) {
|
|
127
|
+
const part = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
128
|
+
return /^[a-z]/.test(part) ? part : part ? `plugin-${part}` : "plugin";
|
|
129
|
+
}
|
|
130
|
+
|
|
49
131
|
export function deriveNames(target) {
|
|
50
132
|
const base = basename(target);
|
|
51
133
|
const slug = base.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
@@ -55,7 +137,7 @@ export function deriveNames(target) {
|
|
|
55
137
|
slug,
|
|
56
138
|
pluginName,
|
|
57
139
|
pluginClass: pluginName.replace(/\s+/g, ""),
|
|
58
|
-
pluginId: `com.example.${base
|
|
140
|
+
pluginId: `com.example.${idPart(base)}`,
|
|
59
141
|
};
|
|
60
142
|
}
|
|
61
143
|
|
|
@@ -74,16 +156,25 @@ function renameSlugPaths(dir, slug) {
|
|
|
74
156
|
// Copy the ros2_ws/ overlay into a plugin and substitute the package slug into
|
|
75
157
|
// every path segment. The dev Dockerfile is NOT scaffolded here — it lives in
|
|
76
158
|
// the kit (cli/docker/) so authors can't edit it. Caller runs applyTokens after.
|
|
77
|
-
|
|
78
|
-
|
|
159
|
+
// rclpy → templates/ros2 (ament_python)
|
|
160
|
+
// rclcpp → templates/ros2-cpp (ament_cmake)
|
|
161
|
+
export async function overlayRos2(target, slug, rosClient = "rclpy") {
|
|
162
|
+
const tmpl = rosClient === "rclcpp" ? "ros2-cpp" : "ros2";
|
|
163
|
+
await copyTemplate(resolve(templatesDir, tmpl), target);
|
|
79
164
|
renameSlugPaths(resolve(target, "ros2_ws"), slug);
|
|
80
165
|
}
|
|
81
166
|
|
|
167
|
+
// Copy the cpp/ overlay (CMake project) into a plugin. Caller runs applyTokens after.
|
|
168
|
+
export async function overlayCpp(target) {
|
|
169
|
+
await copyTemplate(resolve(templatesDir, "cpp"), target);
|
|
170
|
+
}
|
|
171
|
+
|
|
82
172
|
export async function runNew(args) {
|
|
83
173
|
const opts = parseArgs(args, { booleans: ["yes", "no-install", "help"] });
|
|
84
174
|
if (opts.help) {
|
|
85
175
|
console.log(
|
|
86
|
-
"muscat-pk new [dir] [--type python|ros2] [--pm npm|yarn|pnpm|bun]\n" +
|
|
176
|
+
"muscat-pk new [dir] [--type python|cpp|ros2|ros2-cpp] [--pm npm|yarn|pnpm|bun]\n" +
|
|
177
|
+
" [--author <name>] [--email <email>] [--description <text>]\n" +
|
|
87
178
|
" [--yes] [--no-install]",
|
|
88
179
|
);
|
|
89
180
|
return;
|
|
@@ -91,25 +182,53 @@ export async function runNew(args) {
|
|
|
91
182
|
|
|
92
183
|
const dirArg = opts._[0];
|
|
93
184
|
const targetName =
|
|
94
|
-
dirArg || (await prompt("Plugin directory name: ", "my-plugin"));
|
|
185
|
+
dirArg || (await prompt(color.cyan("Plugin directory name: "), "my-plugin"));
|
|
95
186
|
const target = resolve(process.cwd(), targetName);
|
|
96
187
|
if (existsSync(target) && readdirSync(target).length > 0) {
|
|
97
188
|
throw new Error(`directory not empty: ${target}`);
|
|
98
189
|
}
|
|
99
190
|
|
|
191
|
+
let authorArg = opts.author;
|
|
192
|
+
let emailArg = opts.email;
|
|
193
|
+
let descriptionArg = opts.description;
|
|
194
|
+
|
|
195
|
+
if (!opts.yes) {
|
|
196
|
+
if (!authorArg) authorArg = await prompt(color.cyan("Author name: "), "");
|
|
197
|
+
if (!emailArg) emailArg = await prompt(`${color.cyan("Email")} ${color.yellow("(required)")}: `, "");
|
|
198
|
+
if (!descriptionArg) descriptionArg = await prompt(color.cyan("Description (English): "), "");
|
|
199
|
+
}
|
|
200
|
+
|
|
100
201
|
let type = opts.type;
|
|
101
202
|
if (!type && !opts.yes) {
|
|
102
|
-
console.log("
|
|
103
|
-
console.log("
|
|
104
|
-
console.log("
|
|
105
|
-
|
|
106
|
-
|
|
203
|
+
console.log(`\n${color.bold(color.cyan("Select the plugin type:"))}`);
|
|
204
|
+
console.log(` ${color.green("1)") } ${color.bold("Python")}`);
|
|
205
|
+
console.log(` ${color.green("2)") } ${color.bold("C++")}`);
|
|
206
|
+
console.log(` ${color.green("3)") } ${color.bold("ROS2")} ${color.dim("(rclpy / Python)")}`);
|
|
207
|
+
console.log(` ${color.green("4)") } ${color.bold("ROS2")} ${color.dim("(rclcpp / C++)")}`);
|
|
208
|
+
const choice = await prompt(`\n${color.cyan("선택")} ${color.dim("[1/2/3/4, default: 1]")}: `, "1");
|
|
209
|
+
type = { "2": "cpp", "3": "ros2", "4": "ros2-cpp" }[choice] ?? "python";
|
|
107
210
|
}
|
|
108
211
|
type = TYPES.includes(type) ? type : "python";
|
|
212
|
+
const runtimeChoice = TYPE_CHOICES[type];
|
|
213
|
+
|
|
214
|
+
// Auto-detect yarn workspace: if .yarnrc.yml exists anywhere up the tree, default to yarn.
|
|
215
|
+
function isInsideYarnWorkspace(dir) {
|
|
216
|
+
let cur = dir;
|
|
217
|
+
while (true) {
|
|
218
|
+
if (existsSync(resolve(cur, ".yarnrc.yml"))) return true;
|
|
219
|
+
const parent = resolve(cur, "..");
|
|
220
|
+
if (parent === cur) return false;
|
|
221
|
+
cur = parent;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
109
224
|
|
|
110
225
|
let pm = opts.pm;
|
|
111
226
|
if (!pm && !opts.yes) {
|
|
112
|
-
|
|
227
|
+
const defaultPm = isInsideYarnWorkspace(process.cwd()) ? "yarn" : "npm";
|
|
228
|
+
pm = await prompt(
|
|
229
|
+
`${color.cyan("Front-end package manager")} ${color.dim(`(${PMS.join("/")})`)} ${color.dim(`[${defaultPm}]`)}: `,
|
|
230
|
+
defaultPm,
|
|
231
|
+
);
|
|
113
232
|
}
|
|
114
233
|
pm = PMS.includes(pm) ? pm : "npm";
|
|
115
234
|
|
|
@@ -117,11 +236,21 @@ export async function runNew(args) {
|
|
|
117
236
|
|
|
118
237
|
mkdirSync(target, { recursive: true });
|
|
119
238
|
|
|
120
|
-
// common(UI +
|
|
121
|
-
//
|
|
239
|
+
// common(UI + manifest/config)은 항상 포함. 백엔드 오버레이는 타입별로 얹는다.
|
|
240
|
+
// python → common의 py/ 사용
|
|
241
|
+
// cpp → cpp/ 오버레이, py/·requirements.txt 제거
|
|
242
|
+
// ros2(*) → ros2_ws/ 오버레이 (rclpy=ament_python, rclcpp=ament_cmake)
|
|
122
243
|
log.step(`scaffolding ${type} plugin → ${target}`);
|
|
123
|
-
|
|
124
|
-
if (type === "ros2") overlayRos2(target, slug);
|
|
244
|
+
await copyTemplate(resolve(templatesDir, "common"), target);
|
|
245
|
+
if (runtimeChoice.type === "ros2") await overlayRos2(target, slug, runtimeChoice.rosClient);
|
|
246
|
+
if (runtimeChoice.type === "cpp") {
|
|
247
|
+
await overlayCpp(target);
|
|
248
|
+
rmSync(resolve(target, "py"), { recursive: true, force: true });
|
|
249
|
+
rmSync(resolve(target, "requirements.txt"), { force: true });
|
|
250
|
+
}
|
|
251
|
+
if (runtimeChoice.type === "cpp" || runtimeChoice.rosClient === "rclcpp") {
|
|
252
|
+
await copyTemplate(resolve(templatesDir, "readme-cpp"), target);
|
|
253
|
+
}
|
|
125
254
|
|
|
126
255
|
applyTokens(target, {
|
|
127
256
|
__PLUGIN_ID__: pluginId,
|
|
@@ -129,16 +258,31 @@ export async function runNew(args) {
|
|
|
129
258
|
__PLUGIN_DIR__: dir,
|
|
130
259
|
__PLUGIN_SLUG__: slug,
|
|
131
260
|
__PLUGIN_CLASS__: pluginClass,
|
|
261
|
+
__PLUGIN_AUTHOR__: authorArg || "",
|
|
262
|
+
__PLUGIN_EMAIL__: emailArg || "",
|
|
132
263
|
__PM__: pm,
|
|
133
|
-
__RUNTIME_TYPE__: type,
|
|
264
|
+
__RUNTIME_TYPE__: runtimeChoice.type,
|
|
265
|
+
__RB_VERSION__: kitVersion(),
|
|
134
266
|
});
|
|
135
267
|
|
|
268
|
+
// Write the correct runtime block into manifest.json (per-type fields).
|
|
269
|
+
const manifestPath = resolve(target, "manifest.json");
|
|
270
|
+
const manifestJson = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
271
|
+
manifestJson.runtime = runtimeForChoice(runtimeChoice, slug);
|
|
272
|
+
if (descriptionArg) manifestJson.description = { en: descriptionArg };
|
|
273
|
+
writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 2) + "\n");
|
|
274
|
+
|
|
136
275
|
// gitignore (templates can't ship a literal .gitignore via npm reliably)
|
|
137
276
|
writeFileSync(
|
|
138
277
|
resolve(target, ".gitignore"),
|
|
139
|
-
["node_modules", "dist", ".venv", "*.zip", "ros2_ws/install", ""].join("\n"),
|
|
278
|
+
["node_modules", "dist", ".muscat-dev", ".venv", "*.zip", "ros2_ws/install", "ros2_ws/build*", "ros2_ws/log", "cpp/build*", ""].join("\n"),
|
|
140
279
|
);
|
|
141
280
|
|
|
281
|
+
// Rules for AI agents, generated from the same source as the MCP server's instructions.
|
|
282
|
+
writeFileSync(resolve(target, "AGENTS.md"), agentsMd({ pluginName }));
|
|
283
|
+
writeFileSync(resolve(target, "CLAUDE.md"), CLAUDE_MD);
|
|
284
|
+
writeFileSync(resolve(target, ".mcp.json"), MCP_JSON);
|
|
285
|
+
|
|
142
286
|
if (!opts["no-install"]) {
|
|
143
287
|
try {
|
|
144
288
|
log.step(`installing JS deps with ${pm}…`);
|
|
@@ -146,61 +290,54 @@ export async function runNew(args) {
|
|
|
146
290
|
} catch {
|
|
147
291
|
log.warn(`'${pm} install' failed; run it manually in ${targetName}`);
|
|
148
292
|
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let noPython = false;
|
|
152
|
-
let venvFailed = false;
|
|
153
293
|
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
} else if (hasBin("python3.12") || hasBin("python3") || hasBin("python")) {
|
|
165
|
-
const { cmd, is312 } = detectPythonCmd();
|
|
166
|
-
if (!is312) log.warn("python3.12 not found; venv uses fallback python (robot targets 3.12).");
|
|
167
|
-
execFileSync(cmd[0], [...cmd.slice(1), "-m", "venv", venv], { cwd: target, stdio: "inherit" });
|
|
294
|
+
// Local .venv is for your editor/linter (pylint, pyright, ...) only — the
|
|
295
|
+
// backend actually RUNS in Docker (see README), this never affects that.
|
|
296
|
+
// rclcpp is C++, so no venv applies there.
|
|
297
|
+
const needsIdeVenv =
|
|
298
|
+
runtimeChoice.type === "python" ||
|
|
299
|
+
(runtimeChoice.type === "ros2" && runtimeChoice.rosClient === "rclpy");
|
|
300
|
+
if (needsIdeVenv) {
|
|
301
|
+
log.step("creating .venv for your editor (pylint/pyright)…");
|
|
302
|
+
if (ensureIdeVenv(target)) {
|
|
303
|
+
log.ok(".venv ready — point your editor's Python interpreter there.");
|
|
168
304
|
} else {
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
if (!noPython) {
|
|
172
|
-
const py = venvPython(venv);
|
|
173
|
-
if (existsSync(resolve(target, "requirements.txt"))) {
|
|
174
|
-
execFileSync(py, ["-m", "pip", "install", "-q", "-r", "requirements.txt"], { cwd: target, stdio: "inherit" });
|
|
175
|
-
}
|
|
305
|
+
log.warn("Could not create .venv (no local Python/uv?) — skipping, Docker dev still works.");
|
|
176
306
|
}
|
|
307
|
+
}
|
|
308
|
+
if (runtimeChoice.type === "cpp" || runtimeChoice.rosClient === "rclcpp") await prepareCppIde(target, runtimeChoice);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// git init (best-effort). Always runs, even nested inside another repo —
|
|
312
|
+
// delete target/.git if you don't want it.
|
|
313
|
+
if (hasBin("git")) {
|
|
314
|
+
try {
|
|
315
|
+
execSync("git init", { cwd: target, stdio: "ignore" });
|
|
316
|
+
execSync("git add -A", { cwd: target, stdio: "ignore" });
|
|
317
|
+
execSync('git commit -m "Initial commit from muscat-pk new" --no-verify', { cwd: target, stdio: "ignore" });
|
|
177
318
|
} catch {
|
|
178
|
-
|
|
319
|
+
log.warn("git init failed — run it manually if you want version control here.");
|
|
179
320
|
}
|
|
180
|
-
} else {
|
|
181
|
-
noPython = !hasBin("uv") && !hasBin("conda") && !hasBin("python3.12") && !hasBin("python3") && !hasBin("python");
|
|
182
321
|
}
|
|
183
322
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
323
|
+
// The back end (python / cpp / ros2) runs in a Docker container that matches
|
|
324
|
+
// the robot — no local Python/toolchain setup needed. Only `none` skips it.
|
|
325
|
+
const needsDocker = runtimeChoice.type !== "none";
|
|
326
|
+
const noDocker = needsDocker && !hasBin("docker");
|
|
327
|
+
|
|
328
|
+
printNewSuccess({ targetName, type, pm });
|
|
188
329
|
|
|
189
330
|
// ── post-scaffold environment warnings ───────────────────────────────────
|
|
190
331
|
const noNode = !hasBin("node");
|
|
191
|
-
if (noNode ||
|
|
332
|
+
if (noNode || noDocker) {
|
|
192
333
|
log.info("");
|
|
193
334
|
if (noNode) {
|
|
194
335
|
log.warn("Node.js not found. Install Node.js 20+ to run JS deps and dev:ui.");
|
|
195
336
|
log.warn(" https://nodejs.org");
|
|
196
337
|
}
|
|
197
|
-
if (
|
|
198
|
-
log.warn("
|
|
199
|
-
log.warn(" https://
|
|
200
|
-
log.warn(` Then: cd ${targetName} && python3.12 -m venv .venv && pip install -r requirements.txt`);
|
|
201
|
-
} else if (venvFailed) {
|
|
202
|
-
log.warn("venv setup failed. Create it manually:");
|
|
203
|
-
log.warn(` cd ${targetName} && python3.12 -m venv .venv && pip install -r requirements.txt`);
|
|
338
|
+
if (noDocker) {
|
|
339
|
+
log.warn("Docker not found. The plugin back end runs in a container (matches the robot env).");
|
|
340
|
+
log.warn(" https://docs.docker.com/get-docker/");
|
|
204
341
|
}
|
|
205
342
|
log.info("");
|
|
206
343
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { writeFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const pluginRoot = resolve(process.argv[2]);
|
|
8
|
+
const port = Number(process.argv[3] || "4181");
|
|
9
|
+
const readyFile = process.argv[4] ? resolve(process.argv[4]) : "";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
process.chdir(pluginRoot);
|
|
13
|
+
|
|
14
|
+
const require = createRequire(resolve(pluginRoot, "package.json"));
|
|
15
|
+
|
|
16
|
+
const viteMod = await import(pathToFileURL(require.resolve("vite")));
|
|
17
|
+
const createServer = viteMod.createServer ?? viteMod.default?.createServer;
|
|
18
|
+
|
|
19
|
+
const BOOTSTRAP_PREFIX = "\0rb-bootstrap:";
|
|
20
|
+
const PREAMBLE_ID = "\0rb-preamble";
|
|
21
|
+
const bootstrapPlugin = {
|
|
22
|
+
name: "rb-plugin-bootstrap",
|
|
23
|
+
resolveId(id) {
|
|
24
|
+
if (id.startsWith("rb-bootstrap:")) return "\0" + id;
|
|
25
|
+
if (id === "rb-preamble") return PREAMBLE_ID;
|
|
26
|
+
},
|
|
27
|
+
load(id) {
|
|
28
|
+
if (id === PREAMBLE_ID) {
|
|
29
|
+
return `
|
|
30
|
+
import RefreshRuntime from "/@react-refresh";
|
|
31
|
+
RefreshRuntime.injectIntoGlobalHook(window);
|
|
32
|
+
window.$RefreshReg$ = () => {};
|
|
33
|
+
window.$RefreshSig$ = () => (type) => type;
|
|
34
|
+
window.__vite_plugin_react_preamble_installed__ = true;
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
if (!id.startsWith(BOOTSTRAP_PREFIX)) return;
|
|
38
|
+
const ui = id.slice(BOOTSTRAP_PREFIX.length) || "content";
|
|
39
|
+
const entry = JSON.stringify(`muscat-ui-entry:${ui}`);
|
|
40
|
+
return `
|
|
41
|
+
import "rb-preamble";
|
|
42
|
+
import { createElement } from "react";
|
|
43
|
+
import { createRoot } from "react-dom/client";
|
|
44
|
+
import { RbContext } from "@rainbow-robotics/plugin-ui/sdk";
|
|
45
|
+
import { setHostServices } from "@rainbow-robotics/plugin-ui/host-ui";
|
|
46
|
+
import { RbWs, createLazyRbSdk } from "@rainbow-robotics/plugin-ui/ws";
|
|
47
|
+
import App from ${entry};
|
|
48
|
+
|
|
49
|
+
// HMR passes the new module to the accept callback but doesn't rebind the
|
|
50
|
+
// static \`App\` import, so we track the latest at module scope. Without this,
|
|
51
|
+
// navigating away and back (a fresh mount() on this cached module) renders the
|
|
52
|
+
// stale App from first import. \`activeRender\` points at the mounted root so
|
|
53
|
+
// live edits re-render in place.
|
|
54
|
+
let latestApp = App;
|
|
55
|
+
let activeRender = null;
|
|
56
|
+
if (import.meta.hot) {
|
|
57
|
+
import.meta.hot.accept(${entry}, (m) => {
|
|
58
|
+
if (!m || !m.default) return;
|
|
59
|
+
latestApp = m.default;
|
|
60
|
+
activeRender?.(latestApp);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function mount(container, { socketUrl, pluginId, hostServices, data, onSave }) {
|
|
65
|
+
container.setAttribute("data-muscat-plugin-root", "");
|
|
66
|
+
container.setAttribute("data-muscat-plugin-id", pluginId);
|
|
67
|
+
setHostServices(hostServices);
|
|
68
|
+
// Lazy connect: don't block the first render on the WebSocket handshake
|
|
69
|
+
// (there may be no backend at all in dev, e.g. no robot reachable) — each rb
|
|
70
|
+
// method awaits this promise internally instead.
|
|
71
|
+
const wsPromise = RbWs.connect(socketUrl, pluginId);
|
|
72
|
+
const rb = createLazyRbSdk(wsPromise, hostServices, { data, onSave, pluginId });
|
|
73
|
+
const root = createRoot(container);
|
|
74
|
+
let mounted = true;
|
|
75
|
+
const render = (Component) => {
|
|
76
|
+
if (!mounted) return;
|
|
77
|
+
root.render(createElement(RbContext.Provider, { value: rb }, createElement(Component)));
|
|
78
|
+
};
|
|
79
|
+
const boundRender = (Component) => queueMicrotask(() => render(Component));
|
|
80
|
+
activeRender = boundRender;
|
|
81
|
+
render(latestApp);
|
|
82
|
+
return () => {
|
|
83
|
+
mounted = false;
|
|
84
|
+
// Don't clobber a newer mount's render (host defers unmount a microtask).
|
|
85
|
+
if (activeRender === boundRender) activeRender = null;
|
|
86
|
+
root.unmount();
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const server = await createServer({
|
|
94
|
+
root: pluginRoot,
|
|
95
|
+
configFile: resolve(pluginRoot, "vite.config.ts"),
|
|
96
|
+
mode: "development",
|
|
97
|
+
plugins: [bootstrapPlugin],
|
|
98
|
+
server: {
|
|
99
|
+
port,
|
|
100
|
+
strictPort: false,
|
|
101
|
+
cors: true,
|
|
102
|
+
hmr: { host: "localhost", protocol: "ws" },
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await server.listen();
|
|
107
|
+
const address = server.httpServer?.address();
|
|
108
|
+
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
109
|
+
if (readyFile) {
|
|
110
|
+
writeFileSync(readyFile, JSON.stringify({ port: actualPort }), "utf8");
|
|
111
|
+
}
|