datex-studio-cli 0.5.0__tar.gz → 0.5.2__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 (235) hide show
  1. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.github/workflows/ci.yml +29 -0
  2. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.gitignore +6 -0
  3. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/PKG-INFO +1 -1
  4. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/pyproject.toml +8 -3
  5. datex_studio_cli-0.5.2/scripts/check_wheel_completeness.py +58 -0
  6. datex_studio_cli-0.5.2/scripts/datadog_errors.py +275 -0
  7. datex_studio_cli-0.5.2/scripts/wavelength_query.py +428 -0
  8. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/__init__.py +1 -1
  9. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/cli.py +58 -2
  10. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/explore.py +5 -30
  11. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/reset.py +1 -0
  12. datex_studio_cli-0.5.2/src/dxs/commands/skills.py +442 -0
  13. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/endpoints.py +15 -0
  14. datex_studio_cli-0.5.2/src/dxs/core/quartermaster/__init__.py +5 -0
  15. datex_studio_cli-0.5.2/src/dxs/core/quartermaster/client.py +178 -0
  16. datex_studio_cli-0.5.2/src/dxs/core/skills/__init__.py +1 -0
  17. datex_studio_cli-0.5.2/src/dxs/core/skills/frontmatter.py +30 -0
  18. datex_studio_cli-0.5.2/src/dxs/core/skills/installer.py +148 -0
  19. datex_studio_cli-0.5.2/src/dxs/core/skills/layout.py +57 -0
  20. datex_studio_cli-0.5.2/src/dxs/core/skills/lock.py +98 -0
  21. datex_studio_cli-0.5.2/src/dxs/core/skills/resolver.py +48 -0
  22. datex_studio_cli-0.5.2/src/dxs/core/skills/staleness.py +55 -0
  23. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/404/index.html +1 -1
  24. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/404.html +1 -1
  25. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/design/capture/index.html +1 -1
  26. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/design/capture/index.txt +1 -1
  27. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/design/index.html +1 -1
  28. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/design/index.txt +1 -1
  29. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/index.html +1 -1
  30. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/index.txt +1 -1
  31. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.dockerignore +0 -0
  32. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.github/actionlint.yaml +0 -0
  33. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.github/workflows/build-image.yml +0 -0
  34. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.github/workflows/promote-prod.yml +0 -0
  35. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/.github/workflows/release.yml +0 -0
  36. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/CONTEXT.md +0 -0
  37. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/README.md +0 -0
  38. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/.env.example +0 -0
  39. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/Dockerfile +0 -0
  40. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/README.md +0 -0
  41. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/apply-local-cache.js +0 -0
  42. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/collector-config.yaml +0 -0
  43. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/docker-compose.yml +0 -0
  44. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/local-test-settings.json +0 -0
  45. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/otel/remote-settings.json +0 -0
  46. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/report-creator.skill +0 -0
  47. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/scripts/analyze_corpus.py +0 -0
  48. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/scripts/generate_rdlx_schema.py +0 -0
  49. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/__main__.py +0 -0
  50. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/__init__.py +0 -0
  51. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/api.py +0 -0
  52. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/auth.py +0 -0
  53. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/branch.py +0 -0
  54. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/branch_commit_guard.py +0 -0
  55. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/cascade.py +0 -0
  56. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/config.py +0 -0
  57. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/configuration.py +0 -0
  58. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/crm.py +0 -0
  59. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/crm_delivery.py +0 -0
  60. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/crm_time.py +0 -0
  61. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/datasource.py +0 -0
  62. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/devops.py +0 -0
  63. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/document.py +0 -0
  64. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/endpoint.py +0 -0
  65. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/env.py +0 -0
  66. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/function.py +0 -0
  67. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/init.py +0 -0
  68. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/marketplace.py +0 -0
  69. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/__init__.py +0 -0
  70. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/_common.py +0 -0
  71. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/api.py +0 -0
  72. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/create.py +0 -0
  73. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/data.py +0 -0
  74. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/pack.py +0 -0
  75. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/ng/preview.py +0 -0
  76. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/odata.py +0 -0
  77. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/organization.py +0 -0
  78. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/proxy.py +0 -0
  79. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/reference.py +0 -0
  80. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/release_notes.py +0 -0
  81. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/repo.py +0 -0
  82. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/__init__.py +0 -0
  83. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/add_cmds.py +0 -0
  84. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/api.py +0 -0
  85. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/batch.py +0 -0
  86. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/create.py +0 -0
  87. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/data.py +0 -0
  88. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/dataset.py +0 -0
  89. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/edit.py +0 -0
  90. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/folder.py +0 -0
  91. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/preview.py +0 -0
  92. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/report/schema_cmds.py +0 -0
  93. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/schema.py +0 -0
  94. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/servicepack.py +0 -0
  95. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/source.py +0 -0
  96. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/studio.py +0 -0
  97. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/telemetry.py +0 -0
  98. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/user.py +0 -0
  99. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/commands/validation_report.py +0 -0
  100. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/context.py +0 -0
  101. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/__init__.py +0 -0
  102. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/agent_browser.py +0 -0
  103. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/__init__.py +0 -0
  104. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/app_config.py +0 -0
  105. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/client.py +0 -0
  106. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/metadata_models.py +0 -0
  107. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/models.py +0 -0
  108. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/reference_graph.py +0 -0
  109. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/api/replacements.py +0 -0
  110. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/__init__.py +0 -0
  111. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/datex_tenant.py +0 -0
  112. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/decorators.py +0 -0
  113. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/msal_client.py +0 -0
  114. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/tenants.py +0 -0
  115. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/auth/token_cache.py +0 -0
  116. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/cache.py +0 -0
  117. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/__init__.py +0 -0
  118. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/flow_generator.py +0 -0
  119. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/flow_models.py +0 -0
  120. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/flow_shape.py +0 -0
  121. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/generator.py +0 -0
  122. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/models.py +0 -0
  123. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/parsers.py +0 -0
  124. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/resolver.py +0 -0
  125. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/type_def_parser.py +0 -0
  126. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/datasource/validator.py +0 -0
  127. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/designer/__init__.py +0 -0
  128. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/designer/flow_models.py +0 -0
  129. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/designer/models.py +0 -0
  130. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/designer/parsers.py +0 -0
  131. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/devops/__init__.py +0 -0
  132. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/devops/client.py +0 -0
  133. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/devops/helpers.py +0 -0
  134. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/devops/models.py +0 -0
  135. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/__init__.py +0 -0
  136. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/client.py +0 -0
  137. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/html.py +0 -0
  138. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/metadata_cache.py +0 -0
  139. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/pagination.py +0 -0
  140. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/resolvers.py +0 -0
  141. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/dynamics/time_entry.py +0 -0
  142. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/footprint/__init__.py +0 -0
  143. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/footprint/client.py +0 -0
  144. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/footprint/edmx_parser.py +0 -0
  145. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/footprint/metadata.py +0 -0
  146. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/function/__init__.py +0 -0
  147. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/function/generator.py +0 -0
  148. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/function/models.py +0 -0
  149. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/function/validator.py +0 -0
  150. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/graph.py +0 -0
  151. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/onboarding.py +0 -0
  152. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/__init__.py +0 -0
  153. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/csv_fmt.py +0 -0
  154. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/formatter.py +0 -0
  155. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/json_fmt.py +0 -0
  156. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/redaction.py +0 -0
  157. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/output/yaml_fmt.py +0 -0
  158. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/release_notes.py +0 -0
  159. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/responses.py +0 -0
  160. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/__init__.py +0 -0
  161. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/config.py +0 -0
  162. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/identity.py +0 -0
  163. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/recorder.py +0 -0
  164. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/scrubber.py +0 -0
  165. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/core/telemetry/sender.py +0 -0
  166. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/models/__init__.py +0 -0
  167. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/__init__.py +0 -0
  168. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/folder.py +0 -0
  169. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/manifest.py +0 -0
  170. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/mocks.py +0 -0
  171. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/preview.py +0 -0
  172. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/scaffold.py +0 -0
  173. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/ng/serve_lock.py +0 -0
  174. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/py.typed +0 -0
  175. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/__init__.py +0 -0
  176. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/datasource_binding.py +0 -0
  177. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/engine.py +0 -0
  178. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/field_parser.py +0 -0
  179. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/folder.py +0 -0
  180. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/manifest.py +0 -0
  181. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/models.py +0 -0
  182. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/preview.py +0 -0
  183. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/preview_arjs.py +0 -0
  184. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/schema.py +0 -0
  185. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/svg_renderer.py +0 -0
  186. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/templates/__init__.py +0 -0
  187. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
  188. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
  189. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/validate_arjs.py +0 -0
  190. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/validator.py +0 -0
  191. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/report/wrapper.py +0 -0
  192. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/__init__.py +0 -0
  193. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/click_options.py +0 -0
  194. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/clipboard.py +0 -0
  195. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/config.py +0 -0
  196. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/errors.py +0 -0
  197. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/filtering.py +0 -0
  198. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/image.py +0 -0
  199. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/paths.py +0 -0
  200. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/resolvers.py +0 -0
  201. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/responses.py +0 -0
  202. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/restricted.py +0 -0
  203. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/sorting.py +0 -0
  204. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/update_check.py +0 -0
  205. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/utils/versioning.py +0 -0
  206. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/__init__.py +0 -0
  207. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/app.py +0 -0
  208. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/design.py +0 -0
  209. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/proxy_app.py +0 -0
  210. {datex_studio_cli-0.5.0/src/dxs/web/static/_next/static/hfb4z29yVHQge-muP6roV → datex_studio_cli-0.5.2/src/dxs/web/static/_next/static/5ccHgxTeTrw5DjrFowRBg}/_buildManifest.js +0 -0
  211. {datex_studio_cli-0.5.0/src/dxs/web/static/_next/static/hfb4z29yVHQge-muP6roV → datex_studio_cli-0.5.2/src/dxs/web/static/_next/static/5ccHgxTeTrw5DjrFowRBg}/_ssgManifest.js +0 -0
  212. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
  213. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
  214. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
  215. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/538-b06224e65f71c9a1.js +0 -0
  216. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
  217. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
  218. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
  219. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
  220. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-e3c660196c244183.js +0 -0
  221. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-c083dc9f01f32a04.js +0 -0
  222. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/app/design/page-689cd5b9e1d9a74e.js +0 -0
  223. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/app/layout-cec86f90e385371d.js +0 -0
  224. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/app/page-f0380b0f29983264.js +0 -0
  225. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
  226. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
  227. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/main-app-694cf3568b6c9fa9.js +0 -0
  228. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
  229. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
  230. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
  231. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
  232. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
  233. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/render-cli.js +0 -0
  234. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/report-preview.html +0 -0
  235. {datex_studio_cli-0.5.0 → datex_studio_cli-0.5.2}/src/dxs/web/static/report-validate.html +0 -0
@@ -59,3 +59,32 @@ jobs:
59
59
  - run: uv run ruff check quartermaster
60
60
  - run: uv run --package quartermaster mypy quartermaster/src/quartermaster
61
61
  - run: uv run --package quartermaster pytest quartermaster/tests -q
62
+
63
+ # The artifact nobody was looking at. 0.5.0 and 0.5.1 both shipped without
64
+ # dxs.core.quartermaster and dxs.core.skills, and 0.5.1 could not run
65
+ # `dxs --version` at all -- while this suite was fully green, because tests
66
+ # import from the source tree and never open the wheel. release.yml calls
67
+ # this workflow, so a broken artifact now blocks the release too.
68
+ package:
69
+ runs-on: arc-dxs
70
+ timeout-minutes: 15
71
+ steps:
72
+ - uses: actions/checkout@v7
73
+ - uses: astral-sh/setup-uv@v10.0.1
74
+ with:
75
+ enable-cache: true
76
+ cache-suffix: package
77
+ download-from-astral-mirror: false
78
+ - run: uv python install 3.13
79
+ - run: uv build
80
+ - name: Every package under src/ must be in the wheel
81
+ run: uv run python scripts/check_wheel_completeness.py
82
+ - name: The built wheel must actually run
83
+ run: |
84
+ # A clean venv, not the workspace: importing from the source tree is
85
+ # exactly what hid this. Resolve deps from the index like a user does.
86
+ uv venv /tmp/smoke
87
+ uv pip install --python /tmp/smoke/bin/python dist/*.whl
88
+ /tmp/smoke/bin/dxs --version
89
+ /tmp/smoke/bin/dxs skills --help > /dev/null
90
+ /tmp/smoke/bin/python -c "import dxs.core.quartermaster, dxs.core.skills"
@@ -105,3 +105,9 @@ test-preview.svg
105
105
 
106
106
  # Live auth matrix config (real accounts)
107
107
  tests/live/accounts.local.json
108
+
109
+ # Project-scope `dxs skills` install artifacts (.claude/skills is above).
110
+ # Canonical copies, the codex symlink tree, and the install record.
111
+ .agents/
112
+ .codex/
113
+ skills-lock.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datex-studio-cli
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: CLI for Datex Studio low-code platform, designed for LLM-based AI agents
5
5
  Project-URL: Homepage, https://github.com/datex/datex-studio-cli
6
6
  Project-URL: Documentation, https://github.com/datex/datex-studio-cli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datex-studio-cli"
7
- version = "0.5.0"
7
+ version = "0.5.2"
8
8
  description = "CLI for Datex Studio low-code platform, designed for LLM-based AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -94,7 +94,7 @@ exclude = [
94
94
  "documentation/",
95
95
  "exploration/",
96
96
  "exclude/",
97
- "skills/",
97
+ "/skills/",
98
98
  "tests/",
99
99
  "publish.sh",
100
100
  "*.analysis.md",
@@ -102,7 +102,7 @@ exclude = [
102
102
  "uv.lock",
103
103
  "src/dxs/web/frontend/",
104
104
  "example-reports/",
105
- "quartermaster/",
105
+ "/quartermaster/",
106
106
  ]
107
107
 
108
108
  [tool.uv.workspace]
@@ -149,6 +149,10 @@ ignore_missing_imports = true
149
149
  module = ["fastapi.*", "uvicorn.*"]
150
150
  ignore_missing_imports = true
151
151
 
152
+ [[tool.mypy.overrides]]
153
+ module = ["pyodbc"]
154
+ ignore_missing_imports = true
155
+
152
156
  [tool.pytest.ini_options]
153
157
  testpaths = ["tests"]
154
158
  python_files = ["test_*.py"]
@@ -160,6 +164,7 @@ markers = [
160
164
  "real_auth_state: exercise real auth state (no injected DXS_ACCESS_TOKEN; the variable is scrubbed)",
161
165
  "telemetry_live: run without the suite-wide DXS_TELEMETRY=0 kill-switch",
162
166
  "live_auth: sign in against real Entra tenants (opt-in; needs DXS_LIVE_AUTH=1 + a human)",
167
+ "mssql: integration tests against a throwaway SQL Server container (opt-in; needs DXS_MSSQL_TEST=1)",
163
168
  ]
164
169
 
165
170
  [tool.coverage.run]
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python
2
+ """Assert the built wheel carries every package that exists under src/dxs.
3
+
4
+ Versions 0.5.0 and 0.5.1 both shipped without ``dxs.core.quartermaster`` and
5
+ ``dxs.core.skills``. The sdist exclude list named ``skills/`` and
6
+ ``quartermaster/`` to drop the top-level directories of those names; hatchling
7
+ matches an unanchored pattern at any depth, so it dropped the nested packages
8
+ too, and ``uv build`` builds the wheel from the sdist it just made. 0.5.1 could
9
+ not even run ``dxs --version``.
10
+
11
+ A full green suite said nothing about any of it: tests import from the source
12
+ tree and never open the artifact. This looks at the artifact.
13
+
14
+ Usage: check_wheel_completeness.py [dist_dir]
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import sys
20
+ import zipfile
21
+ from pathlib import Path
22
+
23
+ ROOT = Path(__file__).resolve().parent.parent
24
+ SRC = ROOT / "src"
25
+
26
+
27
+ def main() -> int:
28
+ dist = Path(sys.argv[1]) if len(sys.argv) > 1 else ROOT / "dist"
29
+ wheels = sorted(dist.glob("*.whl"))
30
+ if not wheels:
31
+ print(f"no wheel in {dist} — run `uv build` first", file=sys.stderr)
32
+ return 1
33
+ wheel = wheels[-1]
34
+
35
+ # Every directory holding an __init__.py is a package that must ship.
36
+ expected = {
37
+ p.parent.relative_to(SRC).as_posix() for p in SRC.rglob("__init__.py")
38
+ if "__pycache__" not in p.parts
39
+ }
40
+ names = zipfile.ZipFile(wheel).namelist()
41
+ shipped = {n.rsplit("/", 1)[0] for n in names if n.endswith("/__init__.py")}
42
+
43
+ missing = sorted(expected - shipped)
44
+ if missing:
45
+ print(f"{wheel.name} is missing {len(missing)} package(s) present in src/:",
46
+ file=sys.stderr)
47
+ for m in missing:
48
+ print(f" {m}", file=sys.stderr)
49
+ print("\nUsually an unanchored exclude pattern in pyproject.toml matching a "
50
+ "nested path — anchor it with a leading slash.", file=sys.stderr)
51
+ return 1
52
+
53
+ print(f"{wheel.name}: all {len(expected)} packages present")
54
+ return 0
55
+
56
+
57
+ if __name__ == "__main__":
58
+ raise SystemExit(main())
@@ -0,0 +1,275 @@
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = ["httpx", "python-dotenv"]
4
+ # ///
5
+ """Pull recent CLI error events from Datadog Logs for investigation.
6
+
7
+ Queries the Datadog Logs Search API for ``service:dxs-cli status:error`` (the
8
+ telemetry forwarder's shape — see docs/telemetry-datadog-mapping.md) and prints
9
+ either a summary grouped by command + error code, or the raw events.
10
+
11
+ Credentials come from the project ``.env``: DATADOG_API_KEY, DATADOG_APP_KEY,
12
+ DATADOG_SITE (e.g. ``us3.datadoghq.com``).
13
+
14
+ Usage:
15
+ uv run scripts/datadog_errors.py # last 24h, summary
16
+ uv run scripts/datadog_errors.py --since 7d # last 7 days
17
+ uv run scripts/datadog_errors.py --since 7d --list # one line per event
18
+ uv run scripts/datadog_errors.py --code DXS-API-001 --detail # full stacks
19
+ uv run scripts/datadog_errors.py --command source --version 0.5.0 -f json
20
+ uv run scripts/datadog_errors.py -q 'usr.email:*@datexcorp.com' # extra filter
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import json
27
+ import os
28
+ import re
29
+ import sys
30
+ import time
31
+ from collections import Counter, defaultdict
32
+ from datetime import datetime, timedelta, timezone
33
+ from pathlib import Path
34
+ from typing import Any
35
+
36
+ SUMMARY_FIELDS = (
37
+ "ts",
38
+ "command",
39
+ "error_code",
40
+ "error_class",
41
+ "error_message",
42
+ "cli_version",
43
+ "api_target",
44
+ "env",
45
+ "usr_email",
46
+ "org",
47
+ "os",
48
+ )
49
+
50
+
51
+ def parse_since(value: str) -> timedelta:
52
+ m = re.fullmatch(r"(\d+)([mhd])", value.strip())
53
+ if not m:
54
+ raise argparse.ArgumentTypeError("expected like 30m, 6h, 7d")
55
+ n, unit = int(m.group(1)), m.group(2)
56
+ return timedelta(**{{"m": "minutes", "h": "hours", "d": "days"}[unit]: n})
57
+
58
+
59
+ def build_query(args: argparse.Namespace) -> str:
60
+ parts = [f"service:{args.service}", "status:error"]
61
+ if args.env:
62
+ parts.append(f"env:{args.env}")
63
+ if args.api_target:
64
+ parts.append(f"@api_target:{args.api_target}")
65
+ if args.code:
66
+ parts.append(f"@error_code:{args.code}")
67
+ if args.command:
68
+ parts.append(f'@command:"{args.command}"')
69
+ if args.version:
70
+ parts.append(f"@version:{args.version}")
71
+ if args.query:
72
+ parts.append(f"({args.query})")
73
+ return " ".join(parts)
74
+
75
+
76
+ def fetch_events(
77
+ site: str, api_key: str, app_key: str, query: str, since: timedelta, limit: int
78
+ ) -> list[dict[str, Any]]:
79
+ import httpx
80
+
81
+ now = datetime.now(timezone.utc)
82
+ body: dict[str, Any] = {
83
+ "filter": {
84
+ "query": query,
85
+ "from": (now - since).isoformat(),
86
+ "to": now.isoformat(),
87
+ },
88
+ "sort": "-timestamp",
89
+ "page": {"limit": min(limit, 1000)},
90
+ }
91
+ headers = {
92
+ "DD-API-KEY": api_key,
93
+ "DD-APPLICATION-KEY": app_key,
94
+ "Content-Type": "application/json",
95
+ }
96
+ events: list[dict[str, Any]] = []
97
+ retries = 0
98
+ with httpx.Client(base_url=f"https://api.{site}", headers=headers, timeout=30) as client:
99
+ while True:
100
+ resp = client.post("/api/v2/logs/events/search", json=body)
101
+ if resp.status_code == 429 and retries < 3:
102
+ wait = int(
103
+ resp.headers.get("Retry-After") or resp.headers.get("X-RateLimit-Reset") or 5
104
+ )
105
+ print(f"rate limited; retrying in {wait}s", file=sys.stderr)
106
+ time.sleep(wait)
107
+ retries += 1
108
+ continue
109
+ if resp.status_code != 200:
110
+ print(f"Datadog API {resp.status_code}: {resp.text[:500]}", file=sys.stderr)
111
+ sys.exit(1)
112
+ payload = resp.json()
113
+ events.extend(payload.get("data", []))
114
+ cursor = payload.get("meta", {}).get("page", {}).get("after")
115
+ if not cursor or len(events) >= limit:
116
+ break
117
+ body["page"]["cursor"] = cursor
118
+ return events[:limit]
119
+
120
+
121
+ def full_command(group: str | None, argv: Any) -> str | None:
122
+ """`command` is only the Click group (e.g. `configuration`); recover the
123
+ subcommand path from argv: the bare-word tokens that follow the group."""
124
+ if not group or not isinstance(argv, list):
125
+ return group
126
+ try:
127
+ i = argv.index(group)
128
+ except ValueError:
129
+ return group
130
+ words = [group]
131
+ for tok in argv[i + 1 : i + 3]:
132
+ if isinstance(tok, str) and re.fullmatch(r"[a-z][a-z-]*", tok):
133
+ words.append(tok)
134
+ else:
135
+ break
136
+ return " ".join(words)
137
+
138
+
139
+ def flatten(event: dict[str, Any]) -> dict[str, Any]:
140
+ """Reduce a Datadog log event to the attributes worth reading."""
141
+ attrs = event.get("attributes", {})
142
+ custom = attrs.get("attributes", {}) # user attributes live one level down
143
+ tags = dict(t.split(":", 1) for t in attrs.get("tags", []) if ":" in t)
144
+ usr = custom.get("usr", {}) or {}
145
+ err = custom.get("error", {}) or {}
146
+ return {
147
+ "id": event.get("id"),
148
+ "ts": attrs.get("timestamp"),
149
+ "message": attrs.get("message"),
150
+ "command": full_command(custom.get("command"), custom.get("argv")),
151
+ "argv": custom.get("argv"),
152
+ "error_code": custom.get("error_code") or tags.get("error_code"),
153
+ "error_class": custom.get("error_class") or err.get("kind"),
154
+ "error_message": custom.get("error_message") or err.get("message"),
155
+ "error_stack": custom.get("error_stack") or err.get("stack"),
156
+ "exit_code": custom.get("exit_code"),
157
+ "duration_ms": custom.get("duration_ms"),
158
+ "cli_version": custom.get("version") or tags.get("version"),
159
+ "org": custom.get("datex_org_name") or tags.get("org"),
160
+ "python_version": custom.get("python_version"),
161
+ "os": custom.get("os", {}).get("name")
162
+ if isinstance(custom.get("os"), dict)
163
+ else custom.get("os"),
164
+ "os_release": custom.get("os_release"),
165
+ "api_target": custom.get("api_target") or tags.get("api_target"),
166
+ "env": tags.get("env"),
167
+ "usr_id": usr.get("id"),
168
+ "usr_email": usr.get("email"),
169
+ "usr_name": usr.get("name"),
170
+ "session_id": (custom.get("session") or {}).get("id"),
171
+ "install_id": custom.get("install_id"),
172
+ "ctx_branch_id": custom.get("ctx_branch_id"),
173
+ "ctx_org": custom.get("ctx_org"),
174
+ "ctx_env": custom.get("ctx_env"),
175
+ }
176
+
177
+
178
+ def print_summary(rows: list[dict[str, Any]]) -> None:
179
+ if not rows:
180
+ print("No matching error events.")
181
+ return
182
+ groups: dict[tuple[str, str, str], list[dict[str, Any]]] = defaultdict(list)
183
+ for r in rows:
184
+ groups[(r["command"] or "?", r["error_code"] or "-", r["error_class"] or "?")].append(r)
185
+ print(f"{len(rows)} error events, {len(groups)} distinct (command, code, class)\n")
186
+ print(f"{'count':>5} {'command':<28} {'code':<14} {'class':<24} sample message")
187
+ for key, grp in sorted(groups.items(), key=lambda kv: -len(kv[1])):
188
+ cmd, code, cls = key
189
+ msg = (grp[0]["error_message"] or "")[:80].replace("\n", " ")
190
+ print(f"{len(grp):>5} {cmd:<28} {code:<14} {cls:<24} {msg}")
191
+ users = Counter(r["usr_email"] or r["usr_name"] or "?" for r in grp)
192
+ versions = Counter(r["cli_version"] or "?" for r in grp)
193
+ print(
194
+ f" last={grp[0]['ts']} users={dict(users.most_common(3))}"
195
+ f" versions={dict(versions.most_common(3))}"
196
+ )
197
+
198
+
199
+ def print_list(rows: list[dict[str, Any]]) -> None:
200
+ for r in rows:
201
+ msg = (r["error_message"] or "")[:100].replace("\n", " ")
202
+ print(
203
+ f"{r['ts']} {r['cli_version'] or '?':<8} {r['usr_email'] or r['usr_name'] or '?':<32}"
204
+ f" {r['command'] or '?':<28} {r['error_code'] or '-':<14} {msg}"
205
+ )
206
+
207
+
208
+ def print_detail(rows: list[dict[str, Any]]) -> None:
209
+ for r in rows:
210
+ print("=" * 100)
211
+ for k in SUMMARY_FIELDS + ("argv", "ctx_branch_id", "ctx_org", "session_id", "id"):
212
+ if r.get(k) is not None:
213
+ print(f"{k:<14} {r[k]}")
214
+ if r.get("error_stack"):
215
+ print("--- stack ---")
216
+ print(r["error_stack"])
217
+ print("=" * 100)
218
+
219
+
220
+ def main(argv: list[str] | None = None) -> None:
221
+ p = argparse.ArgumentParser(
222
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
223
+ )
224
+ p.add_argument(
225
+ "--since",
226
+ type=parse_since,
227
+ default="24h",
228
+ help="lookback window, e.g. 30m, 6h, 7d (default 24h)",
229
+ )
230
+ p.add_argument("--service", default="dxs-cli", help="Datadog service tag (default dxs-cli)")
231
+ p.add_argument("--env", help="ddtag env: staging|prod|local (which Quartermaster received it)")
232
+ p.add_argument("--api-target", help="dev|qa|prod|default (which Datex environment the CLI hit)")
233
+ p.add_argument("--code", help="filter by DXS-* error code")
234
+ p.add_argument(
235
+ "--command", help="filter by Click group, e.g. configuration (subcommand is only in argv)"
236
+ )
237
+ p.add_argument("--version", help="filter by cli_version")
238
+ p.add_argument("-q", "--query", help="extra raw Datadog log query ANDed onto the filter")
239
+ p.add_argument("-n", "--limit", type=int, default=500, help="max events to fetch (default 500)")
240
+ mode = p.add_mutually_exclusive_group()
241
+ mode.add_argument("--list", action="store_true", help="one line per event")
242
+ mode.add_argument("--detail", action="store_true", help="every field + stack per event")
243
+ p.add_argument("-f", "--format", choices=["text", "json"], default="text")
244
+ p.add_argument("--show-query", action="store_true", help="print the Datadog query and exit")
245
+ args = p.parse_args(argv)
246
+
247
+ from dotenv import load_dotenv
248
+
249
+ load_dotenv(Path(__file__).resolve().parent.parent / ".env")
250
+ api_key, app_key = os.environ.get("DATADOG_API_KEY"), os.environ.get("DATADOG_APP_KEY")
251
+ site = os.environ.get("DATADOG_SITE", "datadoghq.com")
252
+ if not api_key or not app_key:
253
+ print("DATADOG_API_KEY and DATADOG_APP_KEY must be set (expected in .env)", file=sys.stderr)
254
+ sys.exit(1)
255
+
256
+ query = build_query(args)
257
+ if args.show_query:
258
+ print(query)
259
+ return
260
+ print(f"query: {query} window: last {args.since}", file=sys.stderr)
261
+
262
+ rows = [flatten(e) for e in fetch_events(site, api_key, app_key, query, args.since, args.limit)]
263
+ if args.format == "json":
264
+ json.dump(rows, sys.stdout, indent=2, default=str)
265
+ print()
266
+ elif args.detail:
267
+ print_detail(rows)
268
+ elif args.list:
269
+ print_list(rows)
270
+ else:
271
+ print_summary(rows)
272
+
273
+
274
+ if __name__ == "__main__":
275
+ main()