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 CHANGED
@@ -1,2 +1,2 @@
1
1
  # sima_cli/__version__.py
2
- __version__ = "0.0.21"
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.nvme.nvme import nvme_format, nvme_remount
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, 'yocto', internal, flavor='headless')
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", type=click.Choice(ALL_COMPONENTS, case_sensitive=False))
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 tools.
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
- # Perform the installation logic
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
  # ----------------------
@@ -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
- else:
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"✔ File already exists and is complete: {file_name}")
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.")