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,464 @@
|
|
|
1
|
+
"""Handlers for the ``connector`` command family.
|
|
2
|
+
|
|
3
|
+
``connector get``, ``connector list``, and ``connector active`` describe
|
|
4
|
+
connector types and active connections at the workspace level and take no
|
|
5
|
+
project scope. Every other command operates inside a resolved project: the
|
|
6
|
+
project id comes from ``--project`` or the active project (mirroring
|
|
7
|
+
``folder.py``), and the connector/connection/data-source-config keys that
|
|
8
|
+
address a specific resource come from positional arguments, in the order they
|
|
9
|
+
appear in the command path (``connector_key``, then ``connection_key``, then
|
|
10
|
+
``ds_config_key``).
|
|
11
|
+
|
|
12
|
+
Connection secrets always come from the strict ``--input`` document, never a
|
|
13
|
+
positional argument: ``config`` on ``connection create``, ``credentials`` on
|
|
14
|
+
``connection update``, and the AI-chat ``body`` payloads. ``connection
|
|
15
|
+
create``/``update`` and ``ds-config create``/``update`` cause an external
|
|
16
|
+
side effect against a third-party service and always require ``--yes``;
|
|
17
|
+
``connection delete`` and the ``ds-config`` deletes are destructive and
|
|
18
|
+
accept a prompt or ``--yes``.
|
|
19
|
+
|
|
20
|
+
Handlers dispatch through the generic
|
|
21
|
+
:meth:`~mammoth_cli.services.protocol.MammothService.call` seam to the public
|
|
22
|
+
SDK method named by the command's reviewed manifest ``sdk_symbol``.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from mammoth_cli.errors.envelope import (
|
|
30
|
+
CODE_INVALID_ARGUMENT,
|
|
31
|
+
CODE_MISSING_ARGUMENT,
|
|
32
|
+
CODE_MISSING_FIELD,
|
|
33
|
+
CODE_SDK_SYMBOL_UNRESOLVED,
|
|
34
|
+
EXIT_USAGE,
|
|
35
|
+
CliError,
|
|
36
|
+
)
|
|
37
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
38
|
+
from mammoth_cli.runtime.confirm import (
|
|
39
|
+
POLICY_PROMPT_OR_YES,
|
|
40
|
+
POLICY_YES_ALWAYS,
|
|
41
|
+
enforce_confirmation,
|
|
42
|
+
)
|
|
43
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
44
|
+
from mammoth_cli.runtime.session import open_service, require_project, resolved_project
|
|
45
|
+
|
|
46
|
+
HandlerResult = tuple[Any, dict[str, Any]]
|
|
47
|
+
|
|
48
|
+
_DS_CONFIG_CREATE_OPTIONAL = ("query", "file_source", "table", "profile", "validate", "data_sample")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _symbol(invocation: Invocation) -> str:
|
|
52
|
+
"""Return the reviewed backing SDK symbol for this command."""
|
|
53
|
+
record = command_by_id(invocation.command_id)
|
|
54
|
+
if record is None or not record.get("sdk_symbol"):
|
|
55
|
+
raise CliError(
|
|
56
|
+
code=CODE_SDK_SYMBOL_UNRESOLVED,
|
|
57
|
+
message=f"No SDK symbol is recorded for '{invocation.command_id}'.",
|
|
58
|
+
exit_status=EXIT_USAGE,
|
|
59
|
+
)
|
|
60
|
+
return str(record["sdk_symbol"])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _string_positional_at(invocation: Invocation, index: int, name: str) -> str | None:
|
|
64
|
+
"""Return the positional argument at ``index``, or None if absent."""
|
|
65
|
+
if len(invocation.extra_args) <= index:
|
|
66
|
+
return None
|
|
67
|
+
return invocation.extra_args[index]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _require_string_positional_at(invocation: Invocation, index: int, name: str) -> str:
|
|
71
|
+
"""Return the required positional argument at ``index``, or raise usage."""
|
|
72
|
+
value = _string_positional_at(invocation, index, name)
|
|
73
|
+
if not value:
|
|
74
|
+
raise CliError(
|
|
75
|
+
code=CODE_MISSING_ARGUMENT,
|
|
76
|
+
message=f"This command requires a {name} argument.",
|
|
77
|
+
exit_status=EXIT_USAGE,
|
|
78
|
+
hint=f"Pass the {name} as a positional argument.",
|
|
79
|
+
)
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _require_int_positional_at(invocation: Invocation, index: int, name: str) -> int:
|
|
84
|
+
"""Return the required positional argument at ``index`` parsed as an int."""
|
|
85
|
+
raw = _string_positional_at(invocation, index, name)
|
|
86
|
+
if raw is None:
|
|
87
|
+
raise CliError(
|
|
88
|
+
code=CODE_MISSING_ARGUMENT,
|
|
89
|
+
message=f"This command requires a {name} argument.",
|
|
90
|
+
exit_status=EXIT_USAGE,
|
|
91
|
+
hint=f"Pass the {name} as a positional argument.",
|
|
92
|
+
)
|
|
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
|
+
if document is None or field not in document:
|
|
106
|
+
raise CliError(
|
|
107
|
+
code=CODE_MISSING_FIELD,
|
|
108
|
+
message=f"This command requires the '{field}' input field.",
|
|
109
|
+
exit_status=EXIT_USAGE,
|
|
110
|
+
hint=f"Pass it via --input, for example: --input '{{\"{field}\": ...}}'.",
|
|
111
|
+
)
|
|
112
|
+
return document[field]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _forward_optional(
|
|
116
|
+
document: dict[str, Any], kwargs: dict[str, Any], fields: tuple[str, ...]
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Copy each present field from ``document`` into ``kwargs`` unchanged."""
|
|
119
|
+
for field in fields:
|
|
120
|
+
if field in document:
|
|
121
|
+
kwargs[field] = document[field]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _meta(invocation: Invocation, workspace_id: int, project_id: int | None) -> dict[str, Any]:
|
|
125
|
+
"""Build the common envelope metadata for a connector command."""
|
|
126
|
+
return {
|
|
127
|
+
"profile": invocation.profile,
|
|
128
|
+
"workspace_id": workspace_id,
|
|
129
|
+
"project_id": project_id,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def connector_active(invocation: Invocation) -> HandlerResult:
|
|
134
|
+
"""List active connectors with established connections (workspace-scoped)."""
|
|
135
|
+
with open_service(invocation) as (service, auth):
|
|
136
|
+
data = service.call(_symbol(invocation))
|
|
137
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def connector_get(invocation: Invocation) -> HandlerResult:
|
|
141
|
+
"""Get one connector's details by key (workspace-scoped, no project)."""
|
|
142
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
143
|
+
with open_service(invocation) as (service, auth):
|
|
144
|
+
data = service.call(_symbol(invocation), connector_key=connector_key)
|
|
145
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def connector_list(invocation: Invocation) -> HandlerResult:
|
|
149
|
+
"""List all available connectors (workspace-scoped, no project)."""
|
|
150
|
+
with open_service(invocation) as (service, auth):
|
|
151
|
+
data = service.call(_symbol(invocation))
|
|
152
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def connector_ai_chat(invocation: Invocation) -> HandlerResult:
|
|
156
|
+
"""Send a message to the connector AI chat assistant. ``body`` is required input."""
|
|
157
|
+
project_id = require_project(invocation)
|
|
158
|
+
document = invocation.load_input()
|
|
159
|
+
body = _require_field(document, "body")
|
|
160
|
+
with open_service(invocation) as (service, auth):
|
|
161
|
+
data = service.call(_symbol(invocation), body=body, project_id=project_id)
|
|
162
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def connector_ai_history(invocation: Invocation) -> HandlerResult:
|
|
166
|
+
"""Get chat history for a connection. The connection key is positional."""
|
|
167
|
+
project_id = require_project(invocation)
|
|
168
|
+
connection_key = _require_string_positional_at(invocation, 0, "connection key")
|
|
169
|
+
with open_service(invocation) as (service, auth):
|
|
170
|
+
data = service.call(
|
|
171
|
+
_symbol(invocation), connection_key=connection_key, project_id=project_id
|
|
172
|
+
)
|
|
173
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def connector_ai_session_list(invocation: Invocation) -> HandlerResult:
|
|
177
|
+
"""List connector chat sessions in the active project."""
|
|
178
|
+
project_id = require_project(invocation)
|
|
179
|
+
with open_service(invocation) as (service, auth):
|
|
180
|
+
data = service.call(_symbol(invocation), project_id=project_id)
|
|
181
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def connector_ai_session_messages(invocation: Invocation) -> HandlerResult:
|
|
185
|
+
"""Get messages for a connector chat session. The session id is positional."""
|
|
186
|
+
project_id = require_project(invocation)
|
|
187
|
+
session_id = _require_int_positional_at(invocation, 0, "session id")
|
|
188
|
+
with open_service(invocation) as (service, auth):
|
|
189
|
+
data = service.call(_symbol(invocation), session_id=session_id, project_id=project_id)
|
|
190
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def connector_ai_submit_column_selection(invocation: Invocation) -> HandlerResult:
|
|
194
|
+
"""Submit a column selection to the connector chat flow. ``body`` is required input."""
|
|
195
|
+
project_id = require_project(invocation)
|
|
196
|
+
document = invocation.load_input()
|
|
197
|
+
body = _require_field(document, "body")
|
|
198
|
+
with open_service(invocation) as (service, auth):
|
|
199
|
+
data = service.call(_symbol(invocation), body=body, project_id=project_id)
|
|
200
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def connector_ai_submit_credentials(invocation: Invocation) -> HandlerResult:
|
|
204
|
+
"""Submit connector credentials to the connector chat flow.
|
|
205
|
+
|
|
206
|
+
``body`` (the credentials payload) is a required ``--input`` field; it is
|
|
207
|
+
never accepted as a positional argument.
|
|
208
|
+
"""
|
|
209
|
+
project_id = require_project(invocation)
|
|
210
|
+
document = invocation.load_input()
|
|
211
|
+
body = _require_field(document, "body")
|
|
212
|
+
with open_service(invocation) as (service, auth):
|
|
213
|
+
data = service.call(_symbol(invocation), body=body, project_id=project_id)
|
|
214
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def connector_connection_create(invocation: Invocation) -> HandlerResult:
|
|
218
|
+
"""Create a connection for a connector. ``--yes`` is always required.
|
|
219
|
+
|
|
220
|
+
``config`` (the connection credentials) is a required ``--input`` field;
|
|
221
|
+
it is never accepted as a positional argument.
|
|
222
|
+
"""
|
|
223
|
+
project_id = require_project(invocation)
|
|
224
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
225
|
+
document = invocation.load_input()
|
|
226
|
+
config = _require_field(document, "config")
|
|
227
|
+
enforce_confirmation(
|
|
228
|
+
invocation,
|
|
229
|
+
policy=POLICY_YES_ALWAYS,
|
|
230
|
+
action=f"create a {connector_key} connection",
|
|
231
|
+
)
|
|
232
|
+
with open_service(invocation) as (service, auth):
|
|
233
|
+
data = service.call(
|
|
234
|
+
_symbol(invocation), connector_key=connector_key, config=config, project_id=project_id
|
|
235
|
+
)
|
|
236
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def connector_connection_delete(invocation: Invocation) -> HandlerResult:
|
|
240
|
+
"""Delete a connection. Prompt or ``--yes`` required."""
|
|
241
|
+
project_id = require_project(invocation)
|
|
242
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
243
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
244
|
+
enforce_confirmation(
|
|
245
|
+
invocation,
|
|
246
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
247
|
+
action=f"delete connection {connection_key} of connector {connector_key}",
|
|
248
|
+
)
|
|
249
|
+
with open_service(invocation) as (service, auth):
|
|
250
|
+
data = service.call(
|
|
251
|
+
_symbol(invocation),
|
|
252
|
+
connector_key=connector_key,
|
|
253
|
+
connection_key=connection_key,
|
|
254
|
+
project_id=project_id,
|
|
255
|
+
)
|
|
256
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def connector_connection_get(invocation: Invocation) -> HandlerResult:
|
|
260
|
+
"""Get one connection's details. Connector and connection keys are positional."""
|
|
261
|
+
project_id = require_project(invocation)
|
|
262
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
263
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
264
|
+
with open_service(invocation) as (service, auth):
|
|
265
|
+
data = service.call(
|
|
266
|
+
_symbol(invocation),
|
|
267
|
+
connector_key=connector_key,
|
|
268
|
+
connection_key=connection_key,
|
|
269
|
+
project_id=project_id,
|
|
270
|
+
)
|
|
271
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def connector_connection_list(invocation: Invocation) -> HandlerResult:
|
|
275
|
+
"""List connections for a connector type. The connector key is positional."""
|
|
276
|
+
project_id = require_project(invocation)
|
|
277
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
278
|
+
with open_service(invocation) as (service, auth):
|
|
279
|
+
data = service.call(_symbol(invocation), connector_key=connector_key, project_id=project_id)
|
|
280
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def connector_connection_update(invocation: Invocation) -> HandlerResult:
|
|
284
|
+
"""Update a connection's credentials. ``--yes`` is always required.
|
|
285
|
+
|
|
286
|
+
``credentials`` is a required ``--input`` field; it is never accepted as
|
|
287
|
+
a positional argument.
|
|
288
|
+
"""
|
|
289
|
+
project_id = require_project(invocation)
|
|
290
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
291
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
292
|
+
document = invocation.load_input()
|
|
293
|
+
credentials = _require_field(document, "credentials")
|
|
294
|
+
enforce_confirmation(
|
|
295
|
+
invocation,
|
|
296
|
+
policy=POLICY_YES_ALWAYS,
|
|
297
|
+
action=f"update credentials for connection {connection_key} of connector {connector_key}",
|
|
298
|
+
)
|
|
299
|
+
with open_service(invocation) as (service, auth):
|
|
300
|
+
data = service.call(
|
|
301
|
+
_symbol(invocation),
|
|
302
|
+
connector_key=connector_key,
|
|
303
|
+
connection_key=connection_key,
|
|
304
|
+
credentials=credentials,
|
|
305
|
+
project_id=project_id,
|
|
306
|
+
)
|
|
307
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def connector_ds_config_create(invocation: Invocation) -> HandlerResult:
|
|
311
|
+
"""Create a data source configuration. ``--yes`` is always required."""
|
|
312
|
+
project_id = require_project(invocation)
|
|
313
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
314
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
315
|
+
document = invocation.load_input() or {}
|
|
316
|
+
kwargs: dict[str, Any] = {
|
|
317
|
+
"connector_key": connector_key,
|
|
318
|
+
"connection_key": connection_key,
|
|
319
|
+
"project_id": project_id,
|
|
320
|
+
}
|
|
321
|
+
_forward_optional(document, kwargs, _DS_CONFIG_CREATE_OPTIONAL)
|
|
322
|
+
enforce_confirmation(
|
|
323
|
+
invocation,
|
|
324
|
+
policy=POLICY_YES_ALWAYS,
|
|
325
|
+
action=f"create a data source config for connection {connection_key}",
|
|
326
|
+
)
|
|
327
|
+
with open_service(invocation) as (service, auth):
|
|
328
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
329
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def connector_ds_config_delete(invocation: Invocation) -> HandlerResult:
|
|
333
|
+
"""Delete a data source configuration. Prompt or ``--yes`` required."""
|
|
334
|
+
project_id = require_project(invocation)
|
|
335
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
336
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
337
|
+
ds_config_key = _require_string_positional_at(invocation, 2, "ds config key")
|
|
338
|
+
enforce_confirmation(
|
|
339
|
+
invocation,
|
|
340
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
341
|
+
action=f"delete data source config {ds_config_key}",
|
|
342
|
+
)
|
|
343
|
+
with open_service(invocation) as (service, auth):
|
|
344
|
+
data = service.call(
|
|
345
|
+
_symbol(invocation),
|
|
346
|
+
connector_key=connector_key,
|
|
347
|
+
connection_key=connection_key,
|
|
348
|
+
ds_config_key=ds_config_key,
|
|
349
|
+
project_id=project_id,
|
|
350
|
+
)
|
|
351
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def connector_ds_config_delete_all(invocation: Invocation) -> HandlerResult:
|
|
355
|
+
"""Bulk-delete data source configurations for a connection. Prompt or ``--yes`` required."""
|
|
356
|
+
project_id = require_project(invocation)
|
|
357
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
358
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
359
|
+
document = invocation.load_input()
|
|
360
|
+
config_ids = _require_field(document, "config_ids")
|
|
361
|
+
enforce_confirmation(
|
|
362
|
+
invocation,
|
|
363
|
+
policy=POLICY_PROMPT_OR_YES,
|
|
364
|
+
action=f"delete data source configs for connection {connection_key}",
|
|
365
|
+
)
|
|
366
|
+
with open_service(invocation) as (service, auth):
|
|
367
|
+
data = service.call(
|
|
368
|
+
_symbol(invocation),
|
|
369
|
+
connector_key=connector_key,
|
|
370
|
+
connection_key=connection_key,
|
|
371
|
+
config_ids=config_ids,
|
|
372
|
+
project_id=project_id,
|
|
373
|
+
)
|
|
374
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def connector_ds_config_get(invocation: Invocation) -> HandlerResult:
|
|
378
|
+
"""Get one data source configuration by key."""
|
|
379
|
+
project_id = require_project(invocation)
|
|
380
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
381
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
382
|
+
ds_config_key = _require_string_positional_at(invocation, 2, "ds config key")
|
|
383
|
+
with open_service(invocation) as (service, auth):
|
|
384
|
+
data = service.call(
|
|
385
|
+
_symbol(invocation),
|
|
386
|
+
connector_key=connector_key,
|
|
387
|
+
connection_key=connection_key,
|
|
388
|
+
ds_config_key=ds_config_key,
|
|
389
|
+
project_id=project_id,
|
|
390
|
+
)
|
|
391
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def connector_ds_config_list(invocation: Invocation) -> HandlerResult:
|
|
395
|
+
"""List data source configurations for a connection."""
|
|
396
|
+
project_id = require_project(invocation)
|
|
397
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
398
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
399
|
+
with open_service(invocation) as (service, auth):
|
|
400
|
+
data = service.call(
|
|
401
|
+
_symbol(invocation),
|
|
402
|
+
connector_key=connector_key,
|
|
403
|
+
connection_key=connection_key,
|
|
404
|
+
project_id=project_id,
|
|
405
|
+
)
|
|
406
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def connector_ds_config_update(invocation: Invocation) -> HandlerResult:
|
|
410
|
+
"""Update a data source configuration via JSON-patch ops. ``--yes`` is always required."""
|
|
411
|
+
project_id = require_project(invocation)
|
|
412
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
413
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
414
|
+
ds_config_key = _require_string_positional_at(invocation, 2, "ds config key")
|
|
415
|
+
document = invocation.load_input()
|
|
416
|
+
patch = _require_field(document, "patch")
|
|
417
|
+
enforce_confirmation(
|
|
418
|
+
invocation,
|
|
419
|
+
policy=POLICY_YES_ALWAYS,
|
|
420
|
+
action=f"update data source config {ds_config_key}",
|
|
421
|
+
)
|
|
422
|
+
with open_service(invocation) as (service, auth):
|
|
423
|
+
data = service.call(
|
|
424
|
+
_symbol(invocation),
|
|
425
|
+
connector_key=connector_key,
|
|
426
|
+
connection_key=connection_key,
|
|
427
|
+
ds_config_key=ds_config_key,
|
|
428
|
+
patch=patch,
|
|
429
|
+
project_id=project_id,
|
|
430
|
+
)
|
|
431
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def connector_query_generate(invocation: Invocation) -> HandlerResult:
|
|
435
|
+
"""Generate a query for a connector using AI. ``prompt`` is required input."""
|
|
436
|
+
project_id = require_project(invocation)
|
|
437
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
438
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
439
|
+
document = invocation.load_input()
|
|
440
|
+
prompt = _require_field(document, "prompt")
|
|
441
|
+
with open_service(invocation) as (service, auth):
|
|
442
|
+
data = service.call(
|
|
443
|
+
_symbol(invocation),
|
|
444
|
+
connector_key=connector_key,
|
|
445
|
+
connection_key=connection_key,
|
|
446
|
+
prompt=prompt,
|
|
447
|
+
project_id=project_id,
|
|
448
|
+
)
|
|
449
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def connector_query_status(invocation: Invocation) -> HandlerResult:
|
|
453
|
+
"""Get the status of an AI chat session for a connector connection."""
|
|
454
|
+
project_id = require_project(invocation)
|
|
455
|
+
connector_key = _require_string_positional_at(invocation, 0, "connector key")
|
|
456
|
+
connection_key = _require_string_positional_at(invocation, 1, "connection key")
|
|
457
|
+
with open_service(invocation) as (service, auth):
|
|
458
|
+
data = service.call(
|
|
459
|
+
_symbol(invocation),
|
|
460
|
+
connector_key=connector_key,
|
|
461
|
+
connection_key=connection_key,
|
|
462
|
+
project_id=project_id,
|
|
463
|
+
)
|
|
464
|
+
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Bespoke `context project` command family: status, use, clear.
|
|
2
|
+
|
|
3
|
+
A project id is operational context, not authentication. It resolves from
|
|
4
|
+
``--project``, then the selected profile's saved active project, then no
|
|
5
|
+
project at all. All three commands are local; none makes a network call.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
|
+
from mammoth_cli.context import profiles
|
|
15
|
+
from mammoth_cli.errors.envelope import CODE_PROFILE_NOT_FOUND, EXIT_USAGE, CliError
|
|
16
|
+
from mammoth_cli.runtime import executor
|
|
17
|
+
from mammoth_cli.runtime import options as go
|
|
18
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _profile_name(invocation: Invocation) -> str:
|
|
22
|
+
return invocation.profile or profiles.get_selected()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _project_source(invocation: Invocation, record: profiles.ProfileRecord | None) -> str:
|
|
26
|
+
if invocation.project is not None:
|
|
27
|
+
return "flag"
|
|
28
|
+
if record is not None and record.project_id is not None:
|
|
29
|
+
return "profile"
|
|
30
|
+
return "none"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _profile_not_found_error(profile_name: str) -> CliError:
|
|
34
|
+
return CliError(
|
|
35
|
+
code=CODE_PROFILE_NOT_FOUND,
|
|
36
|
+
message=f"No profile named '{profile_name}' exists yet.",
|
|
37
|
+
exit_status=EXIT_USAGE,
|
|
38
|
+
hint="Run 'mammoth auth login' first.",
|
|
39
|
+
recovery_commands=["mammoth auth login"],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _run_status(invocation: Invocation) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
44
|
+
profile_name = _profile_name(invocation)
|
|
45
|
+
record = profiles.get_profile(profile_name)
|
|
46
|
+
project_id = invocation.project
|
|
47
|
+
if project_id is None and record is not None:
|
|
48
|
+
project_id = record.project_id
|
|
49
|
+
data = {
|
|
50
|
+
"profile": profile_name,
|
|
51
|
+
"project_id": project_id,
|
|
52
|
+
"source": _project_source(invocation, record),
|
|
53
|
+
}
|
|
54
|
+
return data, {"profile": profile_name, "project_id": project_id}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def context_project_status(
|
|
58
|
+
output: str = go.output_option(),
|
|
59
|
+
profile: str | None = go.profile_option(),
|
|
60
|
+
project: int | None = go.project_option(),
|
|
61
|
+
timeout: float | None = go.timeout_option(),
|
|
62
|
+
job_timeout: float | None = go.job_timeout_option(),
|
|
63
|
+
pipeline_timeout: float | None = go.pipeline_timeout_option(),
|
|
64
|
+
color: str = go.color_option(),
|
|
65
|
+
no_input: bool = go.no_input_option(),
|
|
66
|
+
no_progress: bool = go.no_progress_option(),
|
|
67
|
+
debug: bool = go.debug_option(),
|
|
68
|
+
input_file: str | None = go.input_file_option(),
|
|
69
|
+
input_format: str | None = go.input_format_option(),
|
|
70
|
+
) -> None:
|
|
71
|
+
"""Report the resolved active project id for one profile."""
|
|
72
|
+
invocation = go.make_invocation(
|
|
73
|
+
"context.project.status",
|
|
74
|
+
output=output,
|
|
75
|
+
profile=profile,
|
|
76
|
+
project=project,
|
|
77
|
+
timeout=timeout,
|
|
78
|
+
job_timeout=job_timeout,
|
|
79
|
+
pipeline_timeout=pipeline_timeout,
|
|
80
|
+
color=color,
|
|
81
|
+
no_input=no_input,
|
|
82
|
+
no_progress=no_progress,
|
|
83
|
+
debug=debug,
|
|
84
|
+
input_file=input_file,
|
|
85
|
+
input_format=input_format,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def producer() -> tuple[Any, dict[str, Any]]:
|
|
89
|
+
return _run_status(invocation)
|
|
90
|
+
|
|
91
|
+
executor.run(invocation.command_id, invocation.output, producer)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _run_use(invocation: Invocation, *, project_id: int) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
95
|
+
if project_id <= 0:
|
|
96
|
+
raise CliError(
|
|
97
|
+
code="invalid_project_id",
|
|
98
|
+
message="project_id must be a positive integer.",
|
|
99
|
+
exit_status=EXIT_USAGE,
|
|
100
|
+
)
|
|
101
|
+
profile_name = _profile_name(invocation)
|
|
102
|
+
existing = profiles.get_profile(profile_name)
|
|
103
|
+
if existing is None:
|
|
104
|
+
raise _profile_not_found_error(profile_name)
|
|
105
|
+
profiles.save_profile(
|
|
106
|
+
profiles.ProfileRecord(
|
|
107
|
+
name=existing.name,
|
|
108
|
+
workspace_id=existing.workspace_id,
|
|
109
|
+
server_prefix=existing.server_prefix,
|
|
110
|
+
project_id=project_id,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
data = {"profile": profile_name, "project_id": project_id}
|
|
114
|
+
return data, {"profile": profile_name, "project_id": project_id}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def context_project_use(
|
|
118
|
+
output: str = go.output_option(),
|
|
119
|
+
profile: str | None = go.profile_option(),
|
|
120
|
+
project: int | None = go.project_option(),
|
|
121
|
+
timeout: float | None = go.timeout_option(),
|
|
122
|
+
job_timeout: float | None = go.job_timeout_option(),
|
|
123
|
+
pipeline_timeout: float | None = go.pipeline_timeout_option(),
|
|
124
|
+
color: str = go.color_option(),
|
|
125
|
+
no_input: bool = go.no_input_option(),
|
|
126
|
+
no_progress: bool = go.no_progress_option(),
|
|
127
|
+
debug: bool = go.debug_option(),
|
|
128
|
+
input_file: str | None = go.input_file_option(),
|
|
129
|
+
input_format: str | None = go.input_format_option(),
|
|
130
|
+
project_id: int = typer.Argument(..., help="Positive project id to make active."),
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Save a positive project id as the active project for one profile."""
|
|
133
|
+
invocation = go.make_invocation(
|
|
134
|
+
"context.project.use",
|
|
135
|
+
output=output,
|
|
136
|
+
profile=profile,
|
|
137
|
+
project=project,
|
|
138
|
+
timeout=timeout,
|
|
139
|
+
job_timeout=job_timeout,
|
|
140
|
+
pipeline_timeout=pipeline_timeout,
|
|
141
|
+
color=color,
|
|
142
|
+
no_input=no_input,
|
|
143
|
+
no_progress=no_progress,
|
|
144
|
+
debug=debug,
|
|
145
|
+
input_file=input_file,
|
|
146
|
+
input_format=input_format,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def producer() -> tuple[Any, dict[str, Any]]:
|
|
150
|
+
return _run_use(invocation, project_id=project_id)
|
|
151
|
+
|
|
152
|
+
executor.run(invocation.command_id, invocation.output, producer)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _run_clear(invocation: Invocation) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
156
|
+
profile_name = _profile_name(invocation)
|
|
157
|
+
existing = profiles.get_profile(profile_name)
|
|
158
|
+
if existing is not None and existing.project_id is not None:
|
|
159
|
+
profiles.save_profile(
|
|
160
|
+
profiles.ProfileRecord(
|
|
161
|
+
name=existing.name,
|
|
162
|
+
workspace_id=existing.workspace_id,
|
|
163
|
+
server_prefix=existing.server_prefix,
|
|
164
|
+
project_id=None,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
data = {"profile": profile_name, "project_id": None}
|
|
168
|
+
return data, {"profile": profile_name}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def context_project_clear(
|
|
172
|
+
output: str = go.output_option(),
|
|
173
|
+
profile: str | None = go.profile_option(),
|
|
174
|
+
project: int | None = go.project_option(),
|
|
175
|
+
timeout: float | None = go.timeout_option(),
|
|
176
|
+
job_timeout: float | None = go.job_timeout_option(),
|
|
177
|
+
pipeline_timeout: float | None = go.pipeline_timeout_option(),
|
|
178
|
+
color: str = go.color_option(),
|
|
179
|
+
no_input: bool = go.no_input_option(),
|
|
180
|
+
no_progress: bool = go.no_progress_option(),
|
|
181
|
+
debug: bool = go.debug_option(),
|
|
182
|
+
input_file: str | None = go.input_file_option(),
|
|
183
|
+
input_format: str | None = go.input_format_option(),
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Clear the active project id for one profile. Idempotent."""
|
|
186
|
+
invocation = go.make_invocation(
|
|
187
|
+
"context.project.clear",
|
|
188
|
+
output=output,
|
|
189
|
+
profile=profile,
|
|
190
|
+
project=project,
|
|
191
|
+
timeout=timeout,
|
|
192
|
+
job_timeout=job_timeout,
|
|
193
|
+
pipeline_timeout=pipeline_timeout,
|
|
194
|
+
color=color,
|
|
195
|
+
no_input=no_input,
|
|
196
|
+
no_progress=no_progress,
|
|
197
|
+
debug=debug,
|
|
198
|
+
input_file=input_file,
|
|
199
|
+
input_format=input_format,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def producer() -> tuple[Any, dict[str, Any]]:
|
|
203
|
+
return _run_clear(invocation)
|
|
204
|
+
|
|
205
|
+
executor.run(invocation.command_id, invocation.output, producer)
|