so101-nexus 0.4.0__tar.gz

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 (261) hide show
  1. so101_nexus-0.4.0/.github/dependabot.yml +43 -0
  2. so101_nexus-0.4.0/.github/scripts/protect_main_branch.sh +126 -0
  3. so101_nexus-0.4.0/.github/scripts/pypi_smoke_check.py +70 -0
  4. so101_nexus-0.4.0/.github/workflows/ci.yml +148 -0
  5. so101_nexus-0.4.0/.github/workflows/dependency-review.yml +32 -0
  6. so101_nexus-0.4.0/.github/workflows/deploy-docs.yml +71 -0
  7. so101_nexus-0.4.0/.github/workflows/publish.yml +68 -0
  8. so101_nexus-0.4.0/.github/workflows/release-smoke.yml +95 -0
  9. so101_nexus-0.4.0/.gitignore +240 -0
  10. so101_nexus-0.4.0/.python-version +1 -0
  11. so101_nexus-0.4.0/.vscode/settings.json +4 -0
  12. so101_nexus-0.4.0/LICENSE.md +201 -0
  13. so101_nexus-0.4.0/Makefile +35 -0
  14. so101_nexus-0.4.0/PKG-INFO +45 -0
  15. so101_nexus-0.4.0/README.md +130 -0
  16. so101_nexus-0.4.0/assets/so101.png +0 -0
  17. so101_nexus-0.4.0/docs/.gitignore +4 -0
  18. so101_nexus-0.4.0/docs/app/api/search/route.ts +5 -0
  19. so101_nexus-0.4.0/docs/app/docs/[[...slug]]/page.tsx +33 -0
  20. so101_nexus-0.4.0/docs/app/docs/layout.tsx +15 -0
  21. so101_nexus-0.4.0/docs/app/global.css +3 -0
  22. so101_nexus-0.4.0/docs/app/layout.tsx +27 -0
  23. so101_nexus-0.4.0/docs/app/page.tsx +5 -0
  24. so101_nexus-0.4.0/docs/content/docs/api/assets-and-geometry.mdx +152 -0
  25. so101_nexus-0.4.0/docs/content/docs/api/configs.mdx +405 -0
  26. so101_nexus-0.4.0/docs/content/docs/api/core-overview.mdx +125 -0
  27. so101_nexus-0.4.0/docs/content/docs/api/environment-registry.mdx +86 -0
  28. so101_nexus-0.4.0/docs/content/docs/api/lerobot-processors.mdx +168 -0
  29. so101_nexus-0.4.0/docs/content/docs/api/meta.json +11 -0
  30. so101_nexus-0.4.0/docs/content/docs/api/objects.mdx +127 -0
  31. so101_nexus-0.4.0/docs/content/docs/concepts/backend-support.mdx +30 -0
  32. so101_nexus-0.4.0/docs/content/docs/concepts/lerobot-compatibility.mdx +135 -0
  33. so101_nexus-0.4.0/docs/content/docs/concepts/meta.json +10 -0
  34. so101_nexus-0.4.0/docs/content/docs/concepts/observations-and-camera-modes.mdx +161 -0
  35. so101_nexus-0.4.0/docs/content/docs/concepts/poses.mdx +72 -0
  36. so101_nexus-0.4.0/docs/content/docs/concepts/tasks-and-success-conditions.mdx +38 -0
  37. so101_nexus-0.4.0/docs/content/docs/environments/index.mdx +29 -0
  38. so101_nexus-0.4.0/docs/content/docs/environments/meta.json +12 -0
  39. so101_nexus-0.4.0/docs/content/docs/environments/mujoco-look-at.mdx +68 -0
  40. so101_nexus-0.4.0/docs/content/docs/environments/mujoco-move.mdx +68 -0
  41. so101_nexus-0.4.0/docs/content/docs/environments/mujoco-pick-and-place.mdx +75 -0
  42. so101_nexus-0.4.0/docs/content/docs/environments/mujoco-pick-lift.mdx +89 -0
  43. so101_nexus-0.4.0/docs/content/docs/environments/mujoco-reach.mdx +68 -0
  44. so101_nexus-0.4.0/docs/content/docs/getting-started/environment-ids.mdx +51 -0
  45. so101_nexus-0.4.0/docs/content/docs/getting-started/installation.mdx +43 -0
  46. so101_nexus-0.4.0/docs/content/docs/getting-started/meta.json +9 -0
  47. so101_nexus-0.4.0/docs/content/docs/getting-started/overview.mdx +42 -0
  48. so101_nexus-0.4.0/docs/content/docs/getting-started/quickstart-mujoco.mdx +70 -0
  49. so101_nexus-0.4.0/docs/content/docs/guides/customizing-environments.mdx +173 -0
  50. so101_nexus-0.4.0/docs/content/docs/guides/meta.json +9 -0
  51. so101_nexus-0.4.0/docs/content/docs/guides/teleop-dataset-recording.mdx +11 -0
  52. so101_nexus-0.4.0/docs/content/docs/guides/training-with-ppo.mdx +80 -0
  53. so101_nexus-0.4.0/docs/content/docs/guides/working-with-objects.mdx +114 -0
  54. so101_nexus-0.4.0/docs/content/docs/guides/ycb-assets.mdx +116 -0
  55. so101_nexus-0.4.0/docs/content/docs/index.mdx +92 -0
  56. so101_nexus-0.4.0/docs/content/docs/meta.json +17 -0
  57. so101_nexus-0.4.0/docs/content/docs/policies/meta.json +6 -0
  58. so101_nexus-0.4.0/docs/content/docs/policies/molmoact.mdx +75 -0
  59. so101_nexus-0.4.0/docs/content/docs/teleoperation/meta.json +7 -0
  60. so101_nexus-0.4.0/docs/content/docs/teleoperation/overview.mdx +296 -0
  61. so101_nexus-0.4.0/docs/content/docs/teleoperation/troubleshooting.mdx +62 -0
  62. so101_nexus-0.4.0/docs/lib/source.ts +7 -0
  63. so101_nexus-0.4.0/docs/next-env.d.ts +6 -0
  64. so101_nexus-0.4.0/docs/next.config.mjs +17 -0
  65. so101_nexus-0.4.0/docs/pnpm-lock.yaml +4098 -0
  66. so101_nexus-0.4.0/docs/postcss.config.mjs +7 -0
  67. so101_nexus-0.4.0/docs/source.config.ts +5 -0
  68. so101_nexus-0.4.0/docs/tsconfig.json +42 -0
  69. so101_nexus-0.4.0/examples/README.md +110 -0
  70. so101_nexus-0.4.0/examples/__init__.py +1 -0
  71. so101_nexus-0.4.0/examples/list_envs.py +6 -0
  72. so101_nexus-0.4.0/examples/ppo.py +495 -0
  73. so101_nexus-0.4.0/examples/ppo_warp.py +251 -0
  74. so101_nexus-0.4.0/pyproject.toml +195 -0
  75. so101_nexus-0.4.0/pyrightconfig.json +7 -0
  76. so101_nexus-0.4.0/scripts/smoke_molmoact_rollout.py +79 -0
  77. so101_nexus-0.4.0/src/so101_nexus/__init__.py +156 -0
  78. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/README.md +35 -0
  79. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/base_motor_holder_so101_v1.part +14 -0
  80. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/base_motor_holder_so101_v1.stl +0 -0
  81. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/base_so101_v2.part +14 -0
  82. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/base_so101_v2.stl +0 -0
  83. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/motor_holder_so101_base_v1.part +14 -0
  84. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/motor_holder_so101_base_v1.stl +0 -0
  85. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/motor_holder_so101_wrist_v1.part +14 -0
  86. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/motor_holder_so101_wrist_v1.stl +0 -0
  87. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/moving_jaw_so101_v1.part +14 -0
  88. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/moving_jaw_so101_v1.stl +0 -0
  89. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/rotation_pitch_so101_v1.part +14 -0
  90. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/rotation_pitch_so101_v1.stl +0 -0
  91. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/sts3215_03a_no_horn_v1.part +14 -0
  92. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/sts3215_03a_no_horn_v1.stl +0 -0
  93. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/sts3215_03a_v1.part +14 -0
  94. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/sts3215_03a_v1.stl +0 -0
  95. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/under_arm_so101_v1.part +14 -0
  96. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/under_arm_so101_v1.stl +0 -0
  97. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/upper_arm_so101_v1.part +14 -0
  98. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/upper_arm_so101_v1.stl +0 -0
  99. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/waveshare_mounting_plate_so101_v2.part +14 -0
  100. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/waveshare_mounting_plate_so101_v2.stl +0 -0
  101. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/wrist_roll_follower_so101_v1.part +14 -0
  102. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/wrist_roll_follower_so101_v1.stl +0 -0
  103. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/wrist_roll_pitch_so101_v2.part +14 -0
  104. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/assets/wrist_roll_pitch_so101_v2.stl +0 -0
  105. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/joints_properties.xml +12 -0
  106. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/scene.xml +24 -0
  107. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/so101_new_calib.urdf +453 -0
  108. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/so101_new_calib.xml +179 -0
  109. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/so101_old_calib.urdf +435 -0
  110. so101_nexus-0.4.0/src/so101_nexus/assets/SO101/so101_old_calib.xml +160 -0
  111. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/CHANGELOG.md +7 -0
  112. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/LICENSE +201 -0
  113. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/PROVENANCE.md +27 -0
  114. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/README.md +32 -0
  115. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/base_motor_holder_so101_v1.stl +0 -0
  116. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/base_so101_v2.stl +0 -0
  117. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/motor_holder_so101_base_v1.stl +0 -0
  118. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/motor_holder_so101_wrist_v1.stl +0 -0
  119. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/moving_jaw_so101_gripper_part0_v1.stl +0 -0
  120. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/moving_jaw_so101_gripper_part1_v1.stl +0 -0
  121. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/moving_jaw_so101_gripper_v1.stl +0 -0
  122. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/moving_jaw_so101_v1.stl +0 -0
  123. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/rotation_pitch_so101_v1.stl +0 -0
  124. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/sts3215_03a_no_horn_v1.stl +0 -0
  125. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/sts3215_03a_v1.stl +0 -0
  126. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/under_arm_so101_v1.stl +0 -0
  127. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/upper_arm_so101_v1.stl +0 -0
  128. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/waveshare_mounting_plate_so101_v2.stl +0 -0
  129. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/wrist_roll_follower_so101_camera_mount.stl +0 -0
  130. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/wrist_roll_follower_so101_gripper_part0_v1.stl +0 -0
  131. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/wrist_roll_follower_so101_gripper_v1.stl +0 -0
  132. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/wrist_roll_follower_so101_v1.stl +0 -0
  133. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/assets/wrist_roll_pitch_so101_v2.stl +0 -0
  134. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/scene.xml +23 -0
  135. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/scene_box.xml +35 -0
  136. so101_nexus-0.4.0/src/so101_nexus/assets/SO101_menagerie/so101.xml +269 -0
  137. so101_nexus-0.4.0/src/so101_nexus/camera_utils.py +182 -0
  138. so101_nexus-0.4.0/src/so101_nexus/cli.py +34 -0
  139. so101_nexus-0.4.0/src/so101_nexus/config.py +900 -0
  140. so101_nexus-0.4.0/src/so101_nexus/constants.py +85 -0
  141. so101_nexus-0.4.0/src/so101_nexus/env_ids.py +44 -0
  142. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/__init__.py +15 -0
  143. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/normalization.py +229 -0
  144. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/sim_camera.py +111 -0
  145. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/sim_camera_config.py +15 -0
  146. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/sim_follower.py +296 -0
  147. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/sim_follower_config.py +25 -0
  148. so101_nexus-0.4.0/src/so101_nexus/lerobot_adapter/synthetic_calibration.py +85 -0
  149. so101_nexus-0.4.0/src/so101_nexus/mujoco/__init__.py +40 -0
  150. so101_nexus-0.4.0/src/so101_nexus/mujoco/base_env.py +667 -0
  151. so101_nexus-0.4.0/src/so101_nexus/mujoco/look_at_env.py +193 -0
  152. so101_nexus-0.4.0/src/so101_nexus/mujoco/move_env.py +156 -0
  153. so101_nexus-0.4.0/src/so101_nexus/mujoco/pick_and_place.py +272 -0
  154. so101_nexus-0.4.0/src/so101_nexus/mujoco/pick_env.py +457 -0
  155. so101_nexus-0.4.0/src/so101_nexus/mujoco/reach_env.py +118 -0
  156. so101_nexus-0.4.0/src/so101_nexus/mujoco/spawn_utils.py +136 -0
  157. so101_nexus-0.4.0/src/so101_nexus/objects.py +130 -0
  158. so101_nexus-0.4.0/src/so101_nexus/observations.py +266 -0
  159. so101_nexus-0.4.0/src/so101_nexus/policy_adapters/__init__.py +9 -0
  160. so101_nexus-0.4.0/src/so101_nexus/policy_adapters/chunked_policy.py +15 -0
  161. so101_nexus-0.4.0/src/so101_nexus/policy_adapters/molmoact.py +187 -0
  162. so101_nexus-0.4.0/src/so101_nexus/policy_adapters/recorder.py +190 -0
  163. so101_nexus-0.4.0/src/so101_nexus/processors/__init__.py +33 -0
  164. so101_nexus-0.4.0/src/so101_nexus/processors/action.py +125 -0
  165. so101_nexus-0.4.0/src/so101_nexus/processors/lerobot_env_wrapper.py +127 -0
  166. so101_nexus-0.4.0/src/so101_nexus/processors/observation.py +67 -0
  167. so101_nexus-0.4.0/src/so101_nexus/processors/pipelines.py +150 -0
  168. so101_nexus-0.4.0/src/so101_nexus/rewards.py +100 -0
  169. so101_nexus-0.4.0/src/so101_nexus/scene.py +110 -0
  170. so101_nexus-0.4.0/src/so101_nexus/teleop/__init__.py +29 -0
  171. so101_nexus-0.4.0/src/so101_nexus/teleop/__main__.py +8 -0
  172. so101_nexus-0.4.0/src/so101_nexus/teleop/app.py +1628 -0
  173. so101_nexus-0.4.0/src/so101_nexus/teleop/cli.py +51 -0
  174. so101_nexus-0.4.0/src/so101_nexus/teleop/config_customization.py +407 -0
  175. so101_nexus-0.4.0/src/so101_nexus/teleop/dataset.py +125 -0
  176. so101_nexus-0.4.0/src/so101_nexus/teleop/leader.py +151 -0
  177. so101_nexus-0.4.0/src/so101_nexus/teleop/recorder.py +327 -0
  178. so101_nexus-0.4.0/src/so101_nexus/teleop/session.py +370 -0
  179. so101_nexus-0.4.0/src/so101_nexus/testing/__init__.py +12 -0
  180. so101_nexus-0.4.0/src/so101_nexus/testing/cli_contract.py +55 -0
  181. so101_nexus-0.4.0/src/so101_nexus/testing/contract.py +114 -0
  182. so101_nexus-0.4.0/src/so101_nexus/testing/env_id_filter.py +32 -0
  183. so101_nexus-0.4.0/src/so101_nexus/testing/invariants.py +107 -0
  184. so101_nexus-0.4.0/src/so101_nexus/visualization.py +168 -0
  185. so101_nexus-0.4.0/src/so101_nexus/warp/__init__.py +41 -0
  186. so101_nexus-0.4.0/src/so101_nexus/warp/base_env.py +587 -0
  187. so101_nexus-0.4.0/src/so101_nexus/warp/look_at_env.py +133 -0
  188. so101_nexus-0.4.0/src/so101_nexus/warp/move_env.py +129 -0
  189. so101_nexus-0.4.0/src/so101_nexus/warp/pick_and_place.py +269 -0
  190. so101_nexus-0.4.0/src/so101_nexus/warp/pick_env.py +209 -0
  191. so101_nexus-0.4.0/src/so101_nexus/warp/reach_env.py +122 -0
  192. so101_nexus-0.4.0/src/so101_nexus/ycb_assets.py +203 -0
  193. so101_nexus-0.4.0/src/so101_nexus/ycb_geometry.py +39 -0
  194. so101_nexus-0.4.0/tests/conftest.py +5 -0
  195. so101_nexus-0.4.0/tests/core/test_assets.py +15 -0
  196. so101_nexus-0.4.0/tests/core/test_camera_utils.py +106 -0
  197. so101_nexus-0.4.0/tests/core/test_camera_utils_properties.py +77 -0
  198. so101_nexus-0.4.0/tests/core/test_config.py +711 -0
  199. so101_nexus-0.4.0/tests/core/test_contract.py +20 -0
  200. so101_nexus-0.4.0/tests/core/test_env_ids.py +79 -0
  201. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_camera.py +85 -0
  202. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_follower.py +363 -0
  203. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_mujoco.py +96 -0
  204. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_normalization.py +240 -0
  205. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_record_loop.py +224 -0
  206. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_registration.py +41 -0
  207. so101_nexus-0.4.0/tests/core/test_lerobot_adapter_synthetic_calibration.py +86 -0
  208. so101_nexus-0.4.0/tests/core/test_lerobot_env_wrapper.py +147 -0
  209. so101_nexus-0.4.0/tests/core/test_molmoact_policy.py +191 -0
  210. so101_nexus-0.4.0/tests/core/test_objects.py +91 -0
  211. so101_nexus-0.4.0/tests/core/test_observations.py +139 -0
  212. so101_nexus-0.4.0/tests/core/test_processors_action.py +100 -0
  213. so101_nexus-0.4.0/tests/core/test_processors_observation.py +62 -0
  214. so101_nexus-0.4.0/tests/core/test_processors_pipelines.py +190 -0
  215. so101_nexus-0.4.0/tests/core/test_rewards.py +59 -0
  216. so101_nexus-0.4.0/tests/core/test_rewards_properties.py +72 -0
  217. so101_nexus-0.4.0/tests/core/test_rewards_tensor.py +77 -0
  218. so101_nexus-0.4.0/tests/core/test_rollout_recorder.py +157 -0
  219. so101_nexus-0.4.0/tests/core/test_rollout_recorder_dataset.py +116 -0
  220. so101_nexus-0.4.0/tests/core/test_scene.py +68 -0
  221. so101_nexus-0.4.0/tests/core/test_source_guardrails.py +195 -0
  222. so101_nexus-0.4.0/tests/core/test_teleop_app_helpers.py +1231 -0
  223. so101_nexus-0.4.0/tests/core/test_teleop_cli.py +83 -0
  224. so101_nexus-0.4.0/tests/core/test_teleop_config_customization.py +356 -0
  225. so101_nexus-0.4.0/tests/core/test_teleop_dataset.py +232 -0
  226. so101_nexus-0.4.0/tests/core/test_teleop_dataset_compat.py +125 -0
  227. so101_nexus-0.4.0/tests/core/test_teleop_leader.py +195 -0
  228. so101_nexus-0.4.0/tests/core/test_teleop_recorder.py +316 -0
  229. so101_nexus-0.4.0/tests/core/test_teleop_session.py +394 -0
  230. so101_nexus-0.4.0/tests/core/test_teleop_ui.py +73 -0
  231. so101_nexus-0.4.0/tests/core/test_visualization.py +165 -0
  232. so101_nexus-0.4.0/tests/core/test_warp_registration.py +25 -0
  233. so101_nexus-0.4.0/tests/core/test_ycb.py +443 -0
  234. so101_nexus-0.4.0/tests/core/test_ycb_geometry_properties.py +83 -0
  235. so101_nexus-0.4.0/tests/mujoco/__init__.py +0 -0
  236. so101_nexus-0.4.0/tests/mujoco/test_base_env_init_qpos.py +243 -0
  237. so101_nexus-0.4.0/tests/mujoco/test_cli.py +86 -0
  238. so101_nexus-0.4.0/tests/mujoco/test_env_ids_filter.py +19 -0
  239. so101_nexus-0.4.0/tests/mujoco/test_env_invariants.py +54 -0
  240. so101_nexus-0.4.0/tests/mujoco/test_envs.py +1112 -0
  241. so101_nexus-0.4.0/tests/mujoco/test_menagerie_model.py +227 -0
  242. so101_nexus-0.4.0/tests/mujoco/test_obs_mode_config.py +201 -0
  243. so101_nexus-0.4.0/tests/mujoco/test_pick_and_place_config.py +77 -0
  244. so101_nexus-0.4.0/tests/mujoco/test_reward_penalties.py +115 -0
  245. so101_nexus-0.4.0/tests/mujoco/test_spawn_utils.py +381 -0
  246. so101_nexus-0.4.0/tests/mujoco/test_teleop_env_kwargs.py +67 -0
  247. so101_nexus-0.4.0/tests/test_docs_consistency.py +76 -0
  248. so101_nexus-0.4.0/tests/test_release_smoke_workflow.py +42 -0
  249. so101_nexus-0.4.0/tests/visual/__init__.py +0 -0
  250. so101_nexus-0.4.0/tests/visual/conftest.py +152 -0
  251. so101_nexus-0.4.0/tests/visual/test_mujoco_visual.py +182 -0
  252. so101_nexus-0.4.0/tests/warp/conftest.py +14 -0
  253. so101_nexus-0.4.0/tests/warp/test_package.py +15 -0
  254. so101_nexus-0.4.0/tests/warp/test_ppo_warp_smoke.py +49 -0
  255. so101_nexus-0.4.0/tests/warp/test_warp_envs.py +314 -0
  256. so101_nexus-0.4.0/tests/warp/test_warp_helpers.py +80 -0
  257. so101_nexus-0.4.0/tests/warp/test_warp_make_vec.py +24 -0
  258. so101_nexus-0.4.0/tests/warp/test_warp_reach.py +221 -0
  259. so101_nexus-0.4.0/tests/warp/test_warp_reset_contract.py +98 -0
  260. so101_nexus-0.4.0/utils/visualize_env.py +150 -0
  261. so101_nexus-0.4.0/uv.lock +4277 -0
@@ -0,0 +1,43 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ day: monday
8
+ time: "10:00"
9
+ timezone: America/New_York
10
+ groups:
11
+ github-actions:
12
+ patterns:
13
+ - "*"
14
+ commit-message:
15
+ prefix: chore
16
+
17
+ - package-ecosystem: uv
18
+ directory: /
19
+ schedule:
20
+ interval: weekly
21
+ day: monday
22
+ time: "10:30"
23
+ timezone: America/New_York
24
+ groups:
25
+ python-dependencies:
26
+ patterns:
27
+ - "*"
28
+ commit-message:
29
+ prefix: chore
30
+
31
+ - package-ecosystem: npm
32
+ directory: /docs
33
+ schedule:
34
+ interval: weekly
35
+ day: monday
36
+ time: "11:00"
37
+ timezone: America/New_York
38
+ groups:
39
+ docs-dependencies:
40
+ patterns:
41
+ - "*"
42
+ commit-message:
43
+ prefix: chore
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ api_version="${GITHUB_API_VERSION:-2026-03-10}"
5
+ token="${GITHUB_TOKEN:-${GH_TOKEN:-}}"
6
+ branch="${BRANCH:-main}"
7
+
8
+ remote_url="$(git config --get remote.origin.url)"
9
+ owner_repo="${remote_url#git@github.com:}"
10
+ owner_repo="${owner_repo#https://github.com/}"
11
+ owner_repo="${owner_repo%.git}"
12
+
13
+ owner="${GITHUB_OWNER:-${OWNER:-${owner_repo%%/*}}}"
14
+ repo="${GITHUB_REPO:-${REPO:-${owner_repo#*/}}}"
15
+
16
+ if [[ -z "$token" ]]; then
17
+ cat >&2 <<'EOF'
18
+ Set GITHUB_TOKEN or GH_TOKEN to a token with Administration: write for this repository.
19
+ For a fine-grained PAT, grant Repository administration: read and write.
20
+ EOF
21
+ exit 2
22
+ fi
23
+
24
+ api() {
25
+ local method="$1"
26
+ local path="$2"
27
+ shift 2
28
+
29
+ curl -fsSL \
30
+ -X "$method" \
31
+ -H "Accept: application/vnd.github+json" \
32
+ -H "Authorization: Bearer ${token}" \
33
+ -H "X-GitHub-Api-Version: ${api_version}" \
34
+ "https://api.github.com${path}" \
35
+ "$@"
36
+ }
37
+
38
+ tmpdir="$(mktemp -d)"
39
+ trap 'rm -rf "$tmpdir"' EXIT
40
+
41
+ cat >"$tmpdir/actions-permissions.json" <<'JSON'
42
+ {
43
+ "enabled": true,
44
+ "allowed_actions": "selected",
45
+ "sha_pinning_required": true
46
+ }
47
+ JSON
48
+
49
+ cat >"$tmpdir/selected-actions.json" <<'JSON'
50
+ {
51
+ "github_owned_allowed": true,
52
+ "verified_allowed": false,
53
+ "patterns_allowed": [
54
+ "astral-sh/setup-uv@*",
55
+ "pnpm/action-setup@*",
56
+ "pypa/gh-action-pypi-publish@*"
57
+ ]
58
+ }
59
+ JSON
60
+
61
+ cat >"$tmpdir/workflow-permissions.json" <<'JSON'
62
+ {
63
+ "default_workflow_permissions": "read",
64
+ "can_approve_pull_request_reviews": false
65
+ }
66
+ JSON
67
+
68
+ cat >"$tmpdir/fork-pr-approval.json" <<'JSON'
69
+ {
70
+ "approval_policy": "all_external_contributors"
71
+ }
72
+ JSON
73
+
74
+ cat >"$tmpdir/branch-protection.json" <<'JSON'
75
+ {
76
+ "required_status_checks": {
77
+ "strict": true,
78
+ "contexts": [
79
+ "lint",
80
+ "test",
81
+ "docs",
82
+ "dependency-review"
83
+ ]
84
+ },
85
+ "enforce_admins": true,
86
+ "required_pull_request_reviews": {
87
+ "dismiss_stale_reviews": true,
88
+ "require_code_owner_reviews": false,
89
+ "required_approving_review_count": 1,
90
+ "require_last_push_approval": true
91
+ },
92
+ "restrictions": null,
93
+ "required_linear_history": true,
94
+ "allow_force_pushes": false,
95
+ "allow_deletions": false,
96
+ "block_creations": false,
97
+ "required_conversation_resolution": true,
98
+ "lock_branch": false,
99
+ "allow_fork_syncing": true
100
+ }
101
+ JSON
102
+
103
+ echo "Applying repository Actions policy for ${owner}/${repo}"
104
+ api PUT "/repos/${owner}/${repo}/actions/permissions" \
105
+ -d @"$tmpdir/actions-permissions.json" >/dev/null
106
+
107
+ echo "Allowing only GitHub-owned actions and explicit third-party action repositories"
108
+ api PUT "/repos/${owner}/${repo}/actions/permissions/selected-actions" \
109
+ -d @"$tmpdir/selected-actions.json" >/dev/null
110
+
111
+ echo "Setting the default GITHUB_TOKEN permission to read-only"
112
+ api PUT "/repos/${owner}/${repo}/actions/permissions/workflow" \
113
+ -d @"$tmpdir/workflow-permissions.json" >/dev/null
114
+
115
+ echo "Requiring approval for all external fork pull request workflows"
116
+ api PUT "/repos/${owner}/${repo}/actions/permissions/fork-pr-contributor-approval" \
117
+ -d @"$tmpdir/fork-pr-approval.json" >/dev/null
118
+
119
+ echo "Applying branch protection to ${branch}"
120
+ api PUT "/repos/${owner}/${repo}/branches/${branch}/protection" \
121
+ -d @"$tmpdir/branch-protection.json" >/dev/null
122
+
123
+ echo "Requiring signed commits on ${branch}"
124
+ api POST "/repos/${owner}/${repo}/branches/${branch}/protection/required_signatures" >/dev/null
125
+
126
+ echo "Repository Actions policy and ${branch} branch protection are configured."
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ """Smoke-check the installed so101-nexus package from PyPI."""
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Literal
6
+
7
+ import gymnasium as gym
8
+ import tyro
9
+
10
+
11
+ def run_mujoco(env_id: str) -> None:
12
+ """Reset and step a registered MuJoCo env to verify the install works."""
13
+ import so101_nexus.mujoco # noqa: F401
14
+
15
+ env = gym.make(env_id)
16
+ try:
17
+ obs, info = env.reset(seed=0)
18
+ action = env.action_space.sample()
19
+ step_result = env.step(action)
20
+ finally:
21
+ env.close()
22
+
23
+ if obs is None or info is None:
24
+ raise RuntimeError("MuJoCo smoke check failed: reset returned empty values.")
25
+ if len(step_result) != 5:
26
+ raise RuntimeError("MuJoCo smoke check failed: step did not return a 5-tuple.")
27
+
28
+
29
+ def run_warp(env_id: str, *, device: str, num_envs: int) -> None:
30
+ """Reset and step a batched Warp vector env to verify the install works."""
31
+ import so101_nexus.warp # noqa: F401
32
+
33
+ env = gym.make_vec(
34
+ env_id, num_envs=num_envs, device=device, vectorization_mode="vector_entry_point"
35
+ )
36
+ try:
37
+ obs, info = env.reset(seed=0)
38
+ action = env.action_space.sample()
39
+ step_result = env.step(action)
40
+ finally:
41
+ env.close()
42
+
43
+ if obs is None or info is None:
44
+ raise RuntimeError("Warp smoke check failed: reset returned empty values.")
45
+ if len(step_result) != 5:
46
+ raise RuntimeError("Warp smoke check failed: step did not return a 5-tuple.")
47
+
48
+
49
+ @dataclass
50
+ class Args:
51
+ """Command-line arguments for the PyPI smoke check."""
52
+
53
+ env_id: str
54
+ backend: Literal["mujoco", "warp"] = "mujoco"
55
+ device: str = "cpu"
56
+ num_envs: int = 2
57
+
58
+
59
+ def main(args: Args) -> None:
60
+ """Run the smoke check for the requested backend."""
61
+ print(f"Running smoke check for backend={args.backend}, env={args.env_id}")
62
+ if args.backend == "warp":
63
+ run_warp(args.env_id, device=args.device, num_envs=args.num_envs)
64
+ else:
65
+ run_mujoco(args.env_id)
66
+ print("Smoke check passed.")
67
+
68
+
69
+ if __name__ == "__main__":
70
+ main(tyro.cli(Args))
@@ -0,0 +1,148 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ env:
17
+ PYTHON_VERSION: "3.12"
18
+ UV_CACHE_DEPENDENCY_GLOB: uv.lock
19
+
20
+ jobs:
21
+ lint:
22
+ name: lint
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 20
25
+ steps:
26
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
27
+ with:
28
+ persist-credentials: false
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
31
+ with:
32
+ enable-cache: true
33
+ cache-dependency-glob: ${{ env.UV_CACHE_DEPENDENCY_GLOB }}
34
+ cache-suffix: ci-lint
35
+ - name: Set up Python
36
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
37
+ with:
38
+ python-version: ${{ env.PYTHON_VERSION }}
39
+ - name: Install dev dependencies
40
+ run: uv sync --locked --group dev
41
+ - name: Lint
42
+ run: make lint
43
+
44
+ test:
45
+ name: test
46
+ runs-on: ubuntu-latest
47
+ timeout-minutes: 60
48
+ steps:
49
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
50
+ with:
51
+ persist-credentials: false
52
+
53
+ - name: System deps
54
+ run: |
55
+ sudo apt-get update
56
+ sudo apt-get install -y xvfb ffmpeg libgl1 libglx-mesa0 libegl1-mesa-dev libosmesa6-dev
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
60
+ with:
61
+ enable-cache: true
62
+ cache-dependency-glob: ${{ env.UV_CACHE_DEPENDENCY_GLOB }}
63
+ cache-suffix: ci-test
64
+
65
+ - name: Set up Python
66
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
67
+ with:
68
+ python-version: ${{ env.PYTHON_VERSION }}
69
+
70
+ - name: Install dependencies
71
+ run: uv sync --locked --group test
72
+
73
+ - name: Run tests
74
+ run: xvfb-run -a make test
75
+
76
+ test-warp:
77
+ name: test-warp
78
+ runs-on: ubuntu-latest
79
+ timeout-minutes: 45
80
+ steps:
81
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
82
+ with:
83
+ persist-credentials: false
84
+
85
+ - name: System deps
86
+ run: |
87
+ sudo apt-get update
88
+ sudo apt-get install -y libgl1 libglx-mesa0 libegl1-mesa-dev libosmesa6-dev
89
+
90
+ - name: Install uv
91
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
92
+ with:
93
+ enable-cache: true
94
+ cache-dependency-glob: ${{ env.UV_CACHE_DEPENDENCY_GLOB }}
95
+ cache-suffix: ci-test-warp
96
+
97
+ - name: Set up Python
98
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
99
+ with:
100
+ python-version: ${{ env.PYTHON_VERSION }}
101
+
102
+ - name: Install dependencies
103
+ run: uv sync --locked --extra warp
104
+
105
+ - name: Run Warp tests
106
+ env:
107
+ MUJOCO_GL: egl
108
+ run: make test-warp
109
+
110
+ docs:
111
+ name: docs
112
+ runs-on: ubuntu-latest
113
+ timeout-minutes: 25
114
+ steps:
115
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
116
+ with:
117
+ persist-credentials: false
118
+ - name: Install uv
119
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
120
+ with:
121
+ enable-cache: true
122
+ cache-dependency-glob: ${{ env.UV_CACHE_DEPENDENCY_GLOB }}
123
+ cache-suffix: ci-docs
124
+ - name: Set up Python
125
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
126
+ with:
127
+ python-version: ${{ env.PYTHON_VERSION }}
128
+ - name: Set up pnpm
129
+ uses: pnpm/action-setup@d15e628ca66d93ee5f352c71671a7bc6a97af5c9 # v6.0.8
130
+ with:
131
+ version: 9
132
+ - name: Set up Node
133
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
134
+ with:
135
+ node-version: "22"
136
+ cache: pnpm
137
+ cache-dependency-path: docs/pnpm-lock.yaml
138
+ - name: Cache Next.js build output
139
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
140
+ with:
141
+ path: docs/.next/cache
142
+ key: ${{ runner.os }}-next-${{ hashFiles('docs/pnpm-lock.yaml') }}-${{ hashFiles('docs/app/**', 'docs/content/**', 'docs/lib/**', 'docs/*.mjs', 'docs/*.ts', 'docs/*.json') }}
143
+ restore-keys: |
144
+ ${{ runner.os }}-next-${{ hashFiles('docs/pnpm-lock.yaml') }}-
145
+ - name: Install dev dependencies
146
+ run: uv sync --locked --group dev
147
+ - name: Run docs checks
148
+ run: make docs-check
@@ -0,0 +1,32 @@
1
+ name: Dependency Review
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ dependency-review:
16
+ name: dependency-review
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 10
19
+ steps:
20
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - name: Dependency Review
25
+ uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
26
+ with:
27
+ fail-on-severity: moderate
28
+ fail-on-scopes: runtime,development
29
+ vulnerability-check: true
30
+ license-check: false
31
+ show-openssf-scorecard: true
32
+ warn-on-openssf-scorecard-level: 3
@@ -0,0 +1,71 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - ".github/workflows/deploy-docs.yml"
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: "pages"
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ build:
20
+ name: build
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 25
23
+ steps:
24
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
25
+ with:
26
+ persist-credentials: false
27
+
28
+ - uses: pnpm/action-setup@d15e628ca66d93ee5f352c71671a7bc6a97af5c9 # v6.0.8
29
+ with:
30
+ version: 10
31
+
32
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
33
+ with:
34
+ node-version: 22
35
+ cache: pnpm
36
+ cache-dependency-path: docs/pnpm-lock.yaml
37
+
38
+ - name: Cache Next.js build output
39
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
40
+ with:
41
+ path: docs/.next/cache
42
+ key: ${{ runner.os }}-next-${{ hashFiles('docs/pnpm-lock.yaml') }}-${{ hashFiles('docs/app/**', 'docs/content/**', 'docs/lib/**', 'docs/*.mjs', 'docs/*.ts', 'docs/*.json') }}
43
+ restore-keys: |
44
+ ${{ runner.os }}-next-${{ hashFiles('docs/pnpm-lock.yaml') }}-
45
+
46
+ - name: Install dependencies
47
+ working-directory: docs
48
+ run: pnpm install --frozen-lockfile
49
+
50
+ - name: Build
51
+ working-directory: docs
52
+ run: pnpm build
53
+
54
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
55
+ with:
56
+ path: docs/out
57
+
58
+ deploy:
59
+ name: deploy
60
+ environment:
61
+ name: github-pages
62
+ url: ${{ steps.deployment.outputs.page_url }}
63
+ runs-on: ubuntu-latest
64
+ needs: build
65
+ timeout-minutes: 10
66
+ permissions:
67
+ pages: write
68
+ id-token: write
69
+ steps:
70
+ - id: deployment
71
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
@@ -0,0 +1,68 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: publish-${{ github.event.release.tag_name }}
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ build:
16
+ name: build
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 25
19
+
20
+ steps:
21
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
22
+ with:
23
+ persist-credentials: false
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
27
+ with:
28
+ enable-cache: false
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
32
+ with:
33
+ python-version: "3.x"
34
+
35
+ - name: Build so101-nexus
36
+ run: uv build --out-dir dist/
37
+
38
+ - name: Upload distributions
39
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
40
+ with:
41
+ name: release-dists
42
+ path: dist/
43
+ if-no-files-found: error
44
+ retention-days: 7
45
+
46
+ pypi-publish:
47
+ name: pypi-publish
48
+ runs-on: ubuntu-latest
49
+ needs: build
50
+ timeout-minutes: 15
51
+ permissions:
52
+ id-token: write
53
+
54
+ environment:
55
+ name: pypi
56
+
57
+ steps:
58
+ - name: Download all distributions
59
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
60
+ with:
61
+ name: release-dists
62
+ path: dist/
63
+
64
+ - name: Publish to PyPI
65
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
66
+ with:
67
+ packages-dir: dist/
68
+ attestations: true
@@ -0,0 +1,95 @@
1
+ name: Release Smoke Test
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version:
9
+ description: "Package version to install (e.g. 0.1.0 or v0.1.0). Empty = latest."
10
+ required: false
11
+ type: string
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: release-smoke-${{ github.event.release.tag_name || github.event.inputs.version || github.ref }}
18
+ cancel-in-progress: false
19
+
20
+ jobs:
21
+ smoke-test:
22
+ name: smoke-test (${{ matrix.backend }})
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 45
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ include:
29
+ - backend: mujoco
30
+ package: so101-nexus
31
+ env_id: MuJoCoReach-v1
32
+ apt_packages: "xvfb libgl1 libglx-mesa0 libegl1-mesa-dev libosmesa6-dev"
33
+ - backend: warp
34
+ package: "so101-nexus[warp]"
35
+ env_id: WarpReach-v1
36
+ apt_packages: "xvfb libgl1 libglx-mesa0 libegl1-mesa-dev libosmesa6-dev"
37
+
38
+ steps:
39
+ - uses: actions/checkout@9f698171ed81b15d1823a05fc7211befd50c8ae0 # v6.0.3
40
+ with:
41
+ persist-credentials: false
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
45
+ with:
46
+ python-version: "3.12"
47
+
48
+ - name: Install system dependencies
49
+ run: |
50
+ sudo apt-get update
51
+ sudo apt-get install -y ${{ matrix.apt_packages }}
52
+
53
+ - name: Install package from PyPI (with retries)
54
+ env:
55
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
56
+ INPUT_VERSION: ${{ github.event.inputs.version }}
57
+ PACKAGE: ${{ matrix.package }}
58
+ run: |
59
+ set -euo pipefail
60
+ python -m pip install --upgrade pip
61
+
62
+ VERSION="${INPUT_VERSION:-}"
63
+ if [ -z "$VERSION" ]; then
64
+ VERSION="${RELEASE_TAG:-}"
65
+ fi
66
+ VERSION="${VERSION#v}"
67
+
68
+ if [ -n "$VERSION" ]; then
69
+ SPEC="${PACKAGE}==${VERSION}"
70
+ else
71
+ SPEC="${PACKAGE}"
72
+ fi
73
+
74
+ echo "Installing ${SPEC}"
75
+ for i in {1..10}; do
76
+ if python -m pip install "${SPEC}"; then
77
+ exit 0
78
+ fi
79
+ echo "Install attempt ${i}/10 failed; retrying in 30s..."
80
+ sleep 30
81
+ done
82
+
83
+ echo "Failed to install ${SPEC} from PyPI after retries."
84
+ exit 1
85
+
86
+ - name: Install smoke-check harness deps
87
+ run: python -m pip install tyro
88
+
89
+ - name: Run backend smoke check
90
+ env:
91
+ MUJOCO_GL: egl
92
+ run: |
93
+ python .github/scripts/pypi_smoke_check.py \
94
+ --backend "${{ matrix.backend }}" \
95
+ --env-id "${{ matrix.env_id }}"