qbraid-cli 0.8.0.dev0__py3-none-any.whl → 0.8.0.dev3__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.

Potentially problematic release.


This version of qbraid-cli might be problematic. Click here for more details.

qbraid_cli/jobs/app.py CHANGED
@@ -1,118 +1,65 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
5
  Module defining commands in the 'qbraid jobs' namespace.
3
6
 
4
7
  """
5
8
 
6
- from typing import Callable, Dict, Optional, Tuple
9
+ import sys
10
+ from typing import Any, Callable, Dict, Tuple
7
11
 
8
12
  import typer
9
13
  from rich.console import Console
10
14
 
11
- from qbraid_cli.handlers import handle_error, run_progress_task, validate_item
12
-
13
- app = typer.Typer(help="Manage qBraid quantum jobs.")
14
-
15
- QJOB_LIBS = ["braket"]
16
-
17
-
18
- def validate_library(value: str) -> str:
19
- """Validate quantum jobs library."""
20
- return validate_item(value, QJOB_LIBS, "Library")
21
-
22
-
23
- def get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
24
- """Get the state of qBraid Quantum Jobs for the specified library."""
25
- from qbraid.api.system import qbraid_jobs_state
26
-
27
- state_values = {}
28
-
29
- if library:
30
- libraries_to_check = [library]
31
- else:
32
- libraries_to_check = QJOB_LIBS
33
-
34
- for lib in libraries_to_check:
35
- state_values[lib] = qbraid_jobs_state(lib)
36
-
37
- return state_values
38
-
39
-
40
- def run_progress_get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
41
- """Run get state function with rich progress UI."""
42
- return run_progress_task(
43
- get_state,
44
- library,
45
- description="Collecting package metadata...",
46
- error_message=f"Failed to collect {library} package metadata.",
47
- )
48
-
49
-
50
- def handle_jobs_state(
51
- library: str,
52
- action: str, # 'enable' or 'disable'
53
- action_callback: Callable[[], None],
54
- ) -> None:
55
- """Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
56
- state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
57
- installed, enabled = state_values[library]
58
-
59
- if not installed:
60
- handle_error(message=f"{library} not installed.")
61
- if (enabled and action == "enable") or (not enabled and action == "disable"):
62
- action_color = "green" if enabled else "red"
63
- console = Console()
64
- console.print(
65
- f"\nqBraid quantum jobs already [bold {action_color}]{action}d[/bold {action_color}] "
66
- f"for [magenta]{library}[/magenta]."
67
- )
68
- console.print(
69
- "To check the state of all quantum jobs libraries in this environment, "
70
- "use: `[bold]qbraid jobs state[/bold]`"
71
- )
72
- raise typer.Exit()
15
+ from qbraid_cli.handlers import handle_error, run_progress_task
16
+ from qbraid_cli.jobs.toggle_braket import disable_braket, enable_braket
17
+ from qbraid_cli.jobs.validation import handle_jobs_state, run_progress_get_state, validate_library
73
18
 
74
- action_callback() # Perform the specific enable/disable action
19
+ jobs_app = typer.Typer(help="Manage qBraid quantum jobs.")
75
20
 
76
21
 
77
- @app.command(name="enable")
22
+ @jobs_app.command(name="enable")
78
23
  def jobs_enable(
79
24
  library: str = typer.Argument(
80
25
  ..., help="Software library with quantum jobs support.", callback=validate_library
81
- )
26
+ ),
27
+ auto_confirm: bool = typer.Option(
28
+ False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
29
+ ),
82
30
  ) -> None:
83
31
  """Enable qBraid Quantum Jobs."""
84
32
 
85
33
  def enable_action():
86
34
  if library == "braket":
87
- from .toggle_braket import enable_braket
88
-
89
- enable_braket()
35
+ enable_braket(auto_confirm=auto_confirm)
90
36
  else:
91
37
  raise RuntimeError(f"Unsupported device library: '{library}'.")
92
38
 
93
39
  handle_jobs_state(library, "enable", enable_action)
94
40
 
95
41
 
96
- @app.command(name="disable")
42
+ @jobs_app.command(name="disable")
97
43
  def jobs_disable(
98
44
  library: str = typer.Argument(
99
45
  ..., help="Software library with quantum jobs support.", callback=validate_library
100
- )
46
+ ),
47
+ auto_confirm: bool = typer.Option(
48
+ False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
49
+ ),
101
50
  ) -> None:
102
51
  """Disable qBraid Quantum Jobs."""
103
52
 
104
53
  def disable_action():
105
54
  if library == "braket":
106
- from .toggle_braket import disable_braket
107
-
108
- disable_braket()
55
+ disable_braket(auto_confirm=auto_confirm)
109
56
  else:
110
57
  raise RuntimeError(f"Unsupported device library: '{library}'.")
111
58
 
112
59
  handle_jobs_state(library, "disable", disable_action)
113
60
 
114
61
 
115
- @app.command(name="state")
62
+ @jobs_app.command(name="state")
116
63
  def jobs_state(
117
64
  library: str = typer.Argument(
118
65
  default=None,
@@ -122,23 +69,26 @@ def jobs_state(
122
69
  ) -> None:
123
70
  """Display the state of qBraid Quantum Jobs for the current environment."""
124
71
  state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
72
+ state_values = dict(sorted(state_values.items()))
125
73
 
126
74
  console = Console()
127
- console.print("\nLibrary State", style="bold")
75
+ header_1, header_2 = "Library", "State"
76
+ max_lib_length = max((len(lib) for lib in state_values.keys()), default=len(header_1))
77
+ padding = max_lib_length + 9
128
78
 
129
- for lib, (installed, enabled) in state_values.items():
130
- if installed:
131
- if enabled:
132
- console.print(f"{lib:<12} ", "[green]enabled", end="")
133
- else:
134
- console.print(f"{lib:<12} ", "[red]disabled", end="")
135
- else:
136
- console.print(f"{lib:<12} n/a", end="")
79
+ console.print(f"Executable: {sys.executable}")
80
+ console.print(f"\n{header_1:<{padding}}{header_2}", style="bold")
137
81
 
138
- console.print("\n")
82
+ for lib, (installed, enabled) in state_values.items():
83
+ state_str = (
84
+ "[green]enabled"
85
+ if enabled and installed
86
+ else "[red]disabled" if installed else "[grey70]unavailable"
87
+ )
88
+ console.print(f"{lib:<{padding-1}}", state_str, end="\n")
139
89
 
140
90
 
141
- @app.command(name="list")
91
+ @jobs_app.command(name="list")
142
92
  def jobs_list(
143
93
  limit: int = typer.Option(
144
94
  10, "--limit", "-l", help="Limit the maximum number of results returned"
@@ -146,20 +96,55 @@ def jobs_list(
146
96
  ) -> None:
147
97
  """List qBraid Quantum Jobs."""
148
98
 
149
- def import_jobs() -> Tuple[Callable, Exception]:
150
- from qbraid import get_jobs
151
- from qbraid.exceptions import QbraidError
99
+ def import_jobs() -> Tuple[Any, Callable]:
100
+ from qbraid_core.services.quantum import QuantumClient, process_job_data
101
+
102
+ client = QuantumClient()
152
103
 
153
- return get_jobs, QbraidError
104
+ return client, process_job_data
154
105
 
155
- result: Tuple[Callable, Exception] = run_progress_task(import_jobs)
156
- get_jobs, QbraidError = result
106
+ result: Tuple[Any, Callable] = run_progress_task(import_jobs)
107
+ client, process_job_data = result
108
+ # https://github.com/qBraid/api/issues/644
109
+ # raw_data = client.search_jobs(query={"numResults": limit})
110
+ raw_data = client.search_jobs(query={})
111
+ job_data, msg = process_job_data(raw_data)
112
+ job_data = job_data[:limit]
157
113
 
114
+ longest_job_id = max(len(item[0]) for item in job_data)
115
+ spacing = longest_job_id + 5
158
116
  try:
159
- get_jobs(filters={"numResults": limit})
160
- except QbraidError:
117
+ console = Console()
118
+ header_1 = "Job ID"
119
+ header_2 = "Submitted"
120
+ header_3 = "Status"
121
+ console.print(f"\n[bold]{header_1.ljust(spacing)}{header_2.ljust(36)}{header_3}[/bold]")
122
+ for job_id, submitted, status in job_data:
123
+ if status == "COMPLETED":
124
+ status_color = "green"
125
+ elif status in ["FAILED", "CANCELLED"]:
126
+ status_color = "red"
127
+ elif status in [
128
+ "INITIALIZING",
129
+ "INITIALIZED",
130
+ "CREATED",
131
+ "QUEUED",
132
+ "VALIDATING",
133
+ "RUNNING",
134
+ ]:
135
+ status_color = "blue"
136
+ else:
137
+ status_color = "grey"
138
+ console.print(
139
+ f"{job_id.ljust(spacing)}{submitted.ljust(35)}",
140
+ f"[{status_color}]{status}[/{status_color}]",
141
+ )
142
+
143
+ console.print(f"\n{msg}", style="italic", justify="left")
144
+
145
+ except Exception: # pylint: disable=broad-exception-caught
161
146
  handle_error(message="Failed to fetch quantum jobs.")
162
147
 
163
148
 
164
149
  if __name__ == "__main__":
165
- app()
150
+ jobs_app()
@@ -1,3 +1,6 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
5
  Module supporting 'qbraid jobs enable/disable braket' and commands.
3
6
 
@@ -11,6 +14,12 @@ from pathlib import Path
11
14
  from typing import Optional, Tuple
12
15
 
13
16
  import typer
17
+ from qbraid_core.system import (
18
+ QbraidSystemError,
19
+ get_active_site_packages_path,
20
+ get_latest_package_version,
21
+ get_local_package_version,
22
+ )
14
23
 
15
24
  from qbraid_cli.exceptions import QbraidException
16
25
  from qbraid_cli.handlers import handle_error, handle_filesystem_operation, run_progress_task
@@ -33,22 +42,16 @@ def get_package_data(package: str) -> Tuple[str, str, str]:
33
42
  QbraidException: If package version or location data cannot be retrieved.
34
43
 
35
44
  """
36
- from qbraid.api.system import (
37
- get_active_site_packages_path,
38
- get_latest_package_version,
39
- get_local_package_version,
40
- )
41
- from qbraid.exceptions import QbraidError
42
45
 
43
46
  try:
44
47
  installed_version = get_local_package_version(package)
45
48
  latest_version = get_latest_package_version(package)
46
- except QbraidError as err:
49
+ except QbraidSystemError as err:
47
50
  raise QbraidException("Failed to retrieve package version information") from err
48
51
 
49
52
  try:
50
53
  site_packages_path = get_active_site_packages_path()
51
- except QbraidError as err:
54
+ except QbraidSystemError as err:
52
55
  raise QbraidException("Failed to retrieve site-package location") from err
53
56
 
54
57
  return installed_version, latest_version, site_packages_path
@@ -70,9 +73,6 @@ def confirm_updates(
70
73
  installed_version (optional, str): The installed version of the target package.
71
74
  latest_version (optional, str): The latest version of the target package available on PyPI.
72
75
 
73
- Returns:
74
- None
75
-
76
76
  Raises:
77
77
  ValueError: If an invalid mode is provided.
78
78
  typer.Exit: If the user declines to proceed with enabling or disabling qBraid Quantum Jobs.
@@ -154,14 +154,16 @@ def aws_configure_dummy() -> None:
154
154
  handle_error(message="Failed to configure qBraid quantum jobs.")
155
155
 
156
156
 
157
- def enable_braket():
157
+ def enable_braket(auto_confirm: bool = False):
158
158
  """Enable qBraid quantum jobs for Amazon Braket."""
159
- installed_version, latest_version, site_packages_path = run_progress_task(
159
+ installed, latest, path = run_progress_task(
160
160
  get_package_data, "boto3", description="Solving environment..."
161
161
  )
162
- confirm_updates("enable", site_packages_path, installed_version, latest_version)
163
- aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
164
162
 
163
+ if not auto_confirm:
164
+ confirm_updates("enable", path, installed_version=installed, latest_version=latest)
165
+
166
+ aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
165
167
  try:
166
168
  subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "boto3"])
167
169
  subprocess.check_call(
@@ -174,19 +176,19 @@ def enable_braket():
174
176
  handle_error(message="Failed to enable qBraid quantum jobs.")
175
177
 
176
178
  typer.secho("\nSuccessfully enabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
177
- typer.secho(
178
- "\nTo disable, run: `qbraid jobs disable braket`\n", fg=typer.colors.GREEN, bold=True
179
- )
179
+ typer.secho("\nTo disable, run: \n\n\t$ qbraid jobs disable braket\n")
180
180
 
181
181
 
182
- def disable_braket():
182
+ def disable_braket(auto_confirm: bool = False):
183
183
  """Disable qBraid quantum jobs for Amazon Braket."""
184
184
  package = "botocore"
185
- installed_version, latest_version, site_packages_path = run_progress_task(
185
+ installed, latest, path = run_progress_task(
186
186
  get_package_data, package, description="Solving environment..."
187
187
  )
188
- package = f"{package}~={installed_version}" if installed_version < latest_version else package
189
- confirm_updates("disable", site_packages_path)
188
+ package = f"{package}~={installed}" if installed < latest else package
189
+
190
+ if not auto_confirm:
191
+ confirm_updates("disable", path)
190
192
 
191
193
  try:
192
194
  subprocess.check_call(
@@ -204,4 +206,4 @@ def disable_braket():
204
206
  handle_error(message="Failed to disable qBraid quantum jobs.")
205
207
 
206
208
  typer.secho("\nSuccessfully disabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
207
- typer.secho("\nTo enable, run: `qbraid jobs enable braket`\n", fg=typer.colors.GREEN, bold=True)
209
+ typer.secho("\nTo enable, run: \n\n\t$ qbraid jobs enable braket\n")
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module for validating command arguments for qBraid Quantum Jobs.
6
+
7
+ """
8
+
9
+ from typing import Callable, Dict, Optional, Tuple
10
+
11
+ import typer
12
+ from qbraid_core.services.quantum.proxy import SUPPORTED_QJOB_LIBS, quantum_lib_proxy_state
13
+ from rich.console import Console
14
+
15
+ from qbraid_cli.handlers import handle_error, run_progress_task, validate_item
16
+
17
+
18
+ def validate_library(value: str) -> str:
19
+ """Validate quantum jobs library."""
20
+ return validate_item(value, SUPPORTED_QJOB_LIBS, "Library")
21
+
22
+
23
+ def get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
24
+ """Get the state of qBraid Quantum Jobs for the specified library."""
25
+
26
+ state_values = {}
27
+
28
+ if library:
29
+ libraries_to_check = [library]
30
+ else:
31
+ libraries_to_check = SUPPORTED_QJOB_LIBS
32
+
33
+ for lib in libraries_to_check:
34
+ state = quantum_lib_proxy_state(lib)
35
+ state_values[lib] = state["supported"], state["enabled"]
36
+
37
+ return state_values
38
+
39
+
40
+ def run_progress_get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
41
+ """Run get state function with rich progress UI."""
42
+ return run_progress_task(
43
+ get_state,
44
+ library,
45
+ description="Collecting package metadata...",
46
+ error_message=f"Failed to collect {library} package metadata.",
47
+ )
48
+
49
+
50
+ def handle_jobs_state(
51
+ library: str,
52
+ action: str, # 'enable' or 'disable'
53
+ action_callback: Callable[[], None],
54
+ ) -> None:
55
+ """Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
56
+ state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
57
+ installed, enabled = state_values[library]
58
+
59
+ if not installed:
60
+ handle_error(message=f"{library} not installed.")
61
+ if (enabled and action == "enable") or (not enabled and action == "disable"):
62
+ action_color = "green" if enabled else "red"
63
+ console = Console()
64
+ console.print(
65
+ f"\nqBraid quantum jobs already [bold {action_color}]{action}d[/bold {action_color}] "
66
+ f"for [magenta]{library}[/magenta]."
67
+ )
68
+ console.print(
69
+ "To check the state of all quantum jobs libraries in this environment, "
70
+ "use: \n\n\t$ qbraid jobs state\n"
71
+ )
72
+ raise typer.Exit()
73
+
74
+ action_callback() # Perform the specific enable/disable action
@@ -1,6 +1,9 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
5
  Module defining the qbraid kernels namespace
3
6
 
4
7
  """
5
8
 
6
- from .app import app
9
+ from .app import kernels_app
qbraid_cli/kernels/app.py CHANGED
@@ -1,29 +1,111 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
5
  Module defining commands in the 'qbraid jobs' namespace.
3
6
 
4
7
  """
5
-
6
- import subprocess
8
+ import sys
9
+ from pathlib import Path
7
10
 
8
11
  import typer
12
+ from jupyter_client.kernelspec import KernelSpecManager
13
+ from rich.console import Console
9
14
 
15
+ from qbraid_cli.envs.data_handling import installed_envs_data
10
16
  from qbraid_cli.handlers import handle_error
11
17
 
12
- app = typer.Typer(help="Manage qBraid kernels.")
18
+ kernels_app = typer.Typer(help="Manage qBraid kernels.")
19
+
20
+
21
+ def _get_kernels_path(environment: str) -> Path:
22
+ """Get the path to the kernels directory for the given environment."""
23
+ slug_to_path, name_to_slug = installed_envs_data()
24
+
25
+ if environment in name_to_slug:
26
+ slug = name_to_slug.get(environment, None)
27
+ else:
28
+ slug = environment
29
+
30
+ if slug not in slug_to_path:
31
+ raise ValueError(f"Environment '{environment}' not found.")
13
32
 
33
+ env_path = slug_to_path[slug]
34
+ kernels_path = env_path / "kernels"
35
+ return kernels_path
14
36
 
15
- @app.command(name="list")
37
+
38
+ @kernels_app.command(name="list")
16
39
  def kernels_list():
17
40
  """List all available kernels."""
41
+ console = Console()
42
+
43
+ kernel_spec_manager = KernelSpecManager()
44
+ kernelspecs = kernel_spec_manager.get_all_specs()
45
+
46
+ if len(kernelspecs) == 0:
47
+ console.print("No qBraid kernels are active.")
48
+ console.print("\nUse 'qbraid kernels add' to add a new kernel.")
49
+ return
50
+
51
+ longest_kernel_name = max(len(kernel_name) for kernel_name in kernelspecs)
52
+ spacing = longest_kernel_name + 10
53
+
54
+ console.print("# qbraid kernels:\n#\n")
55
+
56
+ # Ensure 'python3' kernel is printed first if it exists
57
+ default_kernel_name = "python3"
58
+ python3_kernel_info = kernelspecs.pop(default_kernel_name, None)
59
+ if python3_kernel_info:
60
+ console.print(f"{default_kernel_name.ljust(spacing)}{python3_kernel_info['resource_dir']}")
61
+
62
+ # Print the rest of the kernels
63
+ for kernel_name, kernel_info in sorted(kernelspecs.items()):
64
+ console.print(f"{kernel_name.ljust(spacing)}{kernel_info['resource_dir']}")
65
+
66
+
67
+ @kernels_app.command(name="add")
68
+ def kernels_add(
69
+ environment: str = typer.Argument(
70
+ ..., help="Name of environment for which to add ipykernel. Values from 'qbraid envs list'."
71
+ )
72
+ ):
73
+ """Add a kernel."""
74
+
18
75
  try:
19
- result = subprocess.run(
20
- ["jupyter", "kernelspec", "list"], capture_output=True, text=True, check=True
21
- )
76
+ kernels_path = _get_kernels_path(environment)
77
+ except ValueError:
78
+ handle_error(message=f"Environment '{environment}' not found.", include_traceback=False)
79
+ return
80
+
81
+ is_local = str(kernels_path).startswith(str(Path.home()))
82
+ resource_path = str(Path.home() / ".local") if is_local else sys.prefix
83
+
84
+ kernel_spec_manager = KernelSpecManager()
85
+
86
+ for kernel in kernels_path.iterdir():
87
+ kernel_spec_manager.install_kernel_spec(source_dir=str(kernel), prefix=resource_path)
88
+
89
+
90
+ @kernels_app.command(name="remove")
91
+ def kernels_remove(
92
+ environment: str = typer.Argument(
93
+ ...,
94
+ help=("Name of environment for which to remove ipykernel. Values from 'qbraid envs list'."),
95
+ )
96
+ ):
97
+ """Remove a kernel."""
98
+ try:
99
+ kernels_path = _get_kernels_path(environment)
100
+ except ValueError:
101
+ handle_error(message=f"Environment '{environment}' not found.", include_traceback=False)
102
+ return
103
+
104
+ kernel_spec_manager = KernelSpecManager()
22
105
 
23
- print(result.stdout)
24
- except subprocess.CalledProcessError:
25
- handle_error("Failed to list kernels.")
106
+ for kernel in kernels_path.iterdir():
107
+ kernel_spec_manager.remove_kernel_spec(kernel.name)
26
108
 
27
109
 
28
110
  if __name__ == "__main__":
29
- app()
111
+ kernels_app()
qbraid_cli/main.py CHANGED
@@ -1,18 +1,22 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
5
  Entrypoint for the qBraid CLI.
3
6
 
4
7
  """
5
8
 
6
9
  import typer
10
+ import urllib3
7
11
 
8
- from .configure import app as configure_app
9
- from .credits import app as credits_app
10
- from .devices import app as devices_app
11
- from .envs import app as envs_app
12
- from .jobs import app as jobs_app
13
- from .kernels import app as kernels_app
12
+ from qbraid_cli.configure.app import configure_app
13
+ from qbraid_cli.credits.app import credits_app
14
+ from qbraid_cli.devices.app import devices_app
15
+ from qbraid_cli.envs.app import envs_app
16
+ from qbraid_cli.jobs.app import jobs_app
17
+ from qbraid_cli.kernels.app import kernels_app
14
18
 
15
- app = typer.Typer()
19
+ app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
16
20
  app.add_typer(configure_app, name="configure")
17
21
  app.add_typer(envs_app, name="envs")
18
22
  app.add_typer(jobs_app, name="jobs")
@@ -20,6 +24,8 @@ app.add_typer(devices_app, name="devices")
20
24
  app.add_typer(kernels_app, name="kernels")
21
25
  app.add_typer(credits_app, name="credits")
22
26
 
27
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
28
+
23
29
 
24
30
  def version_callback(value: bool):
25
31
  """Show the version and exit."""
@@ -50,7 +56,7 @@ def show_banner():
50
56
  typer.echo("")
51
57
  typer.echo("- Use 'qbraid --version' to see the current version.")
52
58
  typer.echo("")
53
- typer.echo("Reference Docs: https://docs.qbraid.com/projects/cli/en/latest/cli/qbraid.html")
59
+ typer.echo("Reference Docs: https://docs.qbraid.com/projects/cli/en/stable/guide/overview.html")
54
60
 
55
61
 
56
62
  @app.callback(invoke_without_command=True)
@@ -59,6 +65,7 @@ def main(
59
65
  version: bool = typer.Option(
60
66
  None,
61
67
  "--version",
68
+ "-v",
62
69
  callback=version_callback,
63
70
  is_eager=True,
64
71
  help="Show the version and exit.",