@rainbow-robotics/muscat-plugin-kit 1.3.6-dev.7 → 1.3.6-dev.8

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 (129) hide show
  1. package/cli/README.md +3 -8
  2. package/cli/bin/muscat-pk.mjs +0 -5
  3. package/cli/bundle-templates.mjs +1 -6
  4. package/cli/docker/Dockerfile.cpp +6 -28
  5. package/cli/docker/Dockerfile.dev +0 -32
  6. package/cli/docker/Dockerfile.python +0 -19
  7. package/cli/src/bridge.mjs +12 -45
  8. package/cli/src/build.mjs +79 -48
  9. package/cli/src/cpp.mjs +5 -11
  10. package/cli/src/dev.mjs +2 -7
  11. package/cli/src/devBack.mjs +40 -97
  12. package/cli/src/host_agent.py +83 -143
  13. package/cli/src/manifest.mjs +0 -11
  14. package/cli/src/mcp.mjs +54 -398
  15. package/cli/src/new.mjs +3 -13
  16. package/cli/src/ports.mjs +7 -21
  17. package/cli/src/python.mjs +20 -16
  18. package/cli/src/ros2.mjs +8 -16
  19. package/cli/src/sdk.mjs +21 -23
  20. package/cli/src/templates-embedded.mjs +4 -0
  21. package/cli/src/type.mjs +10 -44
  22. package/cli/src/uninstall.mjs +20 -3
  23. package/cli/src/update.mjs +8 -14
  24. package/cli/src/util.mjs +9 -70
  25. package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +1 -13
  26. package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +1 -98
  27. package/cli/storybook-dist/assets/{Color-YHDXOIA2-C5ttlJWI.js → Color-YHDXOIA2-Du8dJ-nM.js} +1 -1
  28. package/cli/storybook-dist/assets/{Docs-CCfiEyzU.js → Docs--Fm8069p.js} +1 -1
  29. package/cli/storybook-dist/assets/{Docs-B3AV-YvP.js → Docs-Cb72AJnj.js} +1 -1
  30. package/cli/storybook-dist/assets/{Docs-BN206lKl.js → Docs-LdjYwuG4.js} +1 -1
  31. package/cli/storybook-dist/assets/{Docs-BqH2prGO.js → Docs-Sy5RDbtH.js} +1 -1
  32. package/cli/storybook-dist/assets/{Docs-DhncK7Aw.js → Docs-guGflRzZ.js} +1 -1
  33. package/cli/storybook-dist/assets/{Docs-CGN1_Ua8.js → Docs-pwGqGTV_.js} +1 -1
  34. package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-D-w8Kq64.js → DocsRenderer-CFRXHY34-YGBLQ4BD.js} +1 -1
  35. package/cli/storybook-dist/assets/{NxAvatar.stories-oCcYtLwq.js → NxAvatar.stories-C1d4n8cq.js} +1 -1
  36. package/cli/storybook-dist/assets/{NxButton.stories-C5jUScIj.js → NxButton.stories-gCBoZdMq.js} +1 -1
  37. package/cli/storybook-dist/assets/{NxIcons.stories-DeqrVzVs.js → NxIcons.stories-Dg42riT5.js} +1 -1
  38. package/cli/storybook-dist/assets/{NxNumberInput.stories-gjsEulIN.js → NxNumberInput.stories-DB-vLM2J.js} +1 -1
  39. package/cli/storybook-dist/assets/{NxPagination.stories-BK6uSWIa.js → NxPagination.stories-CS8iMIak.js} +1 -1
  40. package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-BDD5CvSZ.js → NxPointerHoldButton.stories-D66BopeS.js} +1 -1
  41. package/cli/storybook-dist/assets/{NxRangeInput.stories-C_nW26_x.js → NxRangeInput.stories-CZxddsJ2.js} +3 -3
  42. package/cli/storybook-dist/assets/{NxSelectBox.stories-DqLfKU9H.js → NxSelectBox.stories-CmezGGrm.js} +1 -1
  43. package/cli/storybook-dist/assets/{NxSlideRange.stories-CQjHA8yv.js → NxSlideRange.stories-CGAFMITi.js} +1 -1
  44. package/cli/storybook-dist/assets/{NxSwitch.stories-BMHCiPoL.js → NxSwitch.stories-B3QInkXX.js} +1 -1
  45. package/cli/storybook-dist/assets/{NxTextArea.stories-k7r1WVQh.js → NxTextArea.stories-BgJcgZNe.js} +1 -1
  46. package/cli/storybook-dist/assets/{NxTextInput.stories-Cqfe1I32.js → NxTextInput.stories-CwMQwI9L.js} +1 -1
  47. package/cli/storybook-dist/assets/{RBAccordion.stories-CJ3Ttuja.js → RBAccordion.stories-BuBVNzJT.js} +1 -1
  48. package/cli/storybook-dist/assets/{RBAccordionGroup.stories-BThv7tM0.js → RBAccordionGroup.stories-Crdb6kFF.js} +1 -1
  49. package/cli/storybook-dist/assets/{RBAlert.stories-CSMP8Vun.js → RBAlert.stories-CqXgUvA2.js} +1 -1
  50. package/cli/storybook-dist/assets/{RBBadge.stories-cBMA2R6F.js → RBBadge.stories-BCw_ksML.js} +1 -1
  51. package/cli/storybook-dist/assets/{RBBattery.stories-BKVOskqs.js → RBBattery.stories-0id4XYoc.js} +1 -1
  52. package/cli/storybook-dist/assets/{RBCard.stories-BTOfK0HC.js → RBCard.stories-CgpG-7Lk.js} +1 -1
  53. package/cli/storybook-dist/assets/{RBCheckBox.stories-DyE912wG.js → RBCheckBox.stories-BG02BoGX.js} +1 -1
  54. package/cli/storybook-dist/assets/{RBCheckIconButton.stories-0sLFU0kM.js → RBCheckIconButton.stories-vrjD4GQ4.js} +1 -1
  55. package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-c8VecClE.js → RBCheckLabelButton.stories-POba8lU-.js} +1 -1
  56. package/cli/storybook-dist/assets/{RBChipTab.stories-CQ9Uav5_.js → RBChipTab.stories-XWJbFrHF.js} +1 -1
  57. package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-Ap4NlhAZ.js → RBChipsBoxTab.stories-BdU0Dzdp.js} +1 -1
  58. package/cli/storybook-dist/assets/{RBColorIcons.stories-BPPXjif2.js → RBColorIcons.stories-C_0dCNXG.js} +1 -1
  59. package/cli/storybook-dist/assets/{RBCommand.stories-PDpYOKlm.js → RBCommand.stories-Dvrko5No.js} +1 -1
  60. package/cli/storybook-dist/assets/{RBConfirm.stories-BEWvMAYP.js → RBConfirm.stories-CkTn0sHa.js} +1 -1
  61. package/cli/storybook-dist/assets/{RBDateInput.stories-DLdyNnVF.js → RBDateInput.stories-BH3D08cs.js} +1 -1
  62. package/cli/storybook-dist/assets/{RBDialog.stories-HsawMNOb.js → RBDialog.stories-C2nNX9fl.js} +1 -1
  63. package/cli/storybook-dist/assets/{RBDivider.stories-dlWwOxik.js → RBDivider.stories-D-GrJ0QZ.js} +1 -1
  64. package/cli/storybook-dist/assets/{RBDrawer.stories-C87iqGk0.js → RBDrawer.stories-XLOnLOnh.js} +1 -1
  65. package/cli/storybook-dist/assets/{RBDropdownMenu.stories-B8EQhA0z.js → RBDropdownMenu.stories-BjBSeoV2.js} +1 -1
  66. package/cli/storybook-dist/assets/{RBEmptyView.stories-SzpIhNDw.js → RBEmptyView.stories-C6U2zKNE.js} +1 -1
  67. package/cli/storybook-dist/assets/{RBIconButton.stories-BQ0nM1gb.js → RBIconButton.stories-MCJq5Hta.js} +1 -1
  68. package/cli/storybook-dist/assets/{RBIcons.stories-C19g5M5z.js → RBIcons.stories-DxOCiCe0.js} +1 -1
  69. package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-DbgI4-1P.js → RBInlineLabelButton.stories-BVZi1aEs.js} +1 -1
  70. package/cli/storybook-dist/assets/{RBJoyStick.stories-smF-vpGL.js → RBJoyStick.stories-BDThbEek.js} +1 -1
  71. package/cli/storybook-dist/assets/{RBLabelButton.stories-s53klwDv.js → RBLabelButton.stories-CaPEKUS6.js} +1 -1
  72. package/cli/storybook-dist/assets/{RBLight.stories-BnzkoR51.js → RBLight.stories-CihQzyEL.js} +1 -1
  73. package/cli/storybook-dist/assets/{RBMenubar.stories-CW014klu.js → RBMenubar.stories-J852XVFh.js} +1 -1
  74. package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-qa4tH_4Y.js → RBMultiRangeInput.stories-DXyX0cOZ.js} +1 -1
  75. package/cli/storybook-dist/assets/{RBPagination.stories-YoQNUpQH.js → RBPagination.stories-C9_49ymO.js} +1 -1
  76. package/cli/storybook-dist/assets/{RBPopover.stories-Bhpi0OaH.js → RBPopover.stories-DxLE912-.js} +1 -1
  77. package/cli/storybook-dist/assets/{RBPrompt.stories-iQM5iAka.js → RBPrompt.stories-DlsfGqWb.js} +1 -1
  78. package/cli/storybook-dist/assets/{RBRadioBox.stories-BGyq4rzx.js → RBRadioBox.stories-hb5cNWW1.js} +1 -1
  79. package/cli/storybook-dist/assets/{RBRadioButton.stories-DF6BcUcJ.js → RBRadioButton.stories-CSuOgeN-.js} +1 -1
  80. package/cli/storybook-dist/assets/RBRangeDateInput.stories-KRg2glX8.js +15 -0
  81. package/cli/storybook-dist/assets/{RBResizable.stories-DSVHvwjp.js → RBResizable.stories-BetjF9vG.js} +1 -1
  82. package/cli/storybook-dist/assets/RBSelectBox.stories-ILLQQ8M8.js +6 -0
  83. package/cli/storybook-dist/assets/{RBSheet.stories-CAK9hb-p.js → RBSheet.stories-D3QbcMKq.js} +1 -1
  84. package/cli/storybook-dist/assets/{RBSideNumberInput.stories-DUymnH-9.js → RBSideNumberInput.stories-DcTh0IjM.js} +1 -1
  85. package/cli/storybook-dist/assets/{RBSlider.stories-BwlDh9--.js → RBSlider.stories-B59We7ei.js} +1 -1
  86. package/cli/storybook-dist/assets/{RBSpinner.stories-VOlkuP6t.js → RBSpinner.stories-9u3_ETbi.js} +1 -1
  87. package/cli/storybook-dist/assets/{RBSwitch.stories-BiW2dQnT.js → RBSwitch.stories-DrVm1WGX.js} +1 -1
  88. package/cli/storybook-dist/assets/{RBTab.stories-DFKVsy0C.js → RBTab.stories-D8Dm7cRW.js} +1 -1
  89. package/cli/storybook-dist/assets/{RBTable.stories-BWG0ZSYI.js → RBTable.stories-dRdZ8g1j.js} +1 -1
  90. package/cli/storybook-dist/assets/{RBTableRow.stories-JVTD83Mh.js → RBTableRow.stories-rZeoGwHZ.js} +1 -1
  91. package/cli/storybook-dist/assets/{RBTabs.stories-D-aN385u.js → RBTabs.stories-STRV2lgO.js} +1 -1
  92. package/cli/storybook-dist/assets/{RBTextArea.stories-Dx5xkmZS.js → RBTextArea.stories-CfgUbrbJ.js} +1 -1
  93. package/cli/storybook-dist/assets/{RBTextField.stories-DmVciExd.js → RBTextField.stories-B5t8-Y97.js} +1 -1
  94. package/cli/storybook-dist/assets/{RBTextInput.stories-CCf0VGEv.js → RBTextInput.stories-Bf1edN6l.js} +1 -1
  95. package/cli/storybook-dist/assets/{RBToast.stories-DT0DGn05.js → RBToast.stories-Cnm8JwQ6.js} +1 -1
  96. package/cli/storybook-dist/assets/{RBToggle.stories-Ce_q3EtP.js → RBToggle.stories-B0OCo6zd.js} +1 -1
  97. package/cli/storybook-dist/assets/{RBToggleGroup.stories-CN40YHsR.js → RBToggleGroup.stories-GdYO2Fxg.js} +1 -1
  98. package/cli/storybook-dist/assets/{RBTooltip.stories-L7btDEpA.js → RBTooltip.stories-DXJTCScO.js} +1 -1
  99. package/cli/storybook-dist/assets/SideNumberInput-CrF-a1Nr.js +1114 -0
  100. package/cli/storybook-dist/assets/browserUtils-CxJ5gea3.js +1 -0
  101. package/cli/storybook-dist/assets/{entry-preview-D_nGAXUk.js → entry-preview-DSRwIaVJ.js} +1 -1
  102. package/cli/storybook-dist/assets/{iframe-23VS_ZeI.js → iframe-Cj53Wn3d.js} +2 -2
  103. package/cli/storybook-dist/assets/{index-ClAc6FBW.js → index-C1OzuK6-.js} +1 -1
  104. package/cli/storybook-dist/assets/{index-Ckae_HWe.js → index-CkHtLEe8.js} +3 -3
  105. package/cli/storybook-dist/assets/preview-CEfMaO1t.css +1 -0
  106. package/cli/storybook-dist/assets/{preview-BVJwlGUF.js → preview-CILRoSnB.js} +1 -1
  107. package/cli/storybook-dist/assets/{preview-CD0LINug.js → preview-CgOtgtCD.js} +2 -2
  108. package/cli/storybook-dist/assets/preview-D_2um8Sd.js +3 -0
  109. package/cli/storybook-dist/assets/react-resizable-panels.browser-CtORL3tG.js +102 -0
  110. package/cli/storybook-dist/iframe.html +1 -1
  111. package/cli/storybook-dist/index.html +1 -1
  112. package/cli/storybook-dist/index.json +1 -1
  113. package/cli/storybook-dist/project.json +1 -1
  114. package/cli/templates/common/README.md +7 -211
  115. package/cli/templates/common/py/main.py +2 -29
  116. package/cli/templates/common/requirements.txt +3 -3
  117. package/cli/templates/cpp/cpp/CMakeLists.txt +7 -6
  118. package/cli/templates/cpp/cpp/main.cpp +10 -57
  119. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +0 -4
  120. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +4 -5
  121. package/package.json +3 -3
  122. package/cli/storybook-dist/assets/RBRangeDateInput.stories-COOQ_HNF.js +0 -15
  123. package/cli/storybook-dist/assets/RBSelectBox.stories-BookV8Sm.js +0 -74
  124. package/cli/storybook-dist/assets/SideNumberInput-B5_xLK8z.js +0 -1109
  125. package/cli/storybook-dist/assets/browserUtils-CQM9Za7e.js +0 -1
  126. package/cli/storybook-dist/assets/preview-P8yWWsME.js +0 -3
  127. package/cli/storybook-dist/assets/preview-zuAvMksL.css +0 -1
  128. package/cli/storybook-dist/assets/react-resizable-panels.browser-CYcsrUgg.js +0 -120
  129. package/cli/templates/readme-cpp/README.md +0 -993
package/cli/README.md CHANGED
@@ -60,10 +60,10 @@ and `50051` are reserved and must not be used by external plugins.
60
60
  | runtime | source | notes |
61
61
  |---|---|---|
62
62
  | `{ "type": "none" }` | — | UI-only plugin; no backend container |
63
- | `{ "type": "python", "entry": "main.py" }` | `py/` | Uses `rainbow.rb_sdk.RBPluginCall` for UI/backend messaging; `plugin.router` exposes external endpoints (`common/plugin/{id}/{path}`, needs sdk ≥ 0.0.9.dev5 on the robot; the dev bridge only registers routes — external calls need a running `common`) |
64
- | `{ "type": "cpp", "entry": "<executable name>" }` | `cpp/` | Built with CMake in Docker against the C++ SDK (apt `rainbow-rb-sdk-dev`, `find_package(rb_sdk)`, dynamic link) |
63
+ | `{ "type": "python", "entry": "main.py" }` | `py/` | Uses `rainbow.rb_sdk.RBPluginCall` for UI/backend messaging |
64
+ | `{ "type": "cpp", "entry": "<executable name>" }` | `cpp/` | Built with CMake in Docker; current template is a run skeleton |
65
65
  | `{ "type": "ros2", "rosClient": "rclpy", ... }` | `ros2_ws/` | ROS2 Python node; may use Python SDK alongside ROS2 APIs |
66
- | `{ "type": "ros2", "rosClient": "rclcpp", ... }` | `ros2_ws/` | ROS2 C++ node built with colcon/CMake, links the C++ SDK like `cpp` |
66
+ | `{ "type": "ros2", "rosClient": "rclcpp", ... }` | `ros2_ws/` | ROS2 C++ node built with colcon/CMake |
67
67
 
68
68
  All non-`none` runtimes run in Docker during `muscat-pk dev:back`, `muscat-pk
69
69
  dev`, and build/export flows so development matches the robot environment.
@@ -257,11 +257,6 @@ would break `dev` offline. Pre-seed an air-gapped machine with
257
257
  `docker save … | gzip` + `docker load`, or force a rebuild with
258
258
  `MUSCAT_PK_REBUILD_IMAGE=1`.
259
259
 
260
- The C++ images (`Dockerfile.cpp`, `Dockerfile.dev`) install `rainbow-rb-sdk-dev`
261
- from `https://rainbowrobotics.github.io/rb-sdk` (suite `dev`), pinned by the
262
- `RB_SDK_VERSION` build arg. Set `MUSCAT_PK_RB_SDK_VERSION=<version>` (with
263
- `MUSCAT_PK_REBUILD_IMAGE=1`) to match a robot running a different SDK version.
264
-
265
260
  On Linux the dev containers run as the invoking user (`--user`), so build output
266
261
  under `dist/` and `cpp/build-*` is not left root-owned.
267
262
 
@@ -35,11 +35,6 @@ function readVersion(binPath) {
35
35
 
36
36
  const isBinaryMode = typeof Bun !== "undefined";
37
37
 
38
- // 바이너리에는 package.json 이 없다 — 컴파일 시 임베드한 버전을 쓴다.
39
- if (isBinaryMode && !process.env.MUSCAT_PK_VERSION) {
40
- process.env.MUSCAT_PK_VERSION = (await import("../src/templates-embedded.mjs")).KIT_VERSION;
41
- }
42
-
43
38
  (function delegateToLocalInstall() {
44
39
  try {
45
40
  const localBin = resolveLocalCliBin();
@@ -2,7 +2,7 @@
2
2
  // Pre-build step: serialise cli/templates/** into cli/src/templates-embedded.mjs
3
3
  // so that `bun build --compile` can embed them without needing the filesystem.
4
4
  import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
5
- import { resolve } from "node:path";
5
+ import { resolve, relative } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
8
  const root = resolve(fileURLToPath(import.meta.url), "..");
@@ -19,15 +19,10 @@ function walk(baseDir, prefix) {
19
19
  }
20
20
  walk(resolve(root, "templates") + "/", "");
21
21
  walk(resolve(root, "docker") + "/", "docker/");
22
- // Dockerfile.bridge 의 COPY 대상
23
- files["src/socketBridge.mjs"] = readFileSync(resolve(root, "src/socketBridge.mjs"), "base64");
24
-
25
- const { version } = JSON.parse(readFileSync(resolve(root, "../package.json"), "utf8"));
26
22
 
27
23
  writeFileSync(out,
28
24
  `// AUTO-GENERATED by cli/bundle-templates.mjs — do not edit.\n` +
29
25
  `// ponytail: generated at publish time; not tracked in git.\n` +
30
- `export const KIT_VERSION = ${JSON.stringify(version)};\n` +
31
26
  `export const TEMPLATES = ${JSON.stringify(files)};\n`
32
27
  );
33
28
  console.log(`bundled ${Object.keys(files).length} template files → src/templates-embedded.mjs`);
@@ -10,34 +10,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
10
10
  cmake \
11
11
  && rm -rf /var/lib/apt/lists/*
12
12
 
13
- # Rainbow Robotics C++ SDK for find_package(rb_sdk).
14
- # Override with MUSCAT_PK_RB_SDK_VERSION=<ver> (+ MUSCAT_PK_REBUILD_IMAGE=1).
15
- ARG RB_SDK_VERSION=0.1.6
16
- RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
17
- && install -d /etc/apt/keyrings \
18
- && curl -fsSL https://rainbowrobotics.github.io/rb-sdk/rb-sdk-apt-repo.gpg.key -o /etc/apt/keyrings/rb-sdk.asc \
19
- && printf 'deb [signed-by=/etc/apt/keyrings/rb-sdk.asc] https://rainbowrobotics.github.io/rb-sdk %s main\n' stable dev > /etc/apt/sources.list.d/rb-sdk.list \
20
- && apt-get update && apt-get install -y --no-install-recommends "rainbow-rb-sdk-dev=${RB_SDK_VERSION}" \
21
- && rm -rf /var/lib/apt/lists/*
22
-
23
- # MediaMTX — same version/ports as the Muscat runtime (rby2 scripts/backend/runtime/mediamtx.yml),
24
- # plus webrtcAdditionalHosts so a browser on the dev host reaches the WebRTC media port.
25
- ARG MEDIAMTX_VERSION=1.12.3
26
- RUN set -eux; \
27
- apt-get update; apt-get install -y --no-install-recommends ca-certificates curl; rm -rf /var/lib/apt/lists/*; \
28
- case "$(uname -m)" in x86_64) a=amd64 ;; aarch64|arm64) a=arm64 ;; *) exit 1 ;; esac; \
29
- curl -fsSL "https://github.com/bluenviron/mediamtx/releases/download/v${MEDIAMTX_VERSION}/mediamtx_v${MEDIAMTX_VERSION}_linux_${a}.tar.gz" \
30
- | tar -xz -C /usr/local/bin mediamtx; \
31
- mkdir -p /etc/mediamtx; \
32
- printf '%s\n' \
33
- 'logLevel: warn' \
34
- 'rtsp: yes' 'rtspAddress: :8554' \
35
- 'webrtc: yes' 'webrtcAddress: :8889' \
36
- 'webrtcLocalUDPAddress: :8189' 'webrtcLocalTCPAddress: :8189' \
37
- 'webrtcAdditionalHosts: [127.0.0.1]' 'webrtcAllowOrigin: "*"' \
38
- 'hls: no' 'rtmp: no' 'srt: no' 'api: no' \
39
- 'paths:' ' all_others:' ' source: publisher' \
40
- > /etc/mediamtx/mediamtx.yml
13
+ # TODO(cpp-sdk): install the rainbow cpp SDK (dev package) here, pinned to the
14
+ # robot's version, so plugins build/link against the same SDK as production.
15
+ # Plan: apt package + dynamic link (the robot provides the .so at runtime;
16
+ # we do NOT bundle it into the plugin binary). e.g.:
17
+ # RUN apt-get update && apt-get install -y rainbow-cpp-sdk-dev=<robot-version> \
18
+ # && rm -rf /var/lib/apt/lists/*
41
19
 
42
20
  WORKDIR /plugin
43
21
 
@@ -9,38 +9,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
9
9
  python3-colcon-common-extensions \
10
10
  && rm -rf /var/lib/apt/lists/*
11
11
 
12
- # Rainbow Robotics C++ SDK for find_package(rb_sdk).
13
- # Override with MUSCAT_PK_RB_SDK_VERSION=<ver> (+ MUSCAT_PK_REBUILD_IMAGE=1).
14
- ARG RB_SDK_VERSION=0.1.6
15
- RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
16
- && install -d /etc/apt/keyrings \
17
- && curl -fsSL https://rainbowrobotics.github.io/rb-sdk/rb-sdk-apt-repo.gpg.key -o /etc/apt/keyrings/rb-sdk.asc \
18
- && printf 'deb [signed-by=/etc/apt/keyrings/rb-sdk.asc] https://rainbowrobotics.github.io/rb-sdk %s main\n' stable dev > /etc/apt/sources.list.d/rb-sdk.list \
19
- && apt-get update && apt-get install -y --no-install-recommends "rainbow-rb-sdk-dev=${RB_SDK_VERSION}" \
20
- && rm -rf /var/lib/apt/lists/*
21
-
22
- # MediaMTX — same version/ports as the Muscat runtime (rby2 scripts/backend/runtime/mediamtx.yml),
23
- # plus webrtcAdditionalHosts so a browser on the dev host reaches the WebRTC media port.
24
- ARG MEDIAMTX_VERSION=1.12.3
25
- RUN set -eux; \
26
- apt-get update; apt-get install -y --no-install-recommends ca-certificates curl; rm -rf /var/lib/apt/lists/*; \
27
- case "$(uname -m)" in x86_64) a=amd64 ;; aarch64|arm64) a=arm64 ;; *) exit 1 ;; esac; \
28
- curl -fsSL "https://github.com/bluenviron/mediamtx/releases/download/v${MEDIAMTX_VERSION}/mediamtx_v${MEDIAMTX_VERSION}_linux_${a}.tar.gz" \
29
- | tar -xz -C /usr/local/bin mediamtx; \
30
- mkdir -p /etc/mediamtx; \
31
- printf '%s\n' \
32
- 'logLevel: warn' \
33
- 'rtsp: yes' 'rtspAddress: :8554' \
34
- 'webrtc: yes' 'webrtcAddress: :8889' \
35
- 'webrtcLocalUDPAddress: :8189' 'webrtcLocalTCPAddress: :8189' \
36
- 'webrtcAdditionalHosts: [127.0.0.1]' 'webrtcAllowOrigin: "*"' \
37
- 'hls: no' 'rtmp: no' 'srt: no' 'api: no' \
38
- 'paths:' ' all_others:' ' source: publisher' \
39
- > /etc/mediamtx/mediamtx.yml
40
-
41
- # `docker exec` skips /ros_entrypoint.sh — source ROS for the login shells dev:back runs.
42
- RUN echo ". /opt/ros/${ROS_DISTRO}/setup.sh" > /etc/profile.d/ros.sh
43
-
44
12
  WORKDIR /plugin
45
13
 
46
14
  CMD ["bash"]
@@ -15,25 +15,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
15
15
  # cpp SDK shared lib must be present for the python module to load. Install the
16
16
  # rainbow cpp SDK (apt, pinned to the robot's version) here once it exists.
17
17
 
18
- # MediaMTX — same version/ports as the Muscat runtime (rby2 scripts/backend/runtime/mediamtx.yml),
19
- # plus webrtcAdditionalHosts so a browser on the dev host reaches the WebRTC media port.
20
- ARG MEDIAMTX_VERSION=1.12.3
21
- RUN set -eux; \
22
- apt-get update; apt-get install -y --no-install-recommends ca-certificates curl; rm -rf /var/lib/apt/lists/*; \
23
- case "$(uname -m)" in x86_64) a=amd64 ;; aarch64|arm64) a=arm64 ;; *) exit 1 ;; esac; \
24
- curl -fsSL "https://github.com/bluenviron/mediamtx/releases/download/v${MEDIAMTX_VERSION}/mediamtx_v${MEDIAMTX_VERSION}_linux_${a}.tar.gz" \
25
- | tar -xz -C /usr/local/bin mediamtx; \
26
- mkdir -p /etc/mediamtx; \
27
- printf '%s\n' \
28
- 'logLevel: warn' \
29
- 'rtsp: yes' 'rtspAddress: :8554' \
30
- 'webrtc: yes' 'webrtcAddress: :8889' \
31
- 'webrtcLocalUDPAddress: :8189' 'webrtcLocalTCPAddress: :8189' \
32
- 'webrtcAdditionalHosts: [127.0.0.1]' 'webrtcAllowOrigin: "*"' \
33
- 'hls: no' 'rtmp: no' 'srt: no' 'api: no' \
34
- 'paths:' ' all_others:' ' source: publisher' \
35
- > /etc/mediamtx/mediamtx.yml
36
-
37
18
  WORKDIR /plugin
38
19
 
39
20
  CMD ["bash"]
@@ -5,41 +5,24 @@
5
5
  // it at localhost:10000 too — no host.docker.internal needed (robust cross-OS).
6
6
  import { spawnSync } from "node:child_process";
7
7
  import { createServer, connect } from "node:net";
8
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { dirname, resolve } from "node:path";
8
+ import { existsSync } from "node:fs";
9
+ import { resolve } from "node:path";
11
10
  import { canSkipImageBuild, hasBin, kitRoot, log, pruneMuskatImages } from "./util.mjs";
12
- import { MEDIA_PORTS, mediaPortFlags } from "./ports.mjs";
13
- import { usesHostNetwork } from "./zenohDev.mjs";
14
11
 
15
12
  export const BRIDGE_IMAGE = "muscat-pk-bridge";
16
13
  export const BRIDGE_CONTAINER = "muscat-pk-bridge-run";
17
14
  export const BRIDGE_PORT = 10000;
18
15
 
19
- // 컴파일된 바이너리에는 cli/ 트리가 없어 임베드 스냅샷에서 build context 를 tmp 에 복원한다.
20
- const BRIDGE_CONTEXT_FILES = ["docker/Dockerfile.bridge", "docker/bridge-package.json", "src/socketBridge.mjs"];
21
-
22
- async function resolveBridgeContext() {
23
- const context = resolve(kitRoot, "cli");
24
- const dockerfile = resolve(context, "docker/Dockerfile.bridge");
25
- if (existsSync(dockerfile)) return { dockerfile, context };
26
-
27
- const { TEMPLATES } = await import("./templates-embedded.mjs");
28
- const dir = resolve(tmpdir(), "muscat-pk-bridge-context");
29
- for (const key of BRIDGE_CONTEXT_FILES) {
30
- const b64 = TEMPLATES[key];
31
- if (!b64) throw new Error(`Embedded file missing: ${key}. Reinstall muscat-plugin-kit.`);
32
- const file = resolve(dir, key);
33
- mkdirSync(dirname(file), { recursive: true });
34
- writeFileSync(file, Buffer.from(b64, "base64"));
35
- }
36
- return { dockerfile: resolve(dir, "docker/Dockerfile.bridge"), context: dir };
37
- }
16
+ const DOCKERFILE = resolve(kitRoot, "cli/docker/Dockerfile.bridge");
17
+ const BUILD_CONTEXT = resolve(kitRoot, "cli");
38
18
 
39
19
  function assertDockerReady() {
40
20
  if (!hasBin("docker")) {
41
21
  throw new Error("Docker not found. `muscat-pk dev` runs the Socket.IO bridge in a container.");
42
22
  }
23
+ if (!existsSync(DOCKERFILE)) {
24
+ throw new Error(`kit Dockerfile missing at ${DOCKERFILE} (reinstall muscat-plugin-kit).`);
25
+ }
43
26
  }
44
27
 
45
28
  // Is a host TCP port already bound? (Catches another bridge or a stray process.)
@@ -69,28 +52,20 @@ function waitForPort(port, timeoutMs = 15_000) {
69
52
  });
70
53
  }
71
54
 
72
- async function ensureImage() {
55
+ function ensureImage() {
73
56
  if (canSkipImageBuild(BRIDGE_IMAGE)) {
74
57
  log.step(`${BRIDGE_IMAGE} already built — skipping docker build`);
75
58
  return;
76
59
  }
77
- const { dockerfile, context } = await resolveBridgeContext();
78
60
  log.step(`docker build -t ${BRIDGE_IMAGE} (first run is slow, then cached)…`);
79
- const r = spawnSync("docker", ["build", "-t", BRIDGE_IMAGE, "-f", dockerfile, context], { stdio: "inherit" });
61
+ const r = spawnSync("docker", ["build", "-t", BRIDGE_IMAGE, "-f", DOCKERFILE, BUILD_CONTEXT], { stdio: "inherit" });
80
62
  if (r.status !== 0) throw new Error("docker build failed (bridge image).");
81
63
  pruneMuskatImages();
82
64
  }
83
65
 
84
- /** A running Muscat container (e.g. muscat-dev) that owns host port 10000, or null. */
85
- export function localMuscatContainer() {
86
- const r = spawnSync("docker", ["ps", "--filter", `publish=${BRIDGE_PORT}`, "--format", "{{.Names}}"], { encoding: "utf8" });
87
- return (r.stdout || "").split("\n").map((n) => n.trim()).find((n) => n && n !== BRIDGE_CONTAINER) ?? null;
88
- }
89
-
90
66
  /**
91
67
  * Build the image, preflight the port, and start the bridge container.
92
- * Returns false if port 10000 was already in use (caller decides), else { media }:
93
- * whether the MediaMTX ports are free for the backend's own MediaMTX.
68
+ * Returns true if bridge started, false if port was already in use (caller decides).
94
69
  */
95
70
  export async function startBridge({ zenohPort = null, pluginPorts = [] } = {}) {
96
71
  assertDockerReady();
@@ -113,14 +88,7 @@ export async function startBridge({ zenohPort = null, pluginPorts = [] } = {}) {
113
88
  }
114
89
  const publishable = pluginPorts.filter((port) => !busy.includes(port));
115
90
 
116
- const mediaBusy = [];
117
- for (const port of MEDIA_PORTS) {
118
- if (await isPortInUse(port)) mediaBusy.push(port);
119
- }
120
- const media = mediaBusy.length === 0;
121
- if (!media) log.warn(`MediaMTX port(s) already in use: ${mediaBusy.join(", ")} — video streaming disabled in dev.`);
122
-
123
- await ensureImage();
91
+ ensureImage();
124
92
 
125
93
  log.step("starting Socket.IO bridge container…");
126
94
  // The backend joins THIS container's netns (--network container:), and a
@@ -130,7 +98,6 @@ export async function startBridge({ zenohPort = null, pluginPorts = [] } = {}) {
130
98
  "-p", `127.0.0.1:${BRIDGE_PORT}:${BRIDGE_PORT}`,
131
99
  ...(zenohPort ? ["-p", `${zenohPort}:${zenohPort}`] : []),
132
100
  ...publishable.flatMap((port) => ["-p", `127.0.0.1:${port}:${port}`]),
133
- ...(media && !usesHostNetwork() ? mediaPortFlags() : []),
134
101
  ];
135
102
  const up = spawnSync(
136
103
  "docker",
@@ -144,7 +111,7 @@ export async function startBridge({ zenohPort = null, pluginPorts = [] } = {}) {
144
111
  if (zenohPort) log.step(`Zenoh dev peer port published on :${zenohPort}`);
145
112
  if (publishable.length > 0)
146
113
  log.step(`plugin backend ports published on 127.0.0.1: ${publishable.join(", ")}`);
147
- return { media };
114
+ return true;
148
115
  }
149
116
 
150
117
  export function stopBridge() {
package/cli/src/build.mjs CHANGED
@@ -74,36 +74,34 @@ function runViteBuild(pluginRoot, { mode = "production" } = {}) {
74
74
  // For ros2 plugins, compile the colcon workspace inside the Jazzy + Python 3.12 container.
75
75
  // rclpy: arch-agnostic (pure Python) → dist/ros2_ws/install/ (built once)
76
76
  // rclcpp: arch-specific C++ binaries → dist/ros2_ws/install-{arch}/ per arch
77
- // Cross-arch C++ builds run inside an emulated container. Docker Desktop ships the
78
- // emulation; native Linux needs binfmt/QEMU — check up front so the error says so.
79
- function assertCanBuildArch(arch, dockerPlatform) {
80
- const info = spawnSync("docker", ["info", "--format", "{{.Architecture}}"], { encoding: "utf8" });
81
- const host = { x86_64: "amd64", aarch64: "arm64", arm64: "arm64" }[(info.stdout || "").trim()] ?? "";
82
- if (arch === host) return;
83
- const r = spawnSync("docker", ["run", "--rm", "--platform", dockerPlatform, "ubuntu:24.04", "true"], { stdio: "ignore" });
84
- if (r.status === 0) return;
85
- throw new Error(
86
- `can't build ${arch} on this ${host || "machine"}: Docker has no ${arch} emulation.\n` +
87
- ` enable it once: docker run --privileged --rm tonistiigi/binfmt --install ${arch}\n` +
88
- ` or build only this machine's arch: muscat-pk build --arch ${host || "<arch>"}`,
89
- );
90
- }
91
-
92
77
  export async function exportRos2Build(root, archs = ["amd64", "arm64"]) {
93
- if (manifestRuntime(root) !== "ros2") return;
78
+ if (manifestRuntime(root) !== "ros2") return [];
94
79
  const { assertDockerReady, ensureImage, colconBuild, ROS2_DEV_IMAGE, ARCH_DOCKER_PLATFORM } = await import("./ros2.mjs");
95
- assertDockerReady();
80
+ try {
81
+ assertDockerReady();
82
+ } catch (e) {
83
+ log.warn(`ros2 build skipped: ${e.message}`);
84
+ return [];
85
+ }
96
86
 
97
87
  const manifest = JSON.parse(readFileSync(resolve(root, "manifest.json"), "utf8"));
98
- if (manifest.runtime?.rosClient === "rclcpp") {
88
+ const rosClient = manifest.runtime?.rosClient;
89
+
90
+ const skipped = [];
91
+ if (rosClient === "rclcpp") {
99
92
  // C++ ROS2: compile per arch so each robot picks the right install-{arch}/
100
93
  for (const arch of archs) {
101
94
  const dockerPlatform = ARCH_DOCKER_PLATFORM[arch];
102
- if (!dockerPlatform) throw new Error(`unknown arch '${arch}'`);
103
- assertCanBuildArch(arch, dockerPlatform);
104
- await ensureImage(`${ROS2_DEV_IMAGE}-${arch}`, { platform: dockerPlatform });
105
- colconBuild(root, { symlink: false, arch });
106
- log.ok(`dist/ros2_ws/install-${arch}/ ready`);
95
+ if (!dockerPlatform) { log.warn(`unknown arch '${arch}' — skipped`); skipped.push(arch); continue; }
96
+ const archImage = `${ROS2_DEV_IMAGE}-${arch}`;
97
+ try {
98
+ await ensureImage(archImage, { platform: dockerPlatform });
99
+ colconBuild(root, { symlink: false, arch });
100
+ log.ok(`dist/ros2_ws/install-${arch}/ ready`);
101
+ } catch {
102
+ log.warn(`${arch} ros2 build skipped — QEMU not available`);
103
+ skipped.push(arch);
104
+ }
107
105
  }
108
106
  } else {
109
107
  // rclpy / pure Python: arch-agnostic, build once
@@ -111,38 +109,54 @@ export async function exportRos2Build(root, archs = ["amd64", "arm64"]) {
111
109
  colconBuild(root, { symlink: false });
112
110
  log.ok("dist/ros2_ws/install/ ready");
113
111
  }
112
+ return skipped;
114
113
  }
115
114
 
116
115
  // For cpp plugins, compile cpp/ inside the Ubuntu container (Release) per arch
117
116
  // and copy each binary into dist/{arch}/{entry} so the ZIP carries relocatable
118
- // robot-compatible executables.
117
+ // robot-compatible executables. rb-runtime prefers dist/{arch}/ and falls back
118
+ // to dist/{entry} (legacy). Skipped (with a warning) if Docker is unavailable.
119
119
  export async function exportCppBuild(root, distDir, archs = ["amd64", "arm64"]) {
120
- if (manifestRuntime(root) !== "cpp") return;
120
+ if (manifestRuntime(root) !== "cpp") return [];
121
121
  const { assertDockerReady, ensureImage, cmakeBuild, CPP_DEV_IMAGE, ARCH_DOCKER_PLATFORM } = await import("./cpp.mjs");
122
- assertDockerReady();
122
+ try {
123
+ assertDockerReady();
124
+ } catch (e) {
125
+ log.warn(`cpp build skipped: ${e.message}`);
126
+ return [];
127
+ }
123
128
 
124
129
  const entry = JSON.parse(readFileSync(resolve(root, "manifest.json"), "utf8")).runtime?.entry;
125
- if (!entry) throw new Error("manifest.json runtime.entry is required for cpp (the built executable name)");
130
+ if (!entry) {
131
+ log.warn("manifest.json runtime.entry not set — cpp build skipped");
132
+ return;
133
+ }
126
134
 
135
+ const skipped = [];
127
136
  for (const arch of archs) {
128
137
  const dockerPlatform = ARCH_DOCKER_PLATFORM[arch];
129
- if (!dockerPlatform) throw new Error(`unknown arch '${arch}'`);
130
- assertCanBuildArch(arch, dockerPlatform);
131
- await ensureImage(`${CPP_DEV_IMAGE}-${arch}`, { platform: dockerPlatform });
132
- cmakeBuild(root, { buildType: "Release", arch });
133
- const built = resolve(root, `cpp/build-${arch}`, entry);
134
- if (!existsSync(built)) {
135
- throw new Error(`cpp/build-${arch}/${entry} not found — the CMake target name must equal runtime.entry`);
138
+ if (!dockerPlatform) { log.warn(`unknown arch '${arch}' — skipped`); skipped.push(arch); continue; }
139
+ const archImage = `${CPP_DEV_IMAGE}-${arch}`;
140
+ try {
141
+ await ensureImage(archImage, { platform: dockerPlatform });
142
+ cmakeBuild(root, { buildType: "Release", arch });
143
+ const built = resolve(root, `cpp/build-${arch}`, entry);
144
+ if (existsSync(built)) {
145
+ const archDir = resolve(distDir, arch);
146
+ mkdirSync(archDir, { recursive: true });
147
+ const dest = resolve(archDir, entry);
148
+ copyFileSync(built, dest);
149
+ chmodSync(dest, 0o755);
150
+ log.ok(`cpp binary → dist/${arch}/${entry}`);
151
+ } else {
152
+ log.warn(`cpp binary not found at cpp/build-${arch}/${entry}`);
153
+ }
154
+ } catch {
155
+ log.warn(`${arch} cpp build skipped — QEMU not available`);
156
+ skipped.push(arch);
136
157
  }
137
- const archDir = resolve(distDir, arch);
138
- mkdirSync(archDir, { recursive: true });
139
- const dest = resolve(archDir, entry);
140
- copyFileSync(built, dest);
141
- chmodSync(dest, 0o755);
142
- const libDir = resolve(root, `cpp/build-${arch}`, "lib");
143
- if (existsSync(libDir)) cpSync(libDir, resolve(archDir, "lib"), { recursive: true, verbatimSymlinks: true });
144
- log.ok(`cpp binary → dist/${arch}/${entry}${existsSync(libDir) ? " (+ lib/)" : ""}`);
145
158
  }
159
+ return skipped;
146
160
  }
147
161
 
148
162
  const PRERELEASE_RE = /\.(dev|a|b|rc|alpha|beta)\d*/i;
@@ -412,12 +426,11 @@ export async function runBuild(args) {
412
426
 
413
427
  await promptVersion(root);
414
428
  const archs = await promptArch(args);
415
- // Drop a previous ZIP of this version so a failed build can't leave a stale one to upload.
416
- const version = JSON.parse(readFileSync(resolve(root, "manifest.json"), "utf8")).version || "0.0.0";
417
- rmSync(resolve(root, `${basename(root)}-${version}.zip`), { force: true });
418
429
 
419
430
  const { pluginRoot, distDir } = await buildPlugin({ root, minify: true });
420
431
 
432
+ const skippedArches = { wheels: [], ros2: [], cpp: [] };
433
+
421
434
  // python / rclpy: lock + wheel vendor in Ubuntu 24.04 container
422
435
  if (!noLock) {
423
436
  const manifest = JSON.parse(readFileSync(resolve(root, "manifest.json"), "utf8")).runtime ?? {};
@@ -426,12 +439,30 @@ export async function runBuild(args) {
426
439
  (manifest.type === "ros2" && manifest.rosClient === "rclpy");
427
440
  if (needsLock) {
428
441
  const { lockAndVendor } = await import("./python.mjs");
429
- await lockAndVendor(pluginRoot, archs);
442
+ try {
443
+ skippedArches.wheels = await lockAndVendor(pluginRoot, archs);
444
+ } catch (e) {
445
+ log.warn(`python lock/vendor skipped: ${e.message}`);
446
+ skippedArches.wheels = archs;
447
+ }
430
448
  }
431
449
  }
432
450
 
433
- await exportRos2Build(root, archs);
434
- await exportCppBuild(root, distDir, archs);
451
+ skippedArches.ros2 = await exportRos2Build(root, archs) ?? [];
452
+ skippedArches.cpp = await exportCppBuild(root, distDir, archs) ?? [];
435
453
  calcInstalledSize(pluginRoot);
436
454
  if (!noZip) packZip(pluginRoot);
455
+
456
+ // Build summary
457
+ const allSkipped = [...new Set([...skippedArches.wheels, ...skippedArches.ros2, ...skippedArches.cpp])];
458
+ if (allSkipped.length > 0) {
459
+ const line = "─".repeat(58);
460
+ console.log(`\n${line}`);
461
+ log.warn(`Cross-arch builds skipped (QEMU not available): ${allSkipped.join(", ")}`);
462
+ if (skippedArches.wheels.length) log.warn(` wheels: ${skippedArches.wheels.join(", ")}`);
463
+ if (skippedArches.ros2.length) log.warn(` ros2: ${skippedArches.ros2.join(", ")}`);
464
+ if (skippedArches.cpp.length) log.warn(` cpp: ${skippedArches.cpp.join(", ")}`);
465
+ log.warn(` Enable: docker run --privileged --rm tonistiigi/binfmt --install all`);
466
+ console.log(line);
467
+ }
437
468
  }
package/cli/src/cpp.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // Used by dev:back (interactive dev loop) and build (export the binary into the ZIP).
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { dirname } from "node:path";
6
- import { canSkipImageBuild, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile } from "./util.mjs";
6
+ import { canSkipImageBuild, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile } from "./util.mjs";
7
7
 
8
8
  export const CPP_DEV_IMAGE = "muscat-pk-cpp-dev";
9
9
 
@@ -15,19 +15,16 @@ export function assertDockerReady() {
15
15
 
16
16
  /** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
17
17
  export async function ensureImage(image = CPP_DEV_IMAGE, { platform = null } = {}) {
18
- const dockerfile = await resolveDockerfile("Dockerfile.cpp");
19
- const sdkVer = process.env.MUSCAT_PK_RB_SDK_VERSION;
20
- const key = imageKey(dockerfile, sdkVer ?? "");
21
- if (canSkipImageBuild(image, key)) {
18
+ if (canSkipImageBuild(image)) {
22
19
  log.step(`${image} already built — skipping docker build`);
23
20
  return;
24
21
  }
22
+ const dockerfile = await resolveDockerfile("Dockerfile.cpp");
25
23
  log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
26
24
  const platformFlags = platform ? ["--platform", platform] : [];
27
- const buildArgs = [...(sdkVer ? ["--build-arg", `RB_SDK_VERSION=${sdkVer}`] : []), ...imageKeyLabel(key)];
28
25
  const r = spawnSync(
29
26
  "docker",
30
- ["build", ...platformFlags, ...buildArgs, "-t", image, "-f", dockerfile, dirname(dockerfile)],
27
+ ["build", ...platformFlags, "-t", image, "-f", dockerfile, dirname(dockerfile)],
31
28
  { stdio: "inherit" },
32
29
  );
33
30
  if (r.status !== 0) throw new Error("docker build failed.");
@@ -53,12 +50,9 @@ export function cmakeBuild(root, { image = CPP_DEV_IMAGE, buildType = "Debug", a
53
50
  const platformFlags = dockerPlatform ? ["--platform", dockerPlatform] : [];
54
51
  // arch 빌드 시 arch별 이미지 사용 (로컬 이미지와 platform 불일치 방지)
55
52
  const actualImage = arch ? `${CPP_DEV_IMAGE}-${arch}` : image;
56
- // Export builds ship lib/ next to the binary: librb_sdk.so (robot has no SDK) + any .so the
57
- // plugin copied there itself. Found via RPATH $ORIGIN/lib.
58
53
  const script = [
59
- `cmake -S cpp -B ${buildDir} -DCMAKE_BUILD_TYPE=${buildType}` + (arch ? ` -DCMAKE_BUILD_RPATH='$ORIGIN/lib'` : ""),
54
+ `cmake -S cpp -B ${buildDir} -DCMAKE_BUILD_TYPE=${buildType}`,
60
55
  `cmake --build ${buildDir} -j`,
61
- ...(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`] : []),
62
56
  ].join(" && ");
63
57
  const label = arch ? `cmake build (${buildType}, ${arch}, in container)…` : `cmake build (${buildType}, in container)…`;
64
58
  log.step(label);
package/cli/src/dev.mjs CHANGED
@@ -6,7 +6,7 @@ import { resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { runDevUi } from "./devUi.mjs";
8
8
  import { runDevBack, stopDevBackContainers } from "./devBack.mjs";
9
- import { localMuscatContainer, startBridge, stopBridge, BRIDGE_CONTAINER, BRIDGE_PORT } from "./bridge.mjs";
9
+ import { startBridge, stopBridge, BRIDGE_CONTAINER, BRIDGE_PORT } from "./bridge.mjs";
10
10
  import { assertManifestPorts, assertManifestVariables } from "./manifest.mjs";
11
11
  import { formatPortRanges } from "./ports.mjs";
12
12
  import {
@@ -138,11 +138,7 @@ export async function runDev(args = []) {
138
138
  pluginPorts: usesHostNetwork() ? [] : portRanges,
139
139
  });
140
140
 
141
- const muscat = bridgeStarted ? null : localMuscatContainer();
142
- if (muscat) {
143
- log.step(`local Muscat container '${muscat}' owns :${BRIDGE_PORT} — backend joins its network (gateway, MediaMTX)`);
144
- process.env.MUSCAT_PK_MUSCAT_CONTAINER = muscat;
145
- } else if (!bridgeStarted) {
141
+ if (!bridgeStarted) {
146
142
  log.warn(`port ${BRIDGE_PORT} is already in use — can't start Socket.IO bridge.`);
147
143
  const answer = await prompt(" Proceed without Socket.IO bridge? [y/N]: ");
148
144
  if (answer.toLowerCase() !== "y") return;
@@ -151,7 +147,6 @@ export async function runDev(args = []) {
151
147
  // Tell dev:back which bridge container to share a netns with, so the backend
152
148
  // reaches the bridge at localhost:10000 (no host.docker.internal).
153
149
  process.env.MUSCAT_PK_BRIDGE_CONTAINER = BRIDGE_CONTAINER;
154
- if (bridgeStarted.media) process.env.MUSCAT_PK_MEDIA = "1";
155
150
  }
156
151
 
157
152
  process.env.MUSCAT_PK_MANAGED_DEV = "1";