cmdbox-cli 1.0.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 (112) hide show
  1. cmdbox/__init__.py +0 -0
  2. cmdbox/cli/__init__.py +0 -0
  3. cmdbox/cli/app.py +125 -0
  4. cmdbox/cli/commands/__init__.py +0 -0
  5. cmdbox/cli/commands/alias_fallback.py +102 -0
  6. cmdbox/cli/commands/command_crud.py +429 -0
  7. cmdbox/cli/commands/command_run.py +255 -0
  8. cmdbox/cli/commands/history.py +109 -0
  9. cmdbox/cli/commands/init.py +54 -0
  10. cmdbox/cli/commands/settings.py +62 -0
  11. cmdbox/cli/commands/tag_crud.py +277 -0
  12. cmdbox/cli/commands/variable_crud.py +349 -0
  13. cmdbox/cli/common/__init__.py +0 -0
  14. cmdbox/cli/common/errors.py +58 -0
  15. cmdbox/cli/common/update_fields.py +88 -0
  16. cmdbox/cli/completions/__init__.py +0 -0
  17. cmdbox/cli/completions/commands.py +26 -0
  18. cmdbox/cli/completions/fields.py +31 -0
  19. cmdbox/cli/completions/tags.py +24 -0
  20. cmdbox/cli/completions/variables.py +26 -0
  21. cmdbox/cli/handlers/__init__.py +0 -0
  22. cmdbox/cli/handlers/command_handlers.py +357 -0
  23. cmdbox/cli/handlers/common_handlers.py +15 -0
  24. cmdbox/cli/handlers/history_handlers.py +94 -0
  25. cmdbox/cli/handlers/init_handler.py +127 -0
  26. cmdbox/cli/handlers/run_handler.py +178 -0
  27. cmdbox/cli/handlers/settings_handler.py +59 -0
  28. cmdbox/cli/handlers/tag_handlers.py +220 -0
  29. cmdbox/cli/handlers/variable_handlers.py +272 -0
  30. cmdbox/cli/prompts/__init__.py +0 -0
  31. cmdbox/cli/prompts/completers.py +161 -0
  32. cmdbox/cli/prompts/prompts.py +108 -0
  33. cmdbox/cli/prompts/validators.py +46 -0
  34. cmdbox/cli/ui/__init__.py +0 -0
  35. cmdbox/cli/ui/console.py +31 -0
  36. cmdbox/cli/ui/editor.py +141 -0
  37. cmdbox/cli/ui/presenters/__init__.py +0 -0
  38. cmdbox/cli/ui/presenters/app_presenter.py +8 -0
  39. cmdbox/cli/ui/presenters/command_presenter.py +168 -0
  40. cmdbox/cli/ui/presenters/history_presenter.py +83 -0
  41. cmdbox/cli/ui/presenters/init_instructions.py +52 -0
  42. cmdbox/cli/ui/presenters/init_presenter.py +57 -0
  43. cmdbox/cli/ui/presenters/result_presenter.py +144 -0
  44. cmdbox/cli/ui/presenters/settings_presenter.py +130 -0
  45. cmdbox/cli/ui/presenters/tag_presenter.py +97 -0
  46. cmdbox/cli/ui/presenters/variable_presenter.py +103 -0
  47. cmdbox/cli/ui/primitives.py +410 -0
  48. cmdbox/cli/ui/theme.py +43 -0
  49. cmdbox/cli/ui/theme_builder.py +49 -0
  50. cmdbox/common/__init__.py +0 -0
  51. cmdbox/common/io.py +34 -0
  52. cmdbox/container.py +156 -0
  53. cmdbox/core/__init__.py +0 -0
  54. cmdbox/core/fields.py +48 -0
  55. cmdbox/core/paths.py +52 -0
  56. cmdbox/database.py +65 -0
  57. cmdbox/exceptions.py +10 -0
  58. cmdbox/init/__init__.py +0 -0
  59. cmdbox/init/detect.py +82 -0
  60. cmdbox/init/integrations/bash.sh +10 -0
  61. cmdbox/init/integrations/cmd.bat +14 -0
  62. cmdbox/init/integrations/fish.fish +11 -0
  63. cmdbox/init/integrations/powershell.ps1 +14 -0
  64. cmdbox/init/integrations/zsh.sh +10 -0
  65. cmdbox/init/io.py +68 -0
  66. cmdbox/init/specs.py +54 -0
  67. cmdbox/logging_setup/__init__.py +0 -0
  68. cmdbox/logging_setup/log_config.py +123 -0
  69. cmdbox/logging_setup/log_decorators.py +40 -0
  70. cmdbox/logging_setup/log_handlers.py +94 -0
  71. cmdbox/migrations/__init__.py +1 -0
  72. cmdbox/migrations/errors.py +10 -0
  73. cmdbox/migrations/runner.py +127 -0
  74. cmdbox/migrations/versions/__init__.py +0 -0
  75. cmdbox/models.py +165 -0
  76. cmdbox/repositories/__init__.py +0 -0
  77. cmdbox/repositories/base_repository.py +181 -0
  78. cmdbox/repositories/command_repository.py +391 -0
  79. cmdbox/repositories/errors.py +120 -0
  80. cmdbox/repositories/history_repository.py +155 -0
  81. cmdbox/repositories/results.py +37 -0
  82. cmdbox/repositories/tag_repository.py +91 -0
  83. cmdbox/repositories/validators.py +256 -0
  84. cmdbox/repositories/variable_repository.py +324 -0
  85. cmdbox/resolve/__init__.py +0 -0
  86. cmdbox/resolve/errors.py +65 -0
  87. cmdbox/resolve/lookup.py +137 -0
  88. cmdbox/resolve/resolver.py +402 -0
  89. cmdbox/resolve/type_defs.py +96 -0
  90. cmdbox/runtime/__init__.py +0 -0
  91. cmdbox/runtime/executor.py +454 -0
  92. cmdbox/runtime/results.py +25 -0
  93. cmdbox/runtime/shell.py +90 -0
  94. cmdbox/services/__init__.py +0 -0
  95. cmdbox/services/command_services.py +261 -0
  96. cmdbox/services/errors.py +37 -0
  97. cmdbox/services/field_selection.py +162 -0
  98. cmdbox/services/history_service.py +68 -0
  99. cmdbox/services/run_service.py +204 -0
  100. cmdbox/services/tag_services.py +134 -0
  101. cmdbox/services/variable_services.py +224 -0
  102. cmdbox/settings/__init__.py +0 -0
  103. cmdbox/settings/models.py +129 -0
  104. cmdbox/settings/settings_repository.py +36 -0
  105. cmdbox/settings/settings_service.py +144 -0
  106. cmdbox/version.py +1 -0
  107. cmdbox_cli-1.0.0.dist-info/METADATA +125 -0
  108. cmdbox_cli-1.0.0.dist-info/RECORD +112 -0
  109. cmdbox_cli-1.0.0.dist-info/WHEEL +5 -0
  110. cmdbox_cli-1.0.0.dist-info/entry_points.txt +2 -0
  111. cmdbox_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
  112. cmdbox_cli-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,204 @@
1
+ import json
2
+ from typing import Callable
3
+
4
+ from cmdbox.models import Command
5
+ from cmdbox.repositories.command_repository import CommandRepository
6
+ from cmdbox.repositories.history_repository import HistoryRepository
7
+ from cmdbox.resolve.resolver import Resolver
8
+ from cmdbox.resolve.type_defs import ResolveResult
9
+ from cmdbox.runtime.executor import Executor, RunContext
10
+ from cmdbox.runtime.results import ExecutionResult
11
+ from cmdbox.settings.models import Settings
12
+
13
+
14
+ class RunService:
15
+ """
16
+ Provides services for executing and previewing commands.
17
+
18
+ This class manages the execution of commands and their previews. It interacts with
19
+ a command repository, a resolver for processing commands, and an executor for
20
+ executing commands. The `run` method is used to execute a command, while the `preview`
21
+ method enables retrieving a resolved result of a command without executing it.
22
+
23
+ Attributes:
24
+ repo (CommandRepository): The repository that stores and provides access to
25
+ command definitions based on their alias.
26
+ resolver (Resolver): Encapsulates the logic required to resolve a command
27
+ template into its executable form.
28
+ executor (Executor): Responsible for executing resolved commands and
29
+ returning the results.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ repo: CommandRepository,
35
+ resolver: Resolver,
36
+ executor: Executor,
37
+ history_repo: HistoryRepository | None = None,
38
+ get_settings: Callable[[], Settings] | None = None,
39
+ ) -> None:
40
+ self._repo = repo
41
+ self._resolver = resolver
42
+ self._executor = executor
43
+ self._history_repo = history_repo
44
+ self._get_settings = get_settings
45
+
46
+ def run(
47
+ self,
48
+ command_alias: str,
49
+ ctx: RunContext | None = None,
50
+ runtime_vars: dict[str, str] | None = None,
51
+ ) -> ExecutionResult:
52
+ """
53
+ Executes a command based on the given alias.
54
+
55
+ This method retrieves a command associated with the provided alias, resolves its
56
+ template using a resolver, and then executes the resolved command text. Execution
57
+ context stored on the command (cwd, shell, env, timeout) is merged with any
58
+ runtime-supplied context, with runtime values taking precedence.
59
+
60
+ Args:
61
+ command_alias (str): The alias for the command to be executed.
62
+ ctx (RunContext | None): The context for running the command.
63
+ runtime_vars (dict[str, str] | None): Runtime variables to be used during command execution.
64
+
65
+ Returns:
66
+ ExecutionResult: The result of executing the command.
67
+ """
68
+ cmd = self._repo.get_by_alias(command_alias)
69
+ resolved_cmd = self._resolver.resolve(cmd.template, runtime_vars=runtime_vars)
70
+ ctx = self.build_context(cmd, ctx)
71
+ result = self._executor.run(resolved_cmd.text, ctx=ctx)
72
+ self._repo.record_use(cmd.id)
73
+ self.record_history(
74
+ alias=command_alias,
75
+ template=cmd.template,
76
+ resolved=resolved_cmd.text,
77
+ runtime_vars=runtime_vars,
78
+ exit_code=result.exit_code,
79
+ )
80
+ return result
81
+
82
+ def preview(
83
+ self,
84
+ command_alias: str,
85
+ runtime_vars: dict[str, str] | None = None,
86
+ ctx: RunContext | None = None,
87
+ ) -> tuple[ResolveResult, RunContext | None]:
88
+ """
89
+ Retrieves and resolves a command template based on its alias.
90
+
91
+ This method takes a command alias, retrieves the associated command template,
92
+ and resolves it to produce a complete command configuration or data structure.
93
+ This resolved template is then returned as a `ResolveResult`.
94
+
95
+ Args:
96
+ command_alias (str): The alias of the command to be resolved.
97
+ runtime_vars (dict[str, str] | None): Runtime variables to be used during command resolution.
98
+ ctx (RunContext | None): The context in which the command is being executed.
99
+
100
+ Returns:
101
+ tuple[ResolveResult, RunContext | None]: The resolved result of the command template and the effective context.
102
+ """
103
+ cmd = self._repo.get_by_alias(command_alias)
104
+ resolved = self._resolver.resolve(cmd.template, runtime_vars=runtime_vars)
105
+ effective_ctx = self.build_context(cmd, ctx)
106
+ return resolved, effective_ctx
107
+
108
+ def record_history(
109
+ self,
110
+ *,
111
+ alias: str,
112
+ template: str,
113
+ resolved: str,
114
+ runtime_vars: dict[str, str] | None = None,
115
+ exit_code: int | None = None,
116
+ ) -> None:
117
+ """
118
+ Records command execution history if the history feature is enabled and
119
+ properly configured. This function interacts with an underlying
120
+ repository to store details about the executed command including alias,
121
+ template, resolved string, runtime variables, and exit code.
122
+
123
+ Args:
124
+ alias (str): The alias or the name of the command executed.
125
+ template (str): The template string representing the command.
126
+ resolved (str): The fully resolved string of the executed command.
127
+ runtime_vars (dict[str, str] | None): Runtime variables used during
128
+ the execution of the command. Defaults to None.
129
+ exit_code (int | None): Exit code that indicates the result of the
130
+ command execution. Defaults to None.
131
+ """
132
+ if not self._history_repo or not self._get_settings:
133
+ return
134
+ settings = self._get_settings()
135
+ if not settings.history.enabled:
136
+ return
137
+ self._history_repo.record(
138
+ alias=alias,
139
+ template=template,
140
+ resolved=resolved,
141
+ variables_used=runtime_vars or None,
142
+ exit_code=exit_code,
143
+ limit=settings.history.limit_per_command,
144
+ )
145
+
146
+ def collect_missing_vars(
147
+ self, command_alias: str, runtime_vars: dict[str, str] | None = None
148
+ ) -> list[str]:
149
+ cmd = self._repo.get_by_alias(command_alias)
150
+ missing = self._resolver.collect_missing_vars(
151
+ cmd.template, runtime_vars=runtime_vars
152
+ )
153
+ return missing
154
+
155
+ def build_context(
156
+ self, cmd: Command, runtime_ctx: RunContext | None
157
+ ) -> RunContext | None:
158
+ """
159
+ Constructs a runtime context by merging command-level settings with runtime-level
160
+ settings. Resolves priority based on input from `runtime_ctx` when available, or
161
+ falls back to `cmd` for default values. If no significant configuration changes
162
+ are detected, returns `None`.
163
+
164
+ Args:
165
+ cmd (Command): The command object containing default execution settings, including
166
+ environment variables, working directory, shell, timeout, and more.
167
+ runtime_ctx (RunContext | None): An optional runtime context object that may override
168
+ specific command-level settings, such as environment variables and capture
169
+ preferences.
170
+
171
+ Returns:
172
+ RunContext | None: Returns a `RunContext` object with the resulting configuration
173
+ or `None` if no significant changes are produced by the merge.
174
+ """
175
+ stored_env = json.loads(cmd.env) if cmd.env else {}
176
+ runtime_env = getattr(runtime_ctx, "env", None) or {}
177
+ merged_env = {**stored_env, **runtime_env} or None
178
+
179
+ cwd = (runtime_ctx.cwd if runtime_ctx and runtime_ctx.cwd else None) or cmd.cwd
180
+ shell = (
181
+ runtime_ctx.shell if runtime_ctx and runtime_ctx.shell else None
182
+ ) or cmd.shell
183
+ timeout = (
184
+ runtime_ctx.timeout
185
+ if (runtime_ctx and runtime_ctx.timeout is not None)
186
+ else cmd.timeout
187
+ )
188
+
189
+ capture = runtime_ctx.capture if runtime_ctx else False
190
+ emit = runtime_ctx.emit if runtime_ctx else False
191
+ verbose = runtime_ctx.verbose if runtime_ctx else False
192
+
193
+ if not any([cwd, shell, merged_env, timeout, capture, emit, verbose]):
194
+ return None
195
+
196
+ return RunContext(
197
+ cwd=cwd,
198
+ shell=shell,
199
+ env=merged_env,
200
+ timeout=timeout,
201
+ capture=capture,
202
+ emit=emit,
203
+ verbose=verbose,
204
+ )
@@ -0,0 +1,134 @@
1
+ from typing import Sequence
2
+
3
+ from cmdbox.models import Tag
4
+ from cmdbox.repositories.tag_repository import TagRepository
5
+
6
+
7
+ class TagServices:
8
+ """
9
+ Provides services for managing tags.
10
+
11
+ The `TagServices` class encapsulates the logic for working with tags,
12
+ offering methods to create, update, delete, and retrieve tag records.
13
+ It also supports additional functionality such as searching and listing tags
14
+ with sorting and limit options.
15
+
16
+ Attributes:
17
+ tag_repository (TagRepository): Repository for managing tag records.
18
+ """
19
+
20
+ def __init__(self, tag_repository: TagRepository):
21
+ self._repo = tag_repository
22
+
23
+ def create_tag(self, name: str, description: str | None = None) -> Tag:
24
+ """
25
+ Creates a new tag by storing the provided name and optional description
26
+ into the database.
27
+
28
+ Args:
29
+ name (str): The name of the tag to be created.
30
+ description (str | None): An optional description for the tag. Defaults to None.
31
+
32
+ Returns:
33
+ Tag: The created tag record.
34
+ """
35
+ return self._repo.create(name=name, description=description)
36
+
37
+ def update_tag(self, name_: str, **fields) -> Tag:
38
+ """
39
+ Updates an existing tag by its name with new field values.
40
+
41
+ Retrieves the tag corresponding to the given name and updates it
42
+ with the provided fields. The update is performed using the repository.
43
+
44
+ Args:
45
+ name_ (str): The name of the tag to update.
46
+ **fields: Arbitrary field values to update on the tag.
47
+
48
+ Returns:
49
+ Tag: The updated tag object.
50
+ """
51
+ tag = self._repo.get_by_name(name_)
52
+ return self._repo.update(tag, **fields)
53
+
54
+ def delete_tag(self, name: str) -> bool:
55
+ """
56
+ Deletes a tag by its name.
57
+
58
+ This method removes a tag record from the repository that matches the
59
+ provided name.
60
+
61
+ Args:
62
+ name: The name of the tag to delete.
63
+
64
+ Returns:
65
+ bool: True if the tag was deleted successfully, False otherwise.
66
+ """
67
+ tag = self._repo.get_by_name(name)
68
+ return self._repo.delete(tag)
69
+
70
+ def get_tag(self, name: str) -> Tag:
71
+ """
72
+ Retrieves a tag by its name.
73
+
74
+ This method fetches a tag object associated with the given name from the
75
+ repository.
76
+
77
+ Args:
78
+ name (str): The name of the tag to retrieve.
79
+
80
+ Returns:
81
+ Tag: The tag object associated with the given name.
82
+ """
83
+ return self._repo.get_by_name(name)
84
+
85
+ def get_tag_by_id(self, tag_id: int) -> Tag:
86
+ tag = self._repo.get_by_id(tag_id)
87
+ return tag
88
+
89
+ def list_tags(
90
+ self,
91
+ order_by: str | Sequence[str] = "name",
92
+ limit: int = 25,
93
+ ) -> list[Tag]:
94
+ """
95
+ Lists tags with sorting and limit options.
96
+
97
+ This function fetches a list of tags from the repository. The results can
98
+ be sorted and limited based on the provided arguments.
99
+
100
+ Args:
101
+ order_by (str | Sequence[str]): Specifies the field(s) to sort the results by.
102
+ Default is "name".
103
+ limit (int, optional): The maximum number of tags to return. Default is 25.
104
+
105
+ Returns:
106
+ list[Tag]: A list of tags sorted according to the specified criteria.
107
+ """
108
+ return self._repo.list_all(order_by, limit)
109
+
110
+ def search(
111
+ self,
112
+ query: str,
113
+ fields: str | Sequence[str] | None = None,
114
+ limit: int = 25,
115
+ ) -> list[Tag]:
116
+ """
117
+ Searches for tags that match the given query across specified fields.
118
+
119
+ This method allows you to perform a search within the data repository for tags
120
+ that match the provided query string in the specified fields. It returns a list of
121
+ tags that satisfy the search criteria.
122
+
123
+ Args:
124
+ query (str): The search term used for matching against the repository.
125
+ fields (str | Sequence[str] | None): The fields to perform the search within. If
126
+ None, defaults to ("name", "description").
127
+ limit (int, optional): The maximum number of tags to return. Default is 25.
128
+
129
+ Returns:
130
+ list[Tag]: A list of Tag objects that match the search query.
131
+ """
132
+ if not fields:
133
+ fields = ("name", "description")
134
+ return self._repo.search(query, fields, limit)
@@ -0,0 +1,224 @@
1
+ from typing import Sequence
2
+
3
+ from cmdbox.models import Tag, Variable
4
+ from cmdbox.repositories.variable_repository import VariableRepository
5
+ from cmdbox.database import db
6
+ from cmdbox.repositories.results import TagAttachResult, TagDetachResult
7
+ from cmdbox.repositories.tag_repository import TagRepository
8
+
9
+
10
+ class VariableServices:
11
+ """
12
+ Provides services for managing variables and their associated tags.
13
+
14
+ The `VariableServices` class encapsulates the logic for working with variables and
15
+ tags, offering methods to create, update, delete, and retrieve variable records
16
+ and their tag associations. It also supports additional functionality such as
17
+ searching, tagging, and listing variables with filtering and sorting options.
18
+
19
+ Attributes:
20
+ variable_repository (VariableRepository): Repository for managing variable records.
21
+ tag_repository (TagRepository): Repository for managing tag-related operations.
22
+ """
23
+
24
+ def __init__(
25
+ self, variable_repository: VariableRepository, tag_repository: TagRepository
26
+ ):
27
+ self._repo = variable_repository
28
+ self._tag_repo = tag_repository
29
+
30
+ def create_variable(
31
+ self,
32
+ name: str,
33
+ value: str,
34
+ tags: list[str] | None = None,
35
+ ) -> Variable:
36
+ """
37
+ Creates a new variable by storing the provided name, value, and optional tags
38
+ into the database. If tags are provided, they are associated with the created
39
+ variable.
40
+
41
+ Args:
42
+ name (str): The name of the variable to be created.
43
+ value (str): The value associated with the variable.
44
+ tags (list[str] | None): An optional list of tags to associate with the variable.
45
+ Defaults to None.
46
+
47
+ Returns:
48
+ Variable: The created variable record.
49
+ """
50
+ with db.atomic():
51
+ tags = self._get_tags(tags)
52
+ var = self._repo.create(name=name, value=value)
53
+ if tags:
54
+ self._repo.add_tags(var, tags)
55
+ return var
56
+
57
+ def update_variable(self, name_: str, **fields) -> Variable:
58
+ """
59
+ Updates an existing variable by its name with new field values.
60
+
61
+ Retrieves the variable corresponding to the given name and updates it
62
+ with the provided fields. The update is performed using the repository.
63
+
64
+ Args:
65
+ name_ (str): The name of the variable to update.
66
+ **fields: Arbitrary field values to update on the variable.
67
+
68
+ Returns:
69
+ Variable: The updated variable object.
70
+ """
71
+ var = self._repo.get_by_name(name_)
72
+ return self._repo.update(var, **fields)
73
+
74
+ def delete_variable(self, name: str) -> bool:
75
+ """
76
+ Deletes a variable by its name.
77
+
78
+ This method removes a variable record from the repository that matches the
79
+ provided name.
80
+
81
+ Args:
82
+ name: The name of the variable to delete.
83
+
84
+ Returns:
85
+ bool: True if the variable was deleted successfully, False otherwise.
86
+ """
87
+ var = self._repo.get_by_name(name)
88
+ return self._repo.delete(var)
89
+
90
+ def add_tags(self, name: str, tags: list[str]) -> TagAttachResult:
91
+ """
92
+ Adds specified tags to an existing variable name.
93
+
94
+ The method retrieves a variable object associated with the given name and
95
+ applies the provided tags to the variable.
96
+
97
+ Args:
98
+ name (str): The name identifying the variable to which tags are to be
99
+ attached.
100
+ tags (list[str]): A list of tags to attach to the variable.
101
+
102
+ Returns:
103
+ TagAttachResult: The result of the tag attachment operation, indicating
104
+ whether the tags were successfully attached to the variable.
105
+ """
106
+ var = self._repo.get_by_name(name)
107
+ tags = self._get_tags(tags)
108
+ return self._repo.add_tags(var, tags)
109
+
110
+ def remove_tags(self, name: str, tags: list[str]) -> TagDetachResult:
111
+ """
112
+ Removes specific tags associated with a variable name.
113
+
114
+ This method retrieves a variable by its name and detaches the specified tags
115
+ from it. The updated tag associations will be handled by the repository.
116
+
117
+ Args:
118
+ name: The unique identifier for the variable to update.
119
+ tags: A list of tags to be removed from the specified variable.
120
+
121
+ Returns:
122
+ TagDetachResult: An object representing the result of tag detachment.
123
+ """
124
+ var = self._repo.get_by_name(name)
125
+ tags = self._get_tags(tags)
126
+ return self._repo.remove_tags(var, tags)
127
+
128
+ def get_variable(self, name: str) -> Variable:
129
+ """
130
+ Retrieves a variable by its name.
131
+
132
+ This method fetches a variable object associated with the given name from the
133
+ repository.
134
+
135
+ Args:
136
+ name (str): The name of the variable to retrieve.
137
+
138
+ Returns:
139
+ Variable: The variable object associated with the given name.
140
+ """
141
+ var = self._repo.get_by_name(name)
142
+ return var
143
+
144
+ def get_variable_by_id(self, var_id: int) -> Variable:
145
+ var = self._repo.get_by_id(var_id)
146
+ return var
147
+
148
+ def list_variables(
149
+ self,
150
+ order_by: str | Sequence[str] = "name",
151
+ tags: Sequence[str] | None = None,
152
+ limit: int = 25,
153
+ ) -> list[Variable]:
154
+ """
155
+ Lists variables, optionally filtered by tags, with sorting and limit options.
156
+
157
+ This function fetches a list of variables, either filtered by specific tags
158
+ or returning all available variables. The results can be sorted and limited
159
+ based on the provided arguments.
160
+
161
+ Args:
162
+ order_by (str | Sequence[str]): Specifies the field(s) to sort the results by.
163
+ Default is "name".
164
+ tags (Sequence[str], optional): A list of tags to filter the variables. If
165
+ provided, only variables matching the tags will be included.
166
+ limit (int, optional): The maximum number of commands to return. Default is 25.
167
+
168
+ Returns:
169
+ list[Variable]: A list of variables matching the provided filters and sorted
170
+ according to the specified criteria.
171
+ """
172
+ if tags:
173
+ tags = self._get_tags(tags)
174
+ return self._repo.list_by_tag(tags, order_by, limit)
175
+ return self._repo.list_all(order_by, limit)
176
+
177
+ def search(
178
+ self,
179
+ query: str,
180
+ fields: str | Sequence[str] | None = None,
181
+ limit: int = 25,
182
+ ) -> list[Variable]:
183
+ """
184
+ Searches for variables that match the given query across specified fields.
185
+
186
+ This method allows you to perform a search within the data repository for variables
187
+ that match the provided query string in the specified fields. It returns a list of
188
+ variables that satisfy the search criteria.
189
+
190
+ Args:
191
+ query (str): The search term used for matching against the repository.
192
+ fields (str | Sequence[str] | None): The fields to perform the search within. Defaults
193
+ to ("name", "value"). If None, no specific fields are targeted.
194
+ limit (int): The maximum number of results to return. Defaults to 25.
195
+
196
+ Returns:
197
+ list[Variable]: A list of Variable objects that match the search query.
198
+ """
199
+ if not fields:
200
+ fields = ("name", "value")
201
+ return self._repo.search(query, fields=fields, limit=limit)
202
+
203
+ def _get_tags(self, tags: Sequence[str] | None) -> list[Tag]:
204
+ """
205
+ Fetches a list of Tag objects based on the provided tag names.
206
+
207
+ This method takes a list of tag names as input and retrieves the corresponding
208
+ Tag objects from the tag repository. The resulting list of Tag objects is then
209
+ returned.
210
+
211
+ Args:
212
+ tags (list[str] | None): A list of string representing the names of the tags to
213
+ retrieve.
214
+
215
+ Returns:
216
+ list[Tag]: A list of Tag objects corresponding to the specified tag names.
217
+ """
218
+ if tags is None:
219
+ return []
220
+ ret_tags: list[Tag] = []
221
+ for name in tags:
222
+ tag = self._tag_repo.get_by_name(name)
223
+ ret_tags.append(tag)
224
+ return ret_tags
File without changes
@@ -0,0 +1,129 @@
1
+ from dataclasses import dataclass, field
2
+
3
+
4
+ @dataclass(frozen=True)
5
+ class UIStyle:
6
+ # Core
7
+ title: str = "bold"
8
+ subtitle: str = "dim"
9
+ muted: str = "dim"
10
+ border: str = "dim"
11
+ panel_title: str = "bold"
12
+
13
+ # Tables
14
+ table_header: str = "bold"
15
+ caption: str = "dim"
16
+
17
+ # Key/value panels
18
+ kv_key: str = "dim"
19
+ kv_value: str = ""
20
+
21
+ # Status
22
+ success: str = "green"
23
+ info: str = "cyan"
24
+ warning: str = "yellow"
25
+ error: str = "red"
26
+ debug: str = "dim blue"
27
+
28
+ # Code
29
+ code: str = "cyan"
30
+ code_inline: str = "cyan"
31
+ code_block: str = "dim cyan"
32
+
33
+ # Entity accents
34
+ entity_name: str = "bold"
35
+ entity_id: str = "magenta"
36
+ entity_count: str = "bold"
37
+ entity_time: str = "dim"
38
+
39
+ # Tags
40
+ tag_pill: str = "bold white on dark_green"
41
+ tag_pill_muted: str = "white on grey23"
42
+
43
+ # Execution and previews
44
+ run_command: str = "cyan"
45
+ run_stdout: str = ""
46
+ run_stderr: str = "bold red"
47
+ trace_kind: str = "dim"
48
+ trace_key: str = "magenta"
49
+ trace_value: str = "cyan"
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class DefaultFields:
54
+ command_output: list[str] = field(
55
+ default_factory=lambda: ["alias", "template", "description"]
56
+ )
57
+ command_search: list[str] = field(
58
+ default_factory=lambda: ["alias", "template", "description"]
59
+ )
60
+ variable_output: list[str] = field(default_factory=lambda: ["name", "value"])
61
+ variable_search: list[str] = field(default_factory=lambda: ["name", "value"])
62
+ tag_output: list[str] = field(default_factory=lambda: ["name", "description"])
63
+ tag_search: list[str] = field(default_factory=lambda: ["name", "description"])
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class FieldAliases:
68
+ alias_mapping: dict[str, list[str]] = field(
69
+ default_factory=lambda: {
70
+ "alias": ["a", "al"],
71
+ "template": ["t", "temp"],
72
+ "description": ["d", "desc"],
73
+ "date_created": ["dc", "created"],
74
+ "last_updated": ["lu", "updated"],
75
+ "used": ["u"],
76
+ "last_used": ["lu"],
77
+ }
78
+ )
79
+
80
+ @property
81
+ def alias_map(self) -> dict[str, str]:
82
+ out: dict[str, str] = {}
83
+ for field_name, aliases in self.alias_mapping.items():
84
+ for alias in aliases:
85
+ out[alias.lower()] = field_name
86
+ return out
87
+
88
+
89
+ @dataclass(frozen=True)
90
+ class UiSettings:
91
+ use_color: bool = True
92
+ colors: UIStyle = UIStyle()
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class ExecutionSettings:
97
+ default_shell: str = "auto" # auto | bash | zsh | pwsh | cmd
98
+ capture_output: bool = False
99
+ default_verbose: bool = False
100
+
101
+
102
+ @dataclass(frozen=True)
103
+ class LoggingFileSettings:
104
+ enabled: bool = False
105
+ level: str = "INFO"
106
+ max_bytes: int = 1_000_000
107
+ backups: int = 3
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class LoggingSettings:
112
+ console_level: str = "WARNING"
113
+ file: LoggingFileSettings = LoggingFileSettings()
114
+
115
+
116
+ @dataclass(frozen=True)
117
+ class HistorySettings:
118
+ enabled: bool = True
119
+ limit_per_command: int = 100 # 0 = unlimited
120
+
121
+
122
+ @dataclass(frozen=True)
123
+ class Settings:
124
+ ui: UiSettings = UiSettings()
125
+ execution_settings: ExecutionSettings = ExecutionSettings()
126
+ default_fields: DefaultFields = DefaultFields()
127
+ field_aliases: FieldAliases = FieldAliases()
128
+ logging: LoggingSettings = LoggingSettings()
129
+ history: HistorySettings = HistorySettings()