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,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mammoth-cli
|
|
3
|
+
description: Drive the Mammoth Analytics platform from the command line. Use for authenticating, browsing projects and datasets, running pipeline transformations and exports, and any automated or agent task that needs deterministic JSON output and safe, confirmable mutations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Mammoth CLI
|
|
7
|
+
|
|
8
|
+
The `mammoth` command controls the Mammoth Analytics platform through the public
|
|
9
|
+
`mammoth-io` SDK. It is built for autonomous agents: every command supports
|
|
10
|
+
deterministic machine output, promptless operation, and stable error envelopes.
|
|
11
|
+
|
|
12
|
+
## Golden rules for agents
|
|
13
|
+
|
|
14
|
+
1. Always pass `--output json` and `--no-input`. Never rely on a prompt.
|
|
15
|
+
2. Discover, do not guess. Use `mammoth capability list` and `mammoth schema get`
|
|
16
|
+
to learn a command before you run it.
|
|
17
|
+
3. Read the exit code, not the text. `0` ok, `2` usage, `4` auth, `5` not found,
|
|
18
|
+
`6` conflict, `7` retryable, `1` other API error, `130` interrupt.
|
|
19
|
+
4. Confirm mutations explicitly. Destructive commands need `--yes`; high-impact
|
|
20
|
+
commands also need `--confirm TARGET`. There is no interactive fallback under
|
|
21
|
+
`--no-input`.
|
|
22
|
+
5. Never put a secret on the command line. Pass credentials through
|
|
23
|
+
`mammoth auth login` (prompt or environment) and structured secrets through
|
|
24
|
+
`--input`.
|
|
25
|
+
|
|
26
|
+
## Authenticate
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export MAMMOTH_API_KEY=... MAMMOTH_API_SECRET=... MAMMOTH_WORKSPACE_ID=4
|
|
30
|
+
mammoth doctor --output json --no-input # verify credentials + endpoint
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Credentials resolve in this order: explicit login, then environment, then the
|
|
34
|
+
saved profile. The endpoint defaults to the `app-eu` server prefix. See
|
|
35
|
+
[references/auth.md](references/auth.md).
|
|
36
|
+
|
|
37
|
+
## Select a project
|
|
38
|
+
|
|
39
|
+
Most dataset, folder, view, and pipeline commands run inside a project.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
mammoth context project use 180 --output json --no-input # save the active project
|
|
43
|
+
mammoth project list --output json --no-input # or pass --project 180
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Machine output and structured input
|
|
47
|
+
|
|
48
|
+
Every command returns `{schema_version, data, meta}` on stdout and a stable
|
|
49
|
+
`{schema_version, error:{code, message, hint, ...}}` on stderr. Feed multi-field
|
|
50
|
+
requests through one document:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mammoth view transform math 1039 --project 180 --output json --no-input \
|
|
54
|
+
--input '{"expression": "price * qty", "new_column": "total"}'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
See [references/machine-output.md](references/machine-output.md) and
|
|
58
|
+
[references/input.md](references/input.md).
|
|
59
|
+
|
|
60
|
+
A transform that takes a nested request is driven the same way. Bulk-replace
|
|
61
|
+
maps many search values to one replacement, across one or more columns:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
mammoth view transform bulk-replace VIEW_ID \
|
|
65
|
+
--project PROJECT_ID \
|
|
66
|
+
--input '{
|
|
67
|
+
"columns": ["Status"],
|
|
68
|
+
"mapping": [
|
|
69
|
+
{"search": ["In progress", "Pending"], "replace": "Open"}
|
|
70
|
+
],
|
|
71
|
+
"match_case": false,
|
|
72
|
+
"match_words": true
|
|
73
|
+
}' \
|
|
74
|
+
--output json --no-input
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `columns` and `mapping` are required; each mapping needs `search` (a list) and
|
|
78
|
+
`replace`.
|
|
79
|
+
- `match_case` defaults to `true`; `match_words` defaults to `false`.
|
|
80
|
+
- `condition` is optional (restrict the rows the replacement touches).
|
|
81
|
+
- Run `mammoth schema get view.transform.bulk-replace --output json --no-input`
|
|
82
|
+
for the full request shape.
|
|
83
|
+
|
|
84
|
+
## Safe mutations
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
mammoth dataset delete 2340 --project 180 --output json --no-input --yes
|
|
88
|
+
mammoth workspace delete 9 --output json --no-input --yes --confirm 9
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
See [references/safety.md](references/safety.md).
|
|
92
|
+
|
|
93
|
+
## Jobs, drafts, and cleanup
|
|
94
|
+
|
|
95
|
+
Long operations return a job. A command's `wait_policy` (see `mammoth schema
|
|
96
|
+
get`) tells you what to expect: `always_wait` and `start_or_wait` commands
|
|
97
|
+
resolve the job for you and return the final result; only a `returns_job`
|
|
98
|
+
command normally needs you to wait on the job id explicitly. Draft mode batches
|
|
99
|
+
pipeline edits before submitting. Always delete resources you created in a
|
|
100
|
+
shared project. See [references/jobs-drafts.md](references/jobs-drafts.md) and
|
|
101
|
+
[references/recovery.md](references/recovery.md).
|
|
102
|
+
|
|
103
|
+
## Discover everything
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
mammoth capability list --output json --no-input # every operation
|
|
107
|
+
mammoth schema list --output json --no-input # every command's schema
|
|
108
|
+
mammoth schema get view.transform.filter --output json --no-input
|
|
109
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Authentication and profiles
|
|
2
|
+
|
|
3
|
+
Required: API key, API secret, workspace id. Optional: a one-label server
|
|
4
|
+
prefix (default `app-eu`, resolving to `https://app-eu.mammoth.io/api/v2`).
|
|
5
|
+
|
|
6
|
+
## Precedence
|
|
7
|
+
1. Explicit credentials given to the current command (secure prompt or stdin).
|
|
8
|
+
2. Environment: `MAMMOTH_API_KEY`, `MAMMOTH_API_SECRET`, `MAMMOTH_WORKSPACE_ID`
|
|
9
|
+
(all three required together), plus optional `MAMMOTH_SERVER_PREFIX`. Setting
|
|
10
|
+
only some of the three is rejected (`incomplete_environment_auth`) rather than
|
|
11
|
+
falling back to a saved profile.
|
|
12
|
+
3. The selected or `--profile` profile's saved credentials.
|
|
13
|
+
|
|
14
|
+
The only supported configuration is the API key, API secret, workspace id, and
|
|
15
|
+
an optional one-label server prefix (default `app-eu`). There is no base-url
|
|
16
|
+
override.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
```bash
|
|
20
|
+
mammoth auth login --output json --no-input # stores secret in the OS keyring
|
|
21
|
+
mammoth auth status --output json --no-input
|
|
22
|
+
mammoth auth logout --profile default --output json --no-input --yes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Secrets live in the OS keyring (or a `0600` file fallback). They are never
|
|
26
|
+
printed, logged, or included in any envelope. Never pass a secret as a plain
|
|
27
|
+
argument; use the prompt, environment, or `--input`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Structured input
|
|
2
|
+
|
|
3
|
+
Drive multi-field commands with one strict document instead of many flags.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
mammoth folder create --project 180 --output json --no-input \
|
|
7
|
+
--input '{"name": "Reports", "parent_resource_id": "r_root"}'
|
|
8
|
+
|
|
9
|
+
mammoth view transform filter 1039 --project 180 --output json --no-input \
|
|
10
|
+
--input '{"condition": {"and": [{"column": "status", "operator": "=", "value": "open"}, {"column": "age", "operator": ">", "value": 30}]}}'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `--input FILE` reads a JSON or YAML file; the format is inferred from the
|
|
14
|
+
extension.
|
|
15
|
+
- `--input -` reads stdin; then `--input-format json|yaml` is required.
|
|
16
|
+
- The top level must be a mapping. A bad path, format, or shape fails with exit
|
|
17
|
+
code 2 and a stable error code.
|
|
18
|
+
|
|
19
|
+
## Condition specs
|
|
20
|
+
A `condition` field is a mapping:
|
|
21
|
+
- leaf: `{"column": ..., "operator": ..., "value": ...}` (plus optional
|
|
22
|
+
`case_sensitive`, `value_is_column`, `component`, `truncate`).
|
|
23
|
+
- compound: `{"and": [spec, ...]}` or `{"or": [spec, ...]}`.
|
|
24
|
+
- negation: `{"not": spec}`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Jobs, draft mode, and bulk replace
|
|
2
|
+
|
|
3
|
+
## Jobs
|
|
4
|
+
A command's `wait_policy` (visible via `mammoth schema get`) determines what
|
|
5
|
+
happens with the job, so you do not have to guess:
|
|
6
|
+
|
|
7
|
+
- `always_wait` and `start_or_wait` commands resolve the job for you and return
|
|
8
|
+
the final result. You do NOT wait manually.
|
|
9
|
+
- Only a `returns_job` command normally needs you to wait on the job id
|
|
10
|
+
explicitly:
|
|
11
|
+
```bash
|
|
12
|
+
mammoth job wait 55123 --output json --no-input
|
|
13
|
+
mammoth job get 55123 --output json --no-input
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A timeout returns exit code 7 with `recovery_commands` in the envelope that
|
|
17
|
+
re-wait or fetch the job — run those commands. The `--job-timeout` /
|
|
18
|
+
`--pipeline-timeout` options bound the wait.
|
|
19
|
+
|
|
20
|
+
## Draft mode
|
|
21
|
+
Batch several pipeline edits, then submit them together:
|
|
22
|
+
```bash
|
|
23
|
+
mammoth view draft enter 1039 --project 180 --output json --no-input
|
|
24
|
+
mammoth view transform add-column 1039 --project 180 --output json --no-input \
|
|
25
|
+
--input '{"name": "flag", "column_type": "TEXT"}'
|
|
26
|
+
mammoth view draft status 1039 --project 180 --output json --no-input
|
|
27
|
+
mammoth view draft submit 1039 --project 180 --output json --no-input
|
|
28
|
+
# or discard the batch
|
|
29
|
+
mammoth view draft discard 1039 --project 180 --output json --no-input --yes
|
|
30
|
+
```
|
|
31
|
+
Draft state is server-side, so it persists across separate CLI processes.
|
|
32
|
+
|
|
33
|
+
## Bulk replace
|
|
34
|
+
Bulk replace is a reversible pipeline mutation; it needs no `--yes`. Preview or
|
|
35
|
+
re-run through the pipeline commands rather than simulating a dry run.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Machine output and exit codes
|
|
2
|
+
|
|
3
|
+
## Success envelope (stdout)
|
|
4
|
+
```json
|
|
5
|
+
{"schema_version": 1, "data": <result>, "meta": {"command": "project list", "profile": "default", "workspace_id": 4, "project_id": 180, "pagination": null}}
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
## Error envelope (stderr)
|
|
9
|
+
```json
|
|
10
|
+
{"schema_version": 1, "error": {"code": "resource_not_found", "message": "...", "hint": "...", "details": {}, "request_id": null, "retryable": false, "authorization_required": false, "recovery_commands": ["..."]}}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Output modes
|
|
14
|
+
`--output` accepts `table` (default, human), `json`, `yaml`, `ndjson`, `plain`.
|
|
15
|
+
Agents should use `json` (or `ndjson` for streams). Machine modes never emit
|
|
16
|
+
color or progress.
|
|
17
|
+
|
|
18
|
+
## Exit codes
|
|
19
|
+
| code | meaning |
|
|
20
|
+
|---|---|
|
|
21
|
+
| 0 | success |
|
|
22
|
+
| 1 | API error |
|
|
23
|
+
| 2 | usage / input / confirmation failure |
|
|
24
|
+
| 4 | authentication failure |
|
|
25
|
+
| 5 | not found |
|
|
26
|
+
| 6 | conflict |
|
|
27
|
+
| 7 | retryable (network/timeout) |
|
|
28
|
+
| 130 | interrupted |
|
|
29
|
+
|
|
30
|
+
Branch on the exit code and the stable `error.code`; never parse the message.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Error recovery and cleanup
|
|
2
|
+
|
|
3
|
+
## Recover from an error
|
|
4
|
+
Every error envelope carries `error.recovery_commands`: an ordered list of exact
|
|
5
|
+
commands to run next. Prefer them over improvising.
|
|
6
|
+
|
|
7
|
+
| exit | error.code (examples) | next step |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| 4 | not_authenticated, authentication_failed | `mammoth auth login` |
|
|
10
|
+
| 5 | resource_not_found | re-list to find the correct id |
|
|
11
|
+
| 2 | project_required | `mammoth context project use ID` or `--project` |
|
|
12
|
+
| 2 | confirmation_required | re-run with `--yes` (and `--confirm TARGET`) |
|
|
13
|
+
| 7 | retryable_error, timeout | wait, then re-run the recovery command |
|
|
14
|
+
|
|
15
|
+
## Cleanup discipline
|
|
16
|
+
In a shared project, delete only the resources you created, and never touch
|
|
17
|
+
pre-existing ones. Track ids you create and remove them when done:
|
|
18
|
+
```bash
|
|
19
|
+
mammoth dataset delete "$DS" --project 180 --output json --no-input --yes
|
|
20
|
+
mammoth folder delete "$F" --project 180 --output json --no-input --yes
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Discovery when stuck
|
|
24
|
+
```bash
|
|
25
|
+
mammoth capability get GetProjectCheckpoints --output json --no-input
|
|
26
|
+
mammoth schema get view.transform.pivot --output json --no-input
|
|
27
|
+
mammoth doctor --output json --no-input
|
|
28
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Safe mutation
|
|
2
|
+
|
|
3
|
+
Each command carries a reviewed confirmation policy:
|
|
4
|
+
|
|
5
|
+
| policy | how to satisfy it noninteractively |
|
|
6
|
+
|---|---|
|
|
7
|
+
| none | nothing required |
|
|
8
|
+
| prompt_or_yes | pass `--yes` |
|
|
9
|
+
| yes_always | pass `--yes` (always, even at a terminal) |
|
|
10
|
+
| confirm_target | pass `--yes` and `--confirm TARGET` (exact match) |
|
|
11
|
+
|
|
12
|
+
Under `--no-input` (and in `json`/`ndjson` modes) there is no prompt: a missing
|
|
13
|
+
`--yes`/`--confirm` fails with exit code 2 and error code
|
|
14
|
+
`confirmation_required` or `confirmation_target_mismatch`.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# normal delete
|
|
18
|
+
mammoth folder delete 7 --project 180 --output json --no-input --yes
|
|
19
|
+
|
|
20
|
+
# high-impact: target must equal the resource
|
|
21
|
+
mammoth project user remove --project 180 --output json --no-input \
|
|
22
|
+
--yes --confirm 180 --input '{"user_ids": ["u_123"]}'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Discover a command's policy with `mammoth schema get <command.id> --output json`.
|
|
26
|
+
Never retry a mutation blindly; only retry on exit code 7 (retryable).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""commands layer for the Mammoth CLI.
|
|
2
|
+
|
|
3
|
+
Assembles :data:`BESPOKE`: fully-typed Typer command callbacks that override
|
|
4
|
+
the generic manifest leaf for the command ids they implement. ``app.py``
|
|
5
|
+
imports this module and swaps in a bespoke callback wherever one exists,
|
|
6
|
+
registering it at the exact same manifest path and name as the generic leaf
|
|
7
|
+
it replaces.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
|
|
14
|
+
from mammoth_cli.commands import auth as auth_cmd
|
|
15
|
+
from mammoth_cli.commands import config as config_cmd
|
|
16
|
+
from mammoth_cli.commands import context as context_cmd
|
|
17
|
+
|
|
18
|
+
BESPOKE: dict[str, Callable[..., None]] = {
|
|
19
|
+
"auth.login": auth_cmd.auth_login,
|
|
20
|
+
"auth.status": auth_cmd.auth_status,
|
|
21
|
+
"auth.logout": auth_cmd.auth_logout,
|
|
22
|
+
"config.get": config_cmd.config_get,
|
|
23
|
+
"config.set": config_cmd.config_set,
|
|
24
|
+
"config.list": config_cmd.config_list,
|
|
25
|
+
"config.path": config_cmd.config_path,
|
|
26
|
+
"context.project.status": context_cmd.context_project_status,
|
|
27
|
+
"context.project.use": context_cmd.context_project_use,
|
|
28
|
+
"context.project.clear": context_cmd.context_project_clear,
|
|
29
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Handlers for the ``activity`` command family (workspace-scoped).
|
|
2
|
+
|
|
3
|
+
Activity logs belong to the authenticated workspace; the SDK client already
|
|
4
|
+
carries the workspace id, so handlers never forward it explicitly. Both
|
|
5
|
+
commands are read-only: ``list`` returns a page of log entries and ``export``
|
|
6
|
+
kicks off a workspace export job. All filters are optional and forwarded only
|
|
7
|
+
when present in the strict ``--input`` document. Handlers dispatch through the
|
|
8
|
+
generic :meth:`~mammoth_cli.services.protocol.MammothService.call` seam to the
|
|
9
|
+
public 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 CODE_SDK_SYMBOL_UNRESOLVED, EXIT_USAGE, CliError
|
|
17
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
18
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
19
|
+
from mammoth_cli.runtime.session import open_service, resolved_project
|
|
20
|
+
|
|
21
|
+
HandlerResult = tuple[Any, dict[str, Any]]
|
|
22
|
+
|
|
23
|
+
_LIST_OPTIONAL = (
|
|
24
|
+
"limit",
|
|
25
|
+
"offset",
|
|
26
|
+
"sort",
|
|
27
|
+
"project_id",
|
|
28
|
+
"categories",
|
|
29
|
+
"activities",
|
|
30
|
+
"resource_id",
|
|
31
|
+
"result",
|
|
32
|
+
"start_time",
|
|
33
|
+
"end_time",
|
|
34
|
+
"origin",
|
|
35
|
+
"user_ids",
|
|
36
|
+
"parent_id",
|
|
37
|
+
"search_text",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
_EXPORT_OPTIONAL = (
|
|
41
|
+
"format",
|
|
42
|
+
"start_time",
|
|
43
|
+
"end_time",
|
|
44
|
+
"categories",
|
|
45
|
+
"activities",
|
|
46
|
+
"user_ids",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _symbol(invocation: Invocation) -> str:
|
|
51
|
+
"""Return the reviewed backing SDK symbol for this command.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
invocation: The current command's resolved global options.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The dotted SDK symbol recorded in the command manifest.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
CliError: ``sdk_symbol_unresolved`` when the manifest has no symbol
|
|
61
|
+
for this command.
|
|
62
|
+
"""
|
|
63
|
+
record = command_by_id(invocation.command_id)
|
|
64
|
+
if record is None or not record.get("sdk_symbol"):
|
|
65
|
+
raise CliError(
|
|
66
|
+
code=CODE_SDK_SYMBOL_UNRESOLVED,
|
|
67
|
+
message=f"No SDK symbol is recorded for '{invocation.command_id}'.",
|
|
68
|
+
exit_status=EXIT_USAGE,
|
|
69
|
+
)
|
|
70
|
+
return str(record["sdk_symbol"])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _forward_optional(
|
|
74
|
+
document: dict[str, Any], kwargs: dict[str, Any], fields: tuple[str, ...]
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Copy each present field from ``document`` into ``kwargs`` unchanged.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
document: The parsed ``--input`` document.
|
|
80
|
+
kwargs: The keyword-argument mapping being built for the SDK call.
|
|
81
|
+
fields: The optional field names to forward when present.
|
|
82
|
+
"""
|
|
83
|
+
for field in fields:
|
|
84
|
+
if field in document:
|
|
85
|
+
kwargs[field] = document[field]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _meta(invocation: Invocation, workspace_id: int, project_id: int | None) -> dict[str, Any]:
|
|
89
|
+
"""Build the common envelope metadata for an activity command.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
invocation: The current command's resolved global options.
|
|
93
|
+
workspace_id: The authenticated workspace id.
|
|
94
|
+
project_id: The active project id, or None when not resolved.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The envelope metadata mapping.
|
|
98
|
+
"""
|
|
99
|
+
return {
|
|
100
|
+
"profile": invocation.profile,
|
|
101
|
+
"workspace_id": workspace_id,
|
|
102
|
+
"project_id": project_id,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def activity_list(invocation: Invocation) -> HandlerResult:
|
|
107
|
+
"""List activity logs in the active workspace, with optional filters."""
|
|
108
|
+
document = invocation.load_input() or {}
|
|
109
|
+
kwargs: dict[str, Any] = {}
|
|
110
|
+
_forward_optional(document, kwargs, _LIST_OPTIONAL)
|
|
111
|
+
with open_service(invocation) as (service, auth):
|
|
112
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
113
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def activity_export(invocation: Invocation) -> HandlerResult:
|
|
117
|
+
"""Export activity logs from the active workspace, with optional filters."""
|
|
118
|
+
document = invocation.load_input() or {}
|
|
119
|
+
kwargs: dict[str, Any] = {}
|
|
120
|
+
_forward_optional(document, kwargs, _EXPORT_OPTIONAL)
|
|
121
|
+
with open_service(invocation) as (service, auth):
|
|
122
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
123
|
+
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Handlers for the ``addon`` command family.
|
|
2
|
+
|
|
3
|
+
None of these commands are project-scoped: every mutation acts on the
|
|
4
|
+
workspace's addon set (connector addons, storage, and user seats) resolved
|
|
5
|
+
from the caller's credentials. ``addon list`` is a plain read. Every mutation
|
|
6
|
+
is ``high_impact`` and requires ``--yes --confirm WORKSPACE_ID``, enforced
|
|
7
|
+
against the authenticated ``auth.workspace_id`` after the service is opened
|
|
8
|
+
(mirroring ``project.py``'s ``project_bulk_update`` and ``workspace.py``'s
|
|
9
|
+
``workspace_update``). All request fields come from the strict ``--input``
|
|
10
|
+
document; none of these commands take a positional argument. Handlers
|
|
11
|
+
dispatch through the generic
|
|
12
|
+
:meth:`~mammoth_cli.services.protocol.MammothService.call` seam to the public
|
|
13
|
+
SDK method named by the command's reviewed manifest ``sdk_symbol``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from mammoth_cli.errors.envelope import (
|
|
21
|
+
CODE_MISSING_FIELD,
|
|
22
|
+
CODE_SDK_SYMBOL_UNRESOLVED,
|
|
23
|
+
EXIT_USAGE,
|
|
24
|
+
CliError,
|
|
25
|
+
)
|
|
26
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
27
|
+
from mammoth_cli.runtime.confirm import POLICY_CONFIRM_TARGET, enforce_confirmation
|
|
28
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
29
|
+
from mammoth_cli.runtime.session import open_service
|
|
30
|
+
|
|
31
|
+
HandlerResult = tuple[Any, dict[str, Any]]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _symbol(invocation: Invocation) -> str:
|
|
35
|
+
"""Return the reviewed backing SDK symbol for this command."""
|
|
36
|
+
record = command_by_id(invocation.command_id)
|
|
37
|
+
if record is None or not record.get("sdk_symbol"):
|
|
38
|
+
raise CliError(
|
|
39
|
+
code=CODE_SDK_SYMBOL_UNRESOLVED,
|
|
40
|
+
message=f"No SDK symbol is recorded for '{invocation.command_id}'.",
|
|
41
|
+
exit_status=EXIT_USAGE,
|
|
42
|
+
)
|
|
43
|
+
return str(record["sdk_symbol"])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _require_field(document: dict[str, Any] | None, field: str) -> Any:
|
|
47
|
+
"""Return a required field from the ``--input`` document, or raise usage."""
|
|
48
|
+
if document is None or field not in document:
|
|
49
|
+
raise CliError(
|
|
50
|
+
code=CODE_MISSING_FIELD,
|
|
51
|
+
message=f"This command requires the '{field}' input field.",
|
|
52
|
+
exit_status=EXIT_USAGE,
|
|
53
|
+
hint=f"Pass it via --input, for example: --input '{{\"{field}\": ...}}'.",
|
|
54
|
+
)
|
|
55
|
+
return document[field]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _forward_optional(
|
|
59
|
+
document: dict[str, Any], kwargs: dict[str, Any], fields: tuple[str, ...]
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Copy each present field from ``document`` into ``kwargs`` under the same name."""
|
|
62
|
+
for field in fields:
|
|
63
|
+
if field in document:
|
|
64
|
+
kwargs[field] = document[field]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _meta(invocation: Invocation, workspace_id: int) -> dict[str, Any]:
|
|
68
|
+
"""Build the common envelope metadata for an addon command (no project scope)."""
|
|
69
|
+
return {
|
|
70
|
+
"profile": invocation.profile,
|
|
71
|
+
"workspace_id": workspace_id,
|
|
72
|
+
"project_id": None,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def addon_connector_add(invocation: Invocation) -> HandlerResult:
|
|
77
|
+
"""Add one or more connector addons. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
78
|
+
document = invocation.load_input() or {}
|
|
79
|
+
kwargs: dict[str, Any] = {}
|
|
80
|
+
_forward_optional(document, kwargs, ("connector_id", "connector_ids"))
|
|
81
|
+
with open_service(invocation) as (service, auth):
|
|
82
|
+
enforce_confirmation(
|
|
83
|
+
invocation,
|
|
84
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
85
|
+
action=f"add connector addon(s) to workspace {auth.workspace_id}",
|
|
86
|
+
target=str(auth.workspace_id),
|
|
87
|
+
)
|
|
88
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
89
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def addon_connector_remove(invocation: Invocation) -> HandlerResult:
|
|
93
|
+
"""Remove one or more connector addons. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
94
|
+
document = invocation.load_input() or {}
|
|
95
|
+
kwargs: dict[str, Any] = {}
|
|
96
|
+
_forward_optional(document, kwargs, ("connector_id", "connector_ids"))
|
|
97
|
+
with open_service(invocation) as (service, auth):
|
|
98
|
+
enforce_confirmation(
|
|
99
|
+
invocation,
|
|
100
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
101
|
+
action=f"remove connector addon(s) from workspace {auth.workspace_id}",
|
|
102
|
+
target=str(auth.workspace_id),
|
|
103
|
+
)
|
|
104
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
105
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def addon_list(invocation: Invocation) -> HandlerResult:
|
|
109
|
+
"""List active addons for the workspace."""
|
|
110
|
+
with open_service(invocation) as (service, auth):
|
|
111
|
+
data = service.call(_symbol(invocation))
|
|
112
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def addon_storage_add(invocation: Invocation) -> HandlerResult:
|
|
116
|
+
"""Add storage capacity. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
117
|
+
document = invocation.load_input()
|
|
118
|
+
additional_storage_gb = _require_field(document, "additional_storage_gb")
|
|
119
|
+
with open_service(invocation) as (service, auth):
|
|
120
|
+
enforce_confirmation(
|
|
121
|
+
invocation,
|
|
122
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
123
|
+
action=f"add storage to workspace {auth.workspace_id}",
|
|
124
|
+
target=str(auth.workspace_id),
|
|
125
|
+
)
|
|
126
|
+
data = service.call(_symbol(invocation), additional_storage_gb=additional_storage_gb)
|
|
127
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def addon_storage_remove(invocation: Invocation) -> HandlerResult:
|
|
131
|
+
"""Remove storage capacity. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
132
|
+
document = invocation.load_input()
|
|
133
|
+
removal_storage_gb = _require_field(document, "removal_storage_gb")
|
|
134
|
+
with open_service(invocation) as (service, auth):
|
|
135
|
+
enforce_confirmation(
|
|
136
|
+
invocation,
|
|
137
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
138
|
+
action=f"remove storage from workspace {auth.workspace_id}",
|
|
139
|
+
target=str(auth.workspace_id),
|
|
140
|
+
)
|
|
141
|
+
data = service.call(_symbol(invocation), removal_storage_gb=removal_storage_gb)
|
|
142
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def addon_user_add(invocation: Invocation) -> HandlerResult:
|
|
146
|
+
"""Add user seats. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
147
|
+
document = invocation.load_input() or {}
|
|
148
|
+
kwargs: dict[str, Any] = {}
|
|
149
|
+
_forward_optional(document, kwargs, ("user_count",))
|
|
150
|
+
with open_service(invocation) as (service, auth):
|
|
151
|
+
enforce_confirmation(
|
|
152
|
+
invocation,
|
|
153
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
154
|
+
action=f"add user seats to workspace {auth.workspace_id}",
|
|
155
|
+
target=str(auth.workspace_id),
|
|
156
|
+
)
|
|
157
|
+
data = service.call(_symbol(invocation), **kwargs)
|
|
158
|
+
return data, _meta(invocation, auth.workspace_id)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def addon_user_remove(invocation: Invocation) -> HandlerResult:
|
|
162
|
+
"""Remove user seats. High-impact: ``--yes --confirm WORKSPACE_ID``."""
|
|
163
|
+
document = invocation.load_input()
|
|
164
|
+
user_count = _require_field(document, "user_count")
|
|
165
|
+
with open_service(invocation) as (service, auth):
|
|
166
|
+
enforce_confirmation(
|
|
167
|
+
invocation,
|
|
168
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
169
|
+
action=f"remove user seats from workspace {auth.workspace_id}",
|
|
170
|
+
target=str(auth.workspace_id),
|
|
171
|
+
)
|
|
172
|
+
data = service.call(_symbol(invocation), user_count=user_count)
|
|
173
|
+
return data, _meta(invocation, auth.workspace_id)
|