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/configure/app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
2
|
# All rights reserved.
|
|
3
3
|
|
|
4
4
|
"""
|
|
@@ -7,7 +7,9 @@ Module defining commands in the 'qbraid configure' namespace.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
|
+
from rich import box
|
|
10
11
|
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
11
13
|
|
|
12
14
|
from qbraid_cli.configure.actions import default_action
|
|
13
15
|
|
|
@@ -36,7 +38,7 @@ def configure(ctx: typer.Context):
|
|
|
36
38
|
def configure_set(
|
|
37
39
|
name: str = typer.Argument(..., help="Config name"),
|
|
38
40
|
value: str = typer.Argument(..., help="Config value"),
|
|
39
|
-
profile: str = typer.Option("default", help="Profile name"),
|
|
41
|
+
profile: str = typer.Option("default", "--profile", "-p", help="Profile name"),
|
|
40
42
|
):
|
|
41
43
|
"""Set configuration value in qbraidrc file."""
|
|
42
44
|
# pylint: disable-next=import-outside-toplevel
|
|
@@ -53,6 +55,62 @@ def configure_set(
|
|
|
53
55
|
typer.echo("Configuration updated successfully.")
|
|
54
56
|
|
|
55
57
|
|
|
58
|
+
@configure_app.command(name="get")
|
|
59
|
+
def configure_get(
|
|
60
|
+
name: str = typer.Argument(..., help="Config name"),
|
|
61
|
+
profile: str = typer.Option("default", "--profile", "-p", help="Profile name"),
|
|
62
|
+
):
|
|
63
|
+
"""Get configuration value from qbraidrc file."""
|
|
64
|
+
# pylint: disable-next=import-outside-toplevel
|
|
65
|
+
from qbraid_core.config import load_config
|
|
66
|
+
|
|
67
|
+
config = load_config()
|
|
68
|
+
|
|
69
|
+
if profile not in config:
|
|
70
|
+
typer.echo(f"Profile '{profile}' not found in configuration.")
|
|
71
|
+
raise typer.Exit(1)
|
|
72
|
+
|
|
73
|
+
if name not in config[profile]:
|
|
74
|
+
typer.echo(f"Configuration '{name}' not found in profile '{profile}'.")
|
|
75
|
+
raise typer.Exit(1)
|
|
76
|
+
|
|
77
|
+
typer.echo(config[profile][name])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@configure_app.command(name="list")
|
|
81
|
+
def configure_list():
|
|
82
|
+
"""List all configuration values in qbraidrc."""
|
|
83
|
+
# pylint: disable-next=import-outside-toplevel
|
|
84
|
+
from qbraid_core.config import load_config
|
|
85
|
+
|
|
86
|
+
config = load_config()
|
|
87
|
+
console = Console()
|
|
88
|
+
profile = "default"
|
|
89
|
+
|
|
90
|
+
if profile not in config:
|
|
91
|
+
typer.echo("Default profile not found in configuration.")
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
|
|
94
|
+
if not config[profile]:
|
|
95
|
+
typer.echo("No configuration values found in default profile.")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
table = Table(show_edge=False, box=box.MINIMAL)
|
|
99
|
+
table.add_column("Name", style="cyan")
|
|
100
|
+
table.add_column("Value", style="green")
|
|
101
|
+
|
|
102
|
+
sensitive_keys = {"api-key", "refresh-token"}
|
|
103
|
+
|
|
104
|
+
for name, value in config[profile].items():
|
|
105
|
+
if name in sensitive_keys and value:
|
|
106
|
+
masked_value = f"*****{str(value)[-3:]}"
|
|
107
|
+
else:
|
|
108
|
+
masked_value = str(value)
|
|
109
|
+
table.add_row(name, masked_value)
|
|
110
|
+
|
|
111
|
+
console.print(table)
|
|
112
|
+
|
|
113
|
+
|
|
56
114
|
@configure_app.command(name="magic")
|
|
57
115
|
def configure_magic():
|
|
58
116
|
"""Enable qBraid IPython magic commands."""
|
|
@@ -73,5 +131,92 @@ def configure_magic():
|
|
|
73
131
|
console.print(f"\n\t{in_1}\n\n\t{in_2}\n")
|
|
74
132
|
|
|
75
133
|
|
|
134
|
+
@configure_app.command(name="claude")
|
|
135
|
+
def configure_claude(
|
|
136
|
+
action: str = typer.Argument(
|
|
137
|
+
...,
|
|
138
|
+
help="Action to perform: 'add', 'remove', or 'show'",
|
|
139
|
+
),
|
|
140
|
+
overwrite: bool = typer.Option(
|
|
141
|
+
False,
|
|
142
|
+
"--overwrite",
|
|
143
|
+
help="Overwrite existing MCP server configuration",
|
|
144
|
+
),
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Manage qBraid MCP server in Claude Desktop configuration.
|
|
148
|
+
|
|
149
|
+
Add, remove, or show the qBraid MCP server in your Claude Desktop config
|
|
150
|
+
(claude_desktop_config.json).
|
|
151
|
+
"""
|
|
152
|
+
# pylint: disable-next=import-outside-toplevel
|
|
153
|
+
from qbraid_cli.configure.claude_config import (
|
|
154
|
+
add_qbraid_mcp_server,
|
|
155
|
+
get_claude_config_path,
|
|
156
|
+
get_qbraid_mcp_server_config,
|
|
157
|
+
remove_qbraid_mcp_server,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
console = Console()
|
|
161
|
+
|
|
162
|
+
# Validate action
|
|
163
|
+
valid_actions = ["add", "remove", "show"]
|
|
164
|
+
if action not in valid_actions:
|
|
165
|
+
console.print(
|
|
166
|
+
f"[red]Error:[/red] Invalid action '{action}'. "
|
|
167
|
+
f"Must be one of: {', '.join(valid_actions)}"
|
|
168
|
+
)
|
|
169
|
+
raise typer.Exit(1)
|
|
170
|
+
|
|
171
|
+
# Check if Claude config exists (except for show which can work without it)
|
|
172
|
+
if action != "show":
|
|
173
|
+
config_path = get_claude_config_path()
|
|
174
|
+
if config_path is None:
|
|
175
|
+
console.print(
|
|
176
|
+
"[yellow]Warning:[/yellow] Claude Desktop config file not found.\n"
|
|
177
|
+
"Expected locations:\n"
|
|
178
|
+
" - macOS: ~/Library/Application Support/Claude/claude_desktop_config.json\n"
|
|
179
|
+
" - Windows: %APPDATA%\\Claude\\claude_desktop_config.json\n"
|
|
180
|
+
" - Linux: ~/.config/Claude/claude_desktop_config.json\n\n"
|
|
181
|
+
"Creating new config file..."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Perform action
|
|
185
|
+
if action == "add":
|
|
186
|
+
success, message = add_qbraid_mcp_server(overwrite=overwrite)
|
|
187
|
+
if success:
|
|
188
|
+
console.print(f"[green]Success:[/green] {message}")
|
|
189
|
+
console.print(
|
|
190
|
+
"\n[yellow]Note:[/yellow] You may need to restart Claude Desktop "
|
|
191
|
+
"for changes to take effect."
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
console.print(f"[red]Error:[/red] {message}")
|
|
195
|
+
raise typer.Exit(1)
|
|
196
|
+
|
|
197
|
+
elif action == "remove":
|
|
198
|
+
success, message = remove_qbraid_mcp_server()
|
|
199
|
+
if success:
|
|
200
|
+
console.print(f"[green]Success:[/green] {message}")
|
|
201
|
+
console.print(
|
|
202
|
+
"\n[yellow]Note:[/yellow] You may need to restart Claude Desktop "
|
|
203
|
+
"for changes to take effect."
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
console.print(f"[red]Error:[/red] {message}")
|
|
207
|
+
raise typer.Exit(1)
|
|
208
|
+
|
|
209
|
+
elif action == "show":
|
|
210
|
+
server_config = get_qbraid_mcp_server_config()
|
|
211
|
+
if server_config is None:
|
|
212
|
+
console.print("No qBraid MCP server found in Claude Desktop configuration.")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
console.print("\n[cyan]qBraid MCP Server Configuration:[/cyan]\n")
|
|
216
|
+
console.print(f" Command: {server_config.get('command', 'N/A')}")
|
|
217
|
+
console.print(f" Args: {server_config.get('args', [])}")
|
|
218
|
+
console.print()
|
|
219
|
+
|
|
220
|
+
|
|
76
221
|
if __name__ == "__main__":
|
|
77
222
|
configure_app()
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Copyright (c) 2025, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Utility functions for managing Claude Desktop configuration.
|
|
6
|
+
|
|
7
|
+
This module provides cross-platform support for locating and updating
|
|
8
|
+
the claude_desktop_config.json file used by Claude Desktop.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import shutil
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_claude_config_path() -> Optional[Path]:
|
|
20
|
+
"""
|
|
21
|
+
Get the path to the Claude Desktop configuration file.
|
|
22
|
+
|
|
23
|
+
Returns the platform-specific path to claude_desktop_config.json:
|
|
24
|
+
- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
25
|
+
- Windows: %APPDATA%\\Claude\\claude_desktop_config.json
|
|
26
|
+
- Linux: ~/.config/Claude/claude_desktop_config.json
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Path to the config file if it exists, None otherwise
|
|
30
|
+
"""
|
|
31
|
+
system = platform.system()
|
|
32
|
+
|
|
33
|
+
if system == "Darwin": # macOS
|
|
34
|
+
config_path = Path.home() / "Library" / "Application Support" / "Claude"
|
|
35
|
+
elif system == "Windows":
|
|
36
|
+
appdata = os.environ.get("APPDATA")
|
|
37
|
+
if not appdata:
|
|
38
|
+
return None
|
|
39
|
+
config_path = Path(appdata) / "Claude"
|
|
40
|
+
elif system == "Linux":
|
|
41
|
+
config_path = Path.home() / ".config" / "Claude"
|
|
42
|
+
else:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
config_file = config_path / "claude_desktop_config.json"
|
|
46
|
+
return config_file if config_file.exists() else None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_claude_config() -> dict:
|
|
50
|
+
"""
|
|
51
|
+
Load the Claude Desktop configuration file.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Configuration dictionary. If file doesn't exist, returns empty dict.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
json.JSONDecodeError: If config file exists but contains invalid JSON
|
|
58
|
+
OSError: If there are permission issues reading the file
|
|
59
|
+
"""
|
|
60
|
+
config_path = get_claude_config_path()
|
|
61
|
+
|
|
62
|
+
if config_path is None or not config_path.exists():
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
66
|
+
return json.load(f)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def save_claude_config(config: dict) -> Path:
|
|
70
|
+
"""
|
|
71
|
+
Save the Claude Desktop configuration file.
|
|
72
|
+
|
|
73
|
+
Creates the configuration directory if it doesn't exist.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
config: Configuration dictionary to save
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Path to the saved configuration file
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
OSError: If there are permission issues or the platform is unsupported
|
|
83
|
+
"""
|
|
84
|
+
system = platform.system()
|
|
85
|
+
|
|
86
|
+
if system == "Darwin": # macOS
|
|
87
|
+
config_dir = Path.home() / "Library" / "Application Support" / "Claude"
|
|
88
|
+
elif system == "Windows":
|
|
89
|
+
appdata = os.environ.get("APPDATA")
|
|
90
|
+
if not appdata:
|
|
91
|
+
raise OSError("APPDATA environment variable not set")
|
|
92
|
+
config_dir = Path(appdata) / "Claude"
|
|
93
|
+
elif system == "Linux":
|
|
94
|
+
config_dir = Path.home() / ".config" / "Claude"
|
|
95
|
+
else:
|
|
96
|
+
raise OSError(f"Unsupported platform: {system}")
|
|
97
|
+
|
|
98
|
+
# Create directory if it doesn't exist
|
|
99
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
config_file = config_dir / "claude_desktop_config.json"
|
|
102
|
+
|
|
103
|
+
# Write config with pretty formatting
|
|
104
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
105
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
106
|
+
f.write("\n") # Add trailing newline
|
|
107
|
+
|
|
108
|
+
return config_file
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def add_qbraid_mcp_server(overwrite: bool = False) -> tuple[bool, str]:
|
|
112
|
+
"""
|
|
113
|
+
Add qBraid MCP server configuration to Claude Desktop config.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
overwrite: If True, overwrite existing qbraid entry
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Tuple of (success: bool, message: str)
|
|
120
|
+
"""
|
|
121
|
+
# Get the full path to qbraid executable
|
|
122
|
+
qbraid_path = shutil.which("qbraid")
|
|
123
|
+
if not qbraid_path:
|
|
124
|
+
return (
|
|
125
|
+
False,
|
|
126
|
+
"Could not find qbraid executable in PATH. "
|
|
127
|
+
"Please ensure qbraid-cli is installed and accessible.",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
config = load_claude_config()
|
|
132
|
+
except json.JSONDecodeError as e:
|
|
133
|
+
return False, f"Invalid JSON in Claude config file: {e}"
|
|
134
|
+
except OSError as e:
|
|
135
|
+
return False, f"Error reading Claude config file: {e}"
|
|
136
|
+
|
|
137
|
+
# Initialize mcpServers section if it doesn't exist
|
|
138
|
+
if "mcpServers" not in config:
|
|
139
|
+
config["mcpServers"] = {}
|
|
140
|
+
|
|
141
|
+
# Determine server name
|
|
142
|
+
server_name = "qbraid"
|
|
143
|
+
|
|
144
|
+
# Check if server already exists
|
|
145
|
+
if server_name in config["mcpServers"] and not overwrite:
|
|
146
|
+
return (
|
|
147
|
+
False,
|
|
148
|
+
f"MCP server '{server_name}' already exists in config. "
|
|
149
|
+
"Use --overwrite to replace it.",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Build server configuration with full path to qbraid
|
|
153
|
+
server_config = {"command": qbraid_path, "args": ["mcp", "serve"]}
|
|
154
|
+
|
|
155
|
+
# Add server to config
|
|
156
|
+
config["mcpServers"][server_name] = server_config
|
|
157
|
+
|
|
158
|
+
# Save config
|
|
159
|
+
try:
|
|
160
|
+
config_path = save_claude_config(config)
|
|
161
|
+
action = "Updated" if server_name in config["mcpServers"] else "Added"
|
|
162
|
+
return True, f"{action} MCP server '{server_name}' in [cyan]{config_path}[/cyan]"
|
|
163
|
+
except OSError as e:
|
|
164
|
+
return False, f"Error saving Claude config file: {e}"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def remove_qbraid_mcp_server() -> tuple[bool, str]:
|
|
168
|
+
"""
|
|
169
|
+
Remove qBraid MCP server configuration from Claude Desktop config.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Tuple of (success: bool, message: str)
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
config = load_claude_config()
|
|
176
|
+
except json.JSONDecodeError as e:
|
|
177
|
+
return False, f"Invalid JSON in Claude config file: {e}"
|
|
178
|
+
except OSError as e:
|
|
179
|
+
return False, f"Error reading Claude config file: {e}"
|
|
180
|
+
|
|
181
|
+
if "mcpServers" not in config:
|
|
182
|
+
return False, "No mcpServers section found in Claude config"
|
|
183
|
+
|
|
184
|
+
server_name = "qbraid"
|
|
185
|
+
|
|
186
|
+
if server_name not in config["mcpServers"]:
|
|
187
|
+
return False, f"MCP server '{server_name}' not found in config"
|
|
188
|
+
|
|
189
|
+
# Remove server
|
|
190
|
+
del config["mcpServers"][server_name]
|
|
191
|
+
|
|
192
|
+
# Save config
|
|
193
|
+
try:
|
|
194
|
+
config_path = save_claude_config(config)
|
|
195
|
+
return True, f"Removed MCP server '{server_name}' from [cyan]{config_path}[/cyan]"
|
|
196
|
+
except OSError as e:
|
|
197
|
+
return False, f"Error saving Claude config file: {e}"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_qbraid_mcp_server_config() -> Optional[dict]:
|
|
201
|
+
"""
|
|
202
|
+
Get qBraid MCP server configuration from Claude Desktop config.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Server configuration dict if found, None otherwise
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
config = load_claude_config()
|
|
209
|
+
except (json.JSONDecodeError, OSError):
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
if "mcpServers" not in config:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
return config["mcpServers"].get("qbraid")
|
qbraid_cli/devices/app.py
CHANGED
|
@@ -12,9 +12,9 @@ import typer
|
|
|
12
12
|
from rich.console import Console
|
|
13
13
|
|
|
14
14
|
from qbraid_cli.devices.validation import validate_provider, validate_status, validate_type
|
|
15
|
-
from qbraid_cli.handlers import run_progress_task
|
|
15
|
+
from qbraid_cli.handlers import print_formatted_data, run_progress_task
|
|
16
16
|
|
|
17
|
-
devices_app = typer.Typer(help="Manage qBraid quantum devices.")
|
|
17
|
+
devices_app = typer.Typer(help="Manage qBraid quantum devices.", no_args_is_help=True)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@devices_app.command(name="list")
|
|
@@ -29,7 +29,10 @@ def devices_list( # pylint: disable=too-many-branches
|
|
|
29
29
|
None,
|
|
30
30
|
"--provider",
|
|
31
31
|
"-p",
|
|
32
|
-
help=
|
|
32
|
+
help=(
|
|
33
|
+
"'AWS'|'IBM'|'IonQ'|'Rigetti'|'OQC'|'QuEra'|'IQM'|"
|
|
34
|
+
"'NEC'|'qBraid'|'Azure'|'Pasqal'|'Quantinuum'|'Equal1'"
|
|
35
|
+
),
|
|
33
36
|
callback=validate_provider,
|
|
34
37
|
),
|
|
35
38
|
) -> None:
|
|
@@ -60,7 +63,7 @@ def devices_list( # pylint: disable=too-many-branches
|
|
|
60
63
|
header_3 = "ID"
|
|
61
64
|
header_4 = "Status"
|
|
62
65
|
console.print(
|
|
63
|
-
f"
|
|
66
|
+
f"[bold]{header_1.ljust(12)}{header_2.ljust(35)}{header_3.ljust(41)}{header_4}[/bold]"
|
|
64
67
|
)
|
|
65
68
|
for device_provider, device_name, device_id, device_status in device_data:
|
|
66
69
|
if device_status == "ONLINE":
|
|
@@ -76,5 +79,25 @@ def devices_list( # pylint: disable=too-many-branches
|
|
|
76
79
|
console.print(f"\n{msg}", style="italic", justify="left")
|
|
77
80
|
|
|
78
81
|
|
|
82
|
+
@devices_app.command(name="get")
|
|
83
|
+
def devices_get(
|
|
84
|
+
device_id: str = typer.Argument(..., help="The ID of the device to get."),
|
|
85
|
+
fmt: bool = typer.Option(
|
|
86
|
+
True, "--no-fmt", help="Disable rich console formatting (output raw data)"
|
|
87
|
+
),
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Get a qBraid quantum device."""
|
|
90
|
+
|
|
91
|
+
def get_device():
|
|
92
|
+
from qbraid_core.services.quantum import QuantumClient
|
|
93
|
+
|
|
94
|
+
client = QuantumClient()
|
|
95
|
+
return client.get_device(device_id)
|
|
96
|
+
|
|
97
|
+
data: dict[str, Any] = run_progress_task(get_device)
|
|
98
|
+
|
|
99
|
+
print_formatted_data(data, fmt)
|
|
100
|
+
|
|
101
|
+
|
|
79
102
|
if __name__ == "__main__":
|
|
80
103
|
devices_app()
|
qbraid_cli/envs/activate.py
CHANGED
|
@@ -14,11 +14,15 @@ import typer
|
|
|
14
14
|
|
|
15
15
|
def find_shell_rc(shell_path: str) -> str:
|
|
16
16
|
"""Finds an existing shell configuration file in the user's home directory."""
|
|
17
|
-
|
|
17
|
+
shell_name = Path(shell_path).name
|
|
18
|
+
|
|
19
|
+
if "bash" in shell_name:
|
|
20
|
+
possible_files = [".bashrc", ".bash_profile", ".bash_login"]
|
|
21
|
+
elif "zsh" in shell_name:
|
|
22
|
+
possible_files = [".zshrc", ".zshenv", ".zprofile"]
|
|
23
|
+
else:
|
|
18
24
|
raise ValueError(f"Unsupported shell: {shell_path}")
|
|
19
25
|
|
|
20
|
-
possible_files = [".bashrc", ".bash_profile", ".bash_login"]
|
|
21
|
-
|
|
22
26
|
for file_name in possible_files:
|
|
23
27
|
rc_file = Path.home() / file_name
|
|
24
28
|
if rc_file.exists():
|
|
@@ -52,14 +56,40 @@ def activate_pyvenv(venv_path: Path):
|
|
|
52
56
|
if shell_path is None:
|
|
53
57
|
print_activate_command(venv_path)
|
|
54
58
|
return # Return early since we can't proceed without a shell
|
|
59
|
+
|
|
60
|
+
shell_name = Path(shell_path).name
|
|
61
|
+
|
|
55
62
|
try:
|
|
56
63
|
shell_rc = find_shell_rc(shell_path)
|
|
57
64
|
except (FileNotFoundError, ValueError):
|
|
58
65
|
print_activate_command(venv_path)
|
|
59
66
|
return # Return early since no suitable shell rc file was found
|
|
67
|
+
|
|
60
68
|
bin_path = str(venv_path / "bin")
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
activate_script = f"{bin_path}/activate"
|
|
70
|
+
|
|
71
|
+
# Combine shell rc and activate script, then launch new shell
|
|
72
|
+
# This works for both bash and zsh
|
|
73
|
+
combined_script = f"{bin_path}/activate2"
|
|
74
|
+
|
|
75
|
+
# Create combined script with shell rc and activate script
|
|
76
|
+
with open(combined_script, "w") as f:
|
|
77
|
+
f.write(f"# Combined shell rc and activate script\n")
|
|
78
|
+
with open(shell_rc, "r") as rc:
|
|
79
|
+
f.write(rc.read())
|
|
80
|
+
f.write(f"\n# Activate virtual environment\n")
|
|
81
|
+
with open(activate_script, "r") as act:
|
|
82
|
+
f.write(act.read())
|
|
83
|
+
|
|
84
|
+
if "zsh" in shell_name:
|
|
85
|
+
# For zsh, use -d to skip global zshenv/zshrc, then source our combined file
|
|
86
|
+
# Then exec zsh to get an interactive shell
|
|
87
|
+
os.system(
|
|
88
|
+
f"{shell_path} -d -c 'source {combined_script}; exec zsh'"
|
|
89
|
+
)
|
|
90
|
+
elif "bash" in shell_name:
|
|
91
|
+
# For bash, use --rcfile
|
|
92
|
+
os.system(f"{shell_path} --rcfile {combined_script}")
|
|
93
|
+
else:
|
|
94
|
+
# Fallback for other shells
|
|
95
|
+
print_activate_command(venv_path)
|