agenticros 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +192 -0
- package/README.md +90 -4
- package/dist/commands/config.d.ts +20 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +179 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +33 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +232 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/down.d.ts +15 -0
- package/dist/commands/down.d.ts.map +1 -0
- package/dist/commands/down.js +91 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +259 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.d.ts +18 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +67 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +56 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/up.d.ts +20 -0
- package/dist/commands/up.d.ts.map +1 -0
- package/dist/commands/up.js +70 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/menu.d.ts +9 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +96 -0
- package/dist/menu.js.map +1 -0
- package/dist/runners/real-robot.d.ts +15 -0
- package/dist/runners/real-robot.d.ts.map +1 -0
- package/dist/runners/real-robot.js +46 -0
- package/dist/runners/real-robot.js.map +1 -0
- package/dist/runners/sim.d.ts +19 -0
- package/dist/runners/sim.d.ts.map +1 -0
- package/dist/runners/sim.js +53 -0
- package/dist/runners/sim.js.map +1 -0
- package/dist/util/env.d.ts +24 -0
- package/dist/util/env.d.ts.map +1 -0
- package/dist/util/env.js +53 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/logger.d.ts +24 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +62 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/paths.d.ts +57 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +132 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/pidfile.d.ts +16 -0
- package/dist/util/pidfile.d.ts.map +1 -0
- package/dist/util/pidfile.js +63 -0
- package/dist/util/pidfile.js.map +1 -0
- package/dist/util/state.d.ts +26 -0
- package/dist/util/state.d.ts.map +1 -0
- package/dist/util/state.js +55 -0
- package/dist/util/state.js.map +1 -0
- package/package.json +60 -1
- package/runtime/BUNDLE.json +11 -0
- package/runtime/LICENSE +192 -0
- package/runtime/README.md +273 -0
- package/runtime/docs/architecture.md +366 -0
- package/runtime/docs/cli.md +140 -0
- package/runtime/docs/memory.md +292 -0
- package/runtime/docs/robot-setup.md +347 -0
- package/runtime/package.json +28 -0
- package/runtime/packages/agenticros/agenticros-agenticros-0.0.1.tgz +0 -0
- package/runtime/packages/agenticros/openclaw.plugin.json +451 -0
- package/runtime/packages/agenticros/package.json +41 -0
- package/runtime/packages/agenticros/src/camera-snapshot-cache.ts +59 -0
- package/runtime/packages/agenticros/src/camera-snapshot-routes.ts +44 -0
- package/runtime/packages/agenticros/src/commands/estop.ts +41 -0
- package/runtime/packages/agenticros/src/commands/transport.ts +195 -0
- package/runtime/packages/agenticros/src/config-file.ts +136 -0
- package/runtime/packages/agenticros/src/config-page.ts +498 -0
- package/runtime/packages/agenticros/src/context/robot-context.ts +373 -0
- package/runtime/packages/agenticros/src/depth.ts +313 -0
- package/runtime/packages/agenticros/src/describer.ts +157 -0
- package/runtime/packages/agenticros/src/image-binary-trim.ts +16 -0
- package/runtime/packages/agenticros/src/index.ts +85 -0
- package/runtime/packages/agenticros/src/landing-page.ts +38 -0
- package/runtime/packages/agenticros/src/memory.ts +44 -0
- package/runtime/packages/agenticros/src/plugin-api.ts +173 -0
- package/runtime/packages/agenticros/src/plugin-image-base64.ts +69 -0
- package/runtime/packages/agenticros/src/preflight.ts +110 -0
- package/runtime/packages/agenticros/src/routes.ts +328 -0
- package/runtime/packages/agenticros/src/safety/validator.ts +43 -0
- package/runtime/packages/agenticros/src/service.ts +359 -0
- package/runtime/packages/agenticros/src/skill-api.ts +65 -0
- package/runtime/packages/agenticros/src/skill-loader.ts +146 -0
- package/runtime/packages/agenticros/src/teleop/page.ts +498 -0
- package/runtime/packages/agenticros/src/teleop/routes.ts +650 -0
- package/runtime/packages/agenticros/src/tools/index.ts +26 -0
- package/runtime/packages/agenticros/src/tools/ros2-action.ts +50 -0
- package/runtime/packages/agenticros/src/tools/ros2-camera.ts +221 -0
- package/runtime/packages/agenticros/src/tools/ros2-depth-distance.ts +58 -0
- package/runtime/packages/agenticros/src/tools/ros2-introspect.ts +62 -0
- package/runtime/packages/agenticros/src/tools/ros2-memory.ts +158 -0
- package/runtime/packages/agenticros/src/tools/ros2-param.ts +87 -0
- package/runtime/packages/agenticros/src/tools/ros2-publish.ts +52 -0
- package/runtime/packages/agenticros/src/tools/ros2-service.ts +46 -0
- package/runtime/packages/agenticros/src/tools/ros2-subscribe.ts +71 -0
- package/runtime/packages/agenticros/tsconfig.json +9 -0
- package/runtime/packages/agenticros-claude-code/README.md +260 -0
- package/runtime/packages/agenticros-claude-code/config.example.json +9 -0
- package/runtime/packages/agenticros-claude-code/dist/config.d.ts +8 -0
- package/runtime/packages/agenticros-claude-code/dist/config.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/config.js +93 -0
- package/runtime/packages/agenticros-claude-code/dist/config.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/depth.d.ts +20 -0
- package/runtime/packages/agenticros-claude-code/dist/depth.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/depth.js +126 -0
- package/runtime/packages/agenticros-claude-code/dist/depth.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts +6 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js +36 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts +33 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js +134 -0
- package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts +43 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js +73 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts +58 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js +251 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts +61 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js +268 -0
- package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/index.d.ts +3 -0
- package/runtime/packages/agenticros-claude-code/dist/index.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/index.js +111 -0
- package/runtime/packages/agenticros-claude-code/dist/index.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/memory.d.ts +17 -0
- package/runtime/packages/agenticros-claude-code/dist/memory.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/memory.js +44 -0
- package/runtime/packages/agenticros-claude-code/dist/memory.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/safety.d.ts +10 -0
- package/runtime/packages/agenticros-claude-code/dist/safety.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/safety.js +34 -0
- package/runtime/packages/agenticros-claude-code/dist/safety.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/tools.d.ts +36 -0
- package/runtime/packages/agenticros-claude-code/dist/tools.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/tools.js +777 -0
- package/runtime/packages/agenticros-claude-code/dist/tools.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/transport.d.ts +17 -0
- package/runtime/packages/agenticros-claude-code/dist/transport.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/transport.js +46 -0
- package/runtime/packages/agenticros-claude-code/dist/transport.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts +42 -0
- package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts.map +1 -0
- package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js +114 -0
- package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js.map +1 -0
- package/runtime/packages/agenticros-claude-code/package.json +29 -0
- package/runtime/packages/agenticros-claude-code/src/config.ts +96 -0
- package/runtime/packages/agenticros-claude-code/src/depth.ts +173 -0
- package/runtime/packages/agenticros-claude-code/src/find-object/coco-classes.ts +38 -0
- package/runtime/packages/agenticros-claude-code/src/find-object/find-object.ts +190 -0
- package/runtime/packages/agenticros-claude-code/src/follow-me/controller.ts +109 -0
- package/runtime/packages/agenticros-claude-code/src/follow-me/depth-loop.ts +420 -0
- package/runtime/packages/agenticros-claude-code/src/follow-me/detector.ts +303 -0
- package/runtime/packages/agenticros-claude-code/src/follow-me/loop.ts +330 -0
- package/runtime/packages/agenticros-claude-code/src/index.ts +125 -0
- package/runtime/packages/agenticros-claude-code/src/memory.ts +51 -0
- package/runtime/packages/agenticros-claude-code/src/safety.ts +44 -0
- package/runtime/packages/agenticros-claude-code/src/tools.ts +891 -0
- package/runtime/packages/agenticros-claude-code/src/transport.ts +58 -0
- package/runtime/packages/agenticros-claude-code/src/zero-shot/detector.ts +169 -0
- package/runtime/packages/agenticros-claude-code/tsconfig.json +9 -0
- package/runtime/packages/agenticros-claude-code/yolo-debug.mjs +106 -0
- package/runtime/packages/agenticros-gemini/README.md +139 -0
- package/runtime/packages/agenticros-gemini/package.json +28 -0
- package/runtime/packages/agenticros-gemini/scripts/smoke-api.mjs +42 -0
- package/runtime/packages/agenticros-gemini/src/chat.ts +139 -0
- package/runtime/packages/agenticros-gemini/src/config.ts +92 -0
- package/runtime/packages/agenticros-gemini/src/depth.ts +173 -0
- package/runtime/packages/agenticros-gemini/src/index.ts +58 -0
- package/runtime/packages/agenticros-gemini/src/memory.ts +32 -0
- package/runtime/packages/agenticros-gemini/src/safety.ts +44 -0
- package/runtime/packages/agenticros-gemini/src/tools.ts +516 -0
- package/runtime/packages/agenticros-gemini/src/transport.ts +58 -0
- package/runtime/packages/agenticros-gemini/tsconfig.json +8 -0
- package/runtime/packages/core/package.json +47 -0
- package/runtime/packages/core/src/banner.ts +32 -0
- package/runtime/packages/core/src/cmd-vel-twist.ts +31 -0
- package/runtime/packages/core/src/config.ts +279 -0
- package/runtime/packages/core/src/index.ts +54 -0
- package/runtime/packages/core/src/memory/__tests__/factory.test.ts +70 -0
- package/runtime/packages/core/src/memory/__tests__/local-provider.test.ts +195 -0
- package/runtime/packages/core/src/memory/__tests__/mem0-provider.test.ts +192 -0
- package/runtime/packages/core/src/memory/__tests__/smart-defaults.test.ts +46 -0
- package/runtime/packages/core/src/memory/factory.ts +63 -0
- package/runtime/packages/core/src/memory/index.ts +10 -0
- package/runtime/packages/core/src/memory/local/provider.ts +229 -0
- package/runtime/packages/core/src/memory/mem0/provider.ts +379 -0
- package/runtime/packages/core/src/memory/types.ts +96 -0
- package/runtime/packages/core/src/topic-utils.ts +95 -0
- package/runtime/packages/core/src/transport/factory.ts +47 -0
- package/runtime/packages/core/src/transport/local/conversion.ts +333 -0
- package/runtime/packages/core/src/transport/local/entities.ts +129 -0
- package/runtime/packages/core/src/transport/local/transport.ts +406 -0
- package/runtime/packages/core/src/transport/rosbridge/actions.ts +81 -0
- package/runtime/packages/core/src/transport/rosbridge/adapter.ts +157 -0
- package/runtime/packages/core/src/transport/rosbridge/client.ts +438 -0
- package/runtime/packages/core/src/transport/rosbridge/services.ts +41 -0
- package/runtime/packages/core/src/transport/rosbridge/topics.ts +60 -0
- package/runtime/packages/core/src/transport/rosbridge/types.ts +118 -0
- package/runtime/packages/core/src/transport/transport.ts +77 -0
- package/runtime/packages/core/src/transport/types.ts +137 -0
- package/runtime/packages/core/src/transport/webrtc/signaling-client.ts +196 -0
- package/runtime/packages/core/src/transport/webrtc/signaling-types.ts +130 -0
- package/runtime/packages/core/src/transport/webrtc/transport.ts +516 -0
- package/runtime/packages/core/src/transport/zenoh/adapter.ts +357 -0
- package/runtime/packages/core/src/transport/zenoh/cdr.ts +183 -0
- package/runtime/packages/core/src/transport/zenoh/keys.ts +51 -0
- package/runtime/packages/core/tsconfig.json +9 -0
- package/runtime/packages/ros-camera/package.json +30 -0
- package/runtime/packages/ros-camera/src/index.ts +13 -0
- package/runtime/packages/ros-camera/src/snapshot.ts +372 -0
- package/runtime/packages/ros-camera/tsconfig.json +9 -0
- package/runtime/pnpm-lock.yaml +5260 -0
- package/runtime/pnpm-workspace.yaml +2 -0
- package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/__init__.py +0 -0
- package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/agent_node.py +561 -0
- package/runtime/ros2_ws/src/agenticros_agent/package.xml +25 -0
- package/runtime/ros2_ws/src/agenticros_agent/resource/agenticros_agent +0 -0
- package/runtime/ros2_ws/src/agenticros_agent/setup.cfg +4 -0
- package/runtime/ros2_ws/src/agenticros_agent/setup.py +25 -0
- package/runtime/ros2_ws/src/agenticros_bringup/README.md +128 -0
- package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/__init__.py +1 -0
- package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/cmd_vel_relay.py +33 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/cmd_vel_bridge.launch.py +58 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/gazebo_turtlebot3.launch.py +69 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo.launch.py +55 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo_rviz.launch.py +48 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/realsense_rosbridge.launch.py +154 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/rosbridge_gazebo.launch.py +54 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/rviz.launch.py +38 -0
- package/runtime/ros2_ws/src/agenticros_bringup/launch/turtlebot3_gazebo_rviz.launch.py +42 -0
- package/runtime/ros2_ws/src/agenticros_bringup/package.xml +24 -0
- package/runtime/ros2_ws/src/agenticros_bringup/resource/agenticros_bringup +0 -0
- package/runtime/ros2_ws/src/agenticros_bringup/rviz/turtlebot3_agenticros.rviz +174 -0
- package/runtime/ros2_ws/src/agenticros_bringup/setup.cfg +4 -0
- package/runtime/ros2_ws/src/agenticros_bringup/setup.py +28 -0
- package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/__init__.py +0 -0
- package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/discovery_node.py +172 -0
- package/runtime/ros2_ws/src/agenticros_discovery/package.xml +22 -0
- package/runtime/ros2_ws/src/agenticros_discovery/resource/agenticros_discovery +0 -0
- package/runtime/ros2_ws/src/agenticros_discovery/setup.cfg +5 -0
- package/runtime/ros2_ws/src/agenticros_discovery/setup.py +25 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/README.md +66 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__init__.py +1 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__main__.py +5 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follow_me_node.py +278 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follower_controller.py +631 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/person_tracker.py +635 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/package.xml +22 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/resource/agenticros_follow_me +0 -0
- package/runtime/ros2_ws/src/agenticros_follow_me/setup.py +25 -0
- package/runtime/ros2_ws/src/agenticros_msgs/CMakeLists.txt +26 -0
- package/runtime/ros2_ws/src/agenticros_msgs/msg/CapabilityManifest.msg +9 -0
- package/runtime/ros2_ws/src/agenticros_msgs/package.xml +22 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeGetStatus.srv +11 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetDistance.srv +4 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetTarget.srv +6 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStart.srv +5 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStop.srv +3 -0
- package/runtime/ros2_ws/src/agenticros_msgs/srv/GetCapabilities.srv +5 -0
- package/runtime/ros2_ws/src/agenticros_sim/CMakeLists.txt +24 -0
- package/runtime/ros2_ws/src/agenticros_sim/README.md +120 -0
- package/runtime/ros2_ws/src/agenticros_sim/config/agenticros-sim.config.json +28 -0
- package/runtime/ros2_ws/src/agenticros_sim/config/amr_bridge.yaml +111 -0
- package/runtime/ros2_ws/src/agenticros_sim/config/amr_view.rviz +172 -0
- package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.dsv.in +3 -0
- package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.sh.in +7 -0
- package/runtime/ros2_ws/src/agenticros_sim/launch/sim_amr.launch.py +159 -0
- package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.config +17 -0
- package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.sdf +244 -0
- package/runtime/ros2_ws/src/agenticros_sim/package.xml +27 -0
- package/runtime/ros2_ws/src/agenticros_sim/worlds/agenticros_indoor.sdf +183 -0
- package/runtime/scripts/activate_workspace.sh +285 -0
- package/runtime/scripts/agenticros-describer.policy.yaml +96 -0
- package/runtime/scripts/agenticros-proxy.cjs +99 -0
- package/runtime/scripts/agenticros-rosbridge.policy.yaml +62 -0
- package/runtime/scripts/check-cli-tarball-size.mjs +42 -0
- package/runtime/scripts/configure_agenticros.sh +200 -0
- package/runtime/scripts/configure_for_sim.sh +64 -0
- package/runtime/scripts/fix-openclaw-control-ui-path.sh +20 -0
- package/runtime/scripts/install_cli.sh +94 -0
- package/runtime/scripts/install_rosbridge_from_source.sh +67 -0
- package/runtime/scripts/lib/agenticros-banner.sh +28 -0
- package/runtime/scripts/onboard_robot.sh +75 -0
- package/runtime/scripts/openai.policy.yaml +77 -0
- package/runtime/scripts/openclaw-dashboard-url.cjs +49 -0
- package/runtime/scripts/pack-runtime.mjs +245 -0
- package/runtime/scripts/run_demo_native.sh +43 -0
- package/runtime/scripts/run_nemoclaw_host_stack.sh +91 -0
- package/runtime/scripts/run_robot_rosbridge.sh +36 -0
- package/runtime/scripts/sandbox_rosbridge_relay.py +137 -0
- package/runtime/scripts/setup-openclaw-local.cjs +75 -0
- package/runtime/scripts/setup_gateway_plugin.sh +329 -0
- package/runtime/scripts/setup_robot.sh +113 -0
- package/runtime/scripts/setup_workspace.sh +484 -0
- package/runtime/scripts/sim/run_sim.sh +146 -0
- package/runtime/scripts/smoke_test_nemoclaw.sh +123 -0
- package/runtime/scripts/start_demo.sh +55 -0
- package/runtime/scripts/sync-skill-tools.mjs +335 -0
- package/runtime/scripts/test-follow-me-sim.mjs +135 -0
- package/runtime/scripts/test-mcp-e2e.mjs +184 -0
- package/runtime/scripts/test-rclnodejs.mts +129 -0
- package/runtime/scripts/use-openclaw-2026.2.26.sh +19 -0
- package/runtime/scripts/use-openclaw-2026.3.11.sh +19 -0
- package/runtime/scripts/zenoh-bridge-ros2dds-robot.json5 +30 -0
- package/runtime/scripts/zenohd-agenticros.json5 +11 -0
- package/runtime/scripts/zenohd-rosclaw.json5 +11 -0
- package/runtime/tsconfig.base.json +19 -0
- package/index.js +0 -6
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import type { RosTransport } from "../transport.js";
|
|
2
|
+
import type {
|
|
3
|
+
ConnectionStatus,
|
|
4
|
+
ConnectionHandler,
|
|
5
|
+
Subscription,
|
|
6
|
+
PublishOptions,
|
|
7
|
+
SubscribeOptions,
|
|
8
|
+
ServiceCallOptions,
|
|
9
|
+
ServiceCallResult,
|
|
10
|
+
ActionGoalOptions,
|
|
11
|
+
ActionResult,
|
|
12
|
+
TopicInfo,
|
|
13
|
+
ServiceInfo,
|
|
14
|
+
ActionInfo,
|
|
15
|
+
MessageHandler,
|
|
16
|
+
} from "../types.js";
|
|
17
|
+
import { Session, Config, Locality, Subscriber, type Sample } from "@eclipse-zenoh/zenoh-ts";
|
|
18
|
+
import { rosTopicToZenohKey, zenohKeyToRosTopic, type ZenohKeyFormat } from "./keys.js";
|
|
19
|
+
import { encodeCdr, decodeCdr, isCdrTypeSupported } from "./cdr.js";
|
|
20
|
+
|
|
21
|
+
export interface ZenohAdapterConfig {
|
|
22
|
+
routerEndpoint: string;
|
|
23
|
+
domainId?: number;
|
|
24
|
+
/** "ros2dds" for zenoh-bridge-ros2dds, "rmw_zenoh" for ROS2 with Zenoh RMW */
|
|
25
|
+
keyFormat?: ZenohKeyFormat;
|
|
26
|
+
/** When ros2dds bridge uses a non-"/" namespace, set to the same value (e.g. "/bot1"). */
|
|
27
|
+
bridgeNamespace?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Zenoh transport adapter (Mode D).
|
|
32
|
+
* Connects to a Zenoh router via zenoh-ts (WebSocket to zenoh-plugin-remote-api).
|
|
33
|
+
* Uses rmw_zenoh key mapping and CDR payloads for ROS 2 compatibility.
|
|
34
|
+
*/
|
|
35
|
+
export class ZenohTransport implements RosTransport {
|
|
36
|
+
private config: ZenohAdapterConfig;
|
|
37
|
+
private session: Session | null = null;
|
|
38
|
+
private status: ConnectionStatus = "disconnected";
|
|
39
|
+
private connectionHandlers: Set<ConnectionHandler> = new Set();
|
|
40
|
+
private subscribers: Map<string, { undeclare: () => Promise<void> }> = new Map();
|
|
41
|
+
/** Serialize Session.open/close so two connect()s never open parallel WebSockets to remote-api. */
|
|
42
|
+
private sessionOpQueue: Promise<void> = Promise.resolve();
|
|
43
|
+
/** Serialize listTopics so overlapping tool calls do not undeclare each other's subscribers. */
|
|
44
|
+
private listTopicsOpQueue: Promise<TopicInfo[]> = Promise.resolve([]);
|
|
45
|
+
|
|
46
|
+
constructor(config: ZenohAdapterConfig) {
|
|
47
|
+
this.config = {
|
|
48
|
+
domainId: 0,
|
|
49
|
+
keyFormat: "ros2dds",
|
|
50
|
+
...config,
|
|
51
|
+
};
|
|
52
|
+
this.publish = this.publish.bind(this);
|
|
53
|
+
this.subscribe = this.subscribe.bind(this);
|
|
54
|
+
this.subscribeAsync = this.subscribeAsync.bind(this);
|
|
55
|
+
this.getStatus = this.getStatus.bind(this);
|
|
56
|
+
this.connect = this.connect.bind(this);
|
|
57
|
+
this.disconnect = this.disconnect.bind(this);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private setStatus(s: ConnectionStatus): void {
|
|
61
|
+
if (this.status === s) return;
|
|
62
|
+
this.status = s;
|
|
63
|
+
this.connectionHandlers.forEach((h) => h(s));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private domainId(): number {
|
|
67
|
+
return this.config.domainId ?? 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private keyFormat(): ZenohKeyFormat {
|
|
71
|
+
return this.config.keyFormat ?? "ros2dds";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private key(topic: string): string {
|
|
75
|
+
return rosTopicToZenohKey(topic, this.domainId(), this.keyFormat(), this.config.bridgeNamespace);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private enqueueSessionOp(fn: () => Promise<void>): Promise<void> {
|
|
79
|
+
const next = this.sessionOpQueue.then(fn, fn);
|
|
80
|
+
this.sessionOpQueue = next.catch(() => {});
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async connect(): Promise<void> {
|
|
85
|
+
return this.enqueueSessionOp(async () => {
|
|
86
|
+
if (this.session && !this.session.isClosed()) return;
|
|
87
|
+
this.setStatus("connecting");
|
|
88
|
+
const locator = (this.config.routerEndpoint ?? "").trim();
|
|
89
|
+
if (!locator) {
|
|
90
|
+
this.setStatus("disconnected");
|
|
91
|
+
throw new Error(
|
|
92
|
+
"Zenoh router endpoint is empty. Set zenoh.routerEndpoint in config (e.g. ws://localhost:10000). See docs/zenoh-agenticros.md.",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (!/^wss?:\/\//i.test(locator)) {
|
|
96
|
+
this.setStatus("disconnected");
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Zenoh router endpoint must be a WebSocket URL (ws:// or wss://). Got: "${locator}". Use e.g. ws://localhost:10000 (zenoh-plugin-remote-api).`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const config = new Config(locator);
|
|
103
|
+
this.session = await Session.open(config);
|
|
104
|
+
this.setStatus("connected");
|
|
105
|
+
console.warn(`[AgenticROS] Zenoh connected to ${locator}`);
|
|
106
|
+
} catch (e) {
|
|
107
|
+
this.setStatus("disconnected");
|
|
108
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
109
|
+
if (/invalid url|invalid uri/i.test(msg)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Zenoh endpoint "${locator}" is not a valid URL. Use a WebSocket URL, e.g. ws://localhost:10000.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
console.error(`[AgenticROS] Zenoh connection failed to ${locator}:`, e);
|
|
115
|
+
throw e;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async disconnect(): Promise<void> {
|
|
121
|
+
return this.enqueueSessionOp(async () => {
|
|
122
|
+
for (const [, sub] of this.subscribers) {
|
|
123
|
+
await sub.undeclare();
|
|
124
|
+
}
|
|
125
|
+
this.subscribers.clear();
|
|
126
|
+
if (this.session) {
|
|
127
|
+
await this.session.close();
|
|
128
|
+
this.session = null;
|
|
129
|
+
}
|
|
130
|
+
this.setStatus("disconnected");
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getStatus(): ConnectionStatus {
|
|
135
|
+
return this.session && !this.session.isClosed() ? "connected" : this.status;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
onConnection(handler: ConnectionHandler): () => void {
|
|
139
|
+
this.connectionHandlers.add(handler);
|
|
140
|
+
return () => this.connectionHandlers.delete(handler);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
publish(options: PublishOptions): Promise<void> {
|
|
144
|
+
if (this == null) throw new Error("Zenoh transport not connected");
|
|
145
|
+
const s = this.session;
|
|
146
|
+
if (!s || s.isClosed()) {
|
|
147
|
+
throw new Error("Zenoh transport not connected");
|
|
148
|
+
}
|
|
149
|
+
// Normalize cmd_vel: /<uuid>/cmd_vel → /robot<uuid-no-dashes>/cmd_vel so bridge/subscribers see the robot-prefixed topic (robot often expects UUID without dashes)
|
|
150
|
+
const topicRaw = (options.topic ?? "").trim();
|
|
151
|
+
const cmdVelMatch = topicRaw.match(/^\/([^/]+)\/cmd_vel$/i);
|
|
152
|
+
const segment = cmdVelMatch?.[1] ?? "";
|
|
153
|
+
const topic =
|
|
154
|
+
cmdVelMatch && !segment.toLowerCase().startsWith("robot")
|
|
155
|
+
? `/robot${segment.replace(/-/g, "")}/cmd_vel`
|
|
156
|
+
: topicRaw;
|
|
157
|
+
const effectiveTopic = topic || (options.topic ?? "").trim();
|
|
158
|
+
const key = this.key(effectiveTopic);
|
|
159
|
+
if (!isCdrTypeSupported(options.type)) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Zenoh CDR publish not implemented for type: ${options.type}. Supported: geometry_msgs/msg/Twist (Image/CompressedImage are decode-only).`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (!key) {
|
|
165
|
+
throw new Error(`Zenoh publish: topic is empty (options.topic=${JSON.stringify(options.topic)})`);
|
|
166
|
+
}
|
|
167
|
+
const payload = encodeCdr(options.type, options.msg);
|
|
168
|
+
console.warn(`[AgenticROS] Zenoh publish: key=${key} topic=${effectiveTopic}`);
|
|
169
|
+
return s.put(key, payload).catch((err: unknown) => {
|
|
170
|
+
console.error("[AgenticROS] Zenoh put failed:", key, err);
|
|
171
|
+
throw new Error(`Zenoh put failed: ${err}`);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
subscribe(options: SubscribeOptions, handler: MessageHandler): Subscription {
|
|
176
|
+
if (this == null) throw new Error("Zenoh transport not connected");
|
|
177
|
+
const s = this.session;
|
|
178
|
+
if (!s || s.isClosed()) {
|
|
179
|
+
throw new Error("Zenoh transport not connected");
|
|
180
|
+
}
|
|
181
|
+
const key = this.key(options.topic);
|
|
182
|
+
const type = options.type ?? "geometry_msgs/msg/Twist";
|
|
183
|
+
|
|
184
|
+
if (!isCdrTypeSupported(type)) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`Zenoh CDR subscribe not implemented for type: ${type}. Supported: geometry_msgs/msg/Twist, sensor_msgs/msg/Image, sensor_msgs/msg/CompressedImage`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const subKey = `${options.topic}\0${type}`;
|
|
191
|
+
const ref: { undeclare: () => Promise<void> } = { undeclare: async () => {} };
|
|
192
|
+
let decodeErrorLogged = false;
|
|
193
|
+
|
|
194
|
+
s.declareSubscriber(key, {
|
|
195
|
+
allowedOrigin: Locality.ANY,
|
|
196
|
+
handler: (sample: Sample) => {
|
|
197
|
+
const payload = sample.payload().toBytes();
|
|
198
|
+
try {
|
|
199
|
+
const msg = decodeCdr(type, payload);
|
|
200
|
+
handler(msg);
|
|
201
|
+
} catch (e) {
|
|
202
|
+
if (!decodeErrorLogged) {
|
|
203
|
+
decodeErrorLogged = true;
|
|
204
|
+
console.warn("[AgenticROS] Zenoh CDR decode failed for", options.topic, type, e instanceof Error ? e.message : String(e));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
}).then((sub) => {
|
|
209
|
+
ref.undeclare = () => sub.undeclare();
|
|
210
|
+
this.subscribers.set(subKey, ref);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
unsubscribe: () => {
|
|
215
|
+
const entry = this.subscribers.get(subKey);
|
|
216
|
+
if (entry) {
|
|
217
|
+
entry.undeclare().catch(() => {});
|
|
218
|
+
this.subscribers.delete(subKey);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async subscribeAsync(options: SubscribeOptions, handler: MessageHandler): Promise<Subscription> {
|
|
225
|
+
if (this == null) throw new Error("Zenoh transport not connected");
|
|
226
|
+
const s = this.session;
|
|
227
|
+
if (!s || s.isClosed()) {
|
|
228
|
+
throw new Error("Zenoh transport not connected");
|
|
229
|
+
}
|
|
230
|
+
const key = this.key(options.topic);
|
|
231
|
+
const type = options.type ?? "geometry_msgs/msg/Twist";
|
|
232
|
+
|
|
233
|
+
if (!isCdrTypeSupported(type)) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Zenoh CDR subscribe not implemented for type: ${type}. Supported: geometry_msgs/msg/Twist, sensor_msgs/msg/Image, sensor_msgs/msg/CompressedImage`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const subKey = `${options.topic}\0${type}`;
|
|
240
|
+
let decodeErrorLogged = false;
|
|
241
|
+
const sub = await s.declareSubscriber(key, {
|
|
242
|
+
allowedOrigin: Locality.ANY,
|
|
243
|
+
handler: (sample: Sample) => {
|
|
244
|
+
const payload = sample.payload().toBytes();
|
|
245
|
+
try {
|
|
246
|
+
const msg = decodeCdr(type, payload);
|
|
247
|
+
handler(msg);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
if (!decodeErrorLogged) {
|
|
250
|
+
decodeErrorLogged = true;
|
|
251
|
+
console.warn("[AgenticROS] Zenoh CDR decode failed for", options.topic, type, e instanceof Error ? e.message : String(e));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
this.subscribers.set(subKey, { undeclare: () => sub.undeclare() });
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
unsubscribe: () => {
|
|
260
|
+
const entry = this.subscribers.get(subKey);
|
|
261
|
+
if (entry) {
|
|
262
|
+
entry.undeclare().catch(() => {});
|
|
263
|
+
this.subscribers.delete(subKey);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async callService(_options: ServiceCallOptions): Promise<ServiceCallResult> {
|
|
270
|
+
return {
|
|
271
|
+
result: false,
|
|
272
|
+
values: { error: "Zenoh transport: service call not implemented" },
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async sendActionGoal(_options: ActionGoalOptions): Promise<ActionResult> {
|
|
277
|
+
return {
|
|
278
|
+
result: false,
|
|
279
|
+
values: { error: "Zenoh transport: action goal not implemented" },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async cancelActionGoal(_action: string): Promise<void> {
|
|
284
|
+
// no-op
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async listTopics(): Promise<TopicInfo[]> {
|
|
288
|
+
const run = async (): Promise<TopicInfo[]> => {
|
|
289
|
+
const s = this.session;
|
|
290
|
+
if (!s || s.isClosed()) {
|
|
291
|
+
console.warn("[AgenticROS] Zenoh listTopics: not connected");
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
const keys = new Set<string>();
|
|
295
|
+
const handler = (sample: Sample) => {
|
|
296
|
+
keys.add(sample.keyexpr().toString());
|
|
297
|
+
};
|
|
298
|
+
// Canon expressions only (`**/*` forbidden → use `*/**`). Do not use bare `**` (whole keyspace).
|
|
299
|
+
// `*/**` does NOT match single-chunk keys (e.g. zenoh key `cmd_vel` from ROS `/cmd_vel`), so include `*`.
|
|
300
|
+
const patterns = ["*/**", "*", "camera/**", "**/camera/**"];
|
|
301
|
+
const subs: Subscriber[] = [];
|
|
302
|
+
for (const keyexpr of patterns) {
|
|
303
|
+
try {
|
|
304
|
+
subs.push(await s.declareSubscriber(keyexpr, { handler, allowedOrigin: Locality.ANY }));
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.warn(
|
|
307
|
+
"[AgenticROS] Zenoh listTopics: declareSubscriber failed for",
|
|
308
|
+
keyexpr,
|
|
309
|
+
e instanceof Error ? e.message : String(e),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (subs.length === 0) {
|
|
314
|
+
console.warn("[AgenticROS] Zenoh listTopics: no subscribers declared (check key expressions)");
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
const rawMs = Number.parseInt(process.env.AGENTICROS_ZENOH_LIST_TOPICS_MS ?? "", 10);
|
|
318
|
+
const sampleMs = Number.isFinite(rawMs) ? Math.min(60_000, Math.max(1000, rawMs)) : 6500;
|
|
319
|
+
await new Promise((r) => setTimeout(r, sampleMs));
|
|
320
|
+
await Promise.all(subs.map((sub) => sub.undeclare()));
|
|
321
|
+
const format = this.keyFormat();
|
|
322
|
+
const topics = Array.from(keys)
|
|
323
|
+
.filter((key) => !key.startsWith("@"))
|
|
324
|
+
.map((key) => ({
|
|
325
|
+
name: zenohKeyToRosTopic(key, format),
|
|
326
|
+
type: "unknown",
|
|
327
|
+
}));
|
|
328
|
+
if (topics.length > 0) {
|
|
329
|
+
console.warn(`[AgenticROS] Zenoh listTopics: found ${topics.length} keys (sampled ${sampleMs}ms)`);
|
|
330
|
+
} else {
|
|
331
|
+
const ep = (this.config.routerEndpoint ?? "").trim() || "(not configured)";
|
|
332
|
+
console.warn(
|
|
333
|
+
"[AgenticROS] Zenoh listTopics: no keys in sampling window — " +
|
|
334
|
+
`waited ${sampleMs}ms on ${ep}. ` +
|
|
335
|
+
"If `zenoh subscribe` on another host shows traffic, ensure it uses the **same Zenoh router** as this session: " +
|
|
336
|
+
"OpenClaw uses **WebSocket to zenoh-plugin-remote-api** (e.g. ws://127.0.0.1:10000), not raw TCP to the robot. " +
|
|
337
|
+
"A CLI client to tcp/ROBOT_IP:7447 sees the robot's local router; data only appears here if the robot bridge peers to the Mac router you connected to. " +
|
|
338
|
+
"`subscribe -k '**'` also prints **binary payloads** (garbled text), not topic names — keys are short path-like strings when Zenoh prints them separately. " +
|
|
339
|
+
"Optional: set AGENTICROS_ZENOH_LIST_TOPICS_MS (ms) to sample longer for low-rate topics.",
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
return topics;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const next = this.listTopicsOpQueue.then(run, run);
|
|
346
|
+
this.listTopicsOpQueue = next.catch(() => []);
|
|
347
|
+
return next;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async listServices(): Promise<ServiceInfo[]> {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async listActions(): Promise<ActionInfo[]> {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDR encode/decode for ROS 2 messages over Zenoh (rmw_zenoh uses CDR payloads).
|
|
3
|
+
* Supports geometry_msgs/msg/Twist, sensor_msgs/msg/Image, sensor_msgs/msg/CompressedImage.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CdrReader, CdrWriter } from "@foxglove/cdr";
|
|
7
|
+
import { parse } from "@foxglove/rosmsg";
|
|
8
|
+
import { MessageReader } from "@foxglove/rosmsg2-serialization";
|
|
9
|
+
|
|
10
|
+
/** Normalize type string to "pkg/msg/Type" form. Handles short names (e.g. "Image") and combined forms. */
|
|
11
|
+
function normalizeType(typeStr: string): string {
|
|
12
|
+
const s = typeStr.trim();
|
|
13
|
+
if (s.includes("CompressedImage")) return "sensor_msgs/msg/CompressedImage";
|
|
14
|
+
if (s === "Image" || s.endsWith("/Image") || (s.includes("sensor_msgs") && s.includes("Image"))) {
|
|
15
|
+
return "sensor_msgs/msg/Image";
|
|
16
|
+
}
|
|
17
|
+
const parts = s.split("/");
|
|
18
|
+
if (parts.length === 3) return s;
|
|
19
|
+
if (parts.length === 2) return `${parts[0]}/msg/${parts[1]}`;
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Read std_msgs/Header from current reader offset (used by Image and CompressedImage). */
|
|
24
|
+
function readStdMsgsHeader(reader: CdrReader): Record<string, unknown> {
|
|
25
|
+
const sec = reader.int32();
|
|
26
|
+
const nanosec = reader.uint32();
|
|
27
|
+
const frame_id = reader.string();
|
|
28
|
+
return {
|
|
29
|
+
stamp: { sec, nanosec },
|
|
30
|
+
frame_id,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `CdrReader` already skips the 4-byte encapsulation header (initial offset 4).
|
|
36
|
+
* CDR2 **appendable** messages (typical for `sensor_msgs` from CycloneDDS / zenoh-bridge-ros2dds)
|
|
37
|
+
* include a 4-byte **DHEADER** next; without consuming it, `CompressedImage` / `Image` decode misaligns
|
|
38
|
+
* and the teleop camera sees no valid frames (timeouts → 503).
|
|
39
|
+
*/
|
|
40
|
+
function createBodyReader(data: Uint8Array): CdrReader {
|
|
41
|
+
const reader = new CdrReader(new DataView(data.buffer, data.byteOffset, data.byteLength));
|
|
42
|
+
if (reader.usesDelimiterHeader && !reader.usesMemberHeader) {
|
|
43
|
+
reader.dHeader();
|
|
44
|
+
}
|
|
45
|
+
return reader;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Embedded defs so Zenoh samples decode like rviz/ros2 bag (CDR1 / CDR2 / delimited / PL_CDR2). */
|
|
49
|
+
const COMPRESSED_IMAGE_MSG_DEF = `
|
|
50
|
+
std_msgs/msg/Header header
|
|
51
|
+
string format
|
|
52
|
+
uint8[] data
|
|
53
|
+
|
|
54
|
+
================================================================================
|
|
55
|
+
MSG: std_msgs/msg/Header
|
|
56
|
+
builtin_interfaces/msg/Time stamp
|
|
57
|
+
string frame_id
|
|
58
|
+
|
|
59
|
+
================================================================================
|
|
60
|
+
MSG: builtin_interfaces/msg/Time
|
|
61
|
+
int32 sec
|
|
62
|
+
uint32 nanosec
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
let compressedImageReader: MessageReader | null = null;
|
|
66
|
+
|
|
67
|
+
function decodeCompressedImageWithFoxglove(data: Uint8Array): Record<string, unknown> {
|
|
68
|
+
if (!compressedImageReader) {
|
|
69
|
+
compressedImageReader = new MessageReader(parse(COMPRESSED_IMAGE_MSG_DEF, { ros2: true }));
|
|
70
|
+
}
|
|
71
|
+
const msg = compressedImageReader.readMessage<Record<string, unknown>>(data);
|
|
72
|
+
return {
|
|
73
|
+
header: msg["header"],
|
|
74
|
+
format: msg["format"],
|
|
75
|
+
data: msg["data"],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function decodeCompressedImageManual(data: Uint8Array): Record<string, unknown> {
|
|
80
|
+
const reader = createBodyReader(data);
|
|
81
|
+
const header = readStdMsgsHeader(reader);
|
|
82
|
+
const format = reader.string();
|
|
83
|
+
const dataLen = reader.sequenceLength();
|
|
84
|
+
const payload = reader.uint8Array(dataLen);
|
|
85
|
+
return {
|
|
86
|
+
header,
|
|
87
|
+
format,
|
|
88
|
+
data: Array.from(payload),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Encode a ROS 2 message to CDR (little-endian) for the given type.
|
|
94
|
+
* Supports: geometry_msgs/msg/Twist.
|
|
95
|
+
*/
|
|
96
|
+
export function encodeCdr(typeStr: string, msg: Record<string, unknown>): Uint8Array {
|
|
97
|
+
const type = normalizeType(typeStr);
|
|
98
|
+
|
|
99
|
+
if (type === "geometry_msgs/msg/Twist") {
|
|
100
|
+
const linear = (msg["linear"] as Record<string, unknown>) ?? {};
|
|
101
|
+
const angular = (msg["angular"] as Record<string, unknown>) ?? {};
|
|
102
|
+
// Standard ROS 2 Twist order: linear.x, linear.y, linear.z, angular.x, angular.y, angular.z (CDR LE, 4-byte encapsulation + 6 float64).
|
|
103
|
+
const buf = new ArrayBuffer(4 + 6 * 8);
|
|
104
|
+
const view = new DataView(buf);
|
|
105
|
+
view.setUint32(0, 0x100, true);
|
|
106
|
+
const vals = [
|
|
107
|
+
Number(linear["x"] ?? 0),
|
|
108
|
+
Number(linear["y"] ?? 0),
|
|
109
|
+
Number(linear["z"] ?? 0),
|
|
110
|
+
Number(angular["x"] ?? 0),
|
|
111
|
+
Number(angular["y"] ?? 0),
|
|
112
|
+
Number(angular["z"] ?? 0),
|
|
113
|
+
];
|
|
114
|
+
for (let i = 0; i < 6; i++) view.setFloat64(4 + i * 8, vals[i], true);
|
|
115
|
+
return new Uint8Array(buf);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
throw new Error(`Zenoh CDR encode not implemented for type: ${typeStr}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Decode a CDR payload to a plain object for the given type.
|
|
123
|
+
* Supports: geometry_msgs/msg/Twist.
|
|
124
|
+
*/
|
|
125
|
+
export function decodeCdr(typeStr: string, data: Uint8Array): Record<string, unknown> {
|
|
126
|
+
const type = normalizeType(typeStr);
|
|
127
|
+
|
|
128
|
+
if (type === "geometry_msgs/msg/Twist") {
|
|
129
|
+
const reader = createBodyReader(data);
|
|
130
|
+
const linear = {
|
|
131
|
+
x: reader.float64(),
|
|
132
|
+
y: reader.float64(),
|
|
133
|
+
z: reader.float64(),
|
|
134
|
+
};
|
|
135
|
+
const angular = {
|
|
136
|
+
x: reader.float64(),
|
|
137
|
+
y: reader.float64(),
|
|
138
|
+
z: reader.float64(),
|
|
139
|
+
};
|
|
140
|
+
return { linear, angular };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (type === "sensor_msgs/msg/CompressedImage") {
|
|
144
|
+
try {
|
|
145
|
+
return decodeCompressedImageWithFoxglove(data);
|
|
146
|
+
} catch {
|
|
147
|
+
return decodeCompressedImageManual(data);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (type === "sensor_msgs/msg/Image") {
|
|
152
|
+
const reader = createBodyReader(data);
|
|
153
|
+
const header = readStdMsgsHeader(reader);
|
|
154
|
+
const height = reader.uint32();
|
|
155
|
+
const width = reader.uint32();
|
|
156
|
+
const encoding = reader.string();
|
|
157
|
+
const is_bigendian = reader.uint8();
|
|
158
|
+
const step = reader.uint32();
|
|
159
|
+
const dataLen = reader.sequenceLength();
|
|
160
|
+
const payload = reader.uint8Array(dataLen);
|
|
161
|
+
return {
|
|
162
|
+
header,
|
|
163
|
+
height,
|
|
164
|
+
width,
|
|
165
|
+
encoding,
|
|
166
|
+
is_bigendian,
|
|
167
|
+
step,
|
|
168
|
+
data: Array.from(payload),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
throw new Error(`Zenoh CDR decode not implemented for type: ${typeStr}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Check if we can encode/decode the given type. */
|
|
176
|
+
export function isCdrTypeSupported(typeStr: string): boolean {
|
|
177
|
+
const type = normalizeType(typeStr);
|
|
178
|
+
return (
|
|
179
|
+
type === "geometry_msgs/msg/Twist" ||
|
|
180
|
+
type === "sensor_msgs/msg/Image" ||
|
|
181
|
+
type === "sensor_msgs/msg/CompressedImage"
|
|
182
|
+
);
|
|
183
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key format used by the Zenoh router / bridge.
|
|
3
|
+
* - rmw_zenoh: ROS2 with Zenoh RMW (domain prefix, slashes → %). Example: 0/%robot-uuid%cmd_vel
|
|
4
|
+
* - ros2dds: zenoh-bridge-ros2dds (no domain prefix, slashes kept). Example: robot-uuid/cmd_vel
|
|
5
|
+
*/
|
|
6
|
+
export type ZenohKeyFormat = "rmw_zenoh" | "ros2dds";
|
|
7
|
+
|
|
8
|
+
/** Strip optional slashes; empty means bridge default "/" (no extra zenoh prefix). */
|
|
9
|
+
function normalizeBridgeNamespace(ns: string | undefined): string {
|
|
10
|
+
const t = (ns ?? "").trim();
|
|
11
|
+
if (!t || t === "/") return "";
|
|
12
|
+
return t.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Map ROS 2 topic name to a Zenoh key expression.
|
|
17
|
+
* - rmw_zenoh: topic "/robot-uuid/cmd_vel", domainId 0 → "0/%robot-uuid%cmd_vel"
|
|
18
|
+
* - ros2dds (zenoh-bridge-ros2dds): topic "/robot-uuid/cmd_vel" → "robot-uuid/cmd_vel"
|
|
19
|
+
* If the bridge sets plugins.ros2dds.namespace to e.g. "/bot1", pass `bridgeNamespace: "/bot1"`
|
|
20
|
+
* so keys become `bot1/robot-uuid/cmd_vel` (matches ros2_name_to_key_expr).
|
|
21
|
+
*/
|
|
22
|
+
export function rosTopicToZenohKey(
|
|
23
|
+
topic: string,
|
|
24
|
+
domainId: number,
|
|
25
|
+
format: ZenohKeyFormat = "ros2dds",
|
|
26
|
+
bridgeNamespace?: string,
|
|
27
|
+
): string {
|
|
28
|
+
const normalized = topic.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
29
|
+
if (!normalized) return format === "rmw_zenoh" ? `${domainId}/` : "";
|
|
30
|
+
if (format === "rmw_zenoh") {
|
|
31
|
+
const keyPart = normalized.replace(/\//g, "%");
|
|
32
|
+
return `${domainId}/%${keyPart}`;
|
|
33
|
+
}
|
|
34
|
+
const bridgeNs = normalizeBridgeNamespace(bridgeNamespace);
|
|
35
|
+
if (!bridgeNs) return normalized;
|
|
36
|
+
if (normalized === bridgeNs || normalized.startsWith(`${bridgeNs}/`)) return normalized;
|
|
37
|
+
return `${bridgeNs}/${normalized}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Map a Zenoh key expression back to a ROS 2 topic name.
|
|
42
|
+
* - rmw_zenoh: "0/%robot-uuid%cmd_vel" → "/robot-uuid/cmd_vel"
|
|
43
|
+
* - ros2dds: "robot-uuid/cmd_vel" → "/robot-uuid/cmd_vel"
|
|
44
|
+
*/
|
|
45
|
+
export function zenohKeyToRosTopic(key: string, format: ZenohKeyFormat = "ros2dds"): string {
|
|
46
|
+
if (format === "ros2dds") return key.startsWith("/") ? key : `/${key}`;
|
|
47
|
+
const match = /^\d+\/(.+)$/.exec(key);
|
|
48
|
+
if (!match) return key;
|
|
49
|
+
const part = match[1].replace(/%/g, "/");
|
|
50
|
+
return part.startsWith("/") ? part : `/${part}`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agenticros/ros-camera",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Shared ROS2 camera snapshot encoding (sensor_msgs Image / CompressedImage) for AgenticROS adapters",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"fzstd": "^0.1.1",
|
|
21
|
+
"pngjs": "^7.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.17.0",
|
|
25
|
+
"@types/pngjs": "^6.0.5",
|
|
26
|
+
"typescript": "^5.7.0"
|
|
27
|
+
},
|
|
28
|
+
"author": "PlaiPin",
|
|
29
|
+
"license": "Apache-2.0"
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ROS_MSG_IMAGE,
|
|
3
|
+
ROS_MSG_COMPRESSED_IMAGE,
|
|
4
|
+
bufferAndMimeFromCompressedImageMessage,
|
|
5
|
+
cameraSnapshotFromPlainMessage,
|
|
6
|
+
coerceRosImageDataToBuffer,
|
|
7
|
+
mimeTypeForSnapshotBase64,
|
|
8
|
+
normalizeDepthImageEncoding,
|
|
9
|
+
rosBoolField,
|
|
10
|
+
rosNumericField,
|
|
11
|
+
rosStringField,
|
|
12
|
+
type CameraSnapshotPayload,
|
|
13
|
+
} from "./snapshot.js";
|