sima-cli 0.0.29__py3-none-any.whl → 0.0.30__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.29"
2
+ __version__ = "0.0.30"
@@ -78,55 +78,116 @@ def _fetch_and_store_csrf_token(session: requests.Session) -> str:
78
78
 
79
79
 
80
80
  def login_external():
81
- """Interactive login workflow with CSRF token, cookie caching, and dummy session validation."""
81
+ """Interactive login workflow with CSRF token, cookie caching, and TOTP handling."""
82
82
  for attempt in range(1, 4):
83
83
  session = requests.Session()
84
84
  session.headers.update(HEADERS)
85
85
 
86
86
  _load_cookie_jar(session)
87
- csrf_token = _load_csrf_token()
88
-
89
- if not csrf_token:
90
- csrf_token = _fetch_and_store_csrf_token(session)
91
-
87
+ csrf_token = _load_csrf_token() or _fetch_and_store_csrf_token(session)
92
88
  if not csrf_token:
93
89
  click.echo("❌ CSRF token is missing or invalid.")
94
90
  continue
95
-
96
91
  session.headers["X-CSRF-Token"] = csrf_token
97
92
 
98
93
  if _is_session_valid(session):
99
94
  click.echo("🚀 You are already logged in.")
100
95
  return session
101
96
 
102
- # Prompt user login
97
+ # Fresh login prompt
103
98
  _delete_auth_files()
104
99
  click.echo(f"🔐 Sima.ai Developer Portal Login Attempt {attempt}/3")
105
100
  username = click.prompt("Email or Username")
106
101
  password = getpass.getpass("Password: ")
107
102
 
108
- login_data = {
103
+ # Base payload (no TOTP yet)
104
+ base_data = {
109
105
  "login": username,
110
106
  "password": password,
111
- "second_factor_method": "1"
107
+ "second_factor_method": "1", # TOTP
112
108
  }
113
109
 
114
- try:
115
- resp = session.post(LOGIN_URL, data=login_data)
116
- name = resp.json().get('users')[0]['name'] if 'users' in resp.json() else ''
117
- if resp.status_code != 200:
118
- click.echo(f"⚠️ Login request returned status {resp.status_code}")
119
- continue
120
- except Exception as e:
121
- click.echo(f"❌ Login request failed: {e}")
122
- continue
123
-
124
- if _is_session_valid(session):
110
+ def _post_login(payload: dict):
111
+ """POST and return (status_code, json or None, text) with robust error handling."""
112
+ try:
113
+ resp = session.post(LOGIN_URL, data=payload, timeout=30)
114
+ except Exception as e:
115
+ return None, None, f"request failed: {e}"
116
+ j = None
117
+ try:
118
+ j = resp.json()
119
+ except Exception:
120
+ pass
121
+ return resp.status_code, j, (j or resp.text)
122
+
123
+ # First try without TOTP (server may ask for it)
124
+ status, j, raw = _post_login(base_data)
125
+
126
+ # Helper: decide if success
127
+ def _success():
128
+ # Prefer server 'ok': True, but also double-check the session cookie validity
129
+ if j and j.get("ok") is True:
130
+ return True
131
+ return _is_session_valid(session)
132
+
133
+ # If immediate success
134
+ if status == 200 and _success():
125
135
  _save_cookie_jar(session)
126
- click.echo(f" Login successful. Welcome to Sima Developer Portal, {name}!")
136
+ welcome = (j.get("users", [{}])[0].get("name") if isinstance(j, dict) else "") or ""
137
+ click.echo(f"✅ Login successful. Welcome to Sima Developer Portal{', ' + welcome if welcome else ''}!")
127
138
  return session
139
+
140
+ # See if TOTP is required/invalid; then prompt and retry up to 3 times
141
+ def _needs_totp(payload_json):
142
+ if not isinstance(payload_json, dict):
143
+ return False
144
+ if payload_json.get("totp_enabled") is True:
145
+ return True
146
+ reason = payload_json.get("reason") or payload_json.get("error")
147
+ return str(reason) in {"invalid_second_factor", "second_factor_required"}
148
+
149
+ if _needs_totp(j):
150
+ # Try up to 3 TOTP attempts within this login attempt
151
+ for totp_try in range(1, 4):
152
+ totp = click.prompt(f"🔢 Enter TOTP code (attempt {totp_try}/3)", hide_input=True)
153
+ data = dict(base_data)
154
+ data["second_factor_token"] = totp
155
+
156
+ status, j2, raw2 = _post_login(data)
157
+ if status == 200 and (j2 and j2.get("ok") is True or _is_session_valid(session)):
158
+ _save_cookie_jar(session)
159
+ welcome = (j2.get("users", [{}])[0].get("name") if isinstance(j2, dict) else "") or ""
160
+ click.echo(f"✅ Login successful. Welcome to Sima Developer Portal{', ' + welcome if welcome else ''}!")
161
+ return session
162
+
163
+ # If still invalid 2FA, let user try again; otherwise break to outer loop
164
+ msg = ""
165
+ if isinstance(j2, dict):
166
+ reason = j2.get("reason") or j2.get("error") or ""
167
+ msg = f" ({reason})" if reason else ""
168
+ if isinstance(j2, dict) and str(j2.get("reason")) in {"invalid_second_factor"}:
169
+ click.echo(f"❌ Invalid authentication code. Please try again.{msg}")
170
+ continue
171
+ else:
172
+ click.echo(f"❌ Login failed with TOTP{msg}.")
173
+ break # go to next overall attempt
174
+
175
+ # exhausted TOTP tries
176
+ click.echo("❌ TOTP verification failed after 3 attempts.")
177
+ continue # next overall attempt
178
+
179
+ # Not a TOTP case; report error and continue
180
+ err_detail = ""
181
+ if isinstance(j, dict):
182
+ err_detail = j.get("error") or j.get("message") or ""
183
+ reason = j.get("reason")
184
+ if reason and reason != err_detail:
185
+ err_detail = f"{err_detail} ({reason})" if err_detail else reason
128
186
  else:
129
- click.echo("❌ Login failed.")
187
+ err_detail = str(raw)[:200]
188
+
189
+ click.echo(f"❌ Login failed. {err_detail or 'Please check your credentials and try again.'}")
130
190
 
131
191
  click.echo("❌ Login failed after 3 attempts.")
132
192
  raise SystemExit(1)
193
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sima-cli
3
- Version: 0.0.29
3
+ Version: 0.0.30
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
@@ -1,11 +1,11 @@
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=2CZTSSWitwWN8SjkJ0aMqI9jsY-Mo2BPRi6u1w3Ympk,49
3
+ sima_cli/__version__.py,sha256=fcUZK1xhI360Deo68dmn4Df_ffnBdYiwwdvLTu09P58,49
4
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
8
- sima_cli/auth/basic_auth.py,sha256=pcocI6v496vC7v5MLYPZq3AEgD-2DdkzNFZiKsbx3eQ,4290
8
+ sima_cli/auth/basic_auth.py,sha256=mEmPrj32TVu1s34xR_UrJlIKHA3xBh98i_FzIZvAWag,7364
9
9
  sima_cli/auth/login.py,sha256=yCYXWgrfbP4jSTZ3hITfxlgHkdVQVzsd8hQKpqaqCKs,3780
10
10
  sima_cli/data/resources_internal.yaml,sha256=zlQD4cSnZK86bLtTWuvEudZTARKiuIKmB--Jv4ajL8o,200
11
11
  sima_cli/data/resources_public.yaml,sha256=U7hmUomGeQ2ULdo1BU2OQHr0PyKBamIdK9qrutDlX8o,201
@@ -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.29.dist-info/licenses/LICENSE,sha256=a260OFuV4SsMZ6sQCkoYbtws_4o2deFtbnT9kg7Rfd4,1082
47
+ sima_cli-0.0.30.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.29.dist-info/METADATA,sha256=1MSfBv5taVOiFdadoLN9hElB33o0aKhHRgU-EgnH-e8,3705
57
- sima_cli-0.0.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
- sima_cli-0.0.29.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
59
- sima_cli-0.0.29.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
60
- sima_cli-0.0.29.dist-info/RECORD,,
56
+ sima_cli-0.0.30.dist-info/METADATA,sha256=CywF-g-sv44c9qK1UIAu3AROiPOwnPISqiSSQMnOEiY,3705
57
+ sima_cli-0.0.30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
+ sima_cli-0.0.30.dist-info/entry_points.txt,sha256=xRYrDq1nCs6R8wEdB3c1kKuimxEjWJkHuCzArQPT0Xk,47
59
+ sima_cli-0.0.30.dist-info/top_level.txt,sha256=FtrbAUdHNohtEPteOblArxQNwoX9_t8qJQd59fagDlc,15
60
+ sima_cli-0.0.30.dist-info/RECORD,,