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.
Files changed (150) hide show
  1. mammoth_cli/__init__.py +14 -0
  2. mammoth_cli/__main__.py +27 -0
  3. mammoth_cli/app.py +565 -0
  4. mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +109 -0
  5. mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +27 -0
  6. mammoth_cli/bundled_skill/mammoth-cli/references/input.md +24 -0
  7. mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +35 -0
  8. mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +30 -0
  9. mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +28 -0
  10. mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +26 -0
  11. mammoth_cli/commands/__init__.py +29 -0
  12. mammoth_cli/commands/activity.py +123 -0
  13. mammoth_cli/commands/addon.py +173 -0
  14. mammoth_cli/commands/agent.py +199 -0
  15. mammoth_cli/commands/ai.py +274 -0
  16. mammoth_cli/commands/annotation.py +166 -0
  17. mammoth_cli/commands/auth.py +567 -0
  18. mammoth_cli/commands/automation.py +245 -0
  19. mammoth_cli/commands/batch.py +204 -0
  20. mammoth_cli/commands/billing.py +462 -0
  21. mammoth_cli/commands/browse.py +151 -0
  22. mammoth_cli/commands/capability.py +43 -0
  23. mammoth_cli/commands/client_app.py +233 -0
  24. mammoth_cli/commands/completion.py +82 -0
  25. mammoth_cli/commands/config.py +309 -0
  26. mammoth_cli/commands/connector.py +464 -0
  27. mammoth_cli/commands/context.py +205 -0
  28. mammoth_cli/commands/dashboard.py +419 -0
  29. mammoth_cli/commands/data_app.py +239 -0
  30. mammoth_cli/commands/dataset.py +332 -0
  31. mammoth_cli/commands/doctor.py +90 -0
  32. mammoth_cli/commands/external_key.py +163 -0
  33. mammoth_cli/commands/file.py +243 -0
  34. mammoth_cli/commands/folder.py +224 -0
  35. mammoth_cli/commands/job.py +147 -0
  36. mammoth_cli/commands/notification.py +201 -0
  37. mammoth_cli/commands/parameter.py +284 -0
  38. mammoth_cli/commands/project.py +327 -0
  39. mammoth_cli/commands/registry.py +587 -0
  40. mammoth_cli/commands/report.py +61 -0
  41. mammoth_cli/commands/schedule.py +214 -0
  42. mammoth_cli/commands/schema.py +271 -0
  43. mammoth_cli/commands/skill.py +59 -0
  44. mammoth_cli/commands/snippet.py +200 -0
  45. mammoth_cli/commands/support.py +800 -0
  46. mammoth_cli/commands/template.py +134 -0
  47. mammoth_cli/commands/trash.py +115 -0
  48. mammoth_cli/commands/user.py +190 -0
  49. mammoth_cli/commands/view.py +1143 -0
  50. mammoth_cli/commands/view_ops.py +596 -0
  51. mammoth_cli/commands/webhook.py +235 -0
  52. mammoth_cli/commands/workflow.py +340 -0
  53. mammoth_cli/commands/workspace.py +359 -0
  54. mammoth_cli/context/__init__.py +1 -0
  55. mammoth_cli/context/credentials.py +223 -0
  56. mammoth_cli/context/endpoint.py +57 -0
  57. mammoth_cli/context/profiles.py +370 -0
  58. mammoth_cli/context/resolver.py +240 -0
  59. mammoth_cli/contracts/__init__.py +1 -0
  60. mammoth_cli/contracts/auth.py +32 -0
  61. mammoth_cli/errors/__init__.py +1 -0
  62. mammoth_cli/errors/envelope.py +144 -0
  63. mammoth_cli/manifest/__init__.py +1 -0
  64. mammoth_cli/manifest/loader.py +96 -0
  65. mammoth_cli/messages/__init__.py +1 -0
  66. mammoth_cli/output/__init__.py +1 -0
  67. mammoth_cli/output/envelope.py +42 -0
  68. mammoth_cli/output/normalize.py +84 -0
  69. mammoth_cli/output/policy.py +71 -0
  70. mammoth_cli/output/render.py +83 -0
  71. mammoth_cli/py.typed +0 -0
  72. mammoth_cli/runtime/__init__.py +0 -0
  73. mammoth_cli/runtime/confirm.py +124 -0
  74. mammoth_cli/runtime/executor.py +126 -0
  75. mammoth_cli/runtime/input_loader.py +140 -0
  76. mammoth_cli/runtime/invocation.py +87 -0
  77. mammoth_cli/runtime/options.py +172 -0
  78. mammoth_cli/runtime/session.py +82 -0
  79. mammoth_cli/runtime/strict.py +240 -0
  80. mammoth_cli/runtime/validate.py +120 -0
  81. mammoth_cli/services/__init__.py +1 -0
  82. mammoth_cli/services/argspec.py +301 -0
  83. mammoth_cli/services/coerce.py +172 -0
  84. mammoth_cli/services/conditions.py +85 -0
  85. mammoth_cli/services/dispatch.py +63 -0
  86. mammoth_cli/services/factory.py +45 -0
  87. mammoth_cli/services/input_fields.py +44 -0
  88. mammoth_cli/services/mapping.py +74 -0
  89. mammoth_cli/services/openapi_types.py +166 -0
  90. mammoth_cli/services/positionals.py +408 -0
  91. mammoth_cli/services/protocol.py +123 -0
  92. mammoth_cli/services/sdk_service.py +274 -0
  93. mammoth_cli/services/testing.py +153 -0
  94. mammoth_cli/services/type_system.py +427 -0
  95. mammoth_cli/skills/__init__.py +1 -0
  96. mammoth_cli/skills/installer.py +321 -0
  97. mammoth_cli/testing.py +55 -0
  98. mammoth_cli-1.0.0.dist-info/METADATA +92 -0
  99. mammoth_cli-1.0.0.dist-info/RECORD +150 -0
  100. mammoth_cli-1.0.0.dist-info/WHEEL +4 -0
  101. mammoth_cli-1.0.0.dist-info/entry_points.txt +3 -0
  102. mammoth_cli-1.0.0.dist-info/licenses/LICENSE +23 -0
  103. spec/manifests/_sdk_introspection.json +1459 -0
  104. spec/manifests/commands/activity.yaml +72 -0
  105. spec/manifests/commands/addon.yaml +239 -0
  106. spec/manifests/commands/agent.yaml +192 -0
  107. spec/manifests/commands/ai.yaml +152 -0
  108. spec/manifests/commands/annotation.yaml +191 -0
  109. spec/manifests/commands/auth.yaml +101 -0
  110. spec/manifests/commands/automation.yaml +273 -0
  111. spec/manifests/commands/batch.yaml +262 -0
  112. spec/manifests/commands/billing.yaml +813 -0
  113. spec/manifests/commands/browse.yaml +145 -0
  114. spec/manifests/commands/capability.yaml +74 -0
  115. spec/manifests/commands/client-app.yaml +193 -0
  116. spec/manifests/commands/completion.yaml +68 -0
  117. spec/manifests/commands/config.yaml +134 -0
  118. spec/manifests/commands/connector.yaml +944 -0
  119. spec/manifests/commands/context.yaml +101 -0
  120. spec/manifests/commands/dashboard.yaml +3595 -0
  121. spec/manifests/commands/data-app.yaml +485 -0
  122. spec/manifests/commands/dataset.yaml +580 -0
  123. spec/manifests/commands/doctor.yaml +35 -0
  124. spec/manifests/commands/external-key.yaml +151 -0
  125. spec/manifests/commands/file.yaml +341 -0
  126. spec/manifests/commands/folder.yaml +334 -0
  127. spec/manifests/commands/job.yaml +151 -0
  128. spec/manifests/commands/notification.yaml +188 -0
  129. spec/manifests/commands/parameter.yaml +527 -0
  130. spec/manifests/commands/project.yaml +669 -0
  131. spec/manifests/commands/report.yaml +36 -0
  132. spec/manifests/commands/schedule.yaml +194 -0
  133. spec/manifests/commands/schema.yaml +74 -0
  134. spec/manifests/commands/skill.yaml +167 -0
  135. spec/manifests/commands/snippet.yaml +311 -0
  136. spec/manifests/commands/support.yaml +1805 -0
  137. spec/manifests/commands/template.yaml +191 -0
  138. spec/manifests/commands/trash.yaml +105 -0
  139. spec/manifests/commands/user.yaml +280 -0
  140. spec/manifests/commands/version.yaml +35 -0
  141. spec/manifests/commands/view.yaml +4232 -0
  142. spec/manifests/commands/webhook.yaml +261 -0
  143. spec/manifests/commands/workflow.yaml +622 -0
  144. spec/manifests/commands/workspace.yaml +705 -0
  145. spec/manifests/openapi-operations.yaml +8853 -0
  146. spec/manifests/schema-v1.json +212 -0
  147. spec/manifests/sdk-catalog.source.yaml +4069 -0
  148. spec/manifests/sdk-methods.yaml +6971 -0
  149. spec/openapi/metadata.json +10 -0
  150. 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)