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,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)