datex-studio-cli 0.4.12__tar.gz → 0.4.14__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 (195) hide show
  1. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/.gitignore +3 -0
  2. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/PKG-INFO +95 -5
  3. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/README.md +93 -3
  4. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/pyproject.toml +1 -1
  5. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/__init__.py +1 -1
  6. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/cli.py +28 -0
  7. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/auth.py +66 -39
  8. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/branch.py +83 -11
  9. datex_studio_cli-0.4.14/src/dxs/commands/cascade.py +701 -0
  10. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/configuration.py +31 -3
  11. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/crm.py +966 -53
  12. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/devops.py +64 -11
  13. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/marketplace.py +36 -18
  14. datex_studio_cli-0.4.14/src/dxs/commands/ng/__init__.py +44 -0
  15. datex_studio_cli-0.4.14/src/dxs/commands/ng/_common.py +37 -0
  16. datex_studio_cli-0.4.14/src/dxs/commands/ng/api.py +149 -0
  17. datex_studio_cli-0.4.14/src/dxs/commands/ng/create.py +82 -0
  18. datex_studio_cli-0.4.14/src/dxs/commands/ng/data.py +222 -0
  19. datex_studio_cli-0.4.14/src/dxs/commands/ng/pack.py +63 -0
  20. datex_studio_cli-0.4.14/src/dxs/commands/ng/preview.py +384 -0
  21. datex_studio_cli-0.4.14/src/dxs/commands/reference.py +192 -0
  22. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/repo.py +6 -6
  23. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/api.py +8 -6
  24. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/folder.py +5 -2
  25. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/preview.py +2 -2
  26. datex_studio_cli-0.4.14/src/dxs/commands/reset.py +216 -0
  27. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/source.py +66 -0
  28. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/app_config.py +1 -17
  29. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/client.py +18 -10
  30. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/endpoints.py +42 -5
  31. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/models.py +33 -7
  32. datex_studio_cli-0.4.14/src/dxs/core/api/reference_graph.py +132 -0
  33. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/auth/datex_tenant.py +12 -10
  34. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/auth/decorators.py +7 -4
  35. datex_studio_cli-0.4.14/src/dxs/core/auth/tenants.py +71 -0
  36. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/auth/token_cache.py +8 -3
  37. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/parsers.py +2 -5
  38. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/resolver.py +4 -8
  39. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/devops/client.py +36 -0
  40. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/dynamics/client.py +255 -41
  41. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/redaction.py +15 -3
  42. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/config.py +8 -3
  43. datex_studio_cli-0.4.14/src/dxs/ng/__init__.py +7 -0
  44. datex_studio_cli-0.4.14/src/dxs/ng/folder.py +256 -0
  45. datex_studio_cli-0.4.14/src/dxs/ng/manifest.py +127 -0
  46. datex_studio_cli-0.4.14/src/dxs/ng/mocks.py +142 -0
  47. datex_studio_cli-0.4.14/src/dxs/ng/preview.py +79 -0
  48. datex_studio_cli-0.4.14/src/dxs/ng/scaffold.py +88 -0
  49. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/datasource_binding.py +2 -5
  50. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/folder.py +2 -6
  51. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/preview_arjs.py +2 -2
  52. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/config.py +15 -0
  53. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/paths.py +17 -3
  54. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/resolvers.py +119 -17
  55. datex_studio_cli-0.4.14/src/dxs/utils/versioning.py +22 -0
  56. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/report-creator.skill +0 -0
  57. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/scripts/analyze_corpus.py +0 -0
  58. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/scripts/generate_rdlx_schema.py +0 -0
  59. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/__main__.py +0 -0
  60. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/__init__.py +0 -0
  61. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/api.py +0 -0
  62. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/config.py +0 -0
  63. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/datasource.py +0 -0
  64. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/document.py +0 -0
  65. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/endpoint.py +0 -0
  66. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/env.py +0 -0
  67. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/explore.py +0 -0
  68. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/function.py +0 -0
  69. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/init.py +0 -0
  70. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/odata.py +0 -0
  71. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/organization.py +0 -0
  72. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/proxy.py +0 -0
  73. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/release_notes.py +0 -0
  74. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/__init__.py +0 -0
  75. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/add_cmds.py +0 -0
  76. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/batch.py +0 -0
  77. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/create.py +0 -0
  78. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/data.py +0 -0
  79. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/dataset.py +0 -0
  80. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/edit.py +0 -0
  81. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/report/schema_cmds.py +0 -0
  82. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/schema.py +0 -0
  83. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/servicepack.py +0 -0
  84. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/studio.py +0 -0
  85. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/telemetry.py +0 -0
  86. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/commands/user.py +0 -0
  87. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/context.py +0 -0
  88. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/__init__.py +0 -0
  89. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/__init__.py +0 -0
  90. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/api/metadata_models.py +0 -0
  91. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/auth/__init__.py +0 -0
  92. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/auth/msal_client.py +0 -0
  93. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/cache.py +0 -0
  94. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/__init__.py +0 -0
  95. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/flow_generator.py +0 -0
  96. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/flow_models.py +0 -0
  97. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/generator.py +0 -0
  98. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/models.py +0 -0
  99. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/type_def_parser.py +0 -0
  100. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/datasource/validator.py +0 -0
  101. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/designer/__init__.py +0 -0
  102. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/designer/flow_models.py +0 -0
  103. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/designer/models.py +0 -0
  104. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/designer/parsers.py +0 -0
  105. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/devops/__init__.py +0 -0
  106. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/devops/helpers.py +0 -0
  107. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/devops/models.py +0 -0
  108. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/dynamics/__init__.py +0 -0
  109. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/dynamics/metadata_cache.py +0 -0
  110. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/footprint/__init__.py +0 -0
  111. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/footprint/client.py +0 -0
  112. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/footprint/edmx_parser.py +0 -0
  113. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/footprint/metadata.py +0 -0
  114. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/function/__init__.py +0 -0
  115. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/function/generator.py +0 -0
  116. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/function/models.py +0 -0
  117. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/function/validator.py +0 -0
  118. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/graph.py +0 -0
  119. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/onboarding.py +0 -0
  120. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/__init__.py +0 -0
  121. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/csv_fmt.py +0 -0
  122. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/formatter.py +0 -0
  123. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/json_fmt.py +0 -0
  124. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/output/yaml_fmt.py +0 -0
  125. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/release_notes.py +0 -0
  126. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/responses.py +0 -0
  127. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/__init__.py +0 -0
  128. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/identity.py +0 -0
  129. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/recorder.py +0 -0
  130. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/scrubber.py +0 -0
  131. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/core/telemetry/sender.py +0 -0
  132. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/models/__init__.py +0 -0
  133. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/py.typed +0 -0
  134. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/__init__.py +0 -0
  135. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/engine.py +0 -0
  136. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/field_parser.py +0 -0
  137. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/manifest.py +0 -0
  138. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/models.py +0 -0
  139. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/preview.py +0 -0
  140. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/schema.py +0 -0
  141. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/svg_renderer.py +0 -0
  142. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/templates/__init__.py +0 -0
  143. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/templates/bill_of_lading.rdlx-json +0 -0
  144. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/templates/shipping_label.rdlx-json +0 -0
  145. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/validate_arjs.py +0 -0
  146. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/validator.py +0 -0
  147. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/report/wrapper.py +0 -0
  148. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/__init__.py +0 -0
  149. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/click_options.py +0 -0
  150. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/clipboard.py +0 -0
  151. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/errors.py +0 -0
  152. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/filtering.py +0 -0
  153. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/image.py +0 -0
  154. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/responses.py +0 -0
  155. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/restricted.py +0 -0
  156. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/sorting.py +0 -0
  157. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/utils/update_check.py +0 -0
  158. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/__init__.py +0 -0
  159. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/app.py +0 -0
  160. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/design.py +0 -0
  161. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/proxy_app.py +0 -0
  162. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/404/index.html +0 -0
  163. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/404.html +0 -0
  164. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_buildManifest.js +0 -0
  165. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/UgyAMtPjH-Pt0RYWOHzIT/_ssgManifest.js +0 -0
  166. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/0b24cca5-c1e1c8810348f107.js +0 -0
  167. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/255-102f2e5b2e3dc2ef.js +0 -0
  168. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -0
  169. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/538-84e2e111415f1fda.js +0 -0
  170. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/697-0e000ab410d9f470.js +0 -0
  171. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/871-1acacdb7b4022abf.js +0 -0
  172. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/8b8f67fc-6295949a4b5485a4.js +0 -0
  173. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/909-c88abba3cf0caec3.js +0 -0
  174. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/app/_not-found/page-ea1be7001c230704.js +0 -0
  175. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/app/design/capture/page-a5e129c2d223432c.js +0 -0
  176. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/app/design/page-21ba66a3dd3b03f0.js +0 -0
  177. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/app/layout-2cc6eac09e476c91.js +0 -0
  178. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/app/page-20c358395882cdac.js +0 -0
  179. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/framework-de98b93a850cfc71.js +0 -0
  180. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/main-0f18f91200dac003.js +0 -0
  181. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/main-app-b69998d8941231d8.js +0 -0
  182. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +0 -0
  183. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -0
  184. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -0
  185. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/chunks/webpack-2b297ada5306c17f.js +0 -0
  186. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/_next/static/css/360c657de5e04df8.css +0 -0
  187. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/design/capture/index.html +0 -0
  188. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/design/capture/index.txt +0 -0
  189. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/design/index.html +0 -0
  190. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/design/index.txt +0 -0
  191. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/index.html +0 -0
  192. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/index.txt +0 -0
  193. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/render-cli.js +0 -0
  194. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/report-preview.html +0 -0
  195. {datex_studio_cli-0.4.12 → datex_studio_cli-0.4.14}/src/dxs/web/static/report-validate.html +0 -0
@@ -99,3 +99,6 @@ Thumbs.db
99
99
  /reports/
100
100
  test-preview.svg
101
101
  test-preview.svg
102
+
103
+ # CAC working copies (dxs ng harnesses) — never committed
104
+ /components/
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: datex-studio-cli
3
- Version: 0.4.12
3
+ Version: 0.4.14
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
@@ -247,6 +247,7 @@ Direct commands on the `source` group:
247
247
  | `locks` | Show current lock status |
248
248
  | `deps <ref>` | Show configuration dependencies |
249
249
  | `deps-diff` | Show dependency changes between versions |
250
+ | `referenced-from <pkg>` | List packages/apps that reference a package |
250
251
  | `workitems` | Extract work items from commit messages |
251
252
  | `compare` | Compare two branches |
252
253
  | `graph` | Show dependency graph |
@@ -278,7 +279,8 @@ dxs source compare --source 100 --target 200
278
279
  | `show <id>` | Show branch details |
279
280
  | `create` | Create a new feature branch |
280
281
  | `delete <id>` | Delete a feature branch |
281
- | `publish <id>` | Publish branch to marketplace |
282
+ | `commit <id>` | Commit (merge) a feature branch into its Main branch |
283
+ | `publish <id>` | Publish branch to marketplace (`--notes` is **required**) |
282
284
  | `baseline <id>` | Find baseline (previous release) |
283
285
  | `search <query>` | Search branches by name |
284
286
  | `roles <id>` | Show branch roles |
@@ -305,6 +307,83 @@ dxs source branch list --repo 10 --with-changes
305
307
  dxs source branch list --repo 10 --sort modified --desc
306
308
  ```
307
309
 
310
+ ### Package References (`dxs source reference`)
311
+
312
+ | Command | Description |
313
+ |---------|-------------|
314
+ | `set` | Re-pin a direct package reference and rebuild transitive references |
315
+
316
+ ```bash
317
+ # Pin Module1 to a newly published version on feature branch 3101
318
+ dxs source reference set -b 3101 -p Module1 -v 20260714.1000
319
+ ```
320
+
321
+ Rebuilds the branch's flattened reference list after the bump and refuses the
322
+ write if the result would contain a version conflict (the same package pinned at
323
+ two versions) or a circular reference.
324
+
325
+ ### Package Cascade (`dxs source cascade`)
326
+
327
+ When a package is republished, every package that references it stays pinned to
328
+ the old version until it is re-pinned and republished itself. The cascade
329
+ automates that fan-out.
330
+
331
+ | Command | Description |
332
+ |---------|-------------|
333
+ | `plan` | Compute the ordered set of packages to re-pin + republish |
334
+ | `run` | Execute a plan bottom-up (branch → re-pin → commit → publish) |
335
+
336
+ ```bash
337
+ # 1. Plan the cascade from a package you just published
338
+ dxs source cascade plan -p Module1 -v 20260714.1000 -O json > plan.json
339
+
340
+ # 2. Review what it would do — mutates nothing
341
+ dxs source cascade run --plan plan.json --dry-run
342
+
343
+ # 3. Execute
344
+ dxs source cascade run --plan plan.json
345
+ ```
346
+
347
+ `plan` walks reverse-references from the origin package and emits update nodes
348
+ grouped into topological levels, plus a `staleApplications` list — applications
349
+ that consume an affected package but cannot be cascaded automatically.
350
+
351
+ - `--org` scopes traversal to a single organization. **Omit it and the cascade
352
+ spans consumers across all organizations**, which is rarely what you want.
353
+ - `--max-depth` limits how far the traversal follows the reference chain.
354
+
355
+ `run` executes each level bottom-up, feeding each package's newly published
356
+ version forward to its dependents.
357
+
358
+ Before mutating anything it applies the same guardrails as `branch publish`:
359
+ each node's Main branch is resolved to its owning organization and checked
360
+ against the current identity (once per distinct organization), then the run is
361
+ confirmed. Each re-pinned feature branch is validated before it is committed, so
362
+ a broken reference set never reaches Main or the marketplace.
363
+
364
+ - `--dry-run` prints the resolved execution order and mutates nothing.
365
+ - `--yes` / `-y` skips the confirmation prompt — use this for unattended runs.
366
+ Branch validation still runs.
367
+ - `--force` / `-f` skips both the confirmation prompt and branch validation.
368
+ - `--select` restricts the run to named packages (repeatable). Deselecting an
369
+ intermediate dependency will fail the run, since its dependents need the
370
+ version it would have produced.
371
+ - `--release-notes` sets the notes for every republish; otherwise a summary of
372
+ the re-pins is used.
373
+
374
+ A node whose dependencies are all already at their target version is skipped
375
+ rather than republished for zero change, and its dependents treat it as a no-op
376
+ instead of failing.
377
+
378
+ Publishing can block on a build, so the publish step uses the longer
379
+ `publish_timeout` setting (default 300s) rather than `api_timeout`. If that call
380
+ still times out, the version is confirmed by looking up the exact
381
+ `(package, version)` pair in the marketplace — the record is committed before the
382
+ build runs, so a timed-out publish has usually still landed.
383
+
384
+ `cascade run` and `reference set` are both blocked when
385
+ `DXS_RESTRICTED_MODE=true`.
386
+
308
387
  ### Repository Management (`dxs source repo`)
309
388
 
310
389
  | Command | Description |
@@ -608,11 +687,22 @@ dxs odata execute -c 1 -q "Products?\$select=Id,Name,Price&\$top=10"
608
687
 
609
688
  | Command | Description |
610
689
  |---------|-------------|
611
- | `list` | List marketplace applications |
690
+ | `list` | List marketplace packages available to your org |
612
691
  | `show <id>` | Show application details |
613
692
  | `versions <id>` | List published versions |
614
693
  | `version <id>` | Show version details |
615
- | `search <query>` | Search applications |
694
+ | `search <query>` | Search available packages by name/description |
695
+
696
+ `list` and `search` mirror the Manager > Marketplace screen: they show packages
697
+ available to a consumer organization, each with an `included` flag indicating
698
+ whether that org already has access.
699
+
700
+ - `--type` selects the marketplace application type, defaulting to
701
+ `componentmodule`. Other types (`web`, `mobile`, `api`, `portal`,
702
+ `footprintapi`, `taskscheduler`) are accepted but are not what the Marketplace
703
+ screen shows.
704
+ - `--org` is the *consumer* organization being browsed on behalf of, not a
705
+ result filter. Omit it to browse as your own organization.
616
706
 
617
707
  ### Azure DevOps (`dxs devops`)
618
708
 
@@ -194,6 +194,7 @@ Direct commands on the `source` group:
194
194
  | `locks` | Show current lock status |
195
195
  | `deps <ref>` | Show configuration dependencies |
196
196
  | `deps-diff` | Show dependency changes between versions |
197
+ | `referenced-from <pkg>` | List packages/apps that reference a package |
197
198
  | `workitems` | Extract work items from commit messages |
198
199
  | `compare` | Compare two branches |
199
200
  | `graph` | Show dependency graph |
@@ -225,7 +226,8 @@ dxs source compare --source 100 --target 200
225
226
  | `show <id>` | Show branch details |
226
227
  | `create` | Create a new feature branch |
227
228
  | `delete <id>` | Delete a feature branch |
228
- | `publish <id>` | Publish branch to marketplace |
229
+ | `commit <id>` | Commit (merge) a feature branch into its Main branch |
230
+ | `publish <id>` | Publish branch to marketplace (`--notes` is **required**) |
229
231
  | `baseline <id>` | Find baseline (previous release) |
230
232
  | `search <query>` | Search branches by name |
231
233
  | `roles <id>` | Show branch roles |
@@ -252,6 +254,83 @@ dxs source branch list --repo 10 --with-changes
252
254
  dxs source branch list --repo 10 --sort modified --desc
253
255
  ```
254
256
 
257
+ ### Package References (`dxs source reference`)
258
+
259
+ | Command | Description |
260
+ |---------|-------------|
261
+ | `set` | Re-pin a direct package reference and rebuild transitive references |
262
+
263
+ ```bash
264
+ # Pin Module1 to a newly published version on feature branch 3101
265
+ dxs source reference set -b 3101 -p Module1 -v 20260714.1000
266
+ ```
267
+
268
+ Rebuilds the branch's flattened reference list after the bump and refuses the
269
+ write if the result would contain a version conflict (the same package pinned at
270
+ two versions) or a circular reference.
271
+
272
+ ### Package Cascade (`dxs source cascade`)
273
+
274
+ When a package is republished, every package that references it stays pinned to
275
+ the old version until it is re-pinned and republished itself. The cascade
276
+ automates that fan-out.
277
+
278
+ | Command | Description |
279
+ |---------|-------------|
280
+ | `plan` | Compute the ordered set of packages to re-pin + republish |
281
+ | `run` | Execute a plan bottom-up (branch → re-pin → commit → publish) |
282
+
283
+ ```bash
284
+ # 1. Plan the cascade from a package you just published
285
+ dxs source cascade plan -p Module1 -v 20260714.1000 -O json > plan.json
286
+
287
+ # 2. Review what it would do — mutates nothing
288
+ dxs source cascade run --plan plan.json --dry-run
289
+
290
+ # 3. Execute
291
+ dxs source cascade run --plan plan.json
292
+ ```
293
+
294
+ `plan` walks reverse-references from the origin package and emits update nodes
295
+ grouped into topological levels, plus a `staleApplications` list — applications
296
+ that consume an affected package but cannot be cascaded automatically.
297
+
298
+ - `--org` scopes traversal to a single organization. **Omit it and the cascade
299
+ spans consumers across all organizations**, which is rarely what you want.
300
+ - `--max-depth` limits how far the traversal follows the reference chain.
301
+
302
+ `run` executes each level bottom-up, feeding each package's newly published
303
+ version forward to its dependents.
304
+
305
+ Before mutating anything it applies the same guardrails as `branch publish`:
306
+ each node's Main branch is resolved to its owning organization and checked
307
+ against the current identity (once per distinct organization), then the run is
308
+ confirmed. Each re-pinned feature branch is validated before it is committed, so
309
+ a broken reference set never reaches Main or the marketplace.
310
+
311
+ - `--dry-run` prints the resolved execution order and mutates nothing.
312
+ - `--yes` / `-y` skips the confirmation prompt — use this for unattended runs.
313
+ Branch validation still runs.
314
+ - `--force` / `-f` skips both the confirmation prompt and branch validation.
315
+ - `--select` restricts the run to named packages (repeatable). Deselecting an
316
+ intermediate dependency will fail the run, since its dependents need the
317
+ version it would have produced.
318
+ - `--release-notes` sets the notes for every republish; otherwise a summary of
319
+ the re-pins is used.
320
+
321
+ A node whose dependencies are all already at their target version is skipped
322
+ rather than republished for zero change, and its dependents treat it as a no-op
323
+ instead of failing.
324
+
325
+ Publishing can block on a build, so the publish step uses the longer
326
+ `publish_timeout` setting (default 300s) rather than `api_timeout`. If that call
327
+ still times out, the version is confirmed by looking up the exact
328
+ `(package, version)` pair in the marketplace — the record is committed before the
329
+ build runs, so a timed-out publish has usually still landed.
330
+
331
+ `cascade run` and `reference set` are both blocked when
332
+ `DXS_RESTRICTED_MODE=true`.
333
+
255
334
  ### Repository Management (`dxs source repo`)
256
335
 
257
336
  | Command | Description |
@@ -555,11 +634,22 @@ dxs odata execute -c 1 -q "Products?\$select=Id,Name,Price&\$top=10"
555
634
 
556
635
  | Command | Description |
557
636
  |---------|-------------|
558
- | `list` | List marketplace applications |
637
+ | `list` | List marketplace packages available to your org |
559
638
  | `show <id>` | Show application details |
560
639
  | `versions <id>` | List published versions |
561
640
  | `version <id>` | Show version details |
562
- | `search <query>` | Search applications |
641
+ | `search <query>` | Search available packages by name/description |
642
+
643
+ `list` and `search` mirror the Manager > Marketplace screen: they show packages
644
+ available to a consumer organization, each with an `included` flag indicating
645
+ whether that org already has access.
646
+
647
+ - `--type` selects the marketplace application type, defaulting to
648
+ `componentmodule`. Other types (`web`, `mobile`, `api`, `portal`,
649
+ `footprintapi`, `taskscheduler`) are accepted but are not what the Marketplace
650
+ screen shows.
651
+ - `--org` is the *consumer* organization being browsed on behalf of, not a
652
+ result filter. Omit it to browse as your own organization.
563
653
 
564
654
  ### Azure DevOps (`dxs devops`)
565
655
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datex-studio-cli"
7
- version = "0.4.12"
7
+ version = "0.4.14"
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"
@@ -1,4 +1,4 @@
1
1
  """Datex Studio CLI - Command-line interface for Datex Studio platform."""
2
2
 
3
- __version__ = "0.4.12"
3
+ __version__ = "0.4.14"
4
4
  __app_name__ = "dxs"
@@ -70,6 +70,29 @@ def _stdout_is_pipe() -> bool:
70
70
  return False
71
71
 
72
72
 
73
+ def _ensure_utf8_streams() -> None:
74
+ """Force stdout/stderr to UTF-8 so non-ASCII output never crashes the CLI.
75
+
76
+ On Windows a piped or redirected stream defaults to the locale encoding
77
+ (typically cp1252), which cannot represent characters such as U+2192 (the
78
+ arrow used in help text like ``dxs source cascade -h`` and in ``cascade
79
+ run`` progress logs). Writing them raises ``UnicodeEncodeError`` and aborts
80
+ the command. Reconfiguring to UTF-8 at entry is lossless and idempotent.
81
+
82
+ Streams that do not support reconfiguration (already detached, or a test
83
+ double) are left untouched.
84
+ """
85
+ for stream in (sys.stdout, sys.stderr):
86
+ reconfigure = getattr(stream, "reconfigure", None)
87
+ if reconfigure is None:
88
+ continue
89
+ try:
90
+ reconfigure(encoding="utf-8")
91
+ except (ValueError, OSError):
92
+ # Reconfiguration is unsupported for this stream; leave it as-is.
93
+ pass
94
+
95
+
73
96
  # Global options shared across all commands
74
97
  CONTEXT_SETTINGS = {
75
98
  "help_option_names": ["-h", "--help"],
@@ -498,10 +521,12 @@ def register_commands() -> None:
498
521
  function,
499
522
  init,
500
523
  marketplace,
524
+ ng,
501
525
  odata,
502
526
  organization,
503
527
  proxy,
504
528
  report,
529
+ reset,
505
530
  source,
506
531
  studio,
507
532
  user,
@@ -520,10 +545,12 @@ def register_commands() -> None:
520
545
  cli.add_command(env.env)
521
546
  cli.add_command(function.function)
522
547
  cli.add_command(marketplace.marketplace)
548
+ cli.add_command(ng.ng)
523
549
  cli.add_command(odata.odata)
524
550
  cli.add_command(organization.organization)
525
551
  cli.add_command(proxy.proxy)
526
552
  cli.add_command(report.report)
553
+ cli.add_command(reset.reset)
527
554
  cli.add_command(source.source)
528
555
  cli.add_command(studio.studio)
529
556
  cli.add_command(user.user)
@@ -632,6 +659,7 @@ register_commands()
632
659
  # Main entry point
633
660
  def main() -> None:
634
661
  """Main entry point for the CLI."""
662
+ _ensure_utf8_streams()
635
663
  try:
636
664
  try:
637
665
  cli()
@@ -1,8 +1,10 @@
1
1
  """Authentication commands: dxs auth [login|logout|status|list|switch]."""
2
2
 
3
+ import secrets
3
4
  from collections.abc import Callable
4
5
  from datetime import datetime, timedelta, timezone
5
6
  from typing import Any
7
+ from urllib.parse import urlencode
6
8
 
7
9
  import click
8
10
 
@@ -10,6 +12,7 @@ from dxs.context import DxsContext, pass_context
10
12
  from dxs.core.auth import MSALClient, MultiIdentityTokenCache
11
13
  from dxs.core.auth.datex_tenant import claims_are_datex, is_datex_identity
12
14
  from dxs.core.auth.decorators import get_access_token
15
+ from dxs.core.auth.tenants import org_needs_tenant_login, tenant_authority_for
13
16
  from dxs.core.auth.token_cache import CachedIdentity, get_identity_override
14
17
  from dxs.core.footprint import get_footprint_token
15
18
  from dxs.utils.errors import AuthenticationError, ValidationError
@@ -48,19 +51,27 @@ def run_standard_device_login(
48
51
  *,
49
52
  tenant_id: str | None = None,
50
53
  domain: str | None = None,
54
+ external_entra_id: bool | None = None,
51
55
  organization_id: int | None = None,
52
56
  organization_name: str | None = None,
53
57
  api_base_url: str | None = None,
54
58
  ) -> CachedIdentity:
55
59
  """Run the multi-resource device-code login and save the identity.
56
60
 
57
- Shared by `dxs auth login`, `dxs auth switch` (external tenant) and `dxs init`.
61
+ Shared by `dxs auth login`, `dxs auth switch` (foreign tenant) and `dxs init`.
58
62
 
59
- When ``tenant_id`` is given, authenticates against that external Entra tenant
60
- (Footprint-only additional scopes) and persists the identity as external.
61
- Otherwise authenticates against the configured home tenant with the full
62
- additional-scope set. Org id/name are discovered from ``/organizations/mine``
63
- unless ``organization_id`` is supplied (staff switch already knows them).
63
+ When ``tenant_id`` is given, authenticates against that Entra tenant
64
+ (Footprint-only additional scopes). Otherwise authenticates against the
65
+ configured home tenant with the full additional-scope set. Org id/name are
66
+ discovered from ``/organizations/mine`` unless ``organization_id`` is
67
+ supplied (staff switch already knows them).
68
+
69
+ ``external_entra_id`` records whether the org has a *dedicated* Entra
70
+ External ID tenant, and is stored verbatim rather than inferred from
71
+ ``tenant_id``: a customer on their own corporate tenant needs a tenant-scoped
72
+ login while the flag stays False. Callers that do not know the org record
73
+ (the ``--tenant-id`` self-service path) leave it None, and it falls back to
74
+ the historical "a tenant was supplied" inference.
64
75
 
65
76
  ``api_base_url`` pins the environment used for that org discovery — callers
66
77
  that target a non-default environment (e.g. ``dxs init --target qa``) must
@@ -71,7 +82,7 @@ def run_standard_device_login(
71
82
 
72
83
  cache = MultiIdentityTokenCache()
73
84
  settings = get_settings()
74
- external = tenant_id is not None
85
+ external = (tenant_id is not None) if external_entra_id is None else external_entra_id
75
86
 
76
87
  def _on_consent(resource_name: str, scopes: list[str]) -> None:
77
88
  ctx.log(f"Additional consent needed for: {resource_name}")
@@ -636,11 +647,12 @@ def status(ctx: DxsContext) -> None:
636
647
  return f"{seconds // 3600} hours"
637
648
 
638
649
  # Check all cached tokens for each resource.
639
- # External-tenant identities must be probed against their own tenant authority;
650
+ # Foreign-tenant identities must be probed against their own tenant authority;
640
651
  # redeeming their refresh token against the default tenant returns invalid_grant.
641
652
  settings = get_settings()
642
- if active_identity.external_entra_id and active_identity.tenant_id:
643
- client = MSALClient.for_tenant(active_identity.tenant_id)
653
+ foreign_tenant = tenant_authority_for(active_identity)
654
+ if foreign_tenant:
655
+ client = MSALClient.for_tenant(foreign_tenant)
644
656
  else:
645
657
  client = MSALClient()
646
658
 
@@ -920,15 +932,15 @@ def consent(
920
932
  )
921
933
  )
922
934
  raise SystemExit(1)
923
- if not org_info.external_entra_id:
935
+ if not org_needs_tenant_login(org_info):
924
936
  ctx.output(
925
937
  single(
926
938
  item={
927
939
  "status": "not_required",
928
- "message": f"Organization '{org}' does not use external authentication",
940
+ "message": f"Organization '{org}' authenticates against the Datex home tenant",
929
941
  "organization": org_info.name,
930
942
  "organization_id": org_info.id,
931
- "note": "Admin consent is only required for organizations with external Entra ID tenants",
943
+ "note": "Admin consent is only required for organizations with their own Entra tenant",
932
944
  },
933
945
  semantic_key="consent",
934
946
  )
@@ -951,38 +963,49 @@ def consent(
951
963
  resolved_domain = org_info.external_entra_id_domain_name
952
964
 
953
965
  settings = get_settings()
954
- base_consent = f"https://login.microsoftonline.com/{resolved_tenant_id}/adminconsent"
955
- datex_consent_url = f"{base_consent}?client_id={settings.azure_client_id}"
956
- fp_scope = settings.footprint_scope
957
- fp_client_id = fp_scope.split("://")[1].split("/")[0] if "://" in fp_scope else None
958
- footprint_consent_url = f"{base_consent}?client_id={fp_client_id}" if fp_client_id else None
966
+ redirect_uri = settings.azure_consent_redirect_uri
967
+ # Traceability rather than CSRF: nothing in the CLI receives the redirect, so
968
+ # there is no state to validate against. It lets staff correlate an admin's
969
+ # "I approved it" with the request they sent.
970
+ state = f"dxs-{secrets.token_urlsafe(12)}"
971
+
972
+ # Only the CLI app needs per-tenant admin consent. The Footprint API app is
973
+ # consented during customer onboarding in every case, and being a resource app
974
+ # it has no reply address — its consent URL could only ever fail with
975
+ # AADSTS500113 before reaching a consent screen.
976
+ query = urlencode(
977
+ {
978
+ "client_id": settings.azure_client_id,
979
+ "redirect_uri": redirect_uri,
980
+ "state": state,
981
+ }
982
+ )
983
+ consent_url = f"https://login.microsoftonline.com/{resolved_tenant_id}/adminconsent?{query}"
959
984
 
960
985
  consent_apps = [
961
986
  {
962
- "name": "Datex Studio API",
987
+ "name": "Datex Studio CLI",
963
988
  "client_id": settings.azure_client_id,
964
- "url": datex_consent_url,
989
+ "url": consent_url,
965
990
  },
966
991
  ]
967
- if footprint_consent_url and fp_client_id:
968
- consent_apps.append(
969
- {"name": "Footprint API", "client_id": fp_client_id, "url": footprint_consent_url}
970
- )
971
992
 
972
993
  if open:
973
994
  ctx.output(
974
995
  single(
975
996
  item={
976
997
  "status": "opening_browser",
977
- "message": f"Opening admin consent pages for {org_name}",
998
+ "message": f"Opening the admin consent page for {org_name}",
978
999
  "organization": org_name,
979
1000
  "organization_id": org_id,
980
1001
  "tenant_id": resolved_tenant_id,
981
1002
  "domain": resolved_domain,
1003
+ "redirect_uri": redirect_uri,
1004
+ "state": state,
982
1005
  "consent_apps": consent_apps,
983
1006
  "instructions": (
984
- "A tenant admin must complete the consent flow for BOTH apps. "
985
- "Two browser tabs will open -- approve each one."
1007
+ "A tenant admin must complete the consent flow. A browser tab "
1008
+ "will open -- approve it."
986
1009
  ),
987
1010
  },
988
1011
  semantic_key="consent",
@@ -995,15 +1018,18 @@ def consent(
995
1018
  single(
996
1019
  item={
997
1020
  "status": "url_generated",
998
- "message": f"Admin consent URLs for {org_name}",
1021
+ "message": f"Admin consent URL for {org_name}",
999
1022
  "organization": org_name,
1000
1023
  "organization_id": org_id,
1001
1024
  "tenant_id": resolved_tenant_id,
1002
1025
  "domain": resolved_domain,
1026
+ "redirect_uri": redirect_uri,
1027
+ "state": state,
1003
1028
  "consent_apps": consent_apps,
1004
1029
  "instructions": (
1005
- "Have a tenant admin open BOTH URLs to grant consent. "
1006
- "Both apps must be approved for full CLI functionality."
1030
+ "Have a tenant admin open this URL and approve it. "
1031
+ "After approving, the browser lands on a Microsoft page carrying "
1032
+ "this state value -- that is success, not an error."
1007
1033
  ),
1008
1034
  },
1009
1035
  semantic_key="consent",
@@ -1268,14 +1294,14 @@ def _switch_by_organization(
1268
1294
  )
1269
1295
  raise SystemExit(1)
1270
1296
 
1271
- # Step 3: Check if external authentication is required
1272
- if not org_info.external_entra_id:
1273
- # No external auth needed - provide informative message
1297
+ # Step 3: Check whether this org lives in a tenant of its own
1298
+ if not org_needs_tenant_login(org_info):
1299
+ # Home-tenant org (Datex): the current identity already covers it.
1274
1300
  ctx.output(
1275
1301
  single(
1276
1302
  item={
1277
1303
  "status": "no_action_required",
1278
- "message": f"Organization '{org_name}' does not require external authentication",
1304
+ "message": f"Organization '{org_name}' authenticates against the Datex home tenant",
1279
1305
  "organization": org_info.name,
1280
1306
  "organization_id": org_info.id,
1281
1307
  "suggestion": "Your current identity should work with this organization",
@@ -1285,7 +1311,7 @@ def _switch_by_organization(
1285
1311
  )
1286
1312
  return
1287
1313
 
1288
- # Step 4: External auth required - check if we have identity for that tenant
1314
+ # Step 4: Tenant login required - check if we have identity for that tenant
1289
1315
  if org_info.tenant_id:
1290
1316
  existing_by_tenant = cache.get_identity_by_tenant(org_info.tenant_id)
1291
1317
  if existing_by_tenant and existing_by_tenant.account_username:
@@ -1457,6 +1483,7 @@ def _authenticate_external_tenant(
1457
1483
  on_additional_consent_needed=on_additional_consent_needed,
1458
1484
  tenant_id=org_info.tenant_id,
1459
1485
  domain=org_info.external_entra_id_domain_name,
1486
+ external_entra_id=org_info.external_entra_id,
1460
1487
  organization_id=org_info.id,
1461
1488
  organization_name=org_info.name,
1462
1489
  )
@@ -1553,13 +1580,13 @@ def _login_to_organization(
1553
1580
  )
1554
1581
  raise SystemExit(1)
1555
1582
 
1556
- # Check if external authentication is required
1557
- if not org_info.external_entra_id:
1583
+ # Check whether this org lives in a tenant of its own
1584
+ if not org_needs_tenant_login(org_info):
1558
1585
  ctx.output(
1559
1586
  single(
1560
1587
  item={
1561
1588
  "status": "no_external_auth",
1562
- "message": f"Organization '{org_name}' does not use external authentication",
1589
+ "message": f"Organization '{org_name}' authenticates against the Datex home tenant",
1563
1590
  "organization": org_info.name,
1564
1591
  "organization_id": org_info.id,
1565
1592
  "suggestion": "Your current identity can access this organization directly",
@@ -1569,7 +1596,7 @@ def _login_to_organization(
1569
1596
  )
1570
1597
  return
1571
1598
 
1572
- # External auth required - authenticate against external tenant
1599
+ # Tenant login required - authenticate against the org's own tenant
1573
1600
  if not org_info.tenant_id:
1574
1601
  ctx.output_error(
1575
1602
  AuthenticationError(