sima-cli 2.1.5__tar.gz → 2.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {sima_cli-2.1.5/sima_cli.egg-info → sima_cli-2.1.6}/PKG-INFO +34 -2
  2. {sima_cli-2.1.5 → sima_cli-2.1.6}/README.md +33 -1
  3. {sima_cli-2.1.5 → sima_cli-2.1.6}/pyproject.toml +1 -1
  4. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/__version__.py +1 -1
  5. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/cli.py +2 -0
  6. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/commands.py +14 -1
  7. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/install.py +20 -7
  8. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/neat.py +28 -10
  9. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/utils.py +30 -5
  10. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/elxr.py +116 -10
  11. sima_cli-2.1.6/sima_cli/vulcan/__init__.py +3 -0
  12. sima_cli-2.1.6/sima_cli/vulcan/artifacts.py +332 -0
  13. sima_cli-2.1.6/sima_cli/vulcan/commands.py +115 -0
  14. {sima_cli-2.1.5 → sima_cli-2.1.6/sima_cli.egg-info}/PKG-INFO +34 -2
  15. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli.egg-info/SOURCES.txt +5 -1
  16. sima_cli-2.1.6/tests/unit/test_elxr_update.py +408 -0
  17. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_sdk_image_detection.py +166 -5
  18. sima_cli-2.1.6/tests/unit/test_vulcan.py +180 -0
  19. sima_cli-2.1.5/tests/unit/test_elxr_update.py +0 -110
  20. {sima_cli-2.1.5 → sima_cli-2.1.6}/LICENSE +0 -0
  21. {sima_cli-2.1.5 → sima_cli-2.1.6}/MANIFEST.in +0 -0
  22. {sima_cli-2.1.5 → sima_cli-2.1.6}/requirements.txt +0 -0
  23. {sima_cli-2.1.5 → sima_cli-2.1.6}/setup.cfg +0 -0
  24. {sima_cli-2.1.5 → sima_cli-2.1.6}/setup.py +0 -0
  25. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/__init__.py +0 -0
  26. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/__main__.py +0 -0
  27. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/app_zoo/__init__.py +0 -0
  28. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/app_zoo/app.py +0 -0
  29. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/app_zoo/commands.py +0 -0
  30. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/auth/__init__.py +0 -0
  31. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/auth/auth0.py +0 -0
  32. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/auth/devportal.py +0 -0
  33. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/auth/login.py +0 -0
  34. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/auth/oauth.py +0 -0
  35. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/data/__init__.py +0 -0
  36. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/data/resources_internal.yaml +0 -0
  37. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/data/resources_public.yaml +0 -0
  38. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/deploy_only/__init__.py +0 -0
  39. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/deploy_only/device/__init__.py +0 -0
  40. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/deploy_only/device/commands.py +0 -0
  41. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/deploy_only/mpk/__init__.py +0 -0
  42. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/deploy_only/mpk/commands.py +0 -0
  43. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/discover/__init__.py +0 -0
  44. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/discover/discover.py +0 -0
  45. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/discover/linuxll.py +0 -0
  46. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/download/__init__.py +0 -0
  47. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/download/downloader.py +0 -0
  48. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/__init__.py +0 -0
  49. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/github_assets.py +0 -0
  50. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/hostdriver.py +0 -0
  51. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/metadata_info.py +0 -0
  52. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/metadata_installer.py +0 -0
  53. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/metadata_validator.py +0 -0
  54. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/optiview.py +0 -0
  55. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/palette.py +0 -0
  56. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/install/registry.py +0 -0
  57. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/mla/__init__.py +0 -0
  58. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/mla/meminfo.py +0 -0
  59. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/model_zoo/__init__.py +0 -0
  60. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/model_zoo/model.py +0 -0
  61. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/network/__init__.py +0 -0
  62. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/network/network.py +0 -0
  63. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/playbooks/__init__.py +0 -0
  64. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/playbooks/commands.py +0 -0
  65. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/playbooks/manager.py +0 -0
  66. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/__init__.py +0 -0
  67. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/cmdexec.py +0 -0
  68. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/config.py +0 -0
  69. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/linux_shared_network.py +0 -0
  70. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/preinstall.py +0 -0
  71. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/requirements.json +0 -0
  72. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/script.py +0 -0
  73. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/stop.py +0 -0
  74. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/sdk/uninstall.py +0 -0
  75. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/serial/__init__.py +0 -0
  76. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/serial/serial.py +0 -0
  77. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/storage/__init__.py +0 -0
  78. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/storage/nvme.py +0 -0
  79. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/storage/sdcard.py +0 -0
  80. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/__init__.py +0 -0
  81. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/bmaptool.py +0 -0
  82. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/bootimg.py +0 -0
  83. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/cleanlog.py +0 -0
  84. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/local.py +0 -0
  85. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/netboot.py +0 -0
  86. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/query.py +0 -0
  87. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/remote.py +0 -0
  88. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/update/updater.py +0 -0
  89. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/upgrade/__init__.py +0 -0
  90. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/upgrade/selfupdate.py +0 -0
  91. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/__init__.py +0 -0
  92. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/api_common.py +0 -0
  93. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/artifactory.py +0 -0
  94. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/common.py +0 -0
  95. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/config.py +0 -0
  96. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/config_loader.py +0 -0
  97. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/container_registries.py +0 -0
  98. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/device_api.py +0 -0
  99. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/disk.py +0 -0
  100. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/docker.py +0 -0
  101. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/env.py +0 -0
  102. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/errors.py +0 -0
  103. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/mpk_api.py +0 -0
  104. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/net.py +0 -0
  105. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/network.py +0 -0
  106. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/pcie.py +0 -0
  107. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/pkg_update_check.py +0 -0
  108. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/serializers.py +0 -0
  109. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/services.py +0 -0
  110. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli/utils/tag.py +0 -0
  111. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli.egg-info/dependency_links.txt +0 -0
  112. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli.egg-info/entry_points.txt +0 -0
  113. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli.egg-info/requires.txt +0 -0
  114. {sima_cli-2.1.5 → sima_cli-2.1.6}/sima_cli.egg-info/top_level.txt +0 -0
  115. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/__init__.py +0 -0
  116. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/e2e/__init__.py +0 -0
  117. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/__init__.py +0 -0
  118. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_app_zoo.py +0 -0
  119. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_auth.py +0 -0
  120. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_cli.py +0 -0
  121. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_cli_stdio.py +0 -0
  122. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_download.py +0 -0
  123. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_firmware.py +0 -0
  124. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_install_stub.py +0 -0
  125. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_model_zoo.py +0 -0
  126. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_pkg_update_check.py +0 -0
  127. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_sdk_preinstall.py +0 -0
  128. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_sdk_uninstall.py +0 -0
  129. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_selfupdate.py +0 -0
  130. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_skills_commands.py +0 -0
  131. {sima_cli-2.1.5 → sima_cli-2.1.6}/tests/unit/test_skills_manager.py +0 -0
  132. {sima_cli-2.1.5 → sima_cli-2.1.6}/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.5
3
+ Version: 2.1.6
4
4
  Summary: CLI tool for SiMa Developer Portal to download models, firmware, and apps.
5
5
  Home-page: https://developer.sima.ai/
6
6
  Author: SiMa.ai
@@ -152,6 +152,34 @@ sima-cli download <URL> [-d DEST]
152
152
 
153
153
  ---
154
154
 
155
+ ## 🔥 Vulcan Artifacts
156
+
157
+ ```bash
158
+ sima-cli vulcan download --env production core main
159
+ ```
160
+
161
+ - Downloads tested artifacts from Vulcan artifact hosting.
162
+ - Environment URLs:
163
+ - `dev`: `https://artifacts.neat.paconsultings.com`
164
+ - `staging`: `https://artifacts.stg.neat.sima.ai`
165
+ - `production`: `https://artifacts.neat.sima.ai`
166
+ - `staging` and `production` are not yet available for Vulcan downloads; use `--env dev` for now.
167
+ - Usage:
168
+ - `sima-cli vulcan --env {dev|staging|production} download [REPO] [BRANCH_OR_TAG]`
169
+ - `sima-cli vulcan download --env {dev|staging|production} [REPO] [BRANCH_OR_TAG]`
170
+ - If `REPO` is omitted, the CLI prompts for a repository.
171
+ - If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
172
+ - For automation, pass both values and add `--json` for structured output.
173
+ - Each download reads `latest.tag`, fetches `manifest.json`, downloads manifest-listed artifacts, and verifies size and SHA256 values when present.
174
+
175
+ Example:
176
+
177
+ ```bash
178
+ sima-cli vulcan --env dev download internals develop --output ./artifacts --json
179
+ ```
180
+
181
+ ---
182
+
155
183
  ## 🔧 Firmware Update
156
184
 
157
185
  ```bash
@@ -379,10 +407,14 @@ sima-cli packages show <PACKAGE_NAME> [--version VERSION]
379
407
  ### Setup SDK
380
408
 
381
409
  ```bash
382
- sima-cli sdk setup [--noninteractive] [-y]
410
+ sima-cli sdk setup [--noninteractive] [-y] [--workspace PATH] [--no-model-sdk] [--no-insight] [--minimal]
383
411
  ```
384
412
 
385
413
  - Initialize SDK environment and select components to start.
414
+ - Use `--workspace PATH` to mount a host directory other than `~/workspace` into SDK containers.
415
+ - Use `--no-model-sdk` to skip Model SDK extension setup.
416
+ - Use `--no-insight` to start Neat SDK without Insight UI/video/WebRTC port mappings.
417
+ - Use `--minimal` for CI compilation jobs. It skips optional Neat SDK setup extras, including Insight setup, installing `sima-cli`, Model SDK extensions, and coding agent playbooks inside the container.
386
418
 
387
419
  ### Start SDK Containers
388
420
 
@@ -118,6 +118,34 @@ sima-cli download <URL> [-d DEST]
118
118
 
119
119
  ---
120
120
 
121
+ ## 🔥 Vulcan Artifacts
122
+
123
+ ```bash
124
+ sima-cli vulcan download --env production core main
125
+ ```
126
+
127
+ - Downloads tested artifacts from Vulcan artifact hosting.
128
+ - Environment URLs:
129
+ - `dev`: `https://artifacts.neat.paconsultings.com`
130
+ - `staging`: `https://artifacts.stg.neat.sima.ai`
131
+ - `production`: `https://artifacts.neat.sima.ai`
132
+ - `staging` and `production` are not yet available for Vulcan downloads; use `--env dev` for now.
133
+ - Usage:
134
+ - `sima-cli vulcan --env {dev|staging|production} download [REPO] [BRANCH_OR_TAG]`
135
+ - `sima-cli vulcan download --env {dev|staging|production} [REPO] [BRANCH_OR_TAG]`
136
+ - If `REPO` is omitted, the CLI prompts for a repository.
137
+ - If `BRANCH_OR_TAG` is omitted, the CLI downloads `branches.json` and prompts for a branch.
138
+ - For automation, pass both values and add `--json` for structured output.
139
+ - Each download reads `latest.tag`, fetches `manifest.json`, downloads manifest-listed artifacts, and verifies size and SHA256 values when present.
140
+
141
+ Example:
142
+
143
+ ```bash
144
+ sima-cli vulcan --env dev download internals develop --output ./artifacts --json
145
+ ```
146
+
147
+ ---
148
+
121
149
  ## 🔧 Firmware Update
122
150
 
123
151
  ```bash
@@ -345,10 +373,14 @@ sima-cli packages show <PACKAGE_NAME> [--version VERSION]
345
373
  ### Setup SDK
346
374
 
347
375
  ```bash
348
- sima-cli sdk setup [--noninteractive] [-y]
376
+ sima-cli sdk setup [--noninteractive] [-y] [--workspace PATH] [--no-model-sdk] [--no-insight] [--minimal]
349
377
  ```
350
378
 
351
379
  - Initialize SDK environment and select components to start.
380
+ - Use `--workspace PATH` to mount a host directory other than `~/workspace` into SDK containers.
381
+ - Use `--no-model-sdk` to skip Model SDK extension setup.
382
+ - Use `--no-insight` to start Neat SDK without Insight UI/video/WebRTC port mappings.
383
+ - Use `--minimal` for CI compilation jobs. It skips optional Neat SDK setup extras, including Insight setup, installing `sima-cli`, Model SDK extensions, and coding agent playbooks inside the container.
352
384
 
353
385
  ### Start SDK Containers
354
386
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sima-cli"
7
- version = "2.1.5"
7
+ version = "2.1.6"
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"
@@ -1,2 +1,2 @@
1
1
  # sima_cli/__version__.py
2
- __version__ = "2.1.5"
2
+ __version__ = "2.1.6"
@@ -29,6 +29,7 @@ from sima_cli.app_zoo.commands import register_appzoo_commands
29
29
  from sima_cli.install.registry import register_packages_commands
30
30
  from sima_cli.upgrade.selfupdate import register_selfupdate_command
31
31
  from sima_cli.playbooks import register_playbook_commands
32
+ from sima_cli.vulcan import register_vulcan_commands
32
33
 
33
34
  def _configure_stdio_errors() -> None:
34
35
  for stream in (getattr(sys, "stdout", None), getattr(sys, "stderr", None)):
@@ -89,6 +90,7 @@ def main(ctx, internal):
89
90
  # ----------------------
90
91
  register_sdk_commands(main)
91
92
  register_playbook_commands(main)
93
+ register_vulcan_commands(main)
92
94
 
93
95
 
94
96
  # ----------------------
@@ -173,8 +173,19 @@ def launch_sdk_tool(tool: str, cmd, ctx):
173
173
  is_flag=True,
174
174
  help="Skip Model SDK extension setup. Intended for CI installation tests.",
175
175
  )
176
+ @click.option(
177
+ "--minimal",
178
+ is_flag=True,
179
+ help="Skip optional Neat SDK container extras for CI compilation jobs.",
180
+ )
181
+ @click.option(
182
+ "--workspace",
183
+ type=click.Path(file_okay=False, dir_okay=True),
184
+ default=None,
185
+ help="Host workspace directory to mount into SDK containers instead of ~/workspace.",
186
+ )
176
187
  @click.pass_context
177
- def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk):
188
+ def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk, minimal, workspace):
178
189
  """Initialize SDK environment and select components to start."""
179
190
  devkit_ip = _resolve_devkit_ip(devkit)
180
191
  try:
@@ -184,6 +195,8 @@ def setup(ctx, yes, noninteractive, devkit, no_insight, no_model_sdk):
184
195
  devkit_ip=devkit_ip,
185
196
  no_insight=no_insight,
186
197
  no_model_sdk=no_model_sdk,
198
+ minimal=minimal,
199
+ workspace=workspace,
187
200
  )
188
201
  except subprocess.CalledProcessError as e:
189
202
  raise click.ClickException(f"SDK setup failed while running: {' '.join(e.cmd)}") from e
@@ -656,6 +656,8 @@ def setup_and_start(
656
656
  devkit_ip: str = "",
657
657
  no_insight: bool = False,
658
658
  no_model_sdk: bool = False,
659
+ minimal: bool = False,
660
+ workspace: Optional[str] = None,
659
661
  ):
660
662
  """Main entry for SDK setup and container start."""
661
663
 
@@ -693,20 +695,29 @@ def setup_and_start(
693
695
  # ──────────────────────────────────────────────
694
696
  # Start containers
695
697
  # ──────────────────────────────────────────────
696
- workspace = get_workspace(yes_to_all, noninteractive=noninteractive)
698
+ workspace = get_workspace(
699
+ yes_to_all,
700
+ noninteractive=noninteractive,
701
+ workspace_override=workspace,
702
+ )
697
703
  uid = os.getuid() if hasattr(os, "getuid") else 900
698
704
  gid = os.getgid() if hasattr(os, "getgid") else 900
699
705
  devkit_env = _setup_devkit_share(devkit_ip, workspace, selected_images, noninteractive=noninteractive)
700
- if no_model_sdk:
706
+ skip_model_sdk = no_model_sdk or minimal
707
+ skip_insight = no_insight or minimal
708
+ if skip_model_sdk:
701
709
  sdk_extensions_dir = ""
702
710
  if any(is_neat_sdk_image(img) for img in selected_images):
703
- click.echo("ℹ️ Skipping Model SDK extension setup because --no-model-sdk was specified.")
711
+ reason = "--minimal" if minimal else "--no-model-sdk"
712
+ click.echo(f"ℹ️ Skipping Model SDK extension setup because {reason} was specified.")
704
713
  else:
705
714
  sdk_extensions_dir = _setup_sdk_extensions(
706
715
  selected_images,
707
716
  noninteractive=noninteractive,
708
717
  yes_to_all=yes_to_all,
709
718
  )
719
+ if minimal and any(is_neat_sdk_image(img) for img in selected_images):
720
+ click.echo("ℹ️ Skipping Insight setup because --minimal was specified.")
710
721
 
711
722
  for img in selected_images:
712
723
  container_name = sanitize_container_name(img)
@@ -739,13 +750,15 @@ def setup_and_start(
739
750
  sdk_extensions_dir=sdk_extensions_dir,
740
751
  noninteractive=noninteractive,
741
752
  yes_to_all=yes_to_all,
742
- no_insight=no_insight,
743
- no_model_sdk=no_model_sdk,
753
+ no_insight=skip_insight,
754
+ no_model_sdk=skip_model_sdk,
755
+ minimal=minimal,
744
756
  )
745
757
  else:
746
- if no_insight and is_neat_sdk_image(img):
758
+ if skip_insight and is_neat_sdk_image(img):
759
+ option = "--minimal" if minimal else "--no-insight"
747
760
  raise RuntimeError(
748
- "Cannot apply --no-insight to an existing Neat SDK container because Docker "
761
+ f"Cannot apply {option} to an existing Neat SDK container because Docker "
749
762
  "port mappings are immutable. Remove and recreate the container when prompted, "
750
763
  f"or run: docker rm -f {existing_container}"
751
764
  )
@@ -157,16 +157,16 @@ def allocate_neat_ports(no_insight: bool = False) -> Tuple[Dict, List[str]]:
157
157
 
158
158
  port_map = {
159
159
  "schema": NEAT_PORT_MAP_SCHEMA,
160
- "cert": {
161
- "mount": "/sdk-cert",
162
- "certFile": "/sdk-cert/neat-sdk.pem",
163
- "keyFile": "/sdk-cert/neat-sdk-key.pem",
164
- },
165
160
  }
166
161
 
167
162
  port_args = []
168
163
 
169
164
  if not no_insight:
165
+ port_map["cert"] = {
166
+ "mount": "/sdk-cert",
167
+ "certFile": "/sdk-cert/neat-sdk.pem",
168
+ "keyFile": "/sdk-cert/neat-sdk-key.pem",
169
+ }
170
170
  web_ssh = _allocate_single_port(8022, "tcp", reserved)
171
171
  rtsp_tcp = _allocate_single_port(8554, "tcp", reserved)
172
172
  main_ui = _allocate_single_port(9900, "tcp", reserved)
@@ -454,11 +454,24 @@ def prepare_neat_container_run(
454
454
  yes_to_all: bool = False,
455
455
  noninteractive: bool = False,
456
456
  no_insight: bool = False,
457
+ minimal: bool = False,
457
458
  ) -> NeatRunConfig:
459
+ no_insight = no_insight or minimal
458
460
  container_dir = Path(workspace) / f".{container_name}"
459
461
  config_dir = container_dir / "insight-config"
460
462
  cert_dir = container_dir / "sdk-cert"
461
463
  port_map, port_args = allocate_neat_ports(no_insight=no_insight)
464
+ if no_insight:
465
+ return NeatRunConfig(
466
+ port_map=port_map,
467
+ port_args=port_args,
468
+ config_host_dir="",
469
+ cert_host_dir="",
470
+ port_map_host_path="",
471
+ cert_file_host_path="",
472
+ key_file_host_path="",
473
+ webrtc_host_ip="",
474
+ )
462
475
  webrtc_host_ip = _detect_webrtc_host_ip(devkit_env)
463
476
  cert_file, key_file = _ensure_certificates(
464
477
  cert_dir,
@@ -480,13 +493,16 @@ def prepare_neat_container_run(
480
493
 
481
494
 
482
495
  def append_neat_docker_args(docker_cmd: List[str], config: NeatRunConfig) -> None:
483
- docker_cmd.extend(["-e", f"MTX_RTSPTRANSPORTS={NEAT_MEDIAMTX_RTSP_TRANSPORTS}"])
496
+ if config.port_args or config.config_host_dir:
497
+ docker_cmd.extend(["-e", f"MTX_RTSPTRANSPORTS={NEAT_MEDIAMTX_RTSP_TRANSPORTS}"])
484
498
  if config.webrtc_host_ip:
485
499
  docker_cmd.extend(["-e", f"CONTAINER_HOST_IP={config.webrtc_host_ip}"])
486
500
  for mapping in config.port_args:
487
501
  docker_cmd.extend(["-p", mapping])
488
- docker_cmd.extend(["-v", f"{config.config_host_dir}:/home/docker/.insight-config"])
489
- docker_cmd.extend(["-v", f"{config.cert_host_dir}:/sdk-cert"])
502
+ if config.config_host_dir:
503
+ docker_cmd.extend(["-v", f"{config.config_host_dir}:/home/docker/.insight-config"])
504
+ if config.cert_host_dir:
505
+ docker_cmd.extend(["-v", f"{config.cert_host_dir}:/sdk-cert"])
490
506
 
491
507
 
492
508
  def print_neat_setup_summary(config: NeatRunConfig) -> None:
@@ -529,8 +545,10 @@ def print_neat_setup_summary(config: NeatRunConfig) -> None:
529
545
  )
530
546
  if config.webrtc_host_ip:
531
547
  print(f" iceHost: {config.webrtc_host_ip}")
532
- print(f" config: {config.port_map_host_path}")
533
- print(f" certs: {config.cert_host_dir}")
548
+ if config.port_map_host_path:
549
+ print(f" config: {config.port_map_host_path}")
550
+ if config.cert_host_dir:
551
+ print(f" certs: {config.cert_host_dir}")
534
552
 
535
553
 
536
554
  def is_docker_port_collision_error(error_text: str) -> bool:
@@ -504,7 +504,7 @@ def get_running_containers():
504
504
  return running
505
505
 
506
506
 
507
- def get_workspace(yes_to_all=False, noninteractive=False):
507
+ def get_workspace(yes_to_all=False, noninteractive=False, workspace_override=None):
508
508
  """
509
509
  Determine the workspace:
510
510
  - If at least one container is running, read from ~/.simaai/.mount
@@ -515,6 +515,17 @@ def get_workspace(yes_to_all=False, noninteractive=False):
515
515
  simaai_dir = os.path.join(home, ".simaai")
516
516
  mount_file = os.path.join(simaai_dir, ".mount")
517
517
 
518
+ if workspace_override:
519
+ workspace = os.path.realpath(os.path.expanduser(workspace_override))
520
+ if not os.path.isdir(workspace):
521
+ os.makedirs(workspace, exist_ok=True)
522
+ print(f"📂 Created workspace: {workspace}")
523
+ print(f"✅ Workspace set to: {workspace}")
524
+ os.makedirs(simaai_dir, exist_ok=True)
525
+ with open(mount_file, "w") as f:
526
+ f.write(workspace)
527
+ return workspace
528
+
518
529
  running_containers = get_running_containers()
519
530
 
520
531
  # Case 1: At least one container running → read workspace
@@ -920,6 +931,7 @@ def configure_container(
920
931
  noninteractive=False,
921
932
  yes_to_all=False,
922
933
  no_model_sdk=False,
934
+ minimal=False,
923
935
  ):
924
936
  """
925
937
  Configure container user mappings and permissions:
@@ -930,6 +942,7 @@ def configure_container(
930
942
  - If configure_network=True, computes and stores .hash for /usr/local/simaai/plugins
931
943
  """
932
944
  platform_os = check_os()
945
+ no_model_sdk = no_model_sdk or minimal
933
946
 
934
947
  # Detect current host user
935
948
  if platform_os in ["linux", "macos"]:
@@ -1016,10 +1029,15 @@ def configure_container(
1016
1029
 
1017
1030
  _copy_sima_cli_auth_cache_to_container(sdk_container_name, login_name, uid, gid)
1018
1031
 
1019
- # Ensure sima-cli is available for the configured default user.
1020
- ensure_sima_cli_installed(sdk_container_name, login_name)
1032
+ # Ensure sima-cli is available for the configured default user unless this
1033
+ # setup is only preparing a lightweight CI compilation container.
1034
+ if minimal:
1035
+ print("ℹ️ Skipping sima-cli installation because --minimal was specified.")
1036
+ else:
1037
+ ensure_sima_cli_installed(sdk_container_name, login_name)
1021
1038
  if no_model_sdk:
1022
- print("ℹ️ Skipping Model SDK extension installation because --no-model-sdk was specified.")
1039
+ reason = "--minimal" if minimal else "--no-model-sdk"
1040
+ print(f"ℹ️ Skipping Model SDK extension installation because {reason} was specified.")
1023
1041
  else:
1024
1042
  ensure_model_sdk_extension_installed(
1025
1043
  sdk_container_name,
@@ -1027,7 +1045,10 @@ def configure_container(
1027
1045
  auto_install=(noninteractive or yes_to_all),
1028
1046
  )
1029
1047
  _sync_codex_skills(sdk_container_name, login_name, uid, gid)
1030
- install_neat_playbooks(sdk_container_name, login_name)
1048
+ if minimal:
1049
+ print("ℹ️ Skipping Neat coding agent playbook installation because --minimal was specified.")
1050
+ else:
1051
+ install_neat_playbooks(sdk_container_name, login_name)
1031
1052
 
1032
1053
  # ---- Optional Network & Syslog Configuration ----
1033
1054
  if configure_network:
@@ -1141,6 +1162,7 @@ def start_docker_container(
1141
1162
  yes_to_all=False,
1142
1163
  no_insight=False,
1143
1164
  no_model_sdk=False,
1165
+ minimal=False,
1144
1166
  ):
1145
1167
  """
1146
1168
  Start a Docker container using an image pulled from either JFrog or AWS ECR.
@@ -1152,6 +1174,7 @@ def start_docker_container(
1152
1174
  # ─────────────────────────────────────────────
1153
1175
  # Generate container name
1154
1176
  # ─────────────────────────────────────────────
1177
+ no_insight = no_insight or minimal
1155
1178
  container_name = sanitize_container_name(image)
1156
1179
  hostname = sanitize_container_hostname(container_name)
1157
1180
  print(f"🚀 Starting container '{container_name}' using image '{image}'")
@@ -1262,6 +1285,7 @@ def start_docker_container(
1262
1285
  yes_to_all=yes_to_all,
1263
1286
  noninteractive=noninteractive,
1264
1287
  no_insight=no_insight,
1288
+ minimal=minimal,
1265
1289
  )
1266
1290
  launch_cmd = list(base_docker_cmd)
1267
1291
  append_neat_docker_args(launch_cmd, neat_run_config)
@@ -1311,6 +1335,7 @@ def start_docker_container(
1311
1335
  noninteractive=noninteractive,
1312
1336
  yes_to_all=yes_to_all,
1313
1337
  no_model_sdk=no_model_sdk,
1338
+ minimal=minimal,
1314
1339
  )
1315
1340
 
1316
1341
  if devkit_env and neat_sdk_image:
@@ -4,15 +4,20 @@ from typing import Optional, List, Tuple
4
4
  import re
5
5
  import os
6
6
  import shutil
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.text import Text
7
10
 
8
11
  from sima_cli.utils.env import is_devkit_running_elxr
9
12
 
10
13
  APT_SOURCE_FILE = "/etc/apt/sources.list.d/0000mirror.list"
14
+ BUILDINFO_FILES = ["/etc/build", "/etc/buildinfo"]
11
15
  EXTERNAL_REPO_URL = "https://repo.sima.ai/elxr/deb/release"
12
16
  INTERNAL_REPO_URL = "http://sw-web.eng.sima.ai/deb/pre-release"
13
17
  DEFAULT_REPO_SUITE = "bookworm"
14
18
  REPO_COMPONENT = "non-free"
15
19
  SIMAAI_OTA_FALLBACK = "/usr/bin/simaai-ota"
20
+ ELXR_UPDATE_DOC_URL = "https://docs.sima.ai/pages/tech-notes/elxr-conversion.html"
16
21
 
17
22
 
18
23
  def _repo_line(repo_url: str, suite: str) -> str:
@@ -181,6 +186,35 @@ def _get_installed_palette_version() -> Optional[str]:
181
186
  return version if version else None
182
187
 
183
188
 
189
+ def _get_installed_elxr_distro_version() -> Optional[str]:
190
+ """Return ELXR DISTRO_VERSION from /etc/build or /etc/buildinfo, if available."""
191
+ pattern = re.compile(r"^\s*DISTRO_VERSION\s*=\s*(\S+)\s*$")
192
+
193
+ for path in BUILDINFO_FILES:
194
+ try:
195
+ with open(path, "r", encoding="utf-8") as f:
196
+ for line in f:
197
+ match = pattern.match(line)
198
+ if match:
199
+ return match.group(1)
200
+ except OSError:
201
+ continue
202
+
203
+ return None
204
+
205
+
206
+ def _is_current_elxr_version(
207
+ requested_version: str,
208
+ installed_palette_version: Optional[str],
209
+ installed_distro_version: Optional[str],
210
+ ) -> bool:
211
+ return requested_version in {
212
+ version
213
+ for version in (installed_palette_version, installed_distro_version)
214
+ if version
215
+ }
216
+
217
+
184
218
  def _get_available_palette_versions() -> List[str]:
185
219
  """Parse apt policy output and return available simaai-palette-modalix versions."""
186
220
  try:
@@ -230,6 +264,37 @@ def _get_available_palette_versions() -> List[str]:
230
264
  return versions
231
265
 
232
266
 
267
+ def _show_unsupported_specific_elxr_update(
268
+ requested_version: str,
269
+ current_version: Optional[str] = None,
270
+ latest_version: Optional[str] = None,
271
+ ) -> None:
272
+ details = [
273
+ "Updating ELXR to a specific version is not currently supported by `sima-cli update`.",
274
+ "Downgrades and non-latest upgrade paths are not reliable enough to automate safely.",
275
+ ]
276
+ if current_version:
277
+ details.append(f"Current simaai-palette-modalix version: {current_version}")
278
+ if requested_version:
279
+ details.append(f"Requested simaai-palette-modalix version: {requested_version}")
280
+ if latest_version:
281
+ details.append(f"Latest available simaai-palette-modalix version: {latest_version}")
282
+ details.extend([
283
+ "",
284
+ "Use `sima-cli update` without a version to update to the latest supported build.",
285
+ f"For full-system conversion/update guidance, see: {ELXR_UPDATE_DOC_URL}",
286
+ ])
287
+
288
+ Console().print(
289
+ Panel(
290
+ Text("\n".join(details), style="yellow"),
291
+ title="[yellow]Unsupported ELXR Update Path[/yellow]",
292
+ border_style="yellow",
293
+ expand=False,
294
+ )
295
+ )
296
+
297
+
233
298
  def print_current_versions():
234
299
  p1 = subprocess.Popen(
235
300
  ["dpkg", "-l"],
@@ -265,8 +330,6 @@ def update_elxr(version_or_url: Optional[str], internal: bool = False):
265
330
 
266
331
  print_current_versions()
267
332
 
268
- from InquirerPy import inquirer
269
-
270
333
  if not _ensure_elxr_repo_channel(internal):
271
334
  return
272
335
 
@@ -292,6 +355,9 @@ def update_elxr(version_or_url: Optional[str], internal: bool = False):
292
355
  # Main interaction loop
293
356
  # -----------------------------
294
357
  simaai_ota = _resolve_simaai_ota()
358
+ if version_or_url is None:
359
+ from InquirerPy import inquirer
360
+
295
361
  while True:
296
362
 
297
363
  # If user did not pass a version, show the update type menu
@@ -321,6 +387,8 @@ def update_elxr(version_or_url: Optional[str], internal: bool = False):
321
387
  # -----------------------------
322
388
  versions = _get_available_palette_versions()
323
389
  installed_version = _get_installed_palette_version()
390
+ installed_distro_version = _get_installed_elxr_distro_version()
391
+ latest_version = versions[0] if versions else None
324
392
 
325
393
  if not versions:
326
394
  click.echo("❌ No versions found in APT policy, aborting.")
@@ -346,8 +414,7 @@ def update_elxr(version_or_url: Optional[str], internal: bool = False):
346
414
  click.echo("❌ Update cancelled")
347
415
  return
348
416
 
349
- # A version was selected
350
- if installed_version and selected == installed_version:
417
+ if _is_current_elxr_version(selected, installed_version, installed_distro_version):
351
418
  same_version_choice = inquirer.select(
352
419
  message=(
353
420
  f"Version {selected} is already running. "
@@ -365,16 +432,55 @@ def update_elxr(version_or_url: Optional[str], internal: bool = False):
365
432
 
366
433
  cmd = [simaai_ota, "-f", "-o", "-v", selected]
367
434
  desc = f"Force reinstall specific version {selected}"
368
- else:
435
+ break
436
+
437
+ if latest_version and selected == latest_version:
369
438
  cmd = [simaai_ota, "-f", "-o", "-v", selected]
370
- desc = f"Update to specific version {selected}"
371
- break
439
+ desc = f"Update to latest version {selected}"
440
+ break
441
+
442
+ _show_unsupported_specific_elxr_update(
443
+ requested_version=selected,
444
+ current_version=installed_distro_version or installed_version,
445
+ latest_version=latest_version,
446
+ )
447
+ warning_choice = inquirer.select(
448
+ message="What would you like to do next?",
449
+ choices=[
450
+ {"name": "⬅️ Back to previous menu", "value": "back"},
451
+ {"name": "❌ Cancel", "value": "cancel"},
452
+ ],
453
+ default="back",
454
+ ).execute()
455
+
456
+ if warning_choice == "cancel":
457
+ click.echo("❌ Update cancelled")
458
+ return
459
+
460
+ continue
372
461
 
373
462
  else:
374
463
  # version_or_url specified by user (non-interactive)
375
- cmd = [simaai_ota, "-f", "-o", "-v", version_or_url]
376
- desc = f"Update to specific version {version_or_url}"
377
- break
464
+ versions = _get_available_palette_versions()
465
+ installed_version = _get_installed_palette_version()
466
+ installed_distro_version = _get_installed_elxr_distro_version()
467
+ latest_version = versions[0] if versions else None
468
+ if _is_current_elxr_version(version_or_url, installed_version, installed_distro_version):
469
+ cmd = [simaai_ota, "-f", "-o", "-v", version_or_url]
470
+ desc = f"Force reinstall specific version {version_or_url}"
471
+ break
472
+
473
+ if latest_version and version_or_url == latest_version:
474
+ cmd = [simaai_ota, "-f", "-o", "-v", version_or_url]
475
+ desc = f"Update to latest version {version_or_url}"
476
+ break
477
+
478
+ _show_unsupported_specific_elxr_update(
479
+ requested_version=version_or_url,
480
+ current_version=installed_distro_version or installed_version,
481
+ latest_version=latest_version,
482
+ )
483
+ return
378
484
 
379
485
  # -----------------------------
380
486
  # Execute update
@@ -0,0 +1,3 @@
1
+ from .commands import register_vulcan_commands
2
+
3
+ __all__ = ["register_vulcan_commands"]