sima-cli 0.0.21__py3-none-any.whl → 0.0.23__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 +73 -10
- 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 +15 -0
- sima_cli/{nvme → storage}/nvme.py +52 -21
- sima_cli/storage/sdcard.py +136 -0
- sima_cli/update/bootimg.py +11 -6
- sima_cli/update/query.py +49 -26
- sima_cli/update/remote.py +8 -6
- sima_cli/update/updater.py +43 -14
- sima_cli/utils/disk.py +15 -0
- sima_cli/utils/env.py +24 -0
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.dist-info}/METADATA +2 -1
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.dist-info}/RECORD +21 -16
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.dist-info}/WHEEL +0 -0
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.dist-info}/entry_points.txt +0 -0
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.dist-info}/licenses/LICENSE +0 -0
- {sima_cli-0.0.21.dist-info → sima_cli-0.0.23.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.23"
|
sima_cli/cli.py
CHANGED
@@ -9,8 +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
|
13
|
-
from sima_cli.
|
14
|
+
from sima_cli.storage.nvme import nvme_format, nvme_remount
|
15
|
+
from sima_cli.storage.sdcard import sdcard_format
|
14
16
|
from sima_cli.network.network import network_menu
|
15
17
|
|
16
18
|
# Entry point for the CLI tool using Click's command group decorator
|
@@ -118,6 +120,7 @@ def download(ctx, url, dest):
|
|
118
120
|
click.echo(f"\n✅ File downloaded successfully to: {path}")
|
119
121
|
return
|
120
122
|
except Exception as e:
|
123
|
+
click.echo(f"❌ Failed to download as file {e}")
|
121
124
|
pass
|
122
125
|
|
123
126
|
# If that fails, try to treat as a folder and download all files
|
@@ -230,10 +233,11 @@ def show_mla_memory_usage(ctx):
|
|
230
233
|
@main.command(name="bootimg")
|
231
234
|
@click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
|
232
235
|
@click.option("-b", "--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
|
236
|
+
@click.option("-t", "--fwtype", type=click.Choice(["yocto", "elxr"], case_sensitive=False), default="yocto", show_default=True, help="Target firmware type.")
|
233
237
|
@click.option("-n", "--netboot", is_flag=True, default=False, show_default=True, help="Prepare image for network boot and launch TFTP server.")
|
234
238
|
@click.option("-a", "--autoflash", is_flag=True, default=False, show_default=True, help="Net boot the DevKit and automatically flash the internal storage - TBD")
|
235
239
|
@click.pass_context
|
236
|
-
def bootimg_cmd(ctx, version, boardtype, netboot, autoflash):
|
240
|
+
def bootimg_cmd(ctx, version, boardtype, netboot, autoflash, fwtype):
|
237
241
|
"""
|
238
242
|
Download and burn a removable media or setup TFTP boot.
|
239
243
|
|
@@ -255,6 +259,7 @@ def bootimg_cmd(ctx, version, boardtype, netboot, autoflash):
|
|
255
259
|
click.echo(f"📦 Preparing boot image:")
|
256
260
|
click.echo(f" 🔹 Version : {version}")
|
257
261
|
click.echo(f" 🔹 Board Type: {boardtype}")
|
262
|
+
click.echo(f" 🔹 F/W Type : {fwtype}")
|
258
263
|
click.echo(f" 🔹 F/W Flavor: headless")
|
259
264
|
|
260
265
|
try:
|
@@ -263,7 +268,7 @@ def bootimg_cmd(ctx, version, boardtype, netboot, autoflash):
|
|
263
268
|
setup_netboot(version, boardtype, internal, autoflash, flavor='headless')
|
264
269
|
click.echo("✅ Netboot image prepared and TFTP server is running.")
|
265
270
|
else:
|
266
|
-
write_image(version, boardtype,
|
271
|
+
write_image(version, boardtype, fwtype, internal, flavor='headless')
|
267
272
|
click.echo("✅ Boot image successfully written.")
|
268
273
|
click.echo("✅ Boot image successfully written.")
|
269
274
|
except Exception as e:
|
@@ -278,22 +283,44 @@ SDK_INDEPENDENT_COMPONENTS = {"optiview"}
|
|
278
283
|
ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
|
279
284
|
|
280
285
|
@main.command(name="install")
|
281
|
-
@click.argument("component",
|
282
|
-
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components)")
|
286
|
+
@click.argument("component", required=False)
|
287
|
+
@click.option("-v", "--version", help="SDK version (required for SDK-dependent components unless --metadata is provided)")
|
288
|
+
@click.option("-m", "--mirror", help="URL to a metadata.json file for generic installation")
|
289
|
+
@click.option("-t", "--tag", help="Tag of the package (optional)")
|
283
290
|
@click.pass_context
|
284
|
-
def install_cmd(ctx, component, version):
|
291
|
+
def install_cmd(ctx, component, version, mirror, tag):
|
285
292
|
"""
|
286
|
-
Install supported components such as SDKs or
|
293
|
+
Install supported components such as SDKs, tools, or generic packages via metadata.
|
287
294
|
|
288
295
|
Examples:
|
289
296
|
|
290
297
|
sima-cli install hostdriver -v 1.6.0
|
291
298
|
|
292
299
|
sima-cli install optiview
|
300
|
+
|
301
|
+
sima-cli install -m https://example.com/packages/foo/metadata.json
|
302
|
+
|
303
|
+
sima-cli install examples.llima -v 1.7.0
|
293
304
|
"""
|
294
|
-
component = component.lower()
|
295
305
|
internal = ctx.obj.get("internal", False)
|
296
306
|
|
307
|
+
# Metadata-based installation path
|
308
|
+
if mirror:
|
309
|
+
if component:
|
310
|
+
click.echo(f"⚠️ Component '{component}' is ignored when using --metadata. Proceeding with metadata-based installation.")
|
311
|
+
click.echo(f"🔧 Installing generic component from metadata URL: {mirror}")
|
312
|
+
if install_from_metadata(metadata_url=mirror, internal=internal):
|
313
|
+
click.echo("✅ Installation complete.")
|
314
|
+
return
|
315
|
+
|
316
|
+
# No component and no metadata: error
|
317
|
+
if not component:
|
318
|
+
click.echo("❌ You must specify either a component name or provide --metadata.")
|
319
|
+
ctx.exit(1)
|
320
|
+
|
321
|
+
component = component.lower()
|
322
|
+
|
323
|
+
# Validate version requirement
|
297
324
|
if component in SDK_DEPENDENT_COMPONENTS and not version:
|
298
325
|
click.echo(f"❌ The component '{component}' requires a specific SDK version. Please provide one using -v.")
|
299
326
|
ctx.exit(1)
|
@@ -301,7 +328,7 @@ def install_cmd(ctx, component, version):
|
|
301
328
|
if component in SDK_INDEPENDENT_COMPONENTS and version:
|
302
329
|
click.echo(f"ℹ️ The component '{component}' does not require an SDK version. Ignoring -v {version}.")
|
303
330
|
|
304
|
-
#
|
331
|
+
# Hardcoded component installation
|
305
332
|
if component == "palette":
|
306
333
|
click.echo(f"🔧 Installing SDK component 'palette' for version {version} is not implemented yet...")
|
307
334
|
elif component == "hostdriver":
|
@@ -310,9 +337,20 @@ def install_cmd(ctx, component, version):
|
|
310
337
|
elif component == "optiview":
|
311
338
|
click.echo("🔧 Installing tool 'optiview'...")
|
312
339
|
install_optiview()
|
340
|
+
else:
|
341
|
+
# Case 4: Try to resolve metadata URL from version + tag
|
342
|
+
try:
|
343
|
+
metadata_url = metadata_resolver(component, version, tag)
|
344
|
+
click.echo(f"🔧 Installing '{component}' from resolved metadata: {metadata_url}")
|
345
|
+
if install_from_metadata(metadata_url=metadata_url, internal=internal):
|
346
|
+
click.echo("✅ Installation complete.")
|
347
|
+
except Exception as e:
|
348
|
+
click.echo(f"❌ Failed to resolve metadata for component '{component}': {e}")
|
349
|
+
ctx.exit(1)
|
313
350
|
|
314
351
|
click.echo("✅ Installation complete.")
|
315
352
|
|
353
|
+
|
316
354
|
# ----------------------
|
317
355
|
# Serial Subcommands
|
318
356
|
# ----------------------
|
@@ -377,14 +415,39 @@ def nvme_cmd(ctx, operation):
|
|
377
415
|
elif operation == "remount":
|
378
416
|
try:
|
379
417
|
nvme_remount()
|
380
|
-
click.echo("✅ NVMe drive successfully remounted at /mnt/nvme.")
|
381
418
|
except Exception as e:
|
382
419
|
click.echo(f"❌ Failed to remount NVMe drive: {e}")
|
420
|
+
ctx.exit(1)
|
383
421
|
|
384
422
|
else:
|
385
423
|
click.echo(f"❌ Unsupported NVMe operation: {operation}")
|
386
424
|
ctx.exit(1)
|
387
425
|
|
426
|
+
# ----------------------
|
427
|
+
# NVME Subcommands
|
428
|
+
# ----------------------
|
429
|
+
NVME_OPERATIONS = {"format"}
|
430
|
+
@main.command(name="sdcard")
|
431
|
+
@click.argument("operation", type=click.Choice(NVME_OPERATIONS, case_sensitive=False))
|
432
|
+
@click.pass_context
|
433
|
+
def sdcard_cmd(ctx, operation):
|
434
|
+
"""
|
435
|
+
Prepare the SD Card as a data storage device for MLSoc DevKit or Modalix Early Access Unit
|
436
|
+
|
437
|
+
Available operations:
|
438
|
+
|
439
|
+
format - Format the SD Card
|
440
|
+
|
441
|
+
Example:
|
442
|
+
sima-cli sdcard format
|
443
|
+
"""
|
444
|
+
operation = operation.lower()
|
445
|
+
|
446
|
+
if operation == "format":
|
447
|
+
sdcard_format()
|
448
|
+
|
449
|
+
|
450
|
+
|
388
451
|
# ----------------------
|
389
452
|
# App Zoo Subcommands
|
390
453
|
# ----------------------
|
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.")
|