amsdal_cli 0.9.5__tar.gz → 0.9.7__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 (261) hide show
  1. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/PKG-INFO +1 -1
  2. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/change-logs.md +39 -0
  3. amsdal_cli-0.9.7/latest-changelogs.md +10 -0
  4. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/__about__.py +1 -1
  5. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/app.py +2 -2
  6. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/callbacks.py +2 -1
  7. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/sync_db.py +2 -2
  8. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/tunnel_db.py +59 -2
  9. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/config/main.py +31 -34
  10. amsdal_cli-0.9.7/src/amsdal_cli/utils/alias_group.py +111 -0
  11. amsdal_cli-0.9.7/src/amsdal_cli/utils/check_versions.py +109 -0
  12. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/__init__.py +4 -1
  13. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/uv.lock +131 -108
  14. amsdal_cli-0.9.5/latest-changelogs.md +0 -12
  15. amsdal_cli-0.9.5/src/amsdal_cli/utils/alias_group.py +0 -33
  16. amsdal_cli-0.9.5/src/amsdal_cli/utils/check_versions.py +0 -50
  17. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.editorconfig +0 -0
  18. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.gitignore +0 -0
  19. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.junie/guidelines.md +0 -0
  20. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/CONTRIBUTING.md +0 -0
  21. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/LICENSE.txt +0 -0
  22. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/README.md +0 -0
  23. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/license_check.py +0 -0
  24. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/pyproject.toml +0 -0
  25. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/release_notes/.gitkeep +0 -0
  26. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/scripts/release.sh +0 -0
  27. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/scripts/tag_check.sh +0 -0
  28. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/Third-Party Materials - AMSDAL Dependencies - License Notices.md +0 -0
  29. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/__init__.py +0 -0
  30. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/__init__.py +0 -0
  31. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/__init__.py +0 -0
  32. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/command.py +0 -0
  33. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/config.py +0 -0
  34. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/data_classes.py +0 -0
  35. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/operation_log.py +0 -0
  36. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/__init__.py +0 -0
  37. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/comparison.py +0 -0
  38. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/data_factory.py +0 -0
  39. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/loader.py +0 -0
  40. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/runner.py +0 -0
  41. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/storage.py +0 -0
  42. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/__init__.py +0 -0
  43. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/command.py +0 -0
  44. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/constants.py +0 -0
  45. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/__init__.py +0 -0
  46. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/__init__.py +0 -0
  47. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/custom_code.py +0 -0
  48. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/options.py +0 -0
  49. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/schemas_directory.py +0 -0
  50. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/__init__.py +0 -0
  51. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/custom_code_extender.py +0 -0
  52. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/options_extender.py +0 -0
  53. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/__init__.py +0 -0
  54. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/base.py +0 -0
  55. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_custom_code_loader.py +0 -0
  56. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_loader.py +0 -0
  57. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_options_loader.py +0 -0
  58. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_statics_loader.py +0 -0
  59. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_transactions_loader.py +0 -0
  60. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/utils.py +0 -0
  61. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/mixins/__init__.py +0 -0
  62. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/mixins/enrich_schemas_mixin.py +0 -0
  63. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/schema_json_loader.py +0 -0
  64. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/utils/__init__.py +0 -0
  65. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/utils/merger.py +0 -0
  66. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/__init__.py +0 -0
  67. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/builder.py +0 -0
  68. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/mixin.py +0 -0
  69. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/utils/__init__.py +0 -0
  70. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/utils/build_config_file.py +0 -0
  71. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/__init__.py +0 -0
  72. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/command.py +0 -0
  73. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/constants.py +0 -0
  74. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/templates/github.yml +0 -0
  75. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/clean/__init__.py +0 -0
  76. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/clean/command.py +0 -0
  77. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/__init__.py +0 -0
  78. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/app.py +0 -0
  79. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/command.py +0 -0
  80. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/__init__.py +0 -0
  81. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/app.py +0 -0
  82. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/command.py +0 -0
  83. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/__init__.py +0 -0
  84. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_delete.py +0 -0
  85. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_list.py +0 -0
  86. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_new.py +0 -0
  87. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/__init__.py +0 -0
  88. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/app.py +0 -0
  89. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/command.py +0 -0
  90. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/__init__.py +0 -0
  91. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_delete.py +0 -0
  92. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_list.py +0 -0
  93. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_new.py +0 -0
  94. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/enums.py +0 -0
  95. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/__init__.py +0 -0
  96. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/app.py +0 -0
  97. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/command.py +0 -0
  98. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/constants.py +0 -0
  99. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/__init__.py +0 -0
  100. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_checkout.py +0 -0
  101. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_delete.py +0 -0
  102. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_list.py +0 -0
  103. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_new.py +0 -0
  104. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/utils.py +0 -0
  105. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/__init__.py +0 -0
  106. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/app.py +0 -0
  107. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/command.py +0 -0
  108. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/__init__.py +0 -0
  109. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_add.py +0 -0
  110. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_list.py +0 -0
  111. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_remove.py +0 -0
  112. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_update.py +0 -0
  113. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/__init__.py +0 -0
  114. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/app.py +0 -0
  115. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/command.py +0 -0
  116. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/constants.py +0 -0
  117. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/__init__.py +0 -0
  118. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_delete.py +0 -0
  119. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_list.py +0 -0
  120. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_new.py +0 -0
  121. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/__init__.py +0 -0
  122. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/__init__.py +0 -0
  123. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/app.py +0 -0
  124. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/command.py +0 -0
  125. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/__init__.py +0 -0
  126. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/allowlist_delete.py +0 -0
  127. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/allowlist_new.py +0 -0
  128. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/app.py +0 -0
  129. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/__init__.py +0 -0
  130. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/app.py +0 -0
  131. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/command.py +0 -0
  132. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/__init__.py +0 -0
  133. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_delete.py +0 -0
  134. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_new.py +0 -0
  135. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_retrieve.py +0 -0
  136. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/command.py +0 -0
  137. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/__init__.py +0 -0
  138. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/expose_db.py +0 -0
  139. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/gen_token.py +0 -0
  140. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/get_monitoring_info.py +0 -0
  141. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/gen_token/__init__.py +0 -0
  142. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/gen_token/command.py +0 -0
  143. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/__init__.py +0 -0
  144. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/app.py +0 -0
  145. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/command.py +0 -0
  146. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/enums.py +0 -0
  147. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/__init__.py +0 -0
  148. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_external_models.py +0 -0
  149. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_frontend_config.py +0 -0
  150. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_hook.py +0 -0
  151. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_model.py +0 -0
  152. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_modifier.py +0 -0
  153. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_permission.py +0 -0
  154. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_property.py +0 -0
  155. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_tests.py +0 -0
  156. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_transaction.py +0 -0
  157. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/async_transaction.pyt +0 -0
  158. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/hook.pyt +0 -0
  159. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/constructor.pyt +0 -0
  160. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/display_name.pyt +0 -0
  161. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/version_name.pyt +0 -0
  162. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/property.pyt +0 -0
  163. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/transaction.pyt +0 -0
  164. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/__init__.py +0 -0
  165. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/build_base_path.py +0 -0
  166. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/cast_to_attribute_type.py +0 -0
  167. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/model_attributes.py +0 -0
  168. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/__init__.py +0 -0
  169. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/async_mode_utils.py +0 -0
  170. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/conftest_utils.py +0 -0
  171. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/function_utils.py +0 -0
  172. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/model_utils.py +0 -0
  173. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/templates/async/conftest.py +0 -0
  174. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/templates/sync/conftest.py +0 -0
  175. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/type_utils.py +0 -0
  176. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/unit.py +0 -0
  177. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/__init__.py +0 -0
  178. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/app.py +0 -0
  179. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/command.py +0 -0
  180. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/constants.py +0 -0
  181. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/__init__.py +0 -0
  182. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/apply.py +0 -0
  183. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/list.py +0 -0
  184. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/make.py +0 -0
  185. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/make_contrib.py +0 -0
  186. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/utils.py +0 -0
  187. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/__init__.py +0 -0
  188. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/command.py +0 -0
  189. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.dependencies +0 -0
  190. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.environment +0 -0
  191. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.secrets +0 -0
  192. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal-cli +0 -0
  193. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.env.template +0 -0
  194. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.gitignore +0 -0
  195. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/README.md +0 -0
  196. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/config.yml +0 -0
  197. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/requirements.txt +0 -0
  198. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/src/app.pyt +0 -0
  199. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/__init__.py +0 -0
  200. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/command.py +0 -0
  201. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/.amsdal-cli +0 -0
  202. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/README.md +0 -0
  203. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/config.yml +0 -0
  204. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/pyproject.toml +0 -0
  205. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/__about__.py +0 -0
  206. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/__init__.py +0 -0
  207. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/models/__init__.py +0 -0
  208. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/transactions/__init__.py +0 -0
  209. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/transactions/example_transaction.py +0 -0
  210. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/__init__.py +0 -0
  211. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/command.py +0 -0
  212. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/__init__.py +0 -0
  213. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/build.py +0 -0
  214. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/config_updater.py +0 -0
  215. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/credentials.py +0 -0
  216. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/initialize.py +0 -0
  217. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/meta.py +0 -0
  218. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/migrate_models.py +0 -0
  219. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/model_generator.py +0 -0
  220. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/tables.py +0 -0
  221. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/__init__.py +0 -0
  222. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/command.py +0 -0
  223. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/enums.py +0 -0
  224. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/__init__.py +0 -0
  225. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/command.py +0 -0
  226. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/__init__.py +0 -0
  227. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/models_watch_filter.py +0 -0
  228. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/static_files_watch_filter.py +0 -0
  229. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/services/__init__.py +0 -0
  230. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/services/supervisor.py +0 -0
  231. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/utils.py +0 -0
  232. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/tests/__init__.py +0 -0
  233. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/tests/command.py +0 -0
  234. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/__init__.py +0 -0
  235. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/command.py +0 -0
  236. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/models.py +0 -0
  237. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/__init__.py +0 -0
  238. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/verify_json_model.py +0 -0
  239. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/verify_python_file.py +0 -0
  240. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/__init__.py +0 -0
  241. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/app.py +0 -0
  242. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/command.py +0 -0
  243. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/sub_commands/__init__.py +0 -0
  244. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/sub_commands/run.py +0 -0
  245. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/config/__init__.py +0 -0
  246. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/main.py +0 -0
  247. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/py.typed +0 -0
  248. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/services/__init__.py +0 -0
  249. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/services/gen_token.py +0 -0
  250. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/__init__.py +0 -0
  251. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/auth.py +0 -0
  252. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/cli_config.py +0 -0
  253. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/copier.py +0 -0
  254. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/markdown_patch.py +0 -0
  255. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/render_template.py +0 -0
  256. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/schema_repository.py +0 -0
  257. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/text.py +0 -0
  258. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/base.py +0 -0
  259. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/dummy.py +0 -0
  260. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/enums.py +0 -0
  261. {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/git.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amsdal_cli
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: CLI for AMSDAL framework
5
5
  Project-URL: Documentation, https://pypi.org/project/amsdal_cli/#readme
6
6
  Project-URL: Issues, https://pypi.org/project/amsdal_cli/
@@ -1,3 +1,42 @@
1
+ ## [v0.9.7](https://pypi.org/project/amsdal_cli/0.9.7/) - 2026-06-23
2
+
3
+
4
+ ### Fixed
5
+
6
+ - `cloud tunnel-db` now fails fast with a clear, actionable error when the local
7
+ port is already in use, instead of doing all the build/auth/remote work first
8
+ and then dying with a cryptic "might be in use" message from sshtunnel. The
9
+ preflight check runs before anything else and suggests picking another port
10
+ with `--local-port <PORT>`.
11
+
12
+ ## [v0.9.6](https://pypi.org/project/amsdal_cli/0.9.6/) - 2026-06-23
13
+
14
+
15
+ ### Changed
16
+
17
+ - CLI start-up is significantly faster. Command modules are now imported lazily,
18
+ so running a single command no longer imports every command tree (and their
19
+ heavy dependencies such as pandas and the AMSDAL manager) on each invocation —
20
+ only the module for the invoked command is loaded.
21
+ - The latest-version check against PyPI is now cached on disk for 20 minutes, so
22
+ most commands no longer make blocking network requests on start-up. The check
23
+ also applies a request timeout and no longer lets a network error (e.g. an
24
+ unreachable PyPI) crash the CLI.
25
+ - GitPython is now imported only for projects that use git version control, and
26
+ the `cloud sync-db` database drivers are imported lazily, further reducing
27
+ start-up overhead.
28
+
29
+ ### Fixed
30
+
31
+ - `cloud tunnel-db` now closes promptly instead of hanging until the database
32
+ client disconnects: the SSH transport is force-closed on stop so connection
33
+ handler threads exit immediately.
34
+ - `cloud tunnel-db` now connects to the bastion on the correct SSH port and uses
35
+ a longer SSH handshake timeout with a keep-alive so idle tunnels are not
36
+ dropped. It runs a fast reachability pre-check that reports a clear, actionable
37
+ error instead of hanging for ~2 minutes when the bastion cannot be reached, and
38
+ verifies the tunnel is actually up before reporting success.
39
+
1
40
  ## [v0.9.5](https://pypi.org/project/amsdal_cli/0.9.5/) - 2026-06-17
2
41
 
3
42
 
@@ -0,0 +1,10 @@
1
+ ## [v0.9.7](https://pypi.org/project/amsdal_cli/0.9.7/) - 2026-06-23
2
+
3
+
4
+ ### Fixed
5
+
6
+ - `cloud tunnel-db` now fails fast with a clear, actionable error when the local
7
+ port is already in use, instead of doing all the build/auth/remote work first
8
+ and then dying with a cryptic "might be in use" message from sshtunnel. The
9
+ preflight check runs before anything else and suggests picking another port
10
+ with `--local-port <PORT>`.
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2023-present
2
2
  #
3
3
  # SPDX-License-Identifier: AMSDAL End User License Agreement
4
- __version__ = '0.9.5'
4
+ __version__ = '0.9.7'
@@ -2,7 +2,7 @@ import typer
2
2
  import typer.rich_utils
3
3
 
4
4
  from amsdal_cli.commands.callbacks import init_app_context
5
- from amsdal_cli.utils.alias_group import AliasGroup
5
+ from amsdal_cli.utils.alias_group import LazyAliasGroup
6
6
  from amsdal_cli.utils.markdown_patch import get_custom_help_text
7
7
 
8
8
  # patch typer to use custom help text
@@ -13,5 +13,5 @@ app = typer.Typer(
13
13
  callback=init_app_context,
14
14
  invoke_without_command=True,
15
15
  rich_markup_mode='markdown',
16
- cls=AliasGroup,
16
+ cls=LazyAliasGroup,
17
17
  )
@@ -4,7 +4,6 @@ from json import JSONDecodeError
4
4
  from pathlib import Path
5
5
 
6
6
  import typer
7
- from amsdal_utils.config.manager import AmsdalConfigManager
8
7
  from rich import print as rprint
9
8
  from typer import Option
10
9
 
@@ -33,6 +32,8 @@ def init_app_context(
33
32
  Returns:
34
33
  None
35
34
  """
35
+ from amsdal_utils.config.manager import AmsdalConfigManager
36
+
36
37
  from amsdal_cli.commands.cloud.environments.utils import get_current_env
37
38
  from amsdal_cli.config.main import settings
38
39
  from amsdal_cli.utils.check_versions import check_latest_amsdal_version
@@ -4,10 +4,8 @@ import typing
4
4
  from pathlib import Path
5
5
  from typing import Union
6
6
 
7
- import amsdal_glue as glue
8
7
  import typer
9
8
  import yaml
10
- from amsdal_data.connections.db_alias_map import CONNECTION_BACKEND_ALIASES
11
9
  from rich import print as rprint
12
10
  from typer import Option
13
11
 
@@ -213,6 +211,8 @@ def _build_config(secrets: dict[str, str], db_type: DBType) -> Path:
213
211
 
214
212
 
215
213
  def _copy_data(config: 'AmsdalConfig', connection_names: list[tuple[str, str]]) -> None:
214
+ import amsdal_glue as glue
215
+ from amsdal_data.connections.db_alias_map import CONNECTION_BACKEND_ALIASES
216
216
  from amsdal_utils.utils.classes import import_class
217
217
 
218
218
  for from_name, to_name in connection_names:
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import socket
2
3
  import tempfile
3
4
  import typing
4
5
  from pathlib import Path
@@ -10,6 +11,19 @@ from typer import Option
10
11
  from amsdal_cli.commands.cloud.app import cloud_sub_app
11
12
  from amsdal_cli.utils.auth import ensure_authenticated
12
13
 
14
+ # The cloud bastion exposes SSH on this port (not the default 22).
15
+ BASTION_SSH_PORT = 5444
16
+
17
+
18
+ def _is_local_port_available(port: int) -> bool:
19
+ """Return True if the tunnel can bind 127.0.0.1:<port>, False if it is taken."""
20
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
21
+ try:
22
+ sock.bind(('127.0.0.1', port))
23
+ except OSError:
24
+ return False
25
+ return True
26
+
13
27
 
14
28
  def _generate_temp_ssh_key() -> tuple[bytes, bytes]:
15
29
  """Generate an ephemeral RSA key pair in OpenSSH format."""
@@ -72,6 +86,18 @@ def tunnel_db_command(
72
86
  if cli_config.verbose:
73
87
  rprint(rich_info(f'Opening database tunnel for environment: {rich_highlight(env_name)}'))
74
88
 
89
+ # Preflight: bail before any build/auth/remote work if the local port is taken,
90
+ # otherwise sshtunnel fails later with a cryptic "might be in use" message.
91
+ if not _is_local_port_available(local_port):
92
+ rprint(
93
+ rich_error(
94
+ f'Local port {rich_highlight(str(local_port))} is already in use. '
95
+ f'Close whatever is using it, or pick another port with '
96
+ f'{rich_highlight("--local-port <PORT>")}.'
97
+ )
98
+ )
99
+ return
100
+
75
101
  with tempfile.TemporaryDirectory() as _temp_dir:
76
102
  output_path: Path = Path(_temp_dir)
77
103
 
@@ -106,16 +132,44 @@ def tunnel_db_command(
106
132
 
107
133
  tunnel: SSHTunnelForwarder | None = None
108
134
 
135
+ # sshtunnel's module-default SSH_TIMEOUT is 0.1s, far too short to complete
136
+ # an SSH handshake to a remote bastion, so start() can fail to connect.
137
+ import sshtunnel
138
+
139
+ sshtunnel.SSH_TIMEOUT = 10.0
140
+
141
+ # Preflight: paramiko opens the bastion socket with no connect timeout, so an
142
+ # unreachable bastion port hangs for the full OS TCP timeout (~2 min). Fail
143
+ # fast with a clear, actionable message instead.
144
+ try:
145
+ with socket.create_connection((details['bastion_host'], BASTION_SSH_PORT), timeout=10):
146
+ pass
147
+ except OSError as e:
148
+ rprint(
149
+ rich_error(
150
+ f'Cannot reach bastion {details["bastion_host"]}:{BASTION_SSH_PORT} ({e}). '
151
+ f'Check that your IP is allowed on the bastion security group and that '
152
+ f'outbound port {BASTION_SSH_PORT} is not blocked by your network/VPN.'
153
+ )
154
+ )
155
+ os.unlink(key_path)
156
+ return
157
+
109
158
  try:
110
159
  tunnel = SSHTunnelForwarder(
111
- (details['bastion_host'], 22),
160
+ (details['bastion_host'], BASTION_SSH_PORT),
112
161
  ssh_username=details['ssh_user'],
113
162
  ssh_pkey=key_path,
114
163
  remote_bind_address=(details['rds_host'], int(details['rds_port'] or db_port)),
115
164
  local_bind_address=('127.0.0.1', local_port),
165
+ set_keepalive=30.0,
116
166
  )
117
167
  tunnel.start()
118
168
 
169
+ if not tunnel.is_active or not all(tunnel.tunnel_is_up.values()):
170
+ rprint(rich_error('Failed to establish the database tunnel.'))
171
+ return
172
+
119
173
  rprint(
120
174
  rich_success(
121
175
  f'Tunnel open. Connect your SQL client to {rich_highlight(f"127.0.0.1:{local_port}")}',
@@ -128,6 +182,9 @@ def tunnel_db_command(
128
182
  rprint(rich_error(f'Failed to open tunnel: {e}'))
129
183
  finally:
130
184
  if tunnel is not None:
131
- tunnel.stop()
185
+ # force=True closes the SSH transport first so active connection
186
+ # handler threads exit immediately; without it stop() blocks
187
+ # joining those threads until the SQL client disconnects.
188
+ tunnel.stop(force=True)
132
189
  os.unlink(key_path)
133
190
  rprint(rich_info('Tunnel closed.'))
@@ -1,14 +1,38 @@
1
- import importlib
2
- from types import ModuleType
3
1
  from typing import TYPE_CHECKING
4
2
  from typing import Any
5
3
  from typing import TypeAlias
6
4
 
7
5
  from pydantic import Field
8
- from pydantic import field_validator
9
6
  from pydantic_settings import BaseSettings
10
7
  from pydantic_settings import SettingsConfigDict
11
8
 
9
+ # Maps each top-level command name (including its comma-separated aliases, exactly
10
+ # as registered via ``@app.command`` / ``app.add_typer``) to the module whose
11
+ # ``.command`` submodule registers it on the root Typer ``app``.
12
+ #
13
+ # This is the single source of truth for lazy command loading: the root group
14
+ # (``LazyAliasGroup``) imports a module only when its command is actually invoked,
15
+ # instead of importing all 16 command trees on every CLI start. See
16
+ # ``amsdal_cli/utils/alias_group.py``.
17
+ COMMAND_REGISTRY: dict[str, str] = {
18
+ 'api-check': 'amsdal_cli.commands.api_check',
19
+ 'new, n': 'amsdal_cli.commands.new',
20
+ 'plugin': 'amsdal_cli.commands.plugin',
21
+ 'generate, gen, g': 'amsdal_cli.commands.generate',
22
+ 'verify, vrf, v': 'amsdal_cli.commands.verify',
23
+ 'build, bld, b': 'amsdal_cli.commands.build',
24
+ 'clean, cln': 'amsdal_cli.commands.clean',
25
+ 'serve, srv, s': 'amsdal_cli.commands.serve',
26
+ 'restore, rst': 'amsdal_cli.commands.restore',
27
+ 'cloud, cld': 'amsdal_cli.commands.cloud',
28
+ 'migrations, migs, mgs': 'amsdal_cli.commands.migrations',
29
+ 'ci-cd': 'amsdal_cli.commands.ci_cd',
30
+ 'tests, test': 'amsdal_cli.commands.tests',
31
+ 'worker, w': 'amsdal_cli.commands.worker',
32
+ 'register-connection, reg-conn': 'amsdal_cli.commands.register_connection',
33
+ 'gen-token, gen_token, gt': 'amsdal_cli.commands.gen_token',
34
+ }
35
+
12
36
 
13
37
  class Settings(BaseSettings):
14
38
  model_config = SettingsConfigDict(
@@ -18,43 +42,16 @@ class Settings(BaseSettings):
18
42
  extra='ignore',
19
43
  )
20
44
 
21
- COMMANDS: list[str | ModuleType] = Field(
22
- default=[
23
- 'amsdal_cli.commands.api_check',
24
- 'amsdal_cli.commands.new',
25
- 'amsdal_cli.commands.plugin',
26
- 'amsdal_cli.commands.generate',
27
- 'amsdal_cli.commands.verify',
28
- 'amsdal_cli.commands.build',
29
- 'amsdal_cli.commands.clean',
30
- 'amsdal_cli.commands.serve',
31
- 'amsdal_cli.commands.restore',
32
- 'amsdal_cli.commands.cloud',
33
- 'amsdal_cli.commands.migrations',
34
- 'amsdal_cli.commands.ci_cd',
35
- 'amsdal_cli.commands.tests',
36
- 'amsdal_cli.commands.worker',
37
- 'amsdal_cli.commands.register_connection',
38
- 'amsdal_cli.commands.gen_token',
39
- ]
40
- )
45
+ # Module paths of the registered commands. Kept for backwards compatibility;
46
+ # commands are now loaded lazily through COMMAND_REGISTRY, so importing this
47
+ # settings object no longer imports any command module.
48
+ COMMANDS: list[str] = Field(default=list(COMMAND_REGISTRY.values()))
41
49
 
42
50
  CHECK_AMSDAL_VERSIONS: bool = True
43
51
  """If True, the latest version of the amsdal modules will be checked and displayed."""
44
52
  AMSDAL_APPLICATION_UUID: str | None = None
45
53
  """The UUID of the application."""
46
54
 
47
- @field_validator('COMMANDS')
48
- @classmethod
49
- def load_commands(cls, value: list[Any]) -> list[ModuleType]:
50
- commands: list[ModuleType] = []
51
-
52
- for module in value:
53
- if isinstance(module, str):
54
- commands.append(importlib.import_module(f'{module}.command'))
55
-
56
- return commands
57
-
58
55
 
59
56
  if TYPE_CHECKING:
60
57
  base: TypeAlias = Settings
@@ -0,0 +1,111 @@
1
+ import importlib
2
+ import re
3
+ from typing import Any
4
+
5
+ from click import Command
6
+ from typer import Context
7
+ from typer.core import TyperGroup
8
+
9
+
10
+ class AliasGroup(TyperGroup):
11
+ """
12
+ A custom TyperGroup that supports aliasing commands.
13
+ """
14
+
15
+ _CMD_SPLIT_P = r'[,| ?\/]'
16
+
17
+ def get_command(self, ctx: Context, cmd_name: str) -> Command | None: # type: ignore[override]
18
+ """
19
+ Get the command associated with the given command name.
20
+
21
+ Args:
22
+ ctx (Context): The Typer context object.
23
+ cmd_name (str): The name of the command to retrieve.
24
+
25
+ Returns:
26
+ Command | None: The command object if found, otherwise None.
27
+ """
28
+ cmd_name = self._group_cmd_name(cmd_name)
29
+ return super().get_command(ctx, cmd_name)
30
+
31
+ def _group_cmd_name(self, default_name: str) -> str:
32
+ for cmd in self.commands.values():
33
+ if cmd.name and default_name in re.split(self._CMD_SPLIT_P, cmd.name):
34
+ return cmd.name
35
+ return default_name
36
+
37
+
38
+ class LazyAliasGroup(AliasGroup):
39
+ """
40
+ Root command group that imports a command's module only when it is invoked.
41
+
42
+ The default flow imported all 16 command trees on every CLI start (dragging in
43
+ pandas, the AMSDAL manager, etc.) just to register them. Instead, this group
44
+ starts empty and, on demand, imports the single module that registers the
45
+ requested command onto the root Typer ``app``, then materialises it.
46
+
47
+ ``--help`` and shell completion still need the full list, so ``list_commands``
48
+ loads everything; a specific command invocation only pays for its own module.
49
+ """
50
+
51
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
52
+ super().__init__(*args, **kwargs)
53
+
54
+ from amsdal_cli.config.main import COMMAND_REGISTRY
55
+
56
+ # token (canonical name or alias) -> module that registers it
57
+ self._token_to_module: dict[str, str] = {}
58
+ for full_name, module in COMMAND_REGISTRY.items():
59
+ for token in re.split(self._CMD_SPLIT_P, full_name):
60
+ if token:
61
+ self._token_to_module[token] = module
62
+
63
+ # ordered, de-duplicated module list for whole-registry loads
64
+ self._all_modules: list[str] = list(dict.fromkeys(COMMAND_REGISTRY.values()))
65
+ self._loaded_modules: set[str] = set()
66
+
67
+ def _sync_from_app(self) -> None:
68
+ """Materialise any newly registered Typer commands/groups into this group."""
69
+ from typer.main import get_command_from_info
70
+ from typer.main import get_group_from_info
71
+
72
+ from amsdal_cli.app import app
73
+
74
+ markup = self.rich_markup_mode
75
+ short = app.pretty_exceptions_short
76
+
77
+ for command_info in app.registered_commands:
78
+ command = get_command_from_info(command_info, pretty_exceptions_short=short, rich_markup_mode=markup)
79
+ if command.name and command.name not in self.commands:
80
+ self.commands[command.name] = command
81
+
82
+ for group_info in app.registered_groups:
83
+ group = get_group_from_info(group_info, pretty_exceptions_short=short, rich_markup_mode=markup)
84
+ if group.name and group.name not in self.commands:
85
+ self.commands[group.name] = group
86
+
87
+ def _load_module(self, module: str) -> None:
88
+ if module in self._loaded_modules:
89
+ return
90
+ importlib.import_module(f'{module}.command')
91
+ self._loaded_modules.add(module)
92
+ self._sync_from_app()
93
+
94
+ def _load_all(self) -> None:
95
+ for module in self._all_modules:
96
+ self._load_module(module)
97
+
98
+ def get_command(self, ctx: Context, cmd_name: str) -> Command | None: # type: ignore[override]
99
+ if self._group_cmd_name(cmd_name) not in self.commands:
100
+ module = self._token_to_module.get(cmd_name)
101
+ if module is not None:
102
+ self._load_module(module)
103
+ else:
104
+ # Unknown token (e.g. a typo): load everything so resolution and
105
+ # the "no such command" suggestions behave exactly as before.
106
+ self._load_all()
107
+ return super().get_command(ctx, cmd_name)
108
+
109
+ def list_commands(self, ctx: Context) -> list[str]: # type: ignore[override]
110
+ self._load_all()
111
+ return super().list_commands(ctx)
@@ -0,0 +1,109 @@
1
+ import importlib
2
+ import json
3
+ import tempfile
4
+ import time
5
+ from pathlib import Path
6
+
7
+ import httpx
8
+ from rich import print as rprint
9
+
10
+ from amsdal_cli.utils.text import rich_command
11
+ from amsdal_cli.utils.text import rich_highlight_version
12
+
13
+ AMSDAL_MODULES = [
14
+ 'amsdal_utils',
15
+ 'amsdal_data',
16
+ 'amsdal_models',
17
+ 'amsdal',
18
+ 'amsdal_server',
19
+ 'amsdal_cli',
20
+ ]
21
+
22
+ # Latest versions on PyPI change rarely, but this check runs on every project
23
+ # command and makes one blocking request per module. Cache the fetched versions
24
+ # on disk for 20 minutes so most invocations skip the network entirely.
25
+ _CACHE_TTL_SECONDS = 20 * 60
26
+ _CACHE_PATH = Path(tempfile.gettempdir()) / 'amsdal_cli_latest_versions.json'
27
+
28
+
29
+ def _read_cached_versions() -> dict[str, str] | None:
30
+ """Return cached ``{module: latest_version}`` if the cache is fresh, else None."""
31
+ try:
32
+ payload = json.loads(_CACHE_PATH.read_text())
33
+ fetched_at = float(payload['fetched_at'])
34
+ versions = payload['versions']
35
+ except (OSError, ValueError, KeyError, TypeError):
36
+ return None
37
+
38
+ if not isinstance(versions, dict) or time.time() - fetched_at > _CACHE_TTL_SECONDS:
39
+ return None
40
+
41
+ return versions
42
+
43
+
44
+ def _write_cached_versions(versions: dict[str, str]) -> None:
45
+ try:
46
+ _CACHE_PATH.write_text(json.dumps({'fetched_at': time.time(), 'versions': versions}))
47
+ except OSError:
48
+ # Caching is best-effort; a failure here must never break the command.
49
+ pass
50
+
51
+
52
+ def _fetch_latest_versions() -> dict[str, str]:
53
+ """Fetch the latest version of each AMSDAL module from PyPI.
54
+
55
+ Modules that cannot be reached (network/HTTP/parse errors) are skipped rather
56
+ than raising, so an offline machine or a PyPI hiccup never breaks the CLI.
57
+ """
58
+ versions: dict[str, str] = {}
59
+
60
+ with httpx.Client(timeout=5.0) as session:
61
+ for amsdal_module in AMSDAL_MODULES:
62
+ try:
63
+ response = session.get(f'https://pypi.org/pypi/{amsdal_module}/json')
64
+ versions[amsdal_module] = response.json()['info']['version']
65
+ except (httpx.HTTPError, KeyError, ValueError):
66
+ continue
67
+
68
+ return versions
69
+
70
+
71
+ def check_latest_amsdal_version() -> None:
72
+ """
73
+ Check the latest version of AMSDAL modules and suggest upgrades if necessary.
74
+
75
+ Returns:
76
+ None
77
+ """
78
+ latest_versions = _read_cached_versions()
79
+ if latest_versions is None:
80
+ latest_versions = _fetch_latest_versions()
81
+ _write_cached_versions(latest_versions)
82
+
83
+ modules_to_update: list[str] = []
84
+
85
+ for amsdal_module in AMSDAL_MODULES:
86
+ latest_version = latest_versions.get(amsdal_module)
87
+ if not latest_version:
88
+ continue
89
+
90
+ try:
91
+ module = importlib.import_module(f'{amsdal_module}.__about__')
92
+ except ImportError:
93
+ continue
94
+
95
+ current_version = module.__version__
96
+
97
+ if current_version == latest_version:
98
+ continue
99
+
100
+ rprint(
101
+ f'You are using {amsdal_module} version {rich_highlight_version(current_version)}, '
102
+ f'however version {rich_highlight_version(latest_version)} is available.'
103
+ )
104
+
105
+ modules_to_update.append(amsdal_module)
106
+
107
+ if modules_to_update:
108
+ upgrade_command = 'pip install --upgrade ' + ' '.join(modules_to_update)
109
+ rprint(f"You should consider upgrading via the '{rich_command(upgrade_command)}' command.")
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
3
3
  from amsdal_cli.utils.vcs.base import VCSBaseService
4
4
  from amsdal_cli.utils.vcs.dummy import DummyVSCService
5
5
  from amsdal_cli.utils.vcs.enums import VCSOptions
6
- from amsdal_cli.utils.vcs.git import GitService
7
6
 
8
7
  if TYPE_CHECKING:
9
8
  from amsdal_cli.utils.cli_config import CliConfig
@@ -11,6 +10,10 @@ if TYPE_CHECKING:
11
10
 
12
11
  def get_vcs_service(config: 'CliConfig') -> VCSBaseService:
13
12
  if config.vcs == VCSOptions.git:
13
+ # Imported lazily so projects that don't use git (the default) never pay
14
+ # the cost of loading GitPython, which runs on every project command.
15
+ from amsdal_cli.utils.vcs.git import GitService
16
+
14
17
  return GitService(config)
15
18
  return DummyVSCService(config)
16
19