@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.5 → 0.1.2-dev.51
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 +82 -49
- package/cli/README.md +191 -5
- package/cli/bin/create-muscat-plugin.mjs +1 -1
- package/cli/bin/muscat-pk.mjs +25 -10
- package/cli/bundle-templates.mjs +28 -0
- package/cli/docker/Dockerfile.bridge +17 -0
- package/cli/docker/Dockerfile.cpp +22 -0
- package/cli/docker/Dockerfile.dev +1 -2
- package/cli/docker/Dockerfile.python +20 -0
- package/cli/docker/bridge-package.json +8 -0
- package/cli/prepack.mjs +10 -0
- package/cli/src/bridge.mjs +95 -0
- package/cli/src/build.mjs +377 -85
- package/cli/src/check-package.mjs +8 -23
- package/cli/src/cpp.mjs +61 -0
- package/cli/src/dev.mjs +49 -4
- package/cli/src/devBack.mjs +311 -49
- package/cli/src/devUi.mjs +198 -19
- package/cli/src/esbuild.mjs +27 -10
- package/cli/src/manifest.mjs +208 -0
- package/cli/src/mcp.mjs +592 -0
- package/cli/src/new.mjs +143 -43
- package/cli/src/pluginDevServer.mjs +112 -0
- package/cli/src/ports.mjs +160 -0
- package/cli/src/python.mjs +109 -0
- package/cli/src/ros2.mjs +43 -18
- package/cli/src/sdk.mjs +11 -9
- package/cli/src/socketBridge.mjs +133 -0
- package/cli/src/storybook.mjs +168 -0
- package/cli/src/templates-embedded.mjs +3 -0
- package/cli/src/type.mjs +52 -29
- package/cli/src/uninstall.mjs +98 -0
- package/cli/src/update.mjs +259 -30
- package/cli/src/util.mjs +114 -7
- package/cli/src/zenohDev.mjs +75 -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 +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 +817 -9
- package/cli/templates/common/assets/icon.svg +44 -2
- package/cli/templates/common/manifest.json +13 -13
- package/cli/templates/common/package.json +11 -11
- package/cli/templates/common/py/main.py +98 -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 +26 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +5 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +17 -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 +22 -0
- package/cli/vite.d.mts +25 -0
- package/cli/vite.mjs +292 -0
- package/package.json +32 -5
- package/cli/src/pack.mjs +0 -61
- package/cli/templates/common/main.py +0 -21
- package/cli/templates/common/src/content.tsx +0 -32
package/cli/src/devUi.mjs
CHANGED
|
@@ -1,38 +1,198 @@
|
|
|
1
|
-
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { createServer as createNetServer } from "node:net";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, watch } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
5
7
|
import { dirname, resolve } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import { checkKitUpdate, findPluginRoot, kitRoot, log } from "./util.mjs";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { checkKitUpdate, findPluginRoot, hasBin, kitRoot, log, parseArgs, prompt } from "./util.mjs";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const DEV_PLUGIN_PORT = 4181;
|
|
13
|
+
|
|
14
|
+
function getFreePort(preferred) {
|
|
15
|
+
return new Promise((res) => {
|
|
16
|
+
const srv = createNetServer();
|
|
17
|
+
srv.once("error", () => {
|
|
18
|
+
const s2 = createNetServer();
|
|
19
|
+
s2.listen(0, () => {
|
|
20
|
+
const { port } = s2.address();
|
|
21
|
+
s2.close(() => res(port));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
srv.listen(preferred, () => srv.close(() => res(preferred)));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const PREVIEW_PKG = "@rainbow-robotics/preview-host";
|
|
29
|
+
const CACHE_DIR = resolve(homedir(), ".muscat-pk");
|
|
30
|
+
const CACHE_ROOT = resolve(CACHE_DIR, "node_modules", "@rainbow-robotics", "preview-host");
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const DEV_OUT = ".muscat-dev";
|
|
34
|
+
|
|
35
|
+
function findNodeBin() {
|
|
36
|
+
if (typeof Bun === "undefined") return process.execPath;
|
|
37
|
+
for (const c of ["node", "node20", "node18"]) {
|
|
38
|
+
if (hasBin(c)) return c;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
8
42
|
|
|
9
|
-
/** Resolve @rainbow-robotics/preview-host package root.
|
|
10
|
-
* Works in monorepo (yarn workspace hoisting) and when both packages are
|
|
11
|
-
* installed as deps of the plugin project. */
|
|
12
43
|
function findPreviewHostRoot() {
|
|
13
44
|
try {
|
|
14
45
|
const req = createRequire(import.meta.url);
|
|
15
|
-
return dirname(req.resolve(
|
|
46
|
+
return dirname(req.resolve(`${PREVIEW_PKG}/package.json`));
|
|
47
|
+
} catch {}
|
|
48
|
+
|
|
49
|
+
const sibling = resolve(kitRoot, "../preview-host");
|
|
50
|
+
if (existsSync(resolve(sibling, "package.json"))) return sibling;
|
|
51
|
+
|
|
52
|
+
if (existsSync(resolve(CACHE_ROOT, "package.json"))) return CACHE_ROOT;
|
|
53
|
+
|
|
54
|
+
if (!hasBin("npm")) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`preview-host를 찾을 수 없습니다.\n` +
|
|
57
|
+
`Node.js 20+ 를 설치하면 자동으로 준비됩니다: https://nodejs.org`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
log.step(`preview-host 설치 중 (~/.muscat-pk) — 첫 실행 시 한 번만 수행됩니다…`);
|
|
61
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
62
|
+
const r = spawnSync(
|
|
63
|
+
"npm",
|
|
64
|
+
["install", "--prefix", CACHE_DIR, "--save", PREVIEW_PKG],
|
|
65
|
+
{ stdio: "inherit" },
|
|
66
|
+
);
|
|
67
|
+
if (r.status !== 0) throw new Error("preview-host 설치 실패.");
|
|
68
|
+
if (!existsSync(resolve(CACHE_ROOT, "package.json"))) {
|
|
69
|
+
throw new Error("preview-host 설치 후에도 패키지를 찾을 수 없습니다.");
|
|
70
|
+
}
|
|
71
|
+
return CACHE_ROOT;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function syncAssets(pluginRoot, outDir) {
|
|
75
|
+
const assetsSrc = resolve(pluginRoot, "assets");
|
|
76
|
+
if (existsSync(assetsSrc)) cpSync(assetsSrc, resolve(outDir, "assets"), { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
function stageDevAssets(pluginRoot) {
|
|
81
|
+
const outDir = resolve(pluginRoot, DEV_OUT);
|
|
82
|
+
mkdirSync(outDir, { recursive: true });
|
|
83
|
+
const manifestSrc = resolve(pluginRoot, "manifest.json");
|
|
84
|
+
if (existsSync(manifestSrc)) copyFileSync(manifestSrc, resolve(outDir, "manifest.json"));
|
|
85
|
+
syncAssets(pluginRoot, outDir);
|
|
86
|
+
return outDir;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function watchAssets(pluginRoot, outDir) {
|
|
90
|
+
const assetsSrc = resolve(pluginRoot, "assets");
|
|
91
|
+
if (!existsSync(assetsSrc)) return;
|
|
92
|
+
watch(assetsSrc, { recursive: true }, () => syncAssets(pluginRoot, outDir));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
function findViteBin(pluginRoot) {
|
|
97
|
+
try {
|
|
98
|
+
const req = createRequire(resolve(pluginRoot, "package.json"));
|
|
99
|
+
const vitePkg = req.resolve("vite/package.json");
|
|
100
|
+
return resolve(dirname(vitePkg), "bin", "vite.js");
|
|
16
101
|
} catch {
|
|
17
|
-
|
|
18
|
-
return resolve(kitRoot, "../preview-host");
|
|
102
|
+
return null;
|
|
19
103
|
}
|
|
20
104
|
}
|
|
21
105
|
|
|
106
|
+
|
|
107
|
+
function spawnPluginDevServer(pluginRoot, node, port, readyFile, sharedRuntimeBase) {
|
|
108
|
+
if (!node || !findViteBin(pluginRoot)) return null;
|
|
109
|
+
const runner = fileURLToPath(new URL("./pluginDevServer.mjs", import.meta.url));
|
|
110
|
+
|
|
111
|
+
rmSync(resolve(pluginRoot, "node_modules", ".vite"), { recursive: true, force: true });
|
|
112
|
+
log.step(`vite dev server (HMR) starting at :${port} — ui/*.tsx`);
|
|
113
|
+
|
|
114
|
+
return spawn(node, [runner, pluginRoot, String(port), readyFile], {
|
|
115
|
+
cwd: pluginRoot,
|
|
116
|
+
stdio: "inherit",
|
|
117
|
+
env: {
|
|
118
|
+
...process.env,
|
|
119
|
+
VITE_PLUGIN_PREVIEW: "true",
|
|
120
|
+
MUSCAT_PLUGIN_SHARED_RUNTIME_BASE: sharedRuntimeBase,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function waitForPluginDevPort(readyFile, proc, timeoutMs = 15_000) {
|
|
126
|
+
const start = Date.now();
|
|
127
|
+
return new Promise((res, rej) => {
|
|
128
|
+
let exited = false;
|
|
129
|
+
proc.once("exit", (code) => {
|
|
130
|
+
exited = true;
|
|
131
|
+
rej(new Error(`plugin dev server exited before ready (code ${code ?? 0})`));
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const tick = () => {
|
|
135
|
+
if (existsSync(readyFile)) {
|
|
136
|
+
try {
|
|
137
|
+
const { port } = JSON.parse(readFileSync(readyFile, "utf8"));
|
|
138
|
+
if (Number.isInteger(port)) return res(port);
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (Date.now() - start > timeoutMs)
|
|
143
|
+
return rej(new Error("plugin dev server did not become ready in time"));
|
|
144
|
+
if (!exited) setTimeout(tick, 100);
|
|
145
|
+
};
|
|
146
|
+
tick();
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
22
150
|
export async function runDevUi(args = []) {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
151
|
+
const parsed = parseArgs(args);
|
|
152
|
+
const requestedPort = Number(parsed.port ?? "4180");
|
|
153
|
+
const robotIp = parsed["robot-ip"] ?? process.env.MUSCAT_ROBOT_HOST ?? await prompt(" Robot IP (skip if no robot): ");
|
|
25
154
|
|
|
26
155
|
await checkKitUpdate();
|
|
27
|
-
const
|
|
156
|
+
const pluginRoot = findPluginRoot();
|
|
157
|
+
const outDir = stageDevAssets(pluginRoot);
|
|
158
|
+
const port = await getFreePort(requestedPort);
|
|
159
|
+
if (port !== requestedPort)
|
|
160
|
+
log.warn(`preview-host port ${requestedPort} is in use — using ${port} instead.`);
|
|
161
|
+
|
|
162
|
+
if (robotIp) log.info(`MUSCAT_API_ORIGIN → http://${robotIp}:*`);
|
|
163
|
+
|
|
164
|
+
watchAssets(pluginRoot, outDir);
|
|
165
|
+
|
|
166
|
+
const node = findNodeBin();
|
|
167
|
+
const readyFile = resolve(outDir, "plugin-dev-port.json");
|
|
168
|
+
rmSync(readyFile, { force: true });
|
|
169
|
+
const sharedRuntimeBase = `http://localhost:${port}/plugin-shared`;
|
|
170
|
+
const viteProc = spawnPluginDevServer(
|
|
171
|
+
pluginRoot,
|
|
172
|
+
node,
|
|
173
|
+
DEV_PLUGIN_PORT,
|
|
174
|
+
readyFile,
|
|
175
|
+
sharedRuntimeBase,
|
|
176
|
+
);
|
|
177
|
+
let devPort = DEV_PLUGIN_PORT;
|
|
178
|
+
|
|
179
|
+
if (!viteProc) {
|
|
180
|
+
log.warn("vite not found — UI will not load. Run `npm install` in the plugin project.");
|
|
181
|
+
} else {
|
|
182
|
+
devPort = await waitForPluginDevPort(readyFile, viteProc);
|
|
183
|
+
viteProc.on("exit", (code) => {
|
|
184
|
+
if (code) log.warn(`plugin dev server exited (code ${code}) — UI won't load (is :${devPort} free?).`);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
28
187
|
|
|
29
188
|
const previewRoot = findPreviewHostRoot();
|
|
30
189
|
const serveEntry = resolve(previewRoot, "serve.mjs");
|
|
31
190
|
if (!existsSync(resolve(previewRoot, "dist", "index.html"))) {
|
|
32
191
|
log.step("building preview-host bundle (first run)…");
|
|
33
192
|
await new Promise((res, rej) => {
|
|
34
|
-
const p = spawn(
|
|
193
|
+
const p = spawn(node ?? process.execPath, [resolve(previewRoot, "build.mjs")], {
|
|
35
194
|
stdio: "inherit",
|
|
195
|
+
env: { ...process.env, VITE_PLUGIN_PREVIEW: "true" },
|
|
36
196
|
});
|
|
37
197
|
p.on("exit", (c) => (c === 0 ? res() : rej(new Error("preview-host build failed"))));
|
|
38
198
|
});
|
|
@@ -40,11 +200,30 @@ export async function runDevUi(args = []) {
|
|
|
40
200
|
|
|
41
201
|
log.step(`starting preview-host on :${port}`);
|
|
42
202
|
const child = spawn(
|
|
43
|
-
|
|
44
|
-
[
|
|
203
|
+
node ?? process.execPath,
|
|
204
|
+
[
|
|
205
|
+
serveEntry,
|
|
206
|
+
"--plugin", outDir,
|
|
207
|
+
"--port", String(port),
|
|
208
|
+
"--dev-server-url", `http://localhost:${devPort}`,
|
|
209
|
+
...(robotIp ? ["--robot-host", robotIp] : []),
|
|
210
|
+
],
|
|
45
211
|
{ stdio: "inherit" },
|
|
46
212
|
);
|
|
47
|
-
|
|
213
|
+
|
|
214
|
+
let cleaned = false;
|
|
215
|
+
const cleanup = () => {
|
|
216
|
+
if (cleaned) return;
|
|
217
|
+
cleaned = true;
|
|
218
|
+
if (viteProc && !viteProc.killed && viteProc.exitCode === null) viteProc.kill();
|
|
219
|
+
if (!child.killed && child.exitCode === null) child.kill();
|
|
220
|
+
};
|
|
221
|
+
process.once("SIGINT", cleanup);
|
|
222
|
+
process.once("SIGTERM", cleanup);
|
|
223
|
+
child.on("exit", (code) => {
|
|
224
|
+
cleanup();
|
|
225
|
+
process.exit(code ?? 0);
|
|
226
|
+
});
|
|
48
227
|
}
|
|
49
228
|
|
|
50
|
-
export { findPluginRoot };
|
|
229
|
+
export { findPluginRoot, findPreviewHostRoot };
|
package/cli/src/esbuild.mjs
CHANGED
|
@@ -20,20 +20,37 @@ async function buildWithBun(options) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
async function buildWithWasm(options) {
|
|
24
|
+
const wasm = await import("esbuild-wasm");
|
|
25
|
+
return wasm.build(options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isNativeMissing(err) {
|
|
29
|
+
const msg = String(err?.message || err?.code || err || "");
|
|
30
|
+
return (
|
|
31
|
+
msg.includes("another platform") ||
|
|
32
|
+
msg.includes("Cannot find module") ||
|
|
33
|
+
msg.includes("Cannot find package") ||
|
|
34
|
+
msg.includes("not found") ||
|
|
35
|
+
msg.includes("No such file") ||
|
|
36
|
+
msg.includes("ENOENT") ||
|
|
37
|
+
msg.includes("MODULE_NOT_FOUND")
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
export async function buildWithFallback(options) {
|
|
24
42
|
if (typeof Bun !== "undefined") return buildWithBun(options);
|
|
25
43
|
|
|
26
|
-
|
|
44
|
+
// Try native esbuild (faster, no extra setup). Falls back to wasm when:
|
|
45
|
+
// - esbuild was not installed (removed from hard dependencies to avoid
|
|
46
|
+
// postinstall script blocking npm i -g on Windows)
|
|
47
|
+
// - binary was built for a different platform
|
|
27
48
|
try {
|
|
49
|
+
const native = await import("esbuild");
|
|
28
50
|
return await native.build(options);
|
|
29
|
-
} catch (
|
|
30
|
-
|
|
31
|
-
if (!message.includes("another platform")) {
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const wasm = await import("esbuild-wasm");
|
|
36
|
-
console.warn("[muscat-pk] native esbuild binary mismatch; falling back to esbuild-wasm");
|
|
37
|
-
return wasm.build(options);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (!isNativeMissing(err)) throw err;
|
|
38
53
|
}
|
|
54
|
+
|
|
55
|
+
return buildWithWasm(options);
|
|
39
56
|
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// manifest.mjs — manifest.json schema definition and validation
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { manifestPortRanges, validatePorts } from "./ports.mjs";
|
|
5
|
+
|
|
6
|
+
export const CONTRIBUTION_POINTS = ["program.command", "toolbar", "move.rnb"];
|
|
7
|
+
const ICON_SIZES = ["16", "48", "128"];
|
|
8
|
+
const RUNTIME_TYPES = ["none", "python", "cpp", "ros2"];
|
|
9
|
+
const ROS_CLIENTS = ["rclpy", "rclcpp"];
|
|
10
|
+
const I18N_LANGS = ["ko", "en", "ja", "zh", "vi", "th", "es"];
|
|
11
|
+
const TARGET_CATEGORIES = ["manipulate", "amr", "rby1", "vision"];
|
|
12
|
+
|
|
13
|
+
function validateI18nLabel(value, field, errors) {
|
|
14
|
+
if (value === undefined || value === null) {
|
|
15
|
+
errors.push(`${field} is required`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === "string") return;
|
|
19
|
+
if (typeof value === "object") {
|
|
20
|
+
if (typeof value.en !== "string")
|
|
21
|
+
errors.push(`${field}: "en" is required when using object form`);
|
|
22
|
+
const unknown = Object.keys(value).filter((k) => !I18N_LANGS.includes(k));
|
|
23
|
+
if (unknown.length > 0)
|
|
24
|
+
errors.push(`${field}: unsupported language key(s) — ${unknown.map((k) => `"${k}"`).join(", ")} (allowed: ${I18N_LANGS.join(", ")})`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
errors.push(`${field}: must be a string or { en (required), ko, ja, zh, vi, th, es }`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function readManifest(root) {
|
|
31
|
+
const path = resolve(root, "manifest.json");
|
|
32
|
+
if (!existsSync(path)) return {};
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function manifestRuntime(root) {
|
|
41
|
+
return readManifest(root)?.runtime?.type ?? "python";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateIcons(icons, errors) {
|
|
45
|
+
if (icons === undefined || icons === null) {
|
|
46
|
+
errors.push("icons is required");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (typeof icons === "string") {
|
|
50
|
+
if (!icons.toLowerCase().endsWith(".svg"))
|
|
51
|
+
errors.push(`icons: non-SVG images (PNG, JPG, etc.) must use the { "16": "...", "48": "...", "128": "..." } object form`);
|
|
52
|
+
} else if (typeof icons === "object") {
|
|
53
|
+
const missing = ICON_SIZES.filter((s) => typeof icons[s] !== "string" || icons[s].length === 0);
|
|
54
|
+
if (missing.length > 0)
|
|
55
|
+
errors.push(`icons: missing size(s) ${missing.map((s) => `"${s}"`).join(", ")} — all three (16, 48, 128) are required for image files`);
|
|
56
|
+
} else {
|
|
57
|
+
errors.push(`icons: must be a string (SVG path) or { "16", "48", "128" } object (PNG/JPG)`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateRuntime(runtime, errors) {
|
|
62
|
+
if (runtime === undefined || runtime === null) {
|
|
63
|
+
errors.push("runtime is required");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (typeof runtime !== "object") {
|
|
67
|
+
errors.push("runtime must be an object");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!RUNTIME_TYPES.includes(runtime.type))
|
|
71
|
+
errors.push(`runtime.type must be one of ${RUNTIME_TYPES.map((t) => `"${t}"`).join(" | ")}`);
|
|
72
|
+
if (runtime.type === "python" && !runtime.entry)
|
|
73
|
+
errors.push("runtime.entry is required for python runtime");
|
|
74
|
+
if (runtime.type === "cpp" && !runtime.entry)
|
|
75
|
+
errors.push("runtime.entry is required for cpp runtime (the built executable name)");
|
|
76
|
+
if (runtime.type === "ros2") {
|
|
77
|
+
if (!runtime.package) errors.push("runtime.package is required for ros2 runtime");
|
|
78
|
+
if (!runtime.launch) errors.push("runtime.launch is required for ros2 runtime");
|
|
79
|
+
if (!ROS_CLIENTS.includes(runtime.rosClient))
|
|
80
|
+
errors.push(`runtime.rosClient must be one of ${ROS_CLIENTS.map((c) => `"${c}"`).join(" | ")}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateCommandInfo(info, prefix, errors) {
|
|
85
|
+
if (typeof info.name !== "string" || info.name.length === 0)
|
|
86
|
+
errors.push(`${prefix}.commandInfo.name is required`);
|
|
87
|
+
if (typeof info.icon !== "string" || info.icon.length === 0)
|
|
88
|
+
errors.push(`${prefix}.commandInfo.icon is required`);
|
|
89
|
+
if (info.iconClassName !== undefined && typeof info.iconClassName !== "string")
|
|
90
|
+
errors.push(`${prefix}.commandInfo.iconClassName must be a string`);
|
|
91
|
+
if (info.funcName !== undefined && typeof info.funcName !== "string")
|
|
92
|
+
errors.push(`${prefix}.commandInfo.funcName must be a string`);
|
|
93
|
+
if (info.args !== undefined && (typeof info.args !== "object" || Array.isArray(info.args)))
|
|
94
|
+
errors.push(`${prefix}.commandInfo.args must be an object`);
|
|
95
|
+
for (const key of ["disabled", "syncGroupId", "disableCopy", "disableDelete", "requireChildren", "requireParent"]) {
|
|
96
|
+
if (info[key] !== undefined && typeof info[key] !== "boolean")
|
|
97
|
+
errors.push(`${prefix}.commandInfo.${key} must be a boolean`);
|
|
98
|
+
}
|
|
99
|
+
for (const key of ["allowChildren", "allowParents", "notAllowChildren"]) {
|
|
100
|
+
if (info[key] !== undefined && (!Array.isArray(info[key]) || info[key].some((v) => typeof v !== "string")))
|
|
101
|
+
errors.push(`${prefix}.commandInfo.${key} must be a string[]`);
|
|
102
|
+
}
|
|
103
|
+
if (info.childrenCommand !== undefined && typeof info.childrenCommand !== "string")
|
|
104
|
+
errors.push(`${prefix}.commandInfo.childrenCommand must be a string`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateContributes(contributes, errors) {
|
|
108
|
+
if (!Array.isArray(contributes) || contributes.length === 0) {
|
|
109
|
+
errors.push("contributes is required and must be a non-empty array");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!contributes.some((c) => c.point === "toolbar"))
|
|
113
|
+
errors.push(`contributes: at least one "toolbar" entry is required`);
|
|
114
|
+
|
|
115
|
+
const uiValues = contributes.map((c) => c.ui).filter(Boolean);
|
|
116
|
+
const duplicateUi = uiValues.filter((v, i) => uiValues.indexOf(v) !== i);
|
|
117
|
+
if (duplicateUi.length > 0)
|
|
118
|
+
errors.push(`contributes: "ui" values must be unique across all entries (duplicates: ${[...new Set(duplicateUi)].map((v) => `"${v}"`).join(", ")})`);
|
|
119
|
+
|
|
120
|
+
contributes.forEach((c, i) => {
|
|
121
|
+
validateI18nLabel(c.label, `contributes[${i}].label`, errors);
|
|
122
|
+
if (c.point === "program.command") {
|
|
123
|
+
if (!c.commandInfo)
|
|
124
|
+
errors.push(`contributes[${i}]: "commandInfo" is required when point is "program.command"`);
|
|
125
|
+
else
|
|
126
|
+
validateCommandInfo(c.commandInfo, `contributes[${i}]`, errors);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate manifest.json. Returns an array of error messages (empty = valid).
|
|
133
|
+
*/
|
|
134
|
+
export function validateManifest(manifest) {
|
|
135
|
+
const errors = [];
|
|
136
|
+
|
|
137
|
+
if (typeof manifest !== "object" || manifest === null)
|
|
138
|
+
return ["manifest.json is not a valid JSON object"];
|
|
139
|
+
|
|
140
|
+
for (const key of ["id", "name", "version", "minMuscatVersion"]) {
|
|
141
|
+
if (typeof manifest[key] !== "string" || manifest[key].length === 0)
|
|
142
|
+
errors.push(`"${key}" is required`);
|
|
143
|
+
}
|
|
144
|
+
if (typeof manifest.author !== "string" || manifest.author.length === 0)
|
|
145
|
+
errors.push(`"author" is required`);
|
|
146
|
+
if (typeof manifest.email !== "string" || manifest.email.length === 0)
|
|
147
|
+
errors.push(`"email" is required`);
|
|
148
|
+
if (manifest.company !== undefined && manifest.company !== null && typeof manifest.company !== "string")
|
|
149
|
+
errors.push(`"company" must be a string`);
|
|
150
|
+
|
|
151
|
+
const hasModels = manifest.targetModels !== undefined && manifest.targetModels !== null;
|
|
152
|
+
const hasCategorys = manifest.targetCategorys !== undefined && manifest.targetCategorys !== null;
|
|
153
|
+
if (!hasModels && !hasCategorys) {
|
|
154
|
+
errors.push(`"targetModels" or "targetCategorys" is required`);
|
|
155
|
+
} else {
|
|
156
|
+
if (hasModels) {
|
|
157
|
+
if (manifest.targetModels !== "all" && (!Array.isArray(manifest.targetModels) || manifest.targetModels.length === 0))
|
|
158
|
+
errors.push(`"targetModels" must be "all" or a non-empty array of model strings`);
|
|
159
|
+
else if (Array.isArray(manifest.targetModels) && manifest.targetModels.some((m) => typeof m !== "string"))
|
|
160
|
+
errors.push(`"targetModels": all entries must be strings`);
|
|
161
|
+
}
|
|
162
|
+
if (hasCategorys) {
|
|
163
|
+
if (manifest.targetCategorys !== "all" && (!Array.isArray(manifest.targetCategorys) || manifest.targetCategorys.length === 0))
|
|
164
|
+
errors.push(`"targetCategorys" must be "all" or a non-empty array`);
|
|
165
|
+
else if (Array.isArray(manifest.targetCategorys)) {
|
|
166
|
+
const invalid = manifest.targetCategorys.filter((c) => !TARGET_CATEGORIES.includes(c));
|
|
167
|
+
if (invalid.length > 0)
|
|
168
|
+
errors.push(`"targetCategorys": invalid value(s) ${invalid.map((c) => `"${c}"`).join(", ")} — allowed: ${TARGET_CATEGORIES.join(", ")}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
validateI18nLabel(manifest.description, "description", errors);
|
|
173
|
+
|
|
174
|
+
validateIcons(manifest.icons, errors);
|
|
175
|
+
validatePorts(manifest.ports, errors);
|
|
176
|
+
validateRuntime(manifest.runtime, errors);
|
|
177
|
+
validateContributes(manifest.contributes, errors);
|
|
178
|
+
|
|
179
|
+
return errors;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Read and validate manifest.json. Throws if any errors are found.
|
|
184
|
+
*/
|
|
185
|
+
export function assertManifest(root) {
|
|
186
|
+
const manifest = readManifest(root);
|
|
187
|
+
if (manifest === null)
|
|
188
|
+
throw new Error("manifest.json parse failed — check JSON syntax");
|
|
189
|
+
if (Object.keys(manifest).length === 0)
|
|
190
|
+
return manifest;
|
|
191
|
+
const errors = validateManifest(manifest);
|
|
192
|
+
if (errors.length > 0)
|
|
193
|
+
throw new Error(`manifest.json errors:\n${errors.map((e) => ` • ${e}`).join("\n")}`);
|
|
194
|
+
return manifest;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function assertManifestPorts(root) {
|
|
198
|
+
const manifest = readManifest(root);
|
|
199
|
+
if (manifest === null)
|
|
200
|
+
throw new Error("manifest.json parse failed — check JSON syntax");
|
|
201
|
+
if (Object.keys(manifest).length === 0)
|
|
202
|
+
return [];
|
|
203
|
+
const errors = [];
|
|
204
|
+
validatePorts(manifest.ports, errors);
|
|
205
|
+
if (errors.length > 0)
|
|
206
|
+
throw new Error(`manifest.json port errors:\n${errors.map((e) => ` • ${e}`).join("\n")}`);
|
|
207
|
+
return manifestPortRanges(manifest);
|
|
208
|
+
}
|