qbraid-cli 0.16.2__tar.gz → 0.16.3__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.
- {qbraid_cli-0.16.2/qbraid_cli.egg-info → qbraid_cli-0.16.3}/PKG-INFO +1 -1
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/pyproject.toml +1 -1
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/agents/app.py +78 -4
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3/qbraid_cli.egg-info}/PKG-INFO +1 -1
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/LICENSE +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/README.md +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/_build_info.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/_version.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/account/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/account/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/admin/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/admin/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/admin/headers.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/admin/validation.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/agents/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/agents/permission_response.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/ai/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/ai/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/ai/token.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/instances.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/profiles.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/server.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/compute/ssh.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/configure/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/configure/actions.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/configure/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/devices/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/devices/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/devices/validation.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/activate.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/create.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/data_handling.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/groups.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/publish.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/registry.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/envs/share.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/exceptions.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/handlers.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/jobs/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/jobs/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/jobs/groups.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/kernels/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/kernels/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/lab/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/lab/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/main.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/pip/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/pip/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/pip/hooks.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/projects/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/projects/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/py.typed +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/skills/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/skills/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/troubleshoot/__init__.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli/troubleshoot/app.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli.egg-info/SOURCES.txt +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli.egg-info/dependency_links.txt +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli.egg-info/entry_points.txt +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli.egg-info/requires.txt +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/qbraid_cli.egg-info/top_level.txt +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/setup.cfg +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/tests/test_cli_e2e.py +0 -0
- {qbraid_cli-0.16.2 → qbraid_cli-0.16.3}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.3
|
|
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.16.
|
|
7
|
+
version = "0.16.3"
|
|
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" }]
|
|
@@ -89,13 +89,43 @@ def _ssh_cmd(ssh_alias: str, remote_cmd: str, timeout: int = 30) -> str:
|
|
|
89
89
|
text=True,
|
|
90
90
|
check=False,
|
|
91
91
|
timeout=timeout,
|
|
92
|
+
# ssh reads stdin unless told otherwise, so inside a script or a
|
|
93
|
+
# `while read` loop it swallowed the caller's remaining input.
|
|
94
|
+
stdin=_sp.DEVNULL,
|
|
92
95
|
)
|
|
93
96
|
if result.returncode != 0:
|
|
94
|
-
console.print(
|
|
97
|
+
console.print(
|
|
98
|
+
f"[red]Remote command failed: {_remote_failure(result.stdout, result.stderr)}[/red]",
|
|
99
|
+
markup=True,
|
|
100
|
+
)
|
|
95
101
|
raise typer.Exit(code=1)
|
|
96
102
|
return result.stdout
|
|
97
103
|
|
|
98
104
|
|
|
105
|
+
# ssh's own chatter, which says nothing about why the remote command failed.
|
|
106
|
+
_SSH_NOISE = ("Warning: Permanently added", "Connection to ", "Shared connection to ")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _remote_failure(stdout: str, stderr: str) -> str:
|
|
110
|
+
"""Say why a remote qbraid command failed.
|
|
111
|
+
|
|
112
|
+
The remote CLI prints its errors through Rich on *stdout*; stderr usually
|
|
113
|
+
holds only ssh's known-hosts warning. Reporting stderr alone turned
|
|
114
|
+
"Session not found" into "Warning: Permanently added ... to the list of
|
|
115
|
+
known hosts", which names the one thing that went right.
|
|
116
|
+
"""
|
|
117
|
+
from rich.markup import escape
|
|
118
|
+
|
|
119
|
+
def useful(text: str) -> list[str]:
|
|
120
|
+
# The remote's Rich error panel arrives as box-drawing borders around
|
|
121
|
+
# the message; keep the message, drop the frame.
|
|
122
|
+
lines = [line.strip().strip("│╭╮╰╯─ ") for line in (text or "").splitlines()]
|
|
123
|
+
return [line for line in lines if line and not line.startswith(_SSH_NOISE)]
|
|
124
|
+
|
|
125
|
+
lines = useful(stdout)[-3:] or useful(stderr)[-3:]
|
|
126
|
+
return escape(" / ".join(lines)) if lines else "no output from the remote command"
|
|
127
|
+
|
|
128
|
+
|
|
99
129
|
def _status_style(status_value: str) -> str:
|
|
100
130
|
"""Return a rich style string for a given agent status."""
|
|
101
131
|
styles = {
|
|
@@ -339,6 +369,7 @@ def agents_list(
|
|
|
339
369
|
text=True,
|
|
340
370
|
check=False,
|
|
341
371
|
timeout=30,
|
|
372
|
+
stdin=subprocess.DEVNULL,
|
|
342
373
|
)
|
|
343
374
|
if result.returncode == 0:
|
|
344
375
|
if json_output:
|
|
@@ -361,10 +392,14 @@ def agents_list(
|
|
|
361
392
|
# branch; the --json path above already bypasses Rich.
|
|
362
393
|
console.print(result.stdout, markup=False)
|
|
363
394
|
else:
|
|
395
|
+
reason = _remote_failure(result.stdout, result.stderr)
|
|
364
396
|
if json_output:
|
|
365
|
-
typer.echo(f"Failed: {
|
|
366
|
-
|
|
367
|
-
|
|
397
|
+
typer.echo(f"Failed: {reason}", err=True)
|
|
398
|
+
else:
|
|
399
|
+
console.print(f"[red]Failed: {reason}[/red]")
|
|
400
|
+
# Both branches fail. The human-readable one used to print
|
|
401
|
+
# "Failed" and exit 0, so a script could not tell.
|
|
402
|
+
raise typer.Exit(code=1)
|
|
368
403
|
return
|
|
369
404
|
|
|
370
405
|
registry = _get_registry()
|
|
@@ -1868,6 +1903,44 @@ def agents_read(
|
|
|
1868
1903
|
# =============================================================================
|
|
1869
1904
|
|
|
1870
1905
|
|
|
1906
|
+
@agents_app.command("notifier")
|
|
1907
|
+
def agents_notifier(
|
|
1908
|
+
interval: float = typer.Option(4.0, "--interval", "-i", min=1.0, help="Seconds between checks"),
|
|
1909
|
+
once: bool = typer.Option(False, "--once", help="Check once, print who was nudged, and exit"),
|
|
1910
|
+
):
|
|
1911
|
+
"""Wake idle agents when inbox mail arrives.
|
|
1912
|
+
|
|
1913
|
+
Inbox delivery is passive: an agent parked at its prompt never notices a
|
|
1914
|
+
message. This watches the inboxes of this machine's managed agents and
|
|
1915
|
+
types a one-line nudge into an idle agent's pane. Busy agents are left
|
|
1916
|
+
alone until they go idle, and a burst of messages is one nudge.
|
|
1917
|
+
|
|
1918
|
+
qBraid Lab runs this for you. Use it on a machine reached only over SSH.
|
|
1919
|
+
Same-host: it watches the inboxes on the machine it runs on.
|
|
1920
|
+
"""
|
|
1921
|
+
try:
|
|
1922
|
+
try:
|
|
1923
|
+
from qbraid_core.services.agents.inbox_watcher import InboxWatcher, watch
|
|
1924
|
+
except ImportError:
|
|
1925
|
+
console.print("[red]This needs a newer qbraid-core.[/red] Run: pip install -U qbraid-core")
|
|
1926
|
+
raise typer.Exit(code=1) # pylint: disable=raise-missing-from
|
|
1927
|
+
|
|
1928
|
+
watcher = InboxWatcher()
|
|
1929
|
+
if once:
|
|
1930
|
+
nudged = watcher.poll_once()
|
|
1931
|
+
console.print(f"Nudged: {', '.join(nudged) if nudged else 'nobody'}", markup=False)
|
|
1932
|
+
return
|
|
1933
|
+
console.print(f"[dim]Watching inboxes every {interval:g}s... (Ctrl+C to stop)[/dim]")
|
|
1934
|
+
watch(interval=interval, watcher=watcher)
|
|
1935
|
+
except KeyboardInterrupt:
|
|
1936
|
+
console.print("[dim]Stopped.[/dim]")
|
|
1937
|
+
except (typer.Exit, SystemExit):
|
|
1938
|
+
raise
|
|
1939
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
1940
|
+
handle_error(message=str(e))
|
|
1941
|
+
raise typer.Exit(code=1)
|
|
1942
|
+
|
|
1943
|
+
|
|
1871
1944
|
@agents_app.command("approve")
|
|
1872
1945
|
def agents_approve(
|
|
1873
1946
|
session_id: str = typer.Argument(..., help="Session ID (prefix match)"),
|
|
@@ -2014,6 +2087,7 @@ def agents_watch(
|
|
|
2014
2087
|
["ssh", "-o", "ConnectTimeout=10", compute, remote_flags],
|
|
2015
2088
|
stdout=_sp.PIPE,
|
|
2016
2089
|
stderr=_sp.PIPE,
|
|
2090
|
+
stdin=_sp.DEVNULL,
|
|
2017
2091
|
text=True,
|
|
2018
2092
|
)
|
|
2019
2093
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.3
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|