sima-cli 0.0.20__py3-none-any.whl → 0.0.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sima_cli/__version__.py +1 -1
- sima_cli/cli.py +109 -15
- sima_cli/download/downloader.py +7 -3
- sima_cli/install/metadata_info.py +57 -0
- sima_cli/install/metadata_installer.py +447 -0
- sima_cli/install/metadata_validator.py +138 -0
- sima_cli/network/network.py +208 -0
- sima_cli/nvme/nvme.py +123 -0
- sima_cli/update/bootimg.py +5 -4
- sima_cli/update/local.py +10 -8
- sima_cli/update/netboot.py +4 -3
- sima_cli/update/query.py +7 -5
- sima_cli/update/remote.py +46 -25
- sima_cli/update/updater.py +71 -40
- sima_cli/utils/disk.py +15 -0
- sima_cli/utils/env.py +46 -0
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/METADATA +2 -1
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/RECORD +22 -16
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/WHEEL +0 -0
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/entry_points.txt +0 -0
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/licenses/LICENSE +0 -0
- {sima_cli-0.0.20.dist-info → sima_cli-0.0.22.dist-info}/top_level.txt +0 -0
sima_cli/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# sima_cli/__version__.py
|
2
|
-
__version__ = "0.0.
|
2
|
+
__version__ = "0.0.22"
|
sima_cli/cli.py
CHANGED
@@ -9,7 +9,10 @@ from sima_cli.__version__ import __version__
|
|
9
9
|
from sima_cli.utils.config import CONFIG_PATH
|
10
10
|
from sima_cli.install.optiview import install_optiview
|
11
11
|
from sima_cli.install.hostdriver import install_hostdriver
|
12
|
+
from sima_cli.install.metadata_installer import install_from_metadata, metadata_resolver
|
12
13
|
from sima_cli.serial.serial import connect_serial
|
14
|
+
from sima_cli.nvme.nvme import nvme_format, nvme_remount
|
15
|
+
from sima_cli.network.network import network_menu
|
13
16
|
|
14
17
|
# Entry point for the CLI tool using Click's command group decorator
|
15
18
|
@click.group()
|
@@ -116,6 +119,7 @@ def download(ctx, url, dest):
|
|
116
119
|
click.echo(f"\n✅ File downloaded successfully to: {path}")
|
117
120
|
return
|
118
121
|
except Exception as e:
|
122
|
+
click.echo(f"❌ Failed to download as file {e}")
|
119
123
|
pass
|
120
124
|
|
121
125
|
# If that fails, try to treat as a folder and download all files
|
@@ -140,12 +144,13 @@ def download(ctx, url, dest):
|
|
140
144
|
help="Skip confirmation after firmware file is downloaded."
|
141
145
|
)
|
142
146
|
@click.option(
|
143
|
-
'--passwd',
|
147
|
+
'-p', '--passwd',
|
144
148
|
default='edgeai',
|
145
149
|
help="Optional SSH password for remote board (default is 'edgeai')."
|
146
150
|
)
|
151
|
+
@click.option("-f", "--flavor", type=click.Choice(["headless", "full"], case_sensitive=False), default="headless", show_default=True, help="firmware flavor.")
|
147
152
|
@click.pass_context
|
148
|
-
def update(ctx, version_or_url, ip, yes, passwd):
|
153
|
+
def update(ctx, version_or_url, ip, yes, passwd, flavor):
|
149
154
|
"""
|
150
155
|
Run system update across different environments.
|
151
156
|
Downloads and applies firmware updates for PCIe host or SiMa board.
|
@@ -153,7 +158,7 @@ def update(ctx, version_or_url, ip, yes, passwd):
|
|
153
158
|
version_or_url: The version string (e.g. '1.5.0') or a direct URL to the firmware package.
|
154
159
|
"""
|
155
160
|
internal = ctx.obj.get("internal", False)
|
156
|
-
perform_update(version_or_url, ip, internal, passwd=passwd, auto_confirm=yes)
|
161
|
+
perform_update(version_or_url, ip, internal, passwd=passwd, auto_confirm=yes, flavor=flavor)
|
157
162
|
|
158
163
|
# ----------------------
|
159
164
|
# Model Zoo Subcommands
|
@@ -226,18 +231,20 @@ def show_mla_memory_usage(ctx):
|
|
226
231
|
# ----------------------
|
227
232
|
@main.command(name="bootimg")
|
228
233
|
@click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
|
229
|
-
@click.option("--boardtype", type=click.Choice(["modalix",
|
230
|
-
@click.option("--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
|
231
|
-
@click.option("--autoflash", is_flag=True, default=False, show_default=True, help="Net boot the DevKit and automatically flash the internal storage")
|
234
|
+
@click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
|
235
|
+
@click.option("-n", "--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
|
236
|
+
@click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="Net boot the DevKit and automatically flash the internal storage - TBD")
|
232
237
|
@click.pass_context
|
233
238
|
def bootimg_cmd(ctx, version, boardtype, netboot, autoflash):
|
234
239
|
"""
|
235
240
|
Download and burn a removable media or setup TFTP boot.
|
236
241
|
|
242
|
+
Only supports headless image
|
243
|
+
|
237
244
|
Examples:
|
238
245
|
sima-cli bootimg -v 1.6.0
|
239
246
|
sima-cli bootimg -v 1.6.0 --boardtype mlsoc
|
240
|
-
sima-cli bootimg -v 1.6.0 --boardtype
|
247
|
+
sima-cli bootimg -v 1.6.0 --boardtype mlsoc
|
241
248
|
sima-cli bootimg -v 1.6.0 --boardtype modalix --netboot
|
242
249
|
sima-cli bootimg -v 1.6.0 --boardtype modalix --autoflash
|
243
250
|
"""
|
@@ -250,14 +257,15 @@ def bootimg_cmd(ctx, version, boardtype, netboot, autoflash):
|
|
250
257
|
click.echo(f"📦 Preparing boot image:")
|
251
258
|
click.echo(f" 🔹 Version : {version}")
|
252
259
|
click.echo(f" 🔹 Board Type: {boardtype}")
|
260
|
+
click.echo(f" 🔹 F/W Flavor: headless")
|
253
261
|
|
254
262
|
try:
|
255
263
|
boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
|
256
264
|
if netboot or autoflash:
|
257
|
-
setup_netboot(version, boardtype, internal, autoflash)
|
265
|
+
setup_netboot(version, boardtype, internal, autoflash, flavor='headless')
|
258
266
|
click.echo("✅ Netboot image prepared and TFTP server is running.")
|
259
267
|
else:
|
260
|
-
write_image(version, boardtype, 'yocto', internal)
|
268
|
+
write_image(version, boardtype, 'yocto', internal, flavor='headless')
|
261
269
|
click.echo("✅ Boot image successfully written.")
|
262
270
|
click.echo("✅ Boot image successfully written.")
|
263
271
|
except Exception as e:
|
@@ -272,22 +280,44 @@ SDK_INDEPENDENT_COMPONENTS = {"optiview"}
|
|
272
280
|
ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
273
281
|
|
274
282
|
@main.command(name="install")
|
275
|
-
@click.argument("component",
|
276
|
-
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components)")
|
283
|
+
@click.argument("component", required=False)
|
284
|
+
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components unless --metadata is provided)")
|
285
|
+
@click.option("-m", "--mirror", help="URL to a metadata.json file for generic installation")
|
286
|
+
@click.option("-t", "--tag", help="Tag of the package (optional)")
|
277
287
|
@click.pass_context
|
278
|
-
def install_cmd(ctx, component, version):
|
288
|
+
def install_cmd(ctx, component, version, mirror, tag):
|
279
289
|
"""
|
280
|
-
Install supported components such as SDKs or
|
290
|
+
Install supported components such as SDKs, tools, or generic packages via metadata.
|
281
291
|
|
282
292
|
Examples:
|
283
293
|
|
284
294
|
sima-cli install hostdriver -v 1.6.0
|
285
295
|
|
286
296
|
sima-cli install optiview
|
297
|
+
|
298
|
+
sima-cli install -m https://example.com/packages/foo/metadata.json
|
299
|
+
|
300
|
+
sima-cli install examples.llima -v 1.7.0
|
287
301
|
"""
|
288
|
-
component = component.lower()
|
289
302
|
internal = ctx.obj.get("internal", False)
|
290
303
|
|
304
|
+
# Metadata-based installation path
|
305
|
+
if mirror:
|
306
|
+
if component:
|
307
|
+
click.echo(f"⚠️ Component '{component}' is ignored when using --metadata. Proceeding with metadata-based installation.")
|
308
|
+
click.echo(f"🔧 Installing generic component from metadata URL: {mirror}")
|
309
|
+
if install_from_metadata(metadata_url=mirror, internal=internal):
|
310
|
+
click.echo("✅ Installation complete.")
|
311
|
+
return
|
312
|
+
|
313
|
+
# No component and no metadata: error
|
314
|
+
if not component:
|
315
|
+
click.echo("❌ You must specify either a component name or provide --metadata.")
|
316
|
+
ctx.exit(1)
|
317
|
+
|
318
|
+
component = component.lower()
|
319
|
+
|
320
|
+
# Validate version requirement
|
291
321
|
if component in SDK_DEPENDENT_COMPONENTS and not version:
|
292
322
|
click.echo(f"❌ The component '{component}' requires a specific SDK version. Please provide one using -v.")
|
293
323
|
ctx.exit(1)
|
@@ -295,7 +325,7 @@ def install_cmd(ctx, component, version):
|
|
295
325
|
if component in SDK_INDEPENDENT_COMPONENTS and version:
|
296
326
|
click.echo(f"ℹ️ The component '{component}' does not require an SDK version. Ignoring -v {version}.")
|
297
327
|
|
298
|
-
#
|
328
|
+
# Hardcoded component installation
|
299
329
|
if component == "palette":
|
300
330
|
click.echo(f"🔧 Installing SDK component 'palette' for version {version} is not implemented yet...")
|
301
331
|
elif component == "hostdriver":
|
@@ -304,9 +334,20 @@ def install_cmd(ctx, component, version):
|
|
304
334
|
elif component == "optiview":
|
305
335
|
click.echo("🔧 Installing tool 'optiview'...")
|
306
336
|
install_optiview()
|
337
|
+
else:
|
338
|
+
# Case 4: Try to resolve metadata URL from version + tag
|
339
|
+
try:
|
340
|
+
metadata_url = metadata_resolver(component, version, tag)
|
341
|
+
click.echo(f"🔧 Installing '{component}' from resolved metadata: {metadata_url}")
|
342
|
+
if install_from_metadata(metadata_url=metadata_url, internal=internal):
|
343
|
+
click.echo("✅ Installation complete.")
|
344
|
+
except Exception as e:
|
345
|
+
click.echo(f"❌ Failed to resolve metadata for component '{component}': {e}")
|
346
|
+
ctx.exit(1)
|
307
347
|
|
308
348
|
click.echo("✅ Installation complete.")
|
309
349
|
|
350
|
+
|
310
351
|
# ----------------------
|
311
352
|
# Serial Subcommands
|
312
353
|
# ----------------------
|
@@ -326,6 +367,59 @@ def serial_cmd(ctx, baud):
|
|
326
367
|
- Windows: shows PuTTY/Tera Term setup instructions
|
327
368
|
"""
|
328
369
|
connect_serial(ctx, baud)
|
370
|
+
|
371
|
+
# ----------------------
|
372
|
+
# Network Subcommands
|
373
|
+
# ----------------------
|
374
|
+
@main.command(name="network")
|
375
|
+
@click.pass_context
|
376
|
+
def network_cmd(ctx):
|
377
|
+
"""
|
378
|
+
Setup Network IP address on the DevKit
|
379
|
+
|
380
|
+
This command only works on the DevKit. It allows user to switch between DHCP and Static (Default addresses) IP.
|
381
|
+
|
382
|
+
"""
|
383
|
+
network_menu()
|
384
|
+
|
385
|
+
# ----------------------
|
386
|
+
# NVME Subcommands
|
387
|
+
# ----------------------
|
388
|
+
NVME_OPERATIONS = {"format", "remount"}
|
389
|
+
@main.command(name="nvme")
|
390
|
+
@click.argument("operation", type=click.Choice(NVME_OPERATIONS, case_sensitive=False))
|
391
|
+
@click.pass_context
|
392
|
+
def nvme_cmd(ctx, operation):
|
393
|
+
"""
|
394
|
+
Perform NVMe operations on the Modalix DevKit.
|
395
|
+
|
396
|
+
Available operations:
|
397
|
+
|
398
|
+
format - Format the NVMe drive and mount it to /mnt/nvme
|
399
|
+
|
400
|
+
remount - Remount the existing NVMe partition to /mnt/nvme
|
401
|
+
|
402
|
+
Example:
|
403
|
+
sima-cli nvme format
|
404
|
+
|
405
|
+
sima-cli nvme remount
|
406
|
+
"""
|
407
|
+
operation = operation.lower()
|
408
|
+
|
409
|
+
if operation == "format":
|
410
|
+
nvme_format()
|
411
|
+
|
412
|
+
elif operation == "remount":
|
413
|
+
try:
|
414
|
+
nvme_remount()
|
415
|
+
except Exception as e:
|
416
|
+
click.echo(f"❌ Failed to remount NVMe drive: {e}")
|
417
|
+
ctx.exit(1)
|
418
|
+
|
419
|
+
else:
|
420
|
+
click.echo(f"❌ Unsupported NVMe operation: {operation}")
|
421
|
+
ctx.exit(1)
|
422
|
+
|
329
423
|
# ----------------------
|
330
424
|
# App Zoo Subcommands
|
331
425
|
# ----------------------
|
sima_cli/download/downloader.py
CHANGED
@@ -80,10 +80,14 @@ def download_file_from_url(url: str, dest_folder: str = ".", internal: bool = Fa
|
|
80
80
|
headers["Authorization"] = f"Bearer {auth_token}"
|
81
81
|
request_fn = requests.get
|
82
82
|
head_fn = requests.head
|
83
|
-
|
83
|
+
elif 'https://docs.sima.ai' in url:
|
84
84
|
session = login_external()
|
85
85
|
request_fn = session.get
|
86
86
|
head_fn = session.head
|
87
|
+
else:
|
88
|
+
session = requests.Session()
|
89
|
+
request_fn = session.get
|
90
|
+
head_fn = session.head
|
87
91
|
|
88
92
|
# HEAD request to get total file size
|
89
93
|
head = head_fn(url, headers=headers, timeout=10)
|
@@ -95,7 +99,7 @@ def download_file_from_url(url: str, dest_folder: str = ".", internal: bool = Fa
|
|
95
99
|
existing_size = os.path.getsize(dest_path)
|
96
100
|
|
97
101
|
if existing_size == total_size:
|
98
|
-
print(f"✔
|
102
|
+
print(f"✔ File already exists and is complete: {file_name}")
|
99
103
|
return dest_path
|
100
104
|
elif existing_size < total_size:
|
101
105
|
resume_header['Range'] = f'bytes={existing_size}-'
|
@@ -113,7 +117,7 @@ def download_file_from_url(url: str, dest_folder: str = ".", internal: bool = Fa
|
|
113
117
|
final_total = existing_size + content_length
|
114
118
|
|
115
119
|
with open(dest_path, mode) as f, tqdm(
|
116
|
-
desc=f"Downloading {file_name}",
|
120
|
+
desc=f"⬇️ Downloading {file_name}",
|
117
121
|
total=final_total,
|
118
122
|
initial=existing_size,
|
119
123
|
unit='B',
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from rich import print
|
2
|
+
from rich.table import Table
|
3
|
+
from rich.panel import Panel
|
4
|
+
|
5
|
+
def print_metadata_summary(metadata: dict):
|
6
|
+
table = Table(show_header=False, box=None, padding=(0, 1))
|
7
|
+
|
8
|
+
table.add_row("[bold]Name[/bold]", metadata.get("name", "N/A"))
|
9
|
+
table.add_row("[bold]Version[/bold]", metadata.get("version", "N/A"))
|
10
|
+
table.add_row("[bold]Release[/bold]", metadata.get("release", "N/A"))
|
11
|
+
table.add_row("[bold]Description[/bold]", metadata.get("description", "N/A"))
|
12
|
+
|
13
|
+
# Platform info
|
14
|
+
platform_info = []
|
15
|
+
for p in metadata.get("platforms", []):
|
16
|
+
platform_type = p.get("type", "unknown")
|
17
|
+
if platform_type == "board":
|
18
|
+
compat = ", ".join(p.get("compatible_with", []))
|
19
|
+
platform_info.append(f"{platform_type} ({compat})")
|
20
|
+
elif platform_type in ("host", "generic"):
|
21
|
+
os_list = ", ".join(p.get("os", []))
|
22
|
+
platform_info.append(f"{platform_type} ({os_list})")
|
23
|
+
else:
|
24
|
+
platform_info.append(platform_type)
|
25
|
+
|
26
|
+
table.add_row("[bold]Platforms[/bold]", "; ".join(platform_info) or "N/A")
|
27
|
+
|
28
|
+
# Resources
|
29
|
+
resource_count = len(metadata.get("resources", []))
|
30
|
+
table.add_row("[bold]Resources[/bold]", f"{resource_count} file(s)")
|
31
|
+
|
32
|
+
# Size
|
33
|
+
size = metadata.get("size", {})
|
34
|
+
table.add_row("[bold]Download Size[/bold]", size.get("download", "N/A"))
|
35
|
+
table.add_row("[bold]Install Size[/bold]", size.get("install", "N/A"))
|
36
|
+
|
37
|
+
print()
|
38
|
+
print(Panel(table, title="📦 Package Summary", expand=False))
|
39
|
+
print()
|
40
|
+
|
41
|
+
|
42
|
+
def parse_size_string_to_bytes(size_str: str) -> int:
|
43
|
+
"""
|
44
|
+
Convert a size string like '40GB' or '512MB' to bytes.
|
45
|
+
"""
|
46
|
+
size_str = size_str.strip().upper()
|
47
|
+
units = {"KB": 10**3, "MB": 10**6, "GB": 10**9}
|
48
|
+
|
49
|
+
for unit, multiplier in units.items():
|
50
|
+
if size_str.endswith(unit):
|
51
|
+
try:
|
52
|
+
value = float(size_str[:-len(unit)].strip())
|
53
|
+
return int(value * multiplier)
|
54
|
+
except ValueError:
|
55
|
+
raise ValueError(f"Invalid numeric value in size string: '{size_str}'")
|
56
|
+
|
57
|
+
raise ValueError(f"Unrecognized size unit in '{size_str}'. Must be KB, MB, or GB.")
|