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,438 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import type { Agent as HttpAgent } from "node:http";
|
|
4
|
+
import type {
|
|
5
|
+
RosbridgeClientOptions,
|
|
6
|
+
ConnectionStatus,
|
|
7
|
+
RosbridgeMessage,
|
|
8
|
+
MessageHandler,
|
|
9
|
+
ConnectionHandler,
|
|
10
|
+
} from "./types.js";
|
|
11
|
+
|
|
12
|
+
/** Pending request/response tracker for service calls and action goals. */
|
|
13
|
+
export interface PendingRequest {
|
|
14
|
+
resolve: (value: unknown) => void;
|
|
15
|
+
reject: (reason: Error) => void;
|
|
16
|
+
timer: ReturnType<typeof setTimeout>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build an http.Agent that tunnels every connect() through an HTTP CONNECT
|
|
21
|
+
* proxy when the environment defines one.
|
|
22
|
+
*
|
|
23
|
+
* Why this exists:
|
|
24
|
+
*
|
|
25
|
+
* * Node 22+ honors HTTP(S)_PROXY for ``https.request`` (via the experimental
|
|
26
|
+
* ``EnvHttpProxyAgent``) but ``ws://`` connections from the ``ws`` library
|
|
27
|
+
* bypass that and try a direct TCP connect to the resolved IP. In a
|
|
28
|
+
* NemoClaw / OpenShell sandbox the gateway runs in a restricted netns
|
|
29
|
+
* where the only routable peer is the OPA policy proxy at
|
|
30
|
+
* ``10.200.0.1:3128``. Direct connects to anything else fail with
|
|
31
|
+
* ``ECONNREFUSED`` — that's exactly what happens when ``rosbridge.url``
|
|
32
|
+
* points at ``ws://host.docker.internal:9090`` from inside the sandbox.
|
|
33
|
+
*
|
|
34
|
+
* * Adding an http-proxy-agent dep would balloon the offline-deploy bundle
|
|
35
|
+
* and is unnecessary — Node's built-in ``http.request`` with method
|
|
36
|
+
* ``CONNECT`` does exactly the same handshake.
|
|
37
|
+
*
|
|
38
|
+
* Returns ``null`` (let ``ws`` use its default Agent) when:
|
|
39
|
+
*
|
|
40
|
+
* * No proxy env var is set, or
|
|
41
|
+
* * The target host appears in ``NO_PROXY`` (case-insensitive match,
|
|
42
|
+
* ``*`` and leading ``.`` wildcards honored RFC-style).
|
|
43
|
+
*
|
|
44
|
+
* The plugin owns its own reconnect/cleanup, so we keep the Agent
|
|
45
|
+
* stateless and use ``keepAlive: false`` — every connect attempt opens a
|
|
46
|
+
* fresh CONNECT tunnel.
|
|
47
|
+
*/
|
|
48
|
+
function buildProxyAgentForUrl(wsUrl: string): HttpAgent | null {
|
|
49
|
+
let parsed: URL;
|
|
50
|
+
try {
|
|
51
|
+
parsed = new URL(wsUrl);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const targetHost = parsed.hostname;
|
|
56
|
+
const targetPort = parsed.port || (parsed.protocol === "wss:" ? "443" : "80");
|
|
57
|
+
|
|
58
|
+
const proxyUrl =
|
|
59
|
+
process.env.HTTPS_PROXY ||
|
|
60
|
+
process.env.https_proxy ||
|
|
61
|
+
process.env.HTTP_PROXY ||
|
|
62
|
+
process.env.http_proxy ||
|
|
63
|
+
"";
|
|
64
|
+
if (!proxyUrl) return null;
|
|
65
|
+
|
|
66
|
+
let proxy: URL;
|
|
67
|
+
try {
|
|
68
|
+
proxy = new URL(proxyUrl);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const noProxy = (process.env.NO_PROXY || process.env.no_proxy || "").toLowerCase();
|
|
74
|
+
if (noProxy) {
|
|
75
|
+
const target = targetHost.toLowerCase();
|
|
76
|
+
const entries = noProxy.split(",").map((s) => s.trim()).filter(Boolean);
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (entry === "*") return null;
|
|
79
|
+
const e = entry.startsWith(".") ? entry.slice(1) : entry;
|
|
80
|
+
if (target === e || target.endsWith("." + e)) return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class TunnelAgent extends http.Agent {
|
|
85
|
+
createConnection(
|
|
86
|
+
opts: { hostname?: string; host?: string; port?: number | string },
|
|
87
|
+
cb: (err: Error | null, socket?: NodeJS.Socket) => void,
|
|
88
|
+
): undefined {
|
|
89
|
+
const host = opts.hostname || opts.host || targetHost;
|
|
90
|
+
const port = String(opts.port ?? targetPort);
|
|
91
|
+
const req = http.request({
|
|
92
|
+
host: proxy.hostname,
|
|
93
|
+
port: proxy.port ? Number(proxy.port) : proxy.protocol === "https:" ? 443 : 80,
|
|
94
|
+
method: "CONNECT",
|
|
95
|
+
path: `${host}:${port}`,
|
|
96
|
+
headers: { Host: `${host}:${port}` },
|
|
97
|
+
});
|
|
98
|
+
req.once("connect", (res, socket) => {
|
|
99
|
+
if (res.statusCode === 200) {
|
|
100
|
+
cb(null, socket);
|
|
101
|
+
} else {
|
|
102
|
+
socket.destroy();
|
|
103
|
+
cb(
|
|
104
|
+
new Error(
|
|
105
|
+
`HTTP CONNECT to proxy ${proxy.host} failed: ${res.statusCode} ${res.statusMessage ?? ""}`.trim(),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
req.once("error", (err) => cb(err));
|
|
111
|
+
req.end();
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return new TunnelAgent({ keepAlive: false });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* WebSocket client for the rosbridge protocol.
|
|
121
|
+
* Handles connection lifecycle, reconnection, and message routing.
|
|
122
|
+
*/
|
|
123
|
+
export class RosbridgeClient {
|
|
124
|
+
private ws: WebSocket | null = null;
|
|
125
|
+
private options: Required<RosbridgeClientOptions>;
|
|
126
|
+
private status: ConnectionStatus = "disconnected";
|
|
127
|
+
private messageHandlers = new Map<string, Set<MessageHandler>>();
|
|
128
|
+
private connectionHandlers = new Set<ConnectionHandler>();
|
|
129
|
+
private pendingRequests = new Map<string, PendingRequest>();
|
|
130
|
+
private reconnectAttempts = 0;
|
|
131
|
+
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
132
|
+
private intentionalClose = false;
|
|
133
|
+
private idCounter = 0;
|
|
134
|
+
|
|
135
|
+
constructor(options: RosbridgeClientOptions) {
|
|
136
|
+
this.options = {
|
|
137
|
+
url: options.url,
|
|
138
|
+
reconnect: options.reconnect ?? true,
|
|
139
|
+
reconnectInterval: options.reconnectInterval ?? 3000,
|
|
140
|
+
maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Connect to the rosbridge WebSocket server. */
|
|
145
|
+
async connect(): Promise<void> {
|
|
146
|
+
if (this.status === "connected") return;
|
|
147
|
+
|
|
148
|
+
this.intentionalClose = false;
|
|
149
|
+
this.setStatus("connecting");
|
|
150
|
+
|
|
151
|
+
return new Promise<void>((resolve, reject) => {
|
|
152
|
+
const connectTimeout = setTimeout(() => {
|
|
153
|
+
if (this.ws) {
|
|
154
|
+
this.ws.close();
|
|
155
|
+
this.ws = null;
|
|
156
|
+
}
|
|
157
|
+
reject(new Error(`Connection to ${this.options.url} timed out`));
|
|
158
|
+
}, 10_000);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const proxyAgent = buildProxyAgentForUrl(this.options.url);
|
|
162
|
+
this.ws = proxyAgent
|
|
163
|
+
? new WebSocket(this.options.url, { agent: proxyAgent })
|
|
164
|
+
: new WebSocket(this.options.url);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
clearTimeout(connectTimeout);
|
|
167
|
+
this.setStatus("disconnected");
|
|
168
|
+
reject(err);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.ws.onopen = () => {
|
|
173
|
+
clearTimeout(connectTimeout);
|
|
174
|
+
this.reconnectAttempts = 0;
|
|
175
|
+
this.setStatus("connected");
|
|
176
|
+
resolve();
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
this.ws.onmessage = (event) => {
|
|
180
|
+
const data = typeof event.data === "string"
|
|
181
|
+
? event.data
|
|
182
|
+
: event.data.toString();
|
|
183
|
+
this.handleMessage(data);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
this.ws.onerror = (event: { message?: string; error?: Error }) => {
|
|
187
|
+
clearTimeout(connectTimeout);
|
|
188
|
+
if (this.status === "connecting") {
|
|
189
|
+
this.ws = null;
|
|
190
|
+
this.setStatus("disconnected");
|
|
191
|
+
const underlying =
|
|
192
|
+
(event?.error && (event.error.message || String(event.error))) ||
|
|
193
|
+
event?.message ||
|
|
194
|
+
"no underlying error message";
|
|
195
|
+
reject(new Error(`WebSocket error connecting to ${this.options.url}: ${underlying}`));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
this.ws.onclose = (event: { code?: number; reason?: Buffer | string }) => {
|
|
200
|
+
clearTimeout(connectTimeout);
|
|
201
|
+
this.ws = null;
|
|
202
|
+
|
|
203
|
+
if (this.status === "connecting") {
|
|
204
|
+
this.setStatus("disconnected");
|
|
205
|
+
const code = event?.code ?? "n/a";
|
|
206
|
+
const reasonRaw = event?.reason;
|
|
207
|
+
const reasonStr =
|
|
208
|
+
typeof reasonRaw === "string"
|
|
209
|
+
? reasonRaw
|
|
210
|
+
: reasonRaw && typeof (reasonRaw as Buffer).toString === "function"
|
|
211
|
+
? (reasonRaw as Buffer).toString()
|
|
212
|
+
: "";
|
|
213
|
+
reject(
|
|
214
|
+
new Error(
|
|
215
|
+
`WebSocket closed during connection to ${this.options.url} (code=${code}${reasonStr ? `, reason="${reasonStr}"` : ""})`,
|
|
216
|
+
),
|
|
217
|
+
);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.setStatus("disconnected");
|
|
222
|
+
this.rejectAllPending(new Error("WebSocket connection closed"));
|
|
223
|
+
|
|
224
|
+
if (!this.intentionalClose && this.options.reconnect) {
|
|
225
|
+
this.attemptReconnect();
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Disconnect from the rosbridge server. */
|
|
232
|
+
async disconnect(): Promise<void> {
|
|
233
|
+
this.intentionalClose = true;
|
|
234
|
+
|
|
235
|
+
if (this.reconnectTimer) {
|
|
236
|
+
clearTimeout(this.reconnectTimer);
|
|
237
|
+
this.reconnectTimer = null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.rejectAllPending(new Error("Client disconnected"));
|
|
241
|
+
|
|
242
|
+
if (this.ws) {
|
|
243
|
+
const ws = this.ws;
|
|
244
|
+
this.ws = null;
|
|
245
|
+
|
|
246
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
247
|
+
await new Promise<void>((resolve) => {
|
|
248
|
+
ws.onclose = () => resolve();
|
|
249
|
+
ws.close();
|
|
250
|
+
// Force-resolve after 2s if server doesn't ack the close
|
|
251
|
+
setTimeout(resolve, 2000);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
this.setStatus("disconnected");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Send a rosbridge protocol message. */
|
|
260
|
+
send(message: RosbridgeMessage & Record<string, unknown>): void {
|
|
261
|
+
if (!this.ws || this.status !== "connected") {
|
|
262
|
+
throw new Error("Not connected to rosbridge server");
|
|
263
|
+
}
|
|
264
|
+
this.ws.send(JSON.stringify(message));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Generate a unique message ID. */
|
|
268
|
+
nextId(prefix = "agenticros"): string {
|
|
269
|
+
return `${prefix}_${++this.idCounter}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Subscribe to messages on a specific topic. */
|
|
273
|
+
onMessage(topic: string, handler: MessageHandler): () => void {
|
|
274
|
+
if (!this.messageHandlers.has(topic)) {
|
|
275
|
+
this.messageHandlers.set(topic, new Set());
|
|
276
|
+
}
|
|
277
|
+
this.messageHandlers.get(topic)!.add(handler);
|
|
278
|
+
return () => {
|
|
279
|
+
this.messageHandlers.get(topic)?.delete(handler);
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Register a connection status change handler. */
|
|
284
|
+
onConnection(handler: ConnectionHandler): () => void {
|
|
285
|
+
this.connectionHandlers.add(handler);
|
|
286
|
+
return () => {
|
|
287
|
+
this.connectionHandlers.delete(handler);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Get current connection status. */
|
|
292
|
+
getStatus(): ConnectionStatus {
|
|
293
|
+
return this.status;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Register a pending request that will be resolved when a response
|
|
298
|
+
* with the matching ID arrives (service_response or action_result).
|
|
299
|
+
*/
|
|
300
|
+
registerPending(id: string, resolve: (value: unknown) => void, reject: (reason: Error) => void, timeoutMs = 30_000): void {
|
|
301
|
+
const timer = setTimeout(() => {
|
|
302
|
+
this.pendingRequests.delete(id);
|
|
303
|
+
reject(new Error(`Request ${id} timed out after ${timeoutMs}ms`));
|
|
304
|
+
}, timeoutMs);
|
|
305
|
+
|
|
306
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Resolve a pending request by ID and clean up. */
|
|
310
|
+
resolvePending(id: string, result: unknown): void {
|
|
311
|
+
const pending = this.pendingRequests.get(id);
|
|
312
|
+
if (pending) {
|
|
313
|
+
clearTimeout(pending.timer);
|
|
314
|
+
this.pendingRequests.delete(id);
|
|
315
|
+
pending.resolve(result);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Reject a pending request by ID and clean up. */
|
|
320
|
+
rejectPending(id: string, error: Error): void {
|
|
321
|
+
const pending = this.pendingRequests.get(id);
|
|
322
|
+
if (pending) {
|
|
323
|
+
clearTimeout(pending.timer);
|
|
324
|
+
this.pendingRequests.delete(id);
|
|
325
|
+
pending.reject(error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private setStatus(status: ConnectionStatus): void {
|
|
330
|
+
this.status = status;
|
|
331
|
+
for (const handler of this.connectionHandlers) {
|
|
332
|
+
handler(status);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Route an incoming rosbridge message to the appropriate handler. */
|
|
337
|
+
private handleMessage(data: string): void {
|
|
338
|
+
let msg: Record<string, unknown>;
|
|
339
|
+
try {
|
|
340
|
+
msg = JSON.parse(data) as Record<string, unknown>;
|
|
341
|
+
} catch {
|
|
342
|
+
return; // Ignore malformed messages
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const op = msg.op as string | undefined;
|
|
346
|
+
if (!op) return;
|
|
347
|
+
|
|
348
|
+
switch (op) {
|
|
349
|
+
case "publish": {
|
|
350
|
+
// Incoming topic message — route to topic subscribers
|
|
351
|
+
const topic = msg.topic as string;
|
|
352
|
+
const payload = msg.msg as Record<string, unknown>;
|
|
353
|
+
const handlers = this.messageHandlers.get(topic);
|
|
354
|
+
if (handlers) {
|
|
355
|
+
for (const handler of handlers) {
|
|
356
|
+
handler(payload);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
case "service_response": {
|
|
363
|
+
// Response to a call_service request
|
|
364
|
+
const id = msg.id as string | undefined;
|
|
365
|
+
if (id) {
|
|
366
|
+
this.resolvePending(id, msg);
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
case "action_result": {
|
|
372
|
+
// Final result of an action goal
|
|
373
|
+
const id = msg.id as string | undefined;
|
|
374
|
+
if (id) {
|
|
375
|
+
this.resolvePending(id, msg);
|
|
376
|
+
}
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
case "action_feedback": {
|
|
381
|
+
// Intermediate feedback for an action goal — route to feedback handlers
|
|
382
|
+
const id = msg.id as string | undefined;
|
|
383
|
+
if (id) {
|
|
384
|
+
const handlers = this.messageHandlers.get(`__action_feedback__${id}`);
|
|
385
|
+
if (handlers) {
|
|
386
|
+
for (const handler of handlers) {
|
|
387
|
+
handler(msg as Record<string, unknown>);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Attempt to reconnect with exponential backoff. */
|
|
397
|
+
private attemptReconnect(): void {
|
|
398
|
+
if (this.intentionalClose) return;
|
|
399
|
+
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) return;
|
|
400
|
+
|
|
401
|
+
this.reconnectAttempts++;
|
|
402
|
+
|
|
403
|
+
// Exponential backoff: interval * 2^(attempt-1), capped at 30s
|
|
404
|
+
const delay = Math.min(
|
|
405
|
+
this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1),
|
|
406
|
+
30_000,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
410
|
+
this.reconnectTimer = null;
|
|
411
|
+
if (this.intentionalClose) return;
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
await this.connect();
|
|
415
|
+
// Re-subscribe to all active topics on successful reconnect
|
|
416
|
+
for (const topic of this.messageHandlers.keys()) {
|
|
417
|
+
if (topic.startsWith("__action_feedback__")) continue;
|
|
418
|
+
this.send({
|
|
419
|
+
op: "subscribe",
|
|
420
|
+
id: this.nextId("resub"),
|
|
421
|
+
topic,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
} catch {
|
|
425
|
+
// connect() failed — onclose will trigger another attemptReconnect
|
|
426
|
+
}
|
|
427
|
+
}, delay);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** Reject all pending requests (used on disconnect/close). */
|
|
431
|
+
private rejectAllPending(error: Error): void {
|
|
432
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
433
|
+
clearTimeout(pending.timer);
|
|
434
|
+
pending.reject(error);
|
|
435
|
+
}
|
|
436
|
+
this.pendingRequests.clear();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RosbridgeClient } from "./client.js";
|
|
2
|
+
import type { ServiceResponseMessage } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Call a ROS2 service via rosbridge.
|
|
6
|
+
*
|
|
7
|
+
* @param client - The rosbridge client instance
|
|
8
|
+
* @param service - The service name (e.g., "/my_node/set_parameters")
|
|
9
|
+
* @param args - The service request arguments
|
|
10
|
+
* @param type - Optional service type
|
|
11
|
+
* @param timeoutMs - Request timeout in milliseconds (default 30s)
|
|
12
|
+
* @returns The service response
|
|
13
|
+
*/
|
|
14
|
+
export async function callService(
|
|
15
|
+
client: RosbridgeClient,
|
|
16
|
+
service: string,
|
|
17
|
+
args?: Record<string, unknown>,
|
|
18
|
+
type?: string,
|
|
19
|
+
timeoutMs = 30_000,
|
|
20
|
+
): Promise<ServiceResponseMessage> {
|
|
21
|
+
const id = client.nextId("service");
|
|
22
|
+
|
|
23
|
+
const responsePromise = new Promise<ServiceResponseMessage>((resolve, reject) => {
|
|
24
|
+
client.registerPending(
|
|
25
|
+
id,
|
|
26
|
+
(result) => resolve(result as ServiceResponseMessage),
|
|
27
|
+
reject,
|
|
28
|
+
timeoutMs,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
client.send({
|
|
33
|
+
op: "call_service",
|
|
34
|
+
id,
|
|
35
|
+
service,
|
|
36
|
+
args,
|
|
37
|
+
type,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return responsePromise;
|
|
41
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RosbridgeClient } from "./client.js";
|
|
2
|
+
import type { MessageHandler } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Helper for publishing messages to a ROS2 topic.
|
|
6
|
+
*/
|
|
7
|
+
export class TopicPublisher {
|
|
8
|
+
constructor(
|
|
9
|
+
private client: RosbridgeClient,
|
|
10
|
+
private topic: string,
|
|
11
|
+
private type: string,
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
/** Publish a message to the topic. */
|
|
15
|
+
publish(msg: Record<string, unknown>): void {
|
|
16
|
+
this.client.send({
|
|
17
|
+
op: "publish",
|
|
18
|
+
topic: this.topic,
|
|
19
|
+
type: this.type,
|
|
20
|
+
msg,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Helper for subscribing to messages from a ROS2 topic.
|
|
27
|
+
*/
|
|
28
|
+
export class TopicSubscriber {
|
|
29
|
+
private unsubscribeFromClient: (() => void) | null = null;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private client: RosbridgeClient,
|
|
33
|
+
private topic: string,
|
|
34
|
+
private type?: string,
|
|
35
|
+
) {}
|
|
36
|
+
|
|
37
|
+
/** Subscribe to the topic and receive messages via the handler. */
|
|
38
|
+
subscribe(handler: MessageHandler): void {
|
|
39
|
+
this.unsubscribeFromClient = this.client.onMessage(this.topic, handler);
|
|
40
|
+
this.client.send({
|
|
41
|
+
op: "subscribe",
|
|
42
|
+
id: this.client.nextId("subscribe"),
|
|
43
|
+
topic: this.topic,
|
|
44
|
+
type: this.type,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Unsubscribe from the topic. */
|
|
49
|
+
unsubscribe(): void {
|
|
50
|
+
if (this.unsubscribeFromClient) {
|
|
51
|
+
this.unsubscribeFromClient();
|
|
52
|
+
this.unsubscribeFromClient = null;
|
|
53
|
+
}
|
|
54
|
+
this.client.send({
|
|
55
|
+
op: "unsubscribe",
|
|
56
|
+
id: this.client.nextId("unsubscribe"),
|
|
57
|
+
topic: this.topic,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rosbridge WebSocket protocol types.
|
|
3
|
+
* @see https://github.com/RobotWebTools/rosbridge_suite/blob/ros2/ROSBRIDGE_PROTOCOL.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// --- Connection ---
|
|
7
|
+
|
|
8
|
+
export interface RosbridgeClientOptions {
|
|
9
|
+
url: string;
|
|
10
|
+
reconnect?: boolean;
|
|
11
|
+
reconnectInterval?: number;
|
|
12
|
+
maxReconnectAttempts?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ConnectionStatus = "disconnected" | "connecting" | "connected";
|
|
16
|
+
|
|
17
|
+
// --- Core Protocol Messages ---
|
|
18
|
+
|
|
19
|
+
export interface RosbridgeMessage {
|
|
20
|
+
op: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --- Publish / Subscribe ---
|
|
25
|
+
|
|
26
|
+
export interface PublishMessage extends RosbridgeMessage {
|
|
27
|
+
op: "publish";
|
|
28
|
+
topic: string;
|
|
29
|
+
msg: Record<string, unknown>;
|
|
30
|
+
type?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SubscribeMessage extends RosbridgeMessage {
|
|
34
|
+
op: "subscribe";
|
|
35
|
+
topic: string;
|
|
36
|
+
type?: string;
|
|
37
|
+
throttle_rate?: number;
|
|
38
|
+
queue_length?: number;
|
|
39
|
+
fragment_size?: number;
|
|
40
|
+
compression?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UnsubscribeMessage extends RosbridgeMessage {
|
|
44
|
+
op: "unsubscribe";
|
|
45
|
+
topic: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TopicMessage extends RosbridgeMessage {
|
|
49
|
+
op: "publish";
|
|
50
|
+
topic: string;
|
|
51
|
+
msg: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Service Call / Response ---
|
|
55
|
+
|
|
56
|
+
export interface ServiceCallMessage extends RosbridgeMessage {
|
|
57
|
+
op: "call_service";
|
|
58
|
+
service: string;
|
|
59
|
+
args?: Record<string, unknown>;
|
|
60
|
+
type?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ServiceResponseMessage extends RosbridgeMessage {
|
|
64
|
+
op: "service_response";
|
|
65
|
+
service: string;
|
|
66
|
+
values?: Record<string, unknown>;
|
|
67
|
+
result: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// --- Action Goal / Feedback / Result ---
|
|
71
|
+
|
|
72
|
+
export interface ActionGoalMessage extends RosbridgeMessage {
|
|
73
|
+
op: "send_action_goal";
|
|
74
|
+
action: string;
|
|
75
|
+
action_type: string;
|
|
76
|
+
args?: Record<string, unknown>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ActionFeedbackMessage extends RosbridgeMessage {
|
|
80
|
+
op: "action_feedback";
|
|
81
|
+
action: string;
|
|
82
|
+
values: Record<string, unknown>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ActionResultMessage extends RosbridgeMessage {
|
|
86
|
+
op: "action_result";
|
|
87
|
+
action: string;
|
|
88
|
+
values?: Record<string, unknown>;
|
|
89
|
+
result: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ActionCancelMessage extends RosbridgeMessage {
|
|
93
|
+
op: "cancel_action_goal";
|
|
94
|
+
action: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// --- Introspection ---
|
|
98
|
+
|
|
99
|
+
export interface TopicInfo {
|
|
100
|
+
name: string;
|
|
101
|
+
type: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ServiceInfo {
|
|
105
|
+
name: string;
|
|
106
|
+
type: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ActionInfo {
|
|
110
|
+
name: string;
|
|
111
|
+
type: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- Event Handlers ---
|
|
115
|
+
|
|
116
|
+
export type MessageHandler = (msg: Record<string, unknown>) => void;
|
|
117
|
+
|
|
118
|
+
export type ConnectionHandler = (status: ConnectionStatus) => void;
|