sima-cli 0.0.25__py3-none-any.whl → 0.0.27__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 +1 -1
- sima_cli/install/metadata_installer.py +54 -16
- sima_cli/serial/serial.py +47 -8
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.dist-info}/METADATA +1 -1
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.dist-info}/RECORD +10 -10
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.dist-info}/WHEEL +0 -0
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.dist-info}/entry_points.txt +0 -0
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.dist-info}/licenses/LICENSE +0 -0
- {sima_cli-0.0.25.dist-info → sima_cli-0.0.27.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.27"
|
sima_cli/cli.py
CHANGED
@@ -149,7 +149,7 @@ def download(ctx, url, dest):
|
|
149
149
|
default='edgeai',
|
150
150
|
help="Optional SSH password for remote board (default is 'edgeai')."
|
151
151
|
)
|
152
|
-
@click.option("-f", "--flavor", type=click.Choice(["headless", "full"], case_sensitive=False), default="headless", show_default=True, help="firmware flavor.")
|
152
|
+
@click.option("-f", "--flavor", type=click.Choice(["headless", "full"], case_sensitive=False), default="headless", show_default=True, help="firmware flavor, full image supports NVMe, GUI on Modalix DevKit.")
|
153
153
|
@click.pass_context
|
154
154
|
def update(ctx, version_or_url, ip, yes, passwd, flavor):
|
155
155
|
"""
|
@@ -7,6 +7,7 @@ import sys
|
|
7
7
|
import shutil
|
8
8
|
import tarfile
|
9
9
|
import zipfile
|
10
|
+
import stat
|
10
11
|
from urllib.parse import urlparse
|
11
12
|
|
12
13
|
from typing import Dict
|
@@ -205,31 +206,68 @@ def _extract_tar_streaming(tar_path: Path, extract_dir: Path):
|
|
205
206
|
|
206
207
|
print(f"✅ Extracted {extracted_files} files to {extract_dir}/")
|
207
208
|
|
208
|
-
def _extract_zip_streaming(zip_path: Path, extract_dir: Path):
|
209
|
+
def _extract_zip_streaming(zip_path: Path, extract_dir: Path, overwrite: bool = True):
|
209
210
|
"""
|
210
|
-
Extract a .zip file using streaming
|
211
|
-
|
211
|
+
Extract a .zip file using streaming and flatten one top-level directory if present.
|
212
|
+
- Handles directory entries correctly
|
213
|
+
- Preserves unix perms when available
|
214
|
+
- Zip-slip safe
|
212
215
|
"""
|
213
|
-
|
214
|
-
|
216
|
+
def strip_top_level(p: str) -> Path:
|
217
|
+
parts = Path(p).parts
|
218
|
+
if not parts:
|
219
|
+
return Path()
|
220
|
+
return Path(*parts[1:]) if len(parts) > 1 else Path(parts[0])
|
221
|
+
|
222
|
+
extract_dir.mkdir(parents=True, exist_ok=True)
|
223
|
+
|
224
|
+
with zipfile.ZipFile(zip_path, "r") as zf:
|
225
|
+
members = zf.infolist()
|
215
226
|
with tqdm(total=len(members), desc=f"📦 Extracting {zip_path.name}", unit="file") as pbar:
|
216
|
-
for
|
217
|
-
#
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
227
|
+
for info in members:
|
228
|
+
# Compute flattened path
|
229
|
+
stripped = strip_top_level(info.filename)
|
230
|
+
|
231
|
+
# Some zips can have '' or '.' entries; skip them
|
232
|
+
if str(stripped).strip() in {"", ".", "./"}:
|
233
|
+
pbar.update(1)
|
234
|
+
continue
|
235
|
+
|
236
|
+
target = (extract_dir / stripped).resolve()
|
223
237
|
|
224
|
-
|
225
|
-
|
238
|
+
# Zip-slip guard: ensure target stays under extract_dir
|
239
|
+
if not str(target).startswith(str(extract_dir.resolve()) + os.sep):
|
240
|
+
pbar.update(1)
|
241
|
+
continue # or raise RuntimeError("Unsafe zip path detected")
|
226
242
|
|
227
|
-
|
243
|
+
if info.is_dir():
|
244
|
+
target.mkdir(parents=True, exist_ok=True)
|
245
|
+
pbar.update(1)
|
246
|
+
continue
|
247
|
+
|
248
|
+
# Ensure parent exists
|
249
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
250
|
+
|
251
|
+
# Skip if exists and not overwriting
|
252
|
+
if target.exists() and not overwrite:
|
253
|
+
pbar.update(1)
|
254
|
+
continue
|
255
|
+
|
256
|
+
# Stream copy the file
|
257
|
+
with zf.open(info) as src, open(target, "wb") as dst:
|
228
258
|
shutil.copyfileobj(src, dst)
|
229
259
|
|
260
|
+
# Preserve unix permissions if present
|
261
|
+
perms = info.external_attr >> 16
|
262
|
+
if perms and not stat.S_ISDIR(perms):
|
263
|
+
try:
|
264
|
+
os.chmod(target, perms)
|
265
|
+
except Exception:
|
266
|
+
pass
|
267
|
+
|
230
268
|
pbar.update(1)
|
231
269
|
|
232
|
-
print(f"✅ Extracted {len(members)}
|
270
|
+
print(f"✅ Extracted {len(members)} entries to {extract_dir}/")
|
233
271
|
|
234
272
|
def _combine_multipart_files(folder: str):
|
235
273
|
"""
|
sima_cli/serial/serial.py
CHANGED
@@ -2,6 +2,7 @@ import platform
|
|
2
2
|
import subprocess
|
3
3
|
import shutil
|
4
4
|
import click
|
5
|
+
import os
|
5
6
|
from sima_cli.utils.env import is_sima_board
|
6
7
|
|
7
8
|
def connect_serial(ctx, baud):
|
@@ -74,11 +75,41 @@ def _connect_linux(baud):
|
|
74
75
|
if not shutil.which(terminal):
|
75
76
|
click.echo("⚙️ 'picocom' is not installed. Attempting to install via apt...")
|
76
77
|
if shutil.which("apt-get"):
|
77
|
-
|
78
|
-
|
78
|
+
try:
|
79
|
+
subprocess.run(["sudo", "apt-get", "update"], check=True)
|
80
|
+
subprocess.run(["sudo", "apt-get", "install", "-y", "picocom"], check=True)
|
81
|
+
except subprocess.CalledProcessError:
|
82
|
+
click.echo("❌ Failed to install picocom (possibly due to lack of sudo privileges). Checking for minicom...")
|
83
|
+
terminal = "minicom"
|
84
|
+
if not shutil.which(terminal):
|
85
|
+
click.echo("❌ Neither 'picocom' nor 'minicom' is available. Please request the admin to install one manually.")
|
86
|
+
raise SystemExit(1)
|
87
|
+
# Create minicom config file to match working settings
|
88
|
+
config_file = os.path.expanduser("~/.minirc.custom")
|
89
|
+
with open(config_file, "w") as f:
|
90
|
+
f.write("pu port /dev/ttyUSB0\n")
|
91
|
+
f.write(f"pu baudrate {baud}\n")
|
92
|
+
f.write("pu bits 8\n")
|
93
|
+
f.write("pu parity N\n")
|
94
|
+
f.write("pu stopbits 1\n")
|
95
|
+
f.write("pu rtscts No\n")
|
96
|
+
f.write("pu xonxoff No\n")
|
79
97
|
else:
|
80
|
-
click.echo("❌ 'apt-get' not found.
|
81
|
-
|
98
|
+
click.echo("❌ 'apt-get' not found. Checking for minicom...")
|
99
|
+
terminal = "minicom"
|
100
|
+
if not shutil.which(terminal):
|
101
|
+
click.echo("❌ Neither 'picocom' nor 'minicom' is available. Please request the admin to install one manually.")
|
102
|
+
raise SystemExit(1)
|
103
|
+
# Create minicom config file to match working settings
|
104
|
+
config_file = os.path.expanduser("~/.minirc.custom")
|
105
|
+
with open(config_file, "w") as f:
|
106
|
+
f.write("pu port /dev/ttyUSB0\n")
|
107
|
+
f.write(f"pu baudrate {baud}\n")
|
108
|
+
f.write("pu bits 8\n")
|
109
|
+
f.write("pu parity N\n")
|
110
|
+
f.write("pu stopbits 1\n")
|
111
|
+
f.write("pu rtscts No\n")
|
112
|
+
f.write("pu xonxoff No\n")
|
82
113
|
|
83
114
|
ports = sorted(
|
84
115
|
subprocess.getoutput("ls /dev/ttyUSB* 2>/dev/null").splitlines()
|
@@ -88,11 +119,19 @@ def _connect_linux(baud):
|
|
88
119
|
raise SystemExit(1)
|
89
120
|
|
90
121
|
port = ports[0]
|
91
|
-
click.echo(f"🔌 Connecting to {port} with
|
122
|
+
click.echo(f"🔌 Connecting to {port} with {terminal} ({baud} 8N1)...")
|
92
123
|
try:
|
93
|
-
|
94
|
-
|
95
|
-
|
124
|
+
if terminal == "picocom":
|
125
|
+
click.echo("🧷 To exit: Press Ctrl + A, then Ctrl + X")
|
126
|
+
subprocess.run(
|
127
|
+
["sudo", terminal, "-b", f"{baud}", "--databits", "8", "--parity", "n", "--stopbits", "1", port]
|
128
|
+
)
|
129
|
+
else: # minicom
|
130
|
+
config_file = os.path.expanduser("~/.minirc.custom")
|
131
|
+
click.echo("🧷 To exit: Press Ctrl + A, then Q")
|
132
|
+
subprocess.run(
|
133
|
+
["sudo", terminal, "-C", config_file, "-D", port]
|
134
|
+
)
|
96
135
|
except KeyboardInterrupt:
|
97
136
|
click.echo("\n❎ Serial connection interrupted by user.")
|
98
137
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
sima_cli/__init__.py,sha256=Nb2jSg9-CX1XvSc1c21U9qQ3atINxphuNkNfmR-9P3o,332
|
2
2
|
sima_cli/__main__.py,sha256=ehzD6AZ7zGytC2gLSvaJatxeD0jJdaEvNJvwYeGsWOg,69
|
3
|
-
sima_cli/__version__.py,sha256=
|
4
|
-
sima_cli/cli.py,sha256=
|
3
|
+
sima_cli/__version__.py,sha256=gxBLTfuQW1ckn_VwGnJeXR1eVJD4qo__oTN9DM-b2n8,49
|
4
|
+
sima_cli/cli.py,sha256=GYmQ7_XObl9VgFwuWWkWDo-_Y_Vn6lM53F7mKiYGubI,17126
|
5
5
|
sima_cli/app_zoo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
sima_cli/app_zoo/app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
sima_cli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -14,7 +14,7 @@ sima_cli/download/downloader.py,sha256=nCBrr_0WdnKTIyecwKpg1sCdfm_4PSQTRPwEbiezy
|
|
14
14
|
sima_cli/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
sima_cli/install/hostdriver.py,sha256=kAWDLebs60mbWIyTbUxmNrChcKW1uD5r7FtWNSUVUE4,5852
|
16
16
|
sima_cli/install/metadata_info.py,sha256=wmMqwzGfXbuilkqaxRVrFOzOtTOiONkmPCyA2oDAQpA,2168
|
17
|
-
sima_cli/install/metadata_installer.py,sha256=
|
17
|
+
sima_cli/install/metadata_installer.py,sha256=itAvglQO4ZAJBGB2nBuyYJdsMb41gN183MaBEDgU2_Y,17935
|
18
18
|
sima_cli/install/metadata_validator.py,sha256=7954rp9vFRNnqmIMvCVTjq40kUIEbGXzfc8HmQmChe0,5221
|
19
19
|
sima_cli/install/optiview.py,sha256=i5eWVor-9MScEfrQm3Ty9OP4VpSsCgWvNh7AvYdZu7s,3365
|
20
20
|
sima_cli/install/palette.py,sha256=uRznoHa4Mv9ZXHp6AoqknfC3RxpYNKi9Ins756Cyifk,3930
|
@@ -25,7 +25,7 @@ sima_cli/network/network.py,sha256=ToDCQBfX0bUFEWWtfS8srImK5T11MX6R4MBQFM80faY,7
|
|
25
25
|
sima_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
26
|
sima_cli/sdk/syscheck.py,sha256=h9zCULW67y4i2hqiGc-hc1ucBDShA5FAe9NxwBGq-fM,4575
|
27
27
|
sima_cli/serial/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
sima_cli/serial/serial.py,sha256=
|
28
|
+
sima_cli/serial/serial.py,sha256=1We85F9-l1FZcsLFxRzxbfPxAHeSCVlBOUyOUpqNf_s,6202
|
29
29
|
sima_cli/storage/nvme.py,sha256=cCzYWcyPwcFu5pSMBkovsS4EwovaIMGolhEFStogXMA,4739
|
30
30
|
sima_cli/storage/sdcard.py,sha256=-WULjdV31-n8v5OOqfxR77qBbIK4hJnrD3xWxUVMoGI,6324
|
31
31
|
sima_cli/update/__init__.py,sha256=0P-z-rSaev40IhfJXytK3AFWv2_sdQU4Ry6ei2sEus0,66
|
@@ -44,7 +44,7 @@ sima_cli/utils/disk.py,sha256=66Kr631yhc_ny19up2aijfycWfD35AeLQOJgUsuH2hY,446
|
|
44
44
|
sima_cli/utils/env.py,sha256=IP5HrH0lE7RMSiBeXcEt5GCLMT5p-QQroG-uGzl5XFU,8181
|
45
45
|
sima_cli/utils/net.py,sha256=WVntA4CqipkNrrkA4tBVRadJft_pMcGYh4Re5xk3rqo,971
|
46
46
|
sima_cli/utils/network.py,sha256=UvqxbqbWUczGFyO-t1SybG7Q-x9kjUVRNIn_D6APzy8,1252
|
47
|
-
sima_cli-0.0.
|
47
|
+
sima_cli-0.0.27.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
|
48
48
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
tests/test_app_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
tests/test_auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -53,8 +53,8 @@ tests/test_download.py,sha256=t87DwxlHs26_ws9rpcHGwr_OrcRPd3hz6Zmm0vRee2U,4465
|
|
53
53
|
tests/test_firmware.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
54
|
tests/test_model_zoo.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
-
sima_cli-0.0.
|
57
|
-
sima_cli-0.0.
|
58
|
-
sima_cli-0.0.
|
59
|
-
sima_cli-0.0.
|
60
|
-
sima_cli-0.0.
|
56
|
+
sima_cli-0.0.27.dist-info/METADATA,sha256=lzAzsKQImaK7MEelhD0MDkPGTf2x8dyaRsXR3dj27bA,3705
|
57
|
+
sima_cli-0.0.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
58
|
+
sima_cli-0.0.27.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
|
59
|
+
sima_cli-0.0.27.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
|
60
|
+
sima_cli-0.0.27.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|