runtime-sdk 0.4.15__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.15 → runtime_sdk-0.4.16}/PKG-INFO +1 -1
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/pyproject.toml +1 -1
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk/cli.py +197 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk/client.py +12 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/PKG-INFO +1 -1
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/README.md +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk/__init__.py +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk/config.py +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/SOURCES.txt +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/dependency_links.txt +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/entry_points.txt +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/requires.txt +0 -0
- {runtime_sdk-0.4.15 → runtime_sdk-0.4.16}/scripts/runtime_sdk.egg-info/top_level.txt +0 -0
- {runtime_sdk-0.4.15 → 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
|
|
@@ -174,6 +174,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
174
174
|
api_keys_revoke = api_keys_subparsers.add_parser("revoke", help="Revoke an API key")
|
|
175
175
|
api_keys_revoke.add_argument("id", nargs="?", default=None, help="API key id")
|
|
176
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
|
+
|
|
177
191
|
integrations_cmd = subparsers.add_parser("integrations", help="Connect account-level integrations")
|
|
178
192
|
integrations_subparsers = integrations_cmd.add_subparsers(dest="integrations_action", required=True)
|
|
179
193
|
integrations_connect = integrations_subparsers.add_parser("connect", help="Connect an integration")
|
|
@@ -306,6 +320,18 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
306
320
|
if args.api_keys_action == "revoke":
|
|
307
321
|
return handle_api_keys_revoke(config, args.id)
|
|
308
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")
|
|
309
335
|
if args.command == "integrations":
|
|
310
336
|
if args.integrations_action == "connect":
|
|
311
337
|
return handle_integrations_connect(config, args.provider, args.slug, args.owner, no_open=args.no_open)
|
|
@@ -1185,6 +1211,45 @@ def _render_api_key_created(payload: dict[str, Any]) -> None:
|
|
|
1185
1211
|
_UI.console().print(Panel(body, title="api key created", border_style="cyan", expand=False))
|
|
1186
1212
|
|
|
1187
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
|
+
|
|
1188
1253
|
# --------------------------------------------------------------------------- #
|
|
1189
1254
|
# Command handlers #
|
|
1190
1255
|
# --------------------------------------------------------------------------- #
|
|
@@ -1420,6 +1485,138 @@ def handle_api_keys_revoke(config: RuntimeConfig, key_id: str | None) -> int:
|
|
|
1420
1485
|
|
|
1421
1486
|
|
|
1422
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
|
+
|
|
1423
1620
|
def handle_integrations_connect(
|
|
1424
1621
|
config: RuntimeConfig,
|
|
1425
1622
|
provider: str,
|
|
@@ -82,6 +82,18 @@ class RuntimeClient:
|
|
|
82
82
|
}
|
|
83
83
|
return self._request("POST", "/api/integrations", json=body, auth_required=True)
|
|
84
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
|
+
|
|
85
97
|
# --- Computers ---
|
|
86
98
|
|
|
87
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
|