@rainbow-robotics/muscat-plugin-kit 0.1.2-dev.3 → 0.1.2-dev.30
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.
- package/README.md +95 -16
- package/cli/README.md +4 -5
- package/cli/bin/create-muscat-plugin.mjs +1 -1
- package/cli/bin/muscat-pk.mjs +16 -10
- package/cli/docker/Dockerfile.bridge +17 -0
- package/cli/docker/Dockerfile.cpp +22 -0
- package/cli/docker/Dockerfile.dev +14 -0
- package/cli/docker/Dockerfile.python +20 -0
- package/cli/docker/bridge-package.json +8 -0
- package/cli/src/bridge.mjs +92 -0
- package/cli/src/build.mjs +406 -36
- package/cli/src/check-package.mjs +2 -1
- package/cli/src/cpp.mjs +65 -0
- package/cli/src/dev.mjs +16 -3
- package/cli/src/devBack.mjs +246 -49
- package/cli/src/devUi.mjs +118 -15
- package/cli/src/esbuild.mjs +51 -10
- package/cli/src/manifest.mjs +189 -0
- package/cli/src/new.mjs +152 -27
- package/cli/src/preview.mjs +91 -0
- package/cli/src/python.mjs +97 -0
- package/cli/src/ros2.mjs +66 -0
- package/cli/src/socketBridge.mjs +129 -0
- package/cli/src/type.mjs +114 -0
- package/cli/src/update.mjs +267 -30
- package/cli/src/util.mjs +65 -4
- package/cli/templates/common/README.md +382 -9
- package/cli/templates/common/manifest.json +13 -13
- package/cli/templates/common/package.json +5 -11
- package/cli/templates/common/py/main.py +88 -0
- package/cli/templates/common/requirements.txt +4 -0
- package/cli/templates/common/tsconfig.json +1 -1
- package/cli/templates/common/{src → ui}/content.tsx +3 -2
- package/cli/templates/common/vite.config.ts +22 -13
- package/cli/templates/cpp/cpp/CMakeLists.txt +14 -0
- package/cli/templates/cpp/cpp/main.cpp +25 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/__init__.py +0 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +25 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/package.xml +10 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/resource/__PLUGIN_SLUG__ +0 -0
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/setup.py +18 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/CMakeLists.txt +17 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -0
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +22 -0
- package/package.json +4 -4
- package/cli/src/pack.mjs +0 -58
- package/cli/templates/sdk/main.py +0 -21
- package/cli/templates/sdk/requirements.txt +0 -1
|
@@ -1,29 +1,38 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
}
|
|
File without changes
|
|
@@ -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>
|
|
File without changes
|
|
@@ -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
|
+
"version": "0.1.2-dev.30",
|
|
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
|
-
"
|
|
34
|
+
"@rainbow-robotics/preview-host": "0.1.2-dev.30",
|
|
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__
|