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,406 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import type { RosTransport } from "../transport.js";
|
|
3
|
+
import type {
|
|
4
|
+
ConnectionStatus,
|
|
5
|
+
ConnectionHandler,
|
|
6
|
+
Subscription,
|
|
7
|
+
PublishOptions,
|
|
8
|
+
SubscribeOptions,
|
|
9
|
+
ServiceCallOptions,
|
|
10
|
+
ServiceCallResult,
|
|
11
|
+
ActionGoalOptions,
|
|
12
|
+
ActionResult,
|
|
13
|
+
TopicInfo,
|
|
14
|
+
ServiceInfo,
|
|
15
|
+
ActionInfo,
|
|
16
|
+
MessageHandler,
|
|
17
|
+
} from "../types.js";
|
|
18
|
+
import { EntityCache } from "./entities.js";
|
|
19
|
+
import { toRosMessage, fromRosMessage, loadMessageClass, clearTypeCache } from "./conversion.js";
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
|
|
23
|
+
export interface LocalTransportOptions {
|
|
24
|
+
domainId?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Internal ROS2 topics/services to filter from introspection results. */
|
|
28
|
+
const INTERNAL_TOPIC_PREFIXES = ["/rosout", "/parameter_events", "/agenticros/"];
|
|
29
|
+
const INTERNAL_SERVICE_SUFFIXES = [
|
|
30
|
+
"/describe_parameters",
|
|
31
|
+
"/get_parameter_types",
|
|
32
|
+
"/get_parameters",
|
|
33
|
+
"/list_parameters",
|
|
34
|
+
"/set_parameters",
|
|
35
|
+
"/set_parameters_atomically",
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Mode A transport: direct local DDS communication via rclnodejs.
|
|
40
|
+
*
|
|
41
|
+
* When OpenClaw runs on the robot itself, this transport talks to ROS2
|
|
42
|
+
* directly via the local DDS bus — no network intermediary needed.
|
|
43
|
+
*/
|
|
44
|
+
export class LocalTransport implements RosTransport {
|
|
45
|
+
private domainId: number;
|
|
46
|
+
private status: ConnectionStatus = "disconnected";
|
|
47
|
+
private connectionHandlers = new Set<ConnectionHandler>();
|
|
48
|
+
/** rclnodejs module — loaded dynamically at runtime (optional dep, no types). */
|
|
49
|
+
private rclnodejs: any = null;
|
|
50
|
+
private node: any = null;
|
|
51
|
+
private entityCache: EntityCache | null = null;
|
|
52
|
+
private activeGoals = new Map<string, any>();
|
|
53
|
+
|
|
54
|
+
/** Singleton guard — rclnodejs.init() must only be called once per process. */
|
|
55
|
+
private static rclInitialized = false;
|
|
56
|
+
|
|
57
|
+
constructor(options?: LocalTransportOptions) {
|
|
58
|
+
this.domainId = options?.domainId ?? 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// --- Connection lifecycle ---
|
|
62
|
+
|
|
63
|
+
async connect(): Promise<void> {
|
|
64
|
+
if (this.status === "connected") return;
|
|
65
|
+
this.setStatus("connecting");
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
this.rclnodejs = require("rclnodejs");
|
|
69
|
+
|
|
70
|
+
// Set domain ID before init if non-default
|
|
71
|
+
if (this.domainId !== 0) {
|
|
72
|
+
process.env.ROS_DOMAIN_ID = String(this.domainId);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Global init — only once per process
|
|
76
|
+
if (!LocalTransport.rclInitialized) {
|
|
77
|
+
await this.rclnodejs!.init();
|
|
78
|
+
LocalTransport.rclInitialized = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Use a process-unique node name. If two AgenticROS adapters (e.g. the
|
|
82
|
+
// OpenClaw gateway plugin + the Claude Code MCP server) share the host,
|
|
83
|
+
// a fixed name collides on the DDS bus: rcl logs "Publisher already
|
|
84
|
+
// registered for provided node name" and DDS discovery on the duplicate
|
|
85
|
+
// becomes slow/lossy — listTopics() then returns 0 right after connect.
|
|
86
|
+
const nodeName = `agenticros_local_${process.pid}`;
|
|
87
|
+
this.node = this.rclnodejs!.createNode(nodeName);
|
|
88
|
+
this.rclnodejs!.spin(this.node);
|
|
89
|
+
this.entityCache = new EntityCache();
|
|
90
|
+
|
|
91
|
+
// Give DDS discovery a brief moment to receive announcements from peers
|
|
92
|
+
// before we report "connected". Without this the very first listTopics()
|
|
93
|
+
// can race the executor and return empty even though peers exist.
|
|
94
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 750));
|
|
95
|
+
|
|
96
|
+
this.setStatus("connected");
|
|
97
|
+
} catch (err) {
|
|
98
|
+
this.setStatus("disconnected");
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async disconnect(): Promise<void> {
|
|
104
|
+
if (this.status === "disconnected") return;
|
|
105
|
+
|
|
106
|
+
// Cancel any active action goals
|
|
107
|
+
for (const [action] of this.activeGoals) {
|
|
108
|
+
try {
|
|
109
|
+
await this.cancelActionGoal(action);
|
|
110
|
+
} catch {
|
|
111
|
+
// Best-effort
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.activeGoals.clear();
|
|
115
|
+
|
|
116
|
+
if (this.entityCache && this.node) {
|
|
117
|
+
this.entityCache.destroyAll(this.node);
|
|
118
|
+
this.entityCache = null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.node) {
|
|
122
|
+
this.node.destroy();
|
|
123
|
+
this.node = null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.rclnodejs && LocalTransport.rclInitialized) {
|
|
127
|
+
try {
|
|
128
|
+
this.rclnodejs.shutdown();
|
|
129
|
+
} catch {
|
|
130
|
+
// May already be shut down
|
|
131
|
+
}
|
|
132
|
+
LocalTransport.rclInitialized = false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
clearTypeCache();
|
|
136
|
+
this.rclnodejs = null;
|
|
137
|
+
this.setStatus("disconnected");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getStatus(): ConnectionStatus {
|
|
141
|
+
return this.status;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onConnection(handler: ConnectionHandler): () => void {
|
|
145
|
+
this.connectionHandlers.add(handler);
|
|
146
|
+
return () => {
|
|
147
|
+
this.connectionHandlers.delete(handler);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- Topics ---
|
|
152
|
+
|
|
153
|
+
publish(options: PublishOptions): void {
|
|
154
|
+
this.ensureConnected();
|
|
155
|
+
const publisher = this.entityCache!.getPublisher(this.node, options.topic, options.type);
|
|
156
|
+
const rosMsg = toRosMessage(options.type, options.msg);
|
|
157
|
+
publisher.publish(rosMsg);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
subscribe(options: SubscribeOptions, handler: MessageHandler): Subscription {
|
|
161
|
+
this.ensureConnected();
|
|
162
|
+
const type = options.type ?? this.resolveTopicType(options.topic);
|
|
163
|
+
if (!type) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Cannot subscribe to ${options.topic}: type is required when it cannot be inferred`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return this.entityCache!.addSubscription(this.node, options.topic, type, handler);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- Services ---
|
|
172
|
+
|
|
173
|
+
async callService(options: ServiceCallOptions): Promise<ServiceCallResult> {
|
|
174
|
+
this.ensureConnected();
|
|
175
|
+
|
|
176
|
+
const type = options.type ?? this.resolveServiceType(options.service);
|
|
177
|
+
if (!type) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Cannot call service ${options.service}: type is required when it cannot be inferred`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const client = this.entityCache!.getServiceClient(this.node, options.service, type);
|
|
184
|
+
|
|
185
|
+
// Wait for service to become available (5s timeout)
|
|
186
|
+
const available = await client.waitForService(5000);
|
|
187
|
+
if (!available) {
|
|
188
|
+
throw new Error(`Service ${options.service} not available after 5 seconds`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Create request from args
|
|
192
|
+
const ServiceClass = loadMessageClass(type);
|
|
193
|
+
const request = new ServiceClass.Request();
|
|
194
|
+
if (options.args) {
|
|
195
|
+
for (const [key, value] of Object.entries(options.args)) {
|
|
196
|
+
request[key] = value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Send request with Promise wrapping + timeout
|
|
201
|
+
const response = await this.sendServiceRequest(client, request, 30_000);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
result: true,
|
|
205
|
+
values: fromRosMessage(response),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// --- Actions ---
|
|
210
|
+
|
|
211
|
+
async sendActionGoal(options: ActionGoalOptions): Promise<ActionResult> {
|
|
212
|
+
this.ensureConnected();
|
|
213
|
+
|
|
214
|
+
const ActionClass = loadMessageClass(options.actionType);
|
|
215
|
+
const actionClient = new (this.rclnodejs!.ActionClient as any)(
|
|
216
|
+
this.node,
|
|
217
|
+
ActionClass,
|
|
218
|
+
options.action,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Wait for action server (5s)
|
|
222
|
+
const available = await actionClient.waitForServer(5000);
|
|
223
|
+
if (!available) {
|
|
224
|
+
actionClient.destroy();
|
|
225
|
+
throw new Error(`Action server ${options.action} not available after 5 seconds`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Build goal message
|
|
229
|
+
const goal = new ActionClass.Goal();
|
|
230
|
+
if (options.args) {
|
|
231
|
+
for (const [key, value] of Object.entries(options.args)) {
|
|
232
|
+
goal[key] = value;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const result = await new Promise<Record<string, unknown>>((resolve, reject) => {
|
|
238
|
+
const timer = setTimeout(() => {
|
|
239
|
+
this.activeGoals.delete(options.action);
|
|
240
|
+
reject(new Error(`Action ${options.action} timed out after 120 seconds`));
|
|
241
|
+
}, 120_000);
|
|
242
|
+
|
|
243
|
+
actionClient.sendGoal(
|
|
244
|
+
goal,
|
|
245
|
+
(goalHandle: any) => {
|
|
246
|
+
// Goal response callback — store for cancellation
|
|
247
|
+
this.activeGoals.set(options.action, goalHandle);
|
|
248
|
+
},
|
|
249
|
+
(feedback: any) => {
|
|
250
|
+
// Feedback callback
|
|
251
|
+
if (options.onFeedback) {
|
|
252
|
+
options.onFeedback(fromRosMessage(feedback));
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
(resultResponse: any) => {
|
|
256
|
+
// Result callback
|
|
257
|
+
clearTimeout(timer);
|
|
258
|
+
this.activeGoals.delete(options.action);
|
|
259
|
+
resolve(fromRosMessage(resultResponse));
|
|
260
|
+
},
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return { result: true, values: result };
|
|
265
|
+
} finally {
|
|
266
|
+
actionClient.destroy();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async cancelActionGoal(action: string): Promise<void> {
|
|
271
|
+
const goalHandle = this.activeGoals.get(action);
|
|
272
|
+
if (goalHandle && typeof goalHandle.cancelGoal === "function") {
|
|
273
|
+
await goalHandle.cancelGoal();
|
|
274
|
+
this.activeGoals.delete(action);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// --- Introspection ---
|
|
279
|
+
|
|
280
|
+
async listTopics(): Promise<TopicInfo[]> {
|
|
281
|
+
this.ensureConnected();
|
|
282
|
+
|
|
283
|
+
// DDS discovery is asynchronous. On a freshly-connected node,
|
|
284
|
+
// getTopicNamesAndTypes() trickles in results as peers respond - the
|
|
285
|
+
// first call may return 0, the second 2, the third all of them.
|
|
286
|
+
// Poll until the count is stable across two consecutive 300ms reads (or
|
|
287
|
+
// time-bounded to ~3s) so we don't return a partial view.
|
|
288
|
+
const externalFilter = (t: { name: string; types: string[] }) =>
|
|
289
|
+
!INTERNAL_TOPIC_PREFIXES.some((prefix) => t.name.startsWith(prefix));
|
|
290
|
+
|
|
291
|
+
const deadline = Date.now() + 3000;
|
|
292
|
+
let raw: Array<{ name: string; types: string[] }> = [];
|
|
293
|
+
let prevCount = -1;
|
|
294
|
+
let stableHits = 0;
|
|
295
|
+
while (Date.now() < deadline) {
|
|
296
|
+
raw = this.node.getTopicNamesAndTypes();
|
|
297
|
+
const externalCount = raw.filter(externalFilter).length;
|
|
298
|
+
if (externalCount === prevCount && externalCount > 0) {
|
|
299
|
+
stableHits++;
|
|
300
|
+
if (stableHits >= 1) break; // one stable confirmation is enough
|
|
301
|
+
} else {
|
|
302
|
+
stableHits = 0;
|
|
303
|
+
prevCount = externalCount;
|
|
304
|
+
}
|
|
305
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 300));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return raw.filter(externalFilter).map((t) => ({ name: t.name, type: t.types[0] ?? "" }));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async listServices(): Promise<ServiceInfo[]> {
|
|
312
|
+
this.ensureConnected();
|
|
313
|
+
|
|
314
|
+
const namesAndTypes: Array<{ name: string; types: string[] }> =
|
|
315
|
+
this.node.getServiceNamesAndTypes();
|
|
316
|
+
|
|
317
|
+
return namesAndTypes
|
|
318
|
+
.filter((s) => !INTERNAL_SERVICE_SUFFIXES.some((suffix) => s.name.endsWith(suffix)))
|
|
319
|
+
.filter((s) => !INTERNAL_TOPIC_PREFIXES.some((prefix) => s.name.startsWith(prefix)))
|
|
320
|
+
.map((s) => ({ name: s.name, type: s.types[0] ?? "" }));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async listActions(): Promise<ActionInfo[]> {
|
|
324
|
+
// Same feedback-topic heuristic as rosbridge/adapter.ts
|
|
325
|
+
const topics = await this.listTopics();
|
|
326
|
+
const actions: ActionInfo[] = [];
|
|
327
|
+
const feedbackSuffix = "/_action/feedback";
|
|
328
|
+
|
|
329
|
+
for (const topic of topics) {
|
|
330
|
+
if (topic.name.endsWith(feedbackSuffix)) {
|
|
331
|
+
const actionName = topic.name.slice(0, -feedbackSuffix.length);
|
|
332
|
+
let actionType = topic.type;
|
|
333
|
+
if (actionType.endsWith("_FeedbackMessage")) {
|
|
334
|
+
actionType = actionType.slice(0, -"_FeedbackMessage".length);
|
|
335
|
+
}
|
|
336
|
+
actions.push({ name: actionName, type: actionType });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return actions;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// --- Private helpers ---
|
|
344
|
+
|
|
345
|
+
private setStatus(status: ConnectionStatus): void {
|
|
346
|
+
this.status = status;
|
|
347
|
+
for (const handler of this.connectionHandlers) {
|
|
348
|
+
handler(status);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private ensureConnected(): void {
|
|
353
|
+
if (this.status !== "connected" || !this.node || !this.entityCache) {
|
|
354
|
+
throw new Error("LocalTransport is not connected");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Resolve a topic's type from the node's graph introspection.
|
|
360
|
+
* Returns undefined if the topic is not yet known.
|
|
361
|
+
*/
|
|
362
|
+
private resolveTopicType(topic: string): string | undefined {
|
|
363
|
+
if (!this.node) return undefined;
|
|
364
|
+
const namesAndTypes: Array<{ name: string; types: string[] }> =
|
|
365
|
+
this.node.getTopicNamesAndTypes();
|
|
366
|
+
const entry = namesAndTypes.find((t) => t.name === topic);
|
|
367
|
+
return entry?.types[0];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Resolve a service's type from the node's graph introspection.
|
|
372
|
+
* Returns undefined if the service is not yet known.
|
|
373
|
+
*/
|
|
374
|
+
private resolveServiceType(service: string): string | undefined {
|
|
375
|
+
if (!this.node) return undefined;
|
|
376
|
+
const namesAndTypes: Array<{ name: string; types: string[] }> =
|
|
377
|
+
this.node.getServiceNamesAndTypes();
|
|
378
|
+
const entry = namesAndTypes.find((s) => s.name === service);
|
|
379
|
+
return entry?.types[0];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Wrap rclnodejs callback-based sendRequest in a Promise with timeout.
|
|
384
|
+
*/
|
|
385
|
+
private sendServiceRequest(client: any, request: any, timeoutMs: number): Promise<any> {
|
|
386
|
+
return new Promise((resolve, reject) => {
|
|
387
|
+
const timer = setTimeout(() => {
|
|
388
|
+
reject(new Error(`Service call timed out after ${timeoutMs}ms`));
|
|
389
|
+
}, timeoutMs);
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
client.sendRequest(request, (response: any) => {
|
|
393
|
+
clearTimeout(timer);
|
|
394
|
+
if (response) {
|
|
395
|
+
resolve(response);
|
|
396
|
+
} else {
|
|
397
|
+
reject(new Error("Service returned no response"));
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
} catch (err) {
|
|
401
|
+
clearTimeout(timer);
|
|
402
|
+
reject(err);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { RosbridgeClient } from "./client.js";
|
|
2
|
+
import type {
|
|
3
|
+
ActionResultMessage,
|
|
4
|
+
ActionFeedbackMessage,
|
|
5
|
+
} from "./types.js";
|
|
6
|
+
|
|
7
|
+
export interface ActionGoalOptions {
|
|
8
|
+
action: string;
|
|
9
|
+
actionType: string;
|
|
10
|
+
args?: Record<string, unknown>;
|
|
11
|
+
onFeedback?: (feedback: ActionFeedbackMessage) => void;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Client for sending action goals and receiving feedback/results.
|
|
17
|
+
*/
|
|
18
|
+
export class ActionClient {
|
|
19
|
+
constructor(private client: RosbridgeClient) {}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Send an action goal and wait for the result.
|
|
23
|
+
*
|
|
24
|
+
* @param options - Action goal options including feedback handler
|
|
25
|
+
* @returns The action result
|
|
26
|
+
*/
|
|
27
|
+
async sendGoal(options: ActionGoalOptions): Promise<ActionResultMessage> {
|
|
28
|
+
const id = this.client.nextId("action");
|
|
29
|
+
const timeoutMs = options.timeoutMs ?? 120_000; // Actions can be long-running
|
|
30
|
+
|
|
31
|
+
// Register feedback handler if provided
|
|
32
|
+
let removeFeedbackHandler: (() => void) | null = null;
|
|
33
|
+
if (options.onFeedback) {
|
|
34
|
+
const feedbackKey = `__action_feedback__${id}`;
|
|
35
|
+
removeFeedbackHandler = this.client.onMessage(feedbackKey, (msg) => {
|
|
36
|
+
options.onFeedback!(msg as unknown as ActionFeedbackMessage);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Create promise that resolves on action_result
|
|
41
|
+
const resultPromise = new Promise<ActionResultMessage>((resolve, reject) => {
|
|
42
|
+
this.client.registerPending(
|
|
43
|
+
id,
|
|
44
|
+
(result) => resolve(result as ActionResultMessage),
|
|
45
|
+
reject,
|
|
46
|
+
timeoutMs,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Send the goal
|
|
51
|
+
this.client.send({
|
|
52
|
+
op: "send_action_goal",
|
|
53
|
+
id,
|
|
54
|
+
action: options.action,
|
|
55
|
+
action_type: options.actionType,
|
|
56
|
+
args: options.args,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
return await resultPromise;
|
|
61
|
+
} finally {
|
|
62
|
+
// Clean up feedback handler regardless of outcome
|
|
63
|
+
if (removeFeedbackHandler) {
|
|
64
|
+
removeFeedbackHandler();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Cancel an in-progress action goal.
|
|
71
|
+
*
|
|
72
|
+
* @param action - The action server name
|
|
73
|
+
*/
|
|
74
|
+
async cancelGoal(action: string): Promise<void> {
|
|
75
|
+
this.client.send({
|
|
76
|
+
op: "cancel_action_goal",
|
|
77
|
+
id: this.client.nextId("cancel"),
|
|
78
|
+
action,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { RosTransport } from "../transport.js";
|
|
2
|
+
import type {
|
|
3
|
+
ConnectionStatus,
|
|
4
|
+
ConnectionHandler,
|
|
5
|
+
Subscription,
|
|
6
|
+
PublishOptions,
|
|
7
|
+
AdvertiseOptions,
|
|
8
|
+
SubscribeOptions,
|
|
9
|
+
ServiceCallOptions,
|
|
10
|
+
ServiceCallResult,
|
|
11
|
+
ActionGoalOptions,
|
|
12
|
+
ActionResult,
|
|
13
|
+
TopicInfo,
|
|
14
|
+
ServiceInfo,
|
|
15
|
+
ActionInfo,
|
|
16
|
+
MessageHandler,
|
|
17
|
+
} from "../types.js";
|
|
18
|
+
import { RosbridgeClient } from "./client.js";
|
|
19
|
+
import { TopicPublisher, TopicSubscriber } from "./topics.js";
|
|
20
|
+
import { callService } from "./services.js";
|
|
21
|
+
import { ActionClient } from "./actions.js";
|
|
22
|
+
import type { RosbridgeClientOptions } from "./types.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* RosTransport adapter that wraps the existing RosbridgeClient.
|
|
26
|
+
*
|
|
27
|
+
* This is the Mode B (Local Network) transport. It connects to a
|
|
28
|
+
* rosbridge_server running on the robot via WebSocket and translates
|
|
29
|
+
* RosTransport method calls into rosbridge protocol messages.
|
|
30
|
+
*/
|
|
31
|
+
export class RosbridgeTransport implements RosTransport {
|
|
32
|
+
private client: RosbridgeClient;
|
|
33
|
+
private actionClient: ActionClient;
|
|
34
|
+
|
|
35
|
+
constructor(options: RosbridgeClientOptions) {
|
|
36
|
+
this.client = new RosbridgeClient(options);
|
|
37
|
+
this.actionClient = new ActionClient(this.client);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async connect(): Promise<void> {
|
|
41
|
+
await this.client.connect();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async disconnect(): Promise<void> {
|
|
45
|
+
await this.client.disconnect();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getStatus(): ConnectionStatus {
|
|
49
|
+
return this.client.getStatus();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onConnection(handler: ConnectionHandler): () => void {
|
|
53
|
+
return this.client.onConnection(handler);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
advertise(options: AdvertiseOptions): void {
|
|
57
|
+
this.client.send({
|
|
58
|
+
op: "advertise",
|
|
59
|
+
topic: options.topic,
|
|
60
|
+
type: options.type,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
publish(options: PublishOptions): void {
|
|
65
|
+
const publisher = new TopicPublisher(this.client, options.topic, options.type);
|
|
66
|
+
publisher.publish(options.msg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
subscribe(options: SubscribeOptions, handler: MessageHandler): Subscription {
|
|
70
|
+
const subscriber = new TopicSubscriber(this.client, options.topic, options.type);
|
|
71
|
+
subscriber.subscribe(handler);
|
|
72
|
+
return {
|
|
73
|
+
unsubscribe() {
|
|
74
|
+
subscriber.unsubscribe();
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async callService(options: ServiceCallOptions): Promise<ServiceCallResult> {
|
|
80
|
+
const response = await callService(
|
|
81
|
+
this.client,
|
|
82
|
+
options.service,
|
|
83
|
+
options.args,
|
|
84
|
+
options.type,
|
|
85
|
+
);
|
|
86
|
+
return {
|
|
87
|
+
result: response.result,
|
|
88
|
+
values: response.values,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async sendActionGoal(options: ActionGoalOptions): Promise<ActionResult> {
|
|
93
|
+
const response = await this.actionClient.sendGoal({
|
|
94
|
+
action: options.action,
|
|
95
|
+
actionType: options.actionType,
|
|
96
|
+
args: options.args,
|
|
97
|
+
onFeedback: options.onFeedback
|
|
98
|
+
? (feedback) => options.onFeedback!(feedback.values)
|
|
99
|
+
: undefined,
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
result: response.result,
|
|
103
|
+
values: response.values,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async cancelActionGoal(action: string): Promise<void> {
|
|
108
|
+
await this.actionClient.cancelGoal(action);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async listTopics(): Promise<TopicInfo[]> {
|
|
112
|
+
const response = await callService(
|
|
113
|
+
this.client,
|
|
114
|
+
"/rosapi/topics",
|
|
115
|
+
{},
|
|
116
|
+
"rosapi/srv/Topics",
|
|
117
|
+
);
|
|
118
|
+
const topics = (response.values?.["topics"] as string[]) ?? [];
|
|
119
|
+
const types = (response.values?.["types"] as string[]) ?? [];
|
|
120
|
+
return topics.map((name, i) => ({ name, type: types[i] ?? "" }));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async listServices(): Promise<ServiceInfo[]> {
|
|
124
|
+
const response = await callService(
|
|
125
|
+
this.client,
|
|
126
|
+
"/rosapi/services",
|
|
127
|
+
{},
|
|
128
|
+
"rosapi/srv/Services",
|
|
129
|
+
);
|
|
130
|
+
const services = (response.values?.["services"] as string[]) ?? [];
|
|
131
|
+
const types = (response.values?.["types"] as string[]) ?? [];
|
|
132
|
+
return services.map((name, i) => ({ name, type: types[i] ?? "" }));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async listActions(): Promise<ActionInfo[]> {
|
|
136
|
+
// rosapi has no built-in action listing. Heuristic: action servers expose
|
|
137
|
+
// topics matching */_action/feedback. Extract action names from that pattern.
|
|
138
|
+
const topics = await this.listTopics();
|
|
139
|
+
const actions: ActionInfo[] = [];
|
|
140
|
+
const feedbackSuffix = "/_action/feedback";
|
|
141
|
+
|
|
142
|
+
for (const topic of topics) {
|
|
143
|
+
if (topic.name.endsWith(feedbackSuffix)) {
|
|
144
|
+
const actionName = topic.name.slice(0, -feedbackSuffix.length);
|
|
145
|
+
// Feedback type is like "pkg/action/Name_FeedbackMessage"
|
|
146
|
+
// Extract base action type by stripping "_FeedbackMessage" suffix
|
|
147
|
+
let actionType = topic.type;
|
|
148
|
+
if (actionType.endsWith("_FeedbackMessage")) {
|
|
149
|
+
actionType = actionType.slice(0, -"_FeedbackMessage".length);
|
|
150
|
+
}
|
|
151
|
+
actions.push({ name: actionName, type: actionType });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return actions;
|
|
156
|
+
}
|
|
157
|
+
}
|