qbraid-cli 0.16.16__tar.gz → 0.16.17__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.16/qbraid_cli.egg-info → qbraid_cli-0.16.17}/PKG-INFO +4 -4
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/pyproject.toml +4 -4
- qbraid_cli-0.16.17/qbraid_cli/artifacts/__init__.py +7 -0
- qbraid_cli-0.16.17/qbraid_cli/artifacts/app.py +245 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/main.py +2 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17/qbraid_cli.egg-info}/PKG-INFO +4 -4
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli.egg-info/SOURCES.txt +2 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli.egg-info/requires.txt +3 -3
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/LICENSE +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/README.md +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/_build_info.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/_version.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/account/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/account/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/admin/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/admin/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/admin/headers.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/admin/validation.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/keep_alive.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/permission_response.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/provider_children.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/queue.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/agents/work.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/ai/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/ai/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/ai/token.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/comms/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/comms/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/instances.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/profiles.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/server.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/compute/ssh.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/configure/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/configure/actions.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/configure/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/devices/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/devices/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/devices/validation.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/activate.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/create.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/data_handling.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/groups.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/publish.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/registry.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/envs/share.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/exceptions.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/handlers.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/jobs/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/jobs/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/jobs/groups.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/kernels/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/kernels/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/lab/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/lab/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/pip/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/pip/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/pip/hooks.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/projects/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/projects/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/py.typed +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/skills/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/skills/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/troubleshoot/__init__.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli/troubleshoot/app.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli.egg-info/dependency_links.txt +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli.egg-info/entry_points.txt +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/qbraid_cli.egg-info/top_level.txt +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/setup.cfg +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/tests/test_cli_e2e.py +0 -0
- {qbraid_cli-0.16.16 → qbraid_cli-0.16.17}/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.17
|
|
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
|
|
@@ -30,11 +30,11 @@ Requires-Dist: typer>=0.12.1
|
|
|
30
30
|
Requires-Dist: rich>=10.11.0
|
|
31
31
|
Requires-Dist: click<=8.5.0
|
|
32
32
|
Requires-Dist: questionary>=2.0.0
|
|
33
|
-
Requires-Dist: qbraid-core>=0.6.
|
|
33
|
+
Requires-Dist: qbraid-core>=0.6.43
|
|
34
34
|
Provides-Extra: compute
|
|
35
|
-
Requires-Dist: qbraid-core[compute]>=0.6.
|
|
35
|
+
Requires-Dist: qbraid-core[compute]>=0.6.43; extra == "compute"
|
|
36
36
|
Provides-Extra: envs
|
|
37
|
-
Requires-Dist: qbraid-core[environments]>=0.6.
|
|
37
|
+
Requires-Dist: qbraid-core[environments]>=0.6.43; extra == "envs"
|
|
38
38
|
Provides-Extra: dev
|
|
39
39
|
Requires-Dist: isort; extra == "dev"
|
|
40
40
|
Requires-Dist: black; extra == "dev"
|
|
@@ -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.17"
|
|
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" }]
|
|
@@ -32,7 +32,7 @@ dependencies = [
|
|
|
32
32
|
"rich>=10.11.0",
|
|
33
33
|
"click<=8.5.0",
|
|
34
34
|
"questionary>=2.0.0",
|
|
35
|
-
"qbraid-core>=0.6.
|
|
35
|
+
"qbraid-core>=0.6.43",
|
|
36
36
|
]
|
|
37
37
|
requires-python = ">= 3.10"
|
|
38
38
|
|
|
@@ -43,8 +43,8 @@ Documentation = "https://docs.qbraid.com/v2/cli/api-reference/qbraid"
|
|
|
43
43
|
Discord = "https://discord.gg/KugF6Cnncm"
|
|
44
44
|
|
|
45
45
|
[project.optional-dependencies]
|
|
46
|
-
compute = ["qbraid-core[compute]>=0.6.
|
|
47
|
-
envs = ["qbraid-core[environments]>=0.6.
|
|
46
|
+
compute = ["qbraid-core[compute]>=0.6.43"]
|
|
47
|
+
envs = ["qbraid-core[environments]>=0.6.43"]
|
|
48
48
|
dev = ["isort", "black", "pytest", "pytest-cov", "pytest-asyncio"]
|
|
49
49
|
|
|
50
50
|
[project.scripts]
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Copyright (c) 2026, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
"""CLI surfaces for hosted HTML artifacts; all operations delegate to Core."""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Callable, Optional, TypeVar
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
from rich.text import Text
|
|
15
|
+
|
|
16
|
+
from qbraid_cli.agents.work import _safe
|
|
17
|
+
from qbraid_cli.handlers import handle_error
|
|
18
|
+
|
|
19
|
+
artifacts_app = typer.Typer(
|
|
20
|
+
help="Publish and share standalone HTML.\n\nLists show only your artifacts.", no_args_is_help=True
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
CORE_MODULE = "qbraid_core.services.artifacts"
|
|
24
|
+
OLD_CORE = "qbraid artifacts requires qbraid-core 0.6.43 or newer; run: pip install -U qbraid-core"
|
|
25
|
+
PUBLIC_NOTE = "Anyone with the link can view this artifact."
|
|
26
|
+
MAX_ERROR = 300
|
|
27
|
+
_ID = re.compile(r"[a-f0-9]{32}")
|
|
28
|
+
_EMAIL = re.compile(r"[^@\s]+@[^@\s]+\.[^@\s]+")
|
|
29
|
+
# An error page (HTML/XML) rather than an API message: show the status instead.
|
|
30
|
+
_MARKUP = re.compile(r"<\s*[!/?a-zA-Z]")
|
|
31
|
+
_Result = TypeVar("_Result")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Access(str, Enum):
|
|
35
|
+
"""Artifact visibility."""
|
|
36
|
+
|
|
37
|
+
PRIVATE = "private"
|
|
38
|
+
SHARED = "shared"
|
|
39
|
+
PUBLIC = "public"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _client():
|
|
43
|
+
from qbraid_core.services.artifacts import ArtifactsClient
|
|
44
|
+
|
|
45
|
+
return ArtifactsClient()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _plain(value) -> str:
|
|
49
|
+
"""API text made terminal-inert: no controls, escapes, bidi or invisible marks."""
|
|
50
|
+
return _safe(value if isinstance(value, str) else str(value)).plain
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _error_text(err: Exception) -> str:
|
|
54
|
+
if isinstance(err, ModuleNotFoundError) and (err.name or "").startswith(CORE_MODULE):
|
|
55
|
+
return OLD_CORE
|
|
56
|
+
if isinstance(err, UnicodeDecodeError):
|
|
57
|
+
return "The file is not valid UTF-8; save it as UTF-8 and try again"
|
|
58
|
+
text = str(err)
|
|
59
|
+
if _MARKUP.search(text):
|
|
60
|
+
status = getattr(err, "status_code", None)
|
|
61
|
+
text = "Artifact request failed" + (f" (HTTP {status})" if status else "")
|
|
62
|
+
return text + "; the API returned a non-JSON error page"
|
|
63
|
+
text = _plain(text)
|
|
64
|
+
return text if len(text) <= MAX_ERROR else text[: MAX_ERROR - 1] + "…"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run(operation: Callable[..., _Result]) -> _Result:
|
|
68
|
+
"""Run one operation with a client that is always closed; failures exit 1."""
|
|
69
|
+
client = None
|
|
70
|
+
try:
|
|
71
|
+
client = _client()
|
|
72
|
+
return operation(client)
|
|
73
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
74
|
+
handle_error(message=_error_text(err), include_traceback=False)
|
|
75
|
+
raise # unreachable: handle_error exits
|
|
76
|
+
finally:
|
|
77
|
+
if client is not None:
|
|
78
|
+
client.close()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _fail(message: str) -> None:
|
|
82
|
+
handle_error(message=message, include_traceback=False)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _check_id(slug: str) -> str:
|
|
86
|
+
if not _ID.fullmatch(slug):
|
|
87
|
+
raise typer.BadParameter("use the 32-character ID shown by 'qbraid artifacts list'")
|
|
88
|
+
return slug
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _check_file(path: Optional[Path]) -> None:
|
|
92
|
+
"""Say precisely why a file is refused before any request; Core re-checks."""
|
|
93
|
+
if path is None:
|
|
94
|
+
return
|
|
95
|
+
if path.is_symlink():
|
|
96
|
+
_fail("Symbolic links are not supported; pass the HTML file itself")
|
|
97
|
+
if path.suffix.lower() not in (".html", ".htm"):
|
|
98
|
+
_fail("Provide one .html or .htm file")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _check_email(email: str) -> str:
|
|
102
|
+
if not _EMAIL.fullmatch(email):
|
|
103
|
+
raise typer.BadParameter("Provide a valid email address")
|
|
104
|
+
return email
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _dump(model) -> str:
|
|
108
|
+
# ASCII-escaped JSON: raw data for scripts, inert in a terminal.
|
|
109
|
+
return json.dumps(model.model_dump(mode="json", by_alias=True))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _emit(artifact, as_json: bool):
|
|
113
|
+
if as_json:
|
|
114
|
+
print(_dump(artifact))
|
|
115
|
+
return
|
|
116
|
+
print(f"{_plain(artifact.title)} ({_plain(artifact.visibility)})\n{_plain(artifact.url)}")
|
|
117
|
+
if artifact.visibility == "public":
|
|
118
|
+
print(PUBLIC_NOTE)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
ID_ARG = typer.Argument(
|
|
122
|
+
..., metavar="ID", callback=_check_id, help="Artifact ID from 'qbraid artifacts list'."
|
|
123
|
+
)
|
|
124
|
+
EMAIL_OPT = typer.Option(..., "--email", callback=_check_email)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@artifacts_app.command("publish")
|
|
128
|
+
def publish(
|
|
129
|
+
file: Path = typer.Argument(..., exists=True, dir_okay=False),
|
|
130
|
+
title: Optional[str] = typer.Option(None, "--title"),
|
|
131
|
+
visibility: Access = typer.Option(Access.PRIVATE, "--visibility"),
|
|
132
|
+
as_json: bool = typer.Option(False, "--json"),
|
|
133
|
+
new: bool = typer.Option(False, "--new", help="Publish a copy even if this file is already published."),
|
|
134
|
+
):
|
|
135
|
+
"""Publish one standalone HTML file.
|
|
136
|
+
|
|
137
|
+
Publishing is immediate; there is no review queue. Publishing the same file
|
|
138
|
+
with the same title and access returns the existing artifact.
|
|
139
|
+
"""
|
|
140
|
+
_check_file(file)
|
|
141
|
+
artifact = _run(
|
|
142
|
+
lambda client: client.publish(file, title=title, visibility=visibility.value, duplicate=new)
|
|
143
|
+
)
|
|
144
|
+
if artifact.created is False and not as_json:
|
|
145
|
+
print("Already published")
|
|
146
|
+
_emit(artifact, as_json)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@artifacts_app.command("list")
|
|
150
|
+
def list_artifacts(
|
|
151
|
+
page: int = typer.Option(1, min=1),
|
|
152
|
+
limit: int = typer.Option(50, min=1, max=100),
|
|
153
|
+
as_json: bool = typer.Option(False, "--json"),
|
|
154
|
+
):
|
|
155
|
+
"""List your published HTML artifacts.
|
|
156
|
+
|
|
157
|
+
This never lists a public or global catalog.
|
|
158
|
+
"""
|
|
159
|
+
result = _run(lambda client: client.list_artifacts(page=page, limit=limit))
|
|
160
|
+
if as_json:
|
|
161
|
+
print(_dump(result))
|
|
162
|
+
return
|
|
163
|
+
# IDs never wrap or truncate so they can be copied; long titles and links fold.
|
|
164
|
+
table = Table()
|
|
165
|
+
table.add_column("ID", no_wrap=True, min_width=32)
|
|
166
|
+
table.add_column("Title", overflow="fold")
|
|
167
|
+
table.add_column("Access", no_wrap=True)
|
|
168
|
+
table.add_column("Link", overflow="fold")
|
|
169
|
+
for item in result.data:
|
|
170
|
+
table.add_row(*(Text(_plain(v)) for v in (item.slug, item.title, item.visibility, item.url)))
|
|
171
|
+
Console().print(table)
|
|
172
|
+
print(f"Page {page}; {result.pagination.total} artifacts total")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@artifacts_app.command("get")
|
|
176
|
+
def get(slug: str = ID_ARG, as_json: bool = typer.Option(False, "--json")):
|
|
177
|
+
"""Show one artifact you can view."""
|
|
178
|
+
_emit(_run(lambda client: client.get_artifact(slug)), as_json)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@artifacts_app.command("update")
|
|
182
|
+
def update(
|
|
183
|
+
slug: str = ID_ARG,
|
|
184
|
+
file: Optional[Path] = typer.Option(None, "--file", exists=True, dir_okay=False),
|
|
185
|
+
title: Optional[str] = typer.Option(None, "--title"),
|
|
186
|
+
visibility: Optional[Access] = typer.Option(None, "--visibility"),
|
|
187
|
+
as_json: bool = typer.Option(False, "--json"),
|
|
188
|
+
):
|
|
189
|
+
"""Update HTML, title, or access.
|
|
190
|
+
|
|
191
|
+
The share link is preserved.
|
|
192
|
+
"""
|
|
193
|
+
_check_file(file)
|
|
194
|
+
artifact = _run(
|
|
195
|
+
lambda client: client.update(
|
|
196
|
+
slug, path=file, title=title, visibility=visibility.value if visibility else None
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
_emit(artifact, as_json)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@artifacts_app.command("share")
|
|
203
|
+
def share(slug: str = ID_ARG, email: str = EMAIL_OPT, as_json: bool = typer.Option(False, "--json")):
|
|
204
|
+
"""Grant a qBraid user viewing access.
|
|
205
|
+
|
|
206
|
+
The recipient must sign in. Send them the link after granting access.
|
|
207
|
+
"""
|
|
208
|
+
_emit(_run(lambda client: client.share(slug, email)), as_json)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@artifacts_app.command("revoke")
|
|
212
|
+
def revoke(slug: str = ID_ARG, email: str = EMAIL_OPT, as_json: bool = typer.Option(False, "--json")):
|
|
213
|
+
"""Remove a collaborator viewing grant.
|
|
214
|
+
|
|
215
|
+
Public artifacts remain accessible via their link.
|
|
216
|
+
"""
|
|
217
|
+
_emit(_run(lambda client: client.revoke(slug, email)), as_json)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@artifacts_app.command("unpublish")
|
|
221
|
+
def unpublish(slug: str = ID_ARG, as_json: bool = typer.Option(False, "--json")):
|
|
222
|
+
"""Make an artifact owner-only.
|
|
223
|
+
|
|
224
|
+
Disables both public and shared viewing.
|
|
225
|
+
"""
|
|
226
|
+
_emit(_run(lambda client: client.update(slug, visibility="private")), as_json)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@artifacts_app.command("delete")
|
|
230
|
+
def delete(
|
|
231
|
+
slug: str = ID_ARG,
|
|
232
|
+
yes: bool = typer.Option(False, "--yes"),
|
|
233
|
+
as_json: bool = typer.Option(False, "--json"),
|
|
234
|
+
):
|
|
235
|
+
"""Delete an artifact and its HTML.
|
|
236
|
+
|
|
237
|
+
Use --yes in non-interactive scripts; --json requires --yes.
|
|
238
|
+
"""
|
|
239
|
+
if as_json and not yes:
|
|
240
|
+
print(json.dumps({"deleted": False, "slug": slug, "error": "--json requires --yes"}))
|
|
241
|
+
raise typer.Exit(code=1)
|
|
242
|
+
if not yes:
|
|
243
|
+
typer.confirm("Delete this artifact and its HTML?", abort=True, err=True)
|
|
244
|
+
_run(lambda client: client.delete(slug))
|
|
245
|
+
print(json.dumps({"deleted": True, "slug": slug}) if as_json else "Artifact deleted")
|
|
@@ -13,6 +13,7 @@ from qbraid_cli.account import account_app
|
|
|
13
13
|
from qbraid_cli.admin import admin_app
|
|
14
14
|
from qbraid_cli.agents import agents_app
|
|
15
15
|
from qbraid_cli.ai.app import ai_app
|
|
16
|
+
from qbraid_cli.artifacts import artifacts_app
|
|
16
17
|
from qbraid_cli.comms import comms_app
|
|
17
18
|
from qbraid_cli.compute import compute_app
|
|
18
19
|
from qbraid_cli.configure import configure_app
|
|
@@ -55,6 +56,7 @@ app.add_typer(devices_app, name="devices")
|
|
|
55
56
|
app.add_typer(jobs_app, name="jobs")
|
|
56
57
|
app.add_typer(projects_app, name="projects")
|
|
57
58
|
app.add_typer(skills_app, name="skills")
|
|
59
|
+
app.add_typer(artifacts_app, name="artifacts")
|
|
58
60
|
app.add_typer(troubleshoot_app, name="troubleshoot")
|
|
59
61
|
|
|
60
62
|
if ENVS_COMMANDS is True:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.17
|
|
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
|
|
@@ -30,11 +30,11 @@ Requires-Dist: typer>=0.12.1
|
|
|
30
30
|
Requires-Dist: rich>=10.11.0
|
|
31
31
|
Requires-Dist: click<=8.5.0
|
|
32
32
|
Requires-Dist: questionary>=2.0.0
|
|
33
|
-
Requires-Dist: qbraid-core>=0.6.
|
|
33
|
+
Requires-Dist: qbraid-core>=0.6.43
|
|
34
34
|
Provides-Extra: compute
|
|
35
|
-
Requires-Dist: qbraid-core[compute]>=0.6.
|
|
35
|
+
Requires-Dist: qbraid-core[compute]>=0.6.43; extra == "compute"
|
|
36
36
|
Provides-Extra: envs
|
|
37
|
-
Requires-Dist: qbraid-core[environments]>=0.6.
|
|
37
|
+
Requires-Dist: qbraid-core[environments]>=0.6.43; extra == "envs"
|
|
38
38
|
Provides-Extra: dev
|
|
39
39
|
Requires-Dist: isort; extra == "dev"
|
|
40
40
|
Requires-Dist: black; extra == "dev"
|
|
@@ -30,6 +30,8 @@ qbraid_cli/agents/work.py
|
|
|
30
30
|
qbraid_cli/ai/__init__.py
|
|
31
31
|
qbraid_cli/ai/app.py
|
|
32
32
|
qbraid_cli/ai/token.py
|
|
33
|
+
qbraid_cli/artifacts/__init__.py
|
|
34
|
+
qbraid_cli/artifacts/app.py
|
|
33
35
|
qbraid_cli/comms/__init__.py
|
|
34
36
|
qbraid_cli/comms/app.py
|
|
35
37
|
qbraid_cli/compute/__init__.py
|
|
@@ -2,10 +2,10 @@ typer>=0.12.1
|
|
|
2
2
|
rich>=10.11.0
|
|
3
3
|
click<=8.5.0
|
|
4
4
|
questionary>=2.0.0
|
|
5
|
-
qbraid-core>=0.6.
|
|
5
|
+
qbraid-core>=0.6.43
|
|
6
6
|
|
|
7
7
|
[compute]
|
|
8
|
-
qbraid-core[compute]>=0.6.
|
|
8
|
+
qbraid-core[compute]>=0.6.43
|
|
9
9
|
|
|
10
10
|
[dev]
|
|
11
11
|
isort
|
|
@@ -15,4 +15,4 @@ pytest-cov
|
|
|
15
15
|
pytest-asyncio
|
|
16
16
|
|
|
17
17
|
[envs]
|
|
18
|
-
qbraid-core[environments]>=0.6.
|
|
18
|
+
qbraid-core[environments]>=0.6.43
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|