@rainbow-robotics/muscat-plugin-kit 1.3.0 → 1.3.3-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/cli/docker/Dockerfile.cpp +22 -5
  2. package/cli/docker/Dockerfile.dev +25 -5
  3. package/cli/docker/Dockerfile.python +19 -0
  4. package/cli/src/bridge.mjs +19 -2
  5. package/cli/src/build.mjs +31 -79
  6. package/cli/src/cpp.mjs +10 -6
  7. package/cli/src/dev.mjs +7 -2
  8. package/cli/src/devBack.mjs +39 -32
  9. package/cli/src/host_agent.py +143 -83
  10. package/cli/src/mcp.mjs +75 -16
  11. package/cli/src/new.mjs +5 -2
  12. package/cli/src/ports.mjs +21 -7
  13. package/cli/src/python.mjs +9 -17
  14. package/cli/src/ros2.mjs +13 -8
  15. package/cli/src/sdk.mjs +23 -21
  16. package/cli/src/templates-embedded.mjs +1 -1
  17. package/cli/src/type.mjs +42 -8
  18. package/cli/src/util.mjs +53 -5
  19. package/cli/storybook-dist/assets/{Color-YHDXOIA2-DixbmWSF.js → Color-YHDXOIA2-DNIJVGRw.js} +1 -1
  20. package/cli/storybook-dist/assets/{Docs-D6oFYDre.js → Docs-CIT3omBi.js} +1 -1
  21. package/cli/storybook-dist/assets/{Docs-Blx_41TT.js → Docs-CdX_ToEv.js} +1 -1
  22. package/cli/storybook-dist/assets/{Docs-DcUa4nGr.js → Docs-D9eMx0Cp.js} +1 -1
  23. package/cli/storybook-dist/assets/{Docs-Dv-5qjF4.js → Docs-RJox-ekp.js} +1 -1
  24. package/cli/storybook-dist/assets/{Docs-C0E3mHmn.js → Docs-wnrZBY76.js} +1 -1
  25. package/cli/storybook-dist/assets/{Docs-CMEiBjLz.js → Docs-x99QNSvG.js} +1 -1
  26. package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-GwWANrWN.js → DocsRenderer-CFRXHY34-CPuvjoGb.js} +1 -1
  27. package/cli/storybook-dist/assets/{NxAvatar.stories-CyZ4AE2b.js → NxAvatar.stories-B3OxXBgc.js} +1 -1
  28. package/cli/storybook-dist/assets/{NxButton.stories-Bx8UtGNd.js → NxButton.stories-UTqX8ZkA.js} +1 -1
  29. package/cli/storybook-dist/assets/{NxNumberInput.stories-skizAdd5.js → NxNumberInput.stories-GoLEe-uJ.js} +1 -1
  30. package/cli/storybook-dist/assets/{NxPagination.stories-gtIxt2u0.js → NxPagination.stories-Jj6dt6eG.js} +1 -1
  31. package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-DSHMk3N5.js → NxPointerHoldButton.stories-tLN9u4nC.js} +1 -1
  32. package/cli/storybook-dist/assets/{NxRangeInput.stories-C1YtrFZ1.js → NxRangeInput.stories-C-MK73xb.js} +1 -1
  33. package/cli/storybook-dist/assets/{NxSelectBox.stories-DCySAtBp.js → NxSelectBox.stories-CBcFcOIX.js} +1 -1
  34. package/cli/storybook-dist/assets/{NxSlideRange.stories-gSaMOWo7.js → NxSlideRange.stories-oNQkoW66.js} +1 -1
  35. package/cli/storybook-dist/assets/{NxSwitch.stories-DF0DBiyG.js → NxSwitch.stories-CqBkO5GC.js} +1 -1
  36. package/cli/storybook-dist/assets/{NxTextArea.stories-Dill0M6z.js → NxTextArea.stories-DtysSyl4.js} +1 -1
  37. package/cli/storybook-dist/assets/{NxTextInput.stories-Bax14r48.js → NxTextInput.stories-BOLDbSgF.js} +1 -1
  38. package/cli/storybook-dist/assets/{RBAccordion.stories-CCwz_pIV.js → RBAccordion.stories-D9-wu9LY.js} +1 -1
  39. package/cli/storybook-dist/assets/{RBAccordionGroup.stories-BjDxxdPV.js → RBAccordionGroup.stories-BPfnIKm6.js} +1 -1
  40. package/cli/storybook-dist/assets/{RBAlert.stories-5z6GasgP.js → RBAlert.stories-CGn-c2ac.js} +1 -1
  41. package/cli/storybook-dist/assets/{RBBadge.stories-BwcM7i7w.js → RBBadge.stories-CBLmeoq1.js} +1 -1
  42. package/cli/storybook-dist/assets/{RBBattery.stories-DOeQH3rK.js → RBBattery.stories-oo47rbDh.js} +1 -1
  43. package/cli/storybook-dist/assets/{RBCard.stories-DcmiOXAV.js → RBCard.stories-SiD2VfQ4.js} +1 -1
  44. package/cli/storybook-dist/assets/{RBCheckBox.stories-BYru87fX.js → RBCheckBox.stories-sEEVrjSg.js} +1 -1
  45. package/cli/storybook-dist/assets/{RBCheckIconButton.stories-CXUjtR1a.js → RBCheckIconButton.stories-C5GQUjZf.js} +1 -1
  46. package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-BSOQV7Du.js → RBCheckLabelButton.stories-DTaTqBvJ.js} +1 -1
  47. package/cli/storybook-dist/assets/{RBChipTab.stories-ByBmeukU.js → RBChipTab.stories-3AZr4kOz.js} +1 -1
  48. package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-GtJ01RTr.js → RBChipsBoxTab.stories-hCJnBYQl.js} +1 -1
  49. package/cli/storybook-dist/assets/{RBColorIcons.stories-C9saET1u.js → RBColorIcons.stories-BX4RJygu.js} +1 -1
  50. package/cli/storybook-dist/assets/{RBCommand.stories-CcvnWlWZ.js → RBCommand.stories-BidZ4H8f.js} +1 -1
  51. package/cli/storybook-dist/assets/{RBConfirm.stories-hdBVSdBS.js → RBConfirm.stories-D3Txw1kT.js} +1 -1
  52. package/cli/storybook-dist/assets/{RBDateInput.stories-BE9JQl-E.js → RBDateInput.stories-CLxRaN6T.js} +1 -1
  53. package/cli/storybook-dist/assets/{RBDialog.stories-BaWuDgl_.js → RBDialog.stories-CEhmUGp-.js} +1 -1
  54. package/cli/storybook-dist/assets/{RBDivider.stories-D3INKm_Z.js → RBDivider.stories-peXP2KG9.js} +1 -1
  55. package/cli/storybook-dist/assets/{RBDrawer.stories-BZ8V0J6-.js → RBDrawer.stories-38iDf6uC.js} +1 -1
  56. package/cli/storybook-dist/assets/{RBDropdownMenu.stories-DxLi8HRw.js → RBDropdownMenu.stories-Dsi8tqSy.js} +1 -1
  57. package/cli/storybook-dist/assets/{RBEmptyView.stories-DQcvDt2S.js → RBEmptyView.stories-VMxWhzLP.js} +1 -1
  58. package/cli/storybook-dist/assets/{RBIconButton.stories-DJfuTpQ0.js → RBIconButton.stories-BPLhceGO.js} +1 -1
  59. package/cli/storybook-dist/assets/{RBIcons.stories-BSsyb0BK.js → RBIcons.stories-DuYdO951.js} +1 -1
  60. package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-Di3th--x.js → RBInlineLabelButton.stories-CGNYhQyl.js} +1 -1
  61. package/cli/storybook-dist/assets/{RBJoyStick.stories-DfGazm4n.js → RBJoyStick.stories-CCmn-LMy.js} +1 -1
  62. package/cli/storybook-dist/assets/{RBLabelButton.stories-CLbxzW56.js → RBLabelButton.stories-IkKjthGk.js} +1 -1
  63. package/cli/storybook-dist/assets/{RBLight.stories-DeiuDm_P.js → RBLight.stories-eJFQOjPH.js} +1 -1
  64. package/cli/storybook-dist/assets/{RBMenubar.stories-BZQTV9e5.js → RBMenubar.stories-kg-xXGrW.js} +1 -1
  65. package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-DDNmdi_n.js → RBMultiRangeInput.stories-Czr1r3gd.js} +1 -1
  66. package/cli/storybook-dist/assets/{RBPagination.stories-CLBCEyCU.js → RBPagination.stories-Dyu6Khnl.js} +1 -1
  67. package/cli/storybook-dist/assets/{RBPopover.stories-DRI5Kfa-.js → RBPopover.stories-CAM6hpf2.js} +1 -1
  68. package/cli/storybook-dist/assets/{RBPrompt.stories-DIAmypoJ.js → RBPrompt.stories-B4M-veaf.js} +1 -1
  69. package/cli/storybook-dist/assets/{RBRadioBox.stories-BS_N4aLf.js → RBRadioBox.stories-DeAOKvil.js} +1 -1
  70. package/cli/storybook-dist/assets/{RBRadioButton.stories-BTm8cYvm.js → RBRadioButton.stories-DjXwYXFy.js} +1 -1
  71. package/cli/storybook-dist/assets/{RBRangeDateInput.stories-D5hBsSyB.js → RBRangeDateInput.stories-9pzKhXGf.js} +1 -1
  72. package/cli/storybook-dist/assets/{RBResizable.stories-CxPEkbXQ.js → RBResizable.stories-B4Hi4DUD.js} +1 -1
  73. package/cli/storybook-dist/assets/{RBSelectBox.stories-FUQ7AcUi.js → RBSelectBox.stories-tVVv4c6z.js} +1 -1
  74. package/cli/storybook-dist/assets/{RBSheet.stories-mhVHH_71.js → RBSheet.stories-BwOCmaNN.js} +1 -1
  75. package/cli/storybook-dist/assets/{RBSideNumberInput.stories-5FMkLUim.js → RBSideNumberInput.stories-BbGOIitU.js} +1 -1
  76. package/cli/storybook-dist/assets/{RBSlider.stories-Bnhcp9pz.js → RBSlider.stories-yIDdawYQ.js} +1 -1
  77. package/cli/storybook-dist/assets/{RBSpinner.stories-Bf4416mP.js → RBSpinner.stories-CIJvpbgK.js} +1 -1
  78. package/cli/storybook-dist/assets/{RBSwitch.stories-BNhFjRaV.js → RBSwitch.stories-Bo8hrSiq.js} +1 -1
  79. package/cli/storybook-dist/assets/{RBTab.stories-Bsr7jlfb.js → RBTab.stories-Cq3N4Dgb.js} +1 -1
  80. package/cli/storybook-dist/assets/{RBTable.stories-BkA6pKzs.js → RBTable.stories-DDeZf-Wb.js} +1 -1
  81. package/cli/storybook-dist/assets/{RBTableRow.stories-D07Z7D5-.js → RBTableRow.stories-BeKVMSo3.js} +1 -1
  82. package/cli/storybook-dist/assets/{RBTabs.stories-DHnZiV21.js → RBTabs.stories-DovB7PPB.js} +1 -1
  83. package/cli/storybook-dist/assets/{RBTextArea.stories-CtLMLuV_.js → RBTextArea.stories-D7H4RvP8.js} +1 -1
  84. package/cli/storybook-dist/assets/{RBTextField.stories-Chz0i1-u.js → RBTextField.stories-CmkNXeEk.js} +1 -1
  85. package/cli/storybook-dist/assets/{RBTextInput.stories-Dk5cZfUF.js → RBTextInput.stories-hnxbQJsb.js} +1 -1
  86. package/cli/storybook-dist/assets/{RBToast.stories-tZxbjmr8.js → RBToast.stories-BklGGkNh.js} +1 -1
  87. package/cli/storybook-dist/assets/{RBToggle.stories-VTe7a4xZ.js → RBToggle.stories-C56AXyxq.js} +1 -1
  88. package/cli/storybook-dist/assets/{RBToggleGroup.stories-WiFnGGYx.js → RBToggleGroup.stories-CpLPdAF0.js} +1 -1
  89. package/cli/storybook-dist/assets/{RBTooltip.stories-B_L4W7Mn.js → RBTooltip.stories-BdOnCwGk.js} +1 -1
  90. package/cli/storybook-dist/assets/{SideNumberInput-os5wKKf7.js → SideNumberInput-DLmM2_Dy.js} +80 -80
  91. package/cli/storybook-dist/assets/{entry-preview-NpzneD8a.js → entry-preview-DqMcNOGI.js} +1 -1
  92. package/cli/storybook-dist/assets/{iframe-DsvRKHwc.js → iframe-C1kyDB-R.js} +2 -2
  93. package/cli/storybook-dist/assets/{index-BzvSJzrT.js → index-CdVSDe2m.js} +3 -3
  94. package/cli/storybook-dist/assets/{preview-yXaYuZ78.js → preview-C_JfiDjl.js} +2 -2
  95. package/cli/storybook-dist/assets/preview-DvwEjyb4.js +3 -0
  96. package/cli/storybook-dist/assets/preview-zuAvMksL.css +1 -0
  97. package/cli/storybook-dist/iframe.html +1 -1
  98. package/cli/storybook-dist/project.json +1 -1
  99. package/cli/templates/common/README.md +65 -4
  100. package/cli/templates/common/py/main.py +5 -1
  101. package/cli/templates/common/requirements.txt +3 -3
  102. package/cli/templates/cpp/cpp/CMakeLists.txt +1 -2
  103. package/cli/templates/cpp/cpp/main.cpp +48 -8
  104. package/cli/templates/readme-cpp/README.md +969 -0
  105. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +1 -1
  106. package/package.json +3 -3
  107. package/cli/storybook-dist/assets/preview-BXXleNPp.css +0 -1
  108. package/cli/storybook-dist/assets/preview-BXxjLQ9M.js +0 -3
@@ -509,7 +509,7 @@
509
509
  }
510
510
  </script>
511
511
 
512
- <script type="module" crossorigin src="./assets/iframe-DsvRKHwc.js"></script>
512
+ <script type="module" crossorigin src="./assets/iframe-C1kyDB-R.js"></script>
513
513
  </head>
514
514
 
515
515
  <body>
@@ -1 +1 @@
1
- {"generatedAt":1789632370434,"userSince":1750932757926,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":true,"refCount":0,"testPackages":{"@vitest/browser":"3.2.4","@vitest/coverage-v8":"3.2.4","playwright":"1.59.1","vitest":"3.2.4"},"hasRouterPackage":false,"monorepo":"Turborepo","packageManager":{"type":"yarn","version":"berry","agent":"yarn@berry"},"preview":{"usesGlobals":false},"framework":{"name":"@storybook/react-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/react","portableStoriesFileCount":0,"applicationFileCount":0,"storybookVersion":"8.6.18","storybookVersionSpecifier":"^8.6.4","language":"typescript","storybookPackages":{"@chromatic-com/storybook":{"version":"3.2.7"},"@storybook/addon-essentials":{"version":"8.6.18"},"@storybook/addon-links":{"version":"8.6.18"},"@storybook/addon-onboarding":{"version":"8.6.18"},"@storybook/addon-themes":{"version":"8.6.18"},"@storybook/blocks":{"version":"8.6.18"},"@storybook/experimental-addon-test":{"version":"8.6.18"},"@storybook/react":{"version":"8.6.18"},"@storybook/react-vite":{"version":"8.6.18"},"@storybook/test":{"version":"8.6.18"},"@storybook/theming":{"version":"8.6.18"},"eslint-plugin-storybook":{"version":"0.11.6"},"storybook":{"version":"8.6.18"},"storybook-addon-react-docgen":{"version":"1.2.44"}},"addons":{"$SNIP/node_modules/@storybook/addon-links":{"version":null},"$SNIP/node_modules/@storybook/addon-essentials":{"version":null},"$SNIP/node_modules/@chromatic-com/storybook":{"version":null},"$SNIP/node_modules/@storybook/addon-themes":{"version":null},"$SNIP/node_modules/@storybook/experimental-addon-test":{"version":null}}}
1
+ {"generatedAt":1790728570661,"userSince":1750932757926,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":true,"refCount":0,"testPackages":{"@vitest/browser":"3.2.4","@vitest/coverage-v8":"3.2.4","playwright":"1.59.1","vitest":"3.2.4"},"hasRouterPackage":false,"monorepo":"Turborepo","packageManager":{"type":"yarn","version":"berry","agent":"yarn@berry"},"preview":{"usesGlobals":false},"framework":{"name":"@storybook/react-vite","options":{}},"builder":"@storybook/builder-vite","renderer":"@storybook/react","portableStoriesFileCount":0,"applicationFileCount":0,"storybookVersion":"8.6.18","storybookVersionSpecifier":"^8.6.4","language":"typescript","storybookPackages":{"@chromatic-com/storybook":{"version":"3.2.7"},"@storybook/addon-essentials":{"version":"8.6.18"},"@storybook/addon-links":{"version":"8.6.18"},"@storybook/addon-onboarding":{"version":"8.6.18"},"@storybook/addon-themes":{"version":"8.6.18"},"@storybook/blocks":{"version":"8.6.18"},"@storybook/experimental-addon-test":{"version":"8.6.18"},"@storybook/react":{"version":"8.6.18"},"@storybook/react-vite":{"version":"8.6.18"},"@storybook/test":{"version":"8.6.18"},"@storybook/theming":{"version":"8.6.18"},"eslint-plugin-storybook":{"version":"0.11.6"},"storybook":{"version":"8.6.18"},"storybook-addon-react-docgen":{"version":"1.2.44"}},"addons":{"$SNIP/node_modules/@storybook/addon-links":{"version":null},"$SNIP/node_modules/@storybook/addon-essentials":{"version":null},"$SNIP/node_modules/@chromatic-com/storybook":{"version":null},"$SNIP/node_modules/@storybook/addon-themes":{"version":null},"$SNIP/node_modules/@storybook/experimental-addon-test":{"version":null}}}
@@ -766,15 +766,15 @@ container (your own Ubuntu 24.04 machine, or the robot PC):
766
766
 
767
767
  ```bash
768
768
  curl -fsSL https://rainbowrobotics.github.io/rb-sdk/rb-sdk-apt-repo.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/rb-sdk-apt.gpg
769
- echo 'deb [signed-by=/usr/share/keyrings/rb-sdk-apt.gpg] https://rainbowrobotics.github.io/rb-sdk dev main' | sudo tee /etc/apt/sources.list.d/rb-sdk.list
770
- sudo apt update && sudo apt install rainbow-rb-sdk-dev=0.1.2~dev1
769
+ echo 'deb [signed-by=/usr/share/keyrings/rb-sdk-apt.gpg] https://rainbowrobotics.github.io/rb-sdk stable main' | sudo tee /etc/apt/sources.list.d/rb-sdk.list
770
+ sudo apt update && sudo apt install rainbow-rb-sdk-dev
771
771
  ```
772
772
 
773
773
  - CMake: `find_package(rb_sdk REQUIRED)` + `target_link_libraries(<target> rb_sdk::rb_sdk)` (C++20). pkg-config: `rb_sdk`.
774
774
  - Headers: `<rainbow/rb_sdk/manipulate/rb_manipulate_sdk.hpp>` (`RBManipulateSDK`),
775
775
  `<rainbow/rb_sdk/program/plugin_program_context.hpp>` (`rb_plugin::PluginProgramContext`, for `program.panel` commands — the header's top comment has a full `main()` example).
776
- - The binary is linked **dynamically** (`librb_sdk.so.<version>` by SONAME) and the robot provides that `.so`. Pin the SDK to the robot's version: the dev image default is `0.1.2~dev1`; change it with `MUSCAT_PK_RB_SDK_VERSION=<version> MUSCAT_PK_REBUILD_IMAGE=1 muscat-pk dev`. Do not bundle or static-link the SDK.
777
- - UI ↔ backend messaging (`RBPluginCall` pub/sub/serve) is Python-only for now.
776
+ - The binary is linked **dynamically**; `muscat-pk build` copies the matching `librb_sdk.so` into `dist/<arch>/lib/` (the robot has no SDK installed), so each plugin keeps the SDK version it was built with. Change the version with `MUSCAT_PK_RB_SDK_VERSION=<version> MUSCAT_PK_REBUILD_IMAGE=1 muscat-pk dev`.
777
+ - UI ↔ backend messaging works from C++ too: `rb_plugin::RBPluginCall` in `<rainbow/rb_sdk/plugin/rb_plugin.hpp>` (see the scaffolded `cpp/main.cpp`).
778
778
 
779
779
  ### ROS2 (rclpy)
780
780
 
@@ -815,6 +815,65 @@ public:
815
815
 
816
816
  ---
817
817
 
818
+ ## Video streaming (MediaMTX)
819
+
820
+ Muscat runs a MediaMTX media server. The backend publishes a stream over RTSP and
821
+ the UI plays it over WebRTC. `muscat-pk dev` runs the same server (or uses a local
822
+ Muscat's), so the addresses are identical in dev and on the robot.
823
+
824
+ | | Address |
825
+ | --- | --- |
826
+ | Publish (backend) | `rtsp://127.0.0.1:8554/<plugin_id>/<name>` |
827
+ | Play (UI) | `http://<host>:8889/<plugin_id>/<name>/` |
828
+
829
+ - Prefix the path with your plugin id — paths are shared with robot cameras and other plugins.
830
+ - The robot runtime has no ffmpeg, GStreamer or OpenCV; bring your own encoder (below).
831
+
832
+ **Backend (Python)** — use PyAV: add `av` and `numpy` to `requirements.txt`. Its wheels
833
+ include FFmpeg, so they ship in the ZIP like any other dependency.
834
+
835
+ ```python
836
+ import fractions, time
837
+ import av, numpy as np
838
+
839
+ out = av.open("rtsp://127.0.0.1:8554/__PLUGIN_ID__/cam", mode="w", format="rtsp",
840
+ options={"rtsp_transport": "tcp"})
841
+ stream = out.add_stream("libx264", rate=25, options={"preset": "ultrafast", "tune": "zerolatency"})
842
+ stream.width, stream.height, stream.pix_fmt = 640, 360, "yuv420p"
843
+
844
+ i = 0
845
+ while True:
846
+ img = np.zeros((360, 640, 3), dtype=np.uint8) # your RGB frame here
847
+ frame = av.VideoFrame.from_ndarray(img, format="rgb24")
848
+ frame.pts, frame.time_base = i, fractions.Fraction(1, 25)
849
+ for packet in stream.encode(frame):
850
+ out.mux(packet)
851
+ i += 1
852
+ time.sleep(1 / 25)
853
+ ```
854
+
855
+ Run the encoder loop in a thread (or `asyncio.to_thread`) so it doesn't block the
856
+ `RBPluginCall` handlers.
857
+
858
+ **UI** — embed MediaMTX's WebRTC player. `location.hostname` is `localhost` in the
859
+ preview and the robot's address on the robot:
860
+
861
+ ```tsx
862
+ const STREAM = "__PLUGIN_ID__/cam"; // the path the backend publishes
863
+
864
+ export default function Camera() {
865
+ return (
866
+ <iframe
867
+ src={`${location.protocol}//${location.hostname}:8889/${STREAM}/`}
868
+ allow="autoplay"
869
+ className="aspect-video w-full border-0"
870
+ />
871
+ );
872
+ }
873
+ ```
874
+
875
+ ---
876
+
818
877
  ## manifest.json
819
878
 
820
879
  ```jsonc
@@ -1043,6 +1102,8 @@ muscat-pk type change --to python # → Python
1043
1102
 
1044
1103
  > Existing backend dirs (`ros2_ws/`, `cpp/`) are not deleted when switching away. Remove them manually if no longer needed.
1045
1104
 
1105
+ > Switching between a Python and a C++ backend asks whether to replace this README with the matching one (the old file is kept as `README.md.bak`). Add `--readme` to answer yes without the prompt.
1106
+
1046
1107
  ---
1047
1108
 
1048
1109
  ## AI coding assistants (MCP)
@@ -24,6 +24,7 @@ import os
24
24
  from typing import Any
25
25
 
26
26
  from rainbow.rb_sdk import RBManipulateSDK, RBPluginCall
27
+ from rainbow.rb_sdk.rb_sdk_cpp import ConnectOptions
27
28
 
28
29
 
29
30
  PLUGIN_NAME = "__PLUGIN_NAME__"
@@ -95,7 +96,10 @@ async def main():
95
96
  # Remote dev: connect to the robot before registering any handlers.
96
97
  # connect() closes and reopens the Zenoh session, so handlers registered
97
98
  # before this call would be lost.
98
- await manipulate.connect(token=robot_token, host=robot_host)
99
+ opts = ConnectOptions()
100
+ opts.token = robot_token
101
+ opts.host = robot_host
102
+ manipulate.connect(opts)
99
103
 
100
104
  register_handlers()
101
105
 
@@ -1,4 +1,4 @@
1
- rainbow-rb-sdk==0.1.1
2
- rainbow-rb-flat-buffers==0.1.1
3
- rainbow-rb-zenoh==0.1.1
1
+ rainbow-rb-sdk==0.1.3
2
+ rainbow-rb-flat-buffers==0.1.3
3
+ rainbow-rb-zenoh==0.1.3
4
4
  requests==2.34.2
@@ -5,8 +5,7 @@ set(CMAKE_CXX_STANDARD 20)
5
5
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
6
 
7
7
  # Rainbow Robotics C++ SDK — provided by the apt package rainbow-rb-sdk-dev
8
- # (pre-installed in the muscat-pk dev container). Linked dynamically: the robot
9
- # ships the matching librb_sdk.so, so do NOT bundle or static-link it.
8
+ # (pre-installed in the muscat-pk dev container).
10
9
  find_package(rb_sdk REQUIRED)
11
10
 
12
11
  # The executable name must match runtime.entry in manifest.json.
@@ -3,14 +3,17 @@
3
3
  // The Rainbow Robotics C++ SDK (apt: rainbow-rb-sdk-dev) is installed in the
4
4
  // dev container and linked via CMake (rb_sdk::rb_sdk). Headers live under
5
5
  // <rainbow/rb_sdk/...>:
6
+ // - plugin/rb_plugin.hpp rb_plugin::RBPluginCall — plugin UI <-> backend
7
+ // (serve/subscribe/publish) and external endpoints (router)
6
8
  // - manipulate/rb_manipulate_sdk.hpp RBManipulateSDK — control the robot
7
9
  // - program/plugin_program_context.hpp rb_plugin::PluginProgramContext —
8
10
  // program.panel commands (done/pause/stop,
9
- // program variables); see the header's
10
- // top comment for a full main() example
11
- // UI <-> backend messaging (RBPluginCall pub/sub/serve) is Python-only for now.
11
+ // program variables)
12
12
 
13
13
  #include <rainbow/rb_sdk/manipulate/rb_manipulate_sdk.hpp>
14
+ #include <rainbow/rb_sdk/plugin/rb_plugin.hpp>
15
+
16
+ #include <nlohmann/json.hpp>
14
17
 
15
18
  #include <chrono>
16
19
  #include <cstdlib>
@@ -18,13 +21,50 @@
18
21
  #include <string>
19
22
  #include <thread>
20
23
 
24
+ using nlohmann::json;
25
+ using rb_plugin::RBPluginCall;
26
+
21
27
  int main() {
22
- const char* plugin_id = std::getenv("RB_PLUGIN_ID");
23
- std::cout << "[__PLUGIN_NAME__] backend ready"
24
- << (plugin_id ? std::string(" (id=") + plugin_id + ")" : "")
25
- << std::endl;
28
+ const std::string name = "__PLUGIN_NAME__";
29
+
30
+ // Remote dev: connect before registering handlers (connect() reopens the session).
31
+ RBManipulateSDK manipulate;
32
+ const char* host = std::getenv("MUSCAT_ROBOT_HOST");
33
+ const char* token = std::getenv("MUSCAT_ROBOT_TOKEN");
34
+ if (host && token) manipulate.connect({.token = token, .host = host});
35
+
36
+ // plugin id comes from RB_PLUGIN_ID.
37
+ auto plugin = RBPluginCall::get_instance(RBPluginCall::resolve_plugin_id(std::nullopt));
38
+
39
+ // --- Plugin UI <-> backend (not reachable from outside Muscat) ---
40
+
41
+ // UI: await rb.action("grip", { width })
42
+ plugin->serve("grip", [&](const json& payload) {
43
+ const int width = payload.is_object() ? payload.value("width", 20) : 20;
44
+ std::cout << "[" << name << "] grip requested: width=" << width << std::endl;
45
+ plugin->publish("state", {{"ready", true}, {"last_grip_width", width}}); // -> rb.sub("state")
46
+ return json{{"ok", true}, {"width", width}};
47
+ });
48
+
49
+ // UI: rb.pub("event", ...)
50
+ auto event_sub = plugin->subscribe("event", [&](const std::string&, const json& payload) {
51
+ std::cout << "[" << name << "] event: " << payload.dump() << std::endl;
52
+ });
53
+
54
+ // --- External endpoints (REST / Socket.IO / TCP / MQTT / WebRTC) ---
55
+ // curl -X POST http://<robot>/common/plugin/<plugin_id>/hello -d '{"name":"kim"}'
56
+ plugin->router().on("/hello", [](const json& payload, const json& meta) {
57
+ const std::string who = payload.is_object() ? payload.value("name", std::string("there")) : "there";
58
+ return json{{"msg", "hi " + who}, {"via", meta.value("transport", std::string())}};
59
+ });
60
+
61
+ // Example: robot control (not called automatically — add safety checks first).
62
+ // IPC::MoveInput_TargetT t; t.tar_values = {0, 0, 0, 0, 0, 0};
63
+ // manipulate.move.call_move_j("C500920", t);
64
+
65
+ plugin->publish("state", {{"ready", true}, {"plugin", name}});
66
+ std::cout << "[" << name << "] backend ready" << std::endl;
26
67
 
27
- // e.g. RBManipulateSDK manipulate; manipulate.move.call_move_j(...);
28
68
  // Keep the process alive.
29
69
  while (true) {
30
70
  std::this_thread::sleep_for(std::chrono::seconds(1));