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.
Files changed (330) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +90 -4
  3. package/dist/commands/config.d.ts +20 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +179 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +33 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +232 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/down.d.ts +15 -0
  12. package/dist/commands/down.d.ts.map +1 -0
  13. package/dist/commands/down.js +91 -0
  14. package/dist/commands/down.js.map +1 -0
  15. package/dist/commands/init.d.ts +21 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +259 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/logs.d.ts +18 -0
  20. package/dist/commands/logs.d.ts.map +1 -0
  21. package/dist/commands/logs.js +67 -0
  22. package/dist/commands/logs.js.map +1 -0
  23. package/dist/commands/status.d.ts +12 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +56 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/commands/up.d.ts +20 -0
  28. package/dist/commands/up.d.ts.map +1 -0
  29. package/dist/commands/up.js +70 -0
  30. package/dist/commands/up.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +107 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/menu.d.ts +9 -0
  36. package/dist/menu.d.ts.map +1 -0
  37. package/dist/menu.js +96 -0
  38. package/dist/menu.js.map +1 -0
  39. package/dist/runners/real-robot.d.ts +15 -0
  40. package/dist/runners/real-robot.d.ts.map +1 -0
  41. package/dist/runners/real-robot.js +46 -0
  42. package/dist/runners/real-robot.js.map +1 -0
  43. package/dist/runners/sim.d.ts +19 -0
  44. package/dist/runners/sim.d.ts.map +1 -0
  45. package/dist/runners/sim.js +53 -0
  46. package/dist/runners/sim.js.map +1 -0
  47. package/dist/util/env.d.ts +24 -0
  48. package/dist/util/env.d.ts.map +1 -0
  49. package/dist/util/env.js +53 -0
  50. package/dist/util/env.js.map +1 -0
  51. package/dist/util/logger.d.ts +24 -0
  52. package/dist/util/logger.d.ts.map +1 -0
  53. package/dist/util/logger.js +62 -0
  54. package/dist/util/logger.js.map +1 -0
  55. package/dist/util/paths.d.ts +57 -0
  56. package/dist/util/paths.d.ts.map +1 -0
  57. package/dist/util/paths.js +132 -0
  58. package/dist/util/paths.js.map +1 -0
  59. package/dist/util/pidfile.d.ts +16 -0
  60. package/dist/util/pidfile.d.ts.map +1 -0
  61. package/dist/util/pidfile.js +63 -0
  62. package/dist/util/pidfile.js.map +1 -0
  63. package/dist/util/state.d.ts +26 -0
  64. package/dist/util/state.d.ts.map +1 -0
  65. package/dist/util/state.js +55 -0
  66. package/dist/util/state.js.map +1 -0
  67. package/package.json +60 -1
  68. package/runtime/BUNDLE.json +11 -0
  69. package/runtime/LICENSE +192 -0
  70. package/runtime/README.md +273 -0
  71. package/runtime/docs/architecture.md +366 -0
  72. package/runtime/docs/cli.md +140 -0
  73. package/runtime/docs/memory.md +292 -0
  74. package/runtime/docs/robot-setup.md +347 -0
  75. package/runtime/package.json +28 -0
  76. package/runtime/packages/agenticros/agenticros-agenticros-0.0.1.tgz +0 -0
  77. package/runtime/packages/agenticros/openclaw.plugin.json +451 -0
  78. package/runtime/packages/agenticros/package.json +41 -0
  79. package/runtime/packages/agenticros/src/camera-snapshot-cache.ts +59 -0
  80. package/runtime/packages/agenticros/src/camera-snapshot-routes.ts +44 -0
  81. package/runtime/packages/agenticros/src/commands/estop.ts +41 -0
  82. package/runtime/packages/agenticros/src/commands/transport.ts +195 -0
  83. package/runtime/packages/agenticros/src/config-file.ts +136 -0
  84. package/runtime/packages/agenticros/src/config-page.ts +498 -0
  85. package/runtime/packages/agenticros/src/context/robot-context.ts +373 -0
  86. package/runtime/packages/agenticros/src/depth.ts +313 -0
  87. package/runtime/packages/agenticros/src/describer.ts +157 -0
  88. package/runtime/packages/agenticros/src/image-binary-trim.ts +16 -0
  89. package/runtime/packages/agenticros/src/index.ts +85 -0
  90. package/runtime/packages/agenticros/src/landing-page.ts +38 -0
  91. package/runtime/packages/agenticros/src/memory.ts +44 -0
  92. package/runtime/packages/agenticros/src/plugin-api.ts +173 -0
  93. package/runtime/packages/agenticros/src/plugin-image-base64.ts +69 -0
  94. package/runtime/packages/agenticros/src/preflight.ts +110 -0
  95. package/runtime/packages/agenticros/src/routes.ts +328 -0
  96. package/runtime/packages/agenticros/src/safety/validator.ts +43 -0
  97. package/runtime/packages/agenticros/src/service.ts +359 -0
  98. package/runtime/packages/agenticros/src/skill-api.ts +65 -0
  99. package/runtime/packages/agenticros/src/skill-loader.ts +146 -0
  100. package/runtime/packages/agenticros/src/teleop/page.ts +498 -0
  101. package/runtime/packages/agenticros/src/teleop/routes.ts +650 -0
  102. package/runtime/packages/agenticros/src/tools/index.ts +26 -0
  103. package/runtime/packages/agenticros/src/tools/ros2-action.ts +50 -0
  104. package/runtime/packages/agenticros/src/tools/ros2-camera.ts +221 -0
  105. package/runtime/packages/agenticros/src/tools/ros2-depth-distance.ts +58 -0
  106. package/runtime/packages/agenticros/src/tools/ros2-introspect.ts +62 -0
  107. package/runtime/packages/agenticros/src/tools/ros2-memory.ts +158 -0
  108. package/runtime/packages/agenticros/src/tools/ros2-param.ts +87 -0
  109. package/runtime/packages/agenticros/src/tools/ros2-publish.ts +52 -0
  110. package/runtime/packages/agenticros/src/tools/ros2-service.ts +46 -0
  111. package/runtime/packages/agenticros/src/tools/ros2-subscribe.ts +71 -0
  112. package/runtime/packages/agenticros/tsconfig.json +9 -0
  113. package/runtime/packages/agenticros-claude-code/README.md +260 -0
  114. package/runtime/packages/agenticros-claude-code/config.example.json +9 -0
  115. package/runtime/packages/agenticros-claude-code/dist/config.d.ts +8 -0
  116. package/runtime/packages/agenticros-claude-code/dist/config.d.ts.map +1 -0
  117. package/runtime/packages/agenticros-claude-code/dist/config.js +93 -0
  118. package/runtime/packages/agenticros-claude-code/dist/config.js.map +1 -0
  119. package/runtime/packages/agenticros-claude-code/dist/depth.d.ts +20 -0
  120. package/runtime/packages/agenticros-claude-code/dist/depth.d.ts.map +1 -0
  121. package/runtime/packages/agenticros-claude-code/dist/depth.js +126 -0
  122. package/runtime/packages/agenticros-claude-code/dist/depth.js.map +1 -0
  123. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts +6 -0
  124. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts.map +1 -0
  125. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js +36 -0
  126. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js.map +1 -0
  127. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts +33 -0
  128. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts.map +1 -0
  129. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js +134 -0
  130. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js.map +1 -0
  131. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts +43 -0
  132. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts.map +1 -0
  133. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js +73 -0
  134. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js.map +1 -0
  135. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts +58 -0
  136. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts.map +1 -0
  137. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js +251 -0
  138. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js.map +1 -0
  139. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts +61 -0
  140. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts.map +1 -0
  141. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js +268 -0
  142. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js.map +1 -0
  143. package/runtime/packages/agenticros-claude-code/dist/index.d.ts +3 -0
  144. package/runtime/packages/agenticros-claude-code/dist/index.d.ts.map +1 -0
  145. package/runtime/packages/agenticros-claude-code/dist/index.js +111 -0
  146. package/runtime/packages/agenticros-claude-code/dist/index.js.map +1 -0
  147. package/runtime/packages/agenticros-claude-code/dist/memory.d.ts +17 -0
  148. package/runtime/packages/agenticros-claude-code/dist/memory.d.ts.map +1 -0
  149. package/runtime/packages/agenticros-claude-code/dist/memory.js +44 -0
  150. package/runtime/packages/agenticros-claude-code/dist/memory.js.map +1 -0
  151. package/runtime/packages/agenticros-claude-code/dist/safety.d.ts +10 -0
  152. package/runtime/packages/agenticros-claude-code/dist/safety.d.ts.map +1 -0
  153. package/runtime/packages/agenticros-claude-code/dist/safety.js +34 -0
  154. package/runtime/packages/agenticros-claude-code/dist/safety.js.map +1 -0
  155. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts +36 -0
  156. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts.map +1 -0
  157. package/runtime/packages/agenticros-claude-code/dist/tools.js +777 -0
  158. package/runtime/packages/agenticros-claude-code/dist/tools.js.map +1 -0
  159. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts +17 -0
  160. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts.map +1 -0
  161. package/runtime/packages/agenticros-claude-code/dist/transport.js +46 -0
  162. package/runtime/packages/agenticros-claude-code/dist/transport.js.map +1 -0
  163. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts +42 -0
  164. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts.map +1 -0
  165. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js +114 -0
  166. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js.map +1 -0
  167. package/runtime/packages/agenticros-claude-code/package.json +29 -0
  168. package/runtime/packages/agenticros-claude-code/src/config.ts +96 -0
  169. package/runtime/packages/agenticros-claude-code/src/depth.ts +173 -0
  170. package/runtime/packages/agenticros-claude-code/src/find-object/coco-classes.ts +38 -0
  171. package/runtime/packages/agenticros-claude-code/src/find-object/find-object.ts +190 -0
  172. package/runtime/packages/agenticros-claude-code/src/follow-me/controller.ts +109 -0
  173. package/runtime/packages/agenticros-claude-code/src/follow-me/depth-loop.ts +420 -0
  174. package/runtime/packages/agenticros-claude-code/src/follow-me/detector.ts +303 -0
  175. package/runtime/packages/agenticros-claude-code/src/follow-me/loop.ts +330 -0
  176. package/runtime/packages/agenticros-claude-code/src/index.ts +125 -0
  177. package/runtime/packages/agenticros-claude-code/src/memory.ts +51 -0
  178. package/runtime/packages/agenticros-claude-code/src/safety.ts +44 -0
  179. package/runtime/packages/agenticros-claude-code/src/tools.ts +891 -0
  180. package/runtime/packages/agenticros-claude-code/src/transport.ts +58 -0
  181. package/runtime/packages/agenticros-claude-code/src/zero-shot/detector.ts +169 -0
  182. package/runtime/packages/agenticros-claude-code/tsconfig.json +9 -0
  183. package/runtime/packages/agenticros-claude-code/yolo-debug.mjs +106 -0
  184. package/runtime/packages/agenticros-gemini/README.md +139 -0
  185. package/runtime/packages/agenticros-gemini/package.json +28 -0
  186. package/runtime/packages/agenticros-gemini/scripts/smoke-api.mjs +42 -0
  187. package/runtime/packages/agenticros-gemini/src/chat.ts +139 -0
  188. package/runtime/packages/agenticros-gemini/src/config.ts +92 -0
  189. package/runtime/packages/agenticros-gemini/src/depth.ts +173 -0
  190. package/runtime/packages/agenticros-gemini/src/index.ts +58 -0
  191. package/runtime/packages/agenticros-gemini/src/memory.ts +32 -0
  192. package/runtime/packages/agenticros-gemini/src/safety.ts +44 -0
  193. package/runtime/packages/agenticros-gemini/src/tools.ts +516 -0
  194. package/runtime/packages/agenticros-gemini/src/transport.ts +58 -0
  195. package/runtime/packages/agenticros-gemini/tsconfig.json +8 -0
  196. package/runtime/packages/core/package.json +47 -0
  197. package/runtime/packages/core/src/banner.ts +32 -0
  198. package/runtime/packages/core/src/cmd-vel-twist.ts +31 -0
  199. package/runtime/packages/core/src/config.ts +279 -0
  200. package/runtime/packages/core/src/index.ts +54 -0
  201. package/runtime/packages/core/src/memory/__tests__/factory.test.ts +70 -0
  202. package/runtime/packages/core/src/memory/__tests__/local-provider.test.ts +195 -0
  203. package/runtime/packages/core/src/memory/__tests__/mem0-provider.test.ts +192 -0
  204. package/runtime/packages/core/src/memory/__tests__/smart-defaults.test.ts +46 -0
  205. package/runtime/packages/core/src/memory/factory.ts +63 -0
  206. package/runtime/packages/core/src/memory/index.ts +10 -0
  207. package/runtime/packages/core/src/memory/local/provider.ts +229 -0
  208. package/runtime/packages/core/src/memory/mem0/provider.ts +379 -0
  209. package/runtime/packages/core/src/memory/types.ts +96 -0
  210. package/runtime/packages/core/src/topic-utils.ts +95 -0
  211. package/runtime/packages/core/src/transport/factory.ts +47 -0
  212. package/runtime/packages/core/src/transport/local/conversion.ts +333 -0
  213. package/runtime/packages/core/src/transport/local/entities.ts +129 -0
  214. package/runtime/packages/core/src/transport/local/transport.ts +406 -0
  215. package/runtime/packages/core/src/transport/rosbridge/actions.ts +81 -0
  216. package/runtime/packages/core/src/transport/rosbridge/adapter.ts +157 -0
  217. package/runtime/packages/core/src/transport/rosbridge/client.ts +438 -0
  218. package/runtime/packages/core/src/transport/rosbridge/services.ts +41 -0
  219. package/runtime/packages/core/src/transport/rosbridge/topics.ts +60 -0
  220. package/runtime/packages/core/src/transport/rosbridge/types.ts +118 -0
  221. package/runtime/packages/core/src/transport/transport.ts +77 -0
  222. package/runtime/packages/core/src/transport/types.ts +137 -0
  223. package/runtime/packages/core/src/transport/webrtc/signaling-client.ts +196 -0
  224. package/runtime/packages/core/src/transport/webrtc/signaling-types.ts +130 -0
  225. package/runtime/packages/core/src/transport/webrtc/transport.ts +516 -0
  226. package/runtime/packages/core/src/transport/zenoh/adapter.ts +357 -0
  227. package/runtime/packages/core/src/transport/zenoh/cdr.ts +183 -0
  228. package/runtime/packages/core/src/transport/zenoh/keys.ts +51 -0
  229. package/runtime/packages/core/tsconfig.json +9 -0
  230. package/runtime/packages/ros-camera/package.json +30 -0
  231. package/runtime/packages/ros-camera/src/index.ts +13 -0
  232. package/runtime/packages/ros-camera/src/snapshot.ts +372 -0
  233. package/runtime/packages/ros-camera/tsconfig.json +9 -0
  234. package/runtime/pnpm-lock.yaml +5260 -0
  235. package/runtime/pnpm-workspace.yaml +2 -0
  236. package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/__init__.py +0 -0
  237. package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/agent_node.py +561 -0
  238. package/runtime/ros2_ws/src/agenticros_agent/package.xml +25 -0
  239. package/runtime/ros2_ws/src/agenticros_agent/resource/agenticros_agent +0 -0
  240. package/runtime/ros2_ws/src/agenticros_agent/setup.cfg +4 -0
  241. package/runtime/ros2_ws/src/agenticros_agent/setup.py +25 -0
  242. package/runtime/ros2_ws/src/agenticros_bringup/README.md +128 -0
  243. package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/__init__.py +1 -0
  244. package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/cmd_vel_relay.py +33 -0
  245. package/runtime/ros2_ws/src/agenticros_bringup/launch/cmd_vel_bridge.launch.py +58 -0
  246. package/runtime/ros2_ws/src/agenticros_bringup/launch/gazebo_turtlebot3.launch.py +69 -0
  247. package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo.launch.py +55 -0
  248. package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo_rviz.launch.py +48 -0
  249. package/runtime/ros2_ws/src/agenticros_bringup/launch/realsense_rosbridge.launch.py +154 -0
  250. package/runtime/ros2_ws/src/agenticros_bringup/launch/rosbridge_gazebo.launch.py +54 -0
  251. package/runtime/ros2_ws/src/agenticros_bringup/launch/rviz.launch.py +38 -0
  252. package/runtime/ros2_ws/src/agenticros_bringup/launch/turtlebot3_gazebo_rviz.launch.py +42 -0
  253. package/runtime/ros2_ws/src/agenticros_bringup/package.xml +24 -0
  254. package/runtime/ros2_ws/src/agenticros_bringup/resource/agenticros_bringup +0 -0
  255. package/runtime/ros2_ws/src/agenticros_bringup/rviz/turtlebot3_agenticros.rviz +174 -0
  256. package/runtime/ros2_ws/src/agenticros_bringup/setup.cfg +4 -0
  257. package/runtime/ros2_ws/src/agenticros_bringup/setup.py +28 -0
  258. package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/__init__.py +0 -0
  259. package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/discovery_node.py +172 -0
  260. package/runtime/ros2_ws/src/agenticros_discovery/package.xml +22 -0
  261. package/runtime/ros2_ws/src/agenticros_discovery/resource/agenticros_discovery +0 -0
  262. package/runtime/ros2_ws/src/agenticros_discovery/setup.cfg +5 -0
  263. package/runtime/ros2_ws/src/agenticros_discovery/setup.py +25 -0
  264. package/runtime/ros2_ws/src/agenticros_follow_me/README.md +66 -0
  265. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__init__.py +1 -0
  266. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__main__.py +5 -0
  267. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follow_me_node.py +278 -0
  268. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follower_controller.py +631 -0
  269. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/person_tracker.py +635 -0
  270. package/runtime/ros2_ws/src/agenticros_follow_me/package.xml +22 -0
  271. package/runtime/ros2_ws/src/agenticros_follow_me/resource/agenticros_follow_me +0 -0
  272. package/runtime/ros2_ws/src/agenticros_follow_me/setup.py +25 -0
  273. package/runtime/ros2_ws/src/agenticros_msgs/CMakeLists.txt +26 -0
  274. package/runtime/ros2_ws/src/agenticros_msgs/msg/CapabilityManifest.msg +9 -0
  275. package/runtime/ros2_ws/src/agenticros_msgs/package.xml +22 -0
  276. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeGetStatus.srv +11 -0
  277. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetDistance.srv +4 -0
  278. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetTarget.srv +6 -0
  279. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStart.srv +5 -0
  280. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStop.srv +3 -0
  281. package/runtime/ros2_ws/src/agenticros_msgs/srv/GetCapabilities.srv +5 -0
  282. package/runtime/ros2_ws/src/agenticros_sim/CMakeLists.txt +24 -0
  283. package/runtime/ros2_ws/src/agenticros_sim/README.md +120 -0
  284. package/runtime/ros2_ws/src/agenticros_sim/config/agenticros-sim.config.json +28 -0
  285. package/runtime/ros2_ws/src/agenticros_sim/config/amr_bridge.yaml +111 -0
  286. package/runtime/ros2_ws/src/agenticros_sim/config/amr_view.rviz +172 -0
  287. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.dsv.in +3 -0
  288. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.sh.in +7 -0
  289. package/runtime/ros2_ws/src/agenticros_sim/launch/sim_amr.launch.py +159 -0
  290. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.config +17 -0
  291. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.sdf +244 -0
  292. package/runtime/ros2_ws/src/agenticros_sim/package.xml +27 -0
  293. package/runtime/ros2_ws/src/agenticros_sim/worlds/agenticros_indoor.sdf +183 -0
  294. package/runtime/scripts/activate_workspace.sh +285 -0
  295. package/runtime/scripts/agenticros-describer.policy.yaml +96 -0
  296. package/runtime/scripts/agenticros-proxy.cjs +99 -0
  297. package/runtime/scripts/agenticros-rosbridge.policy.yaml +62 -0
  298. package/runtime/scripts/check-cli-tarball-size.mjs +42 -0
  299. package/runtime/scripts/configure_agenticros.sh +200 -0
  300. package/runtime/scripts/configure_for_sim.sh +64 -0
  301. package/runtime/scripts/fix-openclaw-control-ui-path.sh +20 -0
  302. package/runtime/scripts/install_cli.sh +94 -0
  303. package/runtime/scripts/install_rosbridge_from_source.sh +67 -0
  304. package/runtime/scripts/lib/agenticros-banner.sh +28 -0
  305. package/runtime/scripts/onboard_robot.sh +75 -0
  306. package/runtime/scripts/openai.policy.yaml +77 -0
  307. package/runtime/scripts/openclaw-dashboard-url.cjs +49 -0
  308. package/runtime/scripts/pack-runtime.mjs +245 -0
  309. package/runtime/scripts/run_demo_native.sh +43 -0
  310. package/runtime/scripts/run_nemoclaw_host_stack.sh +91 -0
  311. package/runtime/scripts/run_robot_rosbridge.sh +36 -0
  312. package/runtime/scripts/sandbox_rosbridge_relay.py +137 -0
  313. package/runtime/scripts/setup-openclaw-local.cjs +75 -0
  314. package/runtime/scripts/setup_gateway_plugin.sh +329 -0
  315. package/runtime/scripts/setup_robot.sh +113 -0
  316. package/runtime/scripts/setup_workspace.sh +484 -0
  317. package/runtime/scripts/sim/run_sim.sh +146 -0
  318. package/runtime/scripts/smoke_test_nemoclaw.sh +123 -0
  319. package/runtime/scripts/start_demo.sh +55 -0
  320. package/runtime/scripts/sync-skill-tools.mjs +335 -0
  321. package/runtime/scripts/test-follow-me-sim.mjs +135 -0
  322. package/runtime/scripts/test-mcp-e2e.mjs +184 -0
  323. package/runtime/scripts/test-rclnodejs.mts +129 -0
  324. package/runtime/scripts/use-openclaw-2026.2.26.sh +19 -0
  325. package/runtime/scripts/use-openclaw-2026.3.11.sh +19 -0
  326. package/runtime/scripts/zenoh-bridge-ros2dds-robot.json5 +30 -0
  327. package/runtime/scripts/zenohd-agenticros.json5 +11 -0
  328. package/runtime/scripts/zenohd-rosclaw.json5 +11 -0
  329. package/runtime/tsconfig.base.json +19 -0
  330. package/index.js +0 -6
@@ -0,0 +1,498 @@
1
+ import type { AgenticROSConfig } from "@agenticros/core";
2
+
3
+ /**
4
+ * Returns the teleop web page HTML (Phase 3).
5
+ * Includes: camera <img>, source selector when multiple streams, twist buttons, speed slider.
6
+ */
7
+ export function getTeleopPageHtml(config: AgenticROSConfig): string {
8
+ const teleop = config.teleop ?? {};
9
+ const speedDefault = teleop.speedDefault ?? 0.3;
10
+ const cameraPollMs = teleop.cameraPollMs ?? 150;
11
+
12
+ return `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="UTF-8" />
16
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
17
+ <title>AgenticROS Teleop</title>
18
+ <style>
19
+ * { box-sizing: border-box; }
20
+ body { font-family: system-ui, sans-serif; margin: 12px; background: #1a1a1a; color: #e0e0e0; }
21
+ h1 { font-size: 1.25rem; margin: 0 0 12px 0; }
22
+ .camera-wrap { position: relative; max-width: 100%; margin-bottom: 12px; }
23
+ .camera-wrap img { width: 100%; max-height: 60vh; object-fit: contain; background: #000; border-radius: 8px; }
24
+ .camera-wrap .no-feed { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: #333; border-radius: 8px; color: #888; }
25
+ .controls { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 12px; }
26
+ .source-select { min-width: 200px; padding: 6px 10px; border-radius: 6px; background: #333; color: #e0e0e0; border: 1px solid #555; }
27
+ .speed-wrap { display: flex; align-items: center; gap: 8px; }
28
+ .speed-wrap label { font-size: 0.9rem; }
29
+ .speed-wrap input[type="range"] { width: 100px; }
30
+ .btn-wrap { display: flex; flex-direction: column; gap: 4px; align-items: center; }
31
+ .btn-row { display: flex; gap: 8px; justify-content: center; }
32
+ button { padding: 12px 20px; font-size: 1rem; border-radius: 8px; border: none; cursor: pointer; color: #fff; }
33
+ button:not(.stop) { background: #086; }
34
+ button:not(.stop):active, button:not(.stop).active { background: #0c9; }
35
+ button.stop { background: #822; }
36
+ button.stop:active { background: #c33; }
37
+ .conn-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-size: 0.8rem; font-weight: 500; margin-bottom: 8px; }
38
+ .conn-badge.connected { background: #0a5; color: #000; }
39
+ .conn-badge.disconnected { background: #822; color: #fff; }
40
+ .gamepad-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-size: 0.8rem; font-weight: 500; margin-bottom: 8px; margin-left: 8px; }
41
+ .gamepad-badge.active { background: #07a; color: #fff; }
42
+ .gamepad-badge.inactive { background: #444; color: #888; }
43
+ .status-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
44
+ .status { font-size: 0.85rem; color: #888; }
45
+ a { color: #0c9; text-decoration: none; }
46
+ a:hover { text-decoration: underline; }
47
+ </style>
48
+ </head>
49
+ <body>
50
+ <h1>AgenticROS Teleop</h1>
51
+ <p><a href="/plugins/agenticros/">Back to AgenticROS</a></p>
52
+ <div id="conn-badge" class="conn-badge disconnected" title="Transport connection">○ Checking…</div>
53
+ <div id="gamepad-badge" class="gamepad-badge inactive" title="Gamepad status">🎮 No gamepad</div>
54
+ <div class="camera-wrap">
55
+ <img id="camera" src="" alt="Camera" style="display:none" />
56
+ <div id="no-feed" class="no-feed">Select a camera source (or waiting for feed)</div>
57
+ </div>
58
+ <div class="controls">
59
+ <div class="speed-wrap">
60
+ <label for="speed">Speed:</label>
61
+ <input type="range" id="speed" min="0.1" max="1" step="0.1" value="${speedDefault}" />
62
+ <span id="speed-val">${speedDefault}</span>
63
+ </div>
64
+ <label for="source" class="source-select-label" style="font-size:0.9rem; color:#aaa; margin-right:6px;">Camera topic</label>
65
+ <select id="source" class="source-select" style="display:none" aria-label="Camera ROS topic">
66
+ <option value="">—</option>
67
+ </select>
68
+ <span class="status" style="font-size:0.8rem; color:#666;">Use a topic ending in <code>/compressed</code> for the feed.</span>
69
+ </div>
70
+ <div class="btn-wrap">
71
+ <div class="btn-row"><button type="button" id="btn-fwd" data-linear-x="1" data-angular-z="0">Fwd</button></div>
72
+ <div class="btn-row">
73
+ <button type="button" id="btn-left" data-linear-x="0" data-angular-z="1">Left</button>
74
+ <button type="button" id="btn-stop" class="stop">Stop</button>
75
+ <button type="button" id="btn-right" data-linear-x="0" data-angular-z="-1">Right</button>
76
+ </div>
77
+ <div class="btn-row"><button type="button" id="btn-back" data-linear-x="-1" data-angular-z="0">Back</button></div>
78
+ </div>
79
+ <div class="status-wrap">
80
+ <span id="status" class="status"></span>
81
+ <button type="button" id="btn-reconnect" style="display:none; margin-left: 12px; padding: 6px 12px; font-size: 0.85rem;">Reconnect</button>
82
+ </div>
83
+ <p style="font-size:0.75rem; color:#888; margin-top:8px;">WASD keys and Bluetooth gamepads also drive (W=Forward, A=Left, S=Back, D=Right). If Fwd/Back send 0s, open this page via the proxy: <code>http://127.0.0.1:18790/plugins/agenticros/</code> → Teleop.</p>
84
+
85
+ <script>
86
+ (function() {
87
+ /** Base URL for teleop API (status, sources, camera, twist). Resolves correctly when the page is not under .../teleop/ (e.g. hash-routed Control UI). */
88
+ function teleopApiBase() {
89
+ var p = window.location.pathname || '';
90
+ var i = p.indexOf('/teleop');
91
+ if (i >= 0) return p.slice(0, i) + '/teleop/';
92
+ var h = (window.location.hash || '').replace(/^#/, '');
93
+ if (h.indexOf('/teleop') >= 0) {
94
+ var j = h.indexOf('/teleop');
95
+ return h.slice(0, j) + '/teleop/';
96
+ }
97
+ return '/plugins/agenticros/teleop/';
98
+ }
99
+ var API_BASE = teleopApiBase();
100
+ function apiUrl(s) {
101
+ var t = String(s);
102
+ while (t.length > 0 && t.charAt(0) === '/') t = t.slice(1);
103
+ return API_BASE + t;
104
+ }
105
+
106
+ const POLL_MS = ${cameraPollMs};
107
+ const SPEED_DEFAULT = ${speedDefault};
108
+ const cameraEl = document.getElementById('camera');
109
+ const noFeedEl = document.getElementById('no-feed');
110
+ const sourceEl = document.getElementById('source');
111
+ const speedEl = document.getElementById('speed');
112
+ const speedVal = document.getElementById('speed-val');
113
+ const statusEl = document.getElementById('status');
114
+ const reconnectBtn = document.getElementById('btn-reconnect');
115
+ const connBadge = document.getElementById('conn-badge');
116
+
117
+ let selectedTopic = '';
118
+ let pollTimer = null;
119
+ let statusInterval = null;
120
+ function setStatus(msg) { statusEl.textContent = msg; }
121
+ function getSpeed() { return parseFloat(speedEl?.value || SPEED_DEFAULT); }
122
+ speedEl?.addEventListener('input', function() { speedVal.textContent = this.value; });
123
+
124
+ function cameraFetchUrl() {
125
+ if (!selectedTopic) return '';
126
+ return apiUrl('camera?topic=' + encodeURIComponent(selectedTopic) + '&type=compressed&t=' + Date.now());
127
+ }
128
+
129
+ /** Monotonic token so a stale async loop stops after topic change / stopPoll. */
130
+ let pollGeneration = 0;
131
+
132
+ function revokeCameraObjectUrl() {
133
+ var prev = cameraEl && cameraEl.dataset ? cameraEl.dataset.objectUrl : '';
134
+ if (prev) {
135
+ try { URL.revokeObjectURL(prev); } catch (e) { /* ignore */ }
136
+ cameraEl.dataset.objectUrl = '';
137
+ }
138
+ }
139
+
140
+ function stopPoll() {
141
+ pollGeneration += 1;
142
+ if (pollTimer) {
143
+ clearTimeout(pollTimer);
144
+ pollTimer = null;
145
+ }
146
+ revokeCameraObjectUrl();
147
+ }
148
+
149
+ function startPoll() {
150
+ stopPoll();
151
+ if (!selectedTopic) { noFeedEl.style.display = 'flex'; cameraEl.style.display = 'none'; return; }
152
+ noFeedEl.style.display = 'none';
153
+ cameraEl.style.display = 'block';
154
+ var gen = pollGeneration;
155
+
156
+ function scheduleNext(delayMs) {
157
+ if (gen !== pollGeneration) return;
158
+ pollTimer = setTimeout(tick, delayMs);
159
+ }
160
+
161
+ function tick() {
162
+ if (gen !== pollGeneration || !selectedTopic) return;
163
+ var url = cameraFetchUrl();
164
+ if (!url) return;
165
+ var t0 = Date.now();
166
+ fetch(url, { cache: 'no-store', credentials: 'same-origin' })
167
+ .then(function(r) {
168
+ if (gen !== pollGeneration) return null;
169
+ var ct = (r.headers.get('Content-Type') || '').toLowerCase();
170
+ if (!r.ok) {
171
+ if (ct.indexOf('application/json') >= 0) {
172
+ return r.json().then(function(j) {
173
+ if (gen !== pollGeneration) return;
174
+ var err = j && j.error ? String(j.error) : r.statusText;
175
+ var code = j && j.code ? String(j.code) : '';
176
+ setStatus(code ? ('Camera [' + code + ']: ' + err) : ('Camera: ' + err));
177
+ scheduleNext(POLL_MS);
178
+ });
179
+ }
180
+ setStatus('Camera: ' + r.status + ' ' + r.statusText);
181
+ scheduleNext(POLL_MS);
182
+ return null;
183
+ }
184
+ if (ct.indexOf('image/') !== 0) {
185
+ return r.json().then(function(j) {
186
+ if (gen !== pollGeneration) return;
187
+ var err = j && j.error ? String(j.error) : 'unexpected response';
188
+ var code = j && j.code ? String(j.code) : '';
189
+ setStatus(code ? ('Camera [' + code + ']: ' + err) : ('Camera: ' + err));
190
+ scheduleNext(POLL_MS);
191
+ });
192
+ }
193
+ return r.blob().then(function(blob) {
194
+ if (gen !== pollGeneration) return;
195
+ setStatus('');
196
+ var objectUrl = URL.createObjectURL(blob);
197
+ revokeCameraObjectUrl();
198
+ cameraEl.dataset.objectUrl = objectUrl;
199
+ cameraEl.src = objectUrl;
200
+ var elapsed = Date.now() - t0;
201
+ var wait = Math.max(0, POLL_MS - elapsed);
202
+ scheduleNext(wait);
203
+ });
204
+ })
205
+ .catch(function() {
206
+ if (gen !== pollGeneration) return;
207
+ setStatus('Camera request failed. Check transport (Reconnect) and gateway logs.');
208
+ scheduleNext(POLL_MS);
209
+ });
210
+ }
211
+
212
+ tick();
213
+ }
214
+
215
+ function loadSources() {
216
+ fetch(apiUrl('sources'))
217
+ .then(function(r) { return r.json(); })
218
+ .then(function(arr) {
219
+ if (!Array.isArray(arr) || arr.length === 0) {
220
+ setStatus('No camera sources found. Publish to an Image/CompressedImage topic.');
221
+ return;
222
+ }
223
+ var prev = selectedTopic;
224
+ sourceEl.innerHTML = '';
225
+ arr.forEach(function(o) {
226
+ const opt = document.createElement('option');
227
+ opt.value = o.topic;
228
+ opt.textContent = o.label || o.topic;
229
+ sourceEl.appendChild(opt);
230
+ });
231
+ sourceEl.style.display = arr.length >= 1 ? 'block' : 'none';
232
+ var labelEl = document.querySelector('.source-select-label');
233
+ if (labelEl) labelEl.style.display = arr.length >= 1 ? 'inline' : 'none';
234
+ var stillThere = prev && arr.some(function(o) { return o.topic === prev; });
235
+ selectedTopic = stillThere ? prev : arr[0].topic;
236
+ sourceEl.value = selectedTopic;
237
+ startPoll();
238
+ })
239
+ .catch(function(e) { setStatus('Failed to load sources: ' + e.message); });
240
+ }
241
+
242
+ function setConnBadge(connected, mode) {
243
+ if (!connBadge) return;
244
+ connBadge.className = 'conn-badge ' + (connected ? 'connected' : 'disconnected');
245
+ connBadge.textContent = connected ? '● Connected (' + (mode || 'ros2') + ')' : '○ Disconnected (mode: ' + (mode || 'none') + ')';
246
+ connBadge.title = connected ? 'Transport connected (camera still needs a publishing CompressedImage topic)' : 'Start Zenoh router (ws://localhost:10000) or rosbridge, then click Reconnect';
247
+ }
248
+
249
+ function updateConnectionStatus() {
250
+ fetch(apiUrl('status'))
251
+ .then(function(r) { return r.json(); })
252
+ .then(function(j) {
253
+ var connected = !!j.connected;
254
+ var mode = j.mode || 'none';
255
+ setConnBadge(connected, mode);
256
+ if (!connected) {
257
+ setStatus('ROS2 transport not connected. Set transport.mode and Zenoh endpoint (ws://localhost:10000) in config, start the Zenoh router, then click Reconnect or restart the gateway.');
258
+ if (reconnectBtn) { reconnectBtn.style.display = 'inline-block'; }
259
+ return;
260
+ }
261
+ if (reconnectBtn) { reconnectBtn.style.display = 'none'; }
262
+ setStatus('');
263
+ loadSources();
264
+ })
265
+ .catch(function() {
266
+ setConnBadge(false, 'none');
267
+ if (reconnectBtn) reconnectBtn.style.display = 'none';
268
+ loadSources();
269
+ });
270
+ }
271
+
272
+ function refreshStatus() {
273
+ fetch(apiUrl('status')).then(function(r) { return r.json(); }).then(function(j) {
274
+ var c = !!j.connected;
275
+ var m = j.mode || 'none';
276
+ setConnBadge(c, m);
277
+ }).catch(function() { setConnBadge(false, 'none'); });
278
+ }
279
+
280
+ reconnectBtn?.addEventListener('click', function() {
281
+ setStatus('Reconnecting...');
282
+ fetch(apiUrl('reconnect'), { method: 'GET' })
283
+ .then(function(r) {
284
+ const ct = (r.headers.get('Content-Type') || '').toLowerCase();
285
+ if (ct.includes('application/json')) return r.json();
286
+ return r.text().then(function(t) { throw new Error(t || r.statusText || 'Non-JSON response'); });
287
+ })
288
+ .then(function(j) {
289
+ if (j && j.ok) { setStatus('Connected.'); updateConnectionStatus(); }
290
+ else { setStatus('Reconnect failed: ' + (j && j.error ? j.error : 'unknown')); }
291
+ })
292
+ .catch(function(e) { setStatus('Reconnect failed: ' + (e.message || String(e))); });
293
+ });
294
+
295
+ sourceEl.addEventListener('change', function() {
296
+ selectedTopic = this.value || '';
297
+ startPoll();
298
+ });
299
+
300
+ // Twist: GET only. POST body is often not passed to the plugin by the gateway. Use the proxy (http://127.0.0.1:18790/plugins/agenticros/) so the query is forwarded in X-AgenticROS-Query.
301
+ function sendTwist(linearX, linearY, linearZ, angularX, angularY, angularZ) {
302
+ const s = getSpeed();
303
+ const lx = (linearX ?? 0) * s;
304
+ const ly = (linearY ?? 0) * s;
305
+ const lz = linearZ ?? 0;
306
+ const ax = angularX ?? 0;
307
+ const ay = angularY ?? 0;
308
+ const az = (angularZ ?? 0) * s;
309
+ var q = 'linear_x=' + encodeURIComponent(lx) + '&linear_y=' + encodeURIComponent(ly) + '&linear_z=' + encodeURIComponent(lz) + '&angular_x=' + encodeURIComponent(ax) + '&angular_y=' + encodeURIComponent(ay) + '&angular_z=' + encodeURIComponent(az);
310
+ fetch(apiUrl('twist?' + q), { method: 'GET' })
311
+ .then(function(r) {
312
+ if (r.status === 502) { setStatus('Twist: 502 — use proxy (http://127.0.0.1:18790/plugins/agenticros/) or single gateway worker.'); return; }
313
+ if (!r.ok) return r.json().then(function(j) { setStatus('Twist: ' + (j && j.error ? j.error : r.statusText)); });
314
+ setStatus('');
315
+ })
316
+ .catch(function(e) { setStatus('Twist error: ' + e.message); });
317
+ }
318
+
319
+ function stop() {
320
+ sendTwist(0,0,0,0,0,0);
321
+ document.querySelectorAll('.btn-wrap button:not(.stop)').forEach(function(b) { b.classList.remove('active'); });
322
+ }
323
+
324
+ ['btn-fwd','btn-back','btn-left','btn-right'].forEach(function(id) {
325
+ const btn = document.getElementById(id);
326
+ if (!btn) return;
327
+ btn.addEventListener('pointerdown', function() {
328
+ btn.classList.add('active');
329
+ const lx = parseFloat(btn.dataset.linearX);
330
+ const az = parseFloat(btn.dataset.angularZ);
331
+ sendTwist(lx || 0, 0, 0, 0, 0, az || 0);
332
+ });
333
+ btn.addEventListener('pointerup', stop);
334
+ btn.addEventListener('pointerleave', stop);
335
+ });
336
+ document.getElementById('btn-stop')?.addEventListener('click', function() { stop(); });
337
+
338
+ // WASD keyboard: same as green directional buttons (ignore repeat via keysDown set)
339
+ var keysDown = {};
340
+ var keyToButton = { w: 'btn-fwd', a: 'btn-left', s: 'btn-back', d: 'btn-right' };
341
+ function applyKey(key, down) {
342
+ var k = key.toLowerCase();
343
+ if (!keyToButton[k]) return;
344
+ if (down) {
345
+ if (keysDown[k]) return;
346
+ keysDown[k] = true;
347
+ var btn = document.getElementById(keyToButton[k]);
348
+ if (btn) {
349
+ btn.classList.add('active');
350
+ var lx = parseFloat(btn.dataset.linearX);
351
+ var az = parseFloat(btn.dataset.angularZ);
352
+ sendTwist(lx || 0, 0, 0, 0, 0, az || 0);
353
+ }
354
+ } else {
355
+ if (!keysDown[k]) return;
356
+ delete keysDown[k];
357
+ stop();
358
+ }
359
+ }
360
+ document.addEventListener('keydown', function(e) {
361
+ if (keyToButton[e.key.toLowerCase()]) { e.preventDefault(); applyKey(e.key, true); }
362
+ });
363
+ document.addEventListener('keyup', function(e) {
364
+ if (keyToButton[e.key.toLowerCase()]) { e.preventDefault(); applyKey(e.key, false); }
365
+ });
366
+
367
+ // Gamepad (Bluetooth controller) support
368
+ var gamepadBadge = document.getElementById('gamepad-badge');
369
+ var gamepadPollInterval = null;
370
+ var lastGamepadDirection = 'STOP';
371
+
372
+ function setGamepadBadge(active, name) {
373
+ if (!gamepadBadge) return;
374
+ if (active) {
375
+ gamepadBadge.className = 'gamepad-badge active';
376
+ gamepadBadge.textContent = '🎮 ' + (name || 'Gamepad');
377
+ gamepadBadge.title = 'Gamepad connected: ' + (name || 'Unknown');
378
+ } else {
379
+ gamepadBadge.className = 'gamepad-badge inactive';
380
+ gamepadBadge.textContent = '🎮 No gamepad';
381
+ gamepadBadge.title = 'Connect a Bluetooth gamepad to use joystick control';
382
+ }
383
+ }
384
+
385
+ function clearGamepadButtons() {
386
+ ['btn-fwd','btn-back','btn-left','btn-right'].forEach(function(id) {
387
+ var btn = document.getElementById(id);
388
+ if (btn) btn.classList.remove('active');
389
+ });
390
+ }
391
+
392
+ function applyGamepadDirection(direction) {
393
+ if (direction === lastGamepadDirection) return;
394
+ lastGamepadDirection = direction;
395
+ clearGamepadButtons();
396
+
397
+ var dirToButton = { FORWARD: 'btn-fwd', BACKWARD: 'btn-back', LEFT: 'btn-left', RIGHT: 'btn-right' };
398
+ var btnId = dirToButton[direction];
399
+ if (btnId) {
400
+ var btn = document.getElementById(btnId);
401
+ if (btn) {
402
+ btn.classList.add('active');
403
+ var lx = parseFloat(btn.dataset.linearX);
404
+ var az = parseFloat(btn.dataset.angularZ);
405
+ sendTwist(lx || 0, 0, 0, 0, 0, az || 0);
406
+ }
407
+ } else {
408
+ sendTwist(0, 0, 0, 0, 0, 0);
409
+ }
410
+ }
411
+
412
+ function pollGamepad() {
413
+ var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
414
+ var controller = null;
415
+ for (var i = 0; i < gamepads.length; i++) {
416
+ if (gamepads[i]) { controller = gamepads[i]; break; }
417
+ }
418
+ if (!controller) {
419
+ setGamepadBadge(false);
420
+ if (lastGamepadDirection !== 'STOP') {
421
+ applyGamepadDirection('STOP');
422
+ }
423
+ return;
424
+ }
425
+
426
+ setGamepadBadge(true, controller.id.split('(')[0].trim());
427
+
428
+ var x = Math.trunc(controller.axes[0] * 100) / 100;
429
+ var y = Math.trunc(controller.axes[1] * 100) / 100;
430
+
431
+ var direction = 'STOP';
432
+ if (Math.abs(x) < 0.1 && Math.abs(y) < 0.1) {
433
+ direction = 'STOP';
434
+ } else if (x > 0.2) {
435
+ direction = 'RIGHT';
436
+ } else if (x < -0.2) {
437
+ direction = 'LEFT';
438
+ } else if (y > 0.2) {
439
+ direction = 'BACKWARD';
440
+ } else if (y < -0.2) {
441
+ direction = 'FORWARD';
442
+ }
443
+
444
+ applyGamepadDirection(direction);
445
+ }
446
+
447
+ function startGamepadPolling() {
448
+ if (gamepadPollInterval) return;
449
+ gamepadPollInterval = setInterval(pollGamepad, 100);
450
+ }
451
+
452
+ function stopGamepadPolling() {
453
+ if (gamepadPollInterval) {
454
+ clearInterval(gamepadPollInterval);
455
+ gamepadPollInterval = null;
456
+ }
457
+ }
458
+
459
+ window.addEventListener('gamepadconnected', function(e) {
460
+ setGamepadBadge(true, e.gamepad.id.split('(')[0].trim());
461
+ startGamepadPolling();
462
+ });
463
+
464
+ window.addEventListener('gamepaddisconnected', function() {
465
+ var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
466
+ var hasGamepad = false;
467
+ for (var i = 0; i < gamepads.length; i++) {
468
+ if (gamepads[i]) { hasGamepad = true; break; }
469
+ }
470
+ if (!hasGamepad) {
471
+ setGamepadBadge(false);
472
+ stopGamepadPolling();
473
+ if (lastGamepadDirection !== 'STOP') {
474
+ applyGamepadDirection('STOP');
475
+ }
476
+ }
477
+ });
478
+
479
+ // Check for already-connected gamepads on page load
480
+ (function checkInitialGamepads() {
481
+ var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
482
+ for (var i = 0; i < gamepads.length; i++) {
483
+ if (gamepads[i]) {
484
+ setGamepadBadge(true, gamepads[i].id.split('(')[0].trim());
485
+ startGamepadPolling();
486
+ break;
487
+ }
488
+ }
489
+ })();
490
+
491
+ updateConnectionStatus();
492
+ statusInterval = setInterval(refreshStatus, 5000);
493
+ window.addEventListener('visibilitychange', function() { if (document.visibilityState === 'visible') refreshStatus(); });
494
+ })();
495
+ </script>
496
+ </body>
497
+ </html>`;
498
+ }