datex-studio-cli 0.4.1__tar.gz → 0.4.3__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 (172) hide show
  1. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/PKG-INFO +1 -1
  2. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/pyproject.toml +1 -1
  3. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/scripts/analyze_corpus.py +12 -12
  4. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/scripts/generate_rdlx_schema.py +42 -18
  5. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/__init__.py +1 -1
  6. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/cli.py +141 -10
  7. datex_studio_cli-0.4.3/src/dxs/commands/api.py +236 -0
  8. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/auth.py +114 -35
  9. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/branch.py +2 -5
  10. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/config.py +7 -3
  11. datex_studio_cli-0.4.3/src/dxs/commands/configuration.py +250 -0
  12. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/crm.py +92 -3
  13. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/datasource.py +41 -23
  14. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/devops.py +15 -13
  15. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/document.py +4 -6
  16. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/explore.py +25 -27
  17. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/function.py +1 -2
  18. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/odata.py +26 -5
  19. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/organization.py +3 -1
  20. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/release_notes.py +3 -8
  21. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/__init__.py +6 -1
  22. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/add_cmds.py +30 -22
  23. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/api.py +8 -15
  24. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/create.py +3 -1
  25. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/data.py +11 -1
  26. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/dataset.py +3 -1
  27. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/folder.py +62 -20
  28. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/schema.py +57 -27
  29. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/source.py +33 -44
  30. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/studio.py +0 -1
  31. datex_studio_cli-0.4.3/src/dxs/commands/telemetry.py +181 -0
  32. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/context.py +10 -1
  33. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/__init__.py +9 -2
  34. datex_studio_cli-0.4.3/src/dxs/core/api/app_config.py +76 -0
  35. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/endpoints.py +11 -17
  36. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_generator.py +1 -3
  37. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/flow_models.py +1 -3
  38. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/models.py +3 -1
  39. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/parsers.py +2 -6
  40. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/models.py +12 -6
  41. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/client.py +117 -2
  42. datex_studio_cli-0.4.3/src/dxs/core/dynamics/metadata_cache.py +133 -0
  43. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/csv_fmt.py +2 -1
  44. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/formatter.py +13 -1
  45. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/redaction.py +61 -0
  46. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/release_notes.py +2 -7
  47. datex_studio_cli-0.4.3/src/dxs/core/telemetry/__init__.py +29 -0
  48. datex_studio_cli-0.4.3/src/dxs/core/telemetry/config.py +141 -0
  49. datex_studio_cli-0.4.3/src/dxs/core/telemetry/identity.py +89 -0
  50. datex_studio_cli-0.4.3/src/dxs/core/telemetry/recorder.py +232 -0
  51. datex_studio_cli-0.4.3/src/dxs/core/telemetry/scrubber.py +83 -0
  52. datex_studio_cli-0.4.3/src/dxs/core/telemetry/sender.py +215 -0
  53. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/engine.py +11 -6
  54. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/folder.py +9 -20
  55. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/preview_arjs.py +4 -2
  56. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/schema.py +1 -1
  57. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/svg_renderer.py +8 -6
  58. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/validator.py +10 -25
  59. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/paths.py +11 -0
  60. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/resolvers.py +27 -11
  61. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/update_check.py +31 -14
  62. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/app.py +1 -1
  63. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/design.py +15 -15
  64. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/proxy_app.py +1 -3
  65. datex_studio_cli-0.4.1/src/dxs/commands/api.py +0 -147
  66. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/.gitignore +0 -0
  67. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/README.md +0 -0
  68. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/report-creator.skill +0 -0
  69. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/__main__.py +0 -0
  70. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/__init__.py +0 -0
  71. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/endpoint.py +0 -0
  72. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/env.py +0 -0
  73. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/marketplace.py +0 -0
  74. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/proxy.py +0 -0
  75. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/repo.py +0 -0
  76. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/batch.py +0 -0
  77. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/edit.py +0 -0
  78. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/preview.py +0 -0
  79. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/report/schema_cmds.py +0 -0
  80. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/servicepack.py +0 -0
  81. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/commands/user.py +0 -0
  82. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/__init__.py +0 -0
  83. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/client.py +0 -0
  84. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/metadata_models.py +0 -0
  85. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/api/models.py +0 -0
  86. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/__init__.py +0 -0
  87. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/decorators.py +0 -0
  88. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/msal_client.py +0 -0
  89. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/auth/token_cache.py +0 -0
  90. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/cache.py +0 -0
  91. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/__init__.py +0 -0
  92. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/generator.py +0 -0
  93. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/parsers.py +0 -0
  94. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/resolver.py +0 -0
  95. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/type_def_parser.py +0 -0
  96. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/datasource/validator.py +0 -0
  97. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/__init__.py +0 -0
  98. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/flow_models.py +0 -0
  99. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/designer/models.py +0 -0
  100. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/__init__.py +0 -0
  101. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/client.py +0 -0
  102. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/devops/helpers.py +0 -0
  103. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/dynamics/__init__.py +0 -0
  104. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/__init__.py +0 -0
  105. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/client.py +0 -0
  106. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/edmx_parser.py +0 -0
  107. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/footprint/metadata.py +0 -0
  108. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/__init__.py +0 -0
  109. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/generator.py +0 -0
  110. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/models.py +0 -0
  111. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/function/validator.py +0 -0
  112. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/graph.py +0 -0
  113. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/__init__.py +0 -0
  114. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/json_fmt.py +0 -0
  115. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/output/yaml_fmt.py +0 -0
  116. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/core/responses.py +0 -0
  117. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/models/__init__.py +0 -0
  118. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/__init__.py +0 -0
  119. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/datasource_binding.py +0 -0
  120. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/field_parser.py +0 -0
  121. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/manifest.py +0 -0
  122. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/models.py +0 -0
  123. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/preview.py +0 -0
  124. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/__init__.py +0 -0
  125. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
  126. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
  127. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/validate_arjs.py +0 -0
  128. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/report/wrapper.py +0 -0
  129. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/__init__.py +0 -0
  130. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/click_options.py +0 -0
  131. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/config.py +0 -0
  132. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/errors.py +0 -0
  133. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/filtering.py +0 -0
  134. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/image.py +0 -0
  135. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/responses.py +0 -0
  136. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/restricted.py +0 -0
  137. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/utils/sorting.py +0 -0
  138. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/__init__.py +0 -0
  139. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/404/index.html +0 -0
  140. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/404.html +0 -0
  141. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
  142. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
  143. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
  144. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
  145. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
  146. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
  147. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
  148. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
  149. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
  150. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
  151. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
  152. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
  153. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
  154. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
  155. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
  156. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
  157. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
  158. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
  159. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
  160. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
  161. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
  162. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
  163. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
  164. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.html +0 -0
  165. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/capture/index.txt +0 -0
  166. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.html +0 -0
  167. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/design/index.txt +0 -0
  168. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.html +0 -0
  169. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/index.txt +0 -0
  170. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/render-cli.js +0 -0
  171. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-preview.html +0 -0
  172. {datex_studio_cli-0.4.1 → datex_studio_cli-0.4.3}/src/dxs/web/static/report-validate.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datex-studio-cli
3
- Version: 0.4.1
3
+ Version: 0.4.3
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.4.1"
7
+ version = "0.4.3"
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"
@@ -226,39 +226,39 @@ def main() -> None:
226
226
  for cat, cnt in category_counts.most_common():
227
227
  print(f" {cat:30s} {cnt:4d}")
228
228
 
229
- print(f"\n## Page Size Distribution")
229
+ print("\n## Page Size Distribution")
230
230
  for size, cnt in page_sizes.most_common():
231
231
  pct = cnt / total * 100
232
232
  print(f" {size:30s} {cnt:4d} ({pct:.0f}%)")
233
233
 
234
- print(f"\n## Margin Distribution")
234
+ print("\n## Margin Distribution")
235
235
  for margin, cnt in margin_counter.most_common(15):
236
236
  pct = cnt / total * 100
237
237
  print(f" {margin:55s} {cnt:4d} ({pct:.0f}%)")
238
238
 
239
- print(f"\n## Item Type Frequency")
239
+ print("\n## Item Type Frequency")
240
240
  for typ, cnt in item_type_counter.most_common():
241
241
  print(f" {typ:25s} {cnt:5d}")
242
242
 
243
- print(f"\n## Barcode Symbology Frequency")
243
+ print("\n## Barcode Symbology Frequency")
244
244
  for sym, cnt in barcode_symbology_counter.most_common():
245
245
  print(f" {sym:25s} {cnt:4d}")
246
246
 
247
- print(f"\n## Font Usage Frequency (top 20)")
247
+ print("\n## Font Usage Frequency (top 20)")
248
248
  for fnt, cnt in font_counter.most_common(20):
249
249
  print(f" {fnt:30s} {cnt:5d}")
250
250
 
251
- print(f"\n## Expression Pattern Frequency")
251
+ print("\n## Expression Pattern Frequency")
252
252
  for pat, cnt in expression_pattern_counter.most_common():
253
253
  print(f" {pat:30s} {cnt:5d}")
254
254
 
255
- print(f"\n## Dataset Count per Report")
255
+ print("\n## Dataset Count per Report")
256
256
  if dataset_counts:
257
257
  print(f" Mean: {mean(dataset_counts):.1f}")
258
258
  print(f" Median: {median(dataset_counts):.1f}")
259
259
  print(f" Range: {min(dataset_counts)} - {max(dataset_counts)}")
260
260
 
261
- print(f"\n## Element Count per Report by Category (body-level items)")
261
+ print("\n## Element Count per Report by Category (body-level items)")
262
262
  for cat in sorted(element_counts_by_category):
263
263
  counts = element_counts_by_category[cat]
264
264
  if counts:
@@ -269,7 +269,7 @@ def main() -> None:
269
269
  f"n={len(counts)}"
270
270
  )
271
271
 
272
- print(f"\n## Common Field Names by Category (top 10)")
272
+ print("\n## Common Field Names by Category (top 10)")
273
273
  for cat in sorted(field_names_by_category):
274
274
  top = field_names_by_category[cat].most_common(10)
275
275
  if top:
@@ -277,9 +277,9 @@ def main() -> None:
277
277
  for name, cnt in top:
278
278
  print(f" {name:30s} {cnt:4d}")
279
279
 
280
- print(f"\n## PageHeader / PageFooter Usage")
281
- print(f" PageHeader: {page_header_count}/{total} ({page_header_count/total*100:.0f}%)")
282
- print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count/total*100:.0f}%)")
280
+ print("\n## PageHeader / PageFooter Usage")
281
+ print(f" PageHeader: {page_header_count}/{total} ({page_header_count / total * 100:.0f}%)")
282
+ print(f" PageFooter: {page_footer_count}/{total} ({page_footer_count / total * 100:.0f}%)")
283
283
 
284
284
 
285
285
  if __name__ == "__main__":
@@ -241,9 +241,22 @@ def compare_schema(item_types: dict, items: list[dict]) -> dict:
241
241
  all_schema_props = schema_props | schema_style_props
242
242
  corpus_props = set(item_types[type_name]["properties"].keys())
243
243
  # Exclude common layout props that aren't type-specific
244
- common_layout = {"Type", "Name", "Left", "Top", "Width", "Height", "ZIndex",
245
- "Visibility", "Style", "LayerName", "OverflowName", "Bookmark",
246
- "Label", "CustomProperties"}
244
+ common_layout = {
245
+ "Type",
246
+ "Name",
247
+ "Left",
248
+ "Top",
249
+ "Width",
250
+ "Height",
251
+ "ZIndex",
252
+ "Visibility",
253
+ "Style",
254
+ "LayerName",
255
+ "OverflowName",
256
+ "Bookmark",
257
+ "Label",
258
+ "CustomProperties",
259
+ }
247
260
  missing = sorted((corpus_props - all_schema_props) - common_layout)
248
261
  if missing:
249
262
  props_not_in_schema[type_name] = missing
@@ -296,13 +309,13 @@ def print_summary(schema: dict) -> None:
296
309
  # Common properties
297
310
  common = schema.get("common_properties", {})
298
311
  if common:
299
- print(f"\n## Common Properties (shared across >=80% of item types)")
312
+ print("\n## Common Properties (shared across >=80% of item types)")
300
313
  for prop, info in sorted(common.items()):
301
314
  print(f" {prop:25s} {info['present_in']}")
302
315
 
303
316
  # Top properties per type (top 5 by count, excluding common)
304
317
  common_names = set(common.keys()) if common else set()
305
- print(f"\n## Type-Specific Properties (top 10, excluding common)")
318
+ print("\n## Type-Specific Properties (top 10, excluding common)")
306
319
  for type_name, type_info in sorted(item_types.items(), key=lambda x: -x[1]["count"]):
307
320
  props = type_info["properties"]
308
321
  specific = [(k, v) for k, v in props.items() if k not in common_names]
@@ -322,41 +335,41 @@ def print_summary(schema: dict) -> None:
322
335
  # Comparison
323
336
  comp = schema.get("comparison", {})
324
337
  if comp and "error" not in comp:
325
- print(f"\n## Schema Comparison (corpus vs schema.py)")
338
+ print("\n## Schema Comparison (corpus vs schema.py)")
326
339
 
327
340
  types_missing = comp.get("item_types_in_corpus_not_in_schema", [])
328
341
  if types_missing:
329
- print(f" Item types in corpus but NOT in schema.py:")
342
+ print(" Item types in corpus but NOT in schema.py:")
330
343
  for t in types_missing:
331
344
  count = item_types.get(t, {}).get("count", "?")
332
345
  print(f" - {t} ({count} instances)")
333
346
 
334
347
  types_extra = comp.get("item_types_in_schema_not_in_corpus", [])
335
348
  if types_extra:
336
- print(f" Item types in schema.py but NOT in corpus:")
349
+ print(" Item types in schema.py but NOT in corpus:")
337
350
  for t in types_extra:
338
351
  print(f" - {t}")
339
352
 
340
353
  props_missing = comp.get("properties_in_corpus_not_in_schema", {})
341
354
  if props_missing:
342
- print(f" Properties in corpus but NOT in schema.py:")
355
+ print(" Properties in corpus but NOT in schema.py:")
343
356
  for type_name, props in sorted(props_missing.items()):
344
357
  print(f" {type_name}: {', '.join(props)}")
345
358
 
346
359
  sym_missing = comp.get("symbologies_in_corpus_not_in_schema", [])
347
360
  if sym_missing:
348
- print(f" Barcode symbologies in corpus but NOT in schema.py:")
361
+ print(" Barcode symbologies in corpus but NOT in schema.py:")
349
362
  for s in sym_missing:
350
363
  print(f" - {s}")
351
364
 
352
365
  sym_extra = comp.get("symbologies_in_schema_not_in_corpus", [])
353
366
  if sym_extra:
354
- print(f" Barcode symbologies in schema.py but NOT in corpus:")
367
+ print(" Barcode symbologies in schema.py but NOT in corpus:")
355
368
  for s in sym_extra:
356
369
  print(f" - {s}")
357
370
 
358
371
  if not comp or all(not v for v in comp.values() if isinstance(v, (list, dict))):
359
- print(f"\n Schema comparison: no gaps found!")
372
+ print("\n Schema comparison: no gaps found!")
360
373
 
361
374
 
362
375
  # ── Main ─────────────────────────────────────────────────────────────────
@@ -364,12 +377,23 @@ def print_summary(schema: dict) -> None:
364
377
 
365
378
  def main() -> None:
366
379
  parser = argparse.ArgumentParser(description="Generate RDLX-JSON schema from report corpus")
367
- parser.add_argument("directory", nargs="?", default="example-reports",
368
- help="Directory containing .rdlx-json files (default: example-reports/)")
369
- parser.add_argument("-o", "--output", default="corpus-schema.json",
370
- help="Output JSON file path (default: corpus-schema.json)")
371
- parser.add_argument("--summary-only", action="store_true",
372
- help="Print human-readable summary only, skip JSON output")
380
+ parser.add_argument(
381
+ "directory",
382
+ nargs="?",
383
+ default="example-reports",
384
+ help="Directory containing .rdlx-json files (default: example-reports/)",
385
+ )
386
+ parser.add_argument(
387
+ "-o",
388
+ "--output",
389
+ default="corpus-schema.json",
390
+ help="Output JSON file path (default: corpus-schema.json)",
391
+ )
392
+ parser.add_argument(
393
+ "--summary-only",
394
+ action="store_true",
395
+ help="Print human-readable summary only, skip JSON output",
396
+ )
373
397
  args = parser.parse_args()
374
398
 
375
399
  root = Path(args.directory)
@@ -1,4 +1,4 @@
1
1
  """Datex Studio CLI - Command-line interface for Datex Studio platform."""
2
2
 
3
- __version__ = "0.4.1"
3
+ __version__ = "0.4.3"
4
4
  __app_name__ = "dxs"
@@ -38,6 +38,37 @@ def set_last_output_format(fmt: OutputFormat) -> None:
38
38
  _last_output_format = fmt
39
39
 
40
40
 
41
+ def _format_set_in_env_or_config() -> bool:
42
+ """Whether ``default_output_format`` was set explicitly outside CLI flags.
43
+
44
+ Used to decide if TTY-auto-script mode should override the default format.
45
+ Returns True when either ``DXS_DEFAULT_OUTPUT_FORMAT`` is in the environment
46
+ or the config file contains a ``default_output_format`` entry.
47
+ """
48
+ import os
49
+
50
+ from dxs.utils.config import load_config_file
51
+
52
+ if os.environ.get("DXS_DEFAULT_OUTPUT_FORMAT"):
53
+ return True
54
+ try:
55
+ return bool(load_config_file().get("default_output_format"))
56
+ except Exception: # noqa: BLE001 - config read should never block startup
57
+ return False
58
+
59
+
60
+ def _stdout_is_pipe() -> bool:
61
+ """Whether stdout looks like a non-interactive consumer (pipe/redirect).
62
+
63
+ Pulled into a helper so tests can patch it independently of Click's
64
+ CliRunner, which swaps ``sys.stdout`` with a non-TTY StringIO.
65
+ """
66
+ try:
67
+ return not sys.stdout.isatty()
68
+ except (AttributeError, ValueError):
69
+ return False
70
+
71
+
41
72
  # Global options shared across all commands
42
73
  CONTEXT_SETTINGS = {
43
74
  "help_option_names": ["-h", "--help"],
@@ -140,6 +171,13 @@ class DxsGroup(click.Group):
140
171
  default=False,
141
172
  help="Show full output including null values and verbose metadata",
142
173
  )
174
+ @click.option(
175
+ "--flat-fv/--no-flat-fv",
176
+ "flat_fv",
177
+ default=None,
178
+ help="Flatten Dynamics @OData FormattedValue siblings into clean keys. "
179
+ "Defaults to on with --concise (the default mode); pass --no-flat-fv to opt out.",
180
+ )
143
181
  @click.option(
144
182
  "-s",
145
183
  "--save",
@@ -181,6 +219,7 @@ def cli(
181
219
  verbose: bool,
182
220
  quiet: bool,
183
221
  full: bool,
222
+ flat_fv: bool | None,
184
223
  save_path: str | None,
185
224
  force_overwrite: bool,
186
225
  diagnose_auth: bool,
@@ -229,19 +268,40 @@ def cli(
229
268
  # Load settings
230
269
  settings = get_settings()
231
270
 
232
- # Set output format (CLI flag > env var > config > default)
271
+ # When stdout is not a TTY (piped/redirected), assume a script consumer:
272
+ # default to JSON output and suppress progress banners unless the user
273
+ # has explicitly chosen otherwise via CLI flag, env var, or config file.
274
+ piped = _stdout_is_pipe()
275
+ click_ctx = click.get_current_context()
276
+ quiet_src = click_ctx.get_parameter_source("quiet")
277
+ fmt_explicit_in_env_or_config = _format_set_in_env_or_config()
278
+
279
+ # Set output format (CLI flag > env var > config > TTY auto > default)
233
280
  if output_format:
234
281
  dxs_ctx.output_format = OutputFormat(output_format.lower())
235
282
  dxs_ctx.explicit_output_format = True
283
+ elif fmt_explicit_in_env_or_config and settings.default_output_format:
284
+ dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
285
+ elif piped:
286
+ dxs_ctx.output_format = OutputFormat.JSON
236
287
  elif settings.default_output_format:
237
288
  dxs_ctx.output_format = OutputFormat(settings.default_output_format.lower())
238
289
  set_last_output_format(dxs_ctx.output_format)
239
290
 
240
291
  # Set verbosity (--verbose enables debug output, --quiet suppresses progress)
241
292
  dxs_ctx.verbose = verbose
242
- dxs_ctx.quiet = quiet
293
+ if quiet_src == click.core.ParameterSource.COMMANDLINE:
294
+ dxs_ctx.quiet = quiet
295
+ else:
296
+ dxs_ctx.quiet = quiet or piped
243
297
  # Concise output is default; --full disables it
244
298
  dxs_ctx.concise = not full
299
+ # Flatten Dynamics FormattedValue siblings: follow --concise unless
300
+ # --flat-fv / --no-flat-fv was passed explicitly.
301
+ if flat_fv is None:
302
+ dxs_ctx.flat_fv = dxs_ctx.concise
303
+ else:
304
+ dxs_ctx.flat_fv = flat_fv
245
305
 
246
306
  # Set save options
247
307
  dxs_ctx.save_path = save_path
@@ -260,6 +320,17 @@ def cli(
260
320
  # Stash for the result_callback to honor --no-update-check
261
321
  dxs_ctx.no_update_check = no_update_check
262
322
 
323
+ # Telemetry: record invocation start (must come before any subcommand runs).
324
+ # Wrapped so a telemetry bug can never block the CLI.
325
+ try:
326
+ from dxs.core.telemetry import maybe_show_first_run_notice, record_start
327
+
328
+ invoked = ctx.invoked_subcommand or "<root>"
329
+ record_start(argv=sys.argv[1:], command=invoked)
330
+ maybe_show_first_run_notice()
331
+ except Exception: # noqa: BLE001
332
+ pass
333
+
263
334
 
264
335
  def _mask_token(token: str | None, visible_chars: int = 8) -> str:
265
336
  """Mask a token for display, showing only first/last few characters.
@@ -316,18 +387,30 @@ def handle_errors(f: F) -> F:
316
387
  try:
317
388
  return ctx.invoke(f, *args, **kwargs)
318
389
  except DxsError as e:
390
+ _record_error_silently(e)
319
391
  dxs_ctx.output_error(e)
320
392
  sys.exit(1)
321
393
  except click.ClickException:
322
394
  # Let Click handle its own exceptions
323
395
  raise
324
396
  except Exception as e:
397
+ _record_error_silently(e)
325
398
  dxs_ctx.output_error(e)
326
399
  sys.exit(1)
327
400
 
328
401
  return cast(F, wrapper)
329
402
 
330
403
 
404
+ def _record_error_silently(exc: BaseException) -> None:
405
+ """Emit a telemetry error event; never raises."""
406
+ try:
407
+ from dxs.core.telemetry import record_error
408
+
409
+ record_error(exc)
410
+ except Exception: # noqa: BLE001
411
+ pass
412
+
413
+
331
414
  # Import and register command groups
332
415
  # These imports are here to avoid circular imports
333
416
  def register_commands() -> None:
@@ -336,6 +419,7 @@ def register_commands() -> None:
336
419
  api,
337
420
  auth,
338
421
  config,
422
+ configuration,
339
423
  crm,
340
424
  datasource,
341
425
  devops,
@@ -355,6 +439,8 @@ def register_commands() -> None:
355
439
  cli.add_command(api.api)
356
440
  cli.add_command(auth.auth)
357
441
  cli.add_command(config.config)
442
+ cli.add_command(config.config, name="settings")
443
+ cli.add_command(configuration.configuration)
358
444
  cli.add_command(crm.crm)
359
445
  cli.add_command(datasource.datasource)
360
446
  cli.add_command(devops.devops)
@@ -376,14 +462,30 @@ def register_commands() -> None:
376
462
 
377
463
  cli.add_command(schema.schema)
378
464
 
465
+ # Telemetry subcommands (status / enable / disable / test)
466
+ from dxs.commands import telemetry as telemetry_cmd
467
+
468
+ cli.add_command(telemetry_cmd.telemetry)
469
+
379
470
 
380
471
  @cli.result_callback()
381
472
  @click.pass_context
382
473
  def _after_command(ctx: click.Context, result: Any, **kwargs: Any) -> Any:
383
- """Run post-command hooks (currently: PyPI update notice)."""
474
+ """Run post-command hooks (telemetry success event + PyPI update notice)."""
384
475
  from dxs.utils.update_check import maybe_show_update_notice
385
476
 
386
477
  dxs_ctx: DxsContext | None = ctx.find_object(DxsContext)
478
+
479
+ # Emit a success event. handle_errors-decorated commands never reach here
480
+ # on failure, so the only path through this branch is a successful run.
481
+ try:
482
+ from dxs.core.telemetry import record_end
483
+
484
+ ctx_extras = _ctx_extras(dxs_ctx)
485
+ record_end(exit_code=0, ctx_extras=ctx_extras)
486
+ except Exception: # noqa: BLE001
487
+ pass
488
+
387
489
  skip = bool(dxs_ctx and dxs_ctx.no_update_check)
388
490
  try:
389
491
  maybe_show_update_notice(skip=skip)
@@ -392,6 +494,18 @@ def _after_command(ctx: click.Context, result: Any, **kwargs: Any) -> Any:
392
494
  return result
393
495
 
394
496
 
497
+ def _ctx_extras(dxs_ctx: "DxsContext | None") -> dict[str, Any]:
498
+ """Pluck branch/repo/org/env from DxsContext for telemetry events."""
499
+ if dxs_ctx is None:
500
+ return {}
501
+ return {
502
+ "branch_id": dxs_ctx.branch,
503
+ "repo_id": dxs_ctx.repo,
504
+ "org": dxs_ctx.org,
505
+ "env": dxs_ctx.env,
506
+ }
507
+
508
+
395
509
  @cli.command("update")
396
510
  @click.option(
397
511
  "--check",
@@ -438,13 +552,30 @@ register_commands()
438
552
  def main() -> None:
439
553
  """Main entry point for the CLI."""
440
554
  try:
441
- cli()
442
- except DxsError as e:
443
- # Use the module-level format captured during CLI init
444
- # (Click context is torn down before exceptions reach here)
445
- formatted = format_error(e, _last_output_format)
446
- click.echo(formatted, err=True)
447
- sys.exit(1)
555
+ try:
556
+ cli()
557
+ except DxsError as e:
558
+ _record_error_silently(e)
559
+ # Use the module-level format captured during CLI init
560
+ # (Click context is torn down before exceptions reach here)
561
+ formatted = format_error(e, _last_output_format)
562
+ click.echo(formatted, err=True)
563
+ sys.exit(1)
564
+ except SystemExit:
565
+ # SystemExit from sys.exit() inside handle_errors — telemetry already
566
+ # recorded an error there; just propagate.
567
+ raise
568
+ except BaseException as e:
569
+ # Anything else escaping cli() shouldn't take telemetry with it.
570
+ _record_error_silently(e)
571
+ raise
572
+ finally:
573
+ try:
574
+ from dxs.core.telemetry import flush
575
+
576
+ flush(timeout=0.5)
577
+ except Exception: # noqa: BLE001
578
+ pass
448
579
 
449
580
 
450
581
  if __name__ == "__main__":