qbraid-cli 0.13.3__tar.gz → 0.14.0__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.
Files changed (70) hide show
  1. {qbraid_cli-0.13.3/qbraid_cli.egg-info → qbraid_cli-0.14.0}/PKG-INFO +4 -4
  2. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/pyproject.toml +4 -4
  3. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/compute/app.py +60 -167
  4. qbraid_cli-0.14.0/qbraid_cli/compute/instances.py +1342 -0
  5. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/compute/profiles.py +4 -0
  6. qbraid_cli-0.14.0/qbraid_cli/compute/server.py +249 -0
  7. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/compute/ssh.py +102 -75
  8. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/app.py +47 -11
  9. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/lab/__init__.py +2 -2
  10. qbraid_cli-0.14.0/qbraid_cli/lab/app.py +49 -0
  11. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/main.py +2 -2
  12. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0/qbraid_cli.egg-info}/PKG-INFO +4 -4
  13. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli.egg-info/SOURCES.txt +1 -0
  14. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli.egg-info/requires.txt +3 -3
  15. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/tests/test_cli_e2e.py +5 -2
  16. qbraid_cli-0.13.3/qbraid_cli/compute/server.py +0 -589
  17. qbraid_cli-0.13.3/qbraid_cli/lab/app.py +0 -50
  18. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/LICENSE +0 -0
  19. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/README.md +0 -0
  20. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/__init__.py +0 -0
  21. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/_build_info.py +0 -0
  22. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/_version.py +0 -0
  23. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/account/__init__.py +0 -0
  24. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/account/app.py +0 -0
  25. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/admin/__init__.py +0 -0
  26. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/admin/app.py +0 -0
  27. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/admin/headers.py +0 -0
  28. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/admin/validation.py +0 -0
  29. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/agents/__init__.py +0 -0
  30. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/agents/app.py +0 -0
  31. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/ai/__init__.py +0 -0
  32. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/ai/app.py +0 -0
  33. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/ai/token.py +0 -0
  34. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/compute/__init__.py +0 -0
  35. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/configure/__init__.py +0 -0
  36. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/configure/actions.py +0 -0
  37. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/configure/app.py +0 -0
  38. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/devices/__init__.py +0 -0
  39. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/devices/app.py +0 -0
  40. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/devices/validation.py +0 -0
  41. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/__init__.py +0 -0
  42. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/activate.py +0 -0
  43. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/create.py +0 -0
  44. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/data_handling.py +0 -0
  45. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/groups.py +0 -0
  46. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/publish.py +0 -0
  47. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/registry.py +0 -0
  48. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/envs/share.py +0 -0
  49. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/exceptions.py +0 -0
  50. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/handlers.py +0 -0
  51. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/jobs/__init__.py +0 -0
  52. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/jobs/app.py +0 -0
  53. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/jobs/groups.py +0 -0
  54. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/kernels/__init__.py +0 -0
  55. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/kernels/app.py +0 -0
  56. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/pip/__init__.py +0 -0
  57. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/pip/app.py +0 -0
  58. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/pip/hooks.py +0 -0
  59. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/projects/__init__.py +0 -0
  60. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/projects/app.py +0 -0
  61. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/py.typed +0 -0
  62. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/skills/__init__.py +0 -0
  63. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/skills/app.py +0 -0
  64. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/troubleshoot/__init__.py +0 -0
  65. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli/troubleshoot/app.py +0 -0
  66. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli.egg-info/dependency_links.txt +0 -0
  67. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli.egg-info/entry_points.txt +0 -0
  68. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/qbraid_cli.egg-info/top_level.txt +0 -0
  69. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/setup.cfg +0 -0
  70. {qbraid_cli-0.13.3 → qbraid_cli-0.14.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qbraid-cli
3
- Version: 0.13.3
3
+ Version: 0.14.0
4
4
  Summary: Command Line Interface for interacting with all parts of the qBraid platform.
5
5
  Author-email: qBraid Development Team <contact@qbraid.com>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -30,11 +30,11 @@ Requires-Dist: typer>=0.12.1
30
30
  Requires-Dist: rich>=10.11.0
31
31
  Requires-Dist: click<=8.3.3
32
32
  Requires-Dist: questionary>=2.0.0
33
- Requires-Dist: qbraid-core>=0.3.11
33
+ Requires-Dist: qbraid-core>=0.5.0
34
34
  Provides-Extra: compute
35
- Requires-Dist: qbraid-core[compute]>=0.3.3; extra == "compute"
35
+ Requires-Dist: qbraid-core[compute]>=0.5.0; extra == "compute"
36
36
  Provides-Extra: envs
37
- Requires-Dist: qbraid-core[environments]>=0.3.0; extra == "envs"
37
+ Requires-Dist: qbraid-core[environments]>=0.5.0; extra == "envs"
38
38
  Provides-Extra: dev
39
39
  Requires-Dist: isort; extra == "dev"
40
40
  Requires-Dist: black; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qbraid-cli"
7
- version = "0.13.3"
7
+ version = "0.14.0"
8
8
  description = "Command Line Interface for interacting with all parts of the qBraid platform."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "qBraid Development Team", email = "contact@qbraid.com" }]
@@ -32,7 +32,7 @@ dependencies = [
32
32
  "rich>=10.11.0",
33
33
  "click<=8.3.3",
34
34
  "questionary>=2.0.0",
35
- "qbraid-core>=0.3.11",
35
+ "qbraid-core>=0.5.0",
36
36
  ]
37
37
  requires-python = ">= 3.10"
38
38
 
@@ -43,8 +43,8 @@ Documentation = "https://docs.qbraid.com/v2/cli/api-reference/qbraid"
43
43
  Discord = "https://discord.gg/KugF6Cnncm"
44
44
 
45
45
  [project.optional-dependencies]
46
- compute = ["qbraid-core[compute]>=0.3.3"]
47
- envs = ["qbraid-core[environments]>=0.3.0"]
46
+ compute = ["qbraid-core[compute]>=0.5.0"]
47
+ envs = ["qbraid-core[environments]>=0.5.0"]
48
48
  dev = ["isort", "black", "pytest", "pytest-cov", "pytest-asyncio"]
49
49
 
50
50
  [project.scripts]
@@ -7,13 +7,21 @@ from typing import Optional
7
7
 
8
8
  import typer
9
9
  from rich.console import Console
10
- from rich.panel import Panel
11
10
  from rich.table import Table
12
11
 
13
12
  from qbraid_cli.handlers import handle_error, run_progress_task
14
13
 
14
+ from .instances import (
15
+ _ON_DEMAND_WAIT_TIMEOUT,
16
+ _SUBSCRIPTION_WAIT_TIMEOUT,
17
+ _fail,
18
+ instances_app,
19
+ run_launch,
20
+ run_list,
21
+ run_stop,
22
+ )
15
23
  from .profiles import profiles_app
16
- from .server import _print_unified_status_table, _select_bma_instance, server_app
24
+ from .server import server_app
17
25
  from .ssh import ssh_app
18
26
 
19
27
  compute_app = typer.Typer(
@@ -29,16 +37,17 @@ compute_app = typer.Typer(
29
37
  " \u2022 Connect VS Code or Cursor to a remote server\n"
30
38
  " \u2022 Launch AI agents on more powerful hardware\n\n"
31
39
  "Quick start:\n"
32
- " qbraid compute up <profile> # Start server + configure SSH\n"
33
- " qbraid compute status # Check server status\n"
34
- " qbraid compute ssh connect # SSH into running server\n"
35
- " qbraid compute down # Stop server"
40
+ " qbraid compute up <profile> # Launch an instance + configure SSH\n"
41
+ " qbraid compute status # List your instances\n"
42
+ " qbraid compute instances ... # launch/start/stop/terminate/ssh/...\n"
43
+ " qbraid compute down # Stop an instance"
36
44
  ),
37
45
  no_args_is_help=True,
38
46
  )
39
47
  console = Console()
40
48
 
41
49
  # Add subcommand groups
50
+ compute_app.add_typer(instances_app, name="instances")
42
51
  compute_app.add_typer(profiles_app, name="profiles")
43
52
  compute_app.add_typer(server_app, name="server")
44
53
  compute_app.add_typer(ssh_app, name="ssh")
@@ -58,204 +67,88 @@ def _get_client():
58
67
 
59
68
  @compute_app.command("up")
60
69
  def compute_up(
61
- profile_slug: str = typer.Argument(..., help="Profile slug to start"),
62
- cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Cluster ID"),
63
- timeout: int = typer.Option(120, "--timeout", "-t", help="Server start timeout"),
70
+ profile_slug: str = typer.Argument(..., help="Profile slug to launch"),
71
+ cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Subscription cluster ID"),
72
+ timeout: Optional[int] = typer.Option(
73
+ None,
74
+ "--timeout",
75
+ "-t",
76
+ help=f"Seconds to wait (default {_ON_DEMAND_WAIT_TIMEOUT} on-demand, "
77
+ f"{_SUBSCRIPTION_WAIT_TIMEOUT} subscription)",
78
+ ),
64
79
  no_ssh: bool = typer.Option(False, "--no-ssh", help="Skip SSH configuration"),
65
80
  ):
66
- """Start server and configure SSH.
81
+ """Launch an instance and configure SSH.
67
82
 
68
- Waits for the server to be ready. This is a convenience command that combines:
69
- - qbraid compute server start <profile> --wait
70
- - qbraid compute ssh setup
83
+ Alias for `qbraid compute instances launch <profile> --ssh`. On-demand
84
+ profiles always launch a new instance; the subscription server attaches
85
+ if it's already running.
71
86
 
72
87
  Example:
73
- qbraid compute up 2vCPU_4GB
88
+ qbraid compute up gpu-l4
74
89
  """
75
90
  try:
76
- client = _get_client()
77
-
78
- # Check if server is already running
79
- status = client.get_server_status(cluster_id)
80
- if status.server_running:
81
- console.print(f"[yellow]Server already running with profile: {status.current_profile}[/yellow]")
82
- console.print()
83
- if not no_ssh:
84
- console.print("Configuring SSH...")
85
- _setup_ssh(client, cluster_id)
86
- return
87
-
88
- # Start server
89
- console.print(f"Starting server with profile '[cyan]{profile_slug}[/cyan]'...")
90
-
91
- def start():
92
- return client.start_server(profile_slug, cluster_id)
93
-
94
- run_progress_task(
95
- start,
96
- description="Starting...",
97
- include_error_traceback=False,
91
+ run_launch(
92
+ profile_slug,
93
+ wait=True,
94
+ ssh=not no_ssh,
95
+ timeout=timeout,
96
+ cluster_id=cluster_id,
97
+ client=_get_client(),
98
98
  )
99
-
100
- # Wait for server
101
- def wait():
102
- return client.wait_for_server(timeout=timeout, cluster_id=cluster_id)
103
-
104
- try:
105
- status = run_progress_task(
106
- wait,
107
- description="Waiting for server to be ready...",
108
- include_error_traceback=False,
109
- )
110
- except TimeoutError:
111
- console.print(f"[yellow]Server did not start within {timeout}s[/yellow]")
112
- console.print("Check status: qbraid compute server status")
113
- raise typer.Exit(code=1)
114
-
115
- # Configure SSH
116
- if not no_ssh:
117
- console.print()
118
- _setup_ssh(client, cluster_id)
119
-
120
99
  except (typer.Exit, SystemExit):
121
100
  raise
122
- except Exception as e:
123
- handle_error(str(e))
124
- raise typer.Exit(code=1)
125
-
126
-
127
- def _setup_ssh(client, cluster_id: Optional[str]):
128
- """Configure SSH for the running server."""
129
- from qbraid_core.services.compute import setup_qbraid_ssh
130
-
131
- token_resp = client.get_session_token(cluster_id)
132
- status = client.get_server_status(cluster_id)
133
- username = status.user_info.name if status.user_info else token_resp.token.user
134
-
135
- ssh_config = setup_qbraid_ssh(
136
- username=username,
137
- token=token_resp.token.token,
138
- cluster_id=token_resp.cluster_id,
139
- )
140
-
141
- # Show connection info
142
- panel_lines = [
143
- "[bold cyan]SSH[/bold cyan]",
144
- f" {ssh_config['ssh_command']}",
145
- "",
146
- "[bold blue]VSCode[/bold blue]",
147
- f" {ssh_config['vscode_command']}",
148
- "",
149
- "[bold magenta]Cursor[/bold magenta]",
150
- f" {ssh_config['cursor_command']}",
151
- ]
152
-
153
- panel = Panel(
154
- "\n".join(panel_lines),
155
- title="Connect with",
156
- border_style="green",
157
- )
158
- console.print(panel)
101
+ except Exception as err: # pylint: disable=broad-exception-caught
102
+ _fail(err)
159
103
 
160
104
 
161
105
  @compute_app.command("down")
162
106
  def compute_down(
163
- cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Cluster ID"),
107
+ target: Optional[str] = typer.Argument(
108
+ None, help="Instance: id, hash, label, profile slug, or 'subscription'"
109
+ ),
110
+ cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Subscription cluster ID"),
111
+ instance: Optional[str] = typer.Option(None, "--instance", "-i", help="Same as the positional target"),
164
112
  force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
165
- cleanup_ssh: bool = typer.Option(False, "--cleanup-ssh", help="Remove SSH config"),
113
+ cleanup_ssh: bool = typer.Option(False, "--cleanup-ssh", help="Remove the subscription SSH config"),
166
114
  ):
167
- """Stop the running server.
115
+ """Stop an instance.
116
+
117
+ Alias for `qbraid compute instances stop`. With no target, stops your
118
+ running on-demand instance (asking which if several) or else the
119
+ subscription server.
168
120
 
169
121
  Example:
170
122
  qbraid compute down
171
- qbraid compute down --cleanup-ssh
123
+ qbraid compute down trainer
172
124
  """
173
125
  try:
174
- client = _get_client()
175
-
176
- # Route to a BMA instance if an on-demand instance is running.
177
- target_instance = _select_bma_instance(client, None, "stop")
178
- if target_instance:
179
- if not force:
180
- confirm = typer.confirm(f"Stop instance {target_instance}?")
181
- if not confirm:
182
- console.print("Cancelled.")
183
- raise typer.Exit(code=0)
184
-
185
- def stop_instance():
186
- return client.stop_bma_instance(target_instance)
187
-
188
- inst = run_progress_task(
189
- stop_instance,
190
- description=f"Stopping {target_instance}...",
191
- include_error_traceback=False,
192
- )
193
- console.print(f"[green]Stopping instance {inst.instance_id}[/green]")
194
- console.print(f" Status: {inst.status.value}")
195
- console.print("[dim]Poll with: qbraid compute status[/dim]")
196
- return
197
-
198
- # Check if a legacy server is running
199
- status = client.get_server_status(cluster_id)
200
- if not status.server_running and not status.server_starting:
201
- console.print("[dim]No server is currently running.[/dim]")
202
- return
203
-
204
- if not force:
205
- profile = status.current_profile or "unknown"
206
- confirm = typer.confirm(f"Stop server (profile: {profile})?")
207
- if not confirm:
208
- console.print("Cancelled.")
209
- raise typer.Exit(code=0)
210
-
211
- # Stop server
212
- def stop():
213
- return client.stop_server(cluster_id)
214
-
215
- run_progress_task(
216
- stop,
217
- description="Stopping server...",
218
- include_error_traceback=False,
219
- )
220
-
221
- # Cleanup SSH if requested
126
+ run_stop(target or instance, yes=force, cluster_id=cluster_id, client=_get_client())
222
127
  if cleanup_ssh:
223
128
  from qbraid_core.services.compute.ssh import remove_pod_ssh_config
224
129
 
225
- # Remove default qbraid-lab config
226
- removed = remove_pod_ssh_config("lab")
227
- if removed:
130
+ if remove_pod_ssh_config("lab"):
228
131
  console.print("[dim]SSH config removed.[/dim]")
229
-
230
132
  except (typer.Exit, SystemExit):
231
133
  raise
232
- except Exception as e:
233
- handle_error(str(e))
234
- raise typer.Exit(code=1)
134
+ except Exception as err: # pylint: disable=broad-exception-caught
135
+ _fail(err)
235
136
 
236
137
 
237
138
  @compute_app.command("status")
238
139
  def compute_status(
239
- cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Cluster ID"),
140
+ cluster_id: Optional[str] = typer.Option(None, "--cluster", "-c", help="Subscription cluster ID"),
240
141
  ):
241
- """Check server status.
142
+ """List your instances.
242
143
 
243
- Quick status check (alias for 'server status').
144
+ Alias for `qbraid compute instances list`.
244
145
  """
245
146
  try:
246
- client = _get_client()
247
- status = client.get_server_status(cluster_id)
248
- instances = _print_unified_status_table(client, status)
249
-
250
- if not status.server_running and not status.server_starting and not instances:
251
- console.print()
252
- console.print("[dim]Start with: qbraid compute up <profile>[/dim]")
253
-
147
+ run_list(cluster_id, client=_get_client())
254
148
  except (typer.Exit, SystemExit):
255
149
  raise
256
- except Exception as e:
257
- handle_error(str(e))
258
- raise typer.Exit(code=1)
150
+ except Exception as err: # pylint: disable=broad-exception-caught
151
+ _fail(err)
259
152
 
260
153
 
261
154
  @compute_app.command("list")