runtime-sdk 0.4.18__tar.gz → 0.4.19__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.18
3
+ Version: 0.4.19
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
@@ -66,8 +66,12 @@ runtime login --api-key rt_live_...
66
66
  runtime api-keys list
67
67
  runtime api-keys create "my laptop"
68
68
  runtime api-keys revoke 123
69
+ runtime integrations connect github
69
70
 
70
71
  # Computers
72
+ runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
73
+ runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
74
+ runtime checkout --repo owner/repo feature/login # alias for switch
71
75
  runtime create # creates a computer with the starter app already published
72
76
  runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
73
77
  runtime enter <name-or-id> # accepts slug/name like test, or the computer id
@@ -157,6 +161,28 @@ show|clear` expose that same durable app state directly. The low-level
157
161
  `runtime run` stays one-shot: the filesystem is restored after going cold, but
158
162
  that ad-hoc process is not.
159
163
 
164
+ ## GitHub branch workspaces
165
+
166
+ `runtime switch` gives a repo branch its own Runtime computer so you can jump
167
+ between parallel tasks without local worktrees.
168
+
169
+ ```bash
170
+ runtime integrations connect github
171
+ runtime switch --repo owner/repo feature/login
172
+ runtime switch -c feature/search --repo owner/repo --from main
173
+ ```
174
+
175
+ Rules:
176
+ - GitHub must already be connected with `runtime integrations connect github`.
177
+ - `runtime switch` opens an existing branch workspace, or creates the computer if
178
+ the branch exists but the workspace does not yet.
179
+ - `runtime switch -c` creates a new branch first, then opens that branch's
180
+ workspace.
181
+ - Each `repo + branch` maps to one dedicated computer.
182
+ - The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
183
+ start there automatically.
184
+ - `runtime checkout ...` is an alias for `runtime switch ...`.
185
+
160
186
  Run the SDK unit tests through the backend project environment:
161
187
 
162
188
  ```bash
@@ -44,8 +44,12 @@ runtime login --api-key rt_live_...
44
44
  runtime api-keys list
45
45
  runtime api-keys create "my laptop"
46
46
  runtime api-keys revoke 123
47
+ runtime integrations connect github
47
48
 
48
49
  # Computers
50
+ runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
51
+ runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
52
+ runtime checkout --repo owner/repo feature/login # alias for switch
49
53
  runtime create # creates a computer with the starter app already published
50
54
  runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
51
55
  runtime enter <name-or-id> # accepts slug/name like test, or the computer id
@@ -135,6 +139,28 @@ show|clear` expose that same durable app state directly. The low-level
135
139
  `runtime run` stays one-shot: the filesystem is restored after going cold, but
136
140
  that ad-hoc process is not.
137
141
 
142
+ ## GitHub branch workspaces
143
+
144
+ `runtime switch` gives a repo branch its own Runtime computer so you can jump
145
+ between parallel tasks without local worktrees.
146
+
147
+ ```bash
148
+ runtime integrations connect github
149
+ runtime switch --repo owner/repo feature/login
150
+ runtime switch -c feature/search --repo owner/repo --from main
151
+ ```
152
+
153
+ Rules:
154
+ - GitHub must already be connected with `runtime integrations connect github`.
155
+ - `runtime switch` opens an existing branch workspace, or creates the computer if
156
+ the branch exists but the workspace does not yet.
157
+ - `runtime switch -c` creates a new branch first, then opens that branch's
158
+ workspace.
159
+ - Each `repo + branch` maps to one dedicated computer.
160
+ - The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
161
+ start there automatically.
162
+ - `runtime checkout ...` is an alias for `runtime switch ...`.
163
+
138
164
  Run the SDK unit tests through the backend project environment:
139
165
 
140
166
  ```bash
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "runtime-sdk"
3
- version = "0.4.18"
3
+ version = "0.4.19"
4
4
  description = "Runtime Python SDK and CLI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -5,6 +5,7 @@ import contextlib
5
5
  import importlib
6
6
  import json
7
7
  import os
8
+ import re
8
9
  import select
9
10
  import shlex
10
11
  import shutil
@@ -195,6 +196,16 @@ def build_parser() -> argparse.ArgumentParser:
195
196
  integrations_connect.add_argument("--owner", default=None, help="GitHub username or org where the app is installed")
196
197
  integrations_connect.add_argument("--no-open", action="store_true", help="Print the install URL instead of opening a browser")
197
198
 
199
+ switch_cmd = subparsers.add_parser(
200
+ "switch",
201
+ aliases=["checkout"],
202
+ help="Open a GitHub branch workspace in its own computer",
203
+ )
204
+ switch_cmd.add_argument("branch", nargs="?", default=None, help="Git branch to open")
205
+ switch_cmd.add_argument("--repo", default=None, help="GitHub repository in owner/name format")
206
+ switch_cmd.add_argument("-c", "--create", action="store_true", help="Create a new branch before opening it")
207
+ switch_cmd.add_argument("--from", dest="base_branch", default=None, help="Base branch for -c (defaults to the repo default branch)")
208
+
198
209
  create_cmd = subparsers.add_parser("create", help="Create a new computer with the starter app published by default")
199
210
  create_cmd.add_argument(
200
211
  "name",
@@ -343,6 +354,14 @@ def main(argv: list[str] | None = None) -> int:
343
354
  if args.integrations_action == "connect":
344
355
  return handle_integrations_connect(config, args.provider, args.slug, args.owner, no_open=args.no_open)
345
356
  return report_error("unknown integrations command")
357
+ if args.command in ("switch", "checkout"):
358
+ return handle_switch(
359
+ config,
360
+ getattr(args, "repo", None),
361
+ getattr(args, "branch", None),
362
+ create_branch=getattr(args, "create", False),
363
+ base_branch=getattr(args, "base_branch", None),
364
+ )
346
365
  if args.command == "create":
347
366
  return handle_create(
348
367
  config,
@@ -764,6 +783,48 @@ def _resolve_computer_ref(client: RuntimeClient, ref: str) -> str:
764
783
 
765
784
 
766
785
 
786
+ def _coerce_github_repo(repo: str | None) -> str | None:
787
+ text = str(repo or "").strip()
788
+ if not text:
789
+ return None
790
+ if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", text):
791
+ raise RuntimeAPIError("repo must be in owner/name format")
792
+ owner, name = text.split("/", 1)
793
+ return f"{owner}/{name}"
794
+
795
+
796
+
797
+ def _coerce_git_branch(branch: str | None) -> str | None:
798
+ text = str(branch or "").strip()
799
+ if not text:
800
+ return None
801
+ if text == "@":
802
+ raise RuntimeAPIError("branch name is invalid")
803
+ if text.startswith("-") or text.endswith("/") or text.endswith(".") or text.endswith(".lock"):
804
+ raise RuntimeAPIError("branch name is invalid")
805
+ if ".." in text or "//" in text or "@{" in text:
806
+ raise RuntimeAPIError("branch name is invalid")
807
+ if any(ch in text for ch in (" ", "~", "^", ":", "?", "*", "[", "\\")):
808
+ raise RuntimeAPIError("branch name is invalid")
809
+ if any(ord(ch) < 32 or ord(ch) == 127 for ch in text):
810
+ raise RuntimeAPIError("branch name is invalid")
811
+ return text
812
+
813
+
814
+
815
+ def _render_branch_workspace(payload: dict[str, Any]) -> None:
816
+ computer = payload.get("computer") or {}
817
+ repo = str(payload.get("repo") or "")
818
+ branch = str(payload.get("branch") or "")
819
+ action = "created branch" if payload.get("create_branch") else "opened branch"
820
+ _render_computer_panel(computer, title="workspace")
821
+ _UI.console().print(f"[green]✓[/green] {action} [bold]{repo}#{branch}[/bold]")
822
+ url = computer.get("public_url")
823
+ if url:
824
+ _UI.console().print(f"[bold green]→[/bold green] [cyan]{url}[/cyan]")
825
+
826
+
827
+
767
828
  def _computer_name(c: dict[str, Any]) -> str:
768
829
  return str(c.get("slug") or c.get("id") or "?")
769
830
 
@@ -1686,6 +1747,74 @@ def handle_integrations_connect(
1686
1747
 
1687
1748
 
1688
1749
 
1750
+ def handle_switch(
1751
+ config: RuntimeConfig,
1752
+ repo: str | None,
1753
+ branch: str | None,
1754
+ *,
1755
+ create_branch: bool = False,
1756
+ base_branch: str | None = None,
1757
+ ) -> int:
1758
+ if (err := _require_api_key(config)) is not None:
1759
+ return err
1760
+ if base_branch is not None and not create_branch:
1761
+ return report_error("--from can only be used together with -c")
1762
+
1763
+ try:
1764
+ resolved_repo = _coerce_github_repo(repo)
1765
+ resolved_branch = _coerce_git_branch(branch)
1766
+ except RuntimeAPIError as exc:
1767
+ return report_error(str(exc), status_code=exc.status_code)
1768
+
1769
+ if resolved_repo is None:
1770
+ if not _interactive():
1771
+ return report_error("--repo is required")
1772
+ resolved_repo = _prompt_text("GitHub repo (owner/name)")
1773
+ try:
1774
+ resolved_repo = _coerce_github_repo(resolved_repo)
1775
+ except RuntimeAPIError as exc:
1776
+ return report_error(str(exc), status_code=exc.status_code)
1777
+ if resolved_repo is None:
1778
+ return report_error("repo is required")
1779
+
1780
+ if resolved_branch is None:
1781
+ if not _interactive():
1782
+ return report_error("branch is required")
1783
+ prompt = "New branch name" if create_branch else "Branch name"
1784
+ resolved_branch = _prompt_text(prompt)
1785
+ try:
1786
+ resolved_branch = _coerce_git_branch(resolved_branch)
1787
+ except RuntimeAPIError as exc:
1788
+ return report_error(str(exc), status_code=exc.status_code)
1789
+ if resolved_branch is None:
1790
+ return report_error("branch is required")
1791
+
1792
+ client = RuntimeClient(base_url=config.base_url, api_key=config.api_key)
1793
+ try:
1794
+ payload = _with_spinner(
1795
+ "opening branch workspace…",
1796
+ lambda: client.switch_workspace(
1797
+ repo=resolved_repo,
1798
+ branch=resolved_branch,
1799
+ create=create_branch,
1800
+ base_branch=base_branch,
1801
+ ),
1802
+ )
1803
+ except RuntimeAPIError as exc:
1804
+ return report_error(str(exc), status_code=exc.status_code)
1805
+
1806
+ computer = payload.get("computer") or {}
1807
+ computer_id = str(computer.get("id") or "").strip()
1808
+ if not computer_id:
1809
+ return report_error("server did not return a computer id")
1810
+
1811
+ code = report_success(payload, _render_branch_workspace)
1812
+ if code != 0 or not _interactive():
1813
+ return code
1814
+ return _enter_computer(config, computer_id)
1815
+
1816
+
1817
+
1689
1818
  def handle_onboarding(config: RuntimeConfig) -> int:
1690
1819
  code = handle_login(config, None, None, None)
1691
1820
  if code != 0:
@@ -96,6 +96,25 @@ class RuntimeClient:
96
96
 
97
97
  # --- Computers ---
98
98
 
99
+ def switch_workspace(
100
+ self,
101
+ *,
102
+ repo: str,
103
+ branch: str,
104
+ create: bool = False,
105
+ base_branch: str | None = None,
106
+ ) -> dict[str, Any]:
107
+ body: dict[str, Any] = {"repo": repo, "branch": branch, "create": create}
108
+ if base_branch is not None:
109
+ body["from"] = base_branch
110
+ return self._request(
111
+ "POST",
112
+ "/api/workspaces/switch",
113
+ json=body,
114
+ auth_required=True,
115
+ timeout=max(self.timeout, DEFAULT_WARMUP_TIMEOUT),
116
+ )
117
+
99
118
  def create_computer(
100
119
  self,
101
120
  *,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runtime-sdk
3
- Version: 0.4.18
3
+ Version: 0.4.19
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
@@ -66,8 +66,12 @@ runtime login --api-key rt_live_...
66
66
  runtime api-keys list
67
67
  runtime api-keys create "my laptop"
68
68
  runtime api-keys revoke 123
69
+ runtime integrations connect github
69
70
 
70
71
  # Computers
72
+ runtime switch --repo owner/repo feature/login # open or create the branch workspace computer
73
+ runtime switch -c feature/login --repo owner/repo # create a new branch workspace from the repo default branch
74
+ runtime checkout --repo owner/repo feature/login # alias for switch
71
75
  runtime create # creates a computer with the starter app already published
72
76
  runtime create myapp --command "python3 app.py" --cwd /home/ubuntu --port 3000
73
77
  runtime enter <name-or-id> # accepts slug/name like test, or the computer id
@@ -157,6 +161,28 @@ show|clear` expose that same durable app state directly. The low-level
157
161
  `runtime run` stays one-shot: the filesystem is restored after going cold, but
158
162
  that ad-hoc process is not.
159
163
 
164
+ ## GitHub branch workspaces
165
+
166
+ `runtime switch` gives a repo branch its own Runtime computer so you can jump
167
+ between parallel tasks without local worktrees.
168
+
169
+ ```bash
170
+ runtime integrations connect github
171
+ runtime switch --repo owner/repo feature/login
172
+ runtime switch -c feature/search --repo owner/repo --from main
173
+ ```
174
+
175
+ Rules:
176
+ - GitHub must already be connected with `runtime integrations connect github`.
177
+ - `runtime switch` opens an existing branch workspace, or creates the computer if
178
+ the branch exists but the workspace does not yet.
179
+ - `runtime switch -c` creates a new branch first, then opens that branch's
180
+ workspace.
181
+ - Each `repo + branch` maps to one dedicated computer.
182
+ - The repo is cloned into `/home/ubuntu/<repo>` and new shells in that computer
183
+ start there automatically.
184
+ - `runtime checkout ...` is an alias for `runtime switch ...`.
185
+
160
186
  Run the SDK unit tests through the backend project environment:
161
187
 
162
188
  ```bash
File without changes