@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.3 → 0.1.2-dev.31

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 (48) hide show
  1. package/README.md +95 -16
  2. package/cli/README.md +4 -5
  3. package/cli/bin/create-muscat-plugin.mjs +1 -1
  4. package/cli/bin/muscat-pk.mjs +16 -10
  5. package/cli/docker/Dockerfile.bridge +17 -0
  6. package/cli/docker/Dockerfile.cpp +22 -0
  7. package/cli/docker/Dockerfile.dev +14 -0
  8. package/cli/docker/Dockerfile.python +20 -0
  9. package/cli/docker/bridge-package.json +8 -0
  10. package/cli/src/bridge.mjs +92 -0
  11. package/cli/src/build.mjs +406 -36
  12. package/cli/src/check-package.mjs +2 -1
  13. package/cli/src/cpp.mjs +65 -0
  14. package/cli/src/dev.mjs +16 -3
  15. package/cli/src/devBack.mjs +246 -49
  16. package/cli/src/devUi.mjs +118 -15
  17. package/cli/src/esbuild.mjs +51 -10
  18. package/cli/src/manifest.mjs +189 -0
  19. package/cli/src/new.mjs +152 -27
  20. package/cli/src/preview.mjs +91 -0
  21. package/cli/src/python.mjs +112 -0
  22. package/cli/src/ros2.mjs +66 -0
  23. package/cli/src/socketBridge.mjs +129 -0
  24. package/cli/src/type.mjs +114 -0
  25. package/cli/src/update.mjs +267 -30
  26. package/cli/src/util.mjs +65 -4
  27. package/cli/templates/common/README.md +382 -9
  28. package/cli/templates/common/manifest.json +13 -13
  29. package/cli/templates/common/package.json +5 -11
  30. package/cli/templates/common/py/main.py +88 -0
  31. package/cli/templates/common/requirements.txt +4 -0
  32. package/cli/templates/common/tsconfig.json +1 -1
  33. package/cli/templates/common/{src → ui}/content.tsx +3 -2
  34. package/cli/templates/common/vite.config.ts +22 -13
  35. package/cli/templates/cpp/cpp/CMakeLists.txt +14 -0
  36. package/cli/templates/cpp/cpp/main.cpp +25 -0
  37. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/__init__.py +0 -0
  38. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +25 -0
  39. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/package.xml +10 -0
  40. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/resource/__PLUGIN_SLUG__ +0 -0
  41. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/setup.py +18 -0
  42. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +17 -0
  43. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -0
  44. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +22 -0
  45. package/package.json +4 -4
  46. package/cli/src/pack.mjs +0 -58
  47. package/cli/templates/sdk/main.py +0 -21
  48. package/cli/templates/sdk/requirements.txt +0 -1
@@ -1,29 +1,38 @@
1
- import { defineConfig } from "vite";
1
+ import { readdirSync } from "fs";
2
+ import { basename, extname, resolve } from "path";
2
3
  import react from "@vitejs/plugin-react";
4
+ import { defineConfig } from "vite";
3
5
 
4
6
  // Plugin UI and React are provided by Muscat/preview-host via import map.
5
7
  // They must NOT be bundled into the plugin ZIP.
6
8
  const HOST_PROVIDED_EXTERNALS = [
7
- "@rainbow-robotics/plugin-ui",
8
- /^@rainbow\/plugin-ui\//,
9
- "react",
10
- "react/jsx-runtime",
11
- "react/jsx-dev-runtime",
12
- "react-dom",
13
- /^react-dom\//,
9
+ /^@rainbow-robotics\/plugin-ui(\/|$)/,
10
+ /^react(\/|$)/,
11
+ /^react-dom(\/|$)/,
14
12
  ];
15
13
 
14
+ // All top-level .tsx/.ts files in ui/ become separate entry points.
15
+ // Subdirectories (components/, hooks/, etc.) are imported by entries and bundled in.
16
+ const uiEntries = Object.fromEntries(
17
+ readdirSync("ui")
18
+ .filter((f) => /\.(tsx?|jsx?)$/.test(f))
19
+ .map((f) => [basename(f, extname(f)), resolve("ui", f)]),
20
+ );
21
+
16
22
  export default defineConfig({
17
23
  plugins: [react()],
18
24
  build: {
19
- lib: {
20
- entry: "src/content.tsx",
21
- formats: ["es"],
22
- fileName: "content",
23
- },
24
25
  outDir: "dist",
26
+ emptyOutDir: false,
25
27
  rollupOptions: {
28
+ input: uiEntries,
26
29
  external: HOST_PROVIDED_EXTERNALS,
30
+ output: {
31
+ format: "es",
32
+ entryFileNames: "[name].js",
33
+ chunkFileNames: "chunks/[name]-[hash].js",
34
+ assetFileNames: "assets/[name]-[hash][extname]",
35
+ },
27
36
  },
28
37
  },
29
38
  });
@@ -0,0 +1,14 @@
1
+ cmake_minimum_required(VERSION 3.16)
2
+ project(__PLUGIN_SLUG__ CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ # The executable name must match runtime.entry in manifest.json.
8
+ add_executable(__PLUGIN_SLUG__ main.cpp)
9
+
10
+ # TODO(cpp-sdk): link the rainbow cpp SDK dynamically (the robot provides the
11
+ # .so at runtime — do NOT bundle/static-link it). Once the SDK ships an apt
12
+ # package with CMake config, enable:
13
+ # find_package(rainbow_cpp_sdk REQUIRED)
14
+ # target_link_libraries(__PLUGIN_SLUG__ PRIVATE rainbow::cpp_sdk)
@@ -0,0 +1,25 @@
1
+ // __PLUGIN_NAME__ — C++ plugin back end.
2
+ //
3
+ // The C++ plugin SDK is not available yet, so this is a build/run skeleton.
4
+ // Once the SDK lands, connect to the plugin gateway here (the host exposes it
5
+ // at the RB_PLUGIN_GATEWAY / RB_PLUGIN_ID environment variables) and wire up
6
+ // pub / sub / serve, mirroring the Python rainbow.rb_sdk.RBPluginCall API.
7
+
8
+ #include <chrono>
9
+ #include <cstdlib>
10
+ #include <iostream>
11
+ #include <thread>
12
+
13
+ int main() {
14
+ const char* plugin_id = std::getenv("RB_PLUGIN_ID");
15
+ std::cout << "[__PLUGIN_NAME__] backend ready"
16
+ << (plugin_id ? std::string(" (id=") + plugin_id + ")" : "")
17
+ << std::endl;
18
+
19
+ // TODO: connect to the plugin gateway and register handlers once the
20
+ // C++ SDK is available. For now, just keep the process alive.
21
+ while (true) {
22
+ std::this_thread::sleep_for(std::chrono::seconds(1));
23
+ }
24
+ return 0;
25
+ }
@@ -0,0 +1,25 @@
1
+ import rclpy
2
+ from rclpy.node import Node
3
+ from rainbow.rb_sdk import RBManipulateSDK # PyPI: rainbow-rb-sdk
4
+
5
+
6
+ sdk = RBManipulateSDK()
7
+
8
+
9
+ class __PLUGIN_CLASS__Node(Node):
10
+ def __init__(self):
11
+ super().__init__("__PLUGIN_SLUG___node")
12
+ self.get_logger().info("__PLUGIN_NAME__ node started")
13
+ # sdk is available here — use it alongside ROS2 topics as needed
14
+
15
+
16
+ def main(args=None):
17
+ rclpy.init(args=args)
18
+ node = __PLUGIN_CLASS__Node()
19
+ rclpy.spin(node)
20
+ node.destroy_node()
21
+ rclpy.shutdown()
22
+
23
+
24
+ if __name__ == "__main__":
25
+ main()
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0"?>
2
+ <package format="3">
3
+ <name>__PLUGIN_SLUG__</name>
4
+ <version>0.0.1</version>
5
+ <description>__PLUGIN_NAME__ ROS2 node</description>
6
+ <maintainer email="todo@example.com">maintainer</maintainer>
7
+ <license>Proprietary</license>
8
+ <depend>rclpy</depend>
9
+ <export><build_type>ament_python</build_type></export>
10
+ </package>
@@ -0,0 +1,18 @@
1
+ from setuptools import setup
2
+
3
+ package_name = "__PLUGIN_SLUG__"
4
+
5
+ setup(
6
+ name=package_name,
7
+ version="0.0.1",
8
+ packages=[package_name],
9
+ data_files=[
10
+ ("share/ament_index/resource_index/packages", ["resource/" + package_name]),
11
+ ("share/" + package_name, ["package.xml"]),
12
+ ],
13
+ install_requires=["setuptools"],
14
+ zip_safe=True,
15
+ entry_points={
16
+ "console_scripts": ["__PLUGIN_SLUG___node = __PLUGIN_SLUG__.node:main"],
17
+ },
18
+ )
@@ -0,0 +1,17 @@
1
+ cmake_minimum_required(VERSION 3.16)
2
+ project(__PLUGIN_SLUG__)
3
+
4
+ if(NOT CMAKE_CXX_STANDARD)
5
+ set(CMAKE_CXX_STANDARD 17)
6
+ endif()
7
+
8
+ find_package(ament_cmake REQUIRED)
9
+ find_package(rclcpp REQUIRED)
10
+
11
+ add_executable(__PLUGIN_SLUG___node src/node.cpp)
12
+ ament_target_dependencies(__PLUGIN_SLUG___node rclcpp)
13
+
14
+ install(TARGETS __PLUGIN_SLUG___node
15
+ DESTINATION lib/${PROJECT_NAME})
16
+
17
+ ament_package()
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0"?>
2
+ <package format="3">
3
+ <name>__PLUGIN_SLUG__</name>
4
+ <version>0.0.1</version>
5
+ <description>__PLUGIN_NAME__ ROS2 node (rclcpp)</description>
6
+ <maintainer email="todo@example.com">maintainer</maintainer>
7
+ <license>Proprietary</license>
8
+ <buildtool_depend>ament_cmake</buildtool_depend>
9
+ <depend>rclcpp</depend>
10
+ <export><build_type>ament_cmake</build_type></export>
11
+ </package>
@@ -0,0 +1,22 @@
1
+ // __PLUGIN_NAME__ — ROS2 node (rclcpp).
2
+ //
3
+ // The node is launched with `--ros-args -r __ns:=/{slug}` automatically by
4
+ // dev:back. ROS2 topics are bridged to the plugin communication layer, so a
5
+ // plain rclcpp node is all you need — relative topic names get the namespace
6
+ // prefix applied automatically.
7
+ #include "rclcpp/rclcpp.hpp"
8
+
9
+ class __PLUGIN_CLASS__Node : public rclcpp::Node {
10
+ public:
11
+ __PLUGIN_CLASS__Node() : Node("__PLUGIN_SLUG___node") {
12
+ RCLCPP_INFO(this->get_logger(), "__PLUGIN_NAME__ node started");
13
+ // create publishers / subscribers / services here
14
+ }
15
+ };
16
+
17
+ int main(int argc, char** argv) {
18
+ rclcpp::init(argc, argv);
19
+ rclcpp::spin(std::make_shared<__PLUGIN_CLASS__Node>());
20
+ rclcpp::shutdown();
21
+ return 0;
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-robotics/muscat-plugin-kit",
3
- "version": "0.1.2-dev.3",
3
+ "version": "0.1.2-dev.31",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -31,10 +31,10 @@
31
31
  "@types/react-dom": "^19.0.4"
32
32
  },
33
33
  "dependencies": {
34
- "@rainbow-robotics/preview-host": "*",
35
- "esbuild": "^0.25.0"
34
+ "@rainbow-robotics/preview-host": "0.1.2-dev.31",
35
+ "socket.io": "^4.8.1"
36
36
  },
37
37
  "optionalDependencies": {
38
38
  "esbuild-wasm": "^0.25.0"
39
39
  }
40
- }
40
+ }
package/cli/src/pack.mjs DELETED
@@ -1,58 +0,0 @@
1
- // muscat-pk pack — build, then create a distributable ZIP with a size report.
2
- import { execSync } from "node:child_process";
3
- import { existsSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
4
- import { basename, resolve } from "node:path";
5
- import { buildPlugin } from "./build.mjs";
6
- import { findPluginRoot, humanSize, log } from "./util.mjs";
7
-
8
- function dirSize(dir) {
9
- let total = 0;
10
- for (const entry of readdirSync(dir)) {
11
- const full = resolve(dir, entry);
12
- const st = statSync(full);
13
- total += st.isDirectory() ? dirSize(full) : st.size;
14
- }
15
- return total;
16
- }
17
-
18
- export async function runPack() {
19
- const pluginRoot = findPluginRoot();
20
- await buildPlugin({ root: pluginRoot, minify: true });
21
-
22
- const manifest = JSON.parse(
23
- readFileSync(resolve(pluginRoot, "manifest.json"), "utf8"),
24
- );
25
- const zipName = `${basename(pluginRoot)}-${manifest.version || "0.0.0"}.zip`;
26
- const zipPath = resolve(pluginRoot, zipName);
27
- rmSync(zipPath, { force: true });
28
-
29
- // Contents: dist/ + back-end files if present.
30
- const entries = ["dist"];
31
- for (const f of ["main.py", "requirements.txt"]) {
32
- if (existsSync(resolve(pluginRoot, f))) entries.push(f);
33
- }
34
- if (existsSync(resolve(pluginRoot, "ros2_ws/install"))) {
35
- entries.push("ros2_ws/install");
36
- }
37
-
38
- log.step(`packing ${entries.join(", ")} → ${zipName}`);
39
- try {
40
- execSync(`zip -r -q "${zipPath}" ${entries.map((e) => `"${e}"`).join(" ")}`, {
41
- cwd: pluginRoot,
42
- stdio: "inherit",
43
- });
44
- } catch {
45
- throw new Error("`zip` not found. Install zip or pack manually.");
46
- }
47
-
48
- const zipBytes = statSync(zipPath).size;
49
- log.ok(`${zipName} ${humanSize(zipBytes)}`);
50
- for (const e of entries) {
51
- const full = resolve(pluginRoot, e);
52
- const size = statSync(full).isDirectory() ? dirSize(full) : statSync(full).size;
53
- log.info(` ├─ ${e} ${humanSize(size)}`);
54
- }
55
- log.warn(
56
- "robot install checks free space via GET /plugin/capacity (507 if low).",
57
- );
58
- }
@@ -1,21 +0,0 @@
1
- import asyncio
2
-
3
- from rb_sdk import RBManipulateSDK # PyPI: rainbow-rb-sdk
4
-
5
- sdk = RBManipulateSDK()
6
-
7
-
8
- async def on_action(payload: dict):
9
- # Matches manifest.actions[].topic — handle the "grip" action here.
10
- width = payload.get("width")
11
- print(f"[__PLUGIN_NAME__] grip width={width}")
12
- # TODO: drive the robot via the SDK.
13
-
14
-
15
- async def main():
16
- await sdk.subscribe("rby2/plugin/__PLUGIN_ID__/grip", on_action)
17
- await sdk.spin()
18
-
19
-
20
- if __name__ == "__main__":
21
- asyncio.run(main())
@@ -1 +0,0 @@
1
- rainbow-rb-sdk==__SDK_VERSION__