runtime-sdk 0.4.5__tar.gz → 0.4.6__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.
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/PKG-INFO +1 -1
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/pyproject.toml +1 -1
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk/cli.py +102 -12
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/PKG-INFO +1 -1
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/README.md +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk/__init__.py +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk/client.py +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk/config.py +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/SOURCES.txt +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/dependency_links.txt +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/entry_points.txt +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/requires.txt +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/scripts/runtime_sdk.egg-info/top_level.txt +0 -0
- {runtime_sdk-0.4.5 → runtime_sdk-0.4.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtime-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
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
|
|
@@ -10,6 +10,7 @@ import sys
|
|
|
10
10
|
import termios
|
|
11
11
|
import threading
|
|
12
12
|
import tty
|
|
13
|
+
from datetime import datetime, timezone
|
|
13
14
|
from typing import Any, Callable
|
|
14
15
|
from urllib.parse import urljoin, urlparse
|
|
15
16
|
|
|
@@ -109,10 +110,12 @@ class _UI:
|
|
|
109
110
|
@classmethod
|
|
110
111
|
def rich(cls) -> dict[str, Any]:
|
|
111
112
|
if cls._rich_mods is None:
|
|
113
|
+
from rich import box
|
|
112
114
|
from rich.panel import Panel
|
|
115
|
+
from rich.table import Table
|
|
113
116
|
from rich.text import Text
|
|
114
117
|
|
|
115
|
-
cls._rich_mods = {"Panel": Panel, "Text": Text}
|
|
118
|
+
cls._rich_mods = {"Panel": Panel, "Text": Text, "Table": Table, "box": box}
|
|
116
119
|
return cls._rich_mods
|
|
117
120
|
|
|
118
121
|
|
|
@@ -425,6 +428,43 @@ def _resolve_computer_ref(client: RuntimeClient, ref: str) -> str:
|
|
|
425
428
|
|
|
426
429
|
|
|
427
430
|
|
|
431
|
+
def _computer_name(c: dict[str, Any]) -> str:
|
|
432
|
+
return str(c.get("slug") or c.get("id") or "?")
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _computer_url_label(c: dict[str, Any]) -> str:
|
|
437
|
+
public_url = str(c.get("public_url") or "").strip()
|
|
438
|
+
if not public_url:
|
|
439
|
+
return ""
|
|
440
|
+
parsed = urlparse(public_url)
|
|
441
|
+
return parsed.netloc or public_url
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _computer_created_label(c: dict[str, Any]) -> str:
|
|
446
|
+
raw = str(c.get("created_at") or "").strip()
|
|
447
|
+
if not raw:
|
|
448
|
+
return ""
|
|
449
|
+
try:
|
|
450
|
+
created = datetime.fromisoformat(raw.replace("Z", "+00:00"))
|
|
451
|
+
except ValueError:
|
|
452
|
+
return raw
|
|
453
|
+
delta = datetime.now(timezone.utc) - created.astimezone(timezone.utc)
|
|
454
|
+
seconds = max(0, int(delta.total_seconds()))
|
|
455
|
+
if seconds < 60:
|
|
456
|
+
return f"{seconds}s ago"
|
|
457
|
+
minutes = seconds // 60
|
|
458
|
+
if minutes < 60:
|
|
459
|
+
return f"{minutes}m ago"
|
|
460
|
+
hours = minutes // 60
|
|
461
|
+
if hours < 24:
|
|
462
|
+
return f"{hours}h ago"
|
|
463
|
+
days = hours // 24
|
|
464
|
+
return f"{days}d ago"
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
428
468
|
def _computer_table_layout(computers: list[dict[str, Any]]) -> dict[str, int]:
|
|
429
469
|
term_width = shutil.get_terminal_size((100, 20)).columns
|
|
430
470
|
slug_width = max(
|
|
@@ -456,10 +496,10 @@ def _computer_table_layout(computers: list[dict[str, Any]]) -> dict[str, int]:
|
|
|
456
496
|
def _format_computer_row(c: dict[str, Any], widths: dict[str, int]) -> str:
|
|
457
497
|
return " │ ".join(
|
|
458
498
|
[
|
|
459
|
-
_fit_cell(
|
|
499
|
+
_fit_cell(_computer_name(c), widths["slug"]),
|
|
460
500
|
_fit_cell(_computer_status_label(c), widths["status"]),
|
|
461
|
-
_fit_cell(c
|
|
462
|
-
_fit_cell(c
|
|
501
|
+
_fit_cell(_computer_url_label(c), widths["public_url"]),
|
|
502
|
+
_fit_cell(_computer_created_label(c), widths["created_at"]),
|
|
463
503
|
]
|
|
464
504
|
)
|
|
465
505
|
|
|
@@ -468,9 +508,9 @@ def _computer_table_prompt(prompt: str, computers: list[dict[str, Any]]) -> tupl
|
|
|
468
508
|
widths = _computer_table_layout(computers)
|
|
469
509
|
header = " │ ".join(
|
|
470
510
|
[
|
|
471
|
-
_fit_cell("
|
|
511
|
+
_fit_cell("name", widths["slug"]),
|
|
472
512
|
_fit_cell("status", widths["status"]),
|
|
473
|
-
_fit_cell("
|
|
513
|
+
_fit_cell("url", widths["public_url"]),
|
|
474
514
|
_fit_cell("created", widths["created_at"]),
|
|
475
515
|
]
|
|
476
516
|
)
|
|
@@ -478,6 +518,12 @@ def _computer_table_prompt(prompt: str, computers: list[dict[str, Any]]) -> tupl
|
|
|
478
518
|
return f"{prompt}\n{header}\n{divider}", widths
|
|
479
519
|
|
|
480
520
|
|
|
521
|
+
|
|
522
|
+
def _computer_choice_title(c: dict[str, Any], widths: dict[str, int]) -> str:
|
|
523
|
+
return _format_computer_row(c, widths)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
481
527
|
def _render_computer_summary(computers: list[dict[str, Any]]) -> None:
|
|
482
528
|
mods = _UI.rich()
|
|
483
529
|
Panel, Text = mods["Panel"], mods["Text"]
|
|
@@ -517,6 +563,48 @@ def _render_computer_summary(computers: list[dict[str, Any]]) -> None:
|
|
|
517
563
|
)
|
|
518
564
|
|
|
519
565
|
|
|
566
|
+
|
|
567
|
+
def _render_computer_table(computers: list[dict[str, Any]], *, title: str | None = None) -> None:
|
|
568
|
+
mods = _UI.rich()
|
|
569
|
+
Table, box = mods["Table"], mods["box"]
|
|
570
|
+
table = Table(
|
|
571
|
+
title=title,
|
|
572
|
+
box=box.SIMPLE_HEAVY,
|
|
573
|
+
expand=False,
|
|
574
|
+
show_lines=False,
|
|
575
|
+
header_style="bold cyan",
|
|
576
|
+
title_style="bold white",
|
|
577
|
+
pad_edge=False,
|
|
578
|
+
)
|
|
579
|
+
table.add_column("Name", style="bold white", no_wrap=True)
|
|
580
|
+
table.add_column("Status", no_wrap=True)
|
|
581
|
+
table.add_column("URL", overflow="fold")
|
|
582
|
+
table.add_column("Created", style="dim", no_wrap=True, justify="right")
|
|
583
|
+
|
|
584
|
+
for computer in computers:
|
|
585
|
+
status = _computer_status_label(computer)
|
|
586
|
+
status_style = "white"
|
|
587
|
+
if "cold" in status:
|
|
588
|
+
status_style = "bold cyan"
|
|
589
|
+
elif "warm" in status or "running" in status:
|
|
590
|
+
status_style = "bold green"
|
|
591
|
+
elif "archiving" in status or "restoring" in status:
|
|
592
|
+
status_style = "bold yellow"
|
|
593
|
+
elif "creating" in status:
|
|
594
|
+
status_style = "bold magenta"
|
|
595
|
+
elif "orphaned" in status:
|
|
596
|
+
status_style = "bold red"
|
|
597
|
+
|
|
598
|
+
table.add_row(
|
|
599
|
+
_computer_name(computer),
|
|
600
|
+
f"[{status_style}]{status}[/{status_style}]",
|
|
601
|
+
_computer_url_label(computer),
|
|
602
|
+
_computer_created_label(computer),
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
_UI.console().print(table)
|
|
606
|
+
|
|
607
|
+
|
|
520
608
|
def _select_prompt(message: str, choices: list[Any]) -> Any:
|
|
521
609
|
return _UI.q().select(
|
|
522
610
|
message,
|
|
@@ -551,12 +639,13 @@ def _pick_computer(
|
|
|
551
639
|
return None
|
|
552
640
|
|
|
553
641
|
questionary = _UI.q()
|
|
554
|
-
|
|
642
|
+
widths = _computer_table_layout(computers)
|
|
643
|
+
_render_computer_table(computers)
|
|
555
644
|
choices = [
|
|
556
|
-
questionary.Choice(title=
|
|
645
|
+
questionary.Choice(title=_computer_choice_title(c, widths), value=c) for c in computers
|
|
557
646
|
]
|
|
558
647
|
choices.append(questionary.Choice(title="← cancel", value=None))
|
|
559
|
-
picked = _select_prompt(
|
|
648
|
+
picked = _select_prompt(prompt, choices).unsafe_ask()
|
|
560
649
|
return picked if isinstance(picked, dict) else None
|
|
561
650
|
|
|
562
651
|
|
|
@@ -1220,16 +1309,17 @@ def _interactive_list(config: RuntimeConfig) -> int:
|
|
|
1220
1309
|
return 0
|
|
1221
1310
|
|
|
1222
1311
|
_render_computer_summary(computers)
|
|
1223
|
-
|
|
1312
|
+
widths = _computer_table_layout(computers)
|
|
1313
|
+
_render_computer_table(computers, title="computers")
|
|
1224
1314
|
choices = [
|
|
1225
|
-
questionary.Choice(title=
|
|
1315
|
+
questionary.Choice(title=_computer_choice_title(c, widths), value=c)
|
|
1226
1316
|
for c in computers
|
|
1227
1317
|
]
|
|
1228
1318
|
choices.append(questionary.Separator())
|
|
1229
1319
|
choices.append(questionary.Choice(title="↻ refresh", value="__refresh__"))
|
|
1230
1320
|
choices.append(questionary.Choice(title="✕ quit", value="__quit__"))
|
|
1231
1321
|
|
|
1232
|
-
picked = _select_prompt(
|
|
1322
|
+
picked = _select_prompt("Pick a computer", choices).unsafe_ask()
|
|
1233
1323
|
|
|
1234
1324
|
if picked == "__quit__":
|
|
1235
1325
|
return 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtime-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|