bijux-cli 0.1.0__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.
- bijux_cli/__init__.py +39 -0
- bijux_cli/__main__.py +417 -0
- bijux_cli/__version__.py +42 -0
- bijux_cli/api.py +326 -0
- bijux_cli/cli.py +68 -0
- bijux_cli/commands/__init__.py +170 -0
- bijux_cli/commands/audit.py +293 -0
- bijux_cli/commands/config/__init__.py +85 -0
- bijux_cli/commands/config/clear.py +124 -0
- bijux_cli/commands/config/export.py +148 -0
- bijux_cli/commands/config/get.py +141 -0
- bijux_cli/commands/config/list_cmd.py +127 -0
- bijux_cli/commands/config/load.py +128 -0
- bijux_cli/commands/config/py.typed +0 -0
- bijux_cli/commands/config/reload.py +125 -0
- bijux_cli/commands/config/service.py +107 -0
- bijux_cli/commands/config/set.py +260 -0
- bijux_cli/commands/config/unset.py +140 -0
- bijux_cli/commands/dev/__init__.py +46 -0
- bijux_cli/commands/dev/di.py +285 -0
- bijux_cli/commands/dev/list_plugins.py +66 -0
- bijux_cli/commands/dev/py.typed +0 -0
- bijux_cli/commands/dev/service.py +120 -0
- bijux_cli/commands/docs.py +329 -0
- bijux_cli/commands/doctor.py +181 -0
- bijux_cli/commands/help.py +436 -0
- bijux_cli/commands/history/__init__.py +43 -0
- bijux_cli/commands/history/clear.py +160 -0
- bijux_cli/commands/history/py.typed +0 -0
- bijux_cli/commands/history/service.py +362 -0
- bijux_cli/commands/memory/__init__.py +59 -0
- bijux_cli/commands/memory/clear.py +114 -0
- bijux_cli/commands/memory/delete.py +144 -0
- bijux_cli/commands/memory/get.py +146 -0
- bijux_cli/commands/memory/list.py +115 -0
- bijux_cli/commands/memory/py.typed +0 -0
- bijux_cli/commands/memory/service.py +242 -0
- bijux_cli/commands/memory/set.py +136 -0
- bijux_cli/commands/memory/utils.py +54 -0
- bijux_cli/commands/plugins/__init__.py +59 -0
- bijux_cli/commands/plugins/check.py +240 -0
- bijux_cli/commands/plugins/info.py +143 -0
- bijux_cli/commands/plugins/install.py +327 -0
- bijux_cli/commands/plugins/list.py +73 -0
- bijux_cli/commands/plugins/py.typed +0 -0
- bijux_cli/commands/plugins/scaffold.py +287 -0
- bijux_cli/commands/plugins/uninstall.py +206 -0
- bijux_cli/commands/plugins/utils.py +158 -0
- bijux_cli/commands/py.typed +0 -0
- bijux_cli/commands/repl.py +709 -0
- bijux_cli/commands/sleep.py +169 -0
- bijux_cli/commands/status.py +289 -0
- bijux_cli/commands/utilities.py +637 -0
- bijux_cli/commands/version.py +152 -0
- bijux_cli/contracts/__init__.py +48 -0
- bijux_cli/contracts/audit.py +66 -0
- bijux_cli/contracts/config.py +127 -0
- bijux_cli/contracts/context.py +93 -0
- bijux_cli/contracts/docs.py +65 -0
- bijux_cli/contracts/doctor.py +32 -0
- bijux_cli/contracts/emitter.py +58 -0
- bijux_cli/contracts/history.py +101 -0
- bijux_cli/contracts/memory.py +76 -0
- bijux_cli/contracts/observability.py +94 -0
- bijux_cli/contracts/process.py +48 -0
- bijux_cli/contracts/py.typed +0 -0
- bijux_cli/contracts/registry.py +115 -0
- bijux_cli/contracts/retry.py +53 -0
- bijux_cli/contracts/serializer.py +100 -0
- bijux_cli/contracts/telemetry.py +46 -0
- bijux_cli/core/__init__.py +44 -0
- bijux_cli/core/constants.py +21 -0
- bijux_cli/core/context.py +218 -0
- bijux_cli/core/di.py +695 -0
- bijux_cli/core/engine.py +199 -0
- bijux_cli/core/enums.py +52 -0
- bijux_cli/core/exceptions.py +184 -0
- bijux_cli/core/paths.py +21 -0
- bijux_cli/core/py.typed +0 -0
- bijux_cli/httpapi.py +552 -0
- bijux_cli/infra/__init__.py +43 -0
- bijux_cli/infra/emitter.py +140 -0
- bijux_cli/infra/observability.py +148 -0
- bijux_cli/infra/process.py +177 -0
- bijux_cli/infra/py.typed +0 -0
- bijux_cli/infra/retry.py +300 -0
- bijux_cli/infra/serializer.py +573 -0
- bijux_cli/infra/telemetry.py +159 -0
- bijux_cli/py.typed +0 -0
- bijux_cli/services/__init__.py +233 -0
- bijux_cli/services/audit.py +268 -0
- bijux_cli/services/config.py +677 -0
- bijux_cli/services/docs.py +170 -0
- bijux_cli/services/doctor.py +34 -0
- bijux_cli/services/history.py +527 -0
- bijux_cli/services/memory.py +129 -0
- bijux_cli/services/plugins/__init__.py +346 -0
- bijux_cli/services/plugins/entrypoints.py +151 -0
- bijux_cli/services/plugins/groups.py +177 -0
- bijux_cli/services/plugins/hooks.py +113 -0
- bijux_cli/services/plugins/py.typed +0 -0
- bijux_cli/services/plugins/registry.py +284 -0
- bijux_cli/services/py.typed +0 -0
- bijux_cli/services/utils.py +68 -0
- bijux_cli-0.1.0.dist-info/METADATA +297 -0
- bijux_cli-0.1.0.dist-info/RECORD +108 -0
- bijux_cli-0.1.0.dist-info/WHEEL +4 -0
- bijux_cli-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright © 2025 Bijan Mousavi
|
|
3
|
+
|
|
4
|
+
"""Audit command for the Bijux CLI.
|
|
5
|
+
|
|
6
|
+
Audits the current environment and configuration, emitting machine-readable structured
|
|
7
|
+
output in JSON or YAML. Supports dry-run simulation and writing results to a file.
|
|
8
|
+
Handles ASCII hygiene and structured error contracts. Output is automation-safe and
|
|
9
|
+
suitable for scripting or monitoring.
|
|
10
|
+
|
|
11
|
+
Output Contract:
|
|
12
|
+
* Success: `{"status": "completed"}`
|
|
13
|
+
* Dry-run: `{"status": "dry-run"}`
|
|
14
|
+
* Written: `{"status": "written", "file": "<path>"}`
|
|
15
|
+
* Verbose: `{"python": str, "platform": str}`
|
|
16
|
+
* Error: `{"error": str, "code": int}`
|
|
17
|
+
|
|
18
|
+
Exit Codes:
|
|
19
|
+
* `0`: Success, dry-run, or write success.
|
|
20
|
+
* `1`: Unexpected/internal error.
|
|
21
|
+
* `2`: CLI argument/flag/format or output-path error.
|
|
22
|
+
* `3`: ASCII/encoding error.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Mapping
|
|
28
|
+
import os
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
import platform
|
|
31
|
+
|
|
32
|
+
import typer
|
|
33
|
+
|
|
34
|
+
from bijux_cli.commands.utilities import (
|
|
35
|
+
ascii_safe,
|
|
36
|
+
contains_non_ascii_env,
|
|
37
|
+
emit_error_and_exit,
|
|
38
|
+
new_run_command,
|
|
39
|
+
validate_common_flags,
|
|
40
|
+
validate_env_file_if_present,
|
|
41
|
+
)
|
|
42
|
+
from bijux_cli.contracts import EmitterProtocol
|
|
43
|
+
from bijux_cli.core.constants import (
|
|
44
|
+
HELP_DEBUG,
|
|
45
|
+
HELP_FORMAT,
|
|
46
|
+
HELP_NO_PRETTY,
|
|
47
|
+
HELP_QUIET,
|
|
48
|
+
HELP_VERBOSE,
|
|
49
|
+
)
|
|
50
|
+
from bijux_cli.core.di import DIContainer
|
|
51
|
+
from bijux_cli.core.enums import OutputFormat
|
|
52
|
+
|
|
53
|
+
typer.core.rich = None # type: ignore[attr-defined,assignment]
|
|
54
|
+
|
|
55
|
+
audit_app = typer.Typer( # pytype: skip-file
|
|
56
|
+
name="audit",
|
|
57
|
+
help="Audit the current environment for configuration and state issues.",
|
|
58
|
+
rich_markup_mode=None,
|
|
59
|
+
context_settings={
|
|
60
|
+
"help_option_names": ["-h", "--help"],
|
|
61
|
+
"ignore_unknown_options": True,
|
|
62
|
+
"allow_extra_args": True,
|
|
63
|
+
},
|
|
64
|
+
no_args_is_help=False,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
OUTPUT_OPTION = typer.Option(
|
|
69
|
+
None, "--output", "-o", help="Write output to file instead of stdout."
|
|
70
|
+
)
|
|
71
|
+
DRY_RUN_OPTION = typer.Option(
|
|
72
|
+
False, "--dry-run", help="Simulate audit without making changes."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _build_payload(include_runtime: bool, dry_run: bool) -> Mapping[str, object]:
|
|
77
|
+
"""Builds the structured result payload for the audit command.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
include_runtime (bool): If True, runtime metadata (Python version,
|
|
81
|
+
platform) is included in the payload.
|
|
82
|
+
dry_run (bool): If True, indicates the audit is a simulation, which
|
|
83
|
+
sets the status field in the payload to "dry-run".
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Mapping[str, object]: A dictionary containing the structured audit results.
|
|
87
|
+
"""
|
|
88
|
+
payload: dict[str, object] = {"status": "dry-run" if dry_run else "completed"}
|
|
89
|
+
if include_runtime:
|
|
90
|
+
payload["python"] = ascii_safe(platform.python_version(), "python_version")
|
|
91
|
+
payload["platform"] = ascii_safe(platform.platform(), "platform")
|
|
92
|
+
return payload
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _write_output_file(
|
|
96
|
+
output_path: Path,
|
|
97
|
+
payload: Mapping[str, object],
|
|
98
|
+
emitter: EmitterProtocol,
|
|
99
|
+
fmt: OutputFormat,
|
|
100
|
+
pretty: bool,
|
|
101
|
+
debug: bool,
|
|
102
|
+
dry_run: bool,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Writes the audit payload to a specified file.
|
|
105
|
+
|
|
106
|
+
This function serializes the payload to JSON or YAML and writes it to the
|
|
107
|
+
given file path. It will fail if the parent directory does not exist.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
output_path (Path): The file path where the payload will be written.
|
|
111
|
+
payload (Mapping[str, object]): The data to serialize and write.
|
|
112
|
+
emitter (EmitterProtocol): The service responsible for serialization and
|
|
113
|
+
output.
|
|
114
|
+
fmt (OutputFormat): The desired output format (JSON or YAML).
|
|
115
|
+
pretty (bool): If True, the output is formatted for human readability.
|
|
116
|
+
debug (bool): If True, enables debug-level logging during emission.
|
|
117
|
+
dry_run (bool): If True, logs a message indicating a dry run.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
None:
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
OSError: If the parent directory of `output_path` does not exist.
|
|
124
|
+
"""
|
|
125
|
+
if not output_path.parent.exists():
|
|
126
|
+
raise OSError(f"Output directory does not exist: {output_path.parent}")
|
|
127
|
+
|
|
128
|
+
emitter.emit(
|
|
129
|
+
payload,
|
|
130
|
+
fmt=fmt,
|
|
131
|
+
pretty=pretty,
|
|
132
|
+
message="Audit dry-run completed" if dry_run else "Audit completed",
|
|
133
|
+
debug=debug,
|
|
134
|
+
output=str(output_path),
|
|
135
|
+
quiet=False,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@audit_app.callback(invoke_without_command=True)
|
|
140
|
+
def audit(
|
|
141
|
+
ctx: typer.Context,
|
|
142
|
+
dry_run: bool = DRY_RUN_OPTION,
|
|
143
|
+
output: Path | None = OUTPUT_OPTION,
|
|
144
|
+
quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
|
|
145
|
+
verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
|
|
146
|
+
fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
|
|
147
|
+
pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
|
|
148
|
+
debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Defines the entrypoint and logic for the `bijux audit` command.
|
|
151
|
+
|
|
152
|
+
This function orchestrates the entire audit process. It validates all CLI
|
|
153
|
+
flags and arguments, performs environment checks (e.g., for non-ASCII
|
|
154
|
+
characters), builds the appropriate result payload, and emits it to
|
|
155
|
+
stdout or a file in the specified format. All errors are handled and
|
|
156
|
+
emitted in a structured format before exiting with a specific code.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
ctx (typer.Context): The Typer context, used to manage command state
|
|
160
|
+
and detect stray arguments.
|
|
161
|
+
dry_run (bool): If True, simulates the audit and reports a "dry-run"
|
|
162
|
+
status without performing actions.
|
|
163
|
+
output (Path | None): If a path is provided, writes the audit result
|
|
164
|
+
to the specified file instead of stdout.
|
|
165
|
+
quiet (bool): If True, suppresses all output except for errors. The
|
|
166
|
+
exit code is the primary indicator of the outcome.
|
|
167
|
+
verbose (bool): If True, includes Python and platform details in the
|
|
168
|
+
output payload.
|
|
169
|
+
fmt (str): The output format, either "json" or "yaml". Defaults to "json".
|
|
170
|
+
pretty (bool): If True, pretty-prints the output for human readability.
|
|
171
|
+
This is overridden by `debug`.
|
|
172
|
+
debug (bool): If True, enables debug diagnostics, which implies `verbose`
|
|
173
|
+
and `pretty`.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
None:
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
SystemExit: Exits with a status code and structured error payload upon
|
|
180
|
+
validation failures (e.g., bad arguments, ASCII errors), I/O
|
|
181
|
+
issues, or unexpected exceptions. The exit code follows the
|
|
182
|
+
contract defined in the module docstring.
|
|
183
|
+
"""
|
|
184
|
+
if ctx.invoked_subcommand:
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
command = "audit"
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
stray_args = [a for a in ctx.args if not a.startswith("-")]
|
|
191
|
+
if stray_args:
|
|
192
|
+
raise typer.BadParameter(f"No such argument: {stray_args[0]}")
|
|
193
|
+
fmt_lower = validate_common_flags(fmt, command, quiet)
|
|
194
|
+
include_runtime = verbose or debug
|
|
195
|
+
effective_pretty = debug or pretty
|
|
196
|
+
out_format = OutputFormat.YAML if fmt_lower == "yaml" else OutputFormat.JSON
|
|
197
|
+
|
|
198
|
+
if contains_non_ascii_env():
|
|
199
|
+
emit_error_and_exit(
|
|
200
|
+
"Non-ASCII environment variables detected",
|
|
201
|
+
code=3,
|
|
202
|
+
failure="ascii_env",
|
|
203
|
+
command=command,
|
|
204
|
+
fmt=fmt_lower,
|
|
205
|
+
quiet=quiet,
|
|
206
|
+
include_runtime=include_runtime,
|
|
207
|
+
)
|
|
208
|
+
try:
|
|
209
|
+
validate_env_file_if_present(os.environ.get("BIJUXCLI_CONFIG", ""))
|
|
210
|
+
except ValueError as exc:
|
|
211
|
+
emit_error_and_exit(
|
|
212
|
+
str(exc),
|
|
213
|
+
code=3,
|
|
214
|
+
failure="ascii",
|
|
215
|
+
command=command,
|
|
216
|
+
fmt=fmt_lower,
|
|
217
|
+
quiet=quiet,
|
|
218
|
+
include_runtime=include_runtime,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
except typer.BadParameter as exc:
|
|
222
|
+
error_fmt = fmt.lower() if fmt.lower() in ("json", "yaml") else "json"
|
|
223
|
+
emit_error_and_exit(
|
|
224
|
+
exc.message,
|
|
225
|
+
code=2,
|
|
226
|
+
failure="args",
|
|
227
|
+
command=command,
|
|
228
|
+
fmt=error_fmt,
|
|
229
|
+
quiet=quiet,
|
|
230
|
+
include_runtime=verbose or debug,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
emitter = DIContainer.current().resolve(EmitterProtocol)
|
|
235
|
+
payload = _build_payload(include_runtime, dry_run)
|
|
236
|
+
|
|
237
|
+
if output is not None:
|
|
238
|
+
_write_output_file(
|
|
239
|
+
output_path=output,
|
|
240
|
+
payload=payload,
|
|
241
|
+
emitter=emitter,
|
|
242
|
+
fmt=out_format,
|
|
243
|
+
pretty=effective_pretty,
|
|
244
|
+
debug=debug,
|
|
245
|
+
dry_run=dry_run,
|
|
246
|
+
)
|
|
247
|
+
payload = {"status": "written", "file": str(output)}
|
|
248
|
+
if include_runtime:
|
|
249
|
+
payload["python"] = ascii_safe(
|
|
250
|
+
platform.python_version(), "python_version"
|
|
251
|
+
)
|
|
252
|
+
payload["platform"] = ascii_safe(platform.platform(), "platform")
|
|
253
|
+
|
|
254
|
+
new_run_command(
|
|
255
|
+
command_name=command,
|
|
256
|
+
payload_builder=lambda _: payload,
|
|
257
|
+
quiet=quiet,
|
|
258
|
+
verbose=(verbose or debug),
|
|
259
|
+
fmt=fmt_lower,
|
|
260
|
+
pretty=(debug or pretty),
|
|
261
|
+
debug=debug,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
except ValueError as exc:
|
|
265
|
+
emit_error_and_exit(
|
|
266
|
+
str(exc),
|
|
267
|
+
code=3,
|
|
268
|
+
failure="ascii",
|
|
269
|
+
command=command,
|
|
270
|
+
fmt=fmt_lower,
|
|
271
|
+
quiet=quiet,
|
|
272
|
+
include_runtime=include_runtime,
|
|
273
|
+
)
|
|
274
|
+
except OSError as exc:
|
|
275
|
+
emit_error_and_exit(
|
|
276
|
+
str(exc),
|
|
277
|
+
code=2,
|
|
278
|
+
failure="output_file",
|
|
279
|
+
command=command,
|
|
280
|
+
fmt=fmt_lower,
|
|
281
|
+
quiet=quiet,
|
|
282
|
+
include_runtime=include_runtime,
|
|
283
|
+
)
|
|
284
|
+
except Exception as exc:
|
|
285
|
+
emit_error_and_exit(
|
|
286
|
+
f"An unexpected error occurred: {exc}",
|
|
287
|
+
code=1,
|
|
288
|
+
failure="unexpected",
|
|
289
|
+
command=command,
|
|
290
|
+
fmt=fmt_lower,
|
|
291
|
+
quiet=quiet,
|
|
292
|
+
include_runtime=include_runtime,
|
|
293
|
+
)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright © 2025 Bijan Mousavi
|
|
3
|
+
|
|
4
|
+
"""Defines the `config` command group for the Bijux CLI.
|
|
5
|
+
|
|
6
|
+
This module serves as the central entry point for all configuration management
|
|
7
|
+
functionality. It aggregates the various subcommands into a single `Typer`
|
|
8
|
+
application, creating the `bijux config` command hierarchy.
|
|
9
|
+
|
|
10
|
+
The `config` command, when run without a subcommand, lists all current
|
|
11
|
+
key-value pairs.
|
|
12
|
+
|
|
13
|
+
The available subcommands are:
|
|
14
|
+
* `set`: Sets a key-value pair.
|
|
15
|
+
* `get`: Retrieves the value for a specific key.
|
|
16
|
+
* `unset`: Removes a key-value pair.
|
|
17
|
+
* `list`: Lists all defined configuration keys.
|
|
18
|
+
* `clear`: Removes all key-value pairs from the configuration.
|
|
19
|
+
* `load`: Replaces the current configuration with one from a file.
|
|
20
|
+
* `reload`: Forces a reload of the configuration from its source file.
|
|
21
|
+
* `export`: Writes the current configuration to a file or stdout.
|
|
22
|
+
|
|
23
|
+
Exit codes for the subcommands generally follow this pattern:
|
|
24
|
+
* `0`: Success.
|
|
25
|
+
* `1`: An internal or unexpected error occurred.
|
|
26
|
+
* `2`: An invalid argument was provided (e.g., bad format, key not found).
|
|
27
|
+
* `3`: An ASCII or encoding error was detected.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import typer
|
|
33
|
+
|
|
34
|
+
from bijux_cli.commands.config.clear import clear_config
|
|
35
|
+
from bijux_cli.commands.config.export import export_config
|
|
36
|
+
from bijux_cli.commands.config.get import get_config
|
|
37
|
+
from bijux_cli.commands.config.list_cmd import list_config
|
|
38
|
+
from bijux_cli.commands.config.load import load_config
|
|
39
|
+
from bijux_cli.commands.config.reload import reload_config
|
|
40
|
+
from bijux_cli.commands.config.service import config
|
|
41
|
+
from bijux_cli.commands.config.set import set_config
|
|
42
|
+
from bijux_cli.commands.config.unset import unset_config
|
|
43
|
+
|
|
44
|
+
typer.core.rich = None # type: ignore[attr-defined,assignment]
|
|
45
|
+
|
|
46
|
+
config_app = typer.Typer( # pytype: skip-file
|
|
47
|
+
name="config",
|
|
48
|
+
help="Manage CLI configuration.",
|
|
49
|
+
rich_markup_mode=None,
|
|
50
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
51
|
+
no_args_is_help=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
config_app.callback(invoke_without_command=True)(config)
|
|
55
|
+
|
|
56
|
+
config_app.command("clear")(clear_config)
|
|
57
|
+
config_app.command("export")(export_config)
|
|
58
|
+
config_app.command("get")(get_config)
|
|
59
|
+
config_app.command("list")(list_config)
|
|
60
|
+
config_app.command("load")(load_config)
|
|
61
|
+
config_app.command("reload")(reload_config)
|
|
62
|
+
config_app.command("set")(set_config)
|
|
63
|
+
config_app.command("unset")(unset_config)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@config_app.command("import", hidden=True)
|
|
67
|
+
def import_config(*args, **kwargs) -> None: # type: ignore
|
|
68
|
+
"""Provides a backward-compatibility alias for the `config load` command.
|
|
69
|
+
|
|
70
|
+
This command is hidden from the main help text and delegates directly to
|
|
71
|
+
`load_config`, forwarding all arguments.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
*args: Positional arguments to forward to `load_config`.
|
|
75
|
+
**kwargs: Keyword arguments to forward to `load_config`.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
None:
|
|
79
|
+
"""
|
|
80
|
+
return load_config(*args, **kwargs)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
"config_app",
|
|
85
|
+
]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright © 2025 Bijan Mousavi
|
|
3
|
+
|
|
4
|
+
"""Implements the `config clear` subcommand for the Bijux CLI.
|
|
5
|
+
|
|
6
|
+
This module contains the logic for completely erasing all key-value pairs from
|
|
7
|
+
the active configuration store. This action is irreversible and effectively
|
|
8
|
+
resets the configuration to an empty state. A structured confirmation is
|
|
9
|
+
emitted upon success.
|
|
10
|
+
|
|
11
|
+
Output Contract:
|
|
12
|
+
* Success: `{"status": "cleared"}`
|
|
13
|
+
* Verbose: Adds `{"python": str, "platform": str}` to the payload.
|
|
14
|
+
* Error: `{"error": str, "code": int}`
|
|
15
|
+
|
|
16
|
+
Exit Codes:
|
|
17
|
+
* `0`: Success.
|
|
18
|
+
* `1`: An unexpected error occurred while clearing the configuration.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import platform
|
|
24
|
+
|
|
25
|
+
import typer
|
|
26
|
+
|
|
27
|
+
from bijux_cli.commands.utilities import (
|
|
28
|
+
ascii_safe,
|
|
29
|
+
emit_error_and_exit,
|
|
30
|
+
new_run_command,
|
|
31
|
+
parse_global_flags,
|
|
32
|
+
)
|
|
33
|
+
from bijux_cli.contracts import ConfigProtocol
|
|
34
|
+
from bijux_cli.core.constants import (
|
|
35
|
+
HELP_DEBUG,
|
|
36
|
+
HELP_FORMAT,
|
|
37
|
+
HELP_NO_PRETTY,
|
|
38
|
+
HELP_QUIET,
|
|
39
|
+
HELP_VERBOSE,
|
|
40
|
+
)
|
|
41
|
+
from bijux_cli.core.di import DIContainer
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def clear_config(
|
|
45
|
+
ctx: typer.Context,
|
|
46
|
+
quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
|
|
47
|
+
verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
|
|
48
|
+
fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
|
|
49
|
+
pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
|
|
50
|
+
debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Clears all configuration settings from the active store.
|
|
53
|
+
|
|
54
|
+
This command erases all key-value pairs, effectively resetting the
|
|
55
|
+
configuration. It emits a structured payload to confirm the operation.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
ctx (typer.Context): The Typer context for the CLI.
|
|
59
|
+
quiet (bool): If True, suppresses all output except for errors.
|
|
60
|
+
verbose (bool): If True, includes Python/platform details in the output.
|
|
61
|
+
fmt (str): The output format, "json" or "yaml".
|
|
62
|
+
pretty (bool): If True, pretty-prints the output.
|
|
63
|
+
debug (bool): If True, enables debug diagnostics.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
None:
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
SystemExit: Always exits with a contract-compliant status code and
|
|
70
|
+
payload, indicating success or detailing the error.
|
|
71
|
+
"""
|
|
72
|
+
flags = parse_global_flags()
|
|
73
|
+
|
|
74
|
+
quiet = flags["quiet"]
|
|
75
|
+
verbose = flags["verbose"]
|
|
76
|
+
fmt = flags["format"]
|
|
77
|
+
pretty = flags["pretty"]
|
|
78
|
+
debug = flags["debug"]
|
|
79
|
+
|
|
80
|
+
include_runtime = verbose
|
|
81
|
+
fmt_lower = fmt.lower()
|
|
82
|
+
|
|
83
|
+
command = "config clear"
|
|
84
|
+
|
|
85
|
+
config_svc = DIContainer.current().resolve(ConfigProtocol)
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
config_svc.clear()
|
|
89
|
+
except Exception as exc:
|
|
90
|
+
emit_error_and_exit(
|
|
91
|
+
f"Failed to clear config: {exc}",
|
|
92
|
+
code=1,
|
|
93
|
+
failure="clear_failed",
|
|
94
|
+
command=command,
|
|
95
|
+
fmt=fmt_lower,
|
|
96
|
+
quiet=quiet,
|
|
97
|
+
include_runtime=include_runtime,
|
|
98
|
+
debug=debug,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def payload_builder(include_runtime: bool) -> dict[str, object]:
|
|
102
|
+
"""Builds the payload confirming a successful configuration clear.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
include_runtime (bool): If True, includes Python and platform info.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
dict[str, object]: The structured payload.
|
|
109
|
+
"""
|
|
110
|
+
payload: dict[str, object] = {"status": "cleared"}
|
|
111
|
+
if include_runtime:
|
|
112
|
+
payload["python"] = ascii_safe(platform.python_version(), "python_version")
|
|
113
|
+
payload["platform"] = ascii_safe(platform.platform(), "platform")
|
|
114
|
+
return payload
|
|
115
|
+
|
|
116
|
+
new_run_command(
|
|
117
|
+
command_name=command,
|
|
118
|
+
payload_builder=payload_builder,
|
|
119
|
+
quiet=quiet,
|
|
120
|
+
verbose=verbose,
|
|
121
|
+
fmt=fmt_lower,
|
|
122
|
+
pretty=pretty,
|
|
123
|
+
debug=debug,
|
|
124
|
+
)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright © 2025 Bijan Mousavi
|
|
3
|
+
|
|
4
|
+
"""Implements the `config export` subcommand for the Bijux CLI.
|
|
5
|
+
|
|
6
|
+
This module contains the logic for exporting the application's entire current
|
|
7
|
+
configuration to a specified destination, which can be a file or standard
|
|
8
|
+
output. The output format can be explicitly set to 'env', 'json', or 'yaml',
|
|
9
|
+
or it can be inferred from the destination file's extension.
|
|
10
|
+
|
|
11
|
+
Output Contract:
|
|
12
|
+
* Success (to file): `{"status": "exported", "file": str, "format": str}`
|
|
13
|
+
* Success (to stdout): The raw exported configuration data is printed directly.
|
|
14
|
+
* Verbose (to file): Adds `{"python": str, "platform": str}` to the payload.
|
|
15
|
+
* Error: `{"error": str, "code": int}`
|
|
16
|
+
|
|
17
|
+
Exit Codes:
|
|
18
|
+
* `0`: Success.
|
|
19
|
+
* `1` or `2`: An error occurred during the export process, such as a file
|
|
20
|
+
write error or invalid format request.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import platform
|
|
26
|
+
|
|
27
|
+
import typer
|
|
28
|
+
|
|
29
|
+
from bijux_cli.commands.utilities import (
|
|
30
|
+
ascii_safe,
|
|
31
|
+
emit_error_and_exit,
|
|
32
|
+
new_run_command,
|
|
33
|
+
parse_global_flags,
|
|
34
|
+
)
|
|
35
|
+
from bijux_cli.contracts import ConfigProtocol
|
|
36
|
+
from bijux_cli.core.constants import (
|
|
37
|
+
HELP_DEBUG,
|
|
38
|
+
HELP_FORMAT,
|
|
39
|
+
HELP_NO_PRETTY,
|
|
40
|
+
HELP_QUIET,
|
|
41
|
+
HELP_VERBOSE,
|
|
42
|
+
)
|
|
43
|
+
from bijux_cli.core.di import DIContainer
|
|
44
|
+
from bijux_cli.core.exceptions import CommandError
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def export_config(
|
|
48
|
+
ctx: typer.Context,
|
|
49
|
+
path: str = typer.Argument(
|
|
50
|
+
..., help="Destination file – use “-” to write to STDOUT"
|
|
51
|
+
),
|
|
52
|
+
out_fmt: str = typer.Option(
|
|
53
|
+
None, "--out-format", help="Force output format: env | json | yaml"
|
|
54
|
+
),
|
|
55
|
+
quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
|
|
56
|
+
verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
|
|
57
|
+
fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
|
|
58
|
+
pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
|
|
59
|
+
debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Exports the current configuration to a file or standard output.
|
|
62
|
+
|
|
63
|
+
This function writes all configuration key-value pairs to a specified
|
|
64
|
+
destination. If the destination is a file path, a structured JSON/YAML
|
|
65
|
+
confirmation message is printed to stdout upon success. If the destination
|
|
66
|
+
is "-", the raw exported configuration is printed directly to stdout.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
ctx (typer.Context): The Typer context for the CLI.
|
|
70
|
+
path (str): The destination file path, or "-" for standard output.
|
|
71
|
+
out_fmt (str): The desired output format ('env', 'json', 'yaml'). If
|
|
72
|
+
unspecified, it is inferred from the file extension.
|
|
73
|
+
quiet (bool): If True, suppresses all output except for errors.
|
|
74
|
+
verbose (bool): If True, includes Python/platform details in the
|
|
75
|
+
confirmation payload (file export only).
|
|
76
|
+
fmt (str): The format for the confirmation payload ("json" or "yaml").
|
|
77
|
+
pretty (bool): If True, pretty-prints the confirmation payload.
|
|
78
|
+
debug (bool): If True, enables debug diagnostics.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
None:
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
SystemExit: Always exits with a contract-compliant status code and
|
|
85
|
+
payload, indicating success or detailing the error.
|
|
86
|
+
"""
|
|
87
|
+
flags = parse_global_flags()
|
|
88
|
+
|
|
89
|
+
quiet = flags["quiet"]
|
|
90
|
+
verbose = flags["verbose"]
|
|
91
|
+
fmt = flags["format"]
|
|
92
|
+
pretty = flags["pretty"]
|
|
93
|
+
debug = flags["debug"]
|
|
94
|
+
|
|
95
|
+
include_runtime = verbose
|
|
96
|
+
fmt_lower = fmt.lower()
|
|
97
|
+
|
|
98
|
+
command = "config export"
|
|
99
|
+
|
|
100
|
+
config_svc = DIContainer.current().resolve(ConfigProtocol)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
config_svc.export(path, out_fmt)
|
|
104
|
+
except CommandError as exc:
|
|
105
|
+
code = 2 if getattr(exc, "http_status", 0) == 400 else 1
|
|
106
|
+
emit_error_and_exit(
|
|
107
|
+
f"Failed to export config: {exc}",
|
|
108
|
+
code=code,
|
|
109
|
+
failure="export_failed",
|
|
110
|
+
command=command,
|
|
111
|
+
fmt=fmt_lower,
|
|
112
|
+
quiet=quiet,
|
|
113
|
+
include_runtime=include_runtime,
|
|
114
|
+
debug=debug,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if path != "-":
|
|
118
|
+
|
|
119
|
+
def payload_builder(include_runtime: bool) -> dict[str, object]:
|
|
120
|
+
"""Builds the payload confirming a successful export to a file.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
include_runtime (bool): If True, includes Python and platform info.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
dict[str, object]: The structured payload.
|
|
127
|
+
"""
|
|
128
|
+
payload: dict[str, object] = {
|
|
129
|
+
"status": "exported",
|
|
130
|
+
"file": path,
|
|
131
|
+
"format": out_fmt or "auto",
|
|
132
|
+
}
|
|
133
|
+
if include_runtime:
|
|
134
|
+
payload["python"] = ascii_safe(
|
|
135
|
+
platform.python_version(), "python_version"
|
|
136
|
+
)
|
|
137
|
+
payload["platform"] = ascii_safe(platform.platform(), "platform")
|
|
138
|
+
return payload
|
|
139
|
+
|
|
140
|
+
new_run_command(
|
|
141
|
+
command_name=command,
|
|
142
|
+
payload_builder=payload_builder,
|
|
143
|
+
quiet=quiet,
|
|
144
|
+
verbose=verbose,
|
|
145
|
+
fmt=fmt_lower,
|
|
146
|
+
pretty=pretty,
|
|
147
|
+
debug=debug,
|
|
148
|
+
)
|