reachy-mini 1.2.5rc1__py3-none-any.whl
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.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +25 -0
- reachy_mini/apps/app.py +258 -0
- reachy_mini/apps/assistant.py +787 -0
- reachy_mini/apps/manager.py +312 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +106 -0
- reachy_mini/apps/sources/local_common_venv.py +567 -0
- reachy_mini/apps/templates/README.md.j2 +12 -0
- reachy_mini/apps/templates/gitignore.j2 +3 -0
- reachy_mini/apps/templates/index.html.j2 +40 -0
- reachy_mini/apps/templates/main.py.j2 +74 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
- reachy_mini/apps/templates/static/index.html.j2 +27 -0
- reachy_mini/apps/templates/static/main.js.j2 +47 -0
- reachy_mini/apps/templates/static/style.css.j2 +25 -0
- reachy_mini/apps/templates/style.css.j2 +411 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +164 -0
- reachy_mini/assets/confused1.wav +0 -0
- reachy_mini/assets/count.wav +0 -0
- reachy_mini/assets/dance1.wav +0 -0
- reachy_mini/assets/firmware/CHANGELOG.md +15 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
- reachy_mini/assets/firmware/update.sh +9 -0
- reachy_mini/assets/go_sleep.wav +0 -0
- reachy_mini/assets/impatient1.wav +0 -0
- reachy_mini/assets/kinematics_data.json +253 -0
- reachy_mini/assets/models/fknetwork.dynamic.onnx +0 -0
- reachy_mini/assets/models/fknetwork.onnx +0 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
- reachy_mini/assets/models/iknetwork.onnx +0 -0
- reachy_mini/assets/wake_up.wav +0 -0
- reachy_mini/daemon/__init__.py +1 -0
- reachy_mini/daemon/app/__init__.py +1 -0
- reachy_mini/daemon/app/bg_job_register.py +142 -0
- reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
- reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
- reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
- reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
- reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
- reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
- reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +469 -0
- reachy_mini/daemon/app/models.py +149 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +85 -0
- reachy_mini/daemon/app/routers/kinematics.py +57 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +265 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/app/routers/update.py +86 -0
- reachy_mini/daemon/app/routers/volume.py +423 -0
- reachy_mini/daemon/app/routers/wifi_config.py +219 -0
- reachy_mini/daemon/app/services/__init__.py +1 -0
- reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
- reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
- reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
- reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
- reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
- reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
- reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
- reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
- reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
- reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +815 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +395 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +597 -0
- reachy_mini/daemon/daemon.py +651 -0
- reachy_mini/daemon/utils.py +116 -0
- reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -0
- reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
- reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +642 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +132 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
- reachy_mini/io/__init__.py +15 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/audio_ws.py +242 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/video_ws.py +135 -0
- reachy_mini/io/ws_controller.py +120 -0
- reachy_mini/io/zenoh_client.py +273 -0
- reachy_mini/io/zenoh_server.py +230 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +151 -0
- reachy_mini/kinematics/nn_kinematics.py +109 -0
- reachy_mini/kinematics/placo_kinematics.py +656 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +111 -0
- reachy_mini/media/audio_control_utils.py +455 -0
- reachy_mini/media/audio_gstreamer.py +272 -0
- reachy_mini/media/audio_sounddevice.py +321 -0
- reachy_mini/media/audio_utils.py +114 -0
- reachy_mini/media/camera_base.py +100 -0
- reachy_mini/media/camera_constants.py +152 -0
- reachy_mini/media/camera_gstreamer.py +231 -0
- reachy_mini/media/camera_opencv.py +89 -0
- reachy_mini/media/camera_utils.py +111 -0
- reachy_mini/media/media_manager.py +281 -0
- reachy_mini/media/webrtc_client_gstreamer.py +288 -0
- reachy_mini/media/webrtc_daemon.py +334 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +43 -0
- reachy_mini/motion/recorded_move.py +146 -0
- reachy_mini/reachy_mini.py +753 -0
- reachy_mini/tools/reflash_motors.py +116 -0
- reachy_mini/tools/setup_motor.py +422 -0
- reachy_mini/tools/setup_motor_rpi.py +108 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/hardware_config/__init__.py +1 -0
- reachy_mini/utils/hardware_config/parser.py +65 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
- reachy_mini/utils/rerun.py +320 -0
- reachy_mini/utils/wireless_version/__init__.py +1 -0
- reachy_mini/utils/wireless_version/startup_check.py +144 -0
- reachy_mini/utils/wireless_version/update.py +20 -0
- reachy_mini/utils/wireless_version/update_available.py +61 -0
- reachy_mini/utils/wireless_version/utils.py +39 -0
- reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
- reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
- reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
- reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
"""Reachy Mini app assistant functions."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
import questionary
|
|
13
|
+
import toml
|
|
14
|
+
import yaml
|
|
15
|
+
from huggingface_hub import CommitOperationAdd, HfApi, get_repo_discussions, whoami
|
|
16
|
+
from jinja2 import Environment, FileSystemLoader
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def validate_app_name(text: str) -> bool | str:
|
|
22
|
+
"""Validate the app name."""
|
|
23
|
+
if not text.strip():
|
|
24
|
+
return "App name cannot be empty."
|
|
25
|
+
if " " in text:
|
|
26
|
+
return "App name cannot contain spaces."
|
|
27
|
+
if "-" in text:
|
|
28
|
+
return "App name cannot contain dashes ('-'). Please use underscores ('_') instead."
|
|
29
|
+
if "/" in text or "\\" in text:
|
|
30
|
+
return "App name cannot contain slashes or backslashes ('/' or '\\')."
|
|
31
|
+
if "*" in text or "?" in text or "." in text:
|
|
32
|
+
return "App name cannot contain wildcard characters ('*', '?', or '.')."
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def is_git_repo(path: Path) -> bool:
|
|
37
|
+
"""Check if the given path is inside a git repository."""
|
|
38
|
+
try:
|
|
39
|
+
subprocess.check_output(
|
|
40
|
+
["git", "-C", path, "rev-parse", "--is-inside-work-tree"],
|
|
41
|
+
stderr=subprocess.STDOUT,
|
|
42
|
+
)
|
|
43
|
+
return True
|
|
44
|
+
except subprocess.CalledProcessError:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def validate_location(text: str) -> bool | str:
|
|
49
|
+
"""Validate the location where to create the app project."""
|
|
50
|
+
path = Path(text).expanduser().resolve()
|
|
51
|
+
if not os.path.exists(path):
|
|
52
|
+
return f"The path {path} does not exist."
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_location_and_git_repo(text: str) -> bool | str:
|
|
58
|
+
"""Validate the location where to create the app project, ensuring it's not in a git repo."""
|
|
59
|
+
path = Path(text).expanduser().resolve()
|
|
60
|
+
if not os.path.exists(path):
|
|
61
|
+
return f"The path {path} does not exist."
|
|
62
|
+
if is_git_repo(path):
|
|
63
|
+
return f"The path {path} is already inside a git repository."
|
|
64
|
+
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def create_cli(
|
|
69
|
+
console: Console, app_name: str | None, app_path: Path | None
|
|
70
|
+
) -> tuple[str, str, Path]:
|
|
71
|
+
"""Create a new Reachy Mini app project using a CLI."""
|
|
72
|
+
if app_name is None:
|
|
73
|
+
# 1) App name
|
|
74
|
+
console.print("$ What is the name of your app ?")
|
|
75
|
+
app_name = questionary.text(
|
|
76
|
+
">",
|
|
77
|
+
default="",
|
|
78
|
+
validate=validate_app_name,
|
|
79
|
+
).ask()
|
|
80
|
+
|
|
81
|
+
if app_name is None:
|
|
82
|
+
console.print("[red]Aborted.[/red]")
|
|
83
|
+
exit()
|
|
84
|
+
app_name = app_name.strip().lower()
|
|
85
|
+
|
|
86
|
+
# 2) Language
|
|
87
|
+
console.print("\n$ Choose the language of your app")
|
|
88
|
+
language = questionary.select(
|
|
89
|
+
">",
|
|
90
|
+
choices=["python", "js"],
|
|
91
|
+
default="python",
|
|
92
|
+
).ask()
|
|
93
|
+
if language is None:
|
|
94
|
+
console.print("[red]Aborted.[/red]")
|
|
95
|
+
exit()
|
|
96
|
+
|
|
97
|
+
# js is not supported yet
|
|
98
|
+
if language != "python":
|
|
99
|
+
console.print("[red]Currently only Python apps are supported. Aborted.[/red]")
|
|
100
|
+
exit()
|
|
101
|
+
|
|
102
|
+
if app_path is None:
|
|
103
|
+
# 3) App path
|
|
104
|
+
console.print("\n$ Where do you want to create your app project ?")
|
|
105
|
+
app_path = questionary.path(
|
|
106
|
+
">",
|
|
107
|
+
default="",
|
|
108
|
+
validate=validate_location_and_git_repo,
|
|
109
|
+
).ask()
|
|
110
|
+
if app_path is None:
|
|
111
|
+
console.print("[red]Aborted.[/red]")
|
|
112
|
+
exit()
|
|
113
|
+
app_path = Path(app_path).expanduser().resolve()
|
|
114
|
+
if is_git_repo(app_path):
|
|
115
|
+
console.print(
|
|
116
|
+
f"[red] The path {app_path} is already inside a git repository. "
|
|
117
|
+
"Please choose another path. Aborted.[/red]"
|
|
118
|
+
)
|
|
119
|
+
exit()
|
|
120
|
+
|
|
121
|
+
return app_name, language, app_path
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def create(console: Console, app_name: str, app_path: Path) -> None:
|
|
125
|
+
"""Create a new Reachy Mini app project with the given name at the specified path.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
console (Console): The console object for printing messages.
|
|
129
|
+
app_name (str): The name of the app to create.
|
|
130
|
+
app_path (Path): The directory where the app project will be created.
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
app_name, language, app_path = create_cli(console, app_name, app_path)
|
|
134
|
+
app_name = app_name.replace(
|
|
135
|
+
"-", "_"
|
|
136
|
+
) # Force underscores in app name (belt and suspenders)
|
|
137
|
+
|
|
138
|
+
TEMPLATE_DIR = Path(__file__).parent / "templates"
|
|
139
|
+
env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
|
|
140
|
+
|
|
141
|
+
def render_template(filename: str, context: Dict[str, str]) -> str:
|
|
142
|
+
template = env.get_template(filename)
|
|
143
|
+
return template.render(context)
|
|
144
|
+
|
|
145
|
+
base_path = Path(app_path).resolve() / app_name
|
|
146
|
+
if base_path.exists():
|
|
147
|
+
console.print(f"ā Folder {base_path} already exists.", style="bold red")
|
|
148
|
+
exit()
|
|
149
|
+
|
|
150
|
+
module_name = app_name
|
|
151
|
+
entrypoint_name = app_name.replace("-", "_")
|
|
152
|
+
class_name = "".join(word.capitalize() for word in module_name.split("_"))
|
|
153
|
+
class_name_display = " ".join(word.capitalize() for word in module_name.split("_"))
|
|
154
|
+
|
|
155
|
+
base_path.mkdir()
|
|
156
|
+
(base_path / module_name).mkdir()
|
|
157
|
+
(base_path / module_name / "static").mkdir()
|
|
158
|
+
|
|
159
|
+
# Generate files
|
|
160
|
+
context = {
|
|
161
|
+
"app_name": app_name,
|
|
162
|
+
"package_name": app_name,
|
|
163
|
+
"module_name": module_name,
|
|
164
|
+
"class_name": class_name,
|
|
165
|
+
"class_name_display": class_name_display,
|
|
166
|
+
"entrypoint_name": entrypoint_name,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
(base_path / module_name / "__init__.py").touch()
|
|
170
|
+
(base_path / module_name / "main.py").write_text(
|
|
171
|
+
render_template("main.py.j2", context)
|
|
172
|
+
)
|
|
173
|
+
(base_path / module_name / "static" / "index.html").write_text(
|
|
174
|
+
render_template("static/index.html.j2", context)
|
|
175
|
+
)
|
|
176
|
+
(base_path / module_name / "static" / "style.css").write_text(
|
|
177
|
+
render_template("static/style.css.j2", context)
|
|
178
|
+
)
|
|
179
|
+
(base_path / module_name / "static" / "main.js").write_text(
|
|
180
|
+
render_template("static/main.js.j2", context)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
(base_path / "pyproject.toml").write_text(
|
|
184
|
+
render_template("pyproject.toml.j2", context)
|
|
185
|
+
)
|
|
186
|
+
(base_path / "README.md").write_text(render_template("README.md.j2", context))
|
|
187
|
+
|
|
188
|
+
(base_path / "index.html").write_text(render_template("index.html.j2", context))
|
|
189
|
+
(base_path / "style.css").write_text(render_template("style.css.j2", context))
|
|
190
|
+
(base_path / ".gitignore").write_text(render_template("gitignore.j2", context))
|
|
191
|
+
|
|
192
|
+
# TODO assets dir with a .gif ?
|
|
193
|
+
|
|
194
|
+
console.print(f"ā
Created app '{app_name}' in {base_path}/", style="bold green")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def install_app_with_progress(
|
|
198
|
+
console: Console, pip_executable: str, app_path: Path
|
|
199
|
+
) -> None:
|
|
200
|
+
"""Install the app in a temporary virtual environment with a progress spinner."""
|
|
201
|
+
console.print("Installing the app in the temporary virtual environment...")
|
|
202
|
+
|
|
203
|
+
# Start pip in the background, discard its output
|
|
204
|
+
process = subprocess.Popen(
|
|
205
|
+
[
|
|
206
|
+
pip_executable,
|
|
207
|
+
"install",
|
|
208
|
+
"-q", # quiet
|
|
209
|
+
str(app_path),
|
|
210
|
+
],
|
|
211
|
+
stdout=subprocess.DEVNULL,
|
|
212
|
+
stderr=subprocess.STDOUT,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
with Progress(
|
|
216
|
+
SpinnerColumn(),
|
|
217
|
+
TextColumn("[progress.description]{task.description}"),
|
|
218
|
+
TimeElapsedColumn(),
|
|
219
|
+
console=console,
|
|
220
|
+
) as progress:
|
|
221
|
+
task_id = progress.add_task("Installing dependencies...", start=True)
|
|
222
|
+
|
|
223
|
+
# Keep the spinner running while pip is working
|
|
224
|
+
while process.poll() is None:
|
|
225
|
+
time.sleep(0.1)
|
|
226
|
+
|
|
227
|
+
# Mark task as finished
|
|
228
|
+
progress.update(task_id, description="Installation finished")
|
|
229
|
+
|
|
230
|
+
# Handle exit code like check=True would
|
|
231
|
+
if process.returncode != 0:
|
|
232
|
+
raise subprocess.CalledProcessError(process.returncode, process.args)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def check(console: Console, app_path: str) -> None:
|
|
236
|
+
"""Check an existing Reachy Mini app project.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
console (Console): The console object for printing messages.
|
|
240
|
+
app_path (str): Local path to the app to check.
|
|
241
|
+
|
|
242
|
+
"""
|
|
243
|
+
if app_path is None:
|
|
244
|
+
console.print("\n$ What is the local path to the app you want to check?")
|
|
245
|
+
app_path = questionary.path(
|
|
246
|
+
">",
|
|
247
|
+
default="",
|
|
248
|
+
validate=validate_location,
|
|
249
|
+
).ask()
|
|
250
|
+
if app_path is None:
|
|
251
|
+
console.print("[red]Aborted.[/red]")
|
|
252
|
+
exit()
|
|
253
|
+
app_path = Path(app_path).expanduser().resolve()
|
|
254
|
+
|
|
255
|
+
if not os.path.exists(app_path):
|
|
256
|
+
console.print(f"[red]App path {app_path} does not exist.[/red]")
|
|
257
|
+
exit()
|
|
258
|
+
|
|
259
|
+
abs_app_path = Path(app_path).resolve()
|
|
260
|
+
|
|
261
|
+
# Check if there is a pyproject.toml file in the root of the app
|
|
262
|
+
pyproject_file = abs_app_path / "pyproject.toml"
|
|
263
|
+
if not pyproject_file.exists():
|
|
264
|
+
console.print("ā pyproject.toml is missing", style="bold red")
|
|
265
|
+
console.print(
|
|
266
|
+
"Make sure you are providing the path to the root of the app. This is the folder that contains pyproject.toml.",
|
|
267
|
+
style="bold blue",
|
|
268
|
+
)
|
|
269
|
+
sys.exit(1)
|
|
270
|
+
|
|
271
|
+
# Extract app name
|
|
272
|
+
with open(pyproject_file, "r", encoding="utf-8") as f:
|
|
273
|
+
pyproject_content = toml.load(f)
|
|
274
|
+
project = pyproject_content.get("project", {})
|
|
275
|
+
app_name = project.get("name", None)
|
|
276
|
+
if app_name is None:
|
|
277
|
+
console.print(
|
|
278
|
+
"ā Project name is missing in pyproject.toml", style="bold red"
|
|
279
|
+
)
|
|
280
|
+
sys.exit(1)
|
|
281
|
+
|
|
282
|
+
entrypoint_name = app_name
|
|
283
|
+
pkg_name = app_name.replace("-", "_")
|
|
284
|
+
class_name = "".join(word.capitalize() for word in pkg_name.split("_"))
|
|
285
|
+
|
|
286
|
+
console.print(f"\tExpected package name: {pkg_name}", style="dim")
|
|
287
|
+
console.print(f"\tExpected class name: {class_name}", style="dim")
|
|
288
|
+
console.print(f"\tExpected entrypoint name: {entrypoint_name}\n", style="dim")
|
|
289
|
+
|
|
290
|
+
# Check that:
|
|
291
|
+
# - index.html, style.css exist in the root of the app
|
|
292
|
+
|
|
293
|
+
if not os.path.exists(os.path.join(abs_app_path, "index.html")):
|
|
294
|
+
console.print("ā index.html is missing", style="bold red")
|
|
295
|
+
sys.exit(1)
|
|
296
|
+
|
|
297
|
+
if not os.path.exists(os.path.join(abs_app_path, "style.css")):
|
|
298
|
+
console.print("ā style.css is missing", style="bold red")
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
console.print("ā
index.html and style.css exist in the root of the app.")
|
|
301
|
+
|
|
302
|
+
# - pkg_name and pkg_name/__init__.py exists
|
|
303
|
+
if not os.path.exists(os.path.join(abs_app_path, pkg_name)) or not os.path.exists(
|
|
304
|
+
os.path.join(abs_app_path, pkg_name, "__init__.py")
|
|
305
|
+
):
|
|
306
|
+
console.print(f"ā Package folder '{pkg_name}' is missing", style="bold red")
|
|
307
|
+
sys.exit(1)
|
|
308
|
+
|
|
309
|
+
if "entry-points" not in pyproject_content["project"]:
|
|
310
|
+
console.print(
|
|
311
|
+
"ā pyproject.toml is missing the entry-points section",
|
|
312
|
+
style="bold red",
|
|
313
|
+
)
|
|
314
|
+
sys.exit(1)
|
|
315
|
+
|
|
316
|
+
entry_points = pyproject_content["project"]["entry-points"]
|
|
317
|
+
|
|
318
|
+
if "reachy_mini_apps" not in entry_points:
|
|
319
|
+
console.print(
|
|
320
|
+
"ā pyproject.toml is missing the reachy_mini_apps entry-points section",
|
|
321
|
+
style="bold red",
|
|
322
|
+
)
|
|
323
|
+
sys.exit(1)
|
|
324
|
+
|
|
325
|
+
ep = entry_points["reachy_mini_apps"]
|
|
326
|
+
for k, v in ep.items():
|
|
327
|
+
console.print(f'Found entrypoint: {k} = "{v}"', style="dim")
|
|
328
|
+
if k == entrypoint_name and v == f"{pkg_name}.main:{class_name}":
|
|
329
|
+
console.print(
|
|
330
|
+
"ā
pyproject.toml contains the correct entrypoint for the app."
|
|
331
|
+
)
|
|
332
|
+
break
|
|
333
|
+
else:
|
|
334
|
+
console.print(
|
|
335
|
+
f'ā pyproject.toml is missing the entrypoint for the app: {entrypoint_name} = "{pkg_name}.main:{class_name}"',
|
|
336
|
+
style="bold red",
|
|
337
|
+
)
|
|
338
|
+
sys.exit(1)
|
|
339
|
+
|
|
340
|
+
# - <app_name>/__init__.py exists
|
|
341
|
+
pkg_path = Path(abs_app_path) / pkg_name
|
|
342
|
+
init_file = pkg_path / "__init__.py"
|
|
343
|
+
|
|
344
|
+
if not init_file.exists():
|
|
345
|
+
console.print("ā __init__.py is missing", style="bold red")
|
|
346
|
+
sys.exit(1)
|
|
347
|
+
|
|
348
|
+
console.print(f"ā
{app_name}/__init__.py exists.")
|
|
349
|
+
|
|
350
|
+
main_file = pkg_path / "main.py"
|
|
351
|
+
if not main_file.exists():
|
|
352
|
+
console.print("ā main.py is missing", style="bold red")
|
|
353
|
+
sys.exit(1)
|
|
354
|
+
console.print(f"ā
{app_name}/main.py exists.")
|
|
355
|
+
|
|
356
|
+
# - <app_name>/main.py contains a class named <AppName> that inherits from ReachyMiniApp
|
|
357
|
+
with open(main_file, "r") as f:
|
|
358
|
+
main_content = f.read()
|
|
359
|
+
class_name = "".join(
|
|
360
|
+
word.capitalize() for word in app_name.replace("-", "_").split("_")
|
|
361
|
+
)
|
|
362
|
+
if f"class {class_name}(ReachyMiniApp)" not in str(main_content):
|
|
363
|
+
console.print(
|
|
364
|
+
f"ā main.py is missing the class {class_name} that inherits from ReachyMiniApp",
|
|
365
|
+
style="bold red",
|
|
366
|
+
)
|
|
367
|
+
sys.exit(1)
|
|
368
|
+
console.print(
|
|
369
|
+
f"ā
main.py contains the class {class_name} that inherits from ReachyMiniApp."
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# - README.md exists in the root of the app
|
|
373
|
+
if not os.path.exists(os.path.join(abs_app_path, "README.md")):
|
|
374
|
+
console.print("ā README.md is missing", style="bold red")
|
|
375
|
+
sys.exit(1)
|
|
376
|
+
console.print("ā
README.md exists in the root of the app.")
|
|
377
|
+
|
|
378
|
+
def parse_readme(file_path: str) -> Any:
|
|
379
|
+
"""Parse the metadata section of the README.md file."""
|
|
380
|
+
with open(file_path, "r") as f:
|
|
381
|
+
lines = f.readlines()
|
|
382
|
+
|
|
383
|
+
in_metadata = False
|
|
384
|
+
metadata = ""
|
|
385
|
+
for line in lines:
|
|
386
|
+
line = line.strip()
|
|
387
|
+
if line == "---":
|
|
388
|
+
if not in_metadata:
|
|
389
|
+
in_metadata = True
|
|
390
|
+
else:
|
|
391
|
+
break
|
|
392
|
+
elif in_metadata:
|
|
393
|
+
metadata += line + "\n"
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
metadata = yaml.safe_load(metadata)
|
|
397
|
+
except yaml.YAMLError as e:
|
|
398
|
+
console.print(f"ā Error parsing YAML metadata: {e}", style="bold red")
|
|
399
|
+
sys.exit(1)
|
|
400
|
+
|
|
401
|
+
return metadata
|
|
402
|
+
|
|
403
|
+
# - README.md contains at least a title and the tags "reachy_mini" and "reachy_mini_{python/js}_app"
|
|
404
|
+
readme_metadata = parse_readme(os.path.join(abs_app_path, "README.md"))
|
|
405
|
+
if len(readme_metadata) == 0:
|
|
406
|
+
console.print("ā README.md is missing metadata section.", style="bold red")
|
|
407
|
+
sys.exit(1)
|
|
408
|
+
if "title" not in readme_metadata.keys():
|
|
409
|
+
console.print(
|
|
410
|
+
"ā README.md is missing the title key in metadata.", style="bold red"
|
|
411
|
+
)
|
|
412
|
+
sys.exit(1)
|
|
413
|
+
if readme_metadata["title"] == "":
|
|
414
|
+
console.print("ā README.md title cannot be empty.", style="bold red")
|
|
415
|
+
sys.exit(1)
|
|
416
|
+
|
|
417
|
+
if "tags" not in readme_metadata.keys():
|
|
418
|
+
console.print(
|
|
419
|
+
"ā README.md is missing the tags key in metadata.", style="bold red"
|
|
420
|
+
)
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
|
|
423
|
+
if "reachy_mini" not in readme_metadata["tags"]:
|
|
424
|
+
console.print(
|
|
425
|
+
'ā README.md must contain the "reachy_mini" tag', style="bold red"
|
|
426
|
+
)
|
|
427
|
+
sys.exit(1)
|
|
428
|
+
|
|
429
|
+
if (
|
|
430
|
+
"reachy_mini_python_app" not in readme_metadata["tags"]
|
|
431
|
+
and "reachy_mini_js_app" not in readme_metadata["tags"]
|
|
432
|
+
):
|
|
433
|
+
console.print(
|
|
434
|
+
'ā README.md must contain either the "reachy_mini_python_app" or "reachy_mini_js_app" tag',
|
|
435
|
+
style="bold red",
|
|
436
|
+
)
|
|
437
|
+
sys.exit(1)
|
|
438
|
+
|
|
439
|
+
console.print("ā
README.md contains the required metadata.")
|
|
440
|
+
# - <app_name>/main.py exists
|
|
441
|
+
|
|
442
|
+
# Now, create a temporary python venv in a temp dir, `pip install . the app, check that it works and that the entrypoint is registered
|
|
443
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
444
|
+
# change dir to tmpdir
|
|
445
|
+
os.chdir(tmpdir)
|
|
446
|
+
|
|
447
|
+
console.print(
|
|
448
|
+
f"\nCreating a temporary virtual environment to test the app... (tmp dir: {tmpdir})"
|
|
449
|
+
)
|
|
450
|
+
venv_path = os.path.join(tmpdir, "venv")
|
|
451
|
+
subprocess.run([sys.executable, "-m", "venv", venv_path], check=True)
|
|
452
|
+
|
|
453
|
+
pip_executable = os.path.join(
|
|
454
|
+
venv_path,
|
|
455
|
+
"Scripts" if os.name == "nt" else "bin",
|
|
456
|
+
"pip",
|
|
457
|
+
)
|
|
458
|
+
python_executable = os.path.join(
|
|
459
|
+
venv_path,
|
|
460
|
+
"Scripts" if os.name == "nt" else "bin",
|
|
461
|
+
"python",
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
install_app_with_progress(console, pip_executable, abs_app_path)
|
|
465
|
+
|
|
466
|
+
console.print("Checking that the app entrypoint is registered...")
|
|
467
|
+
|
|
468
|
+
# use from importlib.metadata import entry_points
|
|
469
|
+
|
|
470
|
+
check_script = (
|
|
471
|
+
f"from importlib.metadata import entry_points; "
|
|
472
|
+
f"eps = entry_points(group='reachy_mini_apps'); "
|
|
473
|
+
f"app_names = [ep.name for ep in eps]; "
|
|
474
|
+
f"import sys; "
|
|
475
|
+
f"sys.exit(0) if '{app_name}' in app_names else sys.exit(1)"
|
|
476
|
+
)
|
|
477
|
+
if (
|
|
478
|
+
subprocess.run(
|
|
479
|
+
[python_executable, "-c", check_script],
|
|
480
|
+
# capture_output=True,
|
|
481
|
+
text=True,
|
|
482
|
+
).returncode
|
|
483
|
+
!= 0
|
|
484
|
+
):
|
|
485
|
+
console.print(
|
|
486
|
+
f"ā App '{app_name}' entrypoint is not registered correctly.",
|
|
487
|
+
style="bold red",
|
|
488
|
+
)
|
|
489
|
+
sys.exit(1)
|
|
490
|
+
console.print("ā
App entrypoint is registered correctly.")
|
|
491
|
+
|
|
492
|
+
# Now try to uninstall the app and check that it uninstalls correctly
|
|
493
|
+
console.print("Uninstalling the app from the temporary virtual environment...")
|
|
494
|
+
subprocess.run(
|
|
495
|
+
[pip_executable, "uninstall", "-y", app_name],
|
|
496
|
+
check=True,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
if (
|
|
500
|
+
subprocess.run(
|
|
501
|
+
[python_executable, "-c", check_script],
|
|
502
|
+
capture_output=True,
|
|
503
|
+
text=True,
|
|
504
|
+
).returncode
|
|
505
|
+
== 0
|
|
506
|
+
):
|
|
507
|
+
console.print(
|
|
508
|
+
f"ā App '{app_name}' was not uninstalled correctly.",
|
|
509
|
+
style="bold red",
|
|
510
|
+
)
|
|
511
|
+
sys.exit(1)
|
|
512
|
+
|
|
513
|
+
console.print("ā
App installation and uninstallation tests passed.")
|
|
514
|
+
|
|
515
|
+
console.print(f"\nā
App '{app_name}' passed all checks!", style="bold green")
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def request_app_addition(new_app_repo_id: str) -> bool:
|
|
519
|
+
"""Request to add the new app to the official Reachy Mini app store."""
|
|
520
|
+
api = HfApi()
|
|
521
|
+
|
|
522
|
+
repo_id = "pollen-robotics/reachy-mini-official-app-store"
|
|
523
|
+
file_path = "app-list.json"
|
|
524
|
+
|
|
525
|
+
# 0. Detect current HF user
|
|
526
|
+
user = whoami()["name"]
|
|
527
|
+
|
|
528
|
+
# 1. Check if there is already an open PR by this user for this app
|
|
529
|
+
# (we used commit_message=f"Add {new_app_repo_id} to app-list.json",
|
|
530
|
+
# which becomes the PR title)
|
|
531
|
+
existing_prs = get_repo_discussions(
|
|
532
|
+
repo_id=repo_id,
|
|
533
|
+
repo_type="dataset",
|
|
534
|
+
author=user,
|
|
535
|
+
discussion_type="pull_request",
|
|
536
|
+
discussion_status="open",
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
for pr in existing_prs:
|
|
540
|
+
if new_app_repo_id in pr.title:
|
|
541
|
+
print(
|
|
542
|
+
f"An open PR already exists for {new_app_repo_id} by {user}: "
|
|
543
|
+
f"https://huggingface.co/{repo_id}/discussions/{pr.num}"
|
|
544
|
+
)
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
# 2. Download current file from the dataset repo
|
|
548
|
+
local_downloaded = api.hf_hub_download(
|
|
549
|
+
repo_id=repo_id,
|
|
550
|
+
filename=file_path,
|
|
551
|
+
repo_type="dataset",
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
with open(local_downloaded, "r") as f:
|
|
555
|
+
app_list = json.load(f)
|
|
556
|
+
|
|
557
|
+
# 3. Modify JSON (append if not already present)
|
|
558
|
+
if new_app_repo_id not in app_list:
|
|
559
|
+
app_list.append(new_app_repo_id)
|
|
560
|
+
else:
|
|
561
|
+
print(f"{new_app_repo_id} is already in the app list.")
|
|
562
|
+
# You might still want to continue and create the PR, or early-return here.
|
|
563
|
+
return False
|
|
564
|
+
|
|
565
|
+
# 4. Save updated JSON to a temporary path
|
|
566
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
567
|
+
updated_path = os.path.join(tmpdir, file_path)
|
|
568
|
+
os.makedirs(os.path.dirname(updated_path), exist_ok=True)
|
|
569
|
+
with open(updated_path, "w") as f:
|
|
570
|
+
json.dump(app_list, f, indent=4)
|
|
571
|
+
f.write("\n")
|
|
572
|
+
|
|
573
|
+
# 5. Commit with create_pr=True
|
|
574
|
+
commit_info = api.create_commit(
|
|
575
|
+
repo_id=repo_id,
|
|
576
|
+
repo_type="dataset",
|
|
577
|
+
commit_message=f"Add {new_app_repo_id} to app-list.json",
|
|
578
|
+
commit_description=(
|
|
579
|
+
f"Append `{new_app_repo_id}` to the list of Reachy Mini apps."
|
|
580
|
+
),
|
|
581
|
+
operations=[
|
|
582
|
+
CommitOperationAdd(
|
|
583
|
+
path_in_repo=file_path,
|
|
584
|
+
path_or_fileobj=updated_path,
|
|
585
|
+
)
|
|
586
|
+
],
|
|
587
|
+
create_pr=True,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
print("Commit URL:", commit_info.commit_url)
|
|
591
|
+
print("PR URL:", commit_info.pr_url) # None if no PR was opened
|
|
592
|
+
return True
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def try_to_push(console: Console, _app_path: Path) -> bool:
|
|
596
|
+
"""Try to push changes to the remote repository."""
|
|
597
|
+
console.print("Pushing changes to the remote repository ...", style="bold blue")
|
|
598
|
+
push_result = subprocess.run(
|
|
599
|
+
f"cd {_app_path} && git push",
|
|
600
|
+
shell=True,
|
|
601
|
+
capture_output=True,
|
|
602
|
+
text=True,
|
|
603
|
+
)
|
|
604
|
+
if push_result.returncode != 0:
|
|
605
|
+
console.print(
|
|
606
|
+
f"[red]Failed to push changes to the remote repository: {push_result.stderr}[/red]"
|
|
607
|
+
)
|
|
608
|
+
return False
|
|
609
|
+
return True
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def publish(
|
|
613
|
+
console: Console,
|
|
614
|
+
app_path: str,
|
|
615
|
+
commit_message: str,
|
|
616
|
+
official: bool = False,
|
|
617
|
+
no_check: bool = False,
|
|
618
|
+
) -> None:
|
|
619
|
+
"""Publish the app to the Reachy Mini app store.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
console (Console): The console object for printing messages.
|
|
623
|
+
app_path (str): Local path to the app to publish.
|
|
624
|
+
commit_message (str): Commit message for the app publish.
|
|
625
|
+
official (bool): Request to publish the app as an official Reachy Mini app.
|
|
626
|
+
no_check (bool): Don't run checks before publishing the app.
|
|
627
|
+
|
|
628
|
+
"""
|
|
629
|
+
import huggingface_hub as hf
|
|
630
|
+
|
|
631
|
+
if app_path is None:
|
|
632
|
+
console.print("\n$ What is the local path to the app you want to publish?")
|
|
633
|
+
app_path = questionary.path(
|
|
634
|
+
">",
|
|
635
|
+
default="",
|
|
636
|
+
validate=validate_location,
|
|
637
|
+
).ask()
|
|
638
|
+
if app_path is None:
|
|
639
|
+
console.print("[red]Aborted.[/red]")
|
|
640
|
+
exit()
|
|
641
|
+
name_of_repo = Path(app_path).name
|
|
642
|
+
if name_of_repo == "reachy_mini":
|
|
643
|
+
console.print(
|
|
644
|
+
"[red] Safeguard : You may have selected reachy_mini repo as your app. Aborted.[/red]"
|
|
645
|
+
)
|
|
646
|
+
exit()
|
|
647
|
+
app_path = Path(app_path).expanduser().resolve() # type: ignore
|
|
648
|
+
if not os.path.exists(app_path):
|
|
649
|
+
console.print(f"[red]App path {app_path} does not exist.[/red]")
|
|
650
|
+
sys.exit()
|
|
651
|
+
|
|
652
|
+
if not hf.get_token():
|
|
653
|
+
console.print(
|
|
654
|
+
"[red]You need to be logged in to Hugging Face to publish an app.[/red]"
|
|
655
|
+
)
|
|
656
|
+
# Do you want to login now (will run hf auth login)
|
|
657
|
+
if questionary.confirm("Do you want to login now?").ask():
|
|
658
|
+
console.print("Generate a token at https://huggingface.co/settings/tokens")
|
|
659
|
+
hf.login()
|
|
660
|
+
else:
|
|
661
|
+
console.print("[red]Aborted.[/red]")
|
|
662
|
+
exit()
|
|
663
|
+
|
|
664
|
+
username = hf.whoami()["name"]
|
|
665
|
+
repo_path = f"{username}/{Path(app_path).name}"
|
|
666
|
+
repo_url = f"https://huggingface.co/spaces/{repo_path}"
|
|
667
|
+
|
|
668
|
+
if hf.repo_exists(repo_path, repo_type="space"):
|
|
669
|
+
if official:
|
|
670
|
+
# ask for confirmation
|
|
671
|
+
if not questionary.confirm(
|
|
672
|
+
"Are you sure you want to ask to publish this app as an official Reachy Mini app?"
|
|
673
|
+
).ask():
|
|
674
|
+
console.print("[red]Aborted.[/red]")
|
|
675
|
+
exit()
|
|
676
|
+
|
|
677
|
+
worked = request_app_addition(repo_path)
|
|
678
|
+
if worked:
|
|
679
|
+
console.print(
|
|
680
|
+
"\nYou have requested to publish your app as an official Reachy Mini app."
|
|
681
|
+
)
|
|
682
|
+
console.print(
|
|
683
|
+
"The Pollen and Hugging Face teams will review your app. Thank you for your contribution!"
|
|
684
|
+
)
|
|
685
|
+
exit()
|
|
686
|
+
|
|
687
|
+
console.print("App already exists on Hugging Face Spaces.", style="bold blue")
|
|
688
|
+
os.system(f"cd {app_path} && git pull {repo_url} main")
|
|
689
|
+
|
|
690
|
+
status_output = (
|
|
691
|
+
subprocess.check_output(
|
|
692
|
+
f"cd {app_path} && git status --porcelain", shell=True
|
|
693
|
+
)
|
|
694
|
+
.decode("utf-8")
|
|
695
|
+
.strip()
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
if status_output == "":
|
|
699
|
+
console.print(
|
|
700
|
+
"ā
No changes to commit.",
|
|
701
|
+
style="bold green",
|
|
702
|
+
)
|
|
703
|
+
push_anyway = questionary.confirm(
|
|
704
|
+
"Do you want to try to push anyway?"
|
|
705
|
+
).ask()
|
|
706
|
+
if not push_anyway:
|
|
707
|
+
console.print("[red]Aborted.[/red]")
|
|
708
|
+
exit()
|
|
709
|
+
else:
|
|
710
|
+
console.print("Trying to push anyway...")
|
|
711
|
+
pushed = try_to_push(console, Path(app_path))
|
|
712
|
+
exit()
|
|
713
|
+
|
|
714
|
+
if no_check:
|
|
715
|
+
console.print(
|
|
716
|
+
"ā ļø Skipping checks as per --nocheck flag.",
|
|
717
|
+
style="bold yellow",
|
|
718
|
+
)
|
|
719
|
+
else:
|
|
720
|
+
console.print(f"\nš Running checks on the app at {app_path}/...")
|
|
721
|
+
check(console, str(app_path))
|
|
722
|
+
|
|
723
|
+
commit_message = questionary.text(
|
|
724
|
+
"\n$ Enter a commit message for the update:",
|
|
725
|
+
default="Update app",
|
|
726
|
+
).ask()
|
|
727
|
+
if commit_message is None:
|
|
728
|
+
console.print("[red]Aborted.[/red]")
|
|
729
|
+
exit()
|
|
730
|
+
|
|
731
|
+
# commit local changes
|
|
732
|
+
console.print("Committing changes locally ...", style="bold blue")
|
|
733
|
+
os.system(f"cd {app_path} && git add . && git commit -m '{commit_message}'")
|
|
734
|
+
|
|
735
|
+
# && git push HEAD:main"
|
|
736
|
+
|
|
737
|
+
pushed = try_to_push(console, Path(app_path))
|
|
738
|
+
if not pushed:
|
|
739
|
+
exit()
|
|
740
|
+
|
|
741
|
+
console.print("ā
App updated successfully.")
|
|
742
|
+
else:
|
|
743
|
+
if no_check:
|
|
744
|
+
console.print(
|
|
745
|
+
"ā ļø Skipping checks as per --nocheck flag.",
|
|
746
|
+
style="bold yellow",
|
|
747
|
+
)
|
|
748
|
+
else:
|
|
749
|
+
console.print(f"\nš Running checks on the app at {app_path}/...")
|
|
750
|
+
check(console, str(app_path))
|
|
751
|
+
|
|
752
|
+
console.print("Do you want your space to be created private or public?")
|
|
753
|
+
privacy = questionary.select(
|
|
754
|
+
">",
|
|
755
|
+
choices=["private", "public"],
|
|
756
|
+
default="public",
|
|
757
|
+
).ask()
|
|
758
|
+
|
|
759
|
+
hf.create_repo(
|
|
760
|
+
repo_path,
|
|
761
|
+
repo_type="space",
|
|
762
|
+
private=(privacy == "private"),
|
|
763
|
+
exist_ok=False,
|
|
764
|
+
space_sdk="static",
|
|
765
|
+
)
|
|
766
|
+
os.system(
|
|
767
|
+
f"cd {app_path} && git init && git remote add space {repo_url} && git add . && git commit -m 'Initial commit' && git push --set-upstream -f space HEAD:main"
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
console.print("ā
App published successfully.", style="bold green")
|
|
771
|
+
|
|
772
|
+
if official:
|
|
773
|
+
# ask for confirmation
|
|
774
|
+
if not questionary.confirm(
|
|
775
|
+
"Are you sure you want to ask to publish this app as an official Reachy Mini app?"
|
|
776
|
+
).ask():
|
|
777
|
+
console.print("[red]Aborted.[/red]")
|
|
778
|
+
exit()
|
|
779
|
+
|
|
780
|
+
worked = request_app_addition(repo_path)
|
|
781
|
+
if worked:
|
|
782
|
+
console.print(
|
|
783
|
+
"\nYou have requested to publish your app as an official Reachy Mini app."
|
|
784
|
+
)
|
|
785
|
+
console.print(
|
|
786
|
+
"The Pollen and Hugging Face teams will review your app. Thank you for your contribution!"
|
|
787
|
+
)
|