sima-cli 2.1.18__tar.gz → 2.1.20__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.18/sima_cli.egg-info → sima_cli-2.1.20}/PKG-INFO +35 -2
- {sima_cli-2.1.18 → sima_cli-2.1.20}/README.md +32 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/pyproject.toml +3 -2
- {sima_cli-2.1.18 → sima_cli-2.1.20}/requirements.txt +2 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20}/setup.py +2 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/__version__.py +1 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/cli.py +52 -15
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/cloudex/client.py +57 -12
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/cloudex/installer.py +33 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/discover/discover.py +4 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/discover/linuxll.py +38 -3
- sima_cli-2.1.20/sima_cli/download/downloader.py +300 -0
- sima_cli-2.1.20/sima_cli/download/parallel.py +164 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/metadata_installer.py +16 -7
- sima_cli-2.1.20/sima_cli/mla/meminfo.py +274 -0
- sima_cli-2.1.20/sima_cli/models/client.py +916 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/models/commands.py +38 -13
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/models/rendering.py +78 -205
- sima_cli-2.1.20/sima_cli/peripherals/__init__.py +5 -0
- sima_cli-2.1.20/sima_cli/peripherals/commands.py +208 -0
- sima_cli-2.1.20/sima_cli/peripherals/model.py +440 -0
- sima_cli-2.1.20/sima_cli/peripherals/sentinel.py +145 -0
- sima_cli-2.1.20/sima_cli/sdk/browser_ca.py +53 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/commands.py +118 -7
- sima_cli-2.1.20/sima_cli/sdk/container_registry.py +385 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/install.py +182 -20
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/neat.py +175 -30
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/network_doctor.py +104 -15
- sima_cli-2.1.20/sima_cli/sdk/preinstall.py +1759 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/utils.py +998 -76
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/vscode_extensions.py +15 -13
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/emmc.py +34 -2
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/host.py +3 -4
- sima_cli-2.1.20/sima_cli/update/mirror_catalog.py +11 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/netboot.py +188 -65
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/netboot_device.py +49 -244
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/overlay.py +19 -7
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/remote.py +14 -3
- sima_cli-2.1.20/sima_cli/update/reset_overlay.py +92 -0
- sima_cli-2.1.20/sima_cli/update/swap.py +81 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/swu.py +40 -13
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/swu_artifacts.py +17 -6
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/swu_certificate.py +1 -2
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/swu_target.py +35 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/uboot_environment.py +11 -3
- sima_cli-2.1.20/sima_cli/utils/streaming.py +84 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/vulcan/artifacts.py +31 -18
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/vulcan/commands.py +1 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20/sima_cli.egg-info}/PKG-INFO +35 -2
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli.egg-info/SOURCES.txt +34 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli.egg-info/requires.txt +6 -1
- sima_cli-2.1.20/tests/unit/peripherals_fixtures.py +136 -0
- sima_cli-2.1.20/tests/unit/test_artifact_parallel_integration.py +174 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cloudex_client.py +138 -16
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cloudex_commands.py +24 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cloudex_installer.py +75 -0
- sima_cli-2.1.20/tests/unit/test_code_ui_readiness.py +81 -0
- sima_cli-2.1.20/tests/unit/test_discovery_host_tools.py +103 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_download.py +1 -0
- sima_cli-2.1.20/tests/unit/test_download_retries.py +115 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_emmc.py +78 -12
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_generate_cli_markdown_docs.py +16 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_host_update.py +11 -1
- sima_cli-2.1.20/tests/unit/test_install_force.py +39 -0
- sima_cli-2.1.20/tests/unit/test_install_script_terminal.py +153 -0
- sima_cli-2.1.20/tests/unit/test_mirror_catalog.py +61 -0
- sima_cli-2.1.20/tests/unit/test_mla_meminfo.py +159 -0
- sima_cli-2.1.20/tests/unit/test_model_deployments.py +794 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_models.py +401 -280
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot.py +5 -1
- sima_cli-2.1.20/tests/unit/test_netboot_autoflash.py +196 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot_device.py +74 -212
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot_trootctl.py +4 -1
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_overlay.py +41 -2
- sima_cli-2.1.20/tests/unit/test_parallel_downloads.py +131 -0
- sima_cli-2.1.20/tests/unit/test_peripherals_commands.py +313 -0
- sima_cli-2.1.20/tests/unit/test_peripherals_contract.py +105 -0
- sima_cli-2.1.20/tests/unit/test_peripherals_model.py +238 -0
- sima_cli-2.1.20/tests/unit/test_peripherals_sentinel.py +127 -0
- sima_cli-2.1.20/tests/unit/test_reset_overlay.py +159 -0
- sima_cli-2.1.20/tests/unit/test_sdk_agent_sessions.py +884 -0
- sima_cli-2.1.20/tests/unit/test_sdk_browser_ca.py +119 -0
- sima_cli-2.1.20/tests/unit/test_sdk_container_registry.py +544 -0
- sima_cli-2.1.20/tests/unit/test_sdk_host_key_trust.py +128 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_sdk_image_detection.py +858 -48
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_sdk_model_compiler.py +24 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_sdk_network_doctor.py +145 -1
- sima_cli-2.1.20/tests/unit/test_sdk_preinstall.py +1325 -0
- sima_cli-2.1.20/tests/unit/test_selectable_resources.py +98 -0
- sima_cli-2.1.20/tests/unit/test_slot_swap.py +145 -0
- sima_cli-2.1.20/tests/unit/test_streaming_downloads.py +296 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_swu.py +77 -33
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_swu_certificate.py +4 -3
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_swu_mirror.py +16 -4
- sima_cli-2.1.20/tests/unit/test_swu_target.py +158 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_uboot_environment.py +43 -4
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_update_staging.py +11 -4
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_vscode_extension_pins.py +80 -24
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_vulcan.py +3 -0
- sima_cli-2.1.18/sima_cli/download/downloader.py +0 -208
- sima_cli-2.1.18/sima_cli/mla/meminfo.py +0 -47
- sima_cli-2.1.18/sima_cli/models/client.py +0 -600
- sima_cli-2.1.18/sima_cli/sdk/preinstall.py +0 -715
- sima_cli-2.1.18/tests/unit/test_netboot_autoflash.py +0 -54
- sima_cli-2.1.18/tests/unit/test_sdk_preinstall.py +0 -432
- sima_cli-2.1.18/tests/unit/test_swu_target.py +0 -58
- {sima_cli-2.1.18 → sima_cli-2.1.20}/LICENSE +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/MANIFEST.in +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/setup.cfg +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/__main__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/app_zoo/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/app_zoo/app.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/app_zoo/commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/auth/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/auth/auth0.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/auth/devportal.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/auth/login.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/cloudex/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/cloudex/commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/data/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/data/resources_internal.yaml +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/data/resources_public.yaml +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/deploy_only/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/deploy_only/device/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/deploy_only/device/commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/deploy_only/mpk/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/deploy_only/mpk/commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/discover/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/download/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/compatibility.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/github_assets.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/hostdriver.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/metadata_info.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/metadata_validator.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/optiview.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/package_builder.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/palette.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/install/registry.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/mla/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/model_zoo/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/model_zoo/model.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/models/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/network/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/network/network.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/playbooks/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/playbooks/commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/playbooks/manager.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/cmdexec.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/config.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/docker_staging.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/linux_devkit_network.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/linux_shared_network.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/model_compiler.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/requirements.json +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/script.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/stop.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/sdk/uninstall.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/serial/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/serial/serial.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/storage/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/storage/nvme.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/storage/sdcard.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/ab_state.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/bmaptool.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/bootimg.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/cleanlog.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/elxr.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/fast_upload.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/local.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/netboot_artifacts.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/query.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/rootfs.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/staging.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/update/updater.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/upgrade/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/upgrade/selfupdate.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/api_common.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/artifactory.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/common.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/config.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/config_loader.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/container_registries.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/deprecation.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/device_api.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/disk.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/docker.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/env.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/errors.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/mpk_api.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/net.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/network.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/pcie.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/pkg_update_check.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/serializers.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/services.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/utils/tag.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli/vulcan/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli.egg-info/dependency_links.txt +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli.egg-info/entry_points.txt +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/sima_cli.egg-info/top_level.txt +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/e2e/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/__init__.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_ab_overlay.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_app_zoo.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_appzoo_commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_auth.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_auth0.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_bootimg_defaults.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_bootimg_recovery.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cli.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cli_stdio.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cli_update.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_cli_update_rerun.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_container_registries.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_deprecation.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_device_discovery_cloudex.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_docker_utils.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_elxr_update.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_fast_upload.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_firmware.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_firmware_creation_order.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_firmware_layout.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_firmware_selector.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_install_stub.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_localized_docs_links.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_metadata_installer.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_metadata_validator.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_model_zoo.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_modelzoo_commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot_images.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot_ip_recovery.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_netboot_mirror.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_network_custom_static.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_network_safety.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_package_builder.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_palette_image_discovery.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_pcie.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_pkg_update_check.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_sdk_linux_shared_network.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_sdk_uninstall.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_selfupdate.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_skills_commands.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_skills_manager.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/tests/unit/test_swu_errors.py +0 -0
- {sima_cli-2.1.18 → sima_cli-2.1.20}/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.
|
|
3
|
+
Version: 2.1.20
|
|
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
|
|
@@ -27,7 +27,8 @@ Requires-Dist: rich==14.0.0
|
|
|
27
27
|
Requires-Dist: InquirerPy==0.3.4
|
|
28
28
|
Requires-Dist: tftpy==0.8.6
|
|
29
29
|
Requires-Dist: psutil==7.0.0
|
|
30
|
-
Requires-Dist: huggingface-hub<
|
|
30
|
+
Requires-Dist: huggingface-hub<3.0,>=2.2.0; python_version >= "3.10"
|
|
31
|
+
Requires-Dist: huggingface-hub<2.0,>=0.34.0; python_version < "3.10"
|
|
31
32
|
Requires-Dist: tabulate==0.9.0
|
|
32
33
|
Dynamic: author
|
|
33
34
|
Dynamic: license-file
|
|
@@ -148,6 +149,20 @@ sima-cli --internal login
|
|
|
148
149
|
SIMA_CLI_INTERNAL=1 sima-cli install -v 2.1.2 tools/model-compiler/amd64
|
|
149
150
|
```
|
|
150
151
|
|
|
152
|
+
Full-system SWU updates prefer RAM-backed `/tmp` when available memory can
|
|
153
|
+
hold both the bundle and its extraction, plus a reserve of at least 512 MiB
|
|
154
|
+
or 10% of total RAM (whichever is larger). Otherwise they stage in `/data`,
|
|
155
|
+
with NVMe as a further fallback if `/data` lacks space. RAM staging is temporary;
|
|
156
|
+
interrupted updates retained there do not survive a reboot.
|
|
157
|
+
|
|
158
|
+
Public-mirror downloads, `models download`, and `neat artifacts` split files
|
|
159
|
+
of at least 64 MiB into four byte ranges when supported by the server. Models
|
|
160
|
+
and Neat artifacts share a limit of four active download connections across
|
|
161
|
+
files and ranges, including single-connection fallbacks. Ranges must match the
|
|
162
|
+
expected size and an object validator or the expected SHA-256 before the file is published. Models retain signed-URL refresh and
|
|
163
|
+
cancellation handling. Existing public-mirror partial downloads continue
|
|
164
|
+
sequentially; servers without range support use a single connection.
|
|
165
|
+
|
|
151
166
|
## Common Workflows
|
|
152
167
|
|
|
153
168
|
Set up SDK containers:
|
|
@@ -157,6 +172,14 @@ sima-cli sdk setup
|
|
|
157
172
|
sima-cli sdk neat
|
|
158
173
|
```
|
|
159
174
|
|
|
175
|
+
When pairing with `sdk setup --devkit`, a new DevKit host key is automatically
|
|
176
|
+
recorded in the SDK user's `~/.ssh/known_hosts`, including during unattended
|
|
177
|
+
setup or after SDK container recreation. Subsequent pairing preserves saved
|
|
178
|
+
keys and rejects a changed key at the same address and SSH port. After a device
|
|
179
|
+
reimage or key rotation, verify the replacement fingerprint before explicitly
|
|
180
|
+
updating that entry. For environments requiring verified trust before the first
|
|
181
|
+
connection, provision verified host keys in the SDK user's `known_hosts`.
|
|
182
|
+
|
|
160
183
|
Install Model Compiler from package metadata:
|
|
161
184
|
|
|
162
185
|
```bash
|
|
@@ -238,3 +261,13 @@ For command-specific prerequisites, see the generated [command reference](docs/s
|
|
|
238
261
|
## Support
|
|
239
262
|
|
|
240
263
|
For issues and feature requests, use the sima-cli GitHub repository or contact the SiMa.ai development team.
|
|
264
|
+
|
|
265
|
+
### Remote browser certificate help
|
|
266
|
+
|
|
267
|
+
SDK setup stages a verified public mkcert CA certificate alongside the SDK HTTPS
|
|
268
|
+
certificate. SDK images with browser certificate help can offer that file for
|
|
269
|
+
download on the sign-in page, with instructions for the browser computer.
|
|
270
|
+
Only the public certificate is staged; the mkcert private key stays on the host.
|
|
271
|
+
If the issuing CA cannot be verified, including a plain self-signed fallback,
|
|
272
|
+
no CA download is published. Re-running setup refreshes the staged file even
|
|
273
|
+
when the existing HTTPS certificate does not need to be reissued.
|
|
@@ -113,6 +113,20 @@ sima-cli --internal login
|
|
|
113
113
|
SIMA_CLI_INTERNAL=1 sima-cli install -v 2.1.2 tools/model-compiler/amd64
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
Full-system SWU updates prefer RAM-backed `/tmp` when available memory can
|
|
117
|
+
hold both the bundle and its extraction, plus a reserve of at least 512 MiB
|
|
118
|
+
or 10% of total RAM (whichever is larger). Otherwise they stage in `/data`,
|
|
119
|
+
with NVMe as a further fallback if `/data` lacks space. RAM staging is temporary;
|
|
120
|
+
interrupted updates retained there do not survive a reboot.
|
|
121
|
+
|
|
122
|
+
Public-mirror downloads, `models download`, and `neat artifacts` split files
|
|
123
|
+
of at least 64 MiB into four byte ranges when supported by the server. Models
|
|
124
|
+
and Neat artifacts share a limit of four active download connections across
|
|
125
|
+
files and ranges, including single-connection fallbacks. Ranges must match the
|
|
126
|
+
expected size and an object validator or the expected SHA-256 before the file is published. Models retain signed-URL refresh and
|
|
127
|
+
cancellation handling. Existing public-mirror partial downloads continue
|
|
128
|
+
sequentially; servers without range support use a single connection.
|
|
129
|
+
|
|
116
130
|
## Common Workflows
|
|
117
131
|
|
|
118
132
|
Set up SDK containers:
|
|
@@ -122,6 +136,14 @@ sima-cli sdk setup
|
|
|
122
136
|
sima-cli sdk neat
|
|
123
137
|
```
|
|
124
138
|
|
|
139
|
+
When pairing with `sdk setup --devkit`, a new DevKit host key is automatically
|
|
140
|
+
recorded in the SDK user's `~/.ssh/known_hosts`, including during unattended
|
|
141
|
+
setup or after SDK container recreation. Subsequent pairing preserves saved
|
|
142
|
+
keys and rejects a changed key at the same address and SSH port. After a device
|
|
143
|
+
reimage or key rotation, verify the replacement fingerprint before explicitly
|
|
144
|
+
updating that entry. For environments requiring verified trust before the first
|
|
145
|
+
connection, provision verified host keys in the SDK user's `known_hosts`.
|
|
146
|
+
|
|
125
147
|
Install Model Compiler from package metadata:
|
|
126
148
|
|
|
127
149
|
```bash
|
|
@@ -203,3 +225,13 @@ For command-specific prerequisites, see the generated [command reference](docs/s
|
|
|
203
225
|
## Support
|
|
204
226
|
|
|
205
227
|
For issues and feature requests, use the sima-cli GitHub repository or contact the SiMa.ai development team.
|
|
228
|
+
|
|
229
|
+
### Remote browser certificate help
|
|
230
|
+
|
|
231
|
+
SDK setup stages a verified public mkcert CA certificate alongside the SDK HTTPS
|
|
232
|
+
certificate. SDK images with browser certificate help can offer that file for
|
|
233
|
+
download on the sign-in page, with instructions for the browser computer.
|
|
234
|
+
Only the public certificate is staged; the mkcert private key stays on the host.
|
|
235
|
+
If the issuing CA cannot be verified, including a plain self-signed fallback,
|
|
236
|
+
no CA download is published. Re-running setup refreshes the staged file even
|
|
237
|
+
when the existing HTTPS certificate does not need to be reissued.
|
|
@@ -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.20"
|
|
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"
|
|
@@ -33,7 +33,8 @@ dependencies = [
|
|
|
33
33
|
"InquirerPy==0.3.4",
|
|
34
34
|
"tftpy==0.8.6",
|
|
35
35
|
"psutil==7.0.0",
|
|
36
|
-
"huggingface-hub>=
|
|
36
|
+
"huggingface-hub>=2.2.0,<3.0; python_version >= '3.10'",
|
|
37
|
+
"huggingface-hub>=0.34.0,<2.0; python_version < '3.10'",
|
|
37
38
|
"tabulate==0.9.0"
|
|
38
39
|
]
|
|
39
40
|
|
|
@@ -23,7 +23,8 @@ setup(
|
|
|
23
23
|
"InquirerPy>=0.3.4,<0.4",
|
|
24
24
|
"tftpy>=0.8.6,<0.9",
|
|
25
25
|
"psutil>=7.0.0,<8.0",
|
|
26
|
-
"huggingface-hub>=
|
|
26
|
+
"huggingface-hub>=2.2.0,<3.0; python_version >= '3.10'",
|
|
27
|
+
"huggingface-hub>=0.34.0,<2.0; python_version < '3.10'",
|
|
27
28
|
"rich>=13.7,<14.0"
|
|
28
29
|
],
|
|
29
30
|
entry_points={
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# sima_cli/__version__.py
|
|
2
|
-
__version__ = "2.1.
|
|
2
|
+
__version__ = "2.1.20"
|
|
@@ -14,7 +14,7 @@ from sima_cli.update.swu import handle_update
|
|
|
14
14
|
from sima_cli.model_zoo.model import list_models, download_model, describe_model
|
|
15
15
|
from sima_cli.app_zoo.app import list_apps, download_app, describe_app
|
|
16
16
|
from sima_cli.utils.config_loader import internal_resource_exists
|
|
17
|
-
from sima_cli.mla.meminfo import monitor_simaai_mem_chart
|
|
17
|
+
from sima_cli.mla.meminfo import MemoryTelemetryError, monitor_simaai_mem_chart
|
|
18
18
|
from sima_cli.__version__ import __version__
|
|
19
19
|
from sima_cli.utils.config import CONFIG_PATH
|
|
20
20
|
from sima_cli.install.optiview import install_optiview
|
|
@@ -42,6 +42,7 @@ from sima_cli.upgrade.selfupdate import register_selfupdate_command
|
|
|
42
42
|
from sima_cli.playbooks import register_playbook_commands
|
|
43
43
|
from sima_cli.vulcan import register_vulcan_commands
|
|
44
44
|
from sima_cli.models import register_models_commands
|
|
45
|
+
from sima_cli.peripherals import register_peripherals_commands
|
|
45
46
|
from sima_cli.cloudex import register_cloudex_commands
|
|
46
47
|
from sima_cli.vulcan.commands import (
|
|
47
48
|
ENV_METAVAR,
|
|
@@ -105,6 +106,13 @@ def _rerun_current_command() -> None:
|
|
|
105
106
|
os.execvpe(sys.executable, cmd, env)
|
|
106
107
|
|
|
107
108
|
|
|
109
|
+
# Command groups whose stdout is a machine-readable document: root diagnostics go to stderr for them.
|
|
110
|
+
QUIET_STDOUT_SUBCOMMANDS = ("models", "peripherals")
|
|
111
|
+
# Command groups that only read the DevKit they run on: no PyPI update check and no Artifactory
|
|
112
|
+
# gate before them, so they work offline and never prompt or exit early.
|
|
113
|
+
LOCAL_ONLY_SUBCOMMANDS = ("peripherals",)
|
|
114
|
+
|
|
115
|
+
|
|
108
116
|
def _initialize_main_context(ctx, internal, yes):
|
|
109
117
|
_configure_stdio_errors()
|
|
110
118
|
auto_accept_update = yes or _update_auto_confirm_requested(sys.argv)
|
|
@@ -112,7 +120,8 @@ def _initialize_main_context(ctx, internal, yes):
|
|
|
112
120
|
if auto_accept_update:
|
|
113
121
|
os.environ["SIMA_CLI_AUTO_ACCEPT_UPDATE"] = "1"
|
|
114
122
|
try:
|
|
115
|
-
if
|
|
123
|
+
if (ctx.invoked_subcommand not in LOCAL_ONLY_SUBCOMMANDS
|
|
124
|
+
and check_for_update('sima-cli') and _should_rerun_after_update(sys.argv)):
|
|
116
125
|
_rerun_current_command()
|
|
117
126
|
finally:
|
|
118
127
|
if auto_accept_update:
|
|
@@ -128,14 +137,15 @@ def _initialize_main_context(ctx, internal, yes):
|
|
|
128
137
|
if not internal:
|
|
129
138
|
internal = os.getenv("SIMA_CLI_INTERNAL", "0") in ("1", "true", "yes")
|
|
130
139
|
|
|
131
|
-
|
|
140
|
+
local_only = ctx.invoked_subcommand in LOCAL_ONLY_SUBCOMMANDS
|
|
141
|
+
defer_artifactory_access = local_only or ctx.invoked_subcommand == "update" or ctx.meta.get("daily_netboot", False)
|
|
132
142
|
if internal and not internal_resource_exists() and not defer_artifactory_access:
|
|
133
143
|
click.echo("❌ You have specified -i or --internal argument to access internal resources, but you do not have an internal resource map configured.")
|
|
134
144
|
click.echo("Refer to the confluence page to find out how to configure internal resource map.")
|
|
135
145
|
exit(0)
|
|
136
146
|
|
|
137
147
|
internal_reachable = True
|
|
138
|
-
if internal:
|
|
148
|
+
if internal and not local_only:
|
|
139
149
|
internal_reachable = check_artifactory_reachability()
|
|
140
150
|
|
|
141
151
|
if internal and not internal_reachable and not defer_artifactory_access and not _allows_external_prerelease_fallback(sys.argv):
|
|
@@ -170,7 +180,7 @@ class InspectionAwareGroup(click.Group):
|
|
|
170
180
|
_command_name_from_argv(['sima-cli'] + options) == 'bootimg'
|
|
171
181
|
and any(arg in ('--netboot', '-n', '--autoflash', '-a') for arg in options)
|
|
172
182
|
)
|
|
173
|
-
if _command_name_from_argv(['sima-cli'] + options) == 'update' and '--inspect' in options:
|
|
183
|
+
if _command_name_from_argv(['sima-cli'] + options) == 'update' and any(arg in ('--inspect', '--swap', '--reset-overlay') for arg in options):
|
|
174
184
|
ctx.meta['update_inspect'] = True
|
|
175
185
|
return super().parse_args(ctx, args)
|
|
176
186
|
|
|
@@ -198,11 +208,11 @@ def main(ctx, internal, yes):
|
|
|
198
208
|
ctx.ensure_object(dict)
|
|
199
209
|
ctx.obj.update(internal=internal, yes=yes)
|
|
200
210
|
return
|
|
201
|
-
# Model Registry JSON must remain parseable on stdout. Route root-level
|
|
202
|
-
# update and environment diagnostics to stderr for
|
|
211
|
+
# Model Registry and peripherals JSON must remain parseable on stdout. Route root-level
|
|
212
|
+
# update and environment diagnostics to stderr for these command groups.
|
|
203
213
|
output_context = (
|
|
204
214
|
redirect_stdout(sys.stderr)
|
|
205
|
-
if ctx.invoked_subcommand
|
|
215
|
+
if ctx.invoked_subcommand in QUIET_STDOUT_SUBCOMMANDS
|
|
206
216
|
else nullcontext()
|
|
207
217
|
)
|
|
208
218
|
with output_context:
|
|
@@ -216,6 +226,7 @@ register_sdk_commands(main)
|
|
|
216
226
|
register_playbook_commands(main)
|
|
217
227
|
register_vulcan_commands(main)
|
|
218
228
|
register_models_commands(main)
|
|
229
|
+
register_peripherals_commands(main)
|
|
219
230
|
register_cloudex_commands(main)
|
|
220
231
|
|
|
221
232
|
|
|
@@ -352,14 +363,16 @@ def download(ctx, url, dest):
|
|
|
352
363
|
"--dryrun",
|
|
353
364
|
is_flag=True,
|
|
354
365
|
default=False,
|
|
355
|
-
help="For eLxr
|
|
366
|
+
help="For eLxr, preview an update, slot swap, or overlay reset without making changes."
|
|
356
367
|
)
|
|
357
368
|
@click.option("--inspect", "inspect_state", is_flag=True, help="Show eLxr 3.0+ A/B slots and overlay customizations without updating (local or --ip).")
|
|
369
|
+
@click.option("--reset-overlay", is_flag=True, help="Discard local overlay software and settings without updating firmware; reboot to use the clean system (eLxr 3.0+).")
|
|
370
|
+
@click.option("--swap", is_flag=True, help="Select the available inactive eLxr 3.0+ slot for next boot; use --reboot to restart now.")
|
|
358
371
|
@click.option("--verbose", is_flag=True, help="Show detailed overlay file categories during eLxr 3.0+ inspection or update.")
|
|
359
372
|
@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+).")
|
|
360
|
-
@click.option("--reboot", is_flag=True, help="Reboot after successful eLxr 3.0+ installation
|
|
373
|
+
@click.option("--reboot", is_flag=True, help="Reboot after successful eLxr 3.0+ installation, slot selection, or overlay reset.")
|
|
361
374
|
@click.pass_context
|
|
362
|
-
def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force, troot_only, dryrun, inspect_state, verbose, reboot, signing_cert):
|
|
375
|
+
def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force, troot_only, dryrun, inspect_state, verbose, reboot, signing_cert, swap, reset_overlay):
|
|
363
376
|
"""
|
|
364
377
|
Update a SiMa DevKit, remote device, or Linux PCIe host.
|
|
365
378
|
|
|
@@ -370,9 +383,23 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
370
383
|
|
|
371
384
|
eLxr 3.0+ uses signed full-system SWU bundles. Use --inspect for A/B
|
|
372
385
|
state, --ip for remote updates, and --reboot to reboot after installation.
|
|
386
|
+
|
|
387
|
+
Use --reset-overlay to discard local software and settings without updating
|
|
388
|
+
firmware or switching slots. Save needed files first; the saved inventory
|
|
389
|
+
contains metadata only. Reboot promptly, or add --reboot. Use --dryrun to preview.
|
|
390
|
+
|
|
391
|
+
Use --swap to select the valid inactive slot without downloading an update.
|
|
392
|
+
The current and target versions are shown before confirmation; --yes skips
|
|
393
|
+
confirmation. Reboot separately to apply, or add --reboot to restart now.
|
|
394
|
+
Shared data and overlay customizations persist and can override files in
|
|
395
|
+
the selected version. Blank, invalid, pending, and unknown states are refused.
|
|
396
|
+
|
|
397
|
+
Examples: sima-cli update --swap; sima-cli update --ip 192.168.6.5 --swap --reboot
|
|
398
|
+
|
|
373
399
|
The verification certificate is downloaded for the selected channel when configured; use
|
|
374
400
|
--signing-cert URL_OR_FILE for an image signed with your own certificate.
|
|
375
|
-
|
|
401
|
+
For official eLxr 3.0+ releases, specify -v 3.0.0 (or -v 3.0) to
|
|
402
|
+
download the SWU from docs.sima.ai using your developer-portal login.
|
|
376
403
|
|
|
377
404
|
Internal mode warns that pre-release software may be unstable and is used
|
|
378
405
|
at your own risk. On eLxr 3.0+, unavailable Artifactory access falls back
|
|
@@ -479,6 +506,8 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
479
506
|
dryrun
|
|
480
507
|
or troot_only
|
|
481
508
|
or inspect_state
|
|
509
|
+
or swap
|
|
510
|
+
or reset_overlay
|
|
482
511
|
or verbose
|
|
483
512
|
or reboot
|
|
484
513
|
or force
|
|
@@ -496,6 +525,10 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
496
525
|
return
|
|
497
526
|
|
|
498
527
|
is_elxr = is_devkit_running_elxr()
|
|
528
|
+
if reset_overlay and (inspect_state or swap or version_or_url or force or troot_only or signing_cert is not None or flavor != "auto"):
|
|
529
|
+
raise click.UsageError("--reset-overlay cannot be combined with --inspect, --swap, or installation options.")
|
|
530
|
+
if swap and (inspect_state or version_or_url or force or troot_only or signing_cert is not None or flavor != "auto"):
|
|
531
|
+
raise click.UsageError("--swap cannot be combined with --inspect or installation options.")
|
|
499
532
|
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'):
|
|
500
533
|
raise click.UsageError("--inspect cannot be combined with installation options.")
|
|
501
534
|
try:
|
|
@@ -503,7 +536,7 @@ def update(ctx, version_or_url, version_option, ip, yes, passwd, flavor, force,
|
|
|
503
536
|
internal=ctx.obj.get("internal", False),
|
|
504
537
|
auto_confirm=yes or ctx.obj.get("yes", False), dryrun=dryrun,
|
|
505
538
|
signing_cert=signing_cert, reboot=reboot, inspect=inspect_state, force=force,
|
|
506
|
-
troot_only=troot_only, flavor=flavor, local_elxr=is_elxr, verbose=verbose):
|
|
539
|
+
troot_only=troot_only, flavor=flavor, local_elxr=is_elxr, verbose=verbose, swap=swap, reset_overlay=reset_overlay):
|
|
507
540
|
return
|
|
508
541
|
except (click.ClickException, click.Abort):
|
|
509
542
|
raise
|
|
@@ -632,8 +665,10 @@ def mla(ctx):
|
|
|
632
665
|
@click.pass_context
|
|
633
666
|
def show_mla_memory_usage(ctx):
|
|
634
667
|
"""Show MLA Memory usage overtime."""
|
|
635
|
-
|
|
636
|
-
|
|
668
|
+
try:
|
|
669
|
+
monitor_simaai_mem_chart()
|
|
670
|
+
except MemoryTelemetryError as exc:
|
|
671
|
+
raise click.ClickException(str(exc)) from exc
|
|
637
672
|
|
|
638
673
|
|
|
639
674
|
# ----------------------
|
|
@@ -789,6 +824,8 @@ def bootimg_cmd(ctx, version, boardtype, netboot, images, delete_cache, devkit_i
|
|
|
789
824
|
else:
|
|
790
825
|
write_image(version, boardtype, fwtype, internal, flavor='headless', recovery=recovery)
|
|
791
826
|
click.echo("✅ Boot image successfully written.")
|
|
827
|
+
except click.ClickException:
|
|
828
|
+
raise
|
|
792
829
|
except Exception as e:
|
|
793
830
|
click.echo(f"❌ Failed to write boot image: {e}", err=True)
|
|
794
831
|
ctx.exit(1)
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
import os
|
|
8
8
|
import shutil
|
|
9
9
|
import subprocess
|
|
10
|
+
import sys
|
|
10
11
|
import time
|
|
11
12
|
import uuid
|
|
12
13
|
from pathlib import Path
|
|
@@ -17,6 +18,8 @@ import requests
|
|
|
17
18
|
|
|
18
19
|
DEFAULT_ROOT = Path.home() / ".sima-cli" / "cloudex"
|
|
19
20
|
FORWARDER_NAME = "kerrigan-p2p-forwarder"
|
|
21
|
+
MACOS_TUNNEL_PATH = "/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/usr/local/bin"
|
|
22
|
+
MACOS_WIREGUARD_INSTALL = "Install WireGuard CLI support with: brew install wireguard-tools"
|
|
20
23
|
CONNECTION_SETUP_TIMEOUT_SECONDS = 60
|
|
21
24
|
DIAGNOSTIC_LOG_LINES = 50
|
|
22
25
|
SAFE_FORWARDER_STAGES = {
|
|
@@ -68,8 +71,8 @@ def decode_key(value):
|
|
|
68
71
|
raise CloudExError("--key contains an invalid value")
|
|
69
72
|
if len(profile["allocation_secret"]) < 32:
|
|
70
73
|
raise CloudExError("--key contains an invalid allocation secret")
|
|
71
|
-
if profile["environment"]
|
|
72
|
-
raise CloudExError("CloudEx
|
|
74
|
+
if profile["environment"] not in {"vulcan-staging", "vulcan-production"}:
|
|
75
|
+
raise CloudExError("CloudEx supports only vulcan-staging or vulcan-production allocation keys")
|
|
73
76
|
return profile
|
|
74
77
|
|
|
75
78
|
|
|
@@ -393,6 +396,9 @@ def _forwarder_failure(log_path):
|
|
|
393
396
|
"start wireguard-go": "the WireGuard interface could not start",
|
|
394
397
|
"invalid session config": "the native forwarder rejected the session configuration",
|
|
395
398
|
}
|
|
399
|
+
if sys.platform == "darwin":
|
|
400
|
+
for needle in ("wireguard is unavailable", "start wireguard-go"):
|
|
401
|
+
mappings[needle] += ". " + MACOS_WIREGUARD_INSTALL
|
|
396
402
|
for needle, message in mappings.items():
|
|
397
403
|
if needle in detail:
|
|
398
404
|
return message + suffix
|
|
@@ -552,22 +558,61 @@ def _reconcile_existing_connection(profile, store, progress):
|
|
|
552
558
|
return None
|
|
553
559
|
|
|
554
560
|
|
|
561
|
+
def _forwarder_sudo_prefix(progress=None):
|
|
562
|
+
"""Install missing macOS tools as the user, then expose them through sudo."""
|
|
563
|
+
prefix = ["sudo", "-n", "--"]
|
|
564
|
+
if sys.platform != "darwin":
|
|
565
|
+
return prefix
|
|
566
|
+
missing = [name for name in ("wg", "wireguard-go")
|
|
567
|
+
if shutil.which(name, path=MACOS_TUNNEL_PATH) is None]
|
|
568
|
+
if missing:
|
|
569
|
+
brew = shutil.which("brew", path=MACOS_TUNNEL_PATH)
|
|
570
|
+
if brew is None:
|
|
571
|
+
raise CloudExError(
|
|
572
|
+
"CloudEx requires {} on macOS. Install Homebrew, then retry CloudEx; "
|
|
573
|
+
"missing WireGuard dependencies will be installed automatically.".format(
|
|
574
|
+
", ".join(missing)
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
packages = ["wireguard-tools" if name == "wg" else name for name in missing]
|
|
578
|
+
if progress:
|
|
579
|
+
progress.update("Installing missing WireGuard dependencies with Homebrew")
|
|
580
|
+
environment = dict(os.environ, PATH=MACOS_TUNNEL_PATH)
|
|
581
|
+
try:
|
|
582
|
+
result = subprocess.run(
|
|
583
|
+
[brew, "install", *packages],
|
|
584
|
+
env=environment, stdin=subprocess.DEVNULL,
|
|
585
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=600,
|
|
586
|
+
)
|
|
587
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
588
|
+
raise CloudExError(
|
|
589
|
+
"Could not install WireGuard dependencies with Homebrew. " + MACOS_WIREGUARD_INSTALL
|
|
590
|
+
) from exc
|
|
591
|
+
if result.returncode:
|
|
592
|
+
detail = (result.stderr or result.stdout or "Homebrew failed").strip()
|
|
593
|
+
raise CloudExError("WireGuard dependency installation failed: " + detail[-1024:])
|
|
594
|
+
missing = [name for name in ("wg", "wireguard-go")
|
|
595
|
+
if shutil.which(name, path=MACOS_TUNNEL_PATH) is None]
|
|
596
|
+
if missing:
|
|
597
|
+
raise CloudExError(
|
|
598
|
+
"Homebrew completed but CloudEx still cannot find: {}. {}".format(
|
|
599
|
+
", ".join(missing), MACOS_WIREGUARD_INSTALL
|
|
600
|
+
)
|
|
601
|
+
)
|
|
602
|
+
return prefix + ["/usr/bin/env", "PATH=" + MACOS_TUNNEL_PATH]
|
|
603
|
+
|
|
604
|
+
|
|
555
605
|
def connect(profile, store, progress, attempts=3, transport="auto"):
|
|
556
606
|
"""Create a session and return its durable local record."""
|
|
557
|
-
|
|
558
|
-
forwarder = find_forwarder()
|
|
559
|
-
except CloudExError:
|
|
560
|
-
# Import lazily: the installer reuses this module's privilege and error
|
|
561
|
-
# handling, while ordinary CloudEx commands should remain quick to load.
|
|
562
|
-
from .installer import DEFAULT_BRANCH, install_forwarder
|
|
607
|
+
from .installer import ensure_forwarder
|
|
563
608
|
|
|
564
|
-
progress.update("CloudEx forwarder is missing; installing from {}".format(DEFAULT_BRANCH))
|
|
565
|
-
forwarder = install_forwarder(DEFAULT_BRANCH, progress=progress)["path"]
|
|
566
609
|
authorize_admin()
|
|
567
610
|
api = CloudExAPI.from_profile(profile)
|
|
568
611
|
existing = _reconcile_existing_connection(profile, store, progress)
|
|
569
612
|
if existing:
|
|
570
613
|
return existing
|
|
614
|
+
sudo_prefix = _forwarder_sudo_prefix(progress=progress)
|
|
615
|
+
forwarder = ensure_forwarder(progress=progress)
|
|
571
616
|
choices = (["p2p"] * attempts + ["routed"] if transport == "auto"
|
|
572
617
|
else ["routed"] if transport == "turn" else ["p2p"] * attempts)
|
|
573
618
|
last_error = None
|
|
@@ -616,8 +661,8 @@ def connect(profile, store, progress, attempts=3, transport="auto"):
|
|
|
616
661
|
config_path.chmod(0o600)
|
|
617
662
|
with log_path.open("x", encoding="utf-8") as log:
|
|
618
663
|
process = subprocess.Popen(
|
|
619
|
-
[
|
|
620
|
-
|
|
664
|
+
sudo_prefix + [
|
|
665
|
+
str(forwarder), "session", "--role", "user",
|
|
621
666
|
"--config", str(config_path), "--report", str(report_path),
|
|
622
667
|
],
|
|
623
668
|
stdin=subprocess.DEVNULL,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Install the native CloudEx forwarder from Kerrigan's Vulcan artifacts."""
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
|
+
import os
|
|
4
5
|
import platform
|
|
5
6
|
import re
|
|
6
7
|
import subprocess
|
|
@@ -99,7 +100,30 @@ def _download_resource(client, base_url, resource, expected_sha256, destination)
|
|
|
99
100
|
return path
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
def
|
|
103
|
+
def ensure_forwarder(progress=None):
|
|
104
|
+
"""Resolve the current published binary before starting a new tunnel."""
|
|
105
|
+
try:
|
|
106
|
+
installed = find_forwarder()
|
|
107
|
+
except CloudExError:
|
|
108
|
+
installed = None
|
|
109
|
+
else:
|
|
110
|
+
if os.environ.get("SIMA_CLOUDEX_FORWARDER"):
|
|
111
|
+
# Preserve an existing controlled binary, but allow the normal
|
|
112
|
+
# installer to bootstrap a missing controlled installation.
|
|
113
|
+
return installed
|
|
114
|
+
return install_forwarder(
|
|
115
|
+
DEFAULT_BRANCH, progress=progress, current_path=installed
|
|
116
|
+
)["path"]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _matches_binary(path, checksum):
|
|
120
|
+
try:
|
|
121
|
+
return hashlib.sha256(Path(path).read_bytes()).hexdigest() == checksum.lower()
|
|
122
|
+
except OSError:
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def install_forwarder(branch=DEFAULT_BRANCH, progress=None, client=None, current_path=None):
|
|
103
127
|
"""Install or update the forwarder and return artifact/install details."""
|
|
104
128
|
branch = _validate_branch(branch)
|
|
105
129
|
variant = _platform_variant()
|
|
@@ -119,6 +143,12 @@ def install_forwarder(branch=DEFAULT_BRANCH, progress=None, client=None):
|
|
|
119
143
|
metadata, resources, script_resource = _checked_metadata(
|
|
120
144
|
client, result.metadata_url, variant
|
|
121
145
|
)
|
|
146
|
+
expected_sha256 = metadata["resources-checksum"][resources[0]]
|
|
147
|
+
if current_path is not None and _matches_binary(current_path, expected_sha256):
|
|
148
|
+
if progress:
|
|
149
|
+
progress.update("CloudEx forwarder is up to date")
|
|
150
|
+
return {"path": current_path, "branch": branch,
|
|
151
|
+
"version": metadata["version"], "artifact": result.resolved_spec}
|
|
122
152
|
base_url = result.metadata_url.rsplit("/", 1)[0]
|
|
123
153
|
with tempfile.TemporaryDirectory(prefix="sima-cloudex-forwarder-") as temporary:
|
|
124
154
|
root = Path(temporary)
|
|
@@ -156,6 +186,8 @@ def install_forwarder(branch=DEFAULT_BRANCH, progress=None, client=None):
|
|
|
156
186
|
raise CloudExError("CloudEx forwarder installation failed: {}".format(detail[:1024]))
|
|
157
187
|
|
|
158
188
|
installed = find_forwarder()
|
|
189
|
+
if current_path is not None and not _matches_binary(installed, expected_sha256):
|
|
190
|
+
raise CloudExError("CloudEx forwarder update did not replace the active binary; check PATH")
|
|
159
191
|
return {
|
|
160
192
|
"path": installed,
|
|
161
193
|
"branch": branch,
|
|
@@ -5,6 +5,7 @@ import subprocess
|
|
|
5
5
|
import json
|
|
6
6
|
import time
|
|
7
7
|
import re
|
|
8
|
+
import shutil
|
|
8
9
|
import psutil
|
|
9
10
|
from typing import List, Dict
|
|
10
11
|
from rich.console import Console
|
|
@@ -51,6 +52,9 @@ def get_sima_devices_from_arp():
|
|
|
51
52
|
try:
|
|
52
53
|
output = subprocess.check_output(["ip", "neigh", "show"], text=True)
|
|
53
54
|
except FileNotFoundError:
|
|
55
|
+
if not shutil.which("arp"):
|
|
56
|
+
console.print("[yellow]ARP lookup unavailable: install iproute2 (ip) or net-tools (arp); continuing with multicast discovery.[/yellow]")
|
|
57
|
+
return []
|
|
54
58
|
output = subprocess.check_output(["arp", "-n"], text=True)
|
|
55
59
|
pattern = re.compile(
|
|
56
60
|
r"(?P<ip>\d+\.\d+\.\d+\.\d+).*?(?P<mac>(?:[0-9a-f]{1,2}:){5}[0-9a-f]{1,2})",
|
|
@@ -11,9 +11,41 @@ import re
|
|
|
11
11
|
import time
|
|
12
12
|
import shlex
|
|
13
13
|
import subprocess
|
|
14
|
+
import shutil
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
import click
|
|
14
18
|
from sima_cli.utils.env import get_environment_type
|
|
15
19
|
from typing import Optional, Dict
|
|
16
20
|
|
|
21
|
+
def ensure_discovery_tools():
|
|
22
|
+
"""Provision required Linux discovery tools only after an interactive opt-in."""
|
|
23
|
+
if shutil.which('ip'):
|
|
24
|
+
return
|
|
25
|
+
guidance = (
|
|
26
|
+
"Device discovery needs 'ip' (package iproute2). "
|
|
27
|
+
"On Ubuntu/Debian run: sudo apt-get update && sudo apt-get install -y iproute2 ethtool. "
|
|
28
|
+
"For netboot, pass --devkit-ip <IPv4> to bypass auto-discovery."
|
|
29
|
+
)
|
|
30
|
+
if not shutil.which('apt-get') or not sys.stdin.isatty():
|
|
31
|
+
raise click.ClickException(guidance)
|
|
32
|
+
packages = ['iproute2']
|
|
33
|
+
if not shutil.which('ethtool'):
|
|
34
|
+
packages.append('ethtool')
|
|
35
|
+
if not click.confirm('Device discovery needs host packages. Install ' + ' '.join(packages) + '?', default=False):
|
|
36
|
+
raise click.ClickException(guidance)
|
|
37
|
+
prefix = [] if os.geteuid() == 0 else ['sudo']
|
|
38
|
+
if prefix and not shutil.which('sudo'):
|
|
39
|
+
raise click.ClickException(guidance + ' sudo is unavailable; ask an administrator to install them.')
|
|
40
|
+
try:
|
|
41
|
+
subprocess.run(prefix + ['apt-get', 'update'], check=True)
|
|
42
|
+
subprocess.run(prefix + ['apt-get', 'install', '-y', '--no-install-recommends'] + packages, check=True)
|
|
43
|
+
except (OSError, subprocess.CalledProcessError) as error:
|
|
44
|
+
raise click.ClickException(f'Device discovery package installation failed: {error}. {guidance}') from error
|
|
45
|
+
if not shutil.which('ip'):
|
|
46
|
+
raise click.ClickException(guidance)
|
|
47
|
+
|
|
48
|
+
|
|
17
49
|
def _run(cmd: str, check=False, capture=True) -> subprocess.CompletedProcess:
|
|
18
50
|
return subprocess.run(
|
|
19
51
|
shlex.split(cmd),
|
|
@@ -24,7 +56,7 @@ def _run(cmd: str, check=False, capture=True) -> subprocess.CompletedProcess:
|
|
|
24
56
|
)
|
|
25
57
|
|
|
26
58
|
def _has_nmcli() -> bool:
|
|
27
|
-
return
|
|
59
|
+
return shutil.which("nmcli") is not None
|
|
28
60
|
|
|
29
61
|
|
|
30
62
|
def _iface_has_ipv4(iface: str) -> bool:
|
|
@@ -41,7 +73,7 @@ def _get_mac(iface: str) -> str:
|
|
|
41
73
|
def _is_usb_ethernet(iface: str) -> bool:
|
|
42
74
|
"""Return True if this interface is a USB Ethernet device."""
|
|
43
75
|
dev_path = f"/sys/class/net/{iface}/device"
|
|
44
|
-
if not os.path.exists(dev_path):
|
|
76
|
+
if not os.path.exists(dev_path) or not shutil.which("udevadm"):
|
|
45
77
|
return False
|
|
46
78
|
|
|
47
79
|
cp = _run(f"udevadm info -q property -p {dev_path}", capture=True)
|
|
@@ -63,7 +95,7 @@ def _usb_attrs(iface: str) -> Dict[str, str]:
|
|
|
63
95
|
Returns possibly empty dict.
|
|
64
96
|
"""
|
|
65
97
|
dev_path = f"/sys/class/net/{iface}/device"
|
|
66
|
-
if not os.path.exists(dev_path):
|
|
98
|
+
if not os.path.exists(dev_path) or not shutil.which("udevadm"):
|
|
67
99
|
return {}
|
|
68
100
|
|
|
69
101
|
cp = _run(f"udevadm info -q property -p {dev_path}", capture=True)
|
|
@@ -100,6 +132,8 @@ def _usb_attrs(iface: str) -> Dict[str, str]:
|
|
|
100
132
|
|
|
101
133
|
|
|
102
134
|
def _driver_info(iface: str) -> Dict[str, str]:
|
|
135
|
+
if not shutil.which("ethtool"):
|
|
136
|
+
return {"driver": "", "bus-info": "", "firmware": ""}
|
|
103
137
|
cp = _run(f"ethtool -i {iface}", capture=True)
|
|
104
138
|
info = {}
|
|
105
139
|
for line in cp.stdout.splitlines():
|
|
@@ -227,6 +261,7 @@ def suggest_and_switch_to_linklocal():
|
|
|
227
261
|
envtype, env_subtype = get_environment_type()
|
|
228
262
|
|
|
229
263
|
if envtype == 'host' and env_subtype == 'linux':
|
|
264
|
+
ensure_discovery_tools()
|
|
230
265
|
cp = _run("ip -o link", capture=True)
|
|
231
266
|
ifaces = []
|
|
232
267
|
for line in cp.stdout.splitlines():
|