runtime-sdk 0.4.12__tar.gz → 0.4.13__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtime-sdk
3
- Version: 0.4.12
3
+ Version: 0.4.13
4
4
  Summary: Runtime Python SDK and CLI
5
5
  Project-URL: Repository, https://github.com/The-Money-Company-Limited/runtimevm
6
6
  Project-URL: Issues, https://github.com/The-Money-Company-Limited/runtimevm/issues
@@ -47,6 +47,8 @@ Or pass `--base-url` per command.
47
47
 
48
48
  ```bash
49
49
  # Auth
50
+ runtime # first run in a real terminal prompts for email + verification code, then creates your first computer
51
+ runtime login # interactive email + verification code flow
50
52
  runtime login you@example.com
51
53
  runtime verify 123456
52
54
  runtime whoami
@@ -28,6 +28,8 @@ Or pass `--base-url` per command.
28
28
 
29
29
  ```bash
30
30
  # Auth
31
+ runtime # first run in a real terminal prompts for email + verification code, then creates your first computer
32
+ runtime login # interactive email + verification code flow
31
33
  runtime login you@example.com
32
34
  runtime verify 123456
33
35
  runtime whoami
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "runtime-sdk"
3
- version = "0.4.12"
3
+ version = "0.4.13"
4
4
  description = "Runtime Python SDK and CLI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -263,11 +263,6 @@ def main(argv: list[str] | None = None) -> int:
263
263
  parser = build_parser()
264
264
  args = parser.parse_args(argv)
265
265
 
266
- # Bare `runtime` → create flow.
267
- if args.command is None:
268
- args.command = "create"
269
- args.name = None
270
-
271
266
  try:
272
267
  config = load_config()
273
268
  base_url = (
@@ -278,6 +273,13 @@ def main(argv: list[str] | None = None) -> int:
278
273
  )
279
274
  config.base_url = base_url
280
275
 
276
+ # Bare `runtime` → first-run onboarding for interactive users, create otherwise.
277
+ if args.command is None:
278
+ if _interactive() and not config.api_key:
279
+ return handle_onboarding(config)
280
+ args.command = "create"
281
+ args.name = None
282
+
281
283
  if args.command == "signup":
282
284
  return handle_signup(config, args.email, args.name)
283
285
  if args.command == "verify":
@@ -1181,14 +1183,7 @@ def _looks_like_api_key(value: str | None) -> bool:
1181
1183
  return candidate.startswith("rt_live_")
1182
1184
 
1183
1185
 
1184
- def _start_verification_flow(config: RuntimeConfig, email: str | None, name: str | None) -> int:
1185
- if email is None:
1186
- if not _interactive():
1187
- return report_error("email is required")
1188
- email = _prompt_text("Email")
1189
- if not email:
1190
- return report_error("email is required")
1191
-
1186
+ def _begin_verification_flow(config: RuntimeConfig, email: str, name: str | None) -> dict[str, Any]:
1192
1187
  client = RuntimeClient(base_url=config.base_url)
1193
1188
  result = client.start_verification(email, name)
1194
1189
  config.pending_signup = PendingSignup(
@@ -1197,6 +1192,19 @@ def _start_verification_flow(config: RuntimeConfig, email: str | None, name: str
1197
1192
  expires_at=result.get("expires_at"),
1198
1193
  )
1199
1194
  save_config(config)
1195
+ return result
1196
+
1197
+
1198
+
1199
+ def _start_verification_flow(config: RuntimeConfig, email: str | None, name: str | None) -> int:
1200
+ if email is None:
1201
+ if not _interactive():
1202
+ return report_error("email is required")
1203
+ email = _prompt_text("Email")
1204
+ if not email:
1205
+ return report_error("email is required")
1206
+
1207
+ result = _begin_verification_flow(config, email, name)
1200
1208
 
1201
1209
  def render(payload: dict[str, Any]) -> None:
1202
1210
  _UI.console().print(
@@ -1209,6 +1217,33 @@ def _start_verification_flow(config: RuntimeConfig, email: str | None, name: str
1209
1217
  return report_success(result, render)
1210
1218
 
1211
1219
 
1220
+
1221
+ def _interactive_email_auth_flow(config: RuntimeConfig, email: str | None, name: str | None) -> int:
1222
+ pending = config.pending_signup
1223
+ if email is None and pending is not None and pending.email:
1224
+ _UI.console().print(
1225
+ f"[green]✓[/green] verification code already sent to [bold]{pending.email}[/bold]"
1226
+ )
1227
+ code = _prompt_text("Verification code")
1228
+ if not code:
1229
+ return report_error("verification code is required")
1230
+ return handle_verify(config, code, pending.flow_id)
1231
+
1232
+ if email is None:
1233
+ email = _prompt_text("Email")
1234
+ if not email:
1235
+ return report_error("email is required")
1236
+
1237
+ _begin_verification_flow(config, email, name)
1238
+ _UI.console().print(
1239
+ f"[green]✓[/green] verification code sent to [bold]{email}[/bold]"
1240
+ )
1241
+ code = _prompt_text("Verification code")
1242
+ if not code:
1243
+ return report_error("verification code is required")
1244
+ return handle_verify(config, code, None)
1245
+
1246
+
1212
1247
  def _login_with_api_key(config: RuntimeConfig, api_key: str | None) -> int:
1213
1248
  if api_key is None or api_key == "":
1214
1249
  if not _interactive():
@@ -1235,6 +1270,8 @@ def _login_with_api_key(config: RuntimeConfig, api_key: str | None) -> int:
1235
1270
 
1236
1271
 
1237
1272
  def handle_signup(config: RuntimeConfig, email: str | None, name: str | None) -> int:
1273
+ if _interactive():
1274
+ return _interactive_email_auth_flow(config, email, name)
1238
1275
  return _start_verification_flow(config, email, name)
1239
1276
 
1240
1277
 
@@ -1284,6 +1321,8 @@ def handle_login(
1284
1321
  if _looks_like_api_key(identifier):
1285
1322
  return _login_with_api_key(config, identifier)
1286
1323
 
1324
+ if _interactive():
1325
+ return _interactive_email_auth_flow(config, identifier, name)
1287
1326
  return _start_verification_flow(config, identifier, name)
1288
1327
 
1289
1328
 
@@ -1369,6 +1408,14 @@ def handle_api_keys_revoke(config: RuntimeConfig, key_id: str | None) -> int:
1369
1408
 
1370
1409
 
1371
1410
 
1411
+ def handle_onboarding(config: RuntimeConfig) -> int:
1412
+ code = handle_login(config, None, None, None)
1413
+ if code != 0:
1414
+ return code
1415
+ return handle_create(config, None, None, None, None)
1416
+
1417
+
1418
+
1372
1419
  def handle_create(
1373
1420
  config: RuntimeConfig,
1374
1421
  name: str | None,
@@ -2141,7 +2188,7 @@ def _strip_terminal_noise(text: str) -> str:
2141
2188
  filtered: list[str] = []
2142
2189
  for line in lines:
2143
2190
  stripped = line.rstrip("\r\n")
2144
- if stripped == "Running bootstrap command: export PS1='\\[\\033[1;34m\\]runtime@\\h\\[\\033[0m\\]:\\w\\$ '":
2191
+ if stripped.startswith("Running bootstrap command: export PS1='\\[\\033[1;34m\\]runtime@"):
2145
2192
  continue
2146
2193
  if stripped == "Connected! Press Ctrl+] to exit.":
2147
2194
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtime-sdk
3
- Version: 0.4.12
3
+ Version: 0.4.13
4
4
  Summary: Runtime Python SDK and CLI
5
5
  Project-URL: Repository, https://github.com/The-Money-Company-Limited/runtimevm
6
6
  Project-URL: Issues, https://github.com/The-Money-Company-Limited/runtimevm/issues
@@ -47,6 +47,8 @@ Or pass `--base-url` per command.
47
47
 
48
48
  ```bash
49
49
  # Auth
50
+ runtime # first run in a real terminal prompts for email + verification code, then creates your first computer
51
+ runtime login # interactive email + verification code flow
50
52
  runtime login you@example.com
51
53
  runtime verify 123456
52
54
  runtime whoami
File without changes