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.
Files changed (108) hide show
  1. bijux_cli/__init__.py +39 -0
  2. bijux_cli/__main__.py +417 -0
  3. bijux_cli/__version__.py +42 -0
  4. bijux_cli/api.py +326 -0
  5. bijux_cli/cli.py +68 -0
  6. bijux_cli/commands/__init__.py +170 -0
  7. bijux_cli/commands/audit.py +293 -0
  8. bijux_cli/commands/config/__init__.py +85 -0
  9. bijux_cli/commands/config/clear.py +124 -0
  10. bijux_cli/commands/config/export.py +148 -0
  11. bijux_cli/commands/config/get.py +141 -0
  12. bijux_cli/commands/config/list_cmd.py +127 -0
  13. bijux_cli/commands/config/load.py +128 -0
  14. bijux_cli/commands/config/py.typed +0 -0
  15. bijux_cli/commands/config/reload.py +125 -0
  16. bijux_cli/commands/config/service.py +107 -0
  17. bijux_cli/commands/config/set.py +260 -0
  18. bijux_cli/commands/config/unset.py +140 -0
  19. bijux_cli/commands/dev/__init__.py +46 -0
  20. bijux_cli/commands/dev/di.py +285 -0
  21. bijux_cli/commands/dev/list_plugins.py +66 -0
  22. bijux_cli/commands/dev/py.typed +0 -0
  23. bijux_cli/commands/dev/service.py +120 -0
  24. bijux_cli/commands/docs.py +329 -0
  25. bijux_cli/commands/doctor.py +181 -0
  26. bijux_cli/commands/help.py +436 -0
  27. bijux_cli/commands/history/__init__.py +43 -0
  28. bijux_cli/commands/history/clear.py +160 -0
  29. bijux_cli/commands/history/py.typed +0 -0
  30. bijux_cli/commands/history/service.py +362 -0
  31. bijux_cli/commands/memory/__init__.py +59 -0
  32. bijux_cli/commands/memory/clear.py +114 -0
  33. bijux_cli/commands/memory/delete.py +144 -0
  34. bijux_cli/commands/memory/get.py +146 -0
  35. bijux_cli/commands/memory/list.py +115 -0
  36. bijux_cli/commands/memory/py.typed +0 -0
  37. bijux_cli/commands/memory/service.py +242 -0
  38. bijux_cli/commands/memory/set.py +136 -0
  39. bijux_cli/commands/memory/utils.py +54 -0
  40. bijux_cli/commands/plugins/__init__.py +59 -0
  41. bijux_cli/commands/plugins/check.py +240 -0
  42. bijux_cli/commands/plugins/info.py +143 -0
  43. bijux_cli/commands/plugins/install.py +327 -0
  44. bijux_cli/commands/plugins/list.py +73 -0
  45. bijux_cli/commands/plugins/py.typed +0 -0
  46. bijux_cli/commands/plugins/scaffold.py +287 -0
  47. bijux_cli/commands/plugins/uninstall.py +206 -0
  48. bijux_cli/commands/plugins/utils.py +158 -0
  49. bijux_cli/commands/py.typed +0 -0
  50. bijux_cli/commands/repl.py +709 -0
  51. bijux_cli/commands/sleep.py +169 -0
  52. bijux_cli/commands/status.py +289 -0
  53. bijux_cli/commands/utilities.py +637 -0
  54. bijux_cli/commands/version.py +152 -0
  55. bijux_cli/contracts/__init__.py +48 -0
  56. bijux_cli/contracts/audit.py +66 -0
  57. bijux_cli/contracts/config.py +127 -0
  58. bijux_cli/contracts/context.py +93 -0
  59. bijux_cli/contracts/docs.py +65 -0
  60. bijux_cli/contracts/doctor.py +32 -0
  61. bijux_cli/contracts/emitter.py +58 -0
  62. bijux_cli/contracts/history.py +101 -0
  63. bijux_cli/contracts/memory.py +76 -0
  64. bijux_cli/contracts/observability.py +94 -0
  65. bijux_cli/contracts/process.py +48 -0
  66. bijux_cli/contracts/py.typed +0 -0
  67. bijux_cli/contracts/registry.py +115 -0
  68. bijux_cli/contracts/retry.py +53 -0
  69. bijux_cli/contracts/serializer.py +100 -0
  70. bijux_cli/contracts/telemetry.py +46 -0
  71. bijux_cli/core/__init__.py +44 -0
  72. bijux_cli/core/constants.py +21 -0
  73. bijux_cli/core/context.py +218 -0
  74. bijux_cli/core/di.py +695 -0
  75. bijux_cli/core/engine.py +199 -0
  76. bijux_cli/core/enums.py +52 -0
  77. bijux_cli/core/exceptions.py +184 -0
  78. bijux_cli/core/paths.py +21 -0
  79. bijux_cli/core/py.typed +0 -0
  80. bijux_cli/httpapi.py +552 -0
  81. bijux_cli/infra/__init__.py +43 -0
  82. bijux_cli/infra/emitter.py +140 -0
  83. bijux_cli/infra/observability.py +148 -0
  84. bijux_cli/infra/process.py +177 -0
  85. bijux_cli/infra/py.typed +0 -0
  86. bijux_cli/infra/retry.py +300 -0
  87. bijux_cli/infra/serializer.py +573 -0
  88. bijux_cli/infra/telemetry.py +159 -0
  89. bijux_cli/py.typed +0 -0
  90. bijux_cli/services/__init__.py +233 -0
  91. bijux_cli/services/audit.py +268 -0
  92. bijux_cli/services/config.py +677 -0
  93. bijux_cli/services/docs.py +170 -0
  94. bijux_cli/services/doctor.py +34 -0
  95. bijux_cli/services/history.py +527 -0
  96. bijux_cli/services/memory.py +129 -0
  97. bijux_cli/services/plugins/__init__.py +346 -0
  98. bijux_cli/services/plugins/entrypoints.py +151 -0
  99. bijux_cli/services/plugins/groups.py +177 -0
  100. bijux_cli/services/plugins/hooks.py +113 -0
  101. bijux_cli/services/plugins/py.typed +0 -0
  102. bijux_cli/services/plugins/registry.py +284 -0
  103. bijux_cli/services/py.typed +0 -0
  104. bijux_cli/services/utils.py +68 -0
  105. bijux_cli-0.1.0.dist-info/METADATA +297 -0
  106. bijux_cli-0.1.0.dist-info/RECORD +108 -0
  107. bijux_cli-0.1.0.dist-info/WHEEL +4 -0
  108. bijux_cli-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,260 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright © 2025 Bijan Mousavi
3
+
4
+ """Implements the `config set` subcommand for the Bijux CLI.
5
+
6
+ This module contains the logic for creating or updating a key-value pair in
7
+ the active configuration store. It accepts input either as a direct argument
8
+ or from stdin, performs strict validation on keys and values, and provides a
9
+ structured, machine-readable response.
10
+
11
+ Output Contract:
12
+ * Success: `{"status": "updated", "key": str, "value": str}`
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, such as a file lock or write failure.
19
+ * `2`: An invalid argument was provided (e.g., malformed pair, invalid key).
20
+ * `3`: The key, value, or configuration path contained non-ASCII or forbidden
21
+ control characters.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from contextlib import suppress
27
+ import os
28
+ import platform
29
+ import re
30
+ import string
31
+ import sys
32
+
33
+ import typer
34
+
35
+ from bijux_cli.commands.utilities import (
36
+ ascii_safe,
37
+ emit_error_and_exit,
38
+ new_run_command,
39
+ parse_global_flags,
40
+ )
41
+ from bijux_cli.contracts import ConfigProtocol
42
+ from bijux_cli.core.constants import (
43
+ HELP_DEBUG,
44
+ HELP_FORMAT,
45
+ HELP_NO_PRETTY,
46
+ HELP_QUIET,
47
+ HELP_VERBOSE,
48
+ )
49
+ from bijux_cli.core.di import DIContainer
50
+
51
+
52
+ def set_config(
53
+ ctx: typer.Context,
54
+ pair: str | None = typer.Argument(
55
+ None, help="KEY=VALUE to set; if omitted, read from stdin"
56
+ ),
57
+ quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
58
+ verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
59
+ fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
60
+ pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
61
+ debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
62
+ ) -> None:
63
+ """Sets or updates a configuration key-value pair.
64
+
65
+ This function orchestrates the `set` operation. It accepts a `KEY=VALUE`
66
+ pair from either a command-line argument or standard input. It performs
67
+ extensive validation on the key and value for format and content, handles
68
+ file locking to prevent race conditions, and emits a structured payload
69
+ confirming the update.
70
+
71
+ Args:
72
+ ctx (typer.Context): The Typer context for the CLI.
73
+ pair (str | None): A string in "KEY=VALUE" format. If None, the pair
74
+ is read from stdin.
75
+ quiet (bool): If True, suppresses all output except for errors.
76
+ verbose (bool): If True, includes Python/platform details in the output.
77
+ fmt (str): The output format, "json" or "yaml".
78
+ pretty (bool): If True, pretty-prints the output.
79
+ debug (bool): If True, enables debug diagnostics.
80
+
81
+ Returns:
82
+ None:
83
+
84
+ Raises:
85
+ SystemExit: Always exits with a contract-compliant status code and
86
+ payload, indicating success or detailing the error.
87
+ """
88
+ cfg_path = os.environ.get("BIJUXCLI_CONFIG", "") or ""
89
+ if cfg_path:
90
+ try:
91
+ cfg_path.encode("ascii")
92
+ except UnicodeEncodeError:
93
+ emit_error_and_exit(
94
+ "Non-ASCII characters in config path",
95
+ code=3,
96
+ failure="ascii",
97
+ command="config set",
98
+ fmt="json",
99
+ quiet=False,
100
+ include_runtime=False,
101
+ debug=False,
102
+ extra={"path": "[non-ascii path provided]"},
103
+ )
104
+ flags = parse_global_flags()
105
+ quiet = flags["quiet"]
106
+ verbose = flags["verbose"]
107
+ fmt = flags["format"]
108
+ pretty = flags["pretty"]
109
+ debug = flags["debug"]
110
+ include_runtime = verbose
111
+ fmt_lower = fmt.lower()
112
+ command = "config set"
113
+ if os.name == "posix":
114
+ with suppress(Exception):
115
+ import fcntl
116
+
117
+ with open(cfg_path, "a+") as fh:
118
+ try:
119
+ fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
120
+ except OSError:
121
+ emit_error_and_exit(
122
+ "Config file is locked",
123
+ code=1,
124
+ failure="file_locked",
125
+ command=command,
126
+ fmt=fmt_lower,
127
+ quiet=quiet,
128
+ include_runtime=include_runtime,
129
+ debug=debug,
130
+ extra={"path": cfg_path},
131
+ )
132
+ finally:
133
+ with suppress(Exception):
134
+ fcntl.flock(fh, fcntl.LOCK_UN)
135
+ if pair is None:
136
+ if sys.stdin.isatty():
137
+ emit_error_and_exit(
138
+ "Missing argument: KEY=VALUE required",
139
+ code=2,
140
+ failure="missing_argument",
141
+ command=command,
142
+ fmt=fmt_lower,
143
+ quiet=quiet,
144
+ include_runtime=include_runtime,
145
+ debug=debug,
146
+ )
147
+ pair = sys.stdin.read().rstrip("\n")
148
+ if not pair or "=" not in pair:
149
+ emit_error_and_exit(
150
+ "Invalid argument: KEY=VALUE required",
151
+ code=2,
152
+ failure="invalid_argument",
153
+ command=command,
154
+ fmt=fmt_lower,
155
+ quiet=quiet,
156
+ include_runtime=include_runtime,
157
+ debug=debug,
158
+ )
159
+ raw_key, raw_value = pair.split("=", 1)
160
+ key = raw_key.strip()
161
+ service_value_str = raw_value
162
+ if len(service_value_str) >= 2 and (
163
+ (service_value_str[0] == service_value_str[-1] == '"')
164
+ or (service_value_str[0] == service_value_str[-1] == "'")
165
+ ):
166
+ import codecs
167
+
168
+ service_value_str = codecs.decode(service_value_str[1:-1], "unicode_escape")
169
+ if not key:
170
+ emit_error_and_exit(
171
+ "Key cannot be empty",
172
+ code=2,
173
+ failure="empty_key",
174
+ command=command,
175
+ fmt=fmt_lower,
176
+ quiet=quiet,
177
+ include_runtime=include_runtime,
178
+ debug=debug,
179
+ )
180
+ if not all(ord(c) < 128 for c in key + service_value_str):
181
+ emit_error_and_exit(
182
+ "Non-ASCII characters are not allowed in keys or values.",
183
+ code=3,
184
+ failure="ascii_error",
185
+ command=command,
186
+ fmt=fmt_lower,
187
+ quiet=quiet,
188
+ include_runtime=include_runtime,
189
+ debug=debug,
190
+ extra={"key": key},
191
+ )
192
+ if not re.match(r"^[A-Za-z0-9_]+$", key):
193
+ emit_error_and_exit(
194
+ "Invalid key: only alphanumerics and underscore allowed.",
195
+ code=2,
196
+ failure="invalid_key",
197
+ command=command,
198
+ fmt=fmt_lower,
199
+ quiet=quiet,
200
+ include_runtime=include_runtime,
201
+ debug=debug,
202
+ extra={"key": key},
203
+ )
204
+ if not all(
205
+ c in string.printable and c not in "\r\n\t\x0b\x0c" for c in service_value_str
206
+ ):
207
+ emit_error_and_exit(
208
+ "Control characters are not allowed in config values.",
209
+ code=3,
210
+ failure="control_char_error",
211
+ command=command,
212
+ fmt=fmt_lower,
213
+ quiet=quiet,
214
+ include_runtime=include_runtime,
215
+ debug=debug,
216
+ extra={"key": key},
217
+ )
218
+ config_svc = DIContainer.current().resolve(ConfigProtocol)
219
+ try:
220
+ config_svc.set(key, service_value_str)
221
+ except Exception as exc:
222
+ emit_error_and_exit(
223
+ f"Failed to set config: {exc}",
224
+ code=1,
225
+ failure="set_failed",
226
+ command=command,
227
+ fmt=fmt_lower,
228
+ quiet=quiet,
229
+ include_runtime=include_runtime,
230
+ debug=debug,
231
+ )
232
+
233
+ def payload_builder(include_runtime: bool) -> dict[str, object]:
234
+ """Builds the payload confirming a key was set or updated.
235
+
236
+ Args:
237
+ include_runtime (bool): If True, includes Python and platform info.
238
+
239
+ Returns:
240
+ dict[str, object]: The structured payload.
241
+ """
242
+ payload: dict[str, object] = {
243
+ "status": "updated",
244
+ "key": key,
245
+ "value": service_value_str,
246
+ }
247
+ if include_runtime:
248
+ payload["python"] = ascii_safe(platform.python_version(), "python_version")
249
+ payload["platform"] = ascii_safe(platform.platform(), "platform")
250
+ return payload
251
+
252
+ new_run_command(
253
+ command_name=command,
254
+ payload_builder=payload_builder,
255
+ quiet=quiet,
256
+ verbose=verbose,
257
+ fmt=fmt_lower,
258
+ pretty=pretty,
259
+ debug=debug,
260
+ )
@@ -0,0 +1,140 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright © 2025 Bijan Mousavi
3
+
4
+ """Implements the `config unset` subcommand for the Bijux CLI.
5
+
6
+ This module contains the logic for removing a key-value pair from the active
7
+ configuration store. It provides a structured, machine-readable response to
8
+ confirm the deletion or report an error, such as if the key does not exist.
9
+
10
+ Output Contract:
11
+ * Success: `{"status": "deleted", "key": str}`
12
+ * Verbose: Adds `{"python": str, "platform": str}` to the payload.
13
+ * Error: `{"error": str, "code": int}`
14
+
15
+ Exit Codes:
16
+ * `0`: Success.
17
+ * `1`: An unexpected error occurred while accessing the configuration.
18
+ * `2`: The specified key was not found in 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 unset_config(
45
+ ctx: typer.Context,
46
+ key: str = typer.Argument(..., help="Key to remove"),
47
+ quiet: bool = typer.Option(False, "-q", "--quiet", help=HELP_QUIET),
48
+ verbose: bool = typer.Option(False, "-v", "--verbose", help=HELP_VERBOSE),
49
+ fmt: str = typer.Option("json", "-f", "--format", help=HELP_FORMAT),
50
+ pretty: bool = typer.Option(True, "--pretty/--no-pretty", help=HELP_NO_PRETTY),
51
+ debug: bool = typer.Option(False, "-d", "--debug", help=HELP_DEBUG),
52
+ ) -> None:
53
+ """Removes a key from the active configuration store.
54
+
55
+ This function orchestrates the `unset` operation. It manually parses global
56
+ flags, resolves the configuration service, attempts to remove the specified
57
+ key, and then uses the `new_run_command` helper to emit a structured
58
+ payload confirming the action.
59
+
60
+ Args:
61
+ ctx (typer.Context): The Typer context for the CLI.
62
+ key (str): The configuration key to remove.
63
+ quiet (bool): If True, suppresses all output except for errors.
64
+ verbose (bool): If True, includes Python/platform details in the output.
65
+ fmt (str): The output format, "json" or "yaml".
66
+ pretty (bool): If True, pretty-prints the output.
67
+ debug (bool): If True, enables debug diagnostics.
68
+
69
+ Returns:
70
+ None:
71
+
72
+ Raises:
73
+ SystemExit: Always exits with a contract-compliant status code and
74
+ payload, indicating success or detailing the error.
75
+ """
76
+ flags = parse_global_flags()
77
+
78
+ quiet = flags["quiet"]
79
+ verbose = flags["verbose"]
80
+ fmt = flags["format"]
81
+ pretty = flags["pretty"]
82
+ debug = flags["debug"]
83
+
84
+ include_runtime = verbose
85
+ fmt_lower = fmt.lower()
86
+
87
+ command = "config unset"
88
+
89
+ config_svc = DIContainer.current().resolve(ConfigProtocol)
90
+
91
+ try:
92
+ config_svc.unset(key)
93
+ except KeyError:
94
+ emit_error_and_exit(
95
+ f"Config key not found: {key}",
96
+ code=2,
97
+ failure="not_found",
98
+ command=command,
99
+ fmt=fmt_lower,
100
+ quiet=quiet,
101
+ include_runtime=include_runtime,
102
+ debug=debug,
103
+ extra={"key": key},
104
+ )
105
+ except Exception as exc:
106
+ emit_error_and_exit(
107
+ f"Failed to unset config: {exc}",
108
+ code=1,
109
+ failure="unset_failed",
110
+ command=command,
111
+ fmt=fmt_lower,
112
+ quiet=quiet,
113
+ include_runtime=include_runtime,
114
+ debug=debug,
115
+ )
116
+
117
+ def payload_builder(include_runtime: bool) -> dict[str, object]:
118
+ """Builds the payload confirming a key was deleted.
119
+
120
+ Args:
121
+ include_runtime (bool): If True, includes Python and platform info.
122
+
123
+ Returns:
124
+ dict[str, object]: The structured payload.
125
+ """
126
+ payload: dict[str, object] = {"status": "deleted", "key": key}
127
+ if include_runtime:
128
+ payload["python"] = ascii_safe(platform.python_version(), "python_version")
129
+ payload["platform"] = ascii_safe(platform.platform(), "platform")
130
+ return payload
131
+
132
+ new_run_command(
133
+ command_name=command,
134
+ payload_builder=payload_builder,
135
+ quiet=quiet,
136
+ verbose=verbose,
137
+ fmt=fmt_lower,
138
+ pretty=pretty,
139
+ debug=debug,
140
+ )
@@ -0,0 +1,46 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright © 2025 Bijan Mousavi
3
+
4
+ """Defines the `dev` command group for the Bijux CLI.
5
+
6
+ This module serves as the central entry point for all developer-focused tools
7
+ and diagnostics. It aggregates the various subcommands into a single `Typer`
8
+ application, creating the `bijux dev` command hierarchy.
9
+
10
+ The `dev` command, when run without a subcommand, provides a simple status
11
+ confirmation.
12
+
13
+ The available subcommands are:
14
+ * `di`: Displays the dependency injection (DI) container graph.
15
+ * `list-plugins`: Lists all installed CLI plugins.
16
+
17
+ Exit codes for the subcommands generally follow this pattern:
18
+ * `0`: Success.
19
+ * `1`: An internal or unexpected error occurred.
20
+ * `2`: An invalid argument was provided (e.g., bad format).
21
+ * `3`: An ASCII or encoding error was detected.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import typer
27
+
28
+ from bijux_cli.commands.dev.di import dev_di_graph
29
+ from bijux_cli.commands.dev.list_plugins import dev_list_plugins
30
+ from bijux_cli.commands.dev.service import dev
31
+
32
+ typer.core.rich = None # type: ignore[attr-defined,assignment]
33
+
34
+ dev_app = typer.Typer( # pytype: skip-file
35
+ name="dev",
36
+ help="Developer tools and diagnostics.",
37
+ rich_markup_mode=None,
38
+ context_settings={"help_option_names": ["-h", "--help"]},
39
+ no_args_is_help=False,
40
+ )
41
+
42
+ dev_app.callback(invoke_without_command=True)(dev)
43
+ dev_app.command("di")(dev_di_graph)
44
+ dev_app.command("list-plugins")(dev_list_plugins)
45
+
46
+ __all__ = ["dev_app"]