mammoth-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.
- mammoth_cli/__init__.py +14 -0
- mammoth_cli/__main__.py +27 -0
- mammoth_cli/app.py +565 -0
- mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +109 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +27 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/input.md +24 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +35 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +30 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +28 -0
- mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +26 -0
- mammoth_cli/commands/__init__.py +29 -0
- mammoth_cli/commands/activity.py +123 -0
- mammoth_cli/commands/addon.py +173 -0
- mammoth_cli/commands/agent.py +199 -0
- mammoth_cli/commands/ai.py +274 -0
- mammoth_cli/commands/annotation.py +166 -0
- mammoth_cli/commands/auth.py +567 -0
- mammoth_cli/commands/automation.py +245 -0
- mammoth_cli/commands/batch.py +204 -0
- mammoth_cli/commands/billing.py +462 -0
- mammoth_cli/commands/browse.py +151 -0
- mammoth_cli/commands/capability.py +43 -0
- mammoth_cli/commands/client_app.py +233 -0
- mammoth_cli/commands/completion.py +82 -0
- mammoth_cli/commands/config.py +309 -0
- mammoth_cli/commands/connector.py +464 -0
- mammoth_cli/commands/context.py +205 -0
- mammoth_cli/commands/dashboard.py +419 -0
- mammoth_cli/commands/data_app.py +239 -0
- mammoth_cli/commands/dataset.py +332 -0
- mammoth_cli/commands/doctor.py +90 -0
- mammoth_cli/commands/external_key.py +163 -0
- mammoth_cli/commands/file.py +243 -0
- mammoth_cli/commands/folder.py +224 -0
- mammoth_cli/commands/job.py +147 -0
- mammoth_cli/commands/notification.py +201 -0
- mammoth_cli/commands/parameter.py +284 -0
- mammoth_cli/commands/project.py +327 -0
- mammoth_cli/commands/registry.py +587 -0
- mammoth_cli/commands/report.py +61 -0
- mammoth_cli/commands/schedule.py +214 -0
- mammoth_cli/commands/schema.py +271 -0
- mammoth_cli/commands/skill.py +59 -0
- mammoth_cli/commands/snippet.py +200 -0
- mammoth_cli/commands/support.py +800 -0
- mammoth_cli/commands/template.py +134 -0
- mammoth_cli/commands/trash.py +115 -0
- mammoth_cli/commands/user.py +190 -0
- mammoth_cli/commands/view.py +1143 -0
- mammoth_cli/commands/view_ops.py +596 -0
- mammoth_cli/commands/webhook.py +235 -0
- mammoth_cli/commands/workflow.py +340 -0
- mammoth_cli/commands/workspace.py +359 -0
- mammoth_cli/context/__init__.py +1 -0
- mammoth_cli/context/credentials.py +223 -0
- mammoth_cli/context/endpoint.py +57 -0
- mammoth_cli/context/profiles.py +370 -0
- mammoth_cli/context/resolver.py +240 -0
- mammoth_cli/contracts/__init__.py +1 -0
- mammoth_cli/contracts/auth.py +32 -0
- mammoth_cli/errors/__init__.py +1 -0
- mammoth_cli/errors/envelope.py +144 -0
- mammoth_cli/manifest/__init__.py +1 -0
- mammoth_cli/manifest/loader.py +96 -0
- mammoth_cli/messages/__init__.py +1 -0
- mammoth_cli/output/__init__.py +1 -0
- mammoth_cli/output/envelope.py +42 -0
- mammoth_cli/output/normalize.py +84 -0
- mammoth_cli/output/policy.py +71 -0
- mammoth_cli/output/render.py +83 -0
- mammoth_cli/py.typed +0 -0
- mammoth_cli/runtime/__init__.py +0 -0
- mammoth_cli/runtime/confirm.py +124 -0
- mammoth_cli/runtime/executor.py +126 -0
- mammoth_cli/runtime/input_loader.py +140 -0
- mammoth_cli/runtime/invocation.py +87 -0
- mammoth_cli/runtime/options.py +172 -0
- mammoth_cli/runtime/session.py +82 -0
- mammoth_cli/runtime/strict.py +240 -0
- mammoth_cli/runtime/validate.py +120 -0
- mammoth_cli/services/__init__.py +1 -0
- mammoth_cli/services/argspec.py +301 -0
- mammoth_cli/services/coerce.py +172 -0
- mammoth_cli/services/conditions.py +85 -0
- mammoth_cli/services/dispatch.py +63 -0
- mammoth_cli/services/factory.py +45 -0
- mammoth_cli/services/input_fields.py +44 -0
- mammoth_cli/services/mapping.py +74 -0
- mammoth_cli/services/openapi_types.py +166 -0
- mammoth_cli/services/positionals.py +408 -0
- mammoth_cli/services/protocol.py +123 -0
- mammoth_cli/services/sdk_service.py +274 -0
- mammoth_cli/services/testing.py +153 -0
- mammoth_cli/services/type_system.py +427 -0
- mammoth_cli/skills/__init__.py +1 -0
- mammoth_cli/skills/installer.py +321 -0
- mammoth_cli/testing.py +55 -0
- mammoth_cli-1.0.0.dist-info/METADATA +92 -0
- mammoth_cli-1.0.0.dist-info/RECORD +150 -0
- mammoth_cli-1.0.0.dist-info/WHEEL +4 -0
- mammoth_cli-1.0.0.dist-info/entry_points.txt +3 -0
- mammoth_cli-1.0.0.dist-info/licenses/LICENSE +23 -0
- spec/manifests/_sdk_introspection.json +1459 -0
- spec/manifests/commands/activity.yaml +72 -0
- spec/manifests/commands/addon.yaml +239 -0
- spec/manifests/commands/agent.yaml +192 -0
- spec/manifests/commands/ai.yaml +152 -0
- spec/manifests/commands/annotation.yaml +191 -0
- spec/manifests/commands/auth.yaml +101 -0
- spec/manifests/commands/automation.yaml +273 -0
- spec/manifests/commands/batch.yaml +262 -0
- spec/manifests/commands/billing.yaml +813 -0
- spec/manifests/commands/browse.yaml +145 -0
- spec/manifests/commands/capability.yaml +74 -0
- spec/manifests/commands/client-app.yaml +193 -0
- spec/manifests/commands/completion.yaml +68 -0
- spec/manifests/commands/config.yaml +134 -0
- spec/manifests/commands/connector.yaml +944 -0
- spec/manifests/commands/context.yaml +101 -0
- spec/manifests/commands/dashboard.yaml +3595 -0
- spec/manifests/commands/data-app.yaml +485 -0
- spec/manifests/commands/dataset.yaml +580 -0
- spec/manifests/commands/doctor.yaml +35 -0
- spec/manifests/commands/external-key.yaml +151 -0
- spec/manifests/commands/file.yaml +341 -0
- spec/manifests/commands/folder.yaml +334 -0
- spec/manifests/commands/job.yaml +151 -0
- spec/manifests/commands/notification.yaml +188 -0
- spec/manifests/commands/parameter.yaml +527 -0
- spec/manifests/commands/project.yaml +669 -0
- spec/manifests/commands/report.yaml +36 -0
- spec/manifests/commands/schedule.yaml +194 -0
- spec/manifests/commands/schema.yaml +74 -0
- spec/manifests/commands/skill.yaml +167 -0
- spec/manifests/commands/snippet.yaml +311 -0
- spec/manifests/commands/support.yaml +1805 -0
- spec/manifests/commands/template.yaml +191 -0
- spec/manifests/commands/trash.yaml +105 -0
- spec/manifests/commands/user.yaml +280 -0
- spec/manifests/commands/version.yaml +35 -0
- spec/manifests/commands/view.yaml +4232 -0
- spec/manifests/commands/webhook.yaml +261 -0
- spec/manifests/commands/workflow.yaml +622 -0
- spec/manifests/commands/workspace.yaml +705 -0
- spec/manifests/openapi-operations.yaml +8853 -0
- spec/manifests/schema-v1.json +212 -0
- spec/manifests/sdk-catalog.source.yaml +4069 -0
- spec/manifests/sdk-methods.yaml +6971 -0
- spec/openapi/metadata.json +10 -0
- spec/openapi/openapi.json +1 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Handlers for the ``automation`` command family (workspace-scoped).
|
|
2
|
+
|
|
3
|
+
Automations belong to the authenticated workspace; the SDK client already
|
|
4
|
+
carries the workspace (and active project) context, so handlers never
|
|
5
|
+
construct or forward those ids explicitly. An automation id always comes from
|
|
6
|
+
the first CLI positional argument; structured fields (``tasks``,
|
|
7
|
+
``conditions``, ``patch``, etc.) come from the strict ``--input`` document.
|
|
8
|
+
Handlers dispatch through the generic
|
|
9
|
+
:meth:`~mammoth_cli.services.protocol.MammothService.call` seam to the public
|
|
10
|
+
SDK method named by the command's reviewed manifest ``sdk_symbol``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from mammoth_cli.errors.envelope import (
|
|
18
|
+
CODE_INVALID_ARGUMENT,
|
|
19
|
+
CODE_MISSING_ARGUMENT,
|
|
20
|
+
CODE_MISSING_FIELD,
|
|
21
|
+
CODE_SDK_SYMBOL_UNRESOLVED,
|
|
22
|
+
EXIT_USAGE,
|
|
23
|
+
CliError,
|
|
24
|
+
)
|
|
25
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
26
|
+
from mammoth_cli.runtime.confirm import (
|
|
27
|
+
POLICY_PROMPT_OR_YES,
|
|
28
|
+
POLICY_YES_ALWAYS,
|
|
29
|
+
enforce_confirmation,
|
|
30
|
+
)
|
|
31
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
32
|
+
from mammoth_cli.runtime.session import open_service, resolved_project
|
|
33
|
+
|
|
34
|
+
HandlerResult = tuple[Any, dict[str, Any]]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _symbol(invocation: Invocation) -> str:
|
|
38
|
+
"""Return the reviewed backing SDK symbol for this command.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
invocation: The current command's resolved global options.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The dotted SDK symbol recorded in the reviewed manifest.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
CliError: ``sdk_symbol_unresolved`` when no symbol is recorded.
|
|
48
|
+
"""
|
|
49
|
+
record = command_by_id(invocation.command_id)
|
|
50
|
+
if record is None or not record.get("sdk_symbol"):
|
|
51
|
+
raise CliError(
|
|
52
|
+
code=CODE_SDK_SYMBOL_UNRESOLVED,
|
|
53
|
+
message=f"No SDK symbol is recorded for '{invocation.command_id}'.",
|
|
54
|
+
exit_status=EXIT_USAGE,
|
|
55
|
+
)
|
|
56
|
+
return str(record["sdk_symbol"])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _string_positional(invocation: Invocation) -> str | None:
|
|
60
|
+
"""Return the first positional argument, or None if absent.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
invocation: The current command's resolved global options.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The first positional argument, or None when there is none.
|
|
67
|
+
"""
|
|
68
|
+
return invocation.extra_args[0] if invocation.extra_args else None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _require_int_positional(invocation: Invocation, name: str) -> int:
|
|
72
|
+
"""Return the first positional argument parsed as a required int.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
invocation: The current command's resolved global options.
|
|
76
|
+
name: A human-readable name for the argument, used in error messages.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The parsed integer id.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
CliError: ``missing_argument`` when no positional was given;
|
|
83
|
+
``invalid_argument`` when it is not an integer.
|
|
84
|
+
"""
|
|
85
|
+
if not invocation.extra_args:
|
|
86
|
+
raise CliError(
|
|
87
|
+
code=CODE_MISSING_ARGUMENT,
|
|
88
|
+
message=f"This command requires a {name} argument.",
|
|
89
|
+
exit_status=EXIT_USAGE,
|
|
90
|
+
hint=f"Pass the {name} as a positional argument.",
|
|
91
|
+
)
|
|
92
|
+
raw = invocation.extra_args[0]
|
|
93
|
+
try:
|
|
94
|
+
return int(raw)
|
|
95
|
+
except ValueError as exc:
|
|
96
|
+
raise CliError(
|
|
97
|
+
code=CODE_INVALID_ARGUMENT,
|
|
98
|
+
message=f"The {name} argument '{raw}' is not an integer.",
|
|
99
|
+
exit_status=EXIT_USAGE,
|
|
100
|
+
) from exc
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _require_field(document: dict[str, Any] | None, field: str) -> Any:
|
|
104
|
+
"""Return a required field from the ``--input`` document, or raise usage.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
document: The parsed ``--input`` document, or None when absent.
|
|
108
|
+
field: The required field name.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
The field's value.
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
CliError: ``missing_field`` when the document is absent or lacks
|
|
115
|
+
``field``.
|
|
116
|
+
"""
|
|
117
|
+
if document is None or field not in document:
|
|
118
|
+
raise CliError(
|
|
119
|
+
code=CODE_MISSING_FIELD,
|
|
120
|
+
message=f"This command requires the '{field}' input field.",
|
|
121
|
+
exit_status=EXIT_USAGE,
|
|
122
|
+
hint=f"Pass it via --input, for example: --input '{{\"{field}\": ...}}'.",
|
|
123
|
+
)
|
|
124
|
+
return document[field]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _forward_optional(
|
|
128
|
+
document: dict[str, Any], kwargs: dict[str, Any], fields: tuple[str, ...]
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Copy each present field from ``document`` into ``kwargs``, unchanged.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
document: The parsed ``--input`` document.
|
|
134
|
+
kwargs: The keyword-argument mapping being built for the SDK call.
|
|
135
|
+
fields: The optional field names to forward when present.
|
|
136
|
+
"""
|
|
137
|
+
for field in fields:
|
|
138
|
+
if field in document:
|
|
139
|
+
kwargs[field] = document[field]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _meta(invocation: Invocation, workspace_id: int, project_id: int | None) -> dict[str, Any]:
|
|
143
|
+
"""Build the common envelope metadata for an automation command.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
invocation: The current command's resolved global options.
|
|
147
|
+
workspace_id: The authenticated workspace id.
|
|
148
|
+
project_id: The active project id, or None when not resolved.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The envelope metadata mapping.
|
|
152
|
+
"""
|
|
153
|
+
return {
|
|
154
|
+
"profile": invocation.profile,
|
|
155
|
+
"workspace_id": workspace_id,
|
|
156
|
+
"project_id": project_id,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def automation_list(invocation: Invocation) -> HandlerResult:
|
|
161
|
+
"""List all automations in the active workspace/project."""
|
|
162
|
+
with open_service(invocation) as (service, auth):
|
|
163
|
+
data = service.call(_symbol(invocation))
|
|
164
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def automation_get(invocation: Invocation) -> HandlerResult:
|
|
168
|
+
"""Get one automation by id. Automation id is positional."""
|
|
169
|
+
automation_id = _require_int_positional(invocation, "automation id")
|
|
170
|
+
with open_service(invocation) as (service, auth):
|
|
171
|
+
data = service.call(_symbol(invocation), automation_id=automation_id)
|
|
172
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def automation_trash(invocation: Invocation) -> HandlerResult:
|
|
176
|
+
"""Move one automation to trash (reversible). Automation id is positional."""
|
|
177
|
+
automation_id = _require_int_positional(invocation, "automation id")
|
|
178
|
+
with open_service(invocation) as (service, auth):
|
|
179
|
+
data = service.call(_symbol(invocation), automation_id=automation_id)
|
|
180
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def automation_restore(invocation: Invocation) -> HandlerResult:
|
|
184
|
+
"""Restore one trashed automation. Automation id is positional."""
|
|
185
|
+
automation_id = _require_int_positional(invocation, "automation id")
|
|
186
|
+
with open_service(invocation) as (service, auth):
|
|
187
|
+
data = service.call(_symbol(invocation), automation_id=automation_id)
|
|
188
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def automation_delete(invocation: Invocation) -> HandlerResult:
|
|
192
|
+
"""Permanently delete one automation by id. Prompt or ``--yes`` required."""
|
|
193
|
+
automation_id = _require_int_positional(invocation, "automation id")
|
|
194
|
+
enforce_confirmation(
|
|
195
|
+
invocation,
|
|
196
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
197
|
+
action=f"delete automation {automation_id}",
|
|
198
|
+
)
|
|
199
|
+
with open_service(invocation) as (service, auth):
|
|
200
|
+
data = service.call(_symbol(invocation), automation_id=automation_id)
|
|
201
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def automation_create(invocation: Invocation) -> HandlerResult:
|
|
205
|
+
"""Create an automation. Name comes from a positional or the ``name`` field.
|
|
206
|
+
|
|
207
|
+
``description`` and ``tasks`` are required ``--input`` fields; ``conditions``
|
|
208
|
+
and ``condition_mode`` are forwarded when present. Requires ``--yes``.
|
|
209
|
+
"""
|
|
210
|
+
document = invocation.load_input() or {}
|
|
211
|
+
name = _string_positional(invocation) or document.get("name")
|
|
212
|
+
if not name:
|
|
213
|
+
raise CliError(
|
|
214
|
+
code=CODE_MISSING_ARGUMENT,
|
|
215
|
+
message="An automation name is required.",
|
|
216
|
+
exit_status=EXIT_USAGE,
|
|
217
|
+
hint="Pass the name as a positional argument or a 'name' input field.",
|
|
218
|
+
)
|
|
219
|
+
description = _require_field(document, "description")
|
|
220
|
+
tasks = _require_field(document, "tasks")
|
|
221
|
+
kwargs: dict[str, Any] = {"name": name, "description": description, "tasks": tasks}
|
|
222
|
+
_forward_optional(document, kwargs, ("conditions", "condition_mode"))
|
|
223
|
+
enforce_confirmation(invocation, policy=POLICY_YES_ALWAYS, action="create an automation")
|
|
224
|
+
with open_service(invocation) as (service, auth):
|
|
225
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
226
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def automation_update(invocation: Invocation) -> HandlerResult:
|
|
230
|
+
"""Apply JSON-patch operations to one automation. Requires ``--yes``.
|
|
231
|
+
|
|
232
|
+
Automation id is positional; the non-empty ``patch`` list comes from the
|
|
233
|
+
``--input`` document.
|
|
234
|
+
"""
|
|
235
|
+
automation_id = _require_int_positional(invocation, "automation id")
|
|
236
|
+
document = invocation.load_input()
|
|
237
|
+
patch = _require_field(document, "patch")
|
|
238
|
+
enforce_confirmation(
|
|
239
|
+
invocation,
|
|
240
|
+
policy=POLICY_YES_ALWAYS,
|
|
241
|
+
action=f"update automation {automation_id}",
|
|
242
|
+
)
|
|
243
|
+
with open_service(invocation) as (service, auth):
|
|
244
|
+
data = service.call(_symbol(invocation), automation_id=automation_id, patch=patch)
|
|
245
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Handlers for the ``batch`` command family (dataset-scoped).
|
|
2
|
+
|
|
3
|
+
Every batch operation targets a specific dataset: the dataset id is always the
|
|
4
|
+
first CLI positional argument, and — for the single-batch commands — the
|
|
5
|
+
batch id is the second positional argument. The project id comes from
|
|
6
|
+
``--project`` or the active project (mirroring ``folder.py``). Handlers
|
|
7
|
+
dispatch through the generic
|
|
8
|
+
:meth:`~mammoth_cli.services.protocol.MammothService.call` seam to the public
|
|
9
|
+
SDK method named by the command's reviewed manifest ``sdk_symbol``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from mammoth_cli.errors.envelope import (
|
|
17
|
+
CODE_INVALID_ARGUMENT,
|
|
18
|
+
CODE_MISSING_ARGUMENT,
|
|
19
|
+
CODE_MISSING_FIELD,
|
|
20
|
+
CODE_SDK_SYMBOL_UNRESOLVED,
|
|
21
|
+
EXIT_USAGE,
|
|
22
|
+
CliError,
|
|
23
|
+
)
|
|
24
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
25
|
+
from mammoth_cli.runtime.confirm import POLICY_PROMPT_OR_YES, enforce_confirmation
|
|
26
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
27
|
+
from mammoth_cli.runtime.session import open_service, require_project
|
|
28
|
+
|
|
29
|
+
HandlerResult = tuple[Any, dict[str, Any]]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _symbol(invocation: Invocation) -> str:
|
|
33
|
+
"""Return the reviewed backing SDK symbol for this command."""
|
|
34
|
+
record = command_by_id(invocation.command_id)
|
|
35
|
+
if record is None or not record.get("sdk_symbol"):
|
|
36
|
+
raise CliError(
|
|
37
|
+
code=CODE_SDK_SYMBOL_UNRESOLVED,
|
|
38
|
+
message=f"No SDK symbol is recorded for '{invocation.command_id}'.",
|
|
39
|
+
exit_status=EXIT_USAGE,
|
|
40
|
+
)
|
|
41
|
+
return str(record["sdk_symbol"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _int_positional_at(invocation: Invocation, index: int, name: str) -> int | None:
|
|
45
|
+
"""Parse the positional argument at ``index`` as an int, or return None."""
|
|
46
|
+
if len(invocation.extra_args) <= index:
|
|
47
|
+
return None
|
|
48
|
+
raw = invocation.extra_args[index]
|
|
49
|
+
try:
|
|
50
|
+
return int(raw)
|
|
51
|
+
except ValueError as exc:
|
|
52
|
+
raise CliError(
|
|
53
|
+
code=CODE_INVALID_ARGUMENT,
|
|
54
|
+
message=f"The {name} argument '{raw}' is not an integer.",
|
|
55
|
+
exit_status=EXIT_USAGE,
|
|
56
|
+
) from exc
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _require_int_positional_at(invocation: Invocation, index: int, name: str) -> int:
|
|
60
|
+
"""Return the required positional argument at ``index`` as an int, or raise."""
|
|
61
|
+
value = _int_positional_at(invocation, index, name)
|
|
62
|
+
if value is None:
|
|
63
|
+
raise CliError(
|
|
64
|
+
code=CODE_MISSING_ARGUMENT,
|
|
65
|
+
message=f"This command requires a {name} argument.",
|
|
66
|
+
exit_status=EXIT_USAGE,
|
|
67
|
+
hint=f"Pass the {name} as a positional argument.",
|
|
68
|
+
)
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _require_field(document: dict[str, Any] | None, field: str) -> Any:
|
|
73
|
+
"""Return a required field from the ``--input`` document, or raise usage."""
|
|
74
|
+
if document is None or field not in document:
|
|
75
|
+
raise CliError(
|
|
76
|
+
code=CODE_MISSING_FIELD,
|
|
77
|
+
message=f"This command requires the '{field}' input field.",
|
|
78
|
+
exit_status=EXIT_USAGE,
|
|
79
|
+
hint=f"Pass it via --input, for example: --input '{{\"{field}\": ...}}'.",
|
|
80
|
+
)
|
|
81
|
+
return document[field]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _meta(invocation: Invocation, workspace_id: int, project_id: int) -> dict[str, Any]:
|
|
85
|
+
"""Build the common envelope metadata for a batch command."""
|
|
86
|
+
return {
|
|
87
|
+
"profile": invocation.profile,
|
|
88
|
+
"workspace_id": workspace_id,
|
|
89
|
+
"project_id": project_id,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _forward_optional(
|
|
94
|
+
document: dict[str, Any], kwargs: dict[str, Any], fields: tuple[str, ...]
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Forward each field present in ``document`` into ``kwargs`` unchanged."""
|
|
97
|
+
for field in fields:
|
|
98
|
+
if field in document:
|
|
99
|
+
kwargs[field] = document[field]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def batch_list(invocation: Invocation) -> HandlerResult:
|
|
103
|
+
"""List batches for a dataset in the active project."""
|
|
104
|
+
project_id = require_project(invocation)
|
|
105
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
106
|
+
document = invocation.load_input() or {}
|
|
107
|
+
kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
|
|
108
|
+
_forward_optional(document, kwargs, ("limit", "offset"))
|
|
109
|
+
with open_service(invocation) as (service, auth):
|
|
110
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
111
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def batch_get(invocation: Invocation) -> HandlerResult:
|
|
115
|
+
"""Get one batch by id for a dataset in the active project."""
|
|
116
|
+
project_id = require_project(invocation)
|
|
117
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
118
|
+
batch_id = _require_int_positional_at(invocation, 1, "batch id")
|
|
119
|
+
with open_service(invocation) as (service, auth):
|
|
120
|
+
data = service.call(
|
|
121
|
+
_symbol(invocation), dataset_id=dataset_id, batch_id=batch_id, project_id=project_id
|
|
122
|
+
)
|
|
123
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def batch_create(invocation: Invocation) -> HandlerResult:
|
|
127
|
+
"""Create a batch for a dataset. ``source_id`` and ``mapping`` are required."""
|
|
128
|
+
project_id = require_project(invocation)
|
|
129
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
130
|
+
document = invocation.load_input()
|
|
131
|
+
source_id = invocation.positional("source_id")
|
|
132
|
+
if source_id is None:
|
|
133
|
+
raise CliError(
|
|
134
|
+
code=CODE_MISSING_ARGUMENT,
|
|
135
|
+
message="This command requires a source id argument.",
|
|
136
|
+
exit_status=EXIT_USAGE,
|
|
137
|
+
)
|
|
138
|
+
source_id = int(source_id)
|
|
139
|
+
mapping = _require_field(document, "mapping")
|
|
140
|
+
kwargs: dict[str, Any] = {
|
|
141
|
+
"dataset_id": dataset_id,
|
|
142
|
+
"source_id": source_id,
|
|
143
|
+
"mapping": mapping,
|
|
144
|
+
"project_id": project_id,
|
|
145
|
+
}
|
|
146
|
+
assert document is not None
|
|
147
|
+
_forward_optional(
|
|
148
|
+
document,
|
|
149
|
+
kwargs,
|
|
150
|
+
("new_ds_params", "is_validation_required", "change_map", "delete_source_ds"),
|
|
151
|
+
)
|
|
152
|
+
with open_service(invocation) as (service, auth):
|
|
153
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
154
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def batch_update(invocation: Invocation) -> HandlerResult:
|
|
158
|
+
"""Apply patch operations to a dataset's batches. ``patch`` is required."""
|
|
159
|
+
project_id = require_project(invocation)
|
|
160
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
161
|
+
document = invocation.load_input()
|
|
162
|
+
patch = _require_field(document, "patch")
|
|
163
|
+
with open_service(invocation) as (service, auth):
|
|
164
|
+
data = service.call(
|
|
165
|
+
_symbol(invocation), dataset_id=dataset_id, patch=patch, project_id=project_id
|
|
166
|
+
)
|
|
167
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def batch_delete(invocation: Invocation) -> HandlerResult:
|
|
171
|
+
"""Delete one batch by id. Prompt or ``--yes`` required."""
|
|
172
|
+
project_id = require_project(invocation)
|
|
173
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
174
|
+
batch_id = _require_int_positional_at(invocation, 1, "batch id")
|
|
175
|
+
enforce_confirmation(
|
|
176
|
+
invocation,
|
|
177
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
178
|
+
action=f"delete batch {batch_id} of dataset {dataset_id}",
|
|
179
|
+
)
|
|
180
|
+
with open_service(invocation) as (service, auth):
|
|
181
|
+
data = service.call(
|
|
182
|
+
_symbol(invocation), dataset_id=dataset_id, batch_id=batch_id, project_id=project_id
|
|
183
|
+
)
|
|
184
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def batch_bulk_delete(invocation: Invocation) -> HandlerResult:
|
|
188
|
+
"""Bulk-delete batches for a dataset. Prompt or ``--yes`` required.
|
|
189
|
+
|
|
190
|
+
When no ``ids`` are given, every batch for the dataset is deleted.
|
|
191
|
+
"""
|
|
192
|
+
project_id = require_project(invocation)
|
|
193
|
+
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
194
|
+
document = invocation.load_input() or {}
|
|
195
|
+
enforce_confirmation(
|
|
196
|
+
invocation,
|
|
197
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
198
|
+
action=f"delete batches of dataset {dataset_id}",
|
|
199
|
+
)
|
|
200
|
+
kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
|
|
201
|
+
_forward_optional(document, kwargs, ("ids",))
|
|
202
|
+
with open_service(invocation) as (service, auth):
|
|
203
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
204
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|