mammoth-cli 2.2.9__tar.gz → 2.2.10__tar.gz

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 (253) hide show
  1. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/PKG-INFO +2 -2
  2. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +9 -10
  4. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +2 -2
  5. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/dashboard.py +13 -7
  6. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/dataset.py +77 -6
  7. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/file.py +12 -0
  8. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/folder.py +22 -27
  9. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/project.py +30 -4
  10. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view.py +32 -9
  11. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view_profile.py +12 -2
  12. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dryrun_targets.py +46 -5
  13. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/listing.py +1 -1
  14. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/sdk_service.py +5 -1
  15. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/pyproject.toml +2 -2
  16. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/sdk-methods.yaml +12 -0
  17. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/.gitignore +0 -0
  18. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/LICENSE +0 -0
  19. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/README.md +0 -0
  20. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/__main__.py +0 -0
  21. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/app.py +0 -0
  22. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
  23. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
  24. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  25. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  26. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  27. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  28. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  29. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  30. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  31. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  32. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  33. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  34. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  35. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  36. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  37. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
  38. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  39. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  40. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  41. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  42. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  43. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  44. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
  45. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
  46. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  47. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  48. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  49. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  50. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  51. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  52. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  53. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  54. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
  55. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  56. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  57. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  58. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  59. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  60. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  61. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  62. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  63. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  64. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  65. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  66. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  67. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  68. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  69. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  70. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
  71. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  72. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  73. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  74. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  75. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  76. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  77. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  78. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  79. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  80. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  81. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  82. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
  83. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  84. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
  85. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  86. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  87. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  88. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
  89. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  90. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  91. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  92. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  93. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  94. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  95. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  96. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/__init__.py +0 -0
  97. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/activity.py +0 -0
  98. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/addon.py +0 -0
  99. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/agent.py +0 -0
  100. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/ai.py +0 -0
  101. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/annotation.py +0 -0
  102. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/auth.py +0 -0
  103. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/automation.py +0 -0
  104. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/batch.py +0 -0
  105. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/billing.py +0 -0
  106. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/browse.py +0 -0
  107. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/calc.py +0 -0
  108. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/capability.py +0 -0
  109. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/client_app.py +0 -0
  110. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/completion.py +0 -0
  111. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/config.py +0 -0
  112. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/connector.py +0 -0
  113. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/context.py +0 -0
  114. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/data_app.py +0 -0
  115. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/doctor.py +0 -0
  116. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/external_key.py +0 -0
  117. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/job.py +0 -0
  118. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/link.py +0 -0
  119. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/notification.py +0 -0
  120. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/parameter.py +0 -0
  121. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/registry.py +0 -0
  122. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/report.py +0 -0
  123. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/schedule.py +0 -0
  124. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/schema.py +0 -0
  125. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/skill.py +0 -0
  126. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/snippet.py +0 -0
  127. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/support.py +0 -0
  128. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/template.py +0 -0
  129. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/trash.py +0 -0
  130. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/upgrade.py +0 -0
  131. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/user.py +0 -0
  132. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view_ops.py +0 -0
  133. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/webhook.py +0 -0
  134. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/workflow.py +0 -0
  135. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/workspace.py +0 -0
  136. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/__init__.py +0 -0
  137. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/credentials.py +0 -0
  138. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/endpoint.py +0 -0
  139. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/profiles.py +0 -0
  140. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/resolver.py +0 -0
  141. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/contracts/__init__.py +0 -0
  142. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/contracts/auth.py +0 -0
  143. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/embed.py +0 -0
  144. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/errors/__init__.py +0 -0
  145. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/errors/envelope.py +0 -0
  146. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/manifest/__init__.py +0 -0
  147. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/manifest/loader.py +0 -0
  148. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/messages/__init__.py +0 -0
  149. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/__init__.py +0 -0
  150. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/envelope.py +0 -0
  151. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/normalize.py +0 -0
  152. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/policy.py +0 -0
  153. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/progress.py +0 -0
  154. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/render.py +0 -0
  155. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/py.typed +0 -0
  156. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/__init__.py +0 -0
  157. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/confirm.py +0 -0
  158. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dataset_health.py +0 -0
  159. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dryrun.py +0 -0
  160. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/embedded.py +0 -0
  161. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/executor.py +0 -0
  162. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/input_loader.py +0 -0
  163. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/intent_only.py +0 -0
  164. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/invocation.py +0 -0
  165. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/new_data.py +0 -0
  166. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/options.py +0 -0
  167. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/parents.py +0 -0
  168. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/runlog.py +0 -0
  169. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/session.py +0 -0
  170. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/state.py +0 -0
  171. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/strict.py +0 -0
  172. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/updates.py +0 -0
  173. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/validate.py +0 -0
  174. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/verify.py +0 -0
  175. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/__init__.py +0 -0
  176. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/argspec.py +0 -0
  177. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/board_values.py +0 -0
  178. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/coerce.py +0 -0
  179. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/command_contract.py +0 -0
  180. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/conditions.py +0 -0
  181. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_filters.py +0 -0
  182. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_pages.py +0 -0
  183. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_review.py +0 -0
  184. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/data_profile.py +0 -0
  185. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/data_quality.py +0 -0
  186. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dispatch.py +0 -0
  187. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/factory.py +0 -0
  188. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/input_fields.py +0 -0
  189. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/mapping.py +0 -0
  190. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/openapi_types.py +0 -0
  191. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/positionals.py +0 -0
  192. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/protocol.py +0 -0
  193. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/read_queries.py +0 -0
  194. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/stored_stats.py +0 -0
  195. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/testing.py +0 -0
  196. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/text_dates.py +0 -0
  197. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/type_system.py +0 -0
  198. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/write_impact.py +0 -0
  199. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/__init__.py +0 -0
  200. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/installer.py +0 -0
  201. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/steering.py +0 -0
  202. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/testing.py +0 -0
  203. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/activity.yaml +0 -0
  204. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/addon.yaml +0 -0
  205. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/agent.yaml +0 -0
  206. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/ai.yaml +0 -0
  207. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/annotation.yaml +0 -0
  208. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/auth.yaml +0 -0
  209. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/automation.yaml +0 -0
  210. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/batch.yaml +0 -0
  211. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/billing.yaml +0 -0
  212. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/browse.yaml +0 -0
  213. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/calc.yaml +0 -0
  214. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/capability.yaml +0 -0
  215. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/client-app.yaml +0 -0
  216. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/completion.yaml +0 -0
  217. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/config.yaml +0 -0
  218. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/connector.yaml +0 -0
  219. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/context.yaml +0 -0
  220. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/dashboard.yaml +0 -0
  221. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/data-app.yaml +0 -0
  222. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/dataset.yaml +0 -0
  223. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/doctor.yaml +0 -0
  224. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/external-key.yaml +0 -0
  225. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/file.yaml +0 -0
  226. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/folder.yaml +0 -0
  227. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/job.yaml +0 -0
  228. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/link.yaml +0 -0
  229. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/log.yaml +0 -0
  230. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/notification.yaml +0 -0
  231. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/parameter.yaml +0 -0
  232. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/project.yaml +0 -0
  233. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/report.yaml +0 -0
  234. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/schedule.yaml +0 -0
  235. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/schema.yaml +0 -0
  236. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/skill.yaml +0 -0
  237. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/snippet.yaml +0 -0
  238. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/support.yaml +0 -0
  239. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/template.yaml +0 -0
  240. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/trash.yaml +0 -0
  241. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/upgrade.yaml +0 -0
  242. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/user.yaml +0 -0
  243. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/version.yaml +0 -0
  244. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/view.yaml +0 -0
  245. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/webhook.yaml +0 -0
  246. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/workflow.yaml +0 -0
  247. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/workspace.yaml +0 -0
  248. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/edits-target.source.yaml +0 -0
  249. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/openapi-operations.yaml +0 -0
  250. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/schema-v1.json +0 -0
  251. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/sdk-catalog.source.yaml +62 -62
  252. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/openapi/metadata.json +0 -0
  253. {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/openapi/openapi.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mammoth-cli
3
- Version: 2.2.9
3
+ Version: 2.2.10
4
4
  Summary: Command-line interface for the Mammoth Analytics platform
5
5
  Project-URL: homepage, https://mammoth.io
6
6
  Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
@@ -17,7 +17,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
17
  Requires-Python: <3.15,>=3.12
18
18
  Requires-Dist: jsonschema>=4.20
19
19
  Requires-Dist: keyring<26,>=25.7
20
- Requires-Dist: mammoth-io<0.9,>=0.8.10
20
+ Requires-Dist: mammoth-io<0.9,>=0.8.11
21
21
  Requires-Dist: platformdirs<5,>=4.10
22
22
  Requires-Dist: pydantic<3,>=2.12.5
23
23
  Requires-Dist: pyyaml<7,>=6
@@ -6,7 +6,7 @@ not implement a second HTTP client and does not call private SDK members.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "2.2.9"
9
+ __version__ = "2.2.10"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -1,7 +1,7 @@
1
1
  # What is proven on release
2
2
 
3
3
  Generated from `docs/release-capability-matrix.json`; do not edit by hand.
4
- The CLI publishes 633 commands. 455 of them bind one of the 528 API operations in the matrix; the remainder are local commands (`schema`, `auth`, `doctor`, `log`, ...) or typed variants that share an operation (`view transform *` commands submit through `view.task.add`, except `rename-columns` and `sort`, which set view display properties through `view.update`). 262 bound commands ran once successfully on release, 2 are not supported there, and the rest are untried. Untried is not broken: discover the contract with `mammoth schema get COMMAND_ID`, run it, and treat the structured error envelope as the answer. Use an untried command normally: confirm a write with the user first, check its result with a read afterwards, and never refuse a command only because it is untried.
4
+ The CLI publishes 633 commands. 504 of them bind one of the 545 API operations in the matrix; the remainder are local commands (`schema`, `auth`, `doctor`, `log`, ...) or typed variants that share an operation (`view transform *` commands submit through `view.task.add`, except `rename-columns` and `sort`, which set view display properties through `view.update`). 262 bound commands ran once successfully on release, 1 are not supported there, and the rest are untried. Untried is not broken: discover the contract with `mammoth schema get COMMAND_ID`, run it, and treat the structured error envelope as the answer. Use an untried command normally: confirm a write with the user first, check its result with a read afterwards, and never refuse a command only because it is untried.
5
5
 
6
6
  Status meanings:
7
7
 
@@ -17,20 +17,21 @@ The typed `view transform *` commands all submit through `view.task.add` (`renam
17
17
 
18
18
  | Family | Commands | Ran once | Not supported | Untried |
19
19
  |---|---|---|---|---|
20
- | `dashboard` | 104 | 81 | 2 | 21 |
20
+ | `dashboard` | 122 | 81 | 1 | 40 |
21
+ | `support` | 66 | 9 | 0 | 57 |
21
22
  | `view` | 62 | 58 | 0 | 4 |
22
- | `support` | 45 | 9 | 0 | 36 |
23
+ | `billing` | 24 | 4 | 0 | 20 |
23
24
  | `connector` | 22 | 3 | 0 | 19 |
24
- | `billing` | 21 | 4 | 0 | 17 |
25
- | `workspace` | 19 | 7 | 0 | 12 |
26
- | `dataset` | 17 | 12 | 0 | 5 |
25
+ | `workspace` | 20 | 7 | 0 | 13 |
26
+ | `dataset` | 18 | 12 | 0 | 6 |
27
27
  | `project` | 17 | 15 | 0 | 2 |
28
28
  | `workflow` | 16 | 9 | 0 | 7 |
29
29
  | `parameter` | 14 | 7 | 0 | 7 |
30
30
  | `data-app` | 12 | 1 | 0 | 11 |
31
+ | `automation` | 8 | 3 | 0 | 5 |
32
+ | `browse` | 8 | 1 | 0 | 7 |
31
33
  | `folder` | 8 | 8 | 0 | 0 |
32
34
  | `snippet` | 8 | 6 | 0 | 2 |
33
- | `automation` | 7 | 3 | 0 | 4 |
34
35
  | `user` | 7 | 3 | 0 | 4 |
35
36
  | `webhook` | 7 | 5 | 0 | 2 |
36
37
  | `addon` | 6 | 0 | 0 | 6 |
@@ -43,7 +44,6 @@ The typed `view transform *` commands all submit through `view.task.add` (`renam
43
44
  | `notification` | 5 | 1 | 0 | 4 |
44
45
  | `schedule` | 5 | 0 | 0 | 5 |
45
46
  | `template` | 5 | 5 | 0 | 0 |
46
- | `browse` | 4 | 1 | 0 | 3 |
47
47
  | `external-key` | 4 | 1 | 0 | 3 |
48
48
  | `trash` | 3 | 3 | 0 | 0 |
49
49
  | `activity` | 2 | 1 | 0 | 1 |
@@ -58,7 +58,6 @@ Ran once: `dashboard.action`, `dashboard.analytics`, `dashboard.archive`, `dashb
58
58
 
59
59
  | Command | State | Note |
60
60
  |---|---|---|
61
- | `dashboard.create` | not supported | Not supported on release: backend returns HTTP 409 4DASH012 DASHBOARD_LEGACY_CREATION_RETIRED (legacy dashboard creation retired). Use dashboard.create-blank or dashboard.v3.generate. Dashboard sweep 2026-09-18, CLI 2.0.12. |
62
61
  | `dashboard.pdf.export` | not supported | Not supported from the CLI on release: backend HTTP 400 4GENR001 requires params.data to be the browser-hydrated DashboardData map and states it cannot rebuild it from stored descriptors (dashboard sweep and Haiku e2e, 2026-09-18). The route works only with a client render pass. |
63
62
  | `dashboard.data.draft` | observed blocker | blocked_missing_fixture: Dashboard 56 (create-blank/v3 engine) has no widgets in its canvas and getDraftData rejects with backend_code 4DASH004 DASHBOARD_WRONG_ENGINE: "This dashbo |
64
63
  | `dashboard.data.published` | observed blocker | blocked_missing_fixture: Same as dashboard.data.draft: backend_code 4DASH004 DASHBOARD_WRONG_ENGINE on /dashboards/56/getPublishData |
@@ -110,7 +109,7 @@ Ran once: `project.bulk-delete`, `project.bulk-update`, `project.checkpoint.list
110
109
 
111
110
  ## `view`
112
111
 
113
- Ran once: `view.active-user.list`, `view.active-user.mark`, `view.ai.generate-data`, `view.ai.profile`, `view.bulk-delete`, `view.checkpoint.create`, `view.checkpoint.delete`, `view.checkpoint.get`, `view.checkpoint.list`, `view.checkpoint.update`, `view.conditional-format.create`, `view.conditional-format.delete-all`, `view.conditional-format.list`, `view.conditional-format.update`, `view.create`, `view.data-check.create`, `view.data-check.delete`, `view.data-check.get`, `view.data-check.list`, `view.data.get`, `view.data.query`, `view.delete`, `view.derivative.create`, `view.derivative.data`, `view.derivative.delete`, `view.derivative.list`, `view.derivative.update`, `view.draft.command`, `view.export.create`, `view.export.delete`, `view.export.get`, `view.export.list`, `view.export.publish-db`, `view.export.update`, `view.exportable-config.apply`, `view.exportable-config.get`, `view.get`, `view.list`, `view.parameter-context`, `view.pipeline.edit`, `view.pipeline.get`, `view.pipeline.items`, `view.pipeline.rerun`, `view.preview`, `view.restore`, `view.task.add`, `view.task.delete`, `view.task.get`, `view.task.list`, `view.task.preview`, `view.task.update`, `view.trash`, `view.version.apply`, `view.version.delete`, `view.version.get`, `view.version.list`, `view.version.update`
112
+ Ran once: `view.active-user.list`, `view.active-user.mark`, `view.ai.generate-data`, `view.ai.profile`, `view.bulk-delete`, `view.checkpoint.create`, `view.checkpoint.delete`, `view.checkpoint.get`, `view.checkpoint.list`, `view.checkpoint.update`, `view.conditional-format.create`, `view.conditional-format.delete-all`, `view.conditional-format.list`, `view.conditional-format.update`, `view.create`, `view.data-check.create`, `view.data-check.delete`, `view.data-check.get`, `view.data-check.list`, `view.data.aggregate`, `view.data.get`, `view.data.query`, `view.delete`, `view.derivative.create`, `view.derivative.data`, `view.derivative.delete`, `view.derivative.list`, `view.derivative.update`, `view.draft.command`, `view.export.create`, `view.export.delete`, `view.export.get`, `view.export.list`, `view.export.publish-db`, `view.export.update`, `view.exportable-config.apply`, `view.exportable-config.get`, `view.get`, `view.list`, `view.parameter-context`, `view.pipeline.edit`, `view.pipeline.get`, `view.pipeline.items`, `view.pipeline.rerun`, `view.preview`, `view.restore`, `view.task.add`, `view.task.delete`, `view.task.get`, `view.task.list`, `view.task.preview`, `view.task.update`, `view.trash`, `view.version.apply`, `view.version.delete`, `view.version.get`, `view.version.list`, `view.version.update`
114
113
 
115
114
  | Command | State | Note |
116
115
  |---|---|---|
@@ -220,7 +220,7 @@ Example: `mammoth view data aggregate 123 --input '{"group_by": ["Channel"], "ag
220
220
 
221
221
  Result: `ViewDataAggregateResult`; mutation `read`, confirmation `none`, wait policy `always_wait`.
222
222
 
223
- Status on release: untried; no live run recorded.
223
+ Status on release: ran once on CLI 2.0.18 — golden-data check 2026-09-19 (CLI 2.0.18): condition on the pivoted view compiled to the backend clause shape and answered (exit 0); EQ with a column select and an AND of CONTAINS/NE also verified read-only on a pre-existing view. Before 2.0.18 the spec was f…
224
224
 
225
225
  ### `view.data.compare`
226
226
 
@@ -270,7 +270,7 @@ Example: `mammoth view data query 123 123`. Placeholders are illustrative; resol
270
270
 
271
271
  Result: `ViewDataQueryResult`; mutation `read`, confirmation `none`, wait policy `always_wait`.
272
272
 
273
- Status on release: ran once on CLI 2.0.18 — golden-data check 2026-09-19 (CLI 2.0.18): condition on the pivoted view compiled to the backend clause shape and answered (exit 0); EQ with a column select and an AND of CONTAINS/NE also verified read-only on a pre-existing view. Before 2.0.18 the spec was f…
273
+ Status on release: ran once on CLI 2.0.28 — ILG simulation 2026-09-19: exit 0 on release with CLI 2.0.28. Paged reads (limit up to 500) used for every value check; envelope data.data rows keyed by display name. Single invocation only.
274
274
 
275
275
  ### `view.delete`
276
276
 
@@ -396,6 +396,10 @@ def dashboard_get(invocation: Invocation) -> HandlerResult:
396
396
  return data, _meta(invocation, auth.workspace_id)
397
397
 
398
398
 
399
+ #: Sources whose owning dataset one ``dashboard get`` looks up (one request each).
400
+ _MAX_SOURCE_LOOKUPS = 20
401
+
402
+
399
403
  def _source_dataset(service: Any, invocation: Invocation, workspace_id: Any, view_id: int) -> Any:
400
404
  """The dataset that owns ``view_id``: remembered, else looked up; never raises."""
401
405
  profile_name = invocation.profile or profiles.get_selected()
@@ -422,16 +426,18 @@ def _with_source_datasets(
422
426
  sources = data.get("sources") if isinstance(data, dict) else None
423
427
  if not isinstance(sources, list) or not sources:
424
428
  return data
429
+ view_ids = [view_id for view_id in sources if isinstance(view_id, int)]
425
430
  views = [
426
431
  _source_dataset(service, invocation, workspace_id, view_id)
427
- for view_id in sources
428
- if isinstance(view_id, int)
432
+ for view_id in view_ids[:_MAX_SOURCE_LOOKUPS]
429
433
  ]
430
- return {
431
- **data,
432
- "source_views": views,
433
- "sources_note": "sources are view ids; use a source's dataset_id for dataset commands.",
434
- }
434
+ note = "sources are view ids; use a source's dataset_id for dataset commands."
435
+ if len(view_ids) > _MAX_SOURCE_LOOKUPS:
436
+ note += (
437
+ f" Only the first {_MAX_SOURCE_LOOKUPS} of {len(view_ids)} sources were labelled;"
438
+ " read the others with 'view get VIEW_ID'."
439
+ )
440
+ return {**data, "source_views": views, "sources_note": note}
435
441
 
436
442
 
437
443
  def _with_board_project(data: Any) -> Any:
@@ -196,6 +196,63 @@ def _find_in_projects(
196
196
  return matches
197
197
 
198
198
 
199
+ #: Pages of resource search read for one name (100 hits each); past it the result says so.
200
+ _SEARCH_PAGES = 5
201
+ _SEARCH_PAGE_SIZE = 100
202
+
203
+
204
+ def search_hits(
205
+ service: Any, resource_type: str, needle: str, project_id: int | None = None
206
+ ) -> tuple[list[dict[str, Any]], bool]:
207
+ """Resources of ``resource_type`` whose name contains ``needle``, from the search route.
208
+
209
+ One request (one more per 100 hits) replaces a listing per project: the
210
+ workspace search, or the project's own resource list when ``project_id`` is
211
+ given. Returns the minimal rows and whether the read stopped at
212
+ ``_SEARCH_PAGES`` pages with more hits unread.
213
+ """
214
+ symbol = (
215
+ "mammoth.api.browse.BrowseAPI.resources_search"
216
+ if project_id is None
217
+ else "mammoth.api.browse.BrowseAPI.resources_list"
218
+ )
219
+ scope: dict[str, Any] = {} if project_id is None else {"project_id": project_id}
220
+ rows: list[dict[str, Any]] = []
221
+ cursor: str | None = None
222
+ for _ in range(_SEARCH_PAGES):
223
+ page = service.call(
224
+ symbol,
225
+ search=needle,
226
+ resource_type=resource_type,
227
+ limit=_SEARCH_PAGE_SIZE,
228
+ cursor=cursor,
229
+ fields="minimal",
230
+ **scope,
231
+ )
232
+ rows += [r for r in page.get("resources", []) if isinstance(r, dict)]
233
+ cursor = page.get("next_cursor")
234
+ if not (page.get("has_more") and cursor):
235
+ return rows, False
236
+ return rows, True
237
+
238
+
239
+ def search_cut_note(kind: str) -> str:
240
+ """Said when a name search stopped early, with how to narrow it."""
241
+ return (
242
+ f"The name matched more than {_SEARCH_PAGES * _SEARCH_PAGE_SIZE} {kind}, so only the "
243
+ "first of them were read. Use a longer name, or --project, to narrow it."
244
+ )
245
+
246
+
247
+ def _projects_holding(
248
+ service: Any, needle: str, visible: list[dict[str, Any]]
249
+ ) -> tuple[list[dict[str, Any]], bool]:
250
+ """The visible projects that hold a dataset whose name contains ``needle``."""
251
+ rows, cut = search_hits(service, "datasource", needle)
252
+ holding = {r.get("project_id") for r in rows}
253
+ return [p for p in visible if p.get("id") in holding], cut
254
+
255
+
199
256
  def _other_projects(visible: list[dict[str, Any]], project_id: int) -> list[dict[str, Any]]:
200
257
  """Every visible project except ``project_id``."""
201
258
  return [p for p in visible if p.get("id") != project_id]
@@ -232,16 +289,20 @@ def dataset_find(invocation: Invocation) -> HandlerResult:
232
289
  skipped: list[dict[str, Any]] = []
233
290
  with open_service(invocation) as (service, auth):
234
291
  visible = _visible_projects(service)
292
+ cut = False
235
293
  if invocation.project is not None:
236
294
  projects: list[dict[str, Any]] = [named_project(service, invocation.project, visible)]
237
295
  matches = _find_in_projects(service, needle, projects, skipped)
238
296
  if not matches:
239
- others = _other_projects(visible, invocation.project)
240
- matches = _find_in_projects(service, needle, others, skipped)
241
- projects += others
297
+ holding, cut = _projects_holding(service, needle, visible)
298
+ matches = _find_in_projects(
299
+ service, needle, _other_projects(holding, invocation.project), skipped
300
+ )
301
+ projects += _other_projects(visible, invocation.project)
242
302
  else:
243
303
  projects = visible
244
- matches = _find_in_projects(service, needle, projects, skipped)
304
+ holding, cut = _projects_holding(service, needle, visible)
305
+ matches = _find_in_projects(service, needle, holding, skipped)
245
306
  meta = {
246
307
  "profile": invocation.profile,
247
308
  "workspace_id": auth.workspace_id,
@@ -253,7 +314,11 @@ def dataset_find(invocation: Invocation) -> HandlerResult:
253
314
  }
254
315
  if skipped:
255
316
  result["projects_skipped"] = skipped
256
- if note := ambiguity_note(len(matches), name_substring):
317
+ notes = [ambiguity_note(len(matches), name_substring)]
318
+ if cut:
319
+ result["truncated"] = True
320
+ notes.append(search_cut_note("datasets"))
321
+ if note := " ".join(n for n in notes if n):
257
322
  result["note"] = note
258
323
  return result, meta
259
324
 
@@ -304,8 +369,9 @@ def _dataset_name_search(
304
369
  records = data.get("datasets", []) if isinstance(data, dict) else []
305
370
  page = search_page(records, name, int(document.get("offset", 0)), document.get("limit"))
306
371
  if page["matched"] == 0:
372
+ holding, cut = _projects_holding(service, name.lower(), _visible_projects(service))
307
373
  elsewhere = _find_in_projects(
308
- service, name.lower(), _other_projects(_visible_projects(service), project_id)
374
+ service, name.lower(), _other_projects(holding, project_id)
309
375
  )
310
376
  if elsewhere:
311
377
  page["in_other_projects"] = elsewhere
@@ -313,6 +379,11 @@ def _dataset_name_search(
313
379
  f"No dataset in project {project_id} matches; the ones listed in "
314
380
  "in_other_projects do. Run the next call with --project <its project_id>."
315
381
  )
382
+ if cut:
383
+ page["truncated"] = True
384
+ page["note"] = " ".join(
385
+ filter(None, (page.get("note"), search_cut_note("datasets")))
386
+ )
316
387
  page["name"] = name
317
388
  return page, _meta(invocation, auth.workspace_id, project_id)
318
389
 
@@ -237,9 +237,17 @@ def file_upload(invocation: Invocation) -> HandlerResult:
237
237
  # the status the platform holds for each dataset, not an assumed one.
238
238
  result = _upload_result(data, lambda dataset_id: _dataset_status(service, dataset_id))
239
239
  project_id = resolved_project(invocation)
240
+ previewed = 0
240
241
  for entry in result["datasets"]:
241
242
  if entry.get("status") != "ready":
242
243
  continue
244
+ if previewed >= _UPLOAD_PREVIEW_LIMIT:
245
+ entry["preview_skipped"] = (
246
+ f"Only the first {_UPLOAD_PREVIEW_LIMIT} datasets are previewed; "
247
+ f"run 'view list {entry['id']}' to see this one."
248
+ )
249
+ continue
250
+ previewed += 1
243
251
  preview = upload_preview(service, int(entry["id"]), project_id)
244
252
  if "preview_error" in preview:
245
253
  entry["preview_error"] = preview["preview_error"]
@@ -253,6 +261,10 @@ def file_upload(invocation: Invocation) -> HandlerResult:
253
261
  return result, _meta(invocation, auth.workspace_id, resolved_project(invocation))
254
262
 
255
263
 
264
+ #: Uploaded datasets one ``file upload`` previews (several requests each).
265
+ _UPLOAD_PREVIEW_LIMIT = 5
266
+
267
+
256
268
  def _require_local_files(files: Any) -> None:
257
269
  """Fail as a usage error, naming the path, when a local file is missing.
258
270
 
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  from typing import Any
13
13
 
14
- from mammoth_cli.commands.dataset import named_project
14
+ from mammoth_cli.commands.dataset import named_project, search_cut_note, search_hits
15
15
  from mammoth_cli.errors.envelope import (
16
16
  CODE_INVALID_ARGUMENT,
17
17
  CODE_MISSING_ARGUMENT,
@@ -107,45 +107,40 @@ def _require_string_positional(invocation: Invocation, name: str) -> str:
107
107
  def folder_find(invocation: Invocation) -> HandlerResult:
108
108
  """Search folder names for a substring across every visible project.
109
109
 
110
- Read-only local composite: lists the projects the credential can see (or
111
- just the one named by ``--project``), then lists folders in each and
112
- keeps a case-insensitive substring match. Does not require an active
113
- project.
110
+ Read-only local composite: one resource-search request (the workspace's, or
111
+ the project's with ``--project``) returns every folder whose name contains
112
+ the substring, case-insensitively, and the projects the credential can see
113
+ name them. Does not require an active project.
114
114
  """
115
115
  name_substring = _require_string_positional(invocation, "name substring")
116
- needle = name_substring.lower()
117
- matches: list[dict[str, Any]] = []
118
116
  with open_service(invocation) as (service, auth):
119
117
  visible = list(service.list_all_projects())
120
118
  if invocation.project is not None:
121
119
  projects: list[dict[str, Any]] = [named_project(service, invocation.project, visible)]
122
120
  else:
123
121
  projects = visible
124
- for project in projects:
125
- project_id = project.get("id")
126
- if project_id is None:
127
- continue
128
- project_name = project.get("name")
129
- for folder in _project_folders(service, project_id):
130
- name = folder.get("name") if isinstance(folder, dict) else None
131
- if isinstance(name, str) and needle in name.lower():
132
- matches.append(
133
- {
134
- "project_id": project_id,
135
- "project_name": project_name,
136
- "id": folder.get("id"),
137
- "name": name,
138
- }
139
- )
122
+ rows, cut = search_hits(service, "label", name_substring, invocation.project)
123
+ names = {p.get("id"): p.get("name") for p in projects}
124
+ matches = [
125
+ {
126
+ "project_id": row["project_id"],
127
+ "project_name": names[row["project_id"]],
128
+ "id": row.get("object_id"),
129
+ "name": row.get("name"),
130
+ }
131
+ for row in rows
132
+ if row.get("project_id") in names
133
+ ]
140
134
  meta = {
141
135
  "profile": invocation.profile,
142
136
  "workspace_id": auth.workspace_id,
143
137
  "project_id": invocation.project,
144
138
  }
145
- return {
146
- "matches": matches,
147
- "projects_searched": len(projects),
148
- }, meta
139
+ result: dict[str, Any] = {"matches": matches, "projects_searched": len(projects)}
140
+ if cut:
141
+ result["truncated"] = True
142
+ result["note"] = search_cut_note("folders")
143
+ return result, meta
149
144
 
150
145
 
151
146
  def folder_list(invocation: Invocation) -> HandlerResult:
@@ -599,11 +599,19 @@ def _datasets_to_check(service: Any, project_id: int, scoped: int | None) -> lis
599
599
  """Every dataset of the project, or only the named one -- never a walk when scoped."""
600
600
  if scoped is not None:
601
601
  return [{"id": scoped, "name": f"dataset {scoped}"}]
602
- listing = service.call(_DATASETS_LIST_SYMBOL, project_id=project_id)
602
+ listing = service.call(_DATASETS_LIST_SYMBOL, project_id=project_id, sort="(created_at:desc)")
603
603
  return listing.get("datasets", []) if isinstance(listing, dict) else []
604
604
 
605
605
 
606
- _CHECK_WORKERS = 6
606
+ _CHECK_WORKERS = 2
607
+ #: Most recent datasets and dashboards one ``project check`` reads (several requests each).
608
+ _CHECK_MAX_DATASETS = 20
609
+ _CHECK_MAX_DASHBOARDS = 10
610
+
611
+
612
+ def _not_checked_line(kind: str, omitted: int, how: str) -> str:
613
+ """The report line for resources left unchecked by the cap, and how to reach them."""
614
+ return f"{omitted} more {kind} were not checked (only the most recent were); {how}"
607
615
 
608
616
 
609
617
  def _in_worker[T](invocation: Invocation, work: Callable[[Any, Any], T]) -> T:
@@ -751,7 +759,16 @@ def project_check(invocation: Invocation) -> HandlerResult:
751
759
  statuses: list[dict[str, Any]] = []
752
760
  scoped_dataset = _scoped_dataset_id(invocation)
753
761
  with open_service(invocation) as (service, auth):
754
- datasets = _datasets_to_check(service, project_id, scoped_dataset)
762
+ every_dataset = _datasets_to_check(service, project_id, scoped_dataset)
763
+ datasets = every_dataset[:_CHECK_MAX_DATASETS]
764
+ if len(every_dataset) > len(datasets):
765
+ to_report.append(
766
+ _not_checked_line(
767
+ "datasets",
768
+ len(every_dataset) - len(datasets),
769
+ "run 'project check DATASET_ID' for a specific one.",
770
+ )
771
+ )
755
772
  previews = _map_checks(
756
773
  invocation,
757
774
  service,
@@ -787,11 +804,20 @@ def project_check(invocation: Invocation) -> HandlerResult:
787
804
  boards = (
788
805
  [] if scoped_dataset else service.call(_DASHBOARDS_LIST_SYMBOL, project_id=project_id)
789
806
  )
807
+ every_board = [b for b in boards if isinstance(b, dict)] if isinstance(boards, list) else []
808
+ if len(every_board) > _CHECK_MAX_DASHBOARDS:
809
+ to_report.append(
810
+ _not_checked_line(
811
+ "dashboards",
812
+ len(every_board) - _CHECK_MAX_DASHBOARDS,
813
+ "check one with 'dashboard get DASHBOARD_ID'.",
814
+ )
815
+ )
790
816
  results = _map_checks(
791
817
  invocation,
792
818
  service,
793
819
  auth,
794
- [b for b in boards if isinstance(b, dict)] if isinstance(boards, list) else [],
820
+ every_board[:_CHECK_MAX_DASHBOARDS],
795
821
  _check_dashboard(invocation),
796
822
  )
797
823
  for result in filter(None, results):
@@ -416,9 +416,14 @@ def brief_view_record(record: Any) -> Any:
416
416
  _DATASETS_LIST_ALL_SYMBOL = "mammoth.api.datasets.DatasetsAPI.list_all"
417
417
  #: Soft floor for the no-DATASET_ID ``view list`` walk: whole per-dataset
418
418
  #: pages are pulled and appended until at least this many views have been
419
- #: collected (or every dataset in the project has been visited), so one call
420
- #: never silently returns a single dataset's worth from a large project.
421
- _VIEW_LIST_ALL_DATASETS_MIN_VIEWS = 100
419
+ #: collected (or the visit cap below is reached), so one call never returns a
420
+ #: single dataset's worth from a large project. Sized to what the agent output
421
+ #: cap shows (about 15 summaries): reading more only to cut them is waste.
422
+ _VIEW_LIST_ALL_DATASETS_MIN_VIEWS = 20
423
+ #: Most datasets one no-DATASET_ID ``view list`` call reads views of, whether or
424
+ #: not they hold any, so a project of many empty datasets costs a fixed number of
425
+ #: requests. The result carries ``next_dataset_offset`` to resume from.
426
+ _VIEW_LIST_ALL_DATASETS_MAX_VISITS = 25
422
427
 
423
428
 
424
429
  def view_list(invocation: Invocation) -> HandlerResult:
@@ -521,6 +526,11 @@ def _compact_view_list(
521
526
  stop = [d.get("id") for d in datasets].index(dropped)
522
527
  result["next_dataset_offset"] = stop
523
528
  result["datasets_visited"] = stop - start
529
+ if "next_dataset_offset" in result and not summary.get("views_omitted"):
530
+ result["more"] = (
531
+ f"Datasets from position {result['next_dataset_offset']} on were not read; "
532
+ "pass dataset_offset to continue, or list one dataset with 'view list DATASET_ID'."
533
+ )
524
534
  if summary.get("views_omitted"):
525
535
  result["more"] = (
526
536
  "Some views were cut to fit the output cap; list one dataset with "
@@ -540,9 +550,10 @@ def _view_list_across_project(
540
550
 
541
551
  Starts at ``dataset_offset`` (default 0) into the project's dataset
542
552
  list, pulling whole per-dataset view pages until either every dataset
543
- has been visited or at least ``_VIEW_LIST_ALL_DATASETS_MIN_VIEWS`` views
544
- have been collected. ``next_dataset_offset`` names where to resume when
545
- the project holds more datasets than were visited. Returns the result and
553
+ has been visited, at least ``_VIEW_LIST_ALL_DATASETS_MIN_VIEWS`` views
554
+ have been collected, or ``_VIEW_LIST_ALL_DATASETS_MAX_VISITS`` datasets were
555
+ read. ``next_dataset_offset`` names where to resume when the project holds
556
+ more datasets than were visited. Returns the result and
546
557
  the project's dataset records (with the summary fields when ``compact``).
547
558
  """
548
559
  dataset_offset = int(document.get("dataset_offset", 0))
@@ -568,7 +579,10 @@ def _view_list_across_project(
568
579
  if isinstance(item, dict):
569
580
  item = {**item, "dataset_id": dataset_id}
570
581
  dataviews.append(item)
571
- if len(dataviews) >= _VIEW_LIST_ALL_DATASETS_MIN_VIEWS:
582
+ if (
583
+ len(dataviews) >= _VIEW_LIST_ALL_DATASETS_MIN_VIEWS
584
+ or visited - dataset_offset >= _VIEW_LIST_ALL_DATASETS_MAX_VISITS
585
+ ):
572
586
  break
573
587
  result: dict[str, Any] = {"dataviews": dataviews, "datasets_visited": visited - dataset_offset}
574
588
  if visited < len(datasets):
@@ -2786,11 +2800,20 @@ def view_pipeline_rerun(invocation: Invocation) -> HandlerResult:
2786
2800
  return data, _meta(invocation, auth.workspace_id, None)
2787
2801
 
2788
2802
 
2803
+ #: Seconds ``view pipeline wait`` waits when the caller names no ``timeout`` (the SDK's own
2804
+ #: default is an hour, one request every few seconds for as long as it runs).
2805
+ _PIPELINE_WAIT_TIMEOUT = 300.0
2806
+
2807
+
2789
2808
  def view_pipeline_wait(invocation: Invocation) -> HandlerResult:
2790
- """Wait for a dataview's pipeline to finish running."""
2809
+ """Wait for a dataview's pipeline to finish running.
2810
+
2811
+ Waits up to ``_PIPELINE_WAIT_TIMEOUT`` seconds unless ``timeout`` says more,
2812
+ polling less often the longer it waits.
2813
+ """
2791
2814
  dataview_id = _require_int_positional_at(invocation, 0, "dataview id")
2792
2815
  document = invocation.load_input() or {}
2793
- kwargs: dict[str, Any] = {"dataview_id": dataview_id}
2816
+ kwargs: dict[str, Any] = {"dataview_id": dataview_id, "timeout": _PIPELINE_WAIT_TIMEOUT}
2794
2817
  _forward_optional(document, kwargs, ("dataset_id", "timeout", "poll_interval"))
2795
2818
  with open_service(invocation) as (service, auth):
2796
2819
  data = service.call(_symbol(invocation), **kwargs)
@@ -39,7 +39,10 @@ HandlerResult = tuple[Any, dict[str, Any]]
39
39
  _AGGREGATE = "mammoth.api.dataviews.DataviewsAPI.aggregate"
40
40
  _STORED_STATS = "mammoth.api.ai.AIAPI.generate_profile"
41
41
  _TOTALS_BATCH = 40
42
- _WORKERS = 6
42
+ #: Concurrent aggregate jobs; each is one request plus its polls on the host's own server.
43
+ _WORKERS = 2
44
+ #: Columns profiled when the caller names none (the rest are reported as not profiled).
45
+ _DEFAULT_PROFILE_COLUMNS = 20
43
46
  _DEFAULT_TOP = 5
44
47
  _DEFAULT_DETAIL_LIMIT = 50
45
48
  _ASSOCIATION_LIMIT = 20
@@ -94,7 +97,8 @@ def view_data_profile(invocation: Invocation) -> HandlerResult:
94
97
 
95
98
  def build_profile(scope: _Scope, document: dict[str, Any], workers: int) -> dict[str, Any]:
96
99
  """Read the stored stats, query the rest, and assemble the result."""
97
- names = _selected_columns(scope, document)
100
+ every_name = _selected_columns(scope, document)
101
+ names = every_name if "columns" in document else every_name[:_DEFAULT_PROFILE_COLUMNS]
98
102
  target = document.get("target")
99
103
  if target is not None and target not in scope.types:
100
104
  raise _usage(f"target column {target!r} is not in the view", sorted(scope.types))
@@ -119,6 +123,12 @@ def build_profile(scope: _Scope, document: dict[str, Any], workers: int) -> dict
119
123
  "spelling_variants_checked": variants_checked,
120
124
  "spelling_variants_skipped": variants_skipped,
121
125
  }
126
+ if len(names) < len(every_name):
127
+ result["columns_not_profiled"] = every_name[len(names) :]
128
+ result["columns_not_profiled_note"] = (
129
+ f"Only the first {len(names)} of {len(every_name)} columns were profiled; "
130
+ "pass input 'columns' (names) to profile others."
131
+ )
122
132
  if target:
123
133
  result["target"] = _target_report(
124
134
  scope, str(target), names, facts, tables, live_rows, workers
@@ -417,6 +417,34 @@ def _read_name(
417
417
  return {"type": kind, "id": target_id, "name": name}
418
418
 
419
419
 
420
+ #: Resource kinds one bulk request can name, and the resource type the route calls each.
421
+ BULK_TYPES: dict[str, str] = {"dataset": "datasource", "view": "dataview", "folder": "label"}
422
+ _BULK_SYMBOL = "mammoth.api.browse.BrowseAPI.resources_bulk"
423
+
424
+
425
+ def _read_names(
426
+ service: MammothService, kind: str, ids: list[int], arguments: Mapping[str, Any]
427
+ ) -> list[dict[str, Any]]:
428
+ """Name every id of ``kind``: one bulk request per 100 ids where the route knows the kind.
429
+
430
+ An id the bulk read does not return (missing, or not readable by the
431
+ caller) is read on its own, so the error it raises is the resource's own.
432
+ """
433
+ if kind not in BULK_TYPES or len(ids) < 2:
434
+ return [_read_name(service, kind, item, arguments) for item in ids]
435
+ scope = {"project_id": arguments["project_id"]} if arguments.get("project_id") else {}
436
+ rows = service.call(_BULK_SYMBOL, items=[(BULK_TYPES[kind], item) for item in ids], **scope)
437
+ names = {row.get("object_id"): row.get("name") for row in rows if isinstance(row, Mapping)}
438
+ return [
439
+ (
440
+ {"type": kind, "id": item, "name": names[item]}
441
+ if isinstance(names.get(item), str) and names[item]
442
+ else _read_name(service, kind, item, arguments)
443
+ )
444
+ for item in ids
445
+ ]
446
+
447
+
420
448
  def _path(record: Any, dotted: str) -> object:
421
449
  """Follow ``a.b`` through nested mappings (or attributes)."""
422
450
  for part in dotted.split("."):
@@ -468,15 +496,26 @@ def _unresolved_name(kind: str, target_id: object, symbol: str, key: str) -> Cli
468
496
  )
469
497
 
470
498
 
471
- def _sub_record(service: MammothService, sub: Sub, target: object, args: Mapping[str, Any]) -> Any:
472
- """Read the sub-resource (or find it in its list); None when nothing is declared."""
499
+ def _sub_record(
500
+ service: MammothService,
501
+ sub: Sub,
502
+ target: object,
503
+ args: Mapping[str, Any],
504
+ lists: dict[str, Any],
505
+ ) -> Any:
506
+ """Read the sub-resource (or find it in its list); None when nothing is declared.
507
+
508
+ A list read serves every target of the call, so it is made once (``lists``).
509
+ """
473
510
  if sub.source is None:
474
511
  return None
475
512
  symbol = _symbol_of(sub.source)
476
513
  kwargs = {key: args[key] for key in sub.forward if args.get(key) is not None}
477
514
  if sub.container is None:
478
515
  return service.call(symbol, **{sub.ids_arg: target}, **kwargs)
479
- listed = service.call(symbol, **kwargs)
516
+ if symbol not in lists:
517
+ lists[symbol] = service.call(symbol, **kwargs)
518
+ listed = lists[symbol]
480
519
  items = listed if isinstance(listed, list) else (listed or {}).get(sub.container, [])
481
520
  found = next((i for i in items if str(_path(i, sub.id_field)) == str(target)), None)
482
521
  if found is None:
@@ -499,8 +538,10 @@ def _sub_targets(
499
538
  raise unresolvable_error(command_id, f"the {sub.parent} id is unknown")
500
539
  parent = f"\u201c{_parent_name(service, sub.parent, parent_ids[0], args)}\u201d"
501
540
  targets = []
541
+ lists: dict[str, Any] = {}
502
542
  for target in ids:
503
- label = _label(_sub_record(service, sub, target, args), sub) if target != "all" else None
543
+ record = _sub_record(service, sub, target, args, lists) if target != "all" else None
544
+ label = _label(record, sub) if target != "all" else None
504
545
  shown = f"\u201c{label}\u201d" if label else str(target)
505
546
  layout = sub.name_fmt or ("{parent} \u203a {kind} {label}" if parent else "{kind} {label}")
506
547
  name = layout.format(**{**args, "parent": parent, "kind": sub.kind, "label": shown})
@@ -595,7 +636,7 @@ def resolve_targets(
595
636
  parent = {**arguments}
596
637
  if "dataset_id" not in parent and would_call.get("dataset_id") is not None:
597
638
  parent["dataset_id"] = would_call["dataset_id"]
598
- return [_read_name(service, kind, item, parent) for item in ids] + invites
639
+ return _read_names(service, kind, ids, parent) + invites
599
640
 
600
641
 
601
642
  #: Deletes whose dry run also lists what depends on the resource: command ->