@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.9 → 0.10.1-dev.1

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.
Files changed (276) hide show
  1. package/README.ko.md +1188 -0
  2. package/README.md +1139 -57
  3. package/cli/README.md +239 -7
  4. package/cli/bin/create-muscat-plugin.mjs +1 -1
  5. package/cli/bin/muscat-pk.mjs +123 -27
  6. package/cli/bundle-templates.mjs +31 -0
  7. package/cli/docker/Dockerfile.cpp +28 -0
  8. package/cli/docker/Dockerfile.dev +17 -0
  9. package/cli/docker/Dockerfile.python +21 -0
  10. package/cli/prepack.mjs +12 -0
  11. package/cli/program-commands.json +2079 -0
  12. package/cli/src/__pycache__/host_agent.cpython-37.pyc +0 -0
  13. package/cli/src/ai-rules.mjs +108 -0
  14. package/cli/src/build.mjs +333 -93
  15. package/cli/src/check-package.mjs +8 -23
  16. package/cli/src/completion.mjs +168 -0
  17. package/cli/src/cpp.mjs +78 -0
  18. package/cli/src/cppIde.mjs +76 -0
  19. package/cli/src/dev.mjs +195 -6
  20. package/cli/src/devBack.mjs +671 -60
  21. package/cli/src/devId.mjs +54 -0
  22. package/cli/src/devSession.mjs +67 -0
  23. package/cli/src/devUi.mjs +218 -25
  24. package/cli/src/esbuild.mjs +27 -10
  25. package/cli/src/host_agent.py +223 -0
  26. package/cli/src/manifest.mjs +288 -0
  27. package/cli/src/mcp.mjs +1232 -0
  28. package/cli/src/mcpProxy.mjs +110 -0
  29. package/cli/src/muscatLocal.mjs +292 -0
  30. package/cli/src/new.mjs +205 -68
  31. package/cli/src/pluginDevServer.mjs +111 -0
  32. package/cli/src/ports.mjs +193 -0
  33. package/cli/src/programCommands.mjs +327 -0
  34. package/cli/src/projectCheck.mjs +102 -0
  35. package/cli/src/python.mjs +112 -0
  36. package/cli/src/ros2.mjs +62 -18
  37. package/cli/src/sdk.mjs +74 -24
  38. package/cli/src/sim.mjs +177 -0
  39. package/cli/src/storybook.mjs +150 -0
  40. package/cli/src/templates-embedded.mjs +4 -0
  41. package/cli/src/type.mjs +93 -34
  42. package/cli/src/uninstall.mjs +81 -0
  43. package/cli/src/update.mjs +364 -35
  44. package/cli/src/util.mjs +341 -11
  45. package/cli/src/zenohDev.mjs +83 -0
  46. package/cli/src/zip.mjs +121 -0
  47. package/cli/storybook-dist/_design-tokens.css +4957 -0
  48. package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -0
  49. package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -0
  50. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -0
  51. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -0
  52. package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -0
  53. package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -0
  54. package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -0
  55. package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -0
  56. package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -0
  57. package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -0
  58. package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -0
  59. package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -0
  60. package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +56 -0
  61. package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -0
  62. package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -0
  63. package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -0
  64. package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -0
  65. package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -0
  66. package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -0
  67. package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -0
  68. package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -0
  69. package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -0
  70. package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -0
  71. package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -0
  72. package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +216 -0
  73. package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -0
  74. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -0
  75. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -0
  76. package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -0
  77. package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -0
  78. package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -0
  79. package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -0
  80. package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -0
  81. package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -0
  82. package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -0
  83. package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -0
  84. package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -0
  85. package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -0
  86. package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -0
  87. package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -0
  88. package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -0
  89. package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -0
  90. package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -0
  91. package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -0
  92. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -0
  93. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -0
  94. package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -0
  95. package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -0
  96. package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -0
  97. package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -0
  98. package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -0
  99. package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -0
  100. package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -0
  101. package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -0
  102. package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -0
  103. package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -0
  104. package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -0
  105. package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -0
  106. package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -0
  107. package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -0
  108. package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -0
  109. package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -0
  110. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -0
  111. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -0
  112. package/cli/storybook-dist/assets/Color-YHDXOIA2-pCKrjUal.js +1 -0
  113. package/cli/storybook-dist/assets/Docs-B-Y7I1GA.js +7 -0
  114. package/cli/storybook-dist/assets/Docs-BFJcE_VS.js +8 -0
  115. package/cli/storybook-dist/assets/Docs-B_QaTtz8.js +8 -0
  116. package/cli/storybook-dist/assets/Docs-CryDGmfh.js +20 -0
  117. package/cli/storybook-dist/assets/Docs-Cw2AVNKx.js +7 -0
  118. package/cli/storybook-dist/assets/Docs-Q6r1fBeL.js +12 -0
  119. package/cli/storybook-dist/assets/DocsRenderer-CFRXHY34-_OmZBCn8.js +2 -0
  120. package/cli/storybook-dist/assets/NxAvatar.stories-Ctf5G2FQ.js +13 -0
  121. package/cli/storybook-dist/assets/NxButton.stories-DeLObVSj.js +20 -0
  122. package/cli/storybook-dist/assets/NxIcons.stories-DeqrVzVs.js +3 -0
  123. package/cli/storybook-dist/assets/NxNumberInput.stories-LqoY0ed1.js +26 -0
  124. package/cli/storybook-dist/assets/NxPagination.stories-DI6bXuJM.js +11 -0
  125. package/cli/storybook-dist/assets/NxPointerHoldButton.stories-3HSV1XmN.js +14 -0
  126. package/cli/storybook-dist/assets/NxRangeInput.stories-1pjgQVaG.js +17 -0
  127. package/cli/storybook-dist/assets/NxSelectBox.stories-bcQPZEuF.js +17 -0
  128. package/cli/storybook-dist/assets/NxSlideRange.stories-BosX4axk.js +14 -0
  129. package/cli/storybook-dist/assets/NxSwitch.stories-DxV6dS33.js +11 -0
  130. package/cli/storybook-dist/assets/NxTextArea.stories-CO545-LP.js +11 -0
  131. package/cli/storybook-dist/assets/NxTextInput.stories-BP0npai1.js +11 -0
  132. package/cli/storybook-dist/assets/PretendardVariable-CJuje-Rk.woff2 +0 -0
  133. package/cli/storybook-dist/assets/RBAccordion.stories-fVR4Va2a.js +17 -0
  134. package/cli/storybook-dist/assets/RBAccordionGroup.stories-Du5bsckD.js +33 -0
  135. package/cli/storybook-dist/assets/RBAlert.stories-DNkMXUu6.js +17 -0
  136. package/cli/storybook-dist/assets/RBBadge.stories-C44pXt8D.js +1 -0
  137. package/cli/storybook-dist/assets/RBBattery.stories-DE2JHvfL.js +10 -0
  138. package/cli/storybook-dist/assets/RBCard.stories-CuIda7NI.js +45 -0
  139. package/cli/storybook-dist/assets/RBCheckBox.stories-D1qM1ayr.js +1 -0
  140. package/cli/storybook-dist/assets/RBCheckIconButton.stories-e4gkliBO.js +1 -0
  141. package/cli/storybook-dist/assets/RBCheckLabelButton.stories-CDzCtsMI.js +24 -0
  142. package/cli/storybook-dist/assets/RBChipTab.stories-DBrc_3Mp.js +21 -0
  143. package/cli/storybook-dist/assets/RBChipsBoxTab.stories-hlrsHjKY.js +1 -0
  144. package/cli/storybook-dist/assets/RBColorIcons.stories-BHUvpkdu.js +3 -0
  145. package/cli/storybook-dist/assets/RBCommand.stories-azAF41NP.js +73 -0
  146. package/cli/storybook-dist/assets/RBConfirm.stories-BMNjwbmT.js +20 -0
  147. package/cli/storybook-dist/assets/RBDateInput.stories-D0VezDzv.js +15 -0
  148. package/cli/storybook-dist/assets/RBDialog.stories-AOJ975Fy.js +38 -0
  149. package/cli/storybook-dist/assets/RBDivider.stories-NSvnj43Z.js +1 -0
  150. package/cli/storybook-dist/assets/RBDrawer.stories-Du0oTvD1.js +14 -0
  151. package/cli/storybook-dist/assets/RBDropdownMenu.stories-Hsxve4PW.js +86 -0
  152. package/cli/storybook-dist/assets/RBEmptyView.stories-BlwHx3zp.js +12 -0
  153. package/cli/storybook-dist/assets/RBIconButton.stories-DAPDY63x.js +1 -0
  154. package/cli/storybook-dist/assets/RBIcons.stories-W5ZUV2L1.js +3 -0
  155. package/cli/storybook-dist/assets/RBInlineLabelButton.stories-C99j0Y3g.js +1 -0
  156. package/cli/storybook-dist/assets/RBJoyStick.stories-Du0H1iAS.js +11 -0
  157. package/cli/storybook-dist/assets/RBLabelButton.stories-BlvHaU_Z.js +1 -0
  158. package/cli/storybook-dist/assets/RBLight.stories-BY6DKrre.js +1 -0
  159. package/cli/storybook-dist/assets/RBMenubar.stories-D2PEGpM9.js +3 -0
  160. package/cli/storybook-dist/assets/RBMultiRangeInput.stories-Bm2qSmjq.js +14 -0
  161. package/cli/storybook-dist/assets/RBPagination.stories-DDleJa5N.js +17 -0
  162. package/cli/storybook-dist/assets/RBPopover.stories-27YxPSFG.js +11 -0
  163. package/cli/storybook-dist/assets/RBPrompt.stories-BFaIOctr.js +20 -0
  164. package/cli/storybook-dist/assets/RBRadioBox.stories-BroDar0t.js +1 -0
  165. package/cli/storybook-dist/assets/RBRadioButton.stories-M3O-wX7V.js +11 -0
  166. package/cli/storybook-dist/assets/RBRangeDateInput.stories-CWlM_D5X.js +15 -0
  167. package/cli/storybook-dist/assets/RBResizable.stories-ui8X1qnc.js +20 -0
  168. package/cli/storybook-dist/assets/RBSelectBox.stories-C8T6cfA0.js +74 -0
  169. package/cli/storybook-dist/assets/RBSheet.stories-BCOo_Zcc.js +39 -0
  170. package/cli/storybook-dist/assets/RBSideNumberInput.stories-CU2_MVlJ.js +5 -0
  171. package/cli/storybook-dist/assets/RBSlider.stories-DgOp1TFc.js +5 -0
  172. package/cli/storybook-dist/assets/RBSpinner.stories-qLScY3zZ.js +14 -0
  173. package/cli/storybook-dist/assets/RBSwitch.stories-SXSY1QMb.js +65 -0
  174. package/cli/storybook-dist/assets/RBTab.stories-BzXXTsXP.js +1 -0
  175. package/cli/storybook-dist/assets/RBTable.stories-DHTeRNVk.js +62 -0
  176. package/cli/storybook-dist/assets/RBTableRow.stories-DRNRsd15.js +11 -0
  177. package/cli/storybook-dist/assets/RBTabs.stories-DP0noBLp.js +21 -0
  178. package/cli/storybook-dist/assets/RBTextArea.stories-TkaejfxJ.js +5 -0
  179. package/cli/storybook-dist/assets/RBTextField.stories-B6VpCGyt.js +45 -0
  180. package/cli/storybook-dist/assets/RBTextInput.stories-D3hZ08e-.js +5 -0
  181. package/cli/storybook-dist/assets/RBToast.stories-BQ2wS2U0.js +11 -0
  182. package/cli/storybook-dist/assets/RBToggle.stories-GVOjGqo3.js +12 -0
  183. package/cli/storybook-dist/assets/RBToggleGroup.stories-pzx0btOD.js +17 -0
  184. package/cli/storybook-dist/assets/RBTooltip.stories-jufB6fa1.js +9 -0
  185. package/cli/storybook-dist/assets/SideNumberInput-BYlmNHEw.js +1109 -0
  186. package/cli/storybook-dist/assets/SideNumberInput-SAkhydsn.css +21 -0
  187. package/cli/storybook-dist/assets/browserUtils-CQM9Za7e.js +1 -0
  188. package/cli/storybook-dist/assets/chunk-XP5HYGXS-BHXC9YeQ.js +1 -0
  189. package/cli/storybook-dist/assets/client-DTf7SId2.js +1 -0
  190. package/cli/storybook-dist/assets/entry-preview-CAkU-e0D.js +2 -0
  191. package/cli/storybook-dist/assets/entry-preview-docs-b8bpVBXy.js +46 -0
  192. package/cli/storybook-dist/assets/iframe-B-BmTsxV.js +211 -0
  193. package/cli/storybook-dist/assets/index-B4FGfHer.js +1 -0
  194. package/cli/storybook-dist/assets/index-BRq5TIsn.js +24 -0
  195. package/cli/storybook-dist/assets/index-CT3U1fyk.js +1 -0
  196. package/cli/storybook-dist/assets/index-ChsGqxH_.js +9 -0
  197. package/cli/storybook-dist/assets/index-CkB0GfaI.js +575 -0
  198. package/cli/storybook-dist/assets/index-ClAc6FBW.js +240 -0
  199. package/cli/storybook-dist/assets/index-DKFBREX_.js +1 -0
  200. package/cli/storybook-dist/assets/index-DgH-xKnr.js +11 -0
  201. package/cli/storybook-dist/assets/index-Dmy2JGbm.js +1 -0
  202. package/cli/storybook-dist/assets/index-DrFu-skq.js +6 -0
  203. package/cli/storybook-dist/assets/jsx-runtime-BTJTZTIL.js +9 -0
  204. package/cli/storybook-dist/assets/preview-B8lJiyuQ.js +34 -0
  205. package/cli/storybook-dist/assets/preview-BBWR9nbA.js +1 -0
  206. package/cli/storybook-dist/assets/preview-BVJwlGUF.js +1 -0
  207. package/cli/storybook-dist/assets/preview-BWzBA1C2.js +396 -0
  208. package/cli/storybook-dist/assets/preview-CVgpLj3b.js +1 -0
  209. package/cli/storybook-dist/assets/preview-CvbIS5ZJ.js +1 -0
  210. package/cli/storybook-dist/assets/preview-CznQwwbo.js +3 -0
  211. package/cli/storybook-dist/assets/preview-DD_OYowb.js +1 -0
  212. package/cli/storybook-dist/assets/preview-DGUiP6tS.js +7 -0
  213. package/cli/storybook-dist/assets/preview-DHQbi4pV.js +1 -0
  214. package/cli/storybook-dist/assets/preview-M77JRg1A.js +2 -0
  215. package/cli/storybook-dist/assets/preview-kTZPgP6D.css +1 -0
  216. package/cli/storybook-dist/assets/react-18-hB3smqWJ.js +1 -0
  217. package/cli/storybook-dist/assets/react-resizable-panels-HsFbxPbn.css +1 -0
  218. package/cli/storybook-dist/assets/react-resizable-panels.browser-DuxpZLkF.js +120 -0
  219. package/cli/storybook-dist/assets/test-utils-BQudk2xt.js +9 -0
  220. package/cli/storybook-dist/favicon.svg +1 -0
  221. package/cli/storybook-dist/iframe.html +666 -0
  222. package/cli/storybook-dist/index.html +190 -0
  223. package/cli/storybook-dist/index.json +1 -0
  224. package/cli/storybook-dist/nunito-sans-bold-italic.woff2 +0 -0
  225. package/cli/storybook-dist/nunito-sans-bold.woff2 +0 -0
  226. package/cli/storybook-dist/nunito-sans-italic.woff2 +0 -0
  227. package/cli/storybook-dist/nunito-sans-regular.woff2 +0 -0
  228. package/cli/storybook-dist/project.json +1 -0
  229. package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js +331 -0
  230. package/cli/storybook-dist/sb-addons/chromatic-com-storybook-10/manager-bundle.js.LEGAL.txt +51 -0
  231. package/cli/storybook-dist/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
  232. package/cli/storybook-dist/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
  233. package/cli/storybook-dist/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
  234. package/cli/storybook-dist/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
  235. package/cli/storybook-dist/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
  236. package/cli/storybook-dist/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
  237. package/cli/storybook-dist/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
  238. package/cli/storybook-dist/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
  239. package/cli/storybook-dist/sb-addons/links-1/manager-bundle.js +3 -0
  240. package/cli/storybook-dist/sb-addons/storybook-13/manager-bundle.js +3 -0
  241. package/cli/storybook-dist/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
  242. package/cli/storybook-dist/sb-addons/storybook-experimental-addon-test-12/manager-bundle.js +223 -0
  243. package/cli/storybook-dist/sb-addons/themes-11/manager-bundle.js +3 -0
  244. package/cli/storybook-dist/sb-common-assets/favicon.svg +1 -0
  245. package/cli/storybook-dist/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  246. package/cli/storybook-dist/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  247. package/cli/storybook-dist/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  248. package/cli/storybook-dist/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  249. package/cli/storybook-dist/sb-manager/globals-module-info.js +1052 -0
  250. package/cli/storybook-dist/sb-manager/globals-runtime.js +41775 -0
  251. package/cli/storybook-dist/sb-manager/globals.js +48 -0
  252. package/cli/storybook-dist/sb-manager/runtime.js +12048 -0
  253. package/cli/templates/common/README.md +1070 -83
  254. package/cli/templates/common/assets/icon.svg +44 -2
  255. package/cli/templates/common/manifest.json +14 -13
  256. package/cli/templates/common/package.json +11 -4
  257. package/cli/templates/common/py/main.py +125 -0
  258. package/cli/templates/common/requirements.txt +4 -3
  259. package/cli/templates/common/tsconfig.json +1 -1
  260. package/cli/templates/common/ui/content.tsx +216 -0
  261. package/cli/templates/common/ui/style.css +6 -0
  262. package/cli/templates/common/vite.config.ts +6 -25
  263. package/cli/templates/cpp/cpp/CMakeLists.txt +14 -0
  264. package/cli/templates/cpp/cpp/main.cpp +73 -0
  265. package/cli/templates/readme-cpp/README.md +1012 -0
  266. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +12 -1
  267. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +22 -0
  268. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -0
  269. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +41 -0
  270. package/cli/vite.d.mts +25 -0
  271. package/cli/vite.mjs +292 -0
  272. package/package.json +34 -7
  273. package/cli/src/pack.mjs +0 -61
  274. package/cli/src/preview.mjs +0 -91
  275. package/cli/templates/common/main.py +0 -114
  276. package/cli/templates/common/src/content.tsx +0 -33
@@ -0,0 +1,168 @@
1
+ // muscat-pk completion — print a shell completion script for bash / zsh / fish.
2
+ //
3
+ // muscat-pk completion zsh >> ~/.zshrc (or source it from there)
4
+ // muscat-pk completion bash >> ~/.bashrc
5
+ // muscat-pk completion fish > ~/.config/fish/completions/muscat-pk.fish
6
+ // muscat-pk completion powershell >> $PROFILE
7
+ //
8
+ // Keep COMMANDS in sync with the switch in bin/muscat-pk.mjs.
9
+ import { log } from "./util.mjs";
10
+
11
+ const COMMANDS = [
12
+ ["new", "Scaffold a new plugin project"],
13
+ ["type", "Switch backend type"],
14
+ ["build", "Build + zip the plugin"],
15
+ ["dev", "Bridge + dev:ui + dev:back"],
16
+ ["dev:ui", "Preview the plugin in a mini-Muscat"],
17
+ ["dev:back", "Run the plugin back end"],
18
+ ["storybook", "Browse the bundled rb-components Storybook"],
19
+ ["mcp", "Start an MCP server (stdio)"],
20
+ ["update", "Update muscat-pk and plugin-ui"],
21
+ ["uninstall", "Remove the installed muscat-pk CLI"],
22
+ ["sdk", "Manage the rainbow-rb-sdk version (python: requirements.txt, C++: CMakeLists.txt)"],
23
+ ["sim", "List the robot simulators for dev --sim"],
24
+ ["version", "Print the installed muscat-pk version"],
25
+ ["completion", "Print a shell completion script"],
26
+ ["help", "Show help"],
27
+ ];
28
+
29
+ const SUBCOMMANDS = {
30
+ type: ["change"],
31
+ sdk: ["list", "update"],
32
+ sim: ["list"],
33
+ completion: ["bash", "zsh", "fish", "powershell"],
34
+ };
35
+
36
+ const names = COMMANDS.map(([name]) => name);
37
+
38
+ function bashScript() {
39
+ const subCases = Object.entries(SUBCOMMANDS)
40
+ .map(([cmd, subs]) => ` ${cmd}) COMPREPLY=( $(compgen -W "${subs.join(" ")}" -- "$cur") ); return ;;`)
41
+ .join("\n");
42
+
43
+ return `_muscat_pk() {
44
+ local cur prev
45
+ cur="\${COMP_WORDS[COMP_CWORD]}"
46
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
47
+ case "$prev" in
48
+ ${subCases}
49
+ esac
50
+ if [ "$COMP_CWORD" -eq 1 ]; then
51
+ COMPREPLY=( $(compgen -W "${names.join(" ")}" -- "$cur") )
52
+ # bash treats ":" as a word break, so "dev:ui" would come back duplicated
53
+ if type __ltrim_colon_completions >/dev/null 2>&1; then
54
+ __ltrim_colon_completions "$cur"
55
+ fi
56
+ fi
57
+ }
58
+ complete -F _muscat_pk muscat-pk`;
59
+ }
60
+
61
+ function zshScript() {
62
+ // _describe splits on ":", so a command name containing one (dev:ui) must escape it
63
+ const cmds = COMMANDS.map(
64
+ ([name, desc]) => ` '${name.replace(/:/g, "\\:")}:${desc}'`,
65
+ ).join("\n");
66
+ const subCases = Object.entries(SUBCOMMANDS)
67
+ .map(([cmd, subs]) => ` ${cmd}) _values 'subcommand' ${subs.join(" ")} ;;`)
68
+ .join("\n");
69
+
70
+ // Sourced from ~/.zshrc, compdef only exists once compinit has run — a bare
71
+ // zsh (no oh-my-zsh, no compinit) would error on every shell start otherwise.
72
+ return `#compdef muscat-pk
73
+ if ! whence compdef > /dev/null; then
74
+ autoload -Uz compinit && compinit
75
+ fi
76
+ _muscat_pk() {
77
+ local -a cmds
78
+ cmds=(
79
+ ${cmds}
80
+ )
81
+ if (( CURRENT == 2 )); then
82
+ _describe 'command' cmds
83
+ else
84
+ case \${words[2]} in
85
+ ${subCases}
86
+ esac
87
+ fi
88
+ }
89
+ if whence compdef > /dev/null; then
90
+ compdef _muscat_pk muscat-pk
91
+ fi`;
92
+ }
93
+
94
+ function fishScript() {
95
+ const top = COMMANDS.map(
96
+ ([name, desc]) =>
97
+ `complete -c muscat-pk -f -n __fish_use_subcommand -a ${name} -d ${JSON.stringify(desc)}`,
98
+ );
99
+ const subs = Object.entries(SUBCOMMANDS).flatMap(([cmd, list]) =>
100
+ list.map(
101
+ (sub) =>
102
+ `complete -c muscat-pk -f -n "__fish_seen_subcommand_from ${cmd}" -a ${sub}`,
103
+ ),
104
+ );
105
+ return [...top, ...subs].join("\n");
106
+ }
107
+
108
+ function powershellScript() {
109
+ const subMap = Object.entries(SUBCOMMANDS)
110
+ .map(([cmd, subs]) => ` '${cmd}' = @(${subs.map((s) => `'${s}'`).join(", ")})`)
111
+ .join("\n");
112
+
113
+ return `Register-ArgumentCompleter -Native -CommandName muscat-pk -ScriptBlock {
114
+ param($wordToComplete, $commandAst, $cursorPosition)
115
+ $commands = @(${names.map((n) => `'${n}'`).join(", ")})
116
+ $subcommands = @{
117
+ ${subMap}
118
+ }
119
+ $elements = @($commandAst.CommandElements | ForEach-Object { $_.ToString() })
120
+ # element 0 is "muscat-pk"; a trailing partial word is also in the list
121
+ $typedCommand = if ($elements.Count -gt 1) { $elements[1] } else { '' }
122
+ $candidates =
123
+ if ($elements.Count -gt 2 -or ($elements.Count -eq 2 -and $wordToComplete -eq '')) {
124
+ if ($subcommands.ContainsKey($typedCommand)) { $subcommands[$typedCommand] } else { @() }
125
+ } else { $commands }
126
+ $candidates |
127
+ Where-Object { $_ -like "$wordToComplete*" } |
128
+ ForEach-Object {
129
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
130
+ }
131
+ }`;
132
+ }
133
+
134
+ const GENERATORS = {
135
+ bash: bashScript,
136
+ zsh: zshScript,
137
+ fish: fishScript,
138
+ powershell: powershellScript,
139
+ };
140
+
141
+ const HINTS = {
142
+ bash: "muscat-pk completion bash >> ~/.bashrc → then: source ~/.bashrc",
143
+ zsh: "muscat-pk completion zsh >> ~/.zshrc → then: source ~/.zshrc",
144
+ fish: "muscat-pk completion fish > ~/.config/fish/completions/muscat-pk.fish",
145
+ powershell: "muscat-pk completion powershell >> $PROFILE → then: . $PROFILE",
146
+ };
147
+
148
+ /** Guess the user's shell from $SHELL; PowerShell on Windows, else bash. */
149
+ function detectShell() {
150
+ const shell = (process.env.SHELL || "").split(/[/\\]/).pop();
151
+ if (shell in GENERATORS) return shell;
152
+ return process.platform === "win32" ? "powershell" : "bash";
153
+ }
154
+
155
+ export async function runCompletion(args = []) {
156
+ const shell = args[0] ?? detectShell();
157
+ const generate = GENERATORS[shell];
158
+
159
+ if (!generate) {
160
+ throw new Error(
161
+ `Unsupported shell "${shell}" (supported: ${Object.keys(GENERATORS).join(", ")})`,
162
+ );
163
+ }
164
+
165
+ // Only the script goes to stdout, so `>> ~/.zshrc` stays valid.
166
+ console.log(generate());
167
+ if (process.stdout.isTTY) log.info(`\nInstall: ${HINTS[shell]}`);
168
+ }
@@ -0,0 +1,78 @@
1
+ // Shared C++ Docker helpers (Ubuntu 24.04 + build-essential + cmake).
2
+ // Matches the robot OS so the compiled binary is relocatable to the robot.
3
+ // Used by dev:back (interactive dev loop) and build (export the binary into the ZIP).
4
+ import { spawnSync } from "node:child_process";
5
+ import { dirname } from "node:path";
6
+ import { canSkipImageBuild, cppSdkVersion, pluginRootOrNull, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile, buildCacheFlags } from "./util.mjs";
7
+ import { syncCppIde } from "./cppIde.mjs";
8
+
9
+ export const CPP_DEV_IMAGE = "muscat-pk-cpp-dev";
10
+
11
+ export function assertDockerReady() {
12
+ if (!hasBin("docker")) {
13
+ throw new Error("Docker not found. C++ builds need Docker (Ubuntu 24.04 + cmake container).");
14
+ }
15
+ }
16
+
17
+ /** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
18
+ export async function ensureImage(image = CPP_DEV_IMAGE, { platform = null, root = pluginRootOrNull() } = {}) {
19
+ await buildImage(image, platform, root);
20
+ // Dev images feed the editor's SDK headers: the native one (dev) or the host-arch one (build).
21
+ const hostArch = process.arch === "arm64" ? "arm64" : "amd64";
22
+ if (root && (image === CPP_DEV_IMAGE || image === `${CPP_DEV_IMAGE}-${hostArch}`)) syncCppIde(root, image);
23
+ }
24
+
25
+ async function buildImage(image, platform, root) {
26
+ const dockerfile = await resolveDockerfile("Dockerfile.cpp");
27
+ const sdkVer = cppSdkVersion(root);
28
+ const key = imageKey(dockerfile, sdkVer ?? "");
29
+ if (canSkipImageBuild(image, key)) {
30
+ return;
31
+ }
32
+ log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
33
+ const platformFlags = platform ? ["--platform", platform] : [];
34
+ const buildArgs = [...(sdkVer ? ["--build-arg", `RB_SDK_VERSION=${sdkVer}`] : []), ...imageKeyLabel(key)];
35
+ const r = spawnSync(
36
+ "docker",
37
+ ["build", ...buildCacheFlags(), ...platformFlags, ...buildArgs, "-t", image, "-f", dockerfile, dirname(dockerfile)],
38
+ { stdio: "inherit" },
39
+ );
40
+ if (r.status !== 0) throw new Error("docker build failed.");
41
+ pruneMuskatImages();
42
+ }
43
+
44
+ // Docker platform token for each target arch
45
+ export const ARCH_DOCKER_PLATFORM = {
46
+ amd64: "linux/amd64",
47
+ arm64: "linux/arm64",
48
+ };
49
+
50
+ /**
51
+ * Configure + build cpp/ inside the container, producing cpp/build-{arch}/<entry>
52
+ * on the host (the workspace is mounted).
53
+ * - arch null → native (dev mode, uses cpp/build/)
54
+ * - arch "x86_64" / "aarch64" → cross via --platform (Release builds for ZIP)
55
+ * buildType "Debug" for dev, "Release" for export builds that ship in the ZIP.
56
+ */
57
+ export function cmakeBuild(root, { image = CPP_DEV_IMAGE, buildType = "Debug", arch = null } = {}) {
58
+ const buildDir = arch ? `cpp/build-${arch}` : "cpp/build";
59
+ const dockerPlatform = arch ? ARCH_DOCKER_PLATFORM[arch] : null;
60
+ const platformFlags = dockerPlatform ? ["--platform", dockerPlatform] : [];
61
+ // arch 빌드 시 arch별 이미지 사용 (로컬 이미지와 platform 불일치 방지)
62
+ const actualImage = arch ? `${CPP_DEV_IMAGE}-${arch}` : image;
63
+ // Export builds ship lib/ next to the binary: librb_sdk.so (robot has no SDK) + any .so the
64
+ // plugin copied there itself. Found via RPATH $ORIGIN/lib.
65
+ const script = [
66
+ `cmake -S cpp -B ${buildDir} -DCMAKE_BUILD_TYPE=${buildType}` + (arch ? ` -DCMAKE_BUILD_RPATH='$ORIGIN/lib'` : ""),
67
+ `cmake --build ${buildDir} -j`,
68
+ ...(arch ? [`rm -f ${buildDir}/lib/librb_sdk.so.* && if readelf -d $(find ${buildDir} -maxdepth 1 -type f -executable) 2>/dev/null | grep -q librb_sdk; then mkdir -p ${buildDir}/lib && cp -a $(dpkg -L rainbow-rb-sdk | grep -E '/librb_sdk[.]so[.][0-9]') ${buildDir}/lib/; fi`] : []),
69
+ ].join(" && ");
70
+ const label = arch ? `cmake build (${buildType}, ${arch}, in container)…` : `cmake build (${buildType}, in container)…`;
71
+ log.step(label);
72
+ const r = spawnSync(
73
+ "docker",
74
+ ["run", "--rm", ...dockerUserFlags(), ...platformFlags, "-v", `${root}:/plugin`, "-w", "/plugin", actualImage, "bash", "-lc", script],
75
+ { cwd: root, stdio: "inherit" },
76
+ );
77
+ if (r.status !== 0) throw new Error(`cmake build failed${arch ? ` (${arch})` : ""}.`);
78
+ }
@@ -0,0 +1,76 @@
1
+ // Editor (IntelliSense) support for C++ backends. The rb_sdk headers only exist inside the dev image,
2
+ // so a host editor flags every SDK name. Copy them to .muscat-dev/sysroot and point
3
+ // .vscode/c_cpp_properties.json at them, using the include dirs / defines rb_sdk's CMake target exports.
4
+ // The build never reads these copies — it runs in the image.
5
+ import { spawnSync } from "node:child_process";
6
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
7
+ import { resolve } from "node:path";
8
+ import { cppSdkFiles, ensureDevDir, log } from "./util.mjs";
9
+
10
+ const SYSROOT = ".muscat-dev/sysroot";
11
+ const CONFIG_NAME = "Muscat";
12
+ const COPY = ["/usr/include/rainbow", "/usr/include/eigen3", "/usr/lib/cmake/rb_sdk/rb_sdkTargets.cmake"];
13
+
14
+ function imageId(image) {
15
+ const r = spawnSync("docker", ["image", "inspect", "-f", "{{.Id}}", image], { encoding: "utf8" });
16
+ return r.status === 0 ? r.stdout.trim() : null;
17
+ }
18
+
19
+ /** Copy the SDK headers out of `image` (once per image build) and write the VS Code C/C++ config. */
20
+ export function syncCppIde(root, image) {
21
+ if (!cppSdkFiles(root).length) return;
22
+ const id = imageId(image);
23
+ if (!id) return;
24
+ const sysroot = resolve(root, SYSROOT);
25
+ const stamp = resolve(sysroot, ".image-id");
26
+ if (!existsSync(stamp) || readFileSync(stamp, "utf8") !== id) {
27
+ rmSync(sysroot, { recursive: true, force: true });
28
+ ensureDevDir(root);
29
+ const create = spawnSync("docker", ["create", image], { encoding: "utf8" });
30
+ if (create.status !== 0) return log.warn("could not copy SDK headers for the editor (docker create failed).");
31
+ const container = create.stdout.trim();
32
+ try {
33
+ for (const src of COPY) {
34
+ const dest = resolve(sysroot, src.slice(1));
35
+ mkdirSync(resolve(dest, ".."), { recursive: true });
36
+ spawnSync("docker", ["cp", `${container}:${src}`, dest], { stdio: "ignore" });
37
+ }
38
+ } finally {
39
+ spawnSync("docker", ["rm", container], { stdio: "ignore" });
40
+ }
41
+ writeFileSync(stamp, id);
42
+ log.step(`SDK headers for the editor → ${SYSROOT}`);
43
+ }
44
+ writeCppProperties(root, readTarget(sysroot));
45
+ }
46
+
47
+ // INTERFACE_INCLUDE_DIRECTORIES / INTERFACE_COMPILE_DEFINITIONS of rb_sdk::rb_sdk, mapped into the sysroot.
48
+ function readTarget(sysroot) {
49
+ const file = resolve(sysroot, "usr/lib/cmake/rb_sdk/rb_sdkTargets.cmake");
50
+ const text = existsSync(file) ? readFileSync(file, "utf8") : "";
51
+ const list = (key) => (text.match(new RegExp(`${key} "([^"]*)"`))?.[1] ?? "").split(";").filter(Boolean);
52
+ const prefix = `\${workspaceFolder}/${SYSROOT}/usr`;
53
+ const includes = list("INTERFACE_INCLUDE_DIRECTORIES").map((d) => d.replace("${_IMPORT_PREFIX}", prefix));
54
+ return {
55
+ includePath: [...(includes.length ? includes : [`${prefix}/include`]), `${prefix}/include/eigen3`],
56
+ defines: list("INTERFACE_COMPILE_DEFINITIONS"),
57
+ };
58
+ }
59
+
60
+ // Keeps other configurations; ours is replaced in place or put first so VS Code picks it by default.
61
+ function writeCppProperties(root, { includePath, defines }) {
62
+ const file = resolve(root, ".vscode", "c_cpp_properties.json");
63
+ let props = { configurations: [], version: 4 };
64
+ if (existsSync(file)) {
65
+ try {
66
+ props = JSON.parse(readFileSync(file, "utf8"));
67
+ } catch {
68
+ return log.warn(".vscode/c_cpp_properties.json is not plain JSON — add the SDK include paths yourself.");
69
+ }
70
+ }
71
+ const config = { name: CONFIG_NAME, includePath: ["${workspaceFolder}/**", ...includePath], defines, cppStandard: "c++20" };
72
+ const configs = (props.configurations ?? []).filter((c) => c.name !== CONFIG_NAME);
73
+ props.configurations = [config, ...configs];
74
+ mkdirSync(resolve(root, ".vscode"), { recursive: true });
75
+ writeFileSync(file, JSON.stringify(props, null, 2) + "\n");
76
+ }
package/cli/src/dev.mjs CHANGED
@@ -1,11 +1,200 @@
1
- // muscat-pk dev — run dev:ui and dev:back together.
1
+ // muscat-pk dev — run the local Muscat runtime (gateway, MediaMTX, common), robot simulators (--sim),
2
+ // dev:ui, and dev:back together.
3
+ import { spawn, spawnSync } from "node:child_process";
4
+ import { existsSync } from "node:fs";
5
+ import { connect as netConnect } from "node:net";
6
+ import { resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
2
8
  import { runDevUi } from "./devUi.mjs";
3
- import { runDevBack } from "./devBack.mjs";
4
- import { log } from "./util.mjs";
9
+ import { prebuildBackend } from "./devBack.mjs";
10
+ import { claimDevSession, stopDevBackContainers } from "./devSession.mjs";
11
+ import { ensureLocalMuscat, GATEWAY_PORT, isPortInUse, MUSCAT_CONTAINER, robotHasMuscat } from "./muscatLocal.mjs";
12
+ import { resolveSim, startSim, stopSims } from "./sim.mjs";
13
+ import { assertManifestPorts, assertManifestVariables, readManifest } from "./manifest.mjs";
14
+ import { devPluginId } from "./devId.mjs";
15
+ import {
16
+ ensureDocker,
17
+ ensureIdeVenv,
18
+ findPluginRoot,
19
+ log,
20
+ parseArgs,
21
+ prompt,
22
+ promptRobotConnection,
23
+ venvPython,
24
+ } from "./util.mjs";
25
+ import { ensureRemoteZenohEnv, isRemoteRobot, isWsl } from "./zenohDev.mjs";
26
+
27
+ const HOST_AGENT_PATH = fileURLToPath(new URL("./host_agent.py", import.meta.url));
28
+ const canAutoStartHostAgent = () => process.platform === "darwin" || process.platform === "win32" || isWsl();
29
+
30
+ function waitForHostAgent(port, { timeoutMs = 30_000 } = {}) {
31
+ const deadline = Date.now() + timeoutMs;
32
+ return new Promise((resolve, reject) => {
33
+ const probe = () => {
34
+ const sock = netConnect(port, "127.0.0.1");
35
+ sock.once("connect", () => { sock.destroy(); resolve(true); });
36
+ sock.once("error", () => {
37
+ sock.destroy();
38
+ if (Date.now() >= deadline) {
39
+ reject(new Error(`host SDK agent did not open port ${port} in time`));
40
+ return;
41
+ }
42
+ setTimeout(probe, 300);
43
+ });
44
+ };
45
+ probe();
46
+ });
47
+ }
48
+
49
+ const DEFAULT_PROXY_PORT = 47100;
50
+
51
+ // Start the host-side Zenoh byte-proxy (host_agent.py) for a remote robot —
52
+ // see host_agent.py for why a Docker Desktop container can't join the
53
+ // robot's Zenoh mesh itself. Sets MUSCAT_PROXY_ENDPOINT for dev:back.
54
+ // Returns the child process, or null if not applicable / disabled.
55
+ async function maybeStartHostAgent(root, { robotIp, robotToken }) {
56
+ if (process.env.MUSCAT_PROXY_ENDPOINT) {
57
+ log.step(`Zenoh via host byte-proxy → ${process.env.MUSCAT_PROXY_ENDPOINT}`);
58
+ return null;
59
+ }
60
+ if (process.env.MUSCAT_HOST_AGENT_DISABLE === "1") return null;
61
+ if (!canAutoStartHostAgent() || !isRemoteRobot(robotIp)) return null;
62
+
63
+ const agentPath = process.env.MUSCAT_HOST_AGENT_SCRIPT || HOST_AGENT_PATH;
64
+ if (!existsSync(agentPath)) return null;
65
+
66
+ if (!ensureIdeVenv(root)) {
67
+ log.warn("host Zenoh byte-proxy skipped: could not prepare .venv");
68
+ return null;
69
+ }
70
+
71
+ // advertiseHost/zenohPort = the host_agent's OWN robot-facing Zenoh session
72
+ // (host LAN IP + port the robot reverse-dials into). The container never
73
+ // sees or needs these — it only talks to the proxyPort below.
74
+ const zenoh = await ensureRemoteZenohEnv(robotIp, { log });
75
+ const advertiseHost = zenoh?.advertiseHost || process.env.MUSCAT_ZENOH_ADVERTISE_HOST;
76
+ const zenohPort = zenoh?.zenohPort || Number(process.env.MUSCAT_ZENOH_PORT);
77
+ if (!advertiseHost || !zenohPort) {
78
+ log.warn("host Zenoh byte-proxy skipped: could not determine host LAN IP / port");
79
+ return null;
80
+ }
81
+ const proxyPort = Number(process.env.MUSCAT_PROXY_PORT) || DEFAULT_PROXY_PORT;
82
+
83
+ log.step("starting host Zenoh byte-proxy (Docker Desktop query-routing workaround)...");
84
+ const py = venvPython(resolve(root, ".venv"));
85
+ const child = spawn(py, [agentPath], {
86
+ cwd: root,
87
+ stdio: ["ignore", "inherit", "inherit"],
88
+ env: {
89
+ ...process.env,
90
+ MUSCAT_ROBOT_HOST: robotIp,
91
+ MUSCAT_ROBOT_TOKEN: robotToken,
92
+ MUSCAT_ZENOH_ADVERTISE_HOST: advertiseHost,
93
+ MUSCAT_ZENOH_PORT: String(zenohPort),
94
+ MUSCAT_PROXY_PORT: String(proxyPort),
95
+ },
96
+ });
97
+
98
+ try {
99
+ await waitForHostAgent(proxyPort);
100
+ } catch (e) {
101
+ child.kill();
102
+ throw e;
103
+ }
104
+
105
+ // advertiseHost (not host.docker.internal): a Docker Desktop container run
106
+ // under `dev` shares the local Muscat container's netns (--network container:),
107
+ // which has no host-gateway alias configured — the host's real LAN IP
108
+ // always resolves regardless of netns mode.
109
+ const proxyEndpoint = `${advertiseHost}:${proxyPort}`;
110
+ process.env.MUSCAT_PROXY_ENDPOINT = proxyEndpoint;
111
+ process.env.MUSCAT_PROXY_PORT = String(proxyPort);
112
+ log.step(`Zenoh via host byte-proxy → ${proxyEndpoint}`);
113
+ return child;
114
+ }
5
115
 
6
116
  export async function runDev(args = []) {
7
- log.step("starting dev:ui + dev:back");
8
- // dev:back runs to completion / detaches on its own; dev:ui owns the process.
9
- runDevBack().catch((e) => log.warn(`dev:back: ${e.message}`));
117
+ const root = findPluginRoot();
118
+ await claimDevSession(root, "dev");
119
+ assertManifestVariables(root);
120
+ const portRanges = assertManifestPorts(root);
121
+ ensureDocker();
122
+ const { robotIp, robotToken } = await promptRobotConnection(args);
123
+ process.env.MUSCAT_ROBOT_HOST = robotIp;
124
+ // Even when empty: the dev:back child (stdin ignored) must not prompt for the token again.
125
+ process.env.MUSCAT_ROBOT_TOKEN = robotToken;
126
+
127
+ const remoteRobot = isRemoteRobot(robotIp);
128
+ // Before dev:back is spawned: it inherits the id through the environment.
129
+ devPluginId(readManifest(root).id, { robotIp, args });
130
+
131
+ // Registered before anything starts: stopped while the local Muscat or a sim is still coming up,
132
+ // they would otherwise keep running. muscat-pk-muscat and the sims are ours — a muscat-dev the
133
+ // user started is another container and stays up.
134
+ let hostAgent = null;
135
+ let backend = null;
136
+ let cleaned = false;
137
+ const cleanup = () => {
138
+ if (cleaned) return;
139
+ cleaned = true;
140
+ if (hostAgent && hostAgent.exitCode === null) hostAgent.kill();
141
+ if (backend && backend.exitCode === null) backend.kill();
142
+ stopDevBackContainers();
143
+ stopSims();
144
+ spawnSync("docker", ["rm", "-f", MUSCAT_CONTAINER], { stdio: "ignore" });
145
+ };
146
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) // SIGHUP: terminal closed (VS Code re-running Dev disposes it)
147
+ process.once(signal, () => {
148
+ cleanup();
149
+ // Once dev:ui is up its own handler stops the UI and exits.
150
+ if (!process.listenerCount(signal)) process.exit(130);
151
+ });
152
+ process.once("exit", cleanup);
153
+
154
+ await prebuildBackend(root);
155
+ hostAgent = await maybeStartHostAgent(root, { robotIp, robotToken });
156
+ const proxyEndpoint = process.env.MUSCAT_PROXY_ENDPOINT || "";
157
+ // No proxy (Linux, disabled, ...): fall back to the container reverse-dialing
158
+ // the robot itself — the local Muscat then publishes the zenoh port for it.
159
+ if (!proxyEndpoint) await ensureRemoteZenohEnv(robotIp, { log });
160
+
161
+ // No robot: a simulator stands in for it (--sim, else the manifest's target).
162
+ const sim = remoteRobot ? null : resolveSim(parseArgs(args).sim, readManifest(root));
163
+
164
+ // A robot running Muscat is the gateway (:10000) and common for both the UI and the backend.
165
+ // Otherwise the local Muscat runtime is; the backend joins its netns, so the plugin's declared
166
+ // ports are published from it too.
167
+ const muscat = (await robotHasMuscat(robotIp)) ? null : await ensureLocalMuscat({
168
+ pluginPorts: portRanges,
169
+ zenohPort: remoteRobot && !proxyEndpoint ? Number(process.env.MUSCAT_ZENOH_PORT) || null : null,
170
+ services: sim?.services,
171
+ env: sim?.env,
172
+ }).catch((e) => { log.warn(`local Muscat: ${e.message}`); return null; });
173
+ if (sim && muscat !== MUSCAT_CONTAINER) {
174
+ log.warn(`--sim needs muscat-pk's own Muscat${muscat ? ` ('${muscat}' is running — stop it to use the simulator)` : ""}; no simulator.`);
175
+ } else if (sim) {
176
+ // dev:ui then serves the real Muscat UI against the local Muscat, like with muscat-dev.
177
+ if (await startSim(sim).catch((e) => log.warn(`simulator: ${e.message}`))) process.env.MUSCAT_PK_SIM = sim.name;
178
+ }
179
+ if (muscat) {
180
+ process.env.MUSCAT_PK_MUSCAT_CONTAINER = muscat;
181
+ } else if (remoteRobot || (await isPortInUse(GATEWAY_PORT))) {
182
+ // e.g. developing on the robot PC: its Muscat already serves the gateway; the backend reaches it via the host.
183
+ } else {
184
+ log.warn(`no local Muscat on :${GATEWAY_PORT} — the plugin UI cannot reach the backend.`);
185
+ const answer = await prompt(" Proceed anyway? [y/N]: ");
186
+ if (answer.toLowerCase() !== "y") return;
187
+ }
188
+
189
+ // dev:back in its own process: its docker build / pip install / cmake build are synchronous and would
190
+ // freeze dev:ui's server on this event loop until they finish (or Ctrl+C killed them).
191
+ // A Bun binary re-runs itself with just the command; under Node argv[1] is the script.
192
+ backend = spawn(process.execPath, [...(typeof Bun === "undefined" ? [process.argv[1]] : []), "dev:back"], {
193
+ stdio: ["ignore", "inherit", "inherit"],
194
+ });
195
+ backend.on("exit", (code) => code && log.warn(`dev:back exited (${code})`));
196
+
197
+ process.env.MUSCAT_PK_MANAGED_DEV = "1";
198
+
10
199
  await runDevUi(args);
11
200
  }