runtime-sdk 0.4.14__tar.gz → 0.4.16__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.14 → runtime_sdk-0.4.16}/PKG-INFO +1 -1
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/pyproject.toml +1 -1
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk/cli.py +272 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk/client.py +42 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/PKG-INFO +1 -1
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/README.md +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk/__init__.py +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk/config.py +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/SOURCES.txt +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/dependency_links.txt +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/entry_points.txt +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/requires.txt +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/top_level.txt +0 -0
- {runtime_sdk-0.4.14 → runtime_sdk-0.4.16}/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.16
|
|
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
|
|
@@ -15,6 +15,7 @@ import termios
|
|
|
15
15
|
import threading
|
|
16
16
|
import time
|
|
17
17
|
import tty
|
|
18
|
+
import webbrowser
|
|
18
19
|
from datetime import datetime, timezone
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any, Callable
|
|
@@ -173,6 +174,28 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
173
174
|
api_keys_revoke = api_keys_subparsers.add_parser("revoke", help="Revoke an API key")
|
|
174
175
|
api_keys_revoke.add_argument("id", nargs="?", default=None, help="API key id")
|
|
175
176
|
|
|
177
|
+
secrets_cmd = subparsers.add_parser("secrets", help="Manage account-level secret sets")
|
|
178
|
+
secrets_subparsers = secrets_cmd.add_subparsers(dest="secrets_action", required=True)
|
|
179
|
+
secrets_subparsers.add_parser("list", aliases=["ls"], help="List secret sets")
|
|
180
|
+
secrets_show = secrets_subparsers.add_parser("show", help="Show one secret set")
|
|
181
|
+
secrets_show.add_argument("slug", nargs="?", default=None, help="Secret set slug")
|
|
182
|
+
secrets_set = secrets_subparsers.add_parser("set", help="Create or replace a secret set from KEY=VALUE pairs")
|
|
183
|
+
secrets_set.add_argument("slug", nargs="?", default=None, help="Secret set slug")
|
|
184
|
+
secrets_set.add_argument("pairs", nargs="*", help="KEY=VALUE pairs")
|
|
185
|
+
secrets_import = secrets_subparsers.add_parser("import", help="Create or replace a secret set from a .env file")
|
|
186
|
+
secrets_import.add_argument("slug", nargs="?", default=None, help="Secret set slug")
|
|
187
|
+
secrets_import.add_argument("--env-file", required=True, help="Path to .env file")
|
|
188
|
+
secrets_delete = secrets_subparsers.add_parser("delete", aliases=["rm"], help="Delete a secret set")
|
|
189
|
+
secrets_delete.add_argument("slug", nargs="?", default=None, help="Secret set slug")
|
|
190
|
+
|
|
191
|
+
integrations_cmd = subparsers.add_parser("integrations", help="Connect account-level integrations")
|
|
192
|
+
integrations_subparsers = integrations_cmd.add_subparsers(dest="integrations_action", required=True)
|
|
193
|
+
integrations_connect = integrations_subparsers.add_parser("connect", help="Connect an integration")
|
|
194
|
+
integrations_connect.add_argument("provider", choices=["github"])
|
|
195
|
+
integrations_connect.add_argument("--slug", default="github", help="Integration slug to create (default: github)")
|
|
196
|
+
integrations_connect.add_argument("--owner", default=None, help="GitHub username or org where the app is installed")
|
|
197
|
+
integrations_connect.add_argument("--no-open", action="store_true", help="Print the install URL instead of opening a browser")
|
|
198
|
+
|
|
176
199
|
create_cmd = subparsers.add_parser("create", help="Create a new computer with the starter app published by default")
|
|
177
200
|
create_cmd.add_argument(
|
|
178
201
|
"name",
|
|
@@ -297,6 +320,22 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
297
320
|
if args.api_keys_action == "revoke":
|
|
298
321
|
return handle_api_keys_revoke(config, args.id)
|
|
299
322
|
return report_error("unknown api-keys command")
|
|
323
|
+
if args.command == "secrets":
|
|
324
|
+
if args.secrets_action in ("list", "ls"):
|
|
325
|
+
return handle_secrets_list(config)
|
|
326
|
+
if args.secrets_action == "show":
|
|
327
|
+
return handle_secrets_show(config, args.slug)
|
|
328
|
+
if args.secrets_action == "set":
|
|
329
|
+
return handle_secrets_set(config, args.slug, args.pairs)
|
|
330
|
+
if args.secrets_action == "import":
|
|
331
|
+
return handle_secrets_import(config, args.slug, args.env_file)
|
|
332
|
+
if args.secrets_action in ("delete", "rm"):
|
|
333
|
+
return handle_secrets_delete(config, args.slug)
|
|
334
|
+
return report_error("unknown secrets command")
|
|
335
|
+
if args.command == "integrations":
|
|
336
|
+
if args.integrations_action == "connect":
|
|
337
|
+
return handle_integrations_connect(config, args.provider, args.slug, args.owner, no_open=args.no_open)
|
|
338
|
+
return report_error("unknown integrations command")
|
|
300
339
|
if args.command == "create":
|
|
301
340
|
return handle_create(
|
|
302
341
|
config,
|
|
@@ -1172,6 +1211,45 @@ def _render_api_key_created(payload: dict[str, Any]) -> None:
|
|
|
1172
1211
|
_UI.console().print(Panel(body, title="api key created", border_style="cyan", expand=False))
|
|
1173
1212
|
|
|
1174
1213
|
|
|
1214
|
+
|
|
1215
|
+
def _render_secret_sets_table(payload: dict[str, Any]) -> None:
|
|
1216
|
+
mods = _UI.rich()
|
|
1217
|
+
Table = mods["Table"]
|
|
1218
|
+
secret_sets = payload.get("secret_sets") or []
|
|
1219
|
+
if not isinstance(secret_sets, list) or not secret_sets:
|
|
1220
|
+
_UI.console().print("[yellow]no secret sets found[/yellow]")
|
|
1221
|
+
return
|
|
1222
|
+
|
|
1223
|
+
table = Table(title="Secret sets", expand=True, box=mods["box"].SIMPLE_HEAVY)
|
|
1224
|
+
table.add_column("Slug", style="bold white")
|
|
1225
|
+
table.add_column("Keys", style="white")
|
|
1226
|
+
table.add_column("Updated", style="dim", no_wrap=True, justify="right")
|
|
1227
|
+
|
|
1228
|
+
for entry in secret_sets:
|
|
1229
|
+
if not isinstance(entry, dict):
|
|
1230
|
+
continue
|
|
1231
|
+
keys = entry.get("keys") or []
|
|
1232
|
+
table.add_row(
|
|
1233
|
+
str(entry.get("slug") or "—"),
|
|
1234
|
+
", ".join(str(key) for key in keys) or "—",
|
|
1235
|
+
_relative_time_label(entry.get("updated_at")) or "—",
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
_UI.console().print(table)
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
def _render_secret_set_panel(payload: dict[str, Any]) -> None:
|
|
1243
|
+
mods = _UI.rich()
|
|
1244
|
+
Panel, Text = mods["Panel"], mods["Text"]
|
|
1245
|
+
body = Text()
|
|
1246
|
+
body.append(f"{'slug':<12}", style="dim")
|
|
1247
|
+
body.append(f"{payload.get('slug') or '—'}\n")
|
|
1248
|
+
body.append(f"{'keys':<12}", style="dim")
|
|
1249
|
+
body.append(", ".join(str(key) for key in (payload.get("keys") or [])) or "—")
|
|
1250
|
+
_UI.console().print(Panel(body, title="secret set", border_style="cyan", expand=False))
|
|
1251
|
+
|
|
1252
|
+
|
|
1175
1253
|
# --------------------------------------------------------------------------- #
|
|
1176
1254
|
# Command handlers #
|
|
1177
1255
|
# --------------------------------------------------------------------------- #
|
|
@@ -1407,6 +1485,200 @@ def handle_api_keys_revoke(config: RuntimeConfig, key_id: str | None) -> int:
|
|
|
1407
1485
|
|
|
1408
1486
|
|
|
1409
1487
|
|
|
1488
|
+
def _coerce_secret_set_slug(slug: str | None) -> str | None:
|
|
1489
|
+
value = str(slug or "").strip()
|
|
1490
|
+
return value or None
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
def _parse_secret_pairs(pairs: list[str] | None) -> dict[str, str]:
|
|
1495
|
+
values: dict[str, str] = {}
|
|
1496
|
+
for pair in pairs or []:
|
|
1497
|
+
if "=" not in pair:
|
|
1498
|
+
raise RuntimeConfigError(f"invalid secret pair: {pair}")
|
|
1499
|
+
key, value = pair.split("=", 1)
|
|
1500
|
+
key = key.strip()
|
|
1501
|
+
if not key:
|
|
1502
|
+
raise RuntimeConfigError(f"invalid secret pair: {pair}")
|
|
1503
|
+
values[key] = value
|
|
1504
|
+
return values
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
def _parse_env_file(path: str) -> dict[str, str]:
|
|
1509
|
+
try:
|
|
1510
|
+
lines = Path(path).read_text(encoding="utf-8").splitlines()
|
|
1511
|
+
except OSError as exc:
|
|
1512
|
+
raise RuntimeConfigError(f"failed to read env file: {exc}") from exc
|
|
1513
|
+
values: dict[str, str] = {}
|
|
1514
|
+
for raw_line in lines:
|
|
1515
|
+
line = raw_line.strip()
|
|
1516
|
+
if not line or line.startswith("#"):
|
|
1517
|
+
continue
|
|
1518
|
+
if line.startswith("export "):
|
|
1519
|
+
line = line[len("export "):].strip()
|
|
1520
|
+
if "=" not in line:
|
|
1521
|
+
raise RuntimeConfigError(f"invalid env line: {raw_line}")
|
|
1522
|
+
key, value = line.split("=", 1)
|
|
1523
|
+
key = key.strip()
|
|
1524
|
+
if not key:
|
|
1525
|
+
raise RuntimeConfigError(f"invalid env line: {raw_line}")
|
|
1526
|
+
value = value.strip()
|
|
1527
|
+
if value and value[0] == value[-1] and value[0] in {'\"', "'"}:
|
|
1528
|
+
value = value[1:-1]
|
|
1529
|
+
values[key] = value
|
|
1530
|
+
return values
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
def handle_secrets_list(config: RuntimeConfig) -> int:
|
|
1535
|
+
if (err := _require_api_key(config)) is not None:
|
|
1536
|
+
return err
|
|
1537
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1538
|
+
return report_success(client.list_secret_sets(), _render_secret_sets_table)
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
def handle_secrets_show(config: RuntimeConfig, slug: str | None) -> int:
|
|
1543
|
+
if (err := _require_api_key(config)) is not None:
|
|
1544
|
+
return err
|
|
1545
|
+
resolved_slug = _coerce_secret_set_slug(slug)
|
|
1546
|
+
if resolved_slug is None:
|
|
1547
|
+
if not _interactive():
|
|
1548
|
+
return report_error("secret set slug is required")
|
|
1549
|
+
resolved_slug = _prompt_text("Secret set slug")
|
|
1550
|
+
if not resolved_slug:
|
|
1551
|
+
return report_error("secret set slug is required")
|
|
1552
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1553
|
+
return report_success(client.get_secret_set(resolved_slug), _render_secret_set_panel)
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
def handle_secrets_set(config: RuntimeConfig, slug: str | None, pairs: list[str] | None) -> int:
|
|
1558
|
+
if (err := _require_api_key(config)) is not None:
|
|
1559
|
+
return err
|
|
1560
|
+
resolved_slug = _coerce_secret_set_slug(slug)
|
|
1561
|
+
if resolved_slug is None:
|
|
1562
|
+
if not _interactive():
|
|
1563
|
+
return report_error("secret set slug is required")
|
|
1564
|
+
resolved_slug = _prompt_text("Secret set slug")
|
|
1565
|
+
if not resolved_slug:
|
|
1566
|
+
return report_error("secret set slug is required")
|
|
1567
|
+
values = _parse_secret_pairs(pairs)
|
|
1568
|
+
if not values:
|
|
1569
|
+
return report_error("at least one KEY=VALUE pair is required")
|
|
1570
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1571
|
+
secret_set = client.put_secret_set(resolved_slug, values)
|
|
1572
|
+
return report_success(
|
|
1573
|
+
{"secret_set": secret_set},
|
|
1574
|
+
lambda payload: _UI.console().print(f"[green]✓[/green] saved [bold]{payload['secret_set'].get('slug', resolved_slug)}[/bold]"),
|
|
1575
|
+
)
|
|
1576
|
+
|
|
1577
|
+
|
|
1578
|
+
|
|
1579
|
+
def handle_secrets_import(config: RuntimeConfig, slug: str | None, env_file: str) -> int:
|
|
1580
|
+
if (err := _require_api_key(config)) is not None:
|
|
1581
|
+
return err
|
|
1582
|
+
resolved_slug = _coerce_secret_set_slug(slug)
|
|
1583
|
+
if resolved_slug is None:
|
|
1584
|
+
if not _interactive():
|
|
1585
|
+
return report_error("secret set slug is required")
|
|
1586
|
+
resolved_slug = _prompt_text("Secret set slug")
|
|
1587
|
+
if not resolved_slug:
|
|
1588
|
+
return report_error("secret set slug is required")
|
|
1589
|
+
values = _parse_env_file(env_file)
|
|
1590
|
+
if not values:
|
|
1591
|
+
return report_error("env file did not contain any secrets")
|
|
1592
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1593
|
+
secret_set = client.put_secret_set(resolved_slug, values)
|
|
1594
|
+
return report_success(
|
|
1595
|
+
{"secret_set": secret_set},
|
|
1596
|
+
lambda payload: _UI.console().print(f"[green]✓[/green] saved [bold]{payload['secret_set'].get('slug', resolved_slug)}[/bold] from [bold]{env_file}[/bold]"),
|
|
1597
|
+
)
|
|
1598
|
+
|
|
1599
|
+
|
|
1600
|
+
|
|
1601
|
+
def handle_secrets_delete(config: RuntimeConfig, slug: str | None) -> int:
|
|
1602
|
+
if (err := _require_api_key(config)) is not None:
|
|
1603
|
+
return err
|
|
1604
|
+
resolved_slug = _coerce_secret_set_slug(slug)
|
|
1605
|
+
if resolved_slug is None:
|
|
1606
|
+
if not _interactive():
|
|
1607
|
+
return report_error("secret set slug is required")
|
|
1608
|
+
resolved_slug = _prompt_text("Secret set slug")
|
|
1609
|
+
if not resolved_slug:
|
|
1610
|
+
return report_error("secret set slug is required")
|
|
1611
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1612
|
+
client.delete_secret_set(resolved_slug)
|
|
1613
|
+
return report_success(
|
|
1614
|
+
{"slug": resolved_slug, "deleted": True},
|
|
1615
|
+
lambda payload: _UI.console().print(f"[green]✓[/green] deleted [bold]{payload['slug']}[/bold]"),
|
|
1616
|
+
)
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
def handle_integrations_connect(
|
|
1621
|
+
config: RuntimeConfig,
|
|
1622
|
+
provider: str,
|
|
1623
|
+
slug: str | None,
|
|
1624
|
+
owner: str | None,
|
|
1625
|
+
*,
|
|
1626
|
+
no_open: bool = False,
|
|
1627
|
+
) -> int:
|
|
1628
|
+
if (err := _require_api_key(config)) is not None:
|
|
1629
|
+
return err
|
|
1630
|
+
if provider != "github":
|
|
1631
|
+
return report_error(f"unsupported integration provider: {provider}")
|
|
1632
|
+
|
|
1633
|
+
client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
|
|
1634
|
+
payload = _with_spinner("getting GitHub install link…", lambda: client.start_github_connect(slug=slug))
|
|
1635
|
+
install_url = str(payload.get("install_url") or "").strip()
|
|
1636
|
+
resolved_slug = str(payload.get("slug") or slug or "github").strip() or "github"
|
|
1637
|
+
if not install_url:
|
|
1638
|
+
return report_error("server did not return a GitHub install URL")
|
|
1639
|
+
|
|
1640
|
+
opened = False
|
|
1641
|
+
if not no_open:
|
|
1642
|
+
with contextlib.suppress(Exception):
|
|
1643
|
+
opened = bool(webbrowser.open(install_url))
|
|
1644
|
+
|
|
1645
|
+
if _interactive():
|
|
1646
|
+
action = "opened in your browser" if opened else "copy this URL into your browser"
|
|
1647
|
+
_UI.console().print(f"[cyan]→[/cyan] {action}: [link={install_url}]{install_url}[/link]")
|
|
1648
|
+
_UI.console().print("[dim]Install the RuntimeVM GitHub App, then come back here.[/dim]")
|
|
1649
|
+
input("Press Enter after the GitHub App is installed... ")
|
|
1650
|
+
if not owner:
|
|
1651
|
+
owner = _prompt_text("GitHub owner (username or org)")
|
|
1652
|
+
elif not owner:
|
|
1653
|
+
return report_error("--owner is required without a TTY")
|
|
1654
|
+
|
|
1655
|
+
resolved_owner = str(owner or "").strip()
|
|
1656
|
+
if not resolved_owner:
|
|
1657
|
+
return report_error("GitHub owner is required")
|
|
1658
|
+
|
|
1659
|
+
integration = _with_spinner(
|
|
1660
|
+
"connecting GitHub…",
|
|
1661
|
+
lambda: client.create_integration(
|
|
1662
|
+
slug=resolved_slug,
|
|
1663
|
+
provider="github",
|
|
1664
|
+
transport="scoped_credential",
|
|
1665
|
+
backend="native",
|
|
1666
|
+
credential_source="external_account",
|
|
1667
|
+
credential_ref=resolved_owner,
|
|
1668
|
+
config={},
|
|
1669
|
+
),
|
|
1670
|
+
)
|
|
1671
|
+
|
|
1672
|
+
def render(result: dict[str, Any]) -> None:
|
|
1673
|
+
connected = result.get("integration") or {}
|
|
1674
|
+
_UI.console().print(
|
|
1675
|
+
f"[green]✓[/green] connected [bold]{connected.get('slug', resolved_slug)}[/bold] → [bold]{connected.get('credential_ref', resolved_owner)}[/bold]"
|
|
1676
|
+
)
|
|
1677
|
+
|
|
1678
|
+
return report_success({"integration": integration, "install_url": install_url}, render)
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
|
|
1410
1682
|
def handle_onboarding(config: RuntimeConfig) -> int:
|
|
1411
1683
|
code = handle_login(config, None, None, None)
|
|
1412
1684
|
if code != 0:
|
|
@@ -52,6 +52,48 @@ class RuntimeClient:
|
|
|
52
52
|
def revoke_api_key(self, key_id: int) -> dict[str, Any]:
|
|
53
53
|
return self._request("POST", f"/api/api-keys/{key_id}/revoke", auth_required=True)
|
|
54
54
|
|
|
55
|
+
# --- Integrations ---
|
|
56
|
+
|
|
57
|
+
def start_github_connect(self, *, slug: str | None = None) -> dict[str, Any]:
|
|
58
|
+
body: dict[str, Any] = {}
|
|
59
|
+
if slug:
|
|
60
|
+
body["slug"] = slug
|
|
61
|
+
return self._request("POST", "/api/integrations/github/connect", json=body, auth_required=True)
|
|
62
|
+
|
|
63
|
+
def create_integration(
|
|
64
|
+
self,
|
|
65
|
+
*,
|
|
66
|
+
slug: str,
|
|
67
|
+
provider: str,
|
|
68
|
+
transport: str,
|
|
69
|
+
backend: str,
|
|
70
|
+
credential_source: str,
|
|
71
|
+
credential_ref: str,
|
|
72
|
+
config: dict[str, Any] | None = None,
|
|
73
|
+
) -> dict[str, Any]:
|
|
74
|
+
body: dict[str, Any] = {
|
|
75
|
+
"slug": slug,
|
|
76
|
+
"provider": provider,
|
|
77
|
+
"transport": transport,
|
|
78
|
+
"backend": backend,
|
|
79
|
+
"credential_source": credential_source,
|
|
80
|
+
"credential_ref": credential_ref,
|
|
81
|
+
"config": config or {},
|
|
82
|
+
}
|
|
83
|
+
return self._request("POST", "/api/integrations", json=body, auth_required=True)
|
|
84
|
+
|
|
85
|
+
def list_secret_sets(self) -> dict[str, Any]:
|
|
86
|
+
return self._request("GET", "/api/secrets", auth_required=True)
|
|
87
|
+
|
|
88
|
+
def get_secret_set(self, slug: str) -> dict[str, Any]:
|
|
89
|
+
return self._request("GET", f"/api/secrets/{slug}", auth_required=True)
|
|
90
|
+
|
|
91
|
+
def put_secret_set(self, slug: str, values: dict[str, str]) -> dict[str, Any]:
|
|
92
|
+
return self._request("PUT", f"/api/secrets/{slug}", json={"values": values}, auth_required=True)
|
|
93
|
+
|
|
94
|
+
def delete_secret_set(self, slug: str) -> dict[str, Any]:
|
|
95
|
+
return self._request("DELETE", f"/api/secrets/{slug}", auth_required=True)
|
|
96
|
+
|
|
55
97
|
# --- Computers ---
|
|
56
98
|
|
|
57
99
|
def create_computer(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runtime-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.16
|
|
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
|