sima-cli 2.1.15__tar.gz → 2.1.17__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 (199) hide show
  1. {sima_cli-2.1.15/sima_cli.egg-info → sima_cli-2.1.17}/PKG-INFO +4 -1
  2. {sima_cli-2.1.15 → sima_cli-2.1.17}/README.md +2 -0
  3. {sima_cli-2.1.15 → sima_cli-2.1.17}/pyproject.toml +2 -1
  4. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/__version__.py +1 -1
  5. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/cli.py +267 -52
  6. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/discover/discover.py +1 -1
  7. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/download/downloader.py +2 -0
  8. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/metadata_installer.py +84 -18
  9. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/model_zoo/model.py +1 -0
  10. sima_cli-2.1.17/sima_cli/models/__init__.py +3 -0
  11. sima_cli-2.1.17/sima_cli/models/client.py +372 -0
  12. sima_cli-2.1.17/sima_cli/models/commands.py +223 -0
  13. sima_cli-2.1.17/sima_cli/models/rendering.py +473 -0
  14. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/playbooks/manager.py +9 -4
  15. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/commands.py +98 -26
  16. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/config.py +5 -0
  17. sima_cli-2.1.17/sima_cli/sdk/docker_staging.py +22 -0
  18. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/install.py +162 -29
  19. sima_cli-2.1.17/sima_cli/sdk/model_compiler.py +140 -0
  20. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/neat.py +56 -8
  21. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/utils.py +504 -142
  22. sima_cli-2.1.17/sima_cli/sdk/vscode_extensions.py +197 -0
  23. sima_cli-2.1.17/sima_cli/update/ab_state.py +91 -0
  24. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/bootimg.py +7 -2
  25. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/elxr.py +97 -33
  26. sima_cli-2.1.17/sima_cli/update/emmc.py +68 -0
  27. sima_cli-2.1.17/sima_cli/update/fast_upload.py +79 -0
  28. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/netboot.py +191 -43
  29. sima_cli-2.1.17/sima_cli/update/netboot_artifacts.py +105 -0
  30. sima_cli-2.1.17/sima_cli/update/netboot_device.py +177 -0
  31. sima_cli-2.1.17/sima_cli/update/query.py +248 -0
  32. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/remote.py +52 -32
  33. sima_cli-2.1.17/sima_cli/update/rootfs.py +96 -0
  34. sima_cli-2.1.17/sima_cli/update/staging.py +76 -0
  35. sima_cli-2.1.17/sima_cli/update/swu.py +462 -0
  36. sima_cli-2.1.17/sima_cli/update/swu_artifacts.py +286 -0
  37. sima_cli-2.1.17/sima_cli/update/swu_certificate.py +57 -0
  38. sima_cli-2.1.17/sima_cli/update/swu_target.py +85 -0
  39. sima_cli-2.1.17/sima_cli/update/uboot_environment.py +78 -0
  40. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/updater.py +115 -22
  41. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/container_registries.py +249 -0
  42. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/docker.py +1 -1
  43. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/pcie.py +126 -29
  44. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/pkg_update_check.py +3 -1
  45. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/vulcan/artifacts.py +56 -5
  46. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/vulcan/commands.py +50 -5
  47. {sima_cli-2.1.15 → sima_cli-2.1.17/sima_cli.egg-info}/PKG-INFO +4 -1
  48. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli.egg-info/SOURCES.txt +46 -0
  49. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli.egg-info/requires.txt +1 -0
  50. sima_cli-2.1.17/tests/unit/test_ab_overlay.py +125 -0
  51. sima_cli-2.1.17/tests/unit/test_bootimg_defaults.py +37 -0
  52. sima_cli-2.1.17/tests/unit/test_bootimg_recovery.py +118 -0
  53. sima_cli-2.1.17/tests/unit/test_cli_update.py +76 -0
  54. sima_cli-2.1.17/tests/unit/test_cli_update_rerun.py +44 -0
  55. sima_cli-2.1.17/tests/unit/test_container_registries.py +220 -0
  56. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_docker_utils.py +23 -0
  57. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_elxr_update.py +110 -1
  58. sima_cli-2.1.17/tests/unit/test_emmc.py +92 -0
  59. sima_cli-2.1.17/tests/unit/test_fast_upload.py +49 -0
  60. sima_cli-2.1.17/tests/unit/test_firmware.py +141 -0
  61. sima_cli-2.1.17/tests/unit/test_firmware_creation_order.py +65 -0
  62. sima_cli-2.1.17/tests/unit/test_firmware_layout.py +66 -0
  63. sima_cli-2.1.17/tests/unit/test_firmware_selector.py +64 -0
  64. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_install_stub.py +69 -4
  65. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_metadata_installer.py +191 -13
  66. sima_cli-2.1.17/tests/unit/test_model_zoo.py +48 -0
  67. sima_cli-2.1.17/tests/unit/test_models.py +632 -0
  68. sima_cli-2.1.17/tests/unit/test_modelzoo_commands.py +11 -0
  69. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_netboot.py +8 -1
  70. sima_cli-2.1.17/tests/unit/test_netboot_autoflash.py +53 -0
  71. sima_cli-2.1.17/tests/unit/test_netboot_device.py +292 -0
  72. sima_cli-2.1.17/tests/unit/test_netboot_ip_recovery.py +129 -0
  73. sima_cli-2.1.17/tests/unit/test_netboot_mirror.py +186 -0
  74. sima_cli-2.1.17/tests/unit/test_netboot_trootctl.py +95 -0
  75. sima_cli-2.1.17/tests/unit/test_palette_image_discovery.py +78 -0
  76. sima_cli-2.1.17/tests/unit/test_pcie.py +171 -0
  77. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_pkg_update_check.py +25 -0
  78. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_sdk_image_detection.py +1065 -81
  79. sima_cli-2.1.17/tests/unit/test_sdk_model_compiler.py +282 -0
  80. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_skills_manager.py +30 -0
  81. sima_cli-2.1.17/tests/unit/test_swu.py +573 -0
  82. sima_cli-2.1.17/tests/unit/test_swu_certificate.py +208 -0
  83. sima_cli-2.1.17/tests/unit/test_swu_errors.py +162 -0
  84. sima_cli-2.1.17/tests/unit/test_swu_mirror.py +331 -0
  85. sima_cli-2.1.17/tests/unit/test_swu_target.py +58 -0
  86. sima_cli-2.1.17/tests/unit/test_uboot_environment.py +67 -0
  87. sima_cli-2.1.17/tests/unit/test_update_staging.py +111 -0
  88. sima_cli-2.1.17/tests/unit/test_vscode_extension_pins.py +362 -0
  89. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_vulcan.py +226 -13
  90. sima_cli-2.1.15/sima_cli/update/query.py +0 -135
  91. sima_cli-2.1.15/tests/unit/test_cli_update.py +0 -25
  92. sima_cli-2.1.15/tests/unit/test_cli_update_rerun.py +0 -20
  93. sima_cli-2.1.15/tests/unit/test_firmware.py +0 -60
  94. sima_cli-2.1.15/tests/unit/test_model_zoo.py +0 -0
  95. {sima_cli-2.1.15 → sima_cli-2.1.17}/LICENSE +0 -0
  96. {sima_cli-2.1.15 → sima_cli-2.1.17}/MANIFEST.in +0 -0
  97. {sima_cli-2.1.15 → sima_cli-2.1.17}/requirements.txt +0 -0
  98. {sima_cli-2.1.15 → sima_cli-2.1.17}/setup.cfg +0 -0
  99. {sima_cli-2.1.15 → sima_cli-2.1.17}/setup.py +0 -0
  100. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/__init__.py +0 -0
  101. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/__main__.py +0 -0
  102. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/app_zoo/__init__.py +0 -0
  103. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/app_zoo/app.py +0 -0
  104. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/app_zoo/commands.py +0 -0
  105. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/auth/__init__.py +0 -0
  106. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/auth/auth0.py +0 -0
  107. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/auth/devportal.py +0 -0
  108. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/auth/login.py +0 -0
  109. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/data/__init__.py +0 -0
  110. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/data/resources_internal.yaml +0 -0
  111. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/data/resources_public.yaml +0 -0
  112. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/deploy_only/__init__.py +0 -0
  113. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/deploy_only/device/__init__.py +0 -0
  114. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/deploy_only/device/commands.py +0 -0
  115. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/deploy_only/mpk/__init__.py +0 -0
  116. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/deploy_only/mpk/commands.py +0 -0
  117. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/discover/__init__.py +0 -0
  118. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/discover/linuxll.py +0 -0
  119. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/download/__init__.py +0 -0
  120. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/__init__.py +0 -0
  121. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/compatibility.py +0 -0
  122. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/github_assets.py +0 -0
  123. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/hostdriver.py +0 -0
  124. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/metadata_info.py +0 -0
  125. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/metadata_validator.py +0 -0
  126. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/optiview.py +0 -0
  127. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/package_builder.py +0 -0
  128. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/palette.py +0 -0
  129. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/install/registry.py +0 -0
  130. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/mla/__init__.py +0 -0
  131. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/mla/meminfo.py +0 -0
  132. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/model_zoo/__init__.py +0 -0
  133. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/network/__init__.py +0 -0
  134. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/network/network.py +0 -0
  135. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/playbooks/__init__.py +0 -0
  136. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/playbooks/commands.py +0 -0
  137. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/__init__.py +0 -0
  138. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/cmdexec.py +0 -0
  139. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/linux_devkit_network.py +0 -0
  140. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/linux_shared_network.py +0 -0
  141. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/network_doctor.py +0 -0
  142. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/preinstall.py +0 -0
  143. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/requirements.json +0 -0
  144. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/script.py +0 -0
  145. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/stop.py +0 -0
  146. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/sdk/uninstall.py +0 -0
  147. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/serial/__init__.py +0 -0
  148. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/serial/serial.py +0 -0
  149. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/storage/__init__.py +0 -0
  150. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/storage/nvme.py +0 -0
  151. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/storage/sdcard.py +0 -0
  152. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/__init__.py +0 -0
  153. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/bmaptool.py +0 -0
  154. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/cleanlog.py +0 -0
  155. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/update/local.py +0 -0
  156. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/upgrade/__init__.py +0 -0
  157. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/upgrade/selfupdate.py +0 -0
  158. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/__init__.py +0 -0
  159. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/api_common.py +0 -0
  160. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/artifactory.py +0 -0
  161. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/common.py +0 -0
  162. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/config.py +0 -0
  163. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/config_loader.py +0 -0
  164. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/deprecation.py +0 -0
  165. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/device_api.py +0 -0
  166. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/disk.py +0 -0
  167. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/env.py +0 -0
  168. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/errors.py +0 -0
  169. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/mpk_api.py +0 -0
  170. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/net.py +0 -0
  171. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/network.py +0 -0
  172. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/serializers.py +0 -0
  173. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/services.py +0 -0
  174. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/utils/tag.py +0 -0
  175. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli/vulcan/__init__.py +0 -0
  176. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli.egg-info/dependency_links.txt +0 -0
  177. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli.egg-info/entry_points.txt +0 -0
  178. {sima_cli-2.1.15 → sima_cli-2.1.17}/sima_cli.egg-info/top_level.txt +0 -0
  179. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/__init__.py +0 -0
  180. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/e2e/__init__.py +0 -0
  181. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/__init__.py +0 -0
  182. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_app_zoo.py +0 -0
  183. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_appzoo_commands.py +0 -0
  184. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_auth.py +0 -0
  185. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_auth0.py +0 -0
  186. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_cli.py +0 -0
  187. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_cli_stdio.py +0 -0
  188. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_deprecation.py +0 -0
  189. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_download.py +0 -0
  190. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_generate_cli_markdown_docs.py +0 -0
  191. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_metadata_validator.py +0 -0
  192. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_package_builder.py +0 -0
  193. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_sdk_linux_shared_network.py +0 -0
  194. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_sdk_network_doctor.py +0 -0
  195. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_sdk_preinstall.py +0 -0
  196. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_sdk_uninstall.py +0 -0
  197. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_selfupdate.py +0 -0
  198. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_skills_commands.py +0 -0
  199. {sima_cli-2.1.15 → sima_cli-2.1.17}/tests/unit/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sima-cli
3
- Version: 2.1.15
3
+ Version: 2.1.17
4
4
  Summary: CLI tool for SiMa Developer Portal to download models, firmware, and apps.
5
5
  Home-page: https://community.sima.ai/
6
6
  Author: SiMa.ai
@@ -21,6 +21,7 @@ Requires-Dist: requests<3.0,>=2.32.4
21
21
  Requires-Dist: tqdm>=4.64
22
22
  Requires-Dist: pyyaml>=6.0
23
23
  Requires-Dist: paramiko>=3.5.1
24
+ Requires-Dist: cryptography>=3.3
24
25
  Requires-Dist: plotext==5.3.2
25
26
  Requires-Dist: rich==14.0.0
26
27
  Requires-Dist: InquirerPy==0.3.4
@@ -34,6 +35,8 @@ Dynamic: requires-python
34
35
 
35
36
  # sima-cli - SiMa Developer Portal CLI Tool
36
37
 
38
+ **Documentation:** [English](docs/sima-cli/index.md) | [한국어](docs/i18n/ko/sima-cli/index.md) | [日本語](docs/i18n/ja/sima-cli/index.md) | [繁體中文](docs/i18n/zh-Hant/sima-cli/index.md) | [Українська](docs/i18n/uk/sima-cli/index.md)
39
+
37
40
  [![Python 3.8](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.8&label=python%203.8)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
38
41
  [![Python 3.9](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.9&label=python%203.9)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
39
42
  [![Python 3.10](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.10&label=python%203.10)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
@@ -1,5 +1,7 @@
1
1
  # sima-cli - SiMa Developer Portal CLI Tool
2
2
 
3
+ **Documentation:** [English](docs/sima-cli/index.md) | [한국어](docs/i18n/ko/sima-cli/index.md) | [日本語](docs/i18n/ja/sima-cli/index.md) | [繁體中文](docs/i18n/zh-Hant/sima-cli/index.md) | [Українська](docs/i18n/uk/sima-cli/index.md)
4
+
3
5
  [![Python 3.8](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.8&label=python%203.8)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
4
6
  [![Python 3.9](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.9&label=python%203.9)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
5
7
  [![Python 3.10](https://img.shields.io/github/actions/workflow/status/sima-neat/sima-cli/vulcan-ci.yml?branch=main&job=Compatibility%20Python%203.10&label=python%203.10)](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sima-cli"
7
- version = "2.1.15"
7
+ version = "2.1.17"
8
8
  description = "CLI tool for SiMa Developer Portal to download models, firmware, and apps."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -27,6 +27,7 @@ dependencies = [
27
27
  "tqdm>=4.64",
28
28
  "pyyaml>=6.0",
29
29
  "paramiko>=3.5.1",
30
+ "cryptography>=3.3",
30
31
  "plotext==5.3.2",
31
32
  "rich==14.0.0",
32
33
  "InquirerPy==0.3.4",
@@ -1,2 +1,2 @@
1
1
  # sima_cli/__version__.py
2
- __version__ = "2.1.15"
2
+ __version__ = "2.1.17"
@@ -1,9 +1,14 @@
1
1
  import os
2
2
  import shutil
3
3
  import sys
4
+ from contextlib import nullcontext, redirect_stdout
4
5
  import click
6
+ from rich.console import Console
7
+ from rich.panel import Panel
8
+ from rich.text import Text
5
9
  from sima_cli.utils.env import get_environment_type
6
10
  from sima_cli.update.updater import perform_update
11
+ from sima_cli.update.swu import handle_update
7
12
  from sima_cli.model_zoo.model import list_models, download_model, describe_model
8
13
  from sima_cli.app_zoo.app import list_apps, download_app, describe_app
9
14
  from sima_cli.utils.config_loader import internal_resource_exists
@@ -12,7 +17,11 @@ from sima_cli.__version__ import __version__
12
17
  from sima_cli.utils.config import CONFIG_PATH
13
18
  from sima_cli.install.optiview import install_optiview
14
19
  from sima_cli.install.hostdriver import install_hostdriver
15
- from sima_cli.install.metadata_installer import install_from_metadata, metadata_resolver
20
+ from sima_cli.install.metadata_installer import (
21
+ MetadataAccessForbidden,
22
+ install_from_metadata,
23
+ metadata_resolver,
24
+ )
16
25
  from sima_cli.serial.serial import connect_serial
17
26
  from sima_cli.storage.nvme import nvme_format, nvme_remount
18
27
  from sima_cli.storage.sdcard import sdcard_format
@@ -30,6 +39,7 @@ from sima_cli.install.registry import register_packages_commands
30
39
  from sima_cli.upgrade.selfupdate import register_selfupdate_command
31
40
  from sima_cli.playbooks import register_playbook_commands
32
41
  from sima_cli.vulcan import register_vulcan_commands
42
+ from sima_cli.models import register_models_commands
33
43
  from sima_cli.vulcan.commands import (
34
44
  ENV_METAVAR,
35
45
  _environment_shortcut_options,
@@ -71,6 +81,19 @@ def _should_rerun_after_update(argv):
71
81
  return command_name not in (None, "selfupdate", "version")
72
82
 
73
83
 
84
+ def _allows_external_prerelease_fallback(argv):
85
+ """Return whether this invocation explicitly allows update mirror fallback."""
86
+ args = argv[1:]
87
+ return "update" in args and any(arg in ("-f", "--force") for arg in args)
88
+
89
+
90
+ def _update_auto_confirm_requested(argv):
91
+ """Return whether an update invocation requests non-interactive confirmation."""
92
+ return _command_name_from_argv(argv) == "update" and any(
93
+ arg in ("-y", "--yes") for arg in argv[1:]
94
+ )
95
+
96
+
74
97
  def _rerun_current_command() -> None:
75
98
  env = os.environ.copy()
76
99
  env["SIMA_CLI_CHECK_FOR_UPDATE"] = "0"
@@ -79,21 +102,21 @@ def _rerun_current_command() -> None:
79
102
  os.execvpe(sys.executable, cmd, env)
80
103
 
81
104
 
82
- # Entry point for the CLI tool using Click's command group decorator
83
- @click.group(context_settings=dict(help_option_names=["-h", "--help", "-?"], max_content_width=120))
84
- @click.option('-i', '--internal', is_flag=True, help="Use internal Artifactory resources, Authorized Sima employees only")
85
- @click.version_option(version=f"{__version__}", message="SiMa CLI version: %(version)s")
86
- @click.pass_context
87
- def main(ctx, internal):
88
- """
89
- sima-cli – SiMa Developer Portal CLI Tool
90
-
91
- Global Options:
92
- --internal Use internal Artifactory resources (can also be set via env variable SIMA_CLI_INTERNAL=1)
93
- """
105
+ def _initialize_main_context(ctx, internal, yes):
94
106
  _configure_stdio_errors()
95
- if check_for_update('sima-cli') and _should_rerun_after_update(sys.argv):
96
- _rerun_current_command()
107
+ auto_accept_update = yes or _update_auto_confirm_requested(sys.argv)
108
+ previous_auto_accept = os.environ.get("SIMA_CLI_AUTO_ACCEPT_UPDATE")
109
+ if auto_accept_update:
110
+ os.environ["SIMA_CLI_AUTO_ACCEPT_UPDATE"] = "1"
111
+ try:
112
+ if check_for_update('sima-cli') and _should_rerun_after_update(sys.argv):
113
+ _rerun_current_command()
114
+ finally:
115
+ if auto_accept_update:
116
+ if previous_auto_accept is None:
117
+ os.environ.pop("SIMA_CLI_AUTO_ACCEPT_UPDATE", None)
118
+ else:
119
+ os.environ["SIMA_CLI_AUTO_ACCEPT_UPDATE"] = previous_auto_accept
97
120
  ctx.ensure_object(dict)
98
121
 
99
122
  os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
@@ -102,18 +125,30 @@ def main(ctx, internal):
102
125
  if not internal:
103
126
  internal = os.getenv("SIMA_CLI_INTERNAL", "0") in ("1", "true", "yes")
104
127
 
105
- if internal and not internal_resource_exists():
106
- click.echo("❌ You have specified -i or --internal argument to access internal resources, but you do not have an internal resource map configured.")
128
+ defer_artifactory_access = ctx.invoked_subcommand == "update" or ctx.meta.get("daily_netboot", False)
129
+ if internal and not internal_resource_exists() and not defer_artifactory_access:
130
+ click.echo("❌ You have specified -i or --internal argument to access internal resources, but you do not have an internal resource map configured.")
107
131
  click.echo("Refer to the confluence page to find out how to configure internal resource map.")
108
- exit(0)
132
+ exit(0)
133
+
134
+ internal_reachable = True
135
+ if internal:
136
+ internal_reachable = check_artifactory_reachability()
109
137
 
110
- if internal and not check_artifactory_reachability():
138
+ if internal and not internal_reachable and not defer_artifactory_access and not _allows_external_prerelease_fallback(sys.argv):
111
139
  click.secho("❌ You have specified -i or --internal argument to access internal resources, but you can't connect to Artifactory.", fg='red')
112
140
  click.secho("Please make sure you are connected to VPN or are on the corporate network.", fg='red')
113
141
  exit(0)
114
-
142
+
143
+ if internal and not internal_reachable and not defer_artifactory_access:
144
+ click.secho(
145
+ "⚠️ Internal resources are unreachable. --force allows this update to use the external pre-release mirror.",
146
+ fg="yellow",
147
+ )
115
148
 
116
149
  ctx.obj["internal"] = internal
150
+ ctx.obj["internal_reachable"] = internal_reachable
151
+ ctx.obj["yes"] = yes
117
152
 
118
153
  env_type, env_subtype = get_environment_type()
119
154
 
@@ -123,12 +158,61 @@ def main(ctx, internal):
123
158
  click.echo(f"🔧 Environment: {env_type} ({env_subtype})")
124
159
 
125
160
 
161
+ class InspectionAwareGroup(click.Group):
162
+ def parse_args(self, ctx, args):
163
+ # Capture the actual Click arguments (also works with CliRunner), before
164
+ # the root callback can perform a CLI self-update or network login.
165
+ options = args[:args.index('--')] if '--' in args else args
166
+ ctx.meta['daily_netboot'] = (
167
+ _command_name_from_argv(['sima-cli'] + options) == 'bootimg'
168
+ and any(arg in ('--netboot', '-n', '--autoflash', '-a') for arg in options)
169
+ )
170
+ if _command_name_from_argv(['sima-cli'] + options) == 'update' and '--inspect' in options:
171
+ ctx.meta['update_inspect'] = True
172
+ return super().parse_args(ctx, args)
173
+
174
+
175
+ # Entry point for the CLI tool using Click's command group decorator
176
+ @click.group(cls=InspectionAwareGroup, context_settings=dict(help_option_names=["-h", "--help", "-?"], max_content_width=120))
177
+ @click.option('-i', '--internal', is_flag=True, help="Use internal Artifactory resources, Authorized Sima employees only")
178
+ @click.option('-y', '--yes', is_flag=True, help="Assume yes for confirmation prompts.")
179
+ @click.version_option(version=f"{__version__}", message="SiMa CLI version: %(version)s")
180
+ @click.pass_context
181
+ def main(ctx, internal, yes):
182
+ """
183
+ sima-cli – SiMa Developer Portal CLI Tool
184
+
185
+ Global Options:
186
+ --internal Use internal Artifactory resources (can also be set via env variable SIMA_CLI_INTERNAL=1)
187
+ """
188
+ internal = internal or os.getenv("SIMA_CLI_INTERNAL", "0") in ("1", "true", "yes")
189
+ if internal:
190
+ Console(stderr=True).print(Panel(
191
+ "Pre-release software may be unstable. Use at your own risk.",
192
+ title="Pre-release software", border_style="yellow",
193
+ ))
194
+ if ctx.meta.get('update_inspect'):
195
+ ctx.ensure_object(dict)
196
+ ctx.obj.update(internal=internal, yes=yes)
197
+ return
198
+ # Model Registry JSON must remain parseable on stdout. Route root-level
199
+ # update and environment diagnostics to stderr for this command group.
200
+ output_context = (
201
+ redirect_stdout(sys.stderr)
202
+ if ctx.invoked_subcommand == "models"
203
+ else nullcontext()
204
+ )
205
+ with output_context:
206
+ _initialize_main_context(ctx, internal, yes)
207
+
208
+
126
209
  # ----------------------
127
210
  # SDK Command
128
211
  # ----------------------
129
212
  register_sdk_commands(main)
130
213
  register_playbook_commands(main)
131
214
  register_vulcan_commands(main)
215
+ register_models_commands(main)
132
216
 
133
217
 
134
218
  # ----------------------
@@ -230,21 +314,30 @@ def download(ctx, url, dest):
230
314
  @click.option(
231
315
  "-y", "--yes",
232
316
  is_flag=True,
233
- help="Skip confirmation after firmware file is downloaded."
317
+ help="Assume yes for update confirmation prompts."
234
318
  )
235
319
  @click.option(
236
320
  "-p", "--passwd",
237
321
  default="edgeai",
238
322
  show_default=True,
239
- help="Optional SSH password for remote board (default is 'edgeai')."
323
+ help="Password for remote board SSH or local ELXR sudo authentication."
240
324
  )
241
325
  @click.option(
242
- "-f", "--flavor",
326
+ "--flavor",
243
327
  type=click.Choice(["headless", "full", "auto"], case_sensitive=False),
244
328
  default="auto",
245
329
  show_default=True,
246
330
  help="Firmware flavor: 'full' image supports NVMe and GUI on Modalix DevKit. This option is deprecated for 2.0 and above"
247
331
  )
332
+ @click.option(
333
+ "-f", "--force",
334
+ is_flag=True,
335
+ help=(
336
+ "If the internal mirror is unreachable, fall back to the external pre-release mirror "
337
+ "(eLxr only). eLxr 3.0+ still verifies signed full-system bundles. "
338
+ "On eLxr 2.1, this disables repository signature verification and, without --internal, selects the mirror directly."
339
+ )
340
+ )
248
341
  @click.option(
249
342
  "-t", "--troot_only",
250
343
  is_flag=True,
@@ -255,10 +348,13 @@ def download(ctx, url, dest):
255
348
  "--dryrun",
256
349
  is_flag=True,
257
350
  default=False,
258
- help="For ELXR updates only, validate the update path and print the simaai-ota command without running it."
351
+ help="For eLxr updates, validate the update path and show the command without installing."
259
352
  )
353
+ @click.option("--inspect", "inspect_state", is_flag=True, help="Show eLxr 3.0+ A/B slot state without updating (local or --ip).")
354
+ @click.option("--signing-cert", metavar="URL_OR_FILE", help="SWUpdate PEM verification certificate: HTTP(S) URL or local file. Defaults to the certificate for the selected channel when configured (eLxr 3.0+).")
355
+ @click.option("--reboot", is_flag=True, help="Reboot after successful eLxr 3.0+ installation; verify remote boot health.")
260
356
  @click.pass_context
261
- def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, troot_only, dryrun):
357
+ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force, troot_only, dryrun, inspect_state, reboot, signing_cert):
262
358
  """
263
359
  Update the software on a SiMa DevKit or remote SiMa device.
264
360
 
@@ -267,6 +363,24 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, troot_o
267
363
  or remote devices accessible over the network). Updates may be
268
364
  installed directly on the device or pushed from a development host.
269
365
 
366
+ eLxr 3.0+ uses signed full-system SWU bundles. Use --inspect for A/B
367
+ state, --ip for remote updates, and --reboot to reboot after installation.
368
+ The verification certificate is downloaded for the selected channel when configured; use
369
+ --signing-cert URL_OR_FILE for an image signed with your own certificate.
370
+ Developer-portal version lookup for 3.0 is not available yet.
371
+
372
+ Internal mode warns that pre-release software may be unstable and is used
373
+ at your own risk. On eLxr 3.0+, unavailable Artifactory access falls back
374
+ to the public daily platform mirror, including missing login, HTTP 401/403,
375
+ connection failures, timeouts, and server errors. Exact build names select
376
+ directly; partial matches show builds newest first by build number.
377
+
378
+ Mirror downloads verify the indexed size and SHA-256 before signed
379
+ installation. A missing build or failed download stops before installation.
380
+ A connection loss after installation starts requires inspecting the board
381
+ before retrying. Devices running eLxr 2.1.3 retain the APT update flow;
382
+ upgrading those devices to 3.0 requires recovery/provisioning first.
383
+
270
384
  How Version Resolution Works:
271
385
 
272
386
  • If a version string is provided (e.g., ``1.7.0``), sima-cli automatically resolves it to the correct downloadable firmware asset based on channel, flavor, and board type.
@@ -316,7 +430,19 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, troot_o
316
430
 
317
431
  sima-cli update -v 1.7.0 -y
318
432
 
319
- # Validate ELXR update path without running simaai-ota
433
+ # Update legacy eLxr (<3.0) to the latest official release without prompts
434
+
435
+ sima-cli update -y
436
+
437
+ # Update ELXR from the internal mirror without prompts
438
+
439
+ sima-cli -i update -y
440
+
441
+ # Update legacy eLxr (<3.0) from the public pre-release mirror without prompts
442
+
443
+ sima-cli -y update -f -y
444
+
445
+ # Validate the eLxr update path without installing
320
446
 
321
447
  sima-cli update --dryrun
322
448
 
@@ -328,6 +454,29 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, troot_o
328
454
  # Prioritize explicit --version option over positional argument
329
455
  version_or_url = version_option or version_or_url
330
456
  is_elxr = is_devkit_running_elxr()
457
+ if inspect_state and (version_or_url or dryrun or force or troot_only or reboot or signing_cert is not None or flavor != 'auto'):
458
+ raise click.UsageError("--inspect cannot be combined with installation options.")
459
+ try:
460
+ if handle_update(version_or_url, ip=ip, passwd=passwd,
461
+ internal=ctx.obj.get("internal", False),
462
+ auto_confirm=yes or ctx.obj.get("yes", False), dryrun=dryrun,
463
+ signing_cert=signing_cert, reboot=reboot, inspect=inspect_state, force=force,
464
+ troot_only=troot_only, flavor=flavor, local_elxr=is_elxr):
465
+ return
466
+ except (click.ClickException, click.Abort):
467
+ raise
468
+ except Exception as exc:
469
+ raise click.ClickException(str(exc)) from exc
470
+
471
+ # Legacy updates retain their original access/--force policy after dispatch.
472
+ if ctx.obj.get("internal", False):
473
+ if not internal_resource_exists():
474
+ raise click.ClickException("Internal resources are not configured on this machine.")
475
+ if not ctx.obj.get("internal_reachable", True) and not force:
476
+ raise click.ClickException(
477
+ "Artifactory is unreachable. Connect to VPN or the corporate network. "
478
+ "Automatic daily SWU fallback requires a device already running eLxr 3.0+."
479
+ )
331
480
 
332
481
  if dryrun and not is_elxr:
333
482
  raise click.ClickException("--dryrun is only supported when running update on an ELXR devkit.")
@@ -345,15 +494,20 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, troot_o
345
494
 
346
495
  # Extract context and run update logic
347
496
  internal = ctx.obj.get("internal", False)
497
+ auto_confirm = yes or ctx.obj.get("yes", False)
498
+ force_external_fallback = force and (
499
+ not internal or not ctx.obj.get("internal_reachable", True)
500
+ )
348
501
  perform_update(
349
502
  version_or_url,
350
503
  ip,
351
504
  internal,
352
505
  passwd=passwd,
353
- auto_confirm=yes,
506
+ auto_confirm=auto_confirm,
354
507
  flavor=flavor,
355
508
  troot_only=troot_only,
356
509
  dryrun=dryrun,
510
+ force_external_fallback=force_external_fallback,
357
511
  )
358
512
 
359
513
  # ----------------------
@@ -407,7 +561,7 @@ def get_model(ctx, model_name):
407
561
  @click.argument('model_name')
408
562
  @click.pass_context
409
563
  def get_model(ctx, model_name):
410
- """Download a specific model."""
564
+ """Provide information about a specific model."""
411
565
  ver = ctx.obj.get("ver")
412
566
  internal = ctx.obj.get("internal", False)
413
567
  boardtype = ctx.obj.get('boardtype')
@@ -445,14 +599,16 @@ def show_mla_memory_usage(ctx):
445
599
  # ----------------------
446
600
  @main.command(name="bootimg")
447
601
  @click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
448
- @click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
449
- @click.option("-t", "--fwtype", type=click.Choice(["yocto", "elxr"], case_sensitive=False), default="yocto", show_default=True, help="Target firmware type.")
602
+ @click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="modalix", show_default=True, help="Target board type.")
603
+ @click.option("-t", "--fwtype", type=click.Choice(["yocto", "elxr"], case_sensitive=False), default="elxr", show_default=True, help="Target firmware type.")
450
604
  @click.option("-n", "--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
451
- @click.option("--devkit-ip", required=False, help="Optional DevKit IP address for pre-netboot version probing.")
605
+ @click.option("-f", "--force", is_flag=True, help="Allow daily mirror fallback if Artifactory is unavailable (internal Modalix eLxr netboot only).")
606
+ @click.option("--recovery", is_flag=True, help="Write eLxr Modalix recovery media for automatic eMMC recovery.")
607
+ @click.option("--devkit", "--devkit-ip", "devkit_ip", required=False, help="DevKit IP for remote netboot; discover and select a DevKit when omitted.")
452
608
  @click.option("-r", "--rootfs", required=False, help="Custom root fs folders (internal use only)")
453
- @click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="Net boot the DevKit and automatically flash the internal storage - TBD")
609
+ @click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="Network boot the selected DevKit, then automatically flash its internal storage once SSH is ready.")
454
610
  @click.pass_context
455
- def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype, rootfs):
611
+ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype, rootfs, recovery=False, force=False):
456
612
  """
457
613
  Prepare a bootable image for the SiMa DevKit.
458
614
 
@@ -461,6 +617,10 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
461
617
  network boot environment. It supports both MLSoC- and Modalix-based
462
618
  DevKits, as well as Yocto and eLxr firmware types.
463
619
 
620
+ Matching internal builds appear in aligned Version and Build time (UTC)
621
+ columns, newest first. Build time uses the newest Artifactory archive
622
+ creation timestamp for each version. Unknown timestamps appear last.
623
+
464
624
  Operations Performed:
465
625
 
466
626
  • Download the correct firmware bundle for the selected version
@@ -486,27 +646,46 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
486
646
 
487
647
  # Write an SD card image for an MLSoC DevKit
488
648
 
489
- sima-cli bootimg -v 1.6.0 --boardtype mlsoc
649
+ sima-cli bootimg -v 1.6.0 --boardtype mlsoc --fwtype yocto
490
650
 
491
651
  # Set up netboot for a Modalix DevKit
492
652
 
493
- sima-cli bootimg -v 1.6.0 --boardtype modalix --netboot
653
+ sima-cli bootimg -v 3.0.0 --netboot
654
+
655
+ # Select a DevKit for confirmed remote U-Boot setup and reboot
494
656
 
495
- # Set up netboot and probe an existing DevKit first
657
+ sima-cli bootimg -v 3.0.0 --netboot --devkit-ip 192.168.1.20
496
658
 
497
- sima-cli bootimg -v 2.1.0 --boardtype modalix --netboot --devkit-ip 192.168.1.20
659
+ # Allow daily mirror fallback for internal eLxr 3.0+ netboot
660
+
661
+ sima-cli -i bootimg -v 1247 --netboot -f
498
662
 
499
663
  # Prepare an eLxr netboot image for Modalix
500
664
 
501
- sima-cli bootimg -v 2.0.0 --boardtype modalix --fwtype elxr --netboot
665
+ sima-cli bootimg -v 2.0.0 --netboot
666
+
667
+ # Prepare USB/SD recovery media that automatically recovers eMMC
668
+
669
+ sima-cli bootimg -v 3.0.0 --recovery
502
670
 
503
671
  """
504
672
 
673
+ if recovery:
674
+ if netboot or autoflash or rootfs or devkit_ip:
675
+ raise click.UsageError("--recovery cannot be combined with --netboot, --autoflash, --rootfs, or --devkit-ip.")
676
+ if ctx.get_parameter_source("boardtype") == click.core.ParameterSource.DEFAULT:
677
+ boardtype = "modalix"
678
+ if ctx.get_parameter_source("fwtype") == click.core.ParameterSource.DEFAULT:
679
+ fwtype = "elxr"
680
+ if boardtype != "modalix" or fwtype != "elxr":
681
+ raise click.UsageError("--recovery requires --boardtype modalix and --fwtype elxr.")
682
+
505
683
  from sima_cli.update.bootimg import write_image
506
684
  from sima_cli.update.netboot import setup_netboot
507
- from sima_cli.update.remote import get_remote_board_info
508
-
509
685
  internal = ctx.obj.get("internal", False)
686
+ if internal and ctx.meta.get('daily_netboot') and (boardtype != 'modalix' or fwtype != 'elxr'):
687
+ if not internal_resource_exists() or not ctx.obj.get('internal_reachable', True):
688
+ raise click.ClickException('Artifactory is unavailable. Daily netboot fallback requires Modalix eLxr.')
510
689
 
511
690
  click.echo(f"📦 Preparing boot image:")
512
691
  click.echo(f" 🔹 Version : {version}")
@@ -517,23 +696,13 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
517
696
  click.echo(f" 🔹 DevKit IP : {devkit_ip}")
518
697
 
519
698
  try:
520
- if devkit_ip and (netboot or autoflash):
521
- click.echo(f"🔎 Probing DevKit version at {devkit_ip} ...")
522
- _, remote_version, _, _, _ = get_remote_board_info(devkit_ip)
523
- if not remote_version:
524
- raise click.ClickException(
525
- f"Unable to retrieve remote version from DevKit at {devkit_ip}."
526
- )
527
- click.echo(f"✅ DevKit current version: {remote_version}")
528
-
529
699
  boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
530
700
  if netboot or autoflash:
531
- setup_netboot(version, boardtype, internal, autoflash, flavor='headless', rootfs=rootfs, swtype=fwtype)
701
+ setup_netboot(version, boardtype, internal, autoflash, flavor='headless', rootfs=rootfs, swtype=fwtype, allow_daily_fallback=force, devkit=devkit_ip)
532
702
  click.echo("✅ Netboot image prepared and TFTP server is running.")
533
703
  else:
534
- write_image(version, boardtype, fwtype, internal, flavor='headless')
704
+ write_image(version, boardtype, fwtype, internal, flavor='headless', recovery=recovery)
535
705
  click.echo("✅ Boot image successfully written.")
536
- click.echo("✅ Boot image successfully written.")
537
706
  except Exception as e:
538
707
  click.echo(f"❌ Failed to write boot image: {e}", err=True)
539
708
  ctx.exit(1)
@@ -544,6 +713,38 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
544
713
  SDK_DEPENDENT_COMPONENTS = {"palette", "hostdriver"}
545
714
  SDK_INDEPENDENT_COMPONENTS = {"optiview"}
546
715
  ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
716
+ NEAT_INSTALL_REPOSITORIES = {
717
+ "apps",
718
+ "core",
719
+ "edgematic-studio",
720
+ "insight",
721
+ "internals",
722
+ "llima",
723
+ "models",
724
+ "sdk",
725
+ "sentinel",
726
+ "sima-cli",
727
+ }
728
+
729
+
730
+ def _is_neat_install_target(component: str) -> bool:
731
+ repository = component.lower().split("/", 1)[0].split("@", 1)[0].split(":", 1)[0]
732
+ return repository in NEAT_INSTALL_REPOSITORIES
733
+
734
+
735
+ def _show_neat_install_fallback(component: str) -> None:
736
+ Console().print(
737
+ Panel(
738
+ Text.from_markup(
739
+ "The standard install source returned HTTP 403 for this Neat component.\n\n"
740
+ f"Switching to [bold]sima-cli neat install {component}[/bold].\n\n"
741
+ "For Neat organization components, use [bold]sima-cli neat install[/bold] in the future."
742
+ ),
743
+ title="Switching to Neat Install",
744
+ border_style="yellow",
745
+ expand=False,
746
+ )
747
+ )
547
748
 
548
749
  @main.command(name="install")
549
750
  @click.argument("component", required=False)
@@ -728,6 +929,20 @@ def install_cmd(
728
929
  command_name="sima-cli install",
729
930
  ):
730
931
  click.echo("✅ Installation complete.")
932
+ except MetadataAccessForbidden:
933
+ if not _is_neat_install_target(component):
934
+ raise
935
+ _show_neat_install_fallback(component)
936
+ install_vulcan_package(
937
+ target=component,
938
+ environment="production",
939
+ package_type=tag,
940
+ install_dir=install_dir,
941
+ force=force,
942
+ json_output=json_output,
943
+ command_name="sima-cli neat install",
944
+ )
945
+ return None
731
946
  except click.ClickException:
732
947
  raise
733
948
  except Exception as e:
@@ -236,7 +236,7 @@ def discover_and_render_pcie_devices():
236
236
  )
237
237
 
238
238
  console.print(pcie_table)
239
- maybe_run_pcie_throughput_test()
239
+ maybe_run_pcie_throughput_test(pcie_devices)
240
240
 
241
241
  # ─────────────────────────────────────────────────────────────
242
242
  # Unified Discovery Orchestration
@@ -97,6 +97,8 @@ def download_file_from_url(url: str, dest_folder: str = ".", internal: bool = Fa
97
97
  return download_github_asset(url, token=token)
98
98
  else:
99
99
  session = requests.Session()
100
+ if parsed_url.hostname == 'artifacts.neat.sima.ai':
101
+ session.trust_env = False
100
102
  request_fn = session.get
101
103
  head_fn = session.head
102
104