sima-cli 2.1.16__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.
- {sima_cli-2.1.16/sima_cli.egg-info → sima_cli-2.1.17}/PKG-INFO +4 -1
- {sima_cli-2.1.16 → sima_cli-2.1.17}/README.md +2 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/pyproject.toml +2 -1
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/__version__.py +1 -1
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/cli.py +150 -49
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/download/downloader.py +2 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/metadata_installer.py +28 -11
- sima_cli-2.1.17/sima_cli/models/__init__.py +3 -0
- sima_cli-2.1.17/sima_cli/models/client.py +372 -0
- sima_cli-2.1.17/sima_cli/models/commands.py +223 -0
- sima_cli-2.1.17/sima_cli/models/rendering.py +473 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/commands.py +66 -25
- sima_cli-2.1.17/sima_cli/sdk/docker_staging.py +22 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/install.py +108 -13
- sima_cli-2.1.17/sima_cli/sdk/model_compiler.py +140 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/neat.py +32 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/utils.py +443 -129
- sima_cli-2.1.17/sima_cli/sdk/vscode_extensions.py +197 -0
- sima_cli-2.1.17/sima_cli/update/ab_state.py +91 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/bootimg.py +7 -2
- sima_cli-2.1.17/sima_cli/update/emmc.py +68 -0
- sima_cli-2.1.17/sima_cli/update/fast_upload.py +79 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/netboot.py +191 -43
- sima_cli-2.1.17/sima_cli/update/netboot_artifacts.py +105 -0
- sima_cli-2.1.17/sima_cli/update/netboot_device.py +177 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/query.py +110 -22
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/remote.py +52 -32
- sima_cli-2.1.17/sima_cli/update/rootfs.py +96 -0
- sima_cli-2.1.17/sima_cli/update/staging.py +76 -0
- sima_cli-2.1.17/sima_cli/update/swu.py +462 -0
- sima_cli-2.1.17/sima_cli/update/swu_artifacts.py +286 -0
- sima_cli-2.1.17/sima_cli/update/swu_certificate.py +57 -0
- sima_cli-2.1.17/sima_cli/update/swu_target.py +85 -0
- sima_cli-2.1.17/sima_cli/update/uboot_environment.py +78 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/updater.py +70 -18
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/pkg_update_check.py +3 -1
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/vulcan/artifacts.py +1 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17/sima_cli.egg-info}/PKG-INFO +4 -1
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli.egg-info/SOURCES.txt +43 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli.egg-info/requires.txt +1 -0
- sima_cli-2.1.17/tests/unit/test_ab_overlay.py +125 -0
- sima_cli-2.1.17/tests/unit/test_bootimg_defaults.py +37 -0
- sima_cli-2.1.17/tests/unit/test_bootimg_recovery.py +118 -0
- sima_cli-2.1.17/tests/unit/test_emmc.py +92 -0
- sima_cli-2.1.17/tests/unit/test_fast_upload.py +49 -0
- sima_cli-2.1.17/tests/unit/test_firmware_creation_order.py +65 -0
- sima_cli-2.1.17/tests/unit/test_firmware_layout.py +66 -0
- sima_cli-2.1.17/tests/unit/test_firmware_selector.py +64 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_install_stub.py +69 -4
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_metadata_installer.py +137 -13
- sima_cli-2.1.17/tests/unit/test_models.py +632 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_netboot.py +8 -1
- sima_cli-2.1.17/tests/unit/test_netboot_autoflash.py +53 -0
- sima_cli-2.1.17/tests/unit/test_netboot_device.py +292 -0
- sima_cli-2.1.17/tests/unit/test_netboot_ip_recovery.py +129 -0
- sima_cli-2.1.17/tests/unit/test_netboot_mirror.py +186 -0
- sima_cli-2.1.17/tests/unit/test_netboot_trootctl.py +95 -0
- sima_cli-2.1.17/tests/unit/test_palette_image_discovery.py +78 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_pkg_update_check.py +25 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_sdk_image_detection.py +840 -56
- sima_cli-2.1.17/tests/unit/test_sdk_model_compiler.py +282 -0
- sima_cli-2.1.17/tests/unit/test_swu.py +573 -0
- sima_cli-2.1.17/tests/unit/test_swu_certificate.py +208 -0
- sima_cli-2.1.17/tests/unit/test_swu_errors.py +162 -0
- sima_cli-2.1.17/tests/unit/test_swu_mirror.py +331 -0
- sima_cli-2.1.17/tests/unit/test_swu_target.py +58 -0
- sima_cli-2.1.17/tests/unit/test_uboot_environment.py +67 -0
- sima_cli-2.1.17/tests/unit/test_update_staging.py +111 -0
- sima_cli-2.1.17/tests/unit/test_vscode_extension_pins.py +362 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/LICENSE +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/MANIFEST.in +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/requirements.txt +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/setup.cfg +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/setup.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/__main__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/app_zoo/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/app_zoo/app.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/app_zoo/commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/auth/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/auth/auth0.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/auth/devportal.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/auth/login.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/data/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/data/resources_internal.yaml +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/data/resources_public.yaml +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/deploy_only/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/deploy_only/device/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/deploy_only/device/commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/deploy_only/mpk/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/deploy_only/mpk/commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/discover/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/discover/discover.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/discover/linuxll.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/download/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/compatibility.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/github_assets.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/hostdriver.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/metadata_info.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/metadata_validator.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/optiview.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/package_builder.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/palette.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/install/registry.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/mla/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/mla/meminfo.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/model_zoo/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/model_zoo/model.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/network/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/network/network.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/playbooks/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/playbooks/commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/playbooks/manager.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/cmdexec.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/config.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/linux_devkit_network.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/linux_shared_network.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/network_doctor.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/preinstall.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/requirements.json +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/script.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/stop.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/sdk/uninstall.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/serial/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/serial/serial.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/storage/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/storage/nvme.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/storage/sdcard.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/bmaptool.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/cleanlog.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/elxr.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/update/local.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/upgrade/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/upgrade/selfupdate.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/api_common.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/artifactory.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/common.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/config.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/config_loader.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/container_registries.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/deprecation.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/device_api.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/disk.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/docker.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/env.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/errors.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/mpk_api.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/net.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/network.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/pcie.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/serializers.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/services.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/utils/tag.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/vulcan/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli/vulcan/commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli.egg-info/dependency_links.txt +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli.egg-info/entry_points.txt +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/sima_cli.egg-info/top_level.txt +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/e2e/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/__init__.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_app_zoo.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_appzoo_commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_auth.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_auth0.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_cli.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_cli_stdio.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_cli_update.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_cli_update_rerun.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_container_registries.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_deprecation.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_docker_utils.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_download.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_elxr_update.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_firmware.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_generate_cli_markdown_docs.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_metadata_validator.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_model_zoo.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_modelzoo_commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_package_builder.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_pcie.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_sdk_linux_shared_network.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_sdk_network_doctor.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_sdk_preinstall.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_sdk_uninstall.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_selfupdate.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_skills_commands.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_skills_manager.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_utils.py +0 -0
- {sima_cli-2.1.16 → sima_cli-2.1.17}/tests/unit/test_vulcan.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sima-cli
|
|
3
|
-
Version: 2.1.
|
|
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
|
[](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
|
|
38
41
|
[](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
|
|
39
42
|
[](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
|
[](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
|
|
4
6
|
[](https://github.com/sima-neat/sima-cli/actions/workflows/vulcan-ci.yml)
|
|
5
7
|
[](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.
|
|
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.
|
|
2
|
+
__version__ = "2.1.17"
|
|
@@ -1,12 +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
|
|
5
6
|
from rich.console import Console
|
|
6
7
|
from rich.panel import Panel
|
|
7
8
|
from rich.text import Text
|
|
8
9
|
from sima_cli.utils.env import get_environment_type
|
|
9
10
|
from sima_cli.update.updater import perform_update
|
|
11
|
+
from sima_cli.update.swu import handle_update
|
|
10
12
|
from sima_cli.model_zoo.model import list_models, download_model, describe_model
|
|
11
13
|
from sima_cli.app_zoo.app import list_apps, download_app, describe_app
|
|
12
14
|
from sima_cli.utils.config_loader import internal_resource_exists
|
|
@@ -37,6 +39,7 @@ from sima_cli.install.registry import register_packages_commands
|
|
|
37
39
|
from sima_cli.upgrade.selfupdate import register_selfupdate_command
|
|
38
40
|
from sima_cli.playbooks import register_playbook_commands
|
|
39
41
|
from sima_cli.vulcan import register_vulcan_commands
|
|
42
|
+
from sima_cli.models import register_models_commands
|
|
40
43
|
from sima_cli.vulcan.commands import (
|
|
41
44
|
ENV_METAVAR,
|
|
42
45
|
_environment_shortcut_options,
|
|
@@ -99,19 +102,7 @@ def _rerun_current_command() -> None:
|
|
|
99
102
|
os.execvpe(sys.executable, cmd, env)
|
|
100
103
|
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
@click.group(context_settings=dict(help_option_names=["-h", "--help", "-?"], max_content_width=120))
|
|
104
|
-
@click.option('-i', '--internal', is_flag=True, help="Use internal Artifactory resources, Authorized Sima employees only")
|
|
105
|
-
@click.option('-y', '--yes', is_flag=True, help="Assume yes for confirmation prompts.")
|
|
106
|
-
@click.version_option(version=f"{__version__}", message="SiMa CLI version: %(version)s")
|
|
107
|
-
@click.pass_context
|
|
108
|
-
def main(ctx, internal, yes):
|
|
109
|
-
"""
|
|
110
|
-
sima-cli – SiMa Developer Portal CLI Tool
|
|
111
|
-
|
|
112
|
-
Global Options:
|
|
113
|
-
--internal Use internal Artifactory resources (can also be set via env variable SIMA_CLI_INTERNAL=1)
|
|
114
|
-
"""
|
|
105
|
+
def _initialize_main_context(ctx, internal, yes):
|
|
115
106
|
_configure_stdio_errors()
|
|
116
107
|
auto_accept_update = yes or _update_auto_confirm_requested(sys.argv)
|
|
117
108
|
previous_auto_accept = os.environ.get("SIMA_CLI_AUTO_ACCEPT_UPDATE")
|
|
@@ -134,26 +125,26 @@ def main(ctx, internal, yes):
|
|
|
134
125
|
if not internal:
|
|
135
126
|
internal = os.getenv("SIMA_CLI_INTERNAL", "0") in ("1", "true", "yes")
|
|
136
127
|
|
|
137
|
-
|
|
138
|
-
|
|
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.")
|
|
139
131
|
click.echo("Refer to the confluence page to find out how to configure internal resource map.")
|
|
140
|
-
exit(0)
|
|
132
|
+
exit(0)
|
|
141
133
|
|
|
142
134
|
internal_reachable = True
|
|
143
135
|
if internal:
|
|
144
136
|
internal_reachable = check_artifactory_reachability()
|
|
145
137
|
|
|
146
|
-
if internal and not internal_reachable and not _allows_external_prerelease_fallback(sys.argv):
|
|
138
|
+
if internal and not internal_reachable and not defer_artifactory_access and not _allows_external_prerelease_fallback(sys.argv):
|
|
147
139
|
click.secho("❌ You have specified -i or --internal argument to access internal resources, but you can't connect to Artifactory.", fg='red')
|
|
148
140
|
click.secho("Please make sure you are connected to VPN or are on the corporate network.", fg='red')
|
|
149
141
|
exit(0)
|
|
150
142
|
|
|
151
|
-
if internal and not internal_reachable:
|
|
143
|
+
if internal and not internal_reachable and not defer_artifactory_access:
|
|
152
144
|
click.secho(
|
|
153
145
|
"⚠️ Internal resources are unreachable. --force allows this update to use the external pre-release mirror.",
|
|
154
146
|
fg="yellow",
|
|
155
147
|
)
|
|
156
|
-
|
|
157
148
|
|
|
158
149
|
ctx.obj["internal"] = internal
|
|
159
150
|
ctx.obj["internal_reachable"] = internal_reachable
|
|
@@ -167,12 +158,61 @@ def main(ctx, internal, yes):
|
|
|
167
158
|
click.echo(f"🔧 Environment: {env_type} ({env_subtype})")
|
|
168
159
|
|
|
169
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
|
+
|
|
170
209
|
# ----------------------
|
|
171
210
|
# SDK Command
|
|
172
211
|
# ----------------------
|
|
173
212
|
register_sdk_commands(main)
|
|
174
213
|
register_playbook_commands(main)
|
|
175
214
|
register_vulcan_commands(main)
|
|
215
|
+
register_models_commands(main)
|
|
176
216
|
|
|
177
217
|
|
|
178
218
|
# ----------------------
|
|
@@ -294,7 +334,8 @@ def download(ctx, url, dest):
|
|
|
294
334
|
is_flag=True,
|
|
295
335
|
help=(
|
|
296
336
|
"If the internal mirror is unreachable, fall back to the external pre-release mirror "
|
|
297
|
-
"
|
|
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."
|
|
298
339
|
)
|
|
299
340
|
)
|
|
300
341
|
@click.option(
|
|
@@ -307,10 +348,13 @@ def download(ctx, url, dest):
|
|
|
307
348
|
"--dryrun",
|
|
308
349
|
is_flag=True,
|
|
309
350
|
default=False,
|
|
310
|
-
help="For
|
|
351
|
+
help="For eLxr updates, validate the update path and show the command without installing."
|
|
311
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.")
|
|
312
356
|
@click.pass_context
|
|
313
|
-
def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force, 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):
|
|
314
358
|
"""
|
|
315
359
|
Update the software on a SiMa DevKit or remote SiMa device.
|
|
316
360
|
|
|
@@ -319,6 +363,24 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
319
363
|
or remote devices accessible over the network). Updates may be
|
|
320
364
|
installed directly on the device or pushed from a development host.
|
|
321
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
|
+
|
|
322
384
|
How Version Resolution Works:
|
|
323
385
|
|
|
324
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.
|
|
@@ -368,7 +430,7 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
368
430
|
|
|
369
431
|
sima-cli update -v 1.7.0 -y
|
|
370
432
|
|
|
371
|
-
# Update
|
|
433
|
+
# Update legacy eLxr (<3.0) to the latest official release without prompts
|
|
372
434
|
|
|
373
435
|
sima-cli update -y
|
|
374
436
|
|
|
@@ -376,11 +438,11 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
376
438
|
|
|
377
439
|
sima-cli -i update -y
|
|
378
440
|
|
|
379
|
-
# Update
|
|
441
|
+
# Update legacy eLxr (<3.0) from the public pre-release mirror without prompts
|
|
380
442
|
|
|
381
443
|
sima-cli -y update -f -y
|
|
382
444
|
|
|
383
|
-
# Validate
|
|
445
|
+
# Validate the eLxr update path without installing
|
|
384
446
|
|
|
385
447
|
sima-cli update --dryrun
|
|
386
448
|
|
|
@@ -392,6 +454,29 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
392
454
|
# Prioritize explicit --version option over positional argument
|
|
393
455
|
version_or_url = version_option or version_or_url
|
|
394
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
|
+
)
|
|
395
480
|
|
|
396
481
|
if dryrun and not is_elxr:
|
|
397
482
|
raise click.ClickException("--dryrun is only supported when running update on an ELXR devkit.")
|
|
@@ -514,14 +599,16 @@ def show_mla_memory_usage(ctx):
|
|
|
514
599
|
# ----------------------
|
|
515
600
|
@main.command(name="bootimg")
|
|
516
601
|
@click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
|
|
517
|
-
@click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="
|
|
518
|
-
@click.option("-t", "--fwtype", type=click.Choice(["yocto", "elxr"], case_sensitive=False), default="
|
|
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.")
|
|
519
604
|
@click.option("-n", "--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
|
|
520
|
-
@click.option("
|
|
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.")
|
|
521
608
|
@click.option("-r", "--rootfs", required=False, help="Custom root fs folders (internal use only)")
|
|
522
|
-
@click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="
|
|
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.")
|
|
523
610
|
@click.pass_context
|
|
524
|
-
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):
|
|
525
612
|
"""
|
|
526
613
|
Prepare a bootable image for the SiMa DevKit.
|
|
527
614
|
|
|
@@ -530,6 +617,10 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
|
|
|
530
617
|
network boot environment. It supports both MLSoC- and Modalix-based
|
|
531
618
|
DevKits, as well as Yocto and eLxr firmware types.
|
|
532
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
|
+
|
|
533
624
|
Operations Performed:
|
|
534
625
|
|
|
535
626
|
• Download the correct firmware bundle for the selected version
|
|
@@ -555,27 +646,46 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
|
|
|
555
646
|
|
|
556
647
|
# Write an SD card image for an MLSoC DevKit
|
|
557
648
|
|
|
558
|
-
sima-cli bootimg -v 1.6.0 --boardtype mlsoc
|
|
649
|
+
sima-cli bootimg -v 1.6.0 --boardtype mlsoc --fwtype yocto
|
|
559
650
|
|
|
560
651
|
# Set up netboot for a Modalix DevKit
|
|
561
652
|
|
|
562
|
-
sima-cli bootimg -v
|
|
653
|
+
sima-cli bootimg -v 3.0.0 --netboot
|
|
654
|
+
|
|
655
|
+
# Select a DevKit for confirmed remote U-Boot setup and reboot
|
|
656
|
+
|
|
657
|
+
sima-cli bootimg -v 3.0.0 --netboot --devkit-ip 192.168.1.20
|
|
563
658
|
|
|
564
|
-
#
|
|
659
|
+
# Allow daily mirror fallback for internal eLxr 3.0+ netboot
|
|
565
660
|
|
|
566
|
-
sima-cli bootimg -v
|
|
661
|
+
sima-cli -i bootimg -v 1247 --netboot -f
|
|
567
662
|
|
|
568
663
|
# Prepare an eLxr netboot image for Modalix
|
|
569
664
|
|
|
570
|
-
sima-cli bootimg -v 2.0.0 --
|
|
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
|
|
571
670
|
|
|
572
671
|
"""
|
|
573
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
|
+
|
|
574
683
|
from sima_cli.update.bootimg import write_image
|
|
575
684
|
from sima_cli.update.netboot import setup_netboot
|
|
576
|
-
from sima_cli.update.remote import get_remote_board_info
|
|
577
|
-
|
|
578
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.')
|
|
579
689
|
|
|
580
690
|
click.echo(f"📦 Preparing boot image:")
|
|
581
691
|
click.echo(f" 🔹 Version : {version}")
|
|
@@ -586,23 +696,13 @@ def bootimg_cmd(ctx, version, boardtype, netboot, devkit_ip, autoflash, fwtype,
|
|
|
586
696
|
click.echo(f" 🔹 DevKit IP : {devkit_ip}")
|
|
587
697
|
|
|
588
698
|
try:
|
|
589
|
-
if devkit_ip and (netboot or autoflash):
|
|
590
|
-
click.echo(f"🔎 Probing DevKit version at {devkit_ip} ...")
|
|
591
|
-
_, remote_version, _, _, _ = get_remote_board_info(devkit_ip)
|
|
592
|
-
if not remote_version:
|
|
593
|
-
raise click.ClickException(
|
|
594
|
-
f"Unable to retrieve remote version from DevKit at {devkit_ip}."
|
|
595
|
-
)
|
|
596
|
-
click.echo(f"✅ DevKit current version: {remote_version}")
|
|
597
|
-
|
|
598
699
|
boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
|
|
599
700
|
if netboot or autoflash:
|
|
600
|
-
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)
|
|
601
702
|
click.echo("✅ Netboot image prepared and TFTP server is running.")
|
|
602
703
|
else:
|
|
603
|
-
write_image(version, boardtype, fwtype, internal, flavor='headless')
|
|
704
|
+
write_image(version, boardtype, fwtype, internal, flavor='headless', recovery=recovery)
|
|
604
705
|
click.echo("✅ Boot image successfully written.")
|
|
605
|
-
click.echo("✅ Boot image successfully written.")
|
|
606
706
|
except Exception as e:
|
|
607
707
|
click.echo(f"❌ Failed to write boot image: {e}", err=True)
|
|
608
708
|
ctx.exit(1)
|
|
@@ -616,6 +716,7 @@ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
|
|
616
716
|
NEAT_INSTALL_REPOSITORIES = {
|
|
617
717
|
"apps",
|
|
618
718
|
"core",
|
|
719
|
+
"edgematic-studio",
|
|
619
720
|
"insight",
|
|
620
721
|
"internals",
|
|
621
722
|
"llima",
|
|
@@ -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
|
|
|
@@ -383,22 +383,35 @@ def _resolve_resource_url_candidates(base_url: str, resource: str) -> List[str]:
|
|
|
383
383
|
return [primary_url, fallback_url]
|
|
384
384
|
|
|
385
385
|
def _metadata_resource_path(dest_folder: str, resource: str, resource_url: str) -> Path:
|
|
386
|
+
install_root = Path(dest_folder).resolve()
|
|
387
|
+
if re.match(r"^[A-Za-z]:[\\/]", resource) or "\\" in resource:
|
|
388
|
+
raise click.ClickException(f"❌ Resource path must be a safe relative path: '{resource}'.")
|
|
389
|
+
|
|
386
390
|
parsed_resource = urlparse(resource)
|
|
387
391
|
if parsed_resource.scheme or parsed_resource.netloc:
|
|
388
392
|
file_name = os.path.basename(urlparse(resource_url).path)
|
|
393
|
+
relative_parts = (file_name,)
|
|
389
394
|
else:
|
|
390
|
-
|
|
395
|
+
relative_parts = tuple(resource.split("/"))
|
|
396
|
+
if resource.startswith("/") or any(part in {"", ".", ".."} for part in relative_parts):
|
|
397
|
+
raise click.ClickException(f"❌ Resource path must be a safe relative path: '{resource}'.")
|
|
398
|
+
file_name = relative_parts[-1]
|
|
391
399
|
|
|
392
400
|
if not file_name:
|
|
393
401
|
raise click.ClickException(f"❌ Cannot determine file name for resource '{resource}'.")
|
|
394
402
|
|
|
395
|
-
|
|
403
|
+
destination = Path(dest_folder).joinpath(*relative_parts)
|
|
404
|
+
resolved_destination = destination.resolve()
|
|
405
|
+
if resolved_destination != install_root and install_root not in resolved_destination.parents:
|
|
406
|
+
raise click.ClickException(f"❌ Resource path escapes the installation directory: '{resource}'.")
|
|
407
|
+
return destination
|
|
396
408
|
|
|
397
409
|
def _normalize_downloaded_metadata_resource(local_path: str, expected_path: Path) -> str:
|
|
398
410
|
downloaded_path = Path(local_path)
|
|
399
411
|
if downloaded_path == expected_path:
|
|
400
412
|
return local_path
|
|
401
413
|
|
|
414
|
+
expected_path.parent.mkdir(parents=True, exist_ok=True)
|
|
402
415
|
if expected_path.exists():
|
|
403
416
|
expected_path.unlink()
|
|
404
417
|
downloaded_path.rename(expected_path)
|
|
@@ -416,7 +429,7 @@ def _download_metadata_file_resource(
|
|
|
416
429
|
try:
|
|
417
430
|
local_path = download_file_from_url(
|
|
418
431
|
url=resource_url,
|
|
419
|
-
dest_folder=
|
|
432
|
+
dest_folder=str(dest_path.parent),
|
|
420
433
|
internal=internal
|
|
421
434
|
)
|
|
422
435
|
return _normalize_downloaded_metadata_resource(local_path, dest_path)
|
|
@@ -1045,7 +1058,11 @@ def _combine_multipart_files(folder: str, local_paths=None):
|
|
|
1045
1058
|
folder_resolved = folder.resolve()
|
|
1046
1059
|
for p in local_paths:
|
|
1047
1060
|
path = Path(p).resolve()
|
|
1048
|
-
if
|
|
1061
|
+
if (
|
|
1062
|
+
path.exists()
|
|
1063
|
+
and path.is_file()
|
|
1064
|
+
and folder_resolved in path.parents
|
|
1065
|
+
):
|
|
1049
1066
|
candidate_files.append(path)
|
|
1050
1067
|
|
|
1051
1068
|
for file in candidate_files:
|
|
@@ -1055,12 +1072,12 @@ def _combine_multipart_files(folder: str, local_paths=None):
|
|
|
1055
1072
|
match = re.match(r"(.+)-split-([a-z]{2})$", file.name)
|
|
1056
1073
|
if match:
|
|
1057
1074
|
base, part = match.groups()
|
|
1058
|
-
parts_by_base.setdefault(base, []).append((part, file))
|
|
1075
|
+
parts_by_base.setdefault((file.parent, base), []).append((part, file))
|
|
1059
1076
|
|
|
1060
1077
|
# Step 2: Process each group
|
|
1061
|
-
for base, parts in parts_by_base.items():
|
|
1078
|
+
for (parent, base), parts in parts_by_base.items():
|
|
1062
1079
|
parts.sort(key=lambda x: x[0])
|
|
1063
|
-
output_file =
|
|
1080
|
+
output_file = parent / f"{base}.tar"
|
|
1064
1081
|
total_size = sum(part_file.stat().st_size for _, part_file in parts)
|
|
1065
1082
|
|
|
1066
1083
|
print(f"\n🧩 Reassembling: {output_file.name} from {len(parts)} parts")
|
|
@@ -1089,7 +1106,7 @@ def _combine_multipart_files(folder: str, local_paths=None):
|
|
|
1089
1106
|
print(f"✅ Created: {output_file.name} ({output_file.stat().st_size / 1e6:.2f} MB)")
|
|
1090
1107
|
|
|
1091
1108
|
# Step 4: Auto-extract .tar
|
|
1092
|
-
extract_dir =
|
|
1109
|
+
extract_dir = parent / base
|
|
1093
1110
|
print(f"📦 Extracting {output_file.name} to {extract_dir}/")
|
|
1094
1111
|
_extract_tar_streaming(output_file, extract_dir)
|
|
1095
1112
|
|
|
@@ -1106,18 +1123,18 @@ def _extract_archives_in_folder(folder: str, local_paths):
|
|
|
1106
1123
|
file = Path(local_path).resolve()
|
|
1107
1124
|
if not file.exists() or not file.is_file():
|
|
1108
1125
|
continue
|
|
1109
|
-
if file.
|
|
1126
|
+
if folder not in file.parents:
|
|
1110
1127
|
continue
|
|
1111
1128
|
|
|
1112
1129
|
# TAR, GZ, TAR.GZ → all handled by _extract_tar_streaming
|
|
1113
1130
|
if file.suffix in [".tar", ".gz"] or file.name.endswith(".tar.gz"):
|
|
1114
|
-
extract_dir =
|
|
1131
|
+
extract_dir = file.parent / file.stem.replace(".tar", "")
|
|
1115
1132
|
print(f"📦 Extracting TAR/GZ: {file.name} to {extract_dir}/")
|
|
1116
1133
|
_extract_tar_streaming(file, extract_dir)
|
|
1117
1134
|
|
|
1118
1135
|
# ZIP
|
|
1119
1136
|
elif file.suffix == ".zip":
|
|
1120
|
-
extract_dir =
|
|
1137
|
+
extract_dir = file.parent / file.stem
|
|
1121
1138
|
print(f"📦 Extracting ZIP: {file.name} to {extract_dir}/")
|
|
1122
1139
|
_extract_zip_streaming(file, extract_dir)
|
|
1123
1140
|
|