qbraid-cli 0.8.5a1__py3-none-any.whl → 0.12.0a0__py3-none-any.whl
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.
- qbraid_cli/_version.py +16 -14
- qbraid_cli/account/__init__.py +11 -0
- qbraid_cli/account/app.py +67 -0
- qbraid_cli/admin/app.py +21 -13
- qbraid_cli/admin/headers.py +132 -23
- qbraid_cli/admin/validation.py +1 -8
- qbraid_cli/chat/__init__.py +11 -0
- qbraid_cli/chat/app.py +76 -0
- qbraid_cli/configure/actions.py +21 -2
- qbraid_cli/configure/app.py +147 -2
- qbraid_cli/configure/claude_config.py +215 -0
- qbraid_cli/devices/app.py +27 -4
- qbraid_cli/envs/activate.py +38 -8
- qbraid_cli/envs/app.py +716 -89
- qbraid_cli/envs/create.py +3 -2
- qbraid_cli/files/__init__.py +11 -0
- qbraid_cli/files/app.py +139 -0
- qbraid_cli/handlers.py +35 -5
- qbraid_cli/jobs/app.py +33 -13
- qbraid_cli/jobs/toggle_braket.py +2 -13
- qbraid_cli/jobs/validation.py +1 -0
- qbraid_cli/kernels/app.py +4 -3
- qbraid_cli/main.py +57 -13
- qbraid_cli/mcp/__init__.py +10 -0
- qbraid_cli/mcp/app.py +126 -0
- qbraid_cli/mcp/serve.py +321 -0
- qbraid_cli/pip/app.py +2 -2
- qbraid_cli/pip/hooks.py +1 -0
- {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/METADATA +37 -14
- qbraid_cli-0.12.0a0.dist-info/RECORD +46 -0
- {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/WHEEL +1 -1
- {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info/licenses}/LICENSE +6 -4
- qbraid_cli/admin/buildlogs.py +0 -114
- qbraid_cli/credits/__init__.py +0 -11
- qbraid_cli/credits/app.py +0 -35
- qbraid_cli-0.8.5a1.dist-info/RECORD +0 -39
- {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/entry_points.txt +0 -0
- {qbraid_cli-0.8.5a1.dist-info → qbraid_cli-0.12.0a0.dist-info}/top_level.txt +0 -0
qbraid_cli/mcp/serve.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
MCP aggregator server implementation.
|
|
6
|
+
|
|
7
|
+
Provides stdio-based MCP server that aggregates multiple qBraid MCP backends.
|
|
8
|
+
"""
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import signal
|
|
13
|
+
import sys
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
from qbraid_core.services.mcp import MCPRouter, MCPWebSocketClient, discover_mcp_servers
|
|
18
|
+
from qbraid_core.sessions import QbraidSession
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def setup_logging(debug: bool = False) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Configure logging for MCP server.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
debug: Enable debug logging
|
|
27
|
+
"""
|
|
28
|
+
level = logging.DEBUG if debug else logging.INFO
|
|
29
|
+
|
|
30
|
+
# Log to stderr so stdout remains clean for MCP protocol
|
|
31
|
+
logging.basicConfig(
|
|
32
|
+
level=level,
|
|
33
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
34
|
+
stream=sys.stderr,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MCPAggregatorServer:
|
|
39
|
+
"""
|
|
40
|
+
MCP aggregator server that bridges stdio to multiple WebSocket backends.
|
|
41
|
+
|
|
42
|
+
Architecture:
|
|
43
|
+
Claude Desktop (stdio) <-> THIS SERVER <-> Multiple MCP WebSocket Servers
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
session: QbraidSession,
|
|
49
|
+
workspace: str = "lab",
|
|
50
|
+
include_staging: bool = False,
|
|
51
|
+
debug: bool = False,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initialize MCP aggregator server.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
session: Authenticated qBraid session
|
|
58
|
+
workspace: Workspace to connect to
|
|
59
|
+
include_staging: Include staging endpoints
|
|
60
|
+
debug: Enable debug logging
|
|
61
|
+
"""
|
|
62
|
+
self.session = session
|
|
63
|
+
self.workspace = workspace
|
|
64
|
+
self.include_staging = include_staging
|
|
65
|
+
self.debug = debug
|
|
66
|
+
self.router: Optional[MCPRouter] = None
|
|
67
|
+
self.logger = logging.getLogger(__name__)
|
|
68
|
+
self._shutdown_event = asyncio.Event()
|
|
69
|
+
|
|
70
|
+
async def initialize_backends(self) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Discover and connect to MCP backend servers.
|
|
73
|
+
"""
|
|
74
|
+
# Get user info for building WebSocket URLs
|
|
75
|
+
try:
|
|
76
|
+
user_info = self.session.get_user()
|
|
77
|
+
username = user_info.get("email")
|
|
78
|
+
if not username:
|
|
79
|
+
raise ValueError("User email not found in session")
|
|
80
|
+
except Exception as err:
|
|
81
|
+
self.logger.error("Failed to get user info: %s", err)
|
|
82
|
+
typer.secho(
|
|
83
|
+
"Error: Could not authenticate. Please run 'qbraid configure' first.",
|
|
84
|
+
fg=typer.colors.RED,
|
|
85
|
+
err=True,
|
|
86
|
+
)
|
|
87
|
+
raise typer.Exit(1)
|
|
88
|
+
|
|
89
|
+
# Discover available MCP endpoints
|
|
90
|
+
endpoints = discover_mcp_servers(
|
|
91
|
+
workspace=self.workspace,
|
|
92
|
+
include_staging=self.include_staging,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if not endpoints:
|
|
96
|
+
typer.secho(
|
|
97
|
+
f"Warning: No MCP endpoints found for workspace '{self.workspace}'",
|
|
98
|
+
fg=typer.colors.YELLOW,
|
|
99
|
+
err=True,
|
|
100
|
+
)
|
|
101
|
+
raise typer.Exit(1)
|
|
102
|
+
|
|
103
|
+
self.logger.info("Discovered %d MCP endpoint(s)", len(endpoints))
|
|
104
|
+
|
|
105
|
+
# Create router
|
|
106
|
+
self.router = MCPRouter(on_message=self._handle_backend_message)
|
|
107
|
+
|
|
108
|
+
# Create WebSocket clients for each endpoint
|
|
109
|
+
for endpoint in endpoints:
|
|
110
|
+
# Use JupyterHub token for lab endpoints, API key for other endpoints
|
|
111
|
+
try:
|
|
112
|
+
if endpoint.name.startswith("lab"):
|
|
113
|
+
try:
|
|
114
|
+
token_data = self.session.get_jupyter_token_data()
|
|
115
|
+
token = token_data.get("token")
|
|
116
|
+
if not token:
|
|
117
|
+
raise ValueError("Token not found in response data")
|
|
118
|
+
except Exception as err:
|
|
119
|
+
self.logger.error(
|
|
120
|
+
"Failed to get Jupyter token for endpoint '%s': %s",
|
|
121
|
+
endpoint.name,
|
|
122
|
+
err,
|
|
123
|
+
)
|
|
124
|
+
typer.secho(
|
|
125
|
+
f"Error: Could not retrieve Jupyter token for '{endpoint.name}'. "
|
|
126
|
+
"Please ensure you are authenticated with qBraid Lab.",
|
|
127
|
+
fg=typer.colors.RED,
|
|
128
|
+
err=True,
|
|
129
|
+
)
|
|
130
|
+
raise typer.Exit(1)
|
|
131
|
+
else:
|
|
132
|
+
token = self.session.api_key
|
|
133
|
+
if not token:
|
|
134
|
+
self.logger.error("No API key available for endpoint '%s'", endpoint.name)
|
|
135
|
+
typer.secho(
|
|
136
|
+
f"Error: No API key available for '{endpoint.name}'. "
|
|
137
|
+
"Please run 'qbraid configure' to set up your credentials.",
|
|
138
|
+
fg=typer.colors.RED,
|
|
139
|
+
err=True,
|
|
140
|
+
)
|
|
141
|
+
raise typer.Exit(1)
|
|
142
|
+
|
|
143
|
+
ws_url = endpoint.build_url(username, token)
|
|
144
|
+
self.logger.info("Configuring backend: %s", endpoint.name)
|
|
145
|
+
|
|
146
|
+
client = MCPWebSocketClient(
|
|
147
|
+
websocket_url=ws_url,
|
|
148
|
+
on_message=self._handle_backend_message,
|
|
149
|
+
name=endpoint.name,
|
|
150
|
+
)
|
|
151
|
+
self.router.add_backend(endpoint.name, client)
|
|
152
|
+
except typer.Exit:
|
|
153
|
+
raise # Re-raise typer.Exit to propagate clean exits
|
|
154
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
155
|
+
self.logger.error("Failed to configure backend '%s': %s", endpoint.name, err)
|
|
156
|
+
typer.secho(
|
|
157
|
+
f"Error: Failed to configure backend '{endpoint.name}': {err}",
|
|
158
|
+
fg=typer.colors.RED,
|
|
159
|
+
err=True,
|
|
160
|
+
)
|
|
161
|
+
raise typer.Exit(1)
|
|
162
|
+
|
|
163
|
+
# Connect to all backends
|
|
164
|
+
self.logger.info("Connecting to backends...")
|
|
165
|
+
await self.router.connect_all()
|
|
166
|
+
|
|
167
|
+
# Check connection status
|
|
168
|
+
connected = self.router.get_connected_backends()
|
|
169
|
+
if not connected:
|
|
170
|
+
typer.secho(
|
|
171
|
+
"Error: Failed to connect to any MCP backends",
|
|
172
|
+
fg=typer.colors.RED,
|
|
173
|
+
err=True,
|
|
174
|
+
)
|
|
175
|
+
raise typer.Exit(1)
|
|
176
|
+
|
|
177
|
+
self.logger.info("Connected to %d backend(s): %s", len(connected), ", ".join(connected))
|
|
178
|
+
typer.secho(
|
|
179
|
+
f"MCP aggregator ready ({len(connected)} backend(s) connected)",
|
|
180
|
+
fg=typer.colors.GREEN,
|
|
181
|
+
err=True,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def _handle_backend_message(self, message: dict) -> None:
|
|
185
|
+
"""
|
|
186
|
+
Handle messages from backend MCP servers.
|
|
187
|
+
|
|
188
|
+
Forwards messages to stdout for Claude Desktop.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
message: Message dictionary from backend
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
self.logger.info(
|
|
195
|
+
"📥 _handle_backend_message called with message: %s", str(message)[:100]
|
|
196
|
+
)
|
|
197
|
+
# Write message to stdout as JSON
|
|
198
|
+
json_str = json.dumps(message)
|
|
199
|
+
self.logger.info("📤 Writing to stdout: %s", json_str[:100])
|
|
200
|
+
sys.stdout.write(json_str + "\n")
|
|
201
|
+
sys.stdout.flush()
|
|
202
|
+
self.logger.info("✅ Successfully sent to client")
|
|
203
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
204
|
+
self.logger.error("❌ Error forwarding message: %s", err, exc_info=True)
|
|
205
|
+
|
|
206
|
+
async def _stdin_loop(self) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Read messages from stdin (Claude Desktop) and route to backends.
|
|
209
|
+
"""
|
|
210
|
+
loop = asyncio.get_event_loop()
|
|
211
|
+
|
|
212
|
+
self.logger.info("Starting stdin loop...")
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
while not self._shutdown_event.is_set():
|
|
216
|
+
# Read line from stdin (non-blocking)
|
|
217
|
+
try:
|
|
218
|
+
line = await loop.run_in_executor(None, sys.stdin.readline)
|
|
219
|
+
if not line:
|
|
220
|
+
# EOF reached (stdin closed)
|
|
221
|
+
self.logger.info("stdin closed, shutting down...")
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
line = line.strip()
|
|
225
|
+
if not line:
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
self.logger.debug("Received from client: %s...", line[:100])
|
|
229
|
+
|
|
230
|
+
# Parse JSON message
|
|
231
|
+
message = json.loads(line)
|
|
232
|
+
|
|
233
|
+
# Route to appropriate backend
|
|
234
|
+
if self.router:
|
|
235
|
+
await self.router.handle_message(message)
|
|
236
|
+
|
|
237
|
+
except json.JSONDecodeError as err:
|
|
238
|
+
self.logger.error("Invalid JSON from client: %s", err)
|
|
239
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
240
|
+
self.logger.error("Error processing message: %s", err)
|
|
241
|
+
|
|
242
|
+
except asyncio.CancelledError:
|
|
243
|
+
self.logger.info("stdin loop cancelled")
|
|
244
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
245
|
+
self.logger.error("Fatal error in stdin loop: %s", err)
|
|
246
|
+
finally:
|
|
247
|
+
self._shutdown_event.set()
|
|
248
|
+
|
|
249
|
+
async def run(self) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Run the MCP aggregator server.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
# Setup signal handlers for graceful shutdown
|
|
255
|
+
def signal_handler(signum, _frame): # pylint: disable=unused-argument
|
|
256
|
+
self.logger.info("Received signal %d, shutting down...", signum)
|
|
257
|
+
self._shutdown_event.set()
|
|
258
|
+
|
|
259
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
260
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
261
|
+
|
|
262
|
+
# Start backend initialization and stdin loop concurrently
|
|
263
|
+
# This allows the server to respond to Claude's initialize request
|
|
264
|
+
# while backends are still connecting in the background
|
|
265
|
+
try:
|
|
266
|
+
await asyncio.gather(
|
|
267
|
+
self.initialize_backends(),
|
|
268
|
+
self._stdin_loop(),
|
|
269
|
+
)
|
|
270
|
+
finally:
|
|
271
|
+
# Cleanup
|
|
272
|
+
self.logger.info("Shutting down backends...")
|
|
273
|
+
if self.router:
|
|
274
|
+
await self.router.shutdown_all()
|
|
275
|
+
self.logger.info("Shutdown complete")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def serve_mcp(workspace: str, include_staging: bool, debug: bool) -> None:
|
|
279
|
+
"""
|
|
280
|
+
Start the qBraid MCP aggregator server.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
workspace: Workspace name (lab, qbook, etc.)
|
|
284
|
+
include_staging: Include staging endpoints
|
|
285
|
+
debug: Enable debug logging
|
|
286
|
+
"""
|
|
287
|
+
# Setup logging
|
|
288
|
+
setup_logging(debug=debug)
|
|
289
|
+
logger = logging.getLogger(__name__)
|
|
290
|
+
|
|
291
|
+
logger.info("Starting qBraid MCP aggregator...")
|
|
292
|
+
logger.info("Workspace: %s, Staging: %s, Debug: %s", workspace, include_staging, debug)
|
|
293
|
+
|
|
294
|
+
# Create qBraid session
|
|
295
|
+
try:
|
|
296
|
+
session = QbraidSession()
|
|
297
|
+
except Exception as err:
|
|
298
|
+
typer.secho(
|
|
299
|
+
f"Error creating qBraid session: {err}\n"
|
|
300
|
+
"Please run 'qbraid configure' to set up your credentials.",
|
|
301
|
+
fg=typer.colors.RED,
|
|
302
|
+
err=True,
|
|
303
|
+
)
|
|
304
|
+
raise typer.Exit(1)
|
|
305
|
+
|
|
306
|
+
# Create and run server
|
|
307
|
+
server = MCPAggregatorServer(
|
|
308
|
+
session=session,
|
|
309
|
+
workspace=workspace,
|
|
310
|
+
include_staging=include_staging,
|
|
311
|
+
debug=debug,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
asyncio.run(server.run())
|
|
316
|
+
except KeyboardInterrupt:
|
|
317
|
+
logger.info("Interrupted by user")
|
|
318
|
+
except Exception as err:
|
|
319
|
+
logger.error("Fatal error: %s", err, exc_info=debug)
|
|
320
|
+
typer.secho(f"Error: {err}", fg=typer.colors.RED, err=True)
|
|
321
|
+
raise typer.Exit(1)
|
qbraid_cli/pip/app.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
Module defining commands in the 'qbraid pip' namespace.
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
|
|
@@ -15,8 +16,7 @@ from qbraid_core.system.executables import get_active_python_path
|
|
|
15
16
|
from qbraid_cli.handlers import handle_error
|
|
16
17
|
from qbraid_cli.pip.hooks import get_env_cfg_path, safe_set_include_sys_packages
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
pip_app = typer.Typer(help="Run pip command in active qBraid environment.")
|
|
19
|
+
pip_app = typer.Typer(help="Run pip command in active qBraid environment.", no_args_is_help=True)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@pip_app.command(
|
qbraid_cli/pip/hooks.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0a0
|
|
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
|
-
License: Proprietary
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
7
|
Project-URL: Homepage, https://docs.qbraid.com/cli/user-guide/overview
|
|
8
8
|
Project-URL: Documentation, https://docs.qbraid.com/cli/api-reference/qbraid
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/qBraid/community/issues
|
|
@@ -12,7 +12,6 @@ Keywords: qbraid,cli,quantum,cloud
|
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Natural Language :: English
|
|
15
|
-
Classifier: License :: Other/Proprietary License
|
|
16
15
|
Classifier: Intended Audience :: System Administrators
|
|
17
16
|
Classifier: Operating System :: Microsoft :: Windows
|
|
18
17
|
Classifier: Operating System :: POSIX :: Linux
|
|
@@ -23,20 +22,28 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
27
|
Requires-Python: >=3.9
|
|
27
28
|
Description-Content-Type: text/markdown
|
|
28
29
|
License-File: LICENSE
|
|
29
30
|
Requires-Dist: typer>=0.12.1
|
|
30
31
|
Requires-Dist: rich>=10.11.0
|
|
31
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: click<=8.1.8
|
|
33
|
+
Requires-Dist: qbraid-core[environments]>=0.2.0a0
|
|
34
|
+
Provides-Extra: jobs
|
|
35
|
+
Requires-Dist: amazon-braket-sdk>=1.48.1; extra == "jobs"
|
|
36
|
+
Provides-Extra: envs
|
|
37
|
+
Requires-Dist: qbraid-core[environments]==0.2.0a0; extra == "envs"
|
|
38
|
+
Provides-Extra: mcp
|
|
39
|
+
Requires-Dist: qbraid-core[mcp]==0.2.0a0; extra == "mcp"
|
|
32
40
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: ruff; extra == "dev"
|
|
34
41
|
Requires-Dist: isort; extra == "dev"
|
|
35
42
|
Requires-Dist: black; extra == "dev"
|
|
36
43
|
Requires-Dist: pytest; extra == "dev"
|
|
37
44
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
46
|
+
Dynamic: license-file
|
|
40
47
|
|
|
41
48
|
<img width="full" alt="qbraid_cli" src="https://qbraid-static.s3.amazonaws.com/logos/qbraid-cli-banner.png">
|
|
42
49
|
|
|
@@ -49,7 +56,11 @@ Requires-Dist: amazon-braket-sdk>=1.48.1; extra == "jobs"
|
|
|
49
56
|
|
|
50
57
|
Command Line Interface for interacting with all parts of the qBraid platform.
|
|
51
58
|
|
|
52
|
-
The **qBraid CLI** is a versatile command-line interface tool designed for seamless interaction with qBraid cloud services and quantum software management tools. Initially exclusive to the [qBraid Lab](https://docs.qbraid.com/lab/user-guide/overview) platform, the CLI now supports local installations as well. This enhancement broadens access to features like [qBraid Quantum Jobs](https://docs.qbraid.com/cli/user-guide/quantum-jobs), enabling direct
|
|
59
|
+
The **qBraid CLI** is a versatile command-line interface tool designed for seamless interaction with qBraid cloud services and quantum software management tools. Initially exclusive to the [qBraid Lab](https://docs.qbraid.com/lab/user-guide/overview) platform, the CLI now supports local installations as well. This enhancement broadens access to features like [qBraid Quantum Jobs](https://docs.qbraid.com/cli/user-guide/quantum-jobs), enabling direct, pre-configured access to QPUs from IonQ, Oxford Quantum Circuits, QuEra, Rigetti, and IQM, as well as on-demand simulators from qBraid, AWS, IonQ, QuEra, and NEC. See [pricing](https://docs.qbraid.com/home/pricing) for more.
|
|
60
|
+
|
|
61
|
+
*Resources*:
|
|
62
|
+
- [User Guide](https://docs.qbraid.com/cli/user-guide/overview)
|
|
63
|
+
- [API Reference](https://docs.qbraid.com/cli/api-reference/qbraid)
|
|
53
64
|
|
|
54
65
|
## Getting Started
|
|
55
66
|
|
|
@@ -66,6 +77,12 @@ You can also install the qBraid-CLI from PyPI with:
|
|
|
66
77
|
pip install qbraid-cli
|
|
67
78
|
```
|
|
68
79
|
|
|
80
|
+
To manage qBraid [environments](https://docs.qbraid.com/lab/user-guide/environments) using the CLI, you must also install the `envs` extra:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install 'qbraid-cli[envs]'
|
|
84
|
+
```
|
|
85
|
+
|
|
69
86
|
## Local configuration
|
|
70
87
|
|
|
71
88
|
After installation, you must configure your account credentials to use the CLI locally:
|
|
@@ -74,12 +91,14 @@ After installation, you must configure your account credentials to use the CLI l
|
|
|
74
91
|
[account.qbraid.com](https://account.qbraid.com/)
|
|
75
92
|
2. Copy your API Key token from the left side of
|
|
76
93
|
your [account page](https://account.qbraid.com/):
|
|
77
|
-
3. Save your API key from step 2 in local configuration file `~/.qbraid/qbraidrc` using:
|
|
94
|
+
3. Save your API key from step 2 in local [configuration file](https://docs.qbraid.com/cli/user-guide/config-files) `~/.qbraid/qbraidrc` using:
|
|
78
95
|
|
|
79
96
|
```bash
|
|
80
97
|
$ qbraid configure
|
|
81
98
|
```
|
|
82
99
|
|
|
100
|
+
For more on API keys, see [documentation](https://docs.qbraid.com/home/account#api-keys).
|
|
101
|
+
|
|
83
102
|
## Basic Commands
|
|
84
103
|
|
|
85
104
|
```bash
|
|
@@ -139,12 +158,18 @@ Options
|
|
|
139
158
|
--help Show this message and exit.
|
|
140
159
|
|
|
141
160
|
Commands
|
|
161
|
+
account Manage qBraid account
|
|
162
|
+
admin CI/CD commands for qBraid maintainers.
|
|
142
163
|
configure Configure qBraid CLI options.
|
|
143
|
-
|
|
164
|
+
account Manage qBraid account.
|
|
165
|
+
chat Interact with qBraid AI chat service.
|
|
144
166
|
devices Manage qBraid quantum devices.
|
|
145
167
|
envs Manage qBraid environments.
|
|
168
|
+
files Manage qBraid cloud storage files.
|
|
146
169
|
jobs Manage qBraid quantum jobs.
|
|
147
170
|
kernels Manage qBraid kernels.
|
|
171
|
+
mcp MCP (Model Context Protocol) aggregator commands.
|
|
172
|
+
pip Run pip command in active qBraid environment.
|
|
148
173
|
```
|
|
149
174
|
|
|
150
175
|
To get the version of the qBraid CLI:
|
|
@@ -170,7 +195,5 @@ In [1]: %load_ext qbraid_magic
|
|
|
170
195
|
Now you can continue to use the qBraid-CLI as normal from within your Jupyter notebook using the magic ``%`` operator, e.g.
|
|
171
196
|
|
|
172
197
|
```python
|
|
173
|
-
In [2]: %qbraid
|
|
174
|
-
|
|
175
|
-
In [3]: %qbraid jobs enable braket -y
|
|
198
|
+
In [2]: %qbraid chat -f code -p "Write a Qiskit bell circuit"
|
|
176
199
|
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
qbraid_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
qbraid_cli/_version.py,sha256=3XwaThsiOWydODTKqEaylQmodo5qZxp3-tSzxEJq1sw,456
|
|
3
|
+
qbraid_cli/exceptions.py,sha256=KjlhYJhSHMVazaNiBjD_Ur06w4sekP8zRsFzBdyIpno,672
|
|
4
|
+
qbraid_cli/handlers.py,sha256=qRxrB37-n9WBYIAf63KLEAPSQ7Hfhb1qRaHgsA2TVH8,8069
|
|
5
|
+
qbraid_cli/main.py,sha256=s0PA-jlebLxrFiI_mPDhioQ8JHTvMOjprFa7vbsmLII,3996
|
|
6
|
+
qbraid_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
qbraid_cli/account/__init__.py,sha256=smlpUcVkM3QEbJG0norGM7i71XBJlUGQYByswTfPnmg,181
|
|
8
|
+
qbraid_cli/account/app.py,sha256=_On93HBaBmyizcxnArpdN6zeoA1lFMNPhya0DvBDTJ4,1956
|
|
9
|
+
qbraid_cli/admin/__init__.py,sha256=qcWD5mQEUCtr49mrUpZmk7eGDe0L_Gtc8RwZmzIXSwo,175
|
|
10
|
+
qbraid_cli/admin/app.py,sha256=i_JeyJYHT6qoJrsTuf_eippp7AG6ZJ_N6-Dsrpv2XHQ,1476
|
|
11
|
+
qbraid_cli/admin/headers.py,sha256=QWAEuOu3rqumngLlOaGTI2R2wqyqmC0gUNpRt_74pd0,10610
|
|
12
|
+
qbraid_cli/admin/validation.py,sha256=fhpttxupCGBk56ExQPuuQm8nMnptLLy_8sj-EjpM8g0,729
|
|
13
|
+
qbraid_cli/chat/__init__.py,sha256=NO41vndEdfr0vDynNcmHFh-nhzWjnWqGm4M9parikck,258
|
|
14
|
+
qbraid_cli/chat/app.py,sha256=-YqCLGDh4ezF149xB3dfuUAQotKAklZwYp0BL3HhA90,2256
|
|
15
|
+
qbraid_cli/configure/__init__.py,sha256=YaJ74Ztz2vl3eYp8_jVBucWkXscxz7EZEIzr70OfuOM,187
|
|
16
|
+
qbraid_cli/configure/actions.py,sha256=Zv-y7iGgj1fwYceMSXrurpK2PyFCIitKXPqCb4H9AZo,3818
|
|
17
|
+
qbraid_cli/configure/app.py,sha256=CxzQmWDpCwpNe4HO7sOxJzbFwJ4dayzD8sjzefQvNNE,7260
|
|
18
|
+
qbraid_cli/configure/claude_config.py,sha256=OJqdEA0wr19F4BnZMAxRBf7rA9FehX12VzC6TQ-rr6c,6457
|
|
19
|
+
qbraid_cli/devices/__init__.py,sha256=hiScO-px6jCL5cJj5Hbty55EUfNejTO4bmqUZuS3aqc,181
|
|
20
|
+
qbraid_cli/devices/app.py,sha256=B-cRdV092C6ACeu3C-5l2P_izpPDxvCzzAKfxO1WkaM,3224
|
|
21
|
+
qbraid_cli/devices/validation.py,sha256=YhShyUufgrKnx2XjXOXF-PqFJYklJT9CgeqIwKcNam4,809
|
|
22
|
+
qbraid_cli/envs/__init__.py,sha256=1-cMvrATsddYxcetPJWxq6bEOqJWMktGdhoZ4qm8euA,172
|
|
23
|
+
qbraid_cli/envs/activate.py,sha256=pYugi2MmEjHKQlky7fVHvB_BpBu7xgOwa1Vrh50DXEo,3251
|
|
24
|
+
qbraid_cli/envs/app.py,sha256=VVLNz_AvlH66RuV1JMB4mDPxXB7VhVg8jEmRKoZz2Ko,33035
|
|
25
|
+
qbraid_cli/envs/create.py,sha256=2isv1eLFKxVutbrkHawMQK67Cz939eLwfhMBtVHiOas,1060
|
|
26
|
+
qbraid_cli/envs/data_handling.py,sha256=Ibnp2yJoUDpivb_sNqi0suYgJZNat_LmM6Ya0Ovez5s,1288
|
|
27
|
+
qbraid_cli/files/__init__.py,sha256=3_yhgFoNcviEtS6B75uJBrfFFUjsrMcccCNEntJ54xU,175
|
|
28
|
+
qbraid_cli/files/app.py,sha256=gNa88cfAmpzYd9VOK7Q-OWJ1LyyHxq88PKhg8Q9w9Mk,3771
|
|
29
|
+
qbraid_cli/jobs/__init__.py,sha256=qVLRHYIzP4XHpx_QWP_vCzd3LsCscCORaEx-Vcbx29U,172
|
|
30
|
+
qbraid_cli/jobs/app.py,sha256=NchePouZkoHWxqV1lwiYuXwrLeP99zITKz5Ezc1pm2I,5383
|
|
31
|
+
qbraid_cli/jobs/toggle_braket.py,sha256=3AEu-Z5q4avduB-fJMyMTVTuyZXuA8m-hnvi325wIv4,7444
|
|
32
|
+
qbraid_cli/jobs/validation.py,sha256=KlkqVH1-vlNCHSayEpxzyXU86_TMN5prGfMFEoyBsFs,2971
|
|
33
|
+
qbraid_cli/kernels/__init__.py,sha256=jORS9vV17s5laQyq8gSVB18EPBImgEIbMZ1wKC094DA,181
|
|
34
|
+
qbraid_cli/kernels/app.py,sha256=n-iyWJHy7_ML6qk4pp-v_rQkGA7WfnZMG8gyiCFGO1c,2948
|
|
35
|
+
qbraid_cli/mcp/__init__.py,sha256=GwW67sQpUViWhTpd6yq-kG8HH87QmCjKO0v2Ys_8Qpw,195
|
|
36
|
+
qbraid_cli/mcp/app.py,sha256=TQnM9LI9OsgbGV6Pkp_Ri7d62dJs5voPKmG1FeweSy0,3463
|
|
37
|
+
qbraid_cli/mcp/serve.py,sha256=4LFXjuv6CQPIuskPxcFXS0qE2igv1vYmLup-u1xhIZ0,11384
|
|
38
|
+
qbraid_cli/pip/__init__.py,sha256=tJtU0rxn-ODogNh5Y4pp_BgDQXMN-3JY1QGj0OZHwjQ,169
|
|
39
|
+
qbraid_cli/pip/app.py,sha256=jkk-djductrDOJIRYfHK_7WDJ12f0zOT3MMkiZp97oM,1365
|
|
40
|
+
qbraid_cli/pip/hooks.py,sha256=jkIeev3cOd-cmaoJSdSqbmhTYCs6z1we84FMqa3ZoZw,2124
|
|
41
|
+
qbraid_cli-0.12.0a0.dist-info/licenses/LICENSE,sha256=3KvWfsaXBCqbZ4qwk5jN9CQXE53tQeaZTySN5a-CCgQ,2753
|
|
42
|
+
qbraid_cli-0.12.0a0.dist-info/METADATA,sha256=9RRo480Itl5YHFjHso2qR_ecYtJThGQTO5OZ9bUbT5Q,7865
|
|
43
|
+
qbraid_cli-0.12.0a0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
44
|
+
qbraid_cli-0.12.0a0.dist-info/entry_points.txt,sha256=c5ZJ7NjbxhDqMpou9q5F03_b_KG34HzFDijIDmEIwgQ,47
|
|
45
|
+
qbraid_cli-0.12.0a0.dist-info/top_level.txt,sha256=LTYJgeYSCHo9Il8vZu0yIPuGdGyNaIw6iRy6BeoZo8o,11
|
|
46
|
+
qbraid_cli-0.12.0a0.dist-info/RECORD,,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
qBraid
|
|
1
|
+
qBraid Commercial Software License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025, qBraid Development Team
|
|
4
4
|
|
|
5
5
|
All rights reserved.
|
|
6
6
|
|
|
7
|
-
This license agreement ("License") is between the qBraid Development Team ("Author") and you, the Licensee.
|
|
7
|
+
This license agreement ("License") is between the qBraid Development Team ("Author") and you, the Licensee. This software and associated documentation files (the "Software") may only be used by authorized qBraid customers who have agreed to qBraid's Terms of Service and have a valid subscription or license agreement with qBraid. By using or distributing this Software, you agree to the following terms:
|
|
8
8
|
|
|
9
9
|
1. Grant of License.
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@ This license agreement ("License") is between the qBraid Development Team ("Auth
|
|
|
24
24
|
|
|
25
25
|
5. Attribution
|
|
26
26
|
|
|
27
|
-
If the Software is used in a non-private setting, including but not limited to academic work, commercial settings, or published literature, attribution must be given to the "qBraid
|
|
27
|
+
If the Software is used in a non-private setting, including but not limited to academic work, commercial settings, or published literature, attribution must be given to the "qBraid CLI authored by the qBraid Development Team."
|
|
28
28
|
|
|
29
29
|
6. Disclaimer of Warranty.
|
|
30
30
|
|
|
@@ -39,3 +39,5 @@ This license agreement ("License") is between the qBraid Development Team ("Auth
|
|
|
39
39
|
This License is effective until terminated. Your rights under this License will terminate automatically without notice from the Author if you fail to comply with any term(s) of this License.
|
|
40
40
|
|
|
41
41
|
By using the Software, you agree to be bound by the terms of this License. Redistribution of the Software or use of the Software other than as specifically authorized under this License is prohibited and may result in severe civil and criminal penalties.
|
|
42
|
+
|
|
43
|
+
For licensing inquiries, please contact: contact@qbraid.com
|
qbraid_cli/admin/buildlogs.py
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
-
# All rights reserved.
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
Module defining commands in the 'qbraid admin buildlogs' namespace.
|
|
6
|
-
|
|
7
|
-
This module uses the Typer library to create CLI commands for managing Docker builds and logs
|
|
8
|
-
in an administrative context.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import json
|
|
12
|
-
|
|
13
|
-
import typer
|
|
14
|
-
from qbraid_core.exceptions import RequestsApiError
|
|
15
|
-
from qbraid_core.services.admin.client import AdminClient
|
|
16
|
-
from rich.console import Console
|
|
17
|
-
|
|
18
|
-
from qbraid_cli.handlers import handle_error
|
|
19
|
-
|
|
20
|
-
buildlogs_app = typer.Typer(
|
|
21
|
-
help="Manage qBraid containerized services logs.", pretty_exceptions_show_locals=False
|
|
22
|
-
)
|
|
23
|
-
console = Console()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@buildlogs_app.command(name="get")
|
|
27
|
-
def get_docker_build_logs(
|
|
28
|
-
build_id: str = typer.Option(None, "--build_id", "-b", help="Name of the build ID")
|
|
29
|
-
) -> None:
|
|
30
|
-
"""
|
|
31
|
-
Fetches and displays Docker build logs for a specified build ID.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
build_id (str, optional): The unique identifier for the Docker build.
|
|
35
|
-
|
|
36
|
-
This function queries the administrative backend to retrieve and display build logs.
|
|
37
|
-
If a build ID is provided, it will retrieve and display logs specific to that build ID.
|
|
38
|
-
If build ID not provided, fetches all logs.
|
|
39
|
-
"""
|
|
40
|
-
client = AdminClient()
|
|
41
|
-
|
|
42
|
-
build_log = client.get_docker_build_logs(build_id)
|
|
43
|
-
if build_id and "buildLogs" in build_log and build_log["buildLogs"]:
|
|
44
|
-
log_entry = build_log["buildLogs"][0]
|
|
45
|
-
console.print(log_entry)
|
|
46
|
-
else:
|
|
47
|
-
console.print(build_log)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@buildlogs_app.command(name="post")
|
|
51
|
-
def post_docker_build_log(
|
|
52
|
-
data: str = typer.Option(..., "--data", "-d", help="Data to post to Docker logs")
|
|
53
|
-
) -> None:
|
|
54
|
-
"""
|
|
55
|
-
Posts a new Docker build log entry.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
data (str): JSON string containing the data to be logged.
|
|
59
|
-
|
|
60
|
-
This command converts a JSON string into a dictionary and sends it to the backend service
|
|
61
|
-
to create a new Docker build log.
|
|
62
|
-
"""
|
|
63
|
-
client = AdminClient()
|
|
64
|
-
|
|
65
|
-
try:
|
|
66
|
-
data_dict = json.loads(data)
|
|
67
|
-
console.print(client.post_docker_build_logs(data_dict))
|
|
68
|
-
except RequestsApiError:
|
|
69
|
-
handle_error(message="Couldn't post a build_log.")
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@buildlogs_app.command(name="put")
|
|
73
|
-
def put_docker_build_log(
|
|
74
|
-
build_id: str = typer.Option(..., "--build_id", "-b", help="Name of the build ID"),
|
|
75
|
-
data: str = typer.Option(..., "--data", "-d", help="Data to post to Docker logs"),
|
|
76
|
-
) -> None:
|
|
77
|
-
"""
|
|
78
|
-
Updates an existing Docker build log entry by a given build ID.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
build_id (str): The unique identifier of the Docker build to update.
|
|
82
|
-
data (str): JSON string containing the updated data for the log.
|
|
83
|
-
|
|
84
|
-
This command updates a Docker build log entry, identified by the provided build ID,
|
|
85
|
-
with the new data provided in JSON format.
|
|
86
|
-
"""
|
|
87
|
-
client = AdminClient()
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
data_dict = json.loads(data)
|
|
91
|
-
console.print(client.put_docker_build_logs(build_id, data_dict))
|
|
92
|
-
except RequestsApiError:
|
|
93
|
-
handle_error(message="Couldn't post a build_log.")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@buildlogs_app.command(name="delete")
|
|
97
|
-
def delete_docker_build_log(
|
|
98
|
-
build_id: str = typer.Option(..., "--build_id", "-b", help="ID of the build log to delete")
|
|
99
|
-
) -> None:
|
|
100
|
-
"""
|
|
101
|
-
Deletes a Docker build log entry by a specified build ID.
|
|
102
|
-
|
|
103
|
-
Args:
|
|
104
|
-
build_id (str): The unique identifier of the Docker build log to delete.
|
|
105
|
-
|
|
106
|
-
This command sends a request to delete a Docker build log identified by the provided build ID.
|
|
107
|
-
"""
|
|
108
|
-
client = AdminClient()
|
|
109
|
-
|
|
110
|
-
console.print(client.delete_docker_build_logs(build_id))
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if __name__ == "__main__":
|
|
114
|
-
buildlogs_app()
|