qbraid-cli 0.15.3__tar.gz → 0.16.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 (69) hide show
  1. {qbraid_cli-0.15.3/qbraid_cli.egg-info → qbraid_cli-0.16.0}/PKG-INFO +1 -1
  2. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/pyproject.toml +1 -1
  3. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/ssh.py +42 -27
  4. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/skills/app.py +40 -4
  5. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0/qbraid_cli.egg-info}/PKG-INFO +1 -1
  6. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/LICENSE +0 -0
  7. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/README.md +0 -0
  8. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/__init__.py +0 -0
  9. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/_build_info.py +0 -0
  10. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/_version.py +0 -0
  11. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/account/__init__.py +0 -0
  12. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/account/app.py +0 -0
  13. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/admin/__init__.py +0 -0
  14. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/admin/app.py +0 -0
  15. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/admin/headers.py +0 -0
  16. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/admin/validation.py +0 -0
  17. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/agents/__init__.py +0 -0
  18. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/agents/app.py +0 -0
  19. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/agents/permission_response.py +0 -0
  20. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/ai/__init__.py +0 -0
  21. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/ai/app.py +0 -0
  22. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/ai/token.py +0 -0
  23. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/__init__.py +0 -0
  24. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/app.py +0 -0
  25. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/instances.py +0 -0
  26. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/profiles.py +0 -0
  27. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/compute/server.py +0 -0
  28. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/configure/__init__.py +0 -0
  29. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/configure/actions.py +0 -0
  30. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/configure/app.py +0 -0
  31. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/devices/__init__.py +0 -0
  32. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/devices/app.py +0 -0
  33. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/devices/validation.py +0 -0
  34. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/__init__.py +0 -0
  35. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/activate.py +0 -0
  36. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/app.py +0 -0
  37. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/create.py +0 -0
  38. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/data_handling.py +0 -0
  39. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/groups.py +0 -0
  40. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/publish.py +0 -0
  41. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/registry.py +0 -0
  42. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/envs/share.py +0 -0
  43. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/exceptions.py +0 -0
  44. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/handlers.py +0 -0
  45. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/jobs/__init__.py +0 -0
  46. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/jobs/app.py +0 -0
  47. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/jobs/groups.py +0 -0
  48. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/kernels/__init__.py +0 -0
  49. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/kernels/app.py +0 -0
  50. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/lab/__init__.py +0 -0
  51. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/lab/app.py +0 -0
  52. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/main.py +0 -0
  53. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/pip/__init__.py +0 -0
  54. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/pip/app.py +0 -0
  55. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/pip/hooks.py +0 -0
  56. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/projects/__init__.py +0 -0
  57. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/projects/app.py +0 -0
  58. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/py.typed +0 -0
  59. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/skills/__init__.py +0 -0
  60. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/troubleshoot/__init__.py +0 -0
  61. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli/troubleshoot/app.py +0 -0
  62. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli.egg-info/SOURCES.txt +0 -0
  63. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli.egg-info/dependency_links.txt +0 -0
  64. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli.egg-info/entry_points.txt +0 -0
  65. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli.egg-info/requires.txt +0 -0
  66. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/qbraid_cli.egg-info/top_level.txt +0 -0
  67. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/setup.cfg +0 -0
  68. {qbraid_cli-0.15.3 → qbraid_cli-0.16.0}/tests/test_cli_e2e.py +0 -0
  69. {qbraid_cli-0.15.3 → qbraid_cli-0.16.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.15.3
3
+ Version: 0.16.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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qbraid-cli"
7
- version = "0.15.3"
7
+ version = "0.16.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" }]
@@ -60,9 +60,12 @@ def setup_subscription_ssh(
60
60
  """Configure SSH for the subscription (JupyterHub) server.
61
61
 
62
62
  The one implementation for `ssh setup`, `instances ssh subscription` and
63
- `up --ssh`: checks the server is running, reads token/host/user from the
64
- status URL, honours ssh-only profiles (``ide``), registers the key and
65
- prints the connection panel. Exits 1 with a message when nothing is up.
63
+ `up --ssh`: checks the server is running, mints a session token, honours
64
+ ssh-only profiles (``ide``), registers the key and prints the connection
65
+ panel. Exits 1 with a message when nothing is up.
66
+
67
+ Re-running this is the supported way to recover from a stale token: each
68
+ call mints a fresh one.
66
69
  """
67
70
  # Check if server is running
68
71
  console.print("Checking server status...")
@@ -73,34 +76,46 @@ def setup_subscription_ssh(
73
76
  console.print("Start a server first: qbraid compute instances launch <profile>")
74
77
  raise typer.Exit(code=1)
75
78
 
76
- # Extract token, hostname, and user path from server status URL
77
- # URL format: https://<host>/user/<username>/s-<session>/<ide>?token=<token>
79
+ # Mint a session token rather than scraping one out of the status URL.
80
+ #
81
+ # The old path read `?token=` off `status.url`. That query parameter is
82
+ # only present in the handful of flows that hand back a click-through link;
83
+ # the Hub's own record of a running server is just `/user/<name>/`, with no
84
+ # query string at all. So the scrape usually found nothing and the command
85
+ # died with "Could not extract token from server URL" -- which reads like
86
+ # the instance is unhealthy and sends you debugging the wrong thing. It
87
+ # also meant a stale token could never be refreshed: the only supply was a
88
+ # URL that no longer carried one.
89
+ #
90
+ # `get_session_token()` is the supported source, is what `ssh token`
91
+ # already uses, and returns a fresh token on every call.
78
92
  console.print("Getting session token...")
79
- token = None
80
- username = None
81
- resolved_cluster = status.cluster_id
82
- hub_host = None
93
+ from qbraid_core.services.compute import parse_lab_url
83
94
 
84
- if status.url:
85
- import re
86
- from urllib.parse import parse_qs, urlparse
87
-
88
- parsed = urlparse(status.url)
89
- token = parse_qs(parsed.query).get("token", [None])[0]
90
- hub_host = parsed.hostname
95
+ resolved_cluster = status.cluster_id
96
+ parts = parse_lab_url(status.url) if status.url else {}
97
+ hub_host = parts.get("hub_domain")
91
98
 
92
- # Extract user path prefix: everything between /user/ and /lab or /ssh or end
93
- # e.g., /user/507f1f-kanavs/s-0e146b01/lab → 507f1f-kanavs/s-0e146b01
94
- user_match = re.search(r"/user/(.+?)/(lab|ssh|tree|vscode)(\?|$)", parsed.path)
95
- if user_match:
96
- username = user_match.group(1)
97
- else:
98
- # Fallback: use user_info.name
99
- username = status.user_info.name if status.user_info else None
99
+ try:
100
+ session = client.get_session_token(cluster_id)
101
+ except Exception as err: # pylint: disable=broad-exception-caught
102
+ console.print(f"[red]Could not get a session token for this server:[/red] {err}")
103
+ raise typer.Exit(code=1) from err
104
+
105
+ token = session.token.token
106
+ # The API resolves the account's own cluster; prefer what it reports over
107
+ # whatever the caller guessed, and over the status response.
108
+ resolved_cluster = session.cluster_id or resolved_cluster
109
+
110
+ # Username still comes from the URL, and only falls back to the token's.
111
+ # `parse_lab_url` keeps a named server's session segment attached (
112
+ # "507f1f-kanavs/s-0e146b01"), and that segment is part of the proxy path
113
+ # -- /user/<name>/s-<id>/sshd/. The token reports the bare user, so using
114
+ # it first would silently drop the segment and address the wrong pod.
115
+ username = (
116
+ parts.get("username") or session.token.user or (status.user_info.name if status.user_info else None)
117
+ )
100
118
 
101
- if not token:
102
- console.print("[red]Could not extract token from server URL.[/red]")
103
- raise typer.Exit(code=1)
104
119
  if not username:
105
120
  console.print("[red]Could not determine username from server status.[/red]")
106
121
  raise typer.Exit(code=1)
@@ -6,6 +6,7 @@ Module defining commands in the 'qbraid skills' namespace.
6
6
 
7
7
  """
8
8
 
9
+ import json
9
10
  from enum import Enum
10
11
  from typing import Optional
11
12
 
@@ -68,6 +69,23 @@ class VisibilityOption(str, Enum):
68
69
  # =============================================================================
69
70
 
70
71
 
72
+ def _emit_skills_json(skills) -> None:
73
+ """Print skills as a JSON array on stdout, and nothing else.
74
+
75
+ Agents discover most skills through `qbraid skills list` / `search` rather
76
+ than having them installed, so this output is a discovery surface, not a
77
+ convenience. The table truncates descriptions (40-50 chars), and a
78
+ description is exactly what an agent matches against to decide whether a
79
+ skill applies -- truncating it hides the words that would have triggered it.
80
+ JSON carries them in full.
81
+
82
+ Uses `print`, not `console.print`: Rich would interpret square brackets in a
83
+ description as markup and mangle the payload.
84
+ """
85
+ payload = [skill.model_dump(mode="json", by_alias=True, exclude_none=True) for skill in skills]
86
+ print(json.dumps(payload, indent=2))
87
+
88
+
71
89
  @skills_app.command(name="list")
72
90
  def skills_list(
73
91
  query: Optional[str] = typer.Option(None, "--query", "-q", help="Search query for skills"),
@@ -76,8 +94,12 @@ def skills_list(
76
94
  None, "--visibility", "-v", help="Filter by visibility"
77
95
  ),
78
96
  limit: int = typer.Option(20, "--limit", "-l", help="Maximum number of results"),
97
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON array (for programmatic use)"),
79
98
  ) -> None:
80
- """List available qBraid skills."""
99
+ """List available qBraid skills.
100
+
101
+ Pass --json for the full, untruncated records.
102
+ """
81
103
 
82
104
  def fetch_skills():
83
105
  from qbraid_core.services.skills import SkillsClient
@@ -88,7 +110,13 @@ def skills_list(
88
110
  return client.list_skills(query=query, tags=tag_list, visibility=vis, limit=limit)
89
111
 
90
112
  try:
91
- skills = run_progress_task(fetch_skills)
113
+ # Skip the progress spinner for --json: Rich writes it to the same
114
+ # stream, and a caller piping this into a parser gets the spinner too.
115
+ skills = fetch_skills() if json_output else run_progress_task(fetch_skills)
116
+
117
+ if json_output:
118
+ _emit_skills_json(skills)
119
+ return
92
120
 
93
121
  console = Console()
94
122
 
@@ -128,8 +156,12 @@ def skills_list(
128
156
  def skills_search(
129
157
  query: str = typer.Argument(..., help="Search query"),
130
158
  limit: int = typer.Option(10, "--limit", "-l", help="Maximum number of results"),
159
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON array (for programmatic use)"),
131
160
  ) -> None:
132
- """Search for qBraid skills."""
161
+ """Search for qBraid skills.
162
+
163
+ Pass --json for the full, untruncated records.
164
+ """
133
165
 
134
166
  def fetch_skills():
135
167
  from qbraid_core.services.skills import SkillsClient
@@ -138,7 +170,11 @@ def skills_search(
138
170
  return client.list_skills(query=query, limit=limit)
139
171
 
140
172
  try:
141
- skills = run_progress_task(fetch_skills)
173
+ skills = fetch_skills() if json_output else run_progress_task(fetch_skills)
174
+
175
+ if json_output:
176
+ _emit_skills_json(skills)
177
+ return
142
178
 
143
179
  console = Console()
144
180
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qbraid-cli
3
- Version: 0.15.3
3
+ Version: 0.16.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
File without changes
File without changes
File without changes