@rainbow-robotics/muscat-plugin-kit 1.10.1-dev.1 → 1.10.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 (205) hide show
  1. package/README.md +282 -282
  2. package/cli/README.md +274 -274
  3. package/cli/bin/create-muscat-plugin.mjs +8 -8
  4. package/cli/bin/muscat-pk.mjs +155 -155
  5. package/cli/bundle-templates.mjs +31 -31
  6. package/cli/docker/Dockerfile.cpp +28 -28
  7. package/cli/docker/Dockerfile.dev +30 -30
  8. package/cli/docker/Dockerfile.python +21 -21
  9. package/cli/m5-e2e.mjs +69 -69
  10. package/cli/prepack.mjs +10 -10
  11. package/cli/src/ai-rules.mjs +70 -70
  12. package/cli/src/build.mjs +447 -447
  13. package/cli/src/check-package.mjs +13 -13
  14. package/cli/src/completion.mjs +166 -166
  15. package/cli/src/cpp.mjs +78 -78
  16. package/cli/src/cppIde.mjs +75 -75
  17. package/cli/src/dev.mjs +165 -165
  18. package/cli/src/devBack.mjs +734 -727
  19. package/cli/src/devUi.mjs +247 -243
  20. package/cli/src/esbuild.mjs +56 -56
  21. package/cli/src/host_agent.py +223 -223
  22. package/cli/src/manifest.mjs +287 -287
  23. package/cli/src/mcp.mjs +1071 -1071
  24. package/cli/src/muscatLocal.mjs +209 -209
  25. package/cli/src/new.mjs +343 -343
  26. package/cli/src/pluginDevServer.mjs +111 -111
  27. package/cli/src/ports.mjs +193 -193
  28. package/cli/src/python.mjs +112 -112
  29. package/cli/src/ros2.mjs +109 -109
  30. package/cli/src/sdk.mjs +117 -117
  31. package/cli/src/storybook.mjs +168 -168
  32. package/cli/src/type.mjs +141 -141
  33. package/cli/src/uninstall.mjs +81 -81
  34. package/cli/src/update.mjs +295 -295
  35. package/cli/src/util.mjs +441 -441
  36. package/cli/src/zenohDev.mjs +83 -81
  37. package/cli/storybook-dist/_design-tokens.css +4957 -4957
  38. package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -27
  39. package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -28
  40. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -46
  41. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -73
  42. package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -93
  43. package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -74
  44. package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -28
  45. package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -84
  46. package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -57
  47. package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -41
  48. package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -60
  49. package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -44
  50. package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +56 -56
  51. package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -51
  52. package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -55
  53. package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -52
  54. package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -93
  55. package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -49
  56. package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -108
  57. package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -106
  58. package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -89
  59. package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -64
  60. package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -65
  61. package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -68
  62. package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +216 -216
  63. package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -90
  64. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -45
  65. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -50
  66. package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -102
  67. package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -31
  68. package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -36
  69. package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -49
  70. package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -89
  71. package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -116
  72. package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -74
  73. package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -99
  74. package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -123
  75. package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -135
  76. package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -69
  77. package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -49
  78. package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -91
  79. package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -38
  80. package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -112
  81. package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -75
  82. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -51
  83. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -84
  84. package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -102
  85. package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -78
  86. package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -57
  87. package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -177
  88. package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -64
  89. package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -117
  90. package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -50
  91. package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -30
  92. package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -54
  93. package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -57
  94. package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -48
  95. package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -103
  96. package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -27
  97. package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -67
  98. package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -45
  99. package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -27
  100. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -48
  101. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -46
  102. package/cli/storybook-dist/assets/{Color-YHDXOIA2-D6JqZzDJ.js → Color-YHDXOIA2-B4IER17l.js} +1 -1
  103. package/cli/storybook-dist/assets/{Docs-ESb7JAHg.js → Docs-C8GCHFtK.js} +3 -3
  104. package/cli/storybook-dist/assets/{Docs-6gD2kUo1.js → Docs-CgdEMPQP.js} +2 -2
  105. package/cli/storybook-dist/assets/{Docs-B9wd8Ok7.js → Docs-Ck3WHvk6.js} +3 -3
  106. package/cli/storybook-dist/assets/{Docs-CgFbWWXM.js → Docs-D9NHsoeZ.js} +2 -2
  107. package/cli/storybook-dist/assets/{Docs-DBQup8Bu.js → Docs-DNYQC7nl.js} +3 -3
  108. package/cli/storybook-dist/assets/{Docs-CMjovnSY.js → Docs-DUfeiM4I.js} +4 -4
  109. package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-BOUI1AHi.js → DocsRenderer-CFRXHY34-C0WhyDoR.js} +1 -1
  110. package/cli/storybook-dist/assets/{NxAvatar.stories-DsYFMUh3.js → NxAvatar.stories-CwnK8AUF.js} +1 -1
  111. package/cli/storybook-dist/assets/{NxButton.stories-CHfNcqsc.js → NxButton.stories-ueYdosfQ.js} +8 -8
  112. package/cli/storybook-dist/assets/{NxNumberInput.stories-BFw_4vFE.js → NxNumberInput.stories-M5ZCLbrK.js} +1 -1
  113. package/cli/storybook-dist/assets/{NxPagination.stories-DVfxyw4M.js → NxPagination.stories-bxxUnHEL.js} +1 -1
  114. package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-BzyxUWoZ.js → NxPointerHoldButton.stories-BHxB2kr5.js} +1 -1
  115. package/cli/storybook-dist/assets/{NxRangeInput.stories-DmRdzBc1.js → NxRangeInput.stories-BKfr-fp4.js} +6 -6
  116. package/cli/storybook-dist/assets/{NxSelectBox.stories-B1KkNUOf.js → NxSelectBox.stories-CG8FuTBq.js} +1 -1
  117. package/cli/storybook-dist/assets/{NxSlideRange.stories-CyOdvfQ6.js → NxSlideRange.stories-BRTFtMTL.js} +1 -1
  118. package/cli/storybook-dist/assets/{NxSwitch.stories-DrDBw0qp.js → NxSwitch.stories-Bwuyzn-R.js} +1 -1
  119. package/cli/storybook-dist/assets/{NxTextArea.stories-DTB2aqNS.js → NxTextArea.stories-COq2sGqu.js} +1 -1
  120. package/cli/storybook-dist/assets/{NxTextInput.stories-DU0rucmM.js → NxTextInput.stories-CjsBLz05.js} +1 -1
  121. package/cli/storybook-dist/assets/{RBAccordion.stories-DTfOtXtp.js → RBAccordion.stories-BARwuZ0Q.js} +3 -3
  122. package/cli/storybook-dist/assets/{RBAccordionGroup.stories-9y9BjrUe.js → RBAccordionGroup.stories-COrPxETW.js} +10 -10
  123. package/cli/storybook-dist/assets/{RBAlert.stories-DLTAv9fT.js → RBAlert.stories-BQrkjZPt.js} +3 -3
  124. package/cli/storybook-dist/assets/{RBBadge.stories-BNMACewA.js → RBBadge.stories-BTsLL-lK.js} +1 -1
  125. package/cli/storybook-dist/assets/{RBBattery.stories-B3kXMAt5.js → RBBattery.stories-DAeQdJbc.js} +1 -1
  126. package/cli/storybook-dist/assets/{RBCard.stories-Y-s8R2n7.js → RBCard.stories-HyhOE80R.js} +39 -39
  127. package/cli/storybook-dist/assets/{RBCheckBox.stories-DBuT9tL4.js → RBCheckBox.stories-KFk0WqX_.js} +1 -1
  128. package/cli/storybook-dist/assets/{RBCheckIconButton.stories-CWu1TL56.js → RBCheckIconButton.stories-CXbbktF5.js} +1 -1
  129. package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-sCu2MGCX.js → RBCheckLabelButton.stories-DWfYz_9Z.js} +17 -17
  130. package/cli/storybook-dist/assets/{RBChipTab.stories-BX9qLJkR.js → RBChipTab.stories-JPEgVFAS.js} +5 -5
  131. package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-35u1aJ-P.js → RBChipsBoxTab.stories-CTPuWXrQ.js} +1 -1
  132. package/cli/storybook-dist/assets/{RBColorIcons.stories-DAOZ5Ies.js → RBColorIcons.stories-DiFRZ5jk.js} +1 -1
  133. package/cli/storybook-dist/assets/RBCommand.stories-Pf1HAVIR.js +73 -0
  134. package/cli/storybook-dist/assets/{RBConfirm.stories-iMXcUh7Z.js → RBConfirm.stories-D1cEPUXk.js} +3 -3
  135. package/cli/storybook-dist/assets/{RBDateInput.stories-hK5rXECf.js → RBDateInput.stories-RPYpCjig.js} +1 -1
  136. package/cli/storybook-dist/assets/RBDialog.stories-DJH-0XSR.js +38 -0
  137. package/cli/storybook-dist/assets/{RBDivider.stories-BAK-iZ1Z.js → RBDivider.stories-uEj1vOgK.js} +1 -1
  138. package/cli/storybook-dist/assets/{RBDrawer.stories-B2nqf2Nh.js → RBDrawer.stories-BiMzElb2.js} +12 -12
  139. package/cli/storybook-dist/assets/RBDropdownMenu.stories-Da_TBkla.js +86 -0
  140. package/cli/storybook-dist/assets/{RBEmptyView.stories-nCenrgBh.js → RBEmptyView.stories-BHeKGfPS.js} +1 -1
  141. package/cli/storybook-dist/assets/{RBIconButton.stories-DRH7O3sp.js → RBIconButton.stories-NEsRFoYp.js} +1 -1
  142. package/cli/storybook-dist/assets/{RBIcons.stories-D3PyJXG8.js → RBIcons.stories-CcZ-TTLS.js} +1 -1
  143. package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-modQbt10.js → RBInlineLabelButton.stories-B0wpdQyR.js} +1 -1
  144. package/cli/storybook-dist/assets/{RBJoyStick.stories-BY14qVgR.js → RBJoyStick.stories-DEgBou3K.js} +1 -1
  145. package/cli/storybook-dist/assets/{RBLabelButton.stories-DzF7vGwm.js → RBLabelButton.stories-DT8drIsF.js} +1 -1
  146. package/cli/storybook-dist/assets/{RBLight.stories-lF4FRmVh.js → RBLight.stories-Dh-aoeyG.js} +1 -1
  147. package/cli/storybook-dist/assets/{RBMenubar.stories-XhrI7bsV.js → RBMenubar.stories-D-H7UqVh.js} +1 -1
  148. package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-DprllQDl.js → RBMultiRangeInput.stories-hlclv6jI.js} +1 -1
  149. package/cli/storybook-dist/assets/{RBPagination.stories-2v2gBJfC.js → RBPagination.stories-DWCeoZ_S.js} +1 -1
  150. package/cli/storybook-dist/assets/{RBPopover.stories-C8iLIZpv.js → RBPopover.stories-C514M09_.js} +1 -1
  151. package/cli/storybook-dist/assets/{RBPrompt.stories-Th_HrWHI.js → RBPrompt.stories-BSItvfiT.js} +1 -1
  152. package/cli/storybook-dist/assets/{RBRadioBox.stories-CoHuLaN3.js → RBRadioBox.stories-BBb4YbFU.js} +1 -1
  153. package/cli/storybook-dist/assets/{RBRadioButton.stories-DULxE_ff.js → RBRadioButton.stories-CXyOGJCv.js} +1 -1
  154. package/cli/storybook-dist/assets/{RBRangeDateInput.stories-D8A9KBSS.js → RBRangeDateInput.stories-qvhubu0e.js} +1 -1
  155. package/cli/storybook-dist/assets/{RBResizable.stories-C41QfrQ3.js → RBResizable.stories-CZKeyGjH.js} +14 -14
  156. package/cli/storybook-dist/assets/{RBSelectBox.stories-C1gNmwzw.js → RBSelectBox.stories-CRKP3ybf.js} +1 -1
  157. package/cli/storybook-dist/assets/RBSheet.stories-sCM3ZJUa.js +39 -0
  158. package/cli/storybook-dist/assets/{RBSideNumberInput.stories-9LlgQ8nh.js → RBSideNumberInput.stories-Bb7gw8RF.js} +3 -3
  159. package/cli/storybook-dist/assets/{RBSlider.stories-oUgo5K9d.js → RBSlider.stories-23OAQU5z.js} +3 -3
  160. package/cli/storybook-dist/assets/{RBSpinner.stories-DDpohbY2.js → RBSpinner.stories-2xcQdcmt.js} +1 -1
  161. package/cli/storybook-dist/assets/{RBSwitch.stories-gW-GN7QO.js → RBSwitch.stories-BACUaJm_.js} +7 -7
  162. package/cli/storybook-dist/assets/{RBTab.stories-CN-PV3HH.js → RBTab.stories-CDHE3sVs.js} +1 -1
  163. package/cli/storybook-dist/assets/{RBTable.stories-BAZLpxtM.js → RBTable.stories-C0Cfhfl7.js} +29 -29
  164. package/cli/storybook-dist/assets/{RBTableRow.stories-CwjVrszB.js → RBTableRow.stories-BF8kIkpp.js} +1 -1
  165. package/cli/storybook-dist/assets/{RBTabs.stories-JuEo2sUL.js → RBTabs.stories-BdJWRq1o.js} +7 -7
  166. package/cli/storybook-dist/assets/{RBTextArea.stories-D0_Pv4qZ.js → RBTextArea.stories-CGy2xeYH.js} +3 -3
  167. package/cli/storybook-dist/assets/{RBTextField.stories-CCsOvZqV.js → RBTextField.stories-IfUOzxCM.js} +15 -15
  168. package/cli/storybook-dist/assets/{RBTextInput.stories-Cccn3VNm.js → RBTextInput.stories-VjXNFo4R.js} +3 -3
  169. package/cli/storybook-dist/assets/{RBToast.stories-I5617t0K.js → RBToast.stories-BzcobblV.js} +1 -1
  170. package/cli/storybook-dist/assets/{RBToggle.stories-DsgKjGsJ.js → RBToggle.stories-DuLE5mKv.js} +4 -4
  171. package/cli/storybook-dist/assets/{RBToggleGroup.stories-BZEzSDuz.js → RBToggleGroup.stories-CuR0N0GJ.js} +5 -5
  172. package/cli/storybook-dist/assets/{RBTooltip.stories-4oSyAWsA.js → RBTooltip.stories--aAgNC8_.js} +1 -1
  173. package/cli/storybook-dist/assets/{SideNumberInput-DSKbd0bG.js → SideNumberInput-DyWQxYUj.js} +743 -743
  174. package/cli/storybook-dist/assets/{entry-preview-B35XREGs.js → entry-preview-D7IHsza_.js} +1 -1
  175. package/cli/storybook-dist/assets/{iframe-BB797lmL.js → iframe-CbaIp5I-.js} +2 -2
  176. package/cli/storybook-dist/assets/{index-BU2567EP.js → index-C6CBgMuG.js} +3 -3
  177. package/cli/storybook-dist/assets/{preview-C7DaSWyc.js → preview-B-iYLDuW.js} +2 -2
  178. package/cli/storybook-dist/assets/{preview-CbuRHit4.js → preview-CRWqc9DH.js} +1 -1
  179. package/cli/storybook-dist/assets/{react-resizable-panels.browser-ChIvlwo7.js → react-resizable-panels.browser-8hC8dr-6.js} +8 -8
  180. package/cli/storybook-dist/iframe.html +2 -2
  181. package/cli/storybook-dist/index.html +1 -1
  182. package/cli/storybook-dist/project.json +1 -1
  183. package/cli/templates/common/.mcp.json +8 -8
  184. package/cli/templates/common/README.md +1231 -1231
  185. package/cli/templates/common/assets/icon.svg +45 -45
  186. package/cli/templates/common/manifest.json +26 -26
  187. package/cli/templates/common/package.json +22 -22
  188. package/cli/templates/common/py/main.py +125 -125
  189. package/cli/templates/common/tsconfig.json +13 -13
  190. package/cli/templates/common/ui/content.tsx +216 -216
  191. package/cli/templates/common/ui/style.css +6 -6
  192. package/cli/templates/common/vite.config.ts +10 -10
  193. package/cli/templates/cpp/cpp/main.cpp +73 -73
  194. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +25 -25
  195. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/package.xml +10 -10
  196. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/setup.py +18 -18
  197. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -11
  198. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +23 -23
  199. package/cli/vite.d.mts +25 -25
  200. package/cli/vite.mjs +292 -292
  201. package/package.json +3 -3
  202. package/cli/storybook-dist/assets/RBCommand.stories-BSqXDkKw.js +0 -73
  203. package/cli/storybook-dist/assets/RBDialog.stories-BAktz-ku.js +0 -38
  204. package/cli/storybook-dist/assets/RBDropdownMenu.stories-DbW2fWAj.js +0 -86
  205. package/cli/storybook-dist/assets/RBSheet.stories-_6VH-RUm.js +0 -39
package/cli/src/sdk.mjs CHANGED
@@ -1,117 +1,117 @@
1
- // muscat-pk sdk <list|update [version]> — manage the rainbow-rb-sdk version.
2
- //
3
- // C++ backends (cpp, ros2 rclcpp) pin it in CMakeLists.txt (find_package(rb_sdk <ver> EXACT)); the
4
- // versions come from the rb-sdk apt repository and the dev image installs the pinned one.
5
- //
6
- // Python backends:
7
- // Pins requirements.txt — the real install happens in the `dev`/`dev:back`
8
- // Docker container. If a local .venv exists (see devBack.mjs's syncIdeVenv,
9
- // for editor/linter support), also update it in place so pylint/pyright see
10
- // the new version immediately; this is best-effort only.
11
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
12
- import { resolve } from "node:path";
13
- import { cppSdkFiles, cppSdkPin, ensureIdeVenv, findPluginRoot, log, setCppSdkPin, venvPython } from "./util.mjs";
14
- import { readManifest } from "./manifest.mjs";
15
-
16
- const PKG = "rainbow-rb-sdk";
17
-
18
- async function fetchPypi() {
19
- try {
20
- const res = await fetch(`https://pypi.org/pypi/${PKG}/json`);
21
- return res.ok ? await res.json() : null;
22
- } catch {
23
- return null;
24
- }
25
- }
26
-
27
- async function fetchVersions() {
28
- const data = await fetchPypi();
29
- return Object.keys(data?.releases || {}).sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
30
- }
31
-
32
- // Stable releases of rainbow-rb-sdk-dev, newest first. Pre-releases (0.1.4~dev1) are skipped: CMake
33
- // versions are numeric only.
34
- const APT_PACKAGES = "https://rainbowrobotics.github.io/rb-sdk/dists/stable/main/binary-amd64/Packages";
35
-
36
- export async function fetchAptVersions() {
37
- try {
38
- const res = await fetch(APT_PACKAGES);
39
- if (!res.ok) return [];
40
- const text = await res.text();
41
- const versions = [...text.matchAll(/^Package: rainbow-rb-sdk-dev\n(?:.*\n)*?Version: (\S+)/gm)].map((m) => m[1]);
42
- return [...new Set(versions)].filter((v) => /^[0-9]+(\.[0-9]+)*$/.test(v)).sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
43
- } catch {
44
- return [];
45
- }
46
- }
47
-
48
- async function runCppSdk(sub, version, root) {
49
- if (sub === "list") {
50
- const versions = await fetchAptVersions();
51
- if (!versions.length) return log.warn("could not fetch versions (offline?).");
52
- log.info(`rainbow-rb-sdk-dev versions:\n ${versions.slice(0, 12).join("\n ")}`);
53
- return;
54
- }
55
- if (sub === "update") {
56
- if (!cppSdkFiles(root).length) throw new Error("no find_package(rb_sdk ...) in cpp/CMakeLists.txt or ros2_ws/src/*/CMakeLists.txt");
57
- if (!version) {
58
- version = (await fetchAptVersions())[0];
59
- if (!version) throw new Error("could not fetch the latest version (offline?). Pass one: muscat-pk sdk update <version>");
60
- }
61
- if (!/^[0-9]+(\.[0-9]+)*$/.test(version)) throw new Error(`"${version}" is not a CMake version (digits and dots only)`);
62
- const before = cppSdkPin(root);
63
- const files = setCppSdkPin(root, version);
64
- log.ok(`rb_sdk ${before ?? "(unpinned)"} → ${version} in ${files.map((f) => f.slice(root.length + 1)).join(", ")}`);
65
- log.info("The dev image is rebuilt with this SDK on the next `muscat-pk build` / `dev:back`.");
66
- return;
67
- }
68
- log.info("Usage: muscat-pk sdk <list|update [version]>");
69
- }
70
-
71
- export async function runSdk(args = []) {
72
- const sub = args[0];
73
- const root = findPluginRoot();
74
- const runtime = readManifest(root)?.runtime ?? {};
75
- if (runtime.type === "cpp" || (runtime.type === "ros2" && runtime.rosClient === "rclcpp")) return runCppSdk(sub, args[1], root);
76
- const reqs = resolve(root, "requirements.txt");
77
-
78
- if (sub === "list") {
79
- const versions = await fetchVersions();
80
- if (!versions.length) {
81
- log.warn("could not fetch versions (offline?).");
82
- return;
83
- }
84
- log.info(`${PKG} versions:\n ${versions.slice(0, 12).join("\n ")}`);
85
- return;
86
- }
87
-
88
- if (sub === "update") {
89
- let version = args[1];
90
- if (!version) {
91
- // info.version = latest stable
92
- version = (await fetchPypi())?.info?.version;
93
- if (!version) throw new Error("could not fetch the latest version (offline?). Pass one: muscat-pk sdk update <version>");
94
- }
95
- // rainbow-rb-* pin each other exactly — bump them together.
96
- let content = existsSync(reqs) ? readFileSync(reqs, "utf8") : "";
97
- const pinned = /^(rainbow-rb-[\w-]+)\s*==.*$/gim;
98
- const bumped = [...content.matchAll(pinned)].map((m) => m[1]);
99
- content = content.replace(pinned, (_m, name) => `${name}==${version}`);
100
- if (!bumped.some((n) => n.toLowerCase() === PKG)) {
101
- content = (content ? content.trimEnd() + "\n" : "") + `${PKG}==${version}\n`;
102
- bumped.push(PKG);
103
- }
104
- writeFileSync(reqs, content);
105
- log.ok(`requirements.txt → ${bumped.map((n) => `${n}==${version}`).join(", ")}`);
106
-
107
- if (existsSync(venvPython(resolve(root, ".venv")))) {
108
- log.step("refreshing .venv (for your editor)…");
109
- if (!ensureIdeVenv(root)) log.warn(".venv refresh failed — the Docker dev container is unaffected.");
110
- } else {
111
- log.info("Installed automatically on the next `muscat-pk dev` / `dev:back` (Docker container).");
112
- }
113
- return;
114
- }
115
-
116
- log.info("Usage: muscat-pk sdk <list|update [version]>");
117
- }
1
+ // muscat-pk sdk <list|update [version]> — manage the rainbow-rb-sdk version.
2
+ //
3
+ // C++ backends (cpp, ros2 rclcpp) pin it in CMakeLists.txt (find_package(rb_sdk <ver> EXACT)); the
4
+ // versions come from the rb-sdk apt repository and the dev image installs the pinned one.
5
+ //
6
+ // Python backends:
7
+ // Pins requirements.txt — the real install happens in the `dev`/`dev:back`
8
+ // Docker container. If a local .venv exists (see devBack.mjs's syncIdeVenv,
9
+ // for editor/linter support), also update it in place so pylint/pyright see
10
+ // the new version immediately; this is best-effort only.
11
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
12
+ import { resolve } from "node:path";
13
+ import { cppSdkFiles, cppSdkPin, ensureIdeVenv, findPluginRoot, log, setCppSdkPin, venvPython } from "./util.mjs";
14
+ import { readManifest } from "./manifest.mjs";
15
+
16
+ const PKG = "rainbow-rb-sdk";
17
+
18
+ async function fetchPypi() {
19
+ try {
20
+ const res = await fetch(`https://pypi.org/pypi/${PKG}/json`);
21
+ return res.ok ? await res.json() : null;
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ async function fetchVersions() {
28
+ const data = await fetchPypi();
29
+ return Object.keys(data?.releases || {}).sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
30
+ }
31
+
32
+ // Stable releases of rainbow-rb-sdk-dev, newest first. Pre-releases (0.1.4~dev1) are skipped: CMake
33
+ // versions are numeric only.
34
+ const APT_PACKAGES = "https://rainbowrobotics.github.io/rb-sdk/dists/stable/main/binary-amd64/Packages";
35
+
36
+ export async function fetchAptVersions() {
37
+ try {
38
+ const res = await fetch(APT_PACKAGES);
39
+ if (!res.ok) return [];
40
+ const text = await res.text();
41
+ const versions = [...text.matchAll(/^Package: rainbow-rb-sdk-dev\n(?:.*\n)*?Version: (\S+)/gm)].map((m) => m[1]);
42
+ return [...new Set(versions)].filter((v) => /^[0-9]+(\.[0-9]+)*$/.test(v)).sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
43
+ } catch {
44
+ return [];
45
+ }
46
+ }
47
+
48
+ async function runCppSdk(sub, version, root) {
49
+ if (sub === "list") {
50
+ const versions = await fetchAptVersions();
51
+ if (!versions.length) return log.warn("could not fetch versions (offline?).");
52
+ log.info(`rainbow-rb-sdk-dev versions:\n ${versions.slice(0, 12).join("\n ")}`);
53
+ return;
54
+ }
55
+ if (sub === "update") {
56
+ if (!cppSdkFiles(root).length) throw new Error("no find_package(rb_sdk ...) in cpp/CMakeLists.txt or ros2_ws/src/*/CMakeLists.txt");
57
+ if (!version) {
58
+ version = (await fetchAptVersions())[0];
59
+ if (!version) throw new Error("could not fetch the latest version (offline?). Pass one: muscat-pk sdk update <version>");
60
+ }
61
+ if (!/^[0-9]+(\.[0-9]+)*$/.test(version)) throw new Error(`"${version}" is not a CMake version (digits and dots only)`);
62
+ const before = cppSdkPin(root);
63
+ const files = setCppSdkPin(root, version);
64
+ log.ok(`rb_sdk ${before ?? "(unpinned)"} → ${version} in ${files.map((f) => f.slice(root.length + 1)).join(", ")}`);
65
+ log.info("The dev image is rebuilt with this SDK on the next `muscat-pk build` / `dev:back`.");
66
+ return;
67
+ }
68
+ log.info("Usage: muscat-pk sdk <list|update [version]>");
69
+ }
70
+
71
+ export async function runSdk(args = []) {
72
+ const sub = args[0];
73
+ const root = findPluginRoot();
74
+ const runtime = readManifest(root)?.runtime ?? {};
75
+ if (runtime.type === "cpp" || (runtime.type === "ros2" && runtime.rosClient === "rclcpp")) return runCppSdk(sub, args[1], root);
76
+ const reqs = resolve(root, "requirements.txt");
77
+
78
+ if (sub === "list") {
79
+ const versions = await fetchVersions();
80
+ if (!versions.length) {
81
+ log.warn("could not fetch versions (offline?).");
82
+ return;
83
+ }
84
+ log.info(`${PKG} versions:\n ${versions.slice(0, 12).join("\n ")}`);
85
+ return;
86
+ }
87
+
88
+ if (sub === "update") {
89
+ let version = args[1];
90
+ if (!version) {
91
+ // info.version = latest stable
92
+ version = (await fetchPypi())?.info?.version;
93
+ if (!version) throw new Error("could not fetch the latest version (offline?). Pass one: muscat-pk sdk update <version>");
94
+ }
95
+ // rainbow-rb-* pin each other exactly — bump them together.
96
+ let content = existsSync(reqs) ? readFileSync(reqs, "utf8") : "";
97
+ const pinned = /^(rainbow-rb-[\w-]+)\s*==.*$/gim;
98
+ const bumped = [...content.matchAll(pinned)].map((m) => m[1]);
99
+ content = content.replace(pinned, (_m, name) => `${name}==${version}`);
100
+ if (!bumped.some((n) => n.toLowerCase() === PKG)) {
101
+ content = (content ? content.trimEnd() + "\n" : "") + `${PKG}==${version}\n`;
102
+ bumped.push(PKG);
103
+ }
104
+ writeFileSync(reqs, content);
105
+ log.ok(`requirements.txt → ${bumped.map((n) => `${n}==${version}`).join(", ")}`);
106
+
107
+ if (existsSync(venvPython(resolve(root, ".venv")))) {
108
+ log.step("refreshing .venv (for your editor)…");
109
+ if (!ensureIdeVenv(root)) log.warn(".venv refresh failed — the Docker dev container is unaffected.");
110
+ } else {
111
+ log.info("Installed automatically on the next `muscat-pk dev` / `dev:back` (Docker container).");
112
+ }
113
+ return;
114
+ }
115
+
116
+ log.info("Usage: muscat-pk sdk <list|update [version]>");
117
+ }
@@ -1,168 +1,168 @@
1
- // muscat-pk storybook — serve the bundled Storybook (foundations + muscat) so
2
- // plugin authors can browse the real rb-components UI while developing.
3
- //
4
- // The static build is produced from apps/storybook (MUSCAT_STORYBOOK=true → only
5
- // the 1.foundations + 4.muscat sections, ordering-number prefixes stripped) and
6
- // staged into cli/storybook-dist so it ships inside the published muscat-pk.
7
- // In the monorepo it's built on demand if missing (or with --rebuild).
8
- import { createServer } from "node:http";
9
- import {
10
- cpSync,
11
- createReadStream,
12
- existsSync,
13
- mkdirSync,
14
- rmSync,
15
- statSync,
16
- } from "node:fs";
17
- import { execSync, spawn } from "node:child_process";
18
- import { copyFileSync } from "node:fs";
19
- import { extname, join, resolve } from "node:path";
20
- import { kitRoot, log, parseArgs } from "./util.mjs";
21
-
22
- // Shipped location (under cli/ → included in the npm "files" list).
23
- const SHIPPED_DIST = resolve(kitRoot, "cli/storybook-dist");
24
- // Raw .stories.tsx sources, staged alongside the compiled build — the `mcp`
25
- // command's get_component_example tool reads these directly (real usage
26
- // code, not just prop types) without needing a Storybook runtime.
27
- export const SHIPPED_STORIES_SRC = resolve(SHIPPED_DIST, "_stories-src");
28
- // Monorepo source (absent in an external install).
29
- const REPO_ROOT = resolve(kitRoot, "../..");
30
- const REPO_STORYBOOK = resolve(REPO_ROOT, "apps/storybook");
31
- const REPO_BUILD_OUT = resolve(REPO_STORYBOOK, "storybook-static-muscat");
32
- const REPO_MUSCAT_STORIES = resolve(REPO_STORYBOOK, "stories/4.muscat");
33
- const REPO_DESIGN_TOKENS = resolve(
34
- REPO_ROOT,
35
- "packages/rb-styles/css/output/rb-token.css",
36
- );
37
- // Generated Tailwind v4 token file (@theme colors + @utility typo-*), staged so
38
- // the `mcp` command's *_design_tokens tools work in an external install too.
39
- export const SHIPPED_DESIGN_TOKENS = resolve(SHIPPED_DIST, "_design-tokens.css");
40
-
41
- const CONTENT_TYPE = {
42
- ".html": "text/html; charset=utf-8",
43
- ".js": "text/javascript; charset=utf-8",
44
- ".mjs": "text/javascript; charset=utf-8",
45
- ".css": "text/css; charset=utf-8",
46
- ".json": "application/json; charset=utf-8",
47
- ".map": "application/json; charset=utf-8",
48
- ".svg": "image/svg+xml",
49
- ".png": "image/png",
50
- ".jpg": "image/jpeg",
51
- ".jpeg": "image/jpeg",
52
- ".webp": "image/webp",
53
- ".gif": "image/gif",
54
- ".ico": "image/x-icon",
55
- ".woff": "font/woff",
56
- ".woff2": "font/woff2",
57
- ".ttf": "font/ttf",
58
- ".wasm": "application/wasm",
59
- };
60
-
61
- // Build apps/storybook (muscat subset) and stage it into cli/storybook-dist.
62
- // Monorepo only. Outside the monorepo (e.g. re-packing an already-published
63
- // copy) it's a no-op — the shipped cli/storybook-dist is already in place.
64
- export function buildStorybook() {
65
- if (!existsSync(REPO_STORYBOOK)) {
66
- log.info("apps/storybook not found — using the pre-staged storybook-dist.");
67
- return;
68
- }
69
- log.step("building Storybook (foundations + muscat)…");
70
- execSync("yarn workspace storybook storybook:build:muscat", {
71
- cwd: REPO_ROOT,
72
- stdio: "inherit",
73
- env: { ...process.env, MUSCAT_STORYBOOK: "true" },
74
- });
75
- rmSync(SHIPPED_DIST, { recursive: true, force: true });
76
- mkdirSync(SHIPPED_DIST, { recursive: true });
77
- cpSync(REPO_BUILD_OUT, SHIPPED_DIST, { recursive: true });
78
-
79
- // Stage raw .stories.tsx sources (not just the compiled JS bundle) for the
80
- // `mcp` command's get_component_example tool.
81
- if (existsSync(REPO_MUSCAT_STORIES)) {
82
- cpSync(REPO_MUSCAT_STORIES, SHIPPED_STORIES_SRC, {
83
- recursive: true,
84
- filter: (src) => statSync(src).isDirectory() || src.endsWith(".stories.tsx"),
85
- });
86
- }
87
-
88
- // Stage the design-token CSS for the `mcp` command's *_design_tokens tools.
89
- if (existsSync(REPO_DESIGN_TOKENS)) {
90
- copyFileSync(REPO_DESIGN_TOKENS, SHIPPED_DESIGN_TOKENS);
91
- }
92
-
93
- log.ok(`storybook → ${SHIPPED_DIST}`);
94
- }
95
-
96
- function openBrowser(url) {
97
- const cmd =
98
- process.platform === "darwin"
99
- ? "open"
100
- : process.platform === "win32"
101
- ? "start"
102
- : "xdg-open";
103
- try {
104
- spawn(cmd, [url], {
105
- stdio: "ignore",
106
- detached: true,
107
- shell: process.platform === "win32",
108
- }).unref();
109
- } catch {
110
- /* headless / no browser — the URL is printed anyway */
111
- }
112
- }
113
-
114
- function serve(distDir, port) {
115
- const server = createServer((req, res) => {
116
- const url = decodeURIComponent((req.url || "/").split("?")[0]);
117
- const rel = url === "/" ? "/index.html" : url;
118
- const filePath = resolve(distDir, `.${rel}`);
119
-
120
- // Serve the file if it's inside distDir and exists; otherwise fall back to
121
- // index.html (Storybook is a single-page app).
122
- if (
123
- filePath.startsWith(distDir) &&
124
- existsSync(filePath) &&
125
- statSync(filePath).isFile()
126
- ) {
127
- const type = CONTENT_TYPE[extname(filePath)];
128
- if (type) res.setHeader("Content-Type", type);
129
- res.setHeader("Access-Control-Allow-Origin", "*");
130
- createReadStream(filePath).pipe(res);
131
- return;
132
- }
133
- res.setHeader("Content-Type", "text/html; charset=utf-8");
134
- createReadStream(join(distDir, "index.html")).pipe(res);
135
- });
136
-
137
- server.on("error", (err) => {
138
- if (err.code === "EADDRINUSE") {
139
- log.error(`port ${port} is in use — pass a free one with --port <n>.`);
140
- process.exit(1);
141
- }
142
- throw err;
143
- });
144
-
145
- server.listen(port, () => {
146
- const url = `http://localhost:${port}`;
147
- log.ok(`Storybook → ${url}`);
148
- openBrowser(url);
149
- });
150
- }
151
-
152
- export async function runStorybook(args = []) {
153
- const { port = "6007", rebuild } = parseArgs(args, { booleans: ["rebuild"] });
154
-
155
- const staged = existsSync(join(SHIPPED_DIST, "index.html"));
156
- if (rebuild || !staged) {
157
- if (existsSync(REPO_STORYBOOK)) {
158
- buildStorybook();
159
- } else if (!staged) {
160
- throw new Error(
161
- `Storybook build not found at ${SHIPPED_DIST}.\n` +
162
- " This build of muscat-pk was published without the bundled Storybook.",
163
- );
164
- }
165
- }
166
-
167
- serve(SHIPPED_DIST, Number(port));
168
- }
1
+ // muscat-pk storybook — serve the bundled Storybook (foundations + muscat) so
2
+ // plugin authors can browse the real rb-components UI while developing.
3
+ //
4
+ // The static build is produced from apps/storybook (MUSCAT_STORYBOOK=true → only
5
+ // the 1.foundations + 4.muscat sections, ordering-number prefixes stripped) and
6
+ // staged into cli/storybook-dist so it ships inside the published muscat-pk.
7
+ // In the monorepo it's built on demand if missing (or with --rebuild).
8
+ import { createServer } from "node:http";
9
+ import {
10
+ cpSync,
11
+ createReadStream,
12
+ existsSync,
13
+ mkdirSync,
14
+ rmSync,
15
+ statSync,
16
+ } from "node:fs";
17
+ import { execSync, spawn } from "node:child_process";
18
+ import { copyFileSync } from "node:fs";
19
+ import { extname, join, resolve } from "node:path";
20
+ import { kitRoot, log, parseArgs } from "./util.mjs";
21
+
22
+ // Shipped location (under cli/ → included in the npm "files" list).
23
+ const SHIPPED_DIST = resolve(kitRoot, "cli/storybook-dist");
24
+ // Raw .stories.tsx sources, staged alongside the compiled build — the `mcp`
25
+ // command's get_component_example tool reads these directly (real usage
26
+ // code, not just prop types) without needing a Storybook runtime.
27
+ export const SHIPPED_STORIES_SRC = resolve(SHIPPED_DIST, "_stories-src");
28
+ // Monorepo source (absent in an external install).
29
+ const REPO_ROOT = resolve(kitRoot, "../..");
30
+ const REPO_STORYBOOK = resolve(REPO_ROOT, "apps/storybook");
31
+ const REPO_BUILD_OUT = resolve(REPO_STORYBOOK, "storybook-static-muscat");
32
+ const REPO_MUSCAT_STORIES = resolve(REPO_STORYBOOK, "stories/4.muscat");
33
+ const REPO_DESIGN_TOKENS = resolve(
34
+ REPO_ROOT,
35
+ "packages/rb-styles/css/output/rb-token.css",
36
+ );
37
+ // Generated Tailwind v4 token file (@theme colors + @utility typo-*), staged so
38
+ // the `mcp` command's *_design_tokens tools work in an external install too.
39
+ export const SHIPPED_DESIGN_TOKENS = resolve(SHIPPED_DIST, "_design-tokens.css");
40
+
41
+ const CONTENT_TYPE = {
42
+ ".html": "text/html; charset=utf-8",
43
+ ".js": "text/javascript; charset=utf-8",
44
+ ".mjs": "text/javascript; charset=utf-8",
45
+ ".css": "text/css; charset=utf-8",
46
+ ".json": "application/json; charset=utf-8",
47
+ ".map": "application/json; charset=utf-8",
48
+ ".svg": "image/svg+xml",
49
+ ".png": "image/png",
50
+ ".jpg": "image/jpeg",
51
+ ".jpeg": "image/jpeg",
52
+ ".webp": "image/webp",
53
+ ".gif": "image/gif",
54
+ ".ico": "image/x-icon",
55
+ ".woff": "font/woff",
56
+ ".woff2": "font/woff2",
57
+ ".ttf": "font/ttf",
58
+ ".wasm": "application/wasm",
59
+ };
60
+
61
+ // Build apps/storybook (muscat subset) and stage it into cli/storybook-dist.
62
+ // Monorepo only. Outside the monorepo (e.g. re-packing an already-published
63
+ // copy) it's a no-op — the shipped cli/storybook-dist is already in place.
64
+ export function buildStorybook() {
65
+ if (!existsSync(REPO_STORYBOOK)) {
66
+ log.info("apps/storybook not found — using the pre-staged storybook-dist.");
67
+ return;
68
+ }
69
+ log.step("building Storybook (foundations + muscat)…");
70
+ execSync("yarn workspace storybook storybook:build:muscat", {
71
+ cwd: REPO_ROOT,
72
+ stdio: "inherit",
73
+ env: { ...process.env, MUSCAT_STORYBOOK: "true" },
74
+ });
75
+ rmSync(SHIPPED_DIST, { recursive: true, force: true });
76
+ mkdirSync(SHIPPED_DIST, { recursive: true });
77
+ cpSync(REPO_BUILD_OUT, SHIPPED_DIST, { recursive: true });
78
+
79
+ // Stage raw .stories.tsx sources (not just the compiled JS bundle) for the
80
+ // `mcp` command's get_component_example tool.
81
+ if (existsSync(REPO_MUSCAT_STORIES)) {
82
+ cpSync(REPO_MUSCAT_STORIES, SHIPPED_STORIES_SRC, {
83
+ recursive: true,
84
+ filter: (src) => statSync(src).isDirectory() || src.endsWith(".stories.tsx"),
85
+ });
86
+ }
87
+
88
+ // Stage the design-token CSS for the `mcp` command's *_design_tokens tools.
89
+ if (existsSync(REPO_DESIGN_TOKENS)) {
90
+ copyFileSync(REPO_DESIGN_TOKENS, SHIPPED_DESIGN_TOKENS);
91
+ }
92
+
93
+ log.ok(`storybook → ${SHIPPED_DIST}`);
94
+ }
95
+
96
+ function openBrowser(url) {
97
+ const cmd =
98
+ process.platform === "darwin"
99
+ ? "open"
100
+ : process.platform === "win32"
101
+ ? "start"
102
+ : "xdg-open";
103
+ try {
104
+ spawn(cmd, [url], {
105
+ stdio: "ignore",
106
+ detached: true,
107
+ shell: process.platform === "win32",
108
+ }).unref();
109
+ } catch {
110
+ /* headless / no browser — the URL is printed anyway */
111
+ }
112
+ }
113
+
114
+ function serve(distDir, port) {
115
+ const server = createServer((req, res) => {
116
+ const url = decodeURIComponent((req.url || "/").split("?")[0]);
117
+ const rel = url === "/" ? "/index.html" : url;
118
+ const filePath = resolve(distDir, `.${rel}`);
119
+
120
+ // Serve the file if it's inside distDir and exists; otherwise fall back to
121
+ // index.html (Storybook is a single-page app).
122
+ if (
123
+ filePath.startsWith(distDir) &&
124
+ existsSync(filePath) &&
125
+ statSync(filePath).isFile()
126
+ ) {
127
+ const type = CONTENT_TYPE[extname(filePath)];
128
+ if (type) res.setHeader("Content-Type", type);
129
+ res.setHeader("Access-Control-Allow-Origin", "*");
130
+ createReadStream(filePath).pipe(res);
131
+ return;
132
+ }
133
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
134
+ createReadStream(join(distDir, "index.html")).pipe(res);
135
+ });
136
+
137
+ server.on("error", (err) => {
138
+ if (err.code === "EADDRINUSE") {
139
+ log.error(`port ${port} is in use — pass a free one with --port <n>.`);
140
+ process.exit(1);
141
+ }
142
+ throw err;
143
+ });
144
+
145
+ server.listen(port, () => {
146
+ const url = `http://localhost:${port}`;
147
+ log.ok(`Storybook → ${url}`);
148
+ openBrowser(url);
149
+ });
150
+ }
151
+
152
+ export async function runStorybook(args = []) {
153
+ const { port = "6007", rebuild } = parseArgs(args, { booleans: ["rebuild"] });
154
+
155
+ const staged = existsSync(join(SHIPPED_DIST, "index.html"));
156
+ if (rebuild || !staged) {
157
+ if (existsSync(REPO_STORYBOOK)) {
158
+ buildStorybook();
159
+ } else if (!staged) {
160
+ throw new Error(
161
+ `Storybook build not found at ${SHIPPED_DIST}.\n` +
162
+ " This build of muscat-pk was published without the bundled Storybook.",
163
+ );
164
+ }
165
+ }
166
+
167
+ serve(SHIPPED_DIST, Number(port));
168
+ }