apizit-cli 0.2.0b4__tar.gz → 0.3.0__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.
- apizit_cli-0.3.0/PKG-INFO +52 -0
- apizit_cli-0.3.0/README.md +35 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/pyproject.toml +1 -2
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/__init__.py +1 -1
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/commands.py +55 -33
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/context.py +78 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/main.py +17 -4
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/operations.py +171 -49
- apizit_cli-0.3.0/src/apizit_cli/presentation.py +552 -0
- apizit_cli-0.3.0/src/apizit_cli/selection.py +60 -0
- apizit_cli-0.3.0/src/apizit_cli.egg-info/PKG-INFO +52 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli.egg-info/SOURCES.txt +2 -0
- apizit_cli-0.2.0b4/PKG-INFO +0 -54
- apizit_cli-0.2.0b4/README.md +0 -36
- apizit_cli-0.2.0b4/src/apizit_cli.egg-info/PKG-INFO +0 -54
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/setup.cfg +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/authentication.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/configuration.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/credential_store.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/domains.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/errors.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/http.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli/sources.py +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli.egg-info/dependency_links.txt +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli.egg-info/entry_points.txt +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli.egg-info/requires.txt +0 -0
- {apizit_cli-0.2.0b4 → apizit_cli-0.3.0}/src/apizit_cli.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apizit-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Launch supported Python, JavaScript, TypeScript or Java APIs from your terminal or CI.
|
|
5
|
+
Author: APIZIT
|
|
6
|
+
Classifier: Environment :: Console
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: requests<3,>=2.32.0
|
|
13
|
+
Requires-Dist: pydantic<3,>=2.12.5
|
|
14
|
+
Requires-Dist: pyyaml<7,>=6.0.3
|
|
15
|
+
Requires-Dist: keyring<26,>=25.7
|
|
16
|
+
Requires-Dist: pathspec<2,>=1
|
|
17
|
+
|
|
18
|
+
# APIZIT CLI
|
|
19
|
+
|
|
20
|
+
Check a project, launch its analyzed source and get its live API URL from your terminal.
|
|
21
|
+
Managed adapters cover Flask, FastAPI, Django, Express, NestJS and Spring Boot;
|
|
22
|
+
APIZIT Linking provides declarative Python routes. Scan to confirm compatibility.
|
|
23
|
+
|
|
24
|
+
Python 3.12 or later is required. Install with pip (or py -m pip on Windows):
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
python -m pip install apizit-cli
|
|
28
|
+
apizit login
|
|
29
|
+
apizit workspace use
|
|
30
|
+
apizit scan .
|
|
31
|
+
apizit deploy --from-scan latest
|
|
32
|
+
apizit api show
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `python -m pip install --upgrade apizit-cli` to update.
|
|
36
|
+
`pipx install apizit-cli` is an isolated alternative. No AWS account, Docker or backend checkout is needed.
|
|
37
|
+
|
|
38
|
+
The default view summarizes outcomes. `--verbose` adds technical details; `--json` preserves
|
|
39
|
+
the complete versioned result and takes priority. Existing IDs and CI scripts remain supported.
|
|
40
|
+
Names and slugs resolve accessible resources only; ambiguous names need IDs.
|
|
41
|
+
`apizit api use "weather-api"` links an existing API without replacing another binding.
|
|
42
|
+
|
|
43
|
+
Interactive operations save non-secret references in `.apizit/operations.json`, scoped by folder,
|
|
44
|
+
endpoint and workspace. `latest` resumes the recorded operation. `deploy --from-scan latest`
|
|
45
|
+
reuses the exact scanned snapshot, excluding later edits. `deploy .` uploads current bytes.
|
|
46
|
+
|
|
47
|
+
APIZIT remains prelaunch. The default endpoint is `https://api.apizit.com/v1` (protected main domain).
|
|
48
|
+
For TEST, pass `--endpoint https://api-test.apizit.com/v1` or set `APIZIT_API_URL` on every command.
|
|
49
|
+
Sessions stay separate by endpoint. Configuration keeps its strict version 2 schema.
|
|
50
|
+
|
|
51
|
+
Run `apizit --help` or `apizit docs`. CI and AI agents use `--json --non-interactive` with
|
|
52
|
+
explicit IDs, stable idempotency keys and an expiring token. Store secrets in the workspace vault.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# APIZIT CLI
|
|
2
|
+
|
|
3
|
+
Check a project, launch its analyzed source and get its live API URL from your terminal.
|
|
4
|
+
Managed adapters cover Flask, FastAPI, Django, Express, NestJS and Spring Boot;
|
|
5
|
+
APIZIT Linking provides declarative Python routes. Scan to confirm compatibility.
|
|
6
|
+
|
|
7
|
+
Python 3.12 or later is required. Install with pip (or py -m pip on Windows):
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
python -m pip install apizit-cli
|
|
11
|
+
apizit login
|
|
12
|
+
apizit workspace use
|
|
13
|
+
apizit scan .
|
|
14
|
+
apizit deploy --from-scan latest
|
|
15
|
+
apizit api show
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use `python -m pip install --upgrade apizit-cli` to update.
|
|
19
|
+
`pipx install apizit-cli` is an isolated alternative. No AWS account, Docker or backend checkout is needed.
|
|
20
|
+
|
|
21
|
+
The default view summarizes outcomes. `--verbose` adds technical details; `--json` preserves
|
|
22
|
+
the complete versioned result and takes priority. Existing IDs and CI scripts remain supported.
|
|
23
|
+
Names and slugs resolve accessible resources only; ambiguous names need IDs.
|
|
24
|
+
`apizit api use "weather-api"` links an existing API without replacing another binding.
|
|
25
|
+
|
|
26
|
+
Interactive operations save non-secret references in `.apizit/operations.json`, scoped by folder,
|
|
27
|
+
endpoint and workspace. `latest` resumes the recorded operation. `deploy --from-scan latest`
|
|
28
|
+
reuses the exact scanned snapshot, excluding later edits. `deploy .` uploads current bytes.
|
|
29
|
+
|
|
30
|
+
APIZIT remains prelaunch. The default endpoint is `https://api.apizit.com/v1` (protected main domain).
|
|
31
|
+
For TEST, pass `--endpoint https://api-test.apizit.com/v1` or set `APIZIT_API_URL` on every command.
|
|
32
|
+
Sessions stay separate by endpoint. Configuration keeps its strict version 2 schema.
|
|
33
|
+
|
|
34
|
+
Run `apizit --help` or `apizit docs`. CI and AI agents use `--json --non-interactive` with
|
|
35
|
+
explicit IDs, stable idempotency keys and an expiring token. Store secrets in the workspace vault.
|
|
@@ -4,13 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apizit-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Launch supported Python, JavaScript, TypeScript or Java APIs from your terminal or CI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
authors = [{name = "APIZIT"}]
|
|
12
12
|
classifiers = [
|
|
13
|
-
"Development Status :: 4 - Beta",
|
|
14
13
|
"Environment :: Console",
|
|
15
14
|
"Operating System :: OS Independent",
|
|
16
15
|
"Programming Language :: Python :: 3",
|
|
@@ -33,8 +33,13 @@ def flag(name, help, **kwargs):
|
|
|
33
33
|
|
|
34
34
|
GLOBAL_OPTIONS = (
|
|
35
35
|
flag("--json", "Write one versioned JSON result to stdout.", action="store_true"),
|
|
36
|
-
flag(
|
|
37
|
-
|
|
36
|
+
flag(
|
|
37
|
+
"--verbose",
|
|
38
|
+
"Include readable technical details; --json takes priority.",
|
|
39
|
+
action="store_true",
|
|
40
|
+
),
|
|
41
|
+
flag("--workspace", "Exact workspace name, slug or ID; overrides saved context."),
|
|
42
|
+
flag("--endpoint", "APIZIT HTTPS endpoint; use api-test.apizit.com/v1 for TEST."),
|
|
38
43
|
flag("--non-interactive", "Never prompt or open a browser.", action="store_true"),
|
|
39
44
|
flag("--token-stdin", "Read a CI token from stdin without saving it.", action="store_true"),
|
|
40
45
|
)
|
|
@@ -53,7 +58,7 @@ FORMAT = flag(
|
|
|
53
58
|
"--format", "Launch format.", choices=("automatic", "standard", "expanded", "max_power")
|
|
54
59
|
)
|
|
55
60
|
REGION = flag("--region", "Logical region, as returned by apizit regions.")
|
|
56
|
-
API = flag("--api", "API ID; defaults to the API linked to this
|
|
61
|
+
API = flag("--api", "Exact API name, slug or ID; defaults to the API linked to this folder.")
|
|
57
62
|
REF = flag("--ref", "Git branch, tag or exact revision to verify on the server.")
|
|
58
63
|
SOURCE = argument("source", "HTTPS Git URL or local folder.", optional=True)
|
|
59
64
|
SCAN_OPTIONS = (REF, FORMAT, REGION) + WAIT_OPTIONS + WRITE_OPTIONS
|
|
@@ -72,7 +77,13 @@ COMMANDS = (
|
|
|
72
77
|
"workspace use",
|
|
73
78
|
"Select and verify a workspace.",
|
|
74
79
|
"read",
|
|
75
|
-
(
|
|
80
|
+
(
|
|
81
|
+
argument(
|
|
82
|
+
"id",
|
|
83
|
+
"Exact workspace name, slug or ID; otherwise select interactively.",
|
|
84
|
+
optional=True,
|
|
85
|
+
),
|
|
86
|
+
),
|
|
76
87
|
),
|
|
77
88
|
Command("workspace show", "Show the selected workspace and its current capabilities.", "read"),
|
|
78
89
|
Command(
|
|
@@ -138,6 +149,16 @@ COMMANDS = (
|
|
|
138
149
|
"apizit deploy . --wait",
|
|
139
150
|
),
|
|
140
151
|
Command("api list", "List APIs in the workspace.", "read"),
|
|
152
|
+
Command(
|
|
153
|
+
"api use",
|
|
154
|
+
"Link an accessible API to this folder without replacing an existing link.",
|
|
155
|
+
"read",
|
|
156
|
+
(
|
|
157
|
+
argument(
|
|
158
|
+
"id", "Exact API name, slug or ID; otherwise select interactively.", optional=True
|
|
159
|
+
),
|
|
160
|
+
),
|
|
161
|
+
),
|
|
141
162
|
Command(
|
|
142
163
|
"api show",
|
|
143
164
|
"Show an API, its live URL and releases.",
|
|
@@ -283,42 +304,43 @@ COMMANDS = (
|
|
|
283
304
|
_EXAMPLES = {
|
|
284
305
|
"login": "apizit login",
|
|
285
306
|
"logout": "apizit logout",
|
|
286
|
-
"whoami": "apizit whoami
|
|
287
|
-
"workspace list": "apizit workspace list
|
|
288
|
-
"workspace use": "apizit workspace use
|
|
289
|
-
"
|
|
307
|
+
"whoami": "apizit whoami",
|
|
308
|
+
"workspace list": "apizit workspace list",
|
|
309
|
+
"workspace use": "apizit workspace use",
|
|
310
|
+
"api use": 'apizit api use "weather-api"',
|
|
311
|
+
"workspace show": "apizit workspace show",
|
|
290
312
|
"workspace create": 'apizit workspace create "Weather APIs"',
|
|
291
313
|
"init": "apizit init . --name weather-api",
|
|
292
|
-
"config validate": "apizit config validate .
|
|
293
|
-
"config show": "apizit config show . --region europe
|
|
294
|
-
"config schema": "apizit config schema
|
|
295
|
-
"api list": "apizit api list
|
|
296
|
-
"api show": "apizit api show
|
|
297
|
-
"api logs": "apizit api logs
|
|
298
|
-
"api usage": "apizit api usage
|
|
299
|
-
"api openapi": "apizit api openapi
|
|
300
|
-
"api key": "apizit api key
|
|
301
|
-
"api delete":
|
|
302
|
-
"release list": "apizit release list --
|
|
303
|
-
"release show": "apizit release show dep_YOUR_RELEASE
|
|
304
|
-
"release promote": "apizit release promote dep_YOUR_CANDIDATE --
|
|
305
|
-
"release rollback": "apizit release rollback --
|
|
306
|
-
"release discard": "apizit release discard dep_YOUR_OLD_RELEASE --yes --wait
|
|
307
|
-
"job show": "apizit job show
|
|
308
|
-
"job wait": "apizit job wait
|
|
309
|
-
"job cancel": "apizit job cancel job_YOUR_JOB --yes
|
|
310
|
-
"regions": "apizit regions --format standard
|
|
311
|
-
"plans": "apizit plans
|
|
312
|
-
"usage": "apizit usage
|
|
313
|
-
"capabilities": "apizit capabilities
|
|
314
|
+
"config validate": "apizit config validate .",
|
|
315
|
+
"config show": "apizit config show . --region europe",
|
|
316
|
+
"config schema": "apizit config schema",
|
|
317
|
+
"api list": "apizit api list",
|
|
318
|
+
"api show": "apizit api show",
|
|
319
|
+
"api logs": "apizit api logs --level error --limit 50",
|
|
320
|
+
"api usage": "apizit api usage",
|
|
321
|
+
"api openapi": "apizit api openapi --output openapi.json",
|
|
322
|
+
"api key": "apizit api key",
|
|
323
|
+
"api delete": 'apizit api delete "weather-api" --wait',
|
|
324
|
+
"release list": "apizit release list --verbose",
|
|
325
|
+
"release show": "apizit release show dep_YOUR_RELEASE",
|
|
326
|
+
"release promote": "apizit release promote dep_YOUR_CANDIDATE --idempotency-key promote-release-001 --yes",
|
|
327
|
+
"release rollback": "apizit release rollback --idempotency-key rollback-release-001 --yes",
|
|
328
|
+
"release discard": "apizit release discard dep_YOUR_OLD_RELEASE --yes --wait",
|
|
329
|
+
"job show": "apizit job show latest",
|
|
330
|
+
"job wait": "apizit job wait latest --timeout 900",
|
|
331
|
+
"job cancel": "apizit job cancel job_YOUR_JOB --yes",
|
|
332
|
+
"regions": "apizit regions --format standard",
|
|
333
|
+
"plans": "apizit plans",
|
|
334
|
+
"usage": "apizit usage",
|
|
335
|
+
"capabilities": "apizit capabilities",
|
|
314
336
|
"token create": 'apizit token create "Release CI" --scope read --scope scan --scope deploy --expires-days 30',
|
|
315
|
-
"token list": "apizit token list
|
|
337
|
+
"token list": "apizit token list",
|
|
316
338
|
"token revoke": "apizit token revoke YOUR_TOKEN_ID --yes",
|
|
317
339
|
"secret set": "apizit secret set weather_api_key --from-env WEATHER_API_KEY",
|
|
318
|
-
"secret list": "apizit secret list
|
|
340
|
+
"secret list": "apizit secret list",
|
|
319
341
|
"secret delete": "apizit secret delete weather_api_key --yes",
|
|
320
342
|
"docs": 'apizit docs "release promote"',
|
|
321
|
-
"doctor": "apizit doctor
|
|
343
|
+
"doctor": "apizit doctor",
|
|
322
344
|
}
|
|
323
345
|
COMMANDS = tuple(
|
|
324
346
|
replace(command, example=command.example or _EXAMPLES[command.path]) for command in COMMANDS
|
|
@@ -93,6 +93,7 @@ class ClientContext:
|
|
|
93
93
|
self.root = Path(root or Path.cwd()).resolve()
|
|
94
94
|
self.path = home_directory() / "context.json"
|
|
95
95
|
self.project_path = self.root / ".apizit" / "project.json"
|
|
96
|
+
self.operations_path = self.root / ".apizit" / "operations.json"
|
|
96
97
|
self.preferences = read_json(self.path)
|
|
97
98
|
self.project = read_json(self.project_path)
|
|
98
99
|
self.origin = normalize_origin(
|
|
@@ -138,6 +139,83 @@ class ClientContext:
|
|
|
138
139
|
write_json(self.path, self.preferences)
|
|
139
140
|
self.workspace_id = workspace_id
|
|
140
141
|
|
|
142
|
+
def record_operation(self, kind, resource_id, *, source_kind=None, snapshot_id=None):
|
|
143
|
+
"""Only selected non-secret references, scoped to this directory and endpoint."""
|
|
144
|
+
import re
|
|
145
|
+
|
|
146
|
+
if kind not in {"scan", "job"} or not re.fullmatch(
|
|
147
|
+
rf"{kind}_[A-Za-z0-9_-]{{1,128}}", resource_id
|
|
148
|
+
):
|
|
149
|
+
raise CliError("Invalid operation reference.", code="INVALID_RESPONSE")
|
|
150
|
+
if kind == "scan" and (
|
|
151
|
+
source_kind not in {"local", "git"}
|
|
152
|
+
or (
|
|
153
|
+
snapshot_id is not None
|
|
154
|
+
and (
|
|
155
|
+
not isinstance(snapshot_id, str)
|
|
156
|
+
or not re.fullmatch(r"src_[A-Za-z0-9_-]{1,128}", snapshot_id)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
):
|
|
160
|
+
raise CliError("Invalid source reference.", code="INVALID_RESPONSE")
|
|
161
|
+
state = read_json(self.operations_path)
|
|
162
|
+
if state and state.get("schema_version") != 1:
|
|
163
|
+
raise CliError(
|
|
164
|
+
"Unsupported operations context.", code="CONTEXT_INVALID", exit_code=EXIT_USAGE
|
|
165
|
+
)
|
|
166
|
+
state.setdefault("schema_version", 1)
|
|
167
|
+
endpoints = state.setdefault("endpoints", {})
|
|
168
|
+
if not isinstance(endpoints, dict):
|
|
169
|
+
raise CliError(
|
|
170
|
+
"Invalid operations context.", code="CONTEXT_INVALID", exit_code=EXIT_USAGE
|
|
171
|
+
)
|
|
172
|
+
workspaces = endpoints.setdefault(self.origin, {})
|
|
173
|
+
if not isinstance(workspaces, dict):
|
|
174
|
+
raise CliError(
|
|
175
|
+
"Invalid operations context.", code="CONTEXT_INVALID", exit_code=EXIT_USAGE
|
|
176
|
+
)
|
|
177
|
+
scope = workspaces.setdefault(self.require_workspace(), {})
|
|
178
|
+
if not isinstance(scope, dict):
|
|
179
|
+
raise CliError(
|
|
180
|
+
"Invalid operations context.", code="CONTEXT_INVALID", exit_code=EXIT_USAGE
|
|
181
|
+
)
|
|
182
|
+
scope[kind] = {"id": resource_id}
|
|
183
|
+
if kind == "scan":
|
|
184
|
+
scope[kind].update(source_kind=source_kind, source_snapshot_id=snapshot_id)
|
|
185
|
+
write_json(self.operations_path, state)
|
|
186
|
+
self.ignore_context()
|
|
187
|
+
|
|
188
|
+
def latest_operation(self, kind):
|
|
189
|
+
import re
|
|
190
|
+
|
|
191
|
+
state = read_json(self.operations_path)
|
|
192
|
+
try:
|
|
193
|
+
value = state["endpoints"][self.origin][self.require_workspace()][kind]
|
|
194
|
+
valid = (
|
|
195
|
+
state["schema_version"] == 1
|
|
196
|
+
and isinstance(value, dict)
|
|
197
|
+
and re.fullmatch(rf"{kind}_[A-Za-z0-9_-]{{1,128}}", value.get("id", ""))
|
|
198
|
+
)
|
|
199
|
+
except (KeyError, TypeError, AttributeError):
|
|
200
|
+
valid = False
|
|
201
|
+
if not valid:
|
|
202
|
+
raise CliError(
|
|
203
|
+
f"No latest {kind} is recorded for this folder, endpoint and workspace. Use an explicit ID or start an interactive operation here.",
|
|
204
|
+
code="LATEST_NOT_FOUND",
|
|
205
|
+
exit_code=EXIT_USAGE,
|
|
206
|
+
)
|
|
207
|
+
return value
|
|
208
|
+
|
|
209
|
+
def ignore_context(self):
|
|
210
|
+
ignore = self.root / ".gitignore"
|
|
211
|
+
assert_safe_path(ignore)
|
|
212
|
+
previous = ignore.read_text(encoding="utf-8") if ignore.exists() else ""
|
|
213
|
+
if "/.apizit/" not in previous.splitlines():
|
|
214
|
+
with ignore.open("a", encoding="utf-8") as stream:
|
|
215
|
+
stream.write(
|
|
216
|
+
("\n" if previous and not previous.endswith("\n") else "") + "/.apizit/\n"
|
|
217
|
+
)
|
|
218
|
+
|
|
141
219
|
def bind_api(self, api_id):
|
|
142
220
|
ignore = self.root / ".gitignore"
|
|
143
221
|
assert_safe_path(ignore)
|
|
@@ -53,7 +53,7 @@ def build_parser():
|
|
|
53
53
|
prog="apizit",
|
|
54
54
|
description=(
|
|
55
55
|
"Launch supported Python, JavaScript, TypeScript or Java projects as managed public "
|
|
56
|
-
"APIs. Framework support follows the APIZIT compatibility guide. APIZIT prelaunch
|
|
56
|
+
"APIs. Framework support follows the APIZIT compatibility guide. APIZIT is prelaunch."
|
|
57
57
|
),
|
|
58
58
|
epilog=(
|
|
59
59
|
"Managed frameworks: Flask, FastAPI, Django, Express, NestJS and Spring Boot. "
|
|
@@ -113,10 +113,12 @@ def main(argv=None):
|
|
|
113
113
|
json_mode = "--json" in argv
|
|
114
114
|
runner = None
|
|
115
115
|
command = None
|
|
116
|
+
verbose = False
|
|
116
117
|
exit_code = 0
|
|
117
118
|
try:
|
|
118
119
|
args = build_parser().parse_args(argv)
|
|
119
120
|
json_mode = bool(getattr(args, "json", False))
|
|
121
|
+
verbose = bool(getattr(args, "verbose", False))
|
|
120
122
|
command = args.command_path
|
|
121
123
|
from apizit_cli.operations import CommandRunner
|
|
122
124
|
|
|
@@ -161,10 +163,21 @@ def main(argv=None):
|
|
|
161
163
|
if json_mode:
|
|
162
164
|
print(json.dumps(result, ensure_ascii=True, separators=(",", ":")))
|
|
163
165
|
elif result["ok"]:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
from apizit_cli.presentation import render_success
|
|
167
|
+
|
|
168
|
+
print(
|
|
169
|
+
render_success(
|
|
170
|
+
command,
|
|
171
|
+
result["data"],
|
|
172
|
+
verbose=verbose,
|
|
173
|
+
latest=getattr(runner, "recorded_scan", False)
|
|
174
|
+
or getattr(runner, "recorded_job", False),
|
|
175
|
+
)
|
|
176
|
+
)
|
|
166
177
|
else:
|
|
167
|
-
|
|
178
|
+
from apizit_cli.presentation import render_error
|
|
179
|
+
|
|
180
|
+
print(render_error(result["error"], verbose=verbose), file=sys.stderr)
|
|
168
181
|
return exit_code
|
|
169
182
|
|
|
170
183
|
|