quantum-framework 0.11.0__tar.gz → 0.12.0__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 (363) hide show
  1. {quantum_framework-0.11.0/quantum_framework.egg-info → quantum_framework-0.12.0}/PKG-INFO +5 -1
  2. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/pyproject.toml +16 -2
  3. quantum_framework-0.12.0/quantum/cli/admin.py +131 -0
  4. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/runner.py +9 -0
  5. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/ast_nodes.py +1 -0
  6. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expressions.py +12 -12
  7. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parser.py +108 -0
  8. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/invoke_parser.py +4 -1
  9. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/action_handler.py +19 -11
  10. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/component.py +3 -4
  11. quantum_framework-0.12.0/quantum/runtime/component_composer.py +133 -0
  12. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/invoke_executor.py +60 -15
  13. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/renderer.py +20 -21
  14. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/web_server.py +33 -8
  15. quantum_framework-0.12.0/quantum/services.py +109 -0
  16. quantum_framework-0.12.0/quantum_admin/__init__.py +4 -0
  17. quantum_framework-0.12.0/quantum_admin/assets/quantum-admin-connectors.js +123 -0
  18. quantum_framework-0.12.0/quantum_admin/assets/quantum-admin.css +2058 -0
  19. quantum_framework-0.12.0/quantum_admin/assets/quantum-source-highlight.js +106 -0
  20. quantum_framework-0.12.0/quantum_admin/backend/__init__.py +4 -0
  21. quantum_framework-0.12.0/quantum_admin/backend/audit_service.py +346 -0
  22. quantum_framework-0.12.0/quantum_admin/backend/auth_service.py +394 -0
  23. quantum_framework-0.12.0/quantum_admin/backend/component_discovery.py +766 -0
  24. quantum_framework-0.12.0/quantum_admin/backend/config_generator.py +586 -0
  25. quantum_framework-0.12.0/quantum_admin/backend/connector_service.py +1016 -0
  26. quantum_framework-0.12.0/quantum_admin/backend/crud.py +557 -0
  27. quantum_framework-0.12.0/quantum_admin/backend/database.py +226 -0
  28. quantum_framework-0.12.0/quantum_admin/backend/db_setup_service.py +193 -0
  29. quantum_framework-0.12.0/quantum_admin/backend/deploy_service.py +698 -0
  30. quantum_framework-0.12.0/quantum_admin/backend/docker_service.py +422 -0
  31. quantum_framework-0.12.0/quantum_admin/backend/environment_service.py +418 -0
  32. quantum_framework-0.12.0/quantum_admin/backend/git_service.py +370 -0
  33. quantum_framework-0.12.0/quantum_admin/backend/health_service.py +390 -0
  34. quantum_framework-0.12.0/quantum_admin/backend/job_service.py +347 -0
  35. quantum_framework-0.12.0/quantum_admin/backend/log_service.py +331 -0
  36. quantum_framework-0.12.0/quantum_admin/backend/main.py +14426 -0
  37. quantum_framework-0.12.0/quantum_admin/backend/models.py +986 -0
  38. quantum_framework-0.12.0/quantum_admin/backend/pipeline_service.py +812 -0
  39. quantum_framework-0.12.0/quantum_admin/backend/resource_manager.py +1118 -0
  40. quantum_framework-0.12.0/quantum_admin/backend/schemas.py +419 -0
  41. quantum_framework-0.12.0/quantum_admin/backend/secret_manager.py +277 -0
  42. quantum_framework-0.12.0/quantum_admin/backend/settings_service.py +488 -0
  43. quantum_framework-0.12.0/quantum_admin/backend/template_service.py +710 -0
  44. quantum_framework-0.12.0/quantum_admin/backend/test_execution_service.py +364 -0
  45. quantum_framework-0.12.0/quantum_admin/backend/test_generator.py +782 -0
  46. quantum_framework-0.12.0/quantum_admin/backend/webhook_service.py +454 -0
  47. quantum_framework-0.12.0/quantum_admin/backend/websocket_manager.py +360 -0
  48. quantum_framework-0.12.0/quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  49. quantum_framework-0.12.0/quantum_admin/components/admin/agents.q +107 -0
  50. quantum_framework-0.12.0/quantum_admin/components/admin/app/[name].q +558 -0
  51. quantum_framework-0.12.0/quantum_admin/components/admin/applications.q +160 -0
  52. quantum_framework-0.12.0/quantum_admin/components/admin/component/[...path].q +205 -0
  53. quantum_framework-0.12.0/quantum_admin/components/admin/components.q +59 -0
  54. quantum_framework-0.12.0/quantum_admin/components/admin/connectors.q +283 -0
  55. quantum_framework-0.12.0/quantum_admin/components/admin/dashboard.q +86 -0
  56. quantum_framework-0.12.0/quantum_admin/components/admin/database.q +95 -0
  57. quantum_framework-0.12.0/quantum_admin/components/admin/features.q +69 -0
  58. quantum_framework-0.12.0/quantum_admin/components/admin/index.q +12 -0
  59. quantum_framework-0.12.0/quantum_admin/components/admin/jobs.q +75 -0
  60. quantum_framework-0.12.0/quantum_admin/components/admin/login.q +65 -0
  61. quantum_framework-0.12.0/quantum_admin/components/admin/logout.q +11 -0
  62. quantum_framework-0.12.0/quantum_admin/components/admin/settings.q +174 -0
  63. quantum_framework-0.12.0/quantum_admin/components/admin/source.q +45 -0
  64. quantum_framework-0.12.0/quantum_admin/components/admin/tests.q +73 -0
  65. quantum_framework-0.12.0/quantum_admin/run.py +13 -0
  66. quantum_framework-0.12.0/quantum_admin/services/__init__.py +16 -0
  67. quantum_framework-0.12.0/quantum_admin/services/_base.py +84 -0
  68. quantum_framework-0.12.0/quantum_admin/services/_component_test_generator.py +205 -0
  69. quantum_framework-0.12.0/quantum_admin/services/_yaml_editor.py +82 -0
  70. quantum_framework-0.12.0/quantum_admin/services/apps.py +330 -0
  71. quantum_framework-0.12.0/quantum_admin/services/auth.py +61 -0
  72. quantum_framework-0.12.0/quantum_admin/services/components.py +210 -0
  73. quantum_framework-0.12.0/quantum_admin/services/connectors.py +164 -0
  74. quantum_framework-0.12.0/quantum_admin/services/projects.py +189 -0
  75. quantum_framework-0.12.0/quantum_admin/services/repository.py +223 -0
  76. quantum_framework-0.12.0/quantum_admin/services/settings.py +150 -0
  77. quantum_framework-0.12.0/quantum_admin/services/yaml_import.py +118 -0
  78. {quantum_framework-0.11.0 → quantum_framework-0.12.0/quantum_framework.egg-info}/PKG-INFO +5 -1
  79. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/SOURCES.txt +64 -0
  80. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/requires.txt +5 -0
  81. quantum_framework-0.12.0/quantum_framework.egg-info/top_level.txt +2 -0
  82. quantum_framework-0.11.0/quantum/runtime/component_composer.py +0 -320
  83. quantum_framework-0.11.0/quantum_framework.egg-info/top_level.txt +0 -1
  84. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/LICENSE +0 -0
  85. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/README.md +0 -0
  86. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/__init__.py +0 -0
  87. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/__init__.py +0 -0
  88. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/__init__.py +0 -0
  89. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/build.py +0 -0
  90. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/dev.py +0 -0
  91. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/docs.py +0 -0
  92. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/lint.py +0 -0
  93. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/migrate.py +0 -0
  94. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/new.py +0 -0
  95. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/serve.py +0 -0
  96. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/test.py +0 -0
  97. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/deploy.py +0 -0
  98. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/hot_reload.py +0 -0
  99. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/jobs.py +0 -0
  100. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/mq.py +0 -0
  101. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/pkg.py +0 -0
  102. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/server_process.py +0 -0
  103. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/utils.py +0 -0
  104. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/__init__.py +0 -0
  105. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/base_generator.py +0 -0
  106. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/cli.py +0 -0
  107. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/expression_transformer.py +0 -0
  108. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/javascript/__init__.py +0 -0
  109. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/javascript/generator.py +0 -0
  110. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/optimizer.py +0 -0
  111. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/__init__.py +0 -0
  112. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/generator.py +0 -0
  113. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/runtime.py +0 -0
  114. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/transpiler.py +0 -0
  115. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/__init__.py +0 -0
  116. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/config_env.py +0 -0
  117. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expression_diagnostics.py +0 -0
  118. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expression_stdlib.py +0 -0
  119. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/agents/src/__init__.py +0 -0
  120. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/agents/src/ast_node.py +0 -0
  121. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
  122. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
  123. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/__init__.py +0 -0
  124. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
  125. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/runtime.py +0 -0
  126. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/__init__.py +0 -0
  127. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/ast_node.py +0 -0
  128. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/parser.py +0 -0
  129. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/runtime.py +0 -0
  130. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/functions/src/__init__.py +0 -0
  131. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/functions/src/ast_node.py +0 -0
  132. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
  133. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
  134. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
  135. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/__init__.py +0 -0
  136. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
  137. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/runtime.py +0 -0
  138. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
  139. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
  140. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
  141. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/__init__.py +0 -0
  142. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/ast_node.py +0 -0
  143. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/parser.py +0 -0
  144. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/runtime.py +0 -0
  145. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/loops/src/__init__.py +0 -0
  146. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/loops/src/ast_node.py +0 -0
  147. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/__init__.py +0 -0
  148. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/database_service.py +0 -0
  149. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/query_validators.py +0 -0
  150. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/state_management/src/__init__.py +0 -0
  151. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
  152. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
  153. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
  154. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
  155. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
  156. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
  157. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
  158. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/__init__.py +0 -0
  159. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/ast_node.py +0 -0
  160. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/presets.py +0 -0
  161. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
  162. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
  163. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/parser.py +0 -0
  164. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/websocket/src/__init__.py +0 -0
  165. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
  166. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/html_compat.py +0 -0
  167. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parser_registry.py +0 -0
  168. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/__init__.py +0 -0
  169. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/__init__.py +0 -0
  170. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
  171. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
  172. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
  173. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/team_parser.py +0 -0
  174. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/base.py +0 -0
  175. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/__init__.py +0 -0
  176. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/import_parser.py +0 -0
  177. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
  178. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
  179. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
  180. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
  181. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
  182. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/__init__.py +0 -0
  183. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/data_parser.py +0 -0
  184. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/query_parser.py +0 -0
  185. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
  186. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/events/__init__.py +0 -0
  187. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
  188. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/__init__.py +0 -0
  189. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/action_parser.py +0 -0
  190. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
  191. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
  192. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/__init__.py +0 -0
  193. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/function_parser.py +0 -0
  194. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/param_parser.py +0 -0
  195. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/return_parser.py +0 -0
  196. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/__init__.py +0 -0
  197. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
  198. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/html_parser.py +0 -0
  199. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/__init__.py +0 -0
  200. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
  201. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
  202. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
  203. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/__init__.py +0 -0
  204. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
  205. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
  206. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
  207. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
  208. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
  209. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/persistence/__init__.py +0 -0
  210. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/persistence/persist_parser.py +0 -0
  211. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/routing/__init__.py +0 -0
  212. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/routing/route_parser.py +0 -0
  213. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/__init__.py +0 -0
  214. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
  215. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
  216. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
  217. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
  218. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/__init__.py +0 -0
  219. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/dump_parser.py +0 -0
  220. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/file_parser.py +0 -0
  221. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/log_parser.py +0 -0
  222. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/mail_parser.py +0 -0
  223. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/tiers.py +0 -0
  224. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/__init__.py +0 -0
  225. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/manager.py +0 -0
  226. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/manifest.py +0 -0
  227. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/registry.py +0 -0
  228. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/resolver.py +0 -0
  229. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/__init__.py +0 -0
  230. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/hooks.py +0 -0
  231. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/loader.py +0 -0
  232. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/manifest.py +0 -0
  233. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/registry.py +0 -0
  234. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/__init__.py +0 -0
  235. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/__init__.py +0 -0
  236. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
  237. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
  238. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
  239. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
  240. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/agent_service.py +0 -0
  241. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ast_cache.py +0 -0
  242. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/auth_service.py +0 -0
  243. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/component_resolver.py +0 -0
  244. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/database_service.py +0 -0
  245. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/email_service.py +0 -0
  246. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/error_handler.py +0 -0
  247. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/execution_context.py +0 -0
  248. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executor_registry.py +0 -0
  249. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/__init__.py +0 -0
  250. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/__init__.py +0 -0
  251. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
  252. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
  253. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
  254. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
  255. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/base.py +0 -0
  256. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
  257. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
  258. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
  259. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
  260. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/__init__.py +0 -0
  261. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/data_executor.py +0 -0
  262. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/query_executor.py +0 -0
  263. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
  264. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
  265. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
  266. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
  267. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
  268. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
  269. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
  270. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
  271. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
  272. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
  273. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
  274. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
  275. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
  276. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
  277. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/__init__.py +0 -0
  278. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
  279. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/file_executor.py +0 -0
  280. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/log_executor.py +0 -0
  281. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
  282. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/expression_cache.py +0 -0
  283. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/file_upload_service.py +0 -0
  284. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/function_registry.py +0 -0
  285. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_builder.py +0 -0
  286. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_code_generator.py +0 -0
  287. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_templates.py +0 -0
  288. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/godot_code_generator.py +0 -0
  289. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/godot_templates.py +0 -0
  290. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/job_executor.py +0 -0
  291. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/knowledge_service.py +0 -0
  292. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_cache.py +0 -0
  293. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_providers.py +0 -0
  294. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_service.py +0 -0
  295. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/logging_setup.py +0 -0
  296. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/message_broker.py +0 -0
  297. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/message_queue_service.py +0 -0
  298. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/param_validation.py +0 -0
  299. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/pypy_compat.py +0 -0
  300. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/python_bridge.py +0 -0
  301. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/query_validators.py +0 -0
  302. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/service_container.py +0 -0
  303. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_builder.py +0 -0
  304. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_code_generator.py +0 -0
  305. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_templates.py +0 -0
  306. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_builder.py +0 -0
  307. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_code_generator.py +0 -0
  308. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_templates.py +0 -0
  309. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_builder.py +0 -0
  310. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_desktop_adapter.py +0 -0
  311. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_desktop_templates.py +0 -0
  312. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_html_adapter.py +0 -0
  313. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_html_templates.py +0 -0
  314. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
  315. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_mobile_templates.py +0 -0
  316. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_textual_adapter.py +0 -0
  317. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_textual_templates.py +0 -0
  318. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_tokens.py +0 -0
  319. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_validator.py +0 -0
  320. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/validators.py +0 -0
  321. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_adapter.py +0 -0
  322. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_service.py +0 -0
  323. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_transport.py +0 -0
  324. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/wsgi.py +0 -0
  325. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/utils/__init__.py +0 -0
  326. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
  327. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/entry_points.txt +0 -0
  328. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/setup.cfg +0 -0
  329. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_agent.py +0 -0
  330. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_animations.py +0 -0
  331. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ast_cache.py +0 -0
  332. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_cli_commands.py +0 -0
  333. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_component_library.py +0 -0
  334. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_desktop_bridge.py +0 -0
  335. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_expression_cache.py +0 -0
  336. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_form_validation.py +0 -0
  337. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_codegen.py +0 -0
  338. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_e2e.py +0 -0
  339. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_engine.py +0 -0
  340. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_parser.py +0 -0
  341. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_godot_codegen.py +0 -0
  342. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_job_executor.py +0 -0
  343. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_llm_providers.py +0 -0
  344. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_message_broker.py +0 -0
  345. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_message_queue.py +0 -0
  346. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_mobile_target.py +0 -0
  347. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_multi_agent.py +0 -0
  348. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_packages.py +0 -0
  349. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_plugins.py +0 -0
  350. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_projects_blog_components_admin.py +0 -0
  351. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_pypy_compat.py +0 -0
  352. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_python_scripting.py +0 -0
  353. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_state_persistence.py +0 -0
  354. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_terminal_codegen.py +0 -0
  355. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_terminal_parser.py +0 -0
  356. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_testing_codegen.py +0 -0
  357. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_testing_parser.py +0 -0
  358. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_theming.py +0 -0
  359. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_html_adapter.py +0 -0
  360. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_parity.py +0 -0
  361. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_parser.py +0 -0
  362. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_textual_adapter.py +0 -0
  363. {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantum-framework
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Full-stack declarative framework for building web, desktop, and mobile applications
5
5
  Author-email: Daniel Gregorio <danielgregorio2611@gmail.com>
6
6
  License: MIT
@@ -37,6 +37,10 @@ Provides-Extra: rag
37
37
  Requires-Dist: chromadb>=0.5.0; extra == "rag"
38
38
  Provides-Extra: jobs
39
39
  Requires-Dist: apscheduler>=3.10.0; extra == "jobs"
40
+ Provides-Extra: admin
41
+ Requires-Dist: sqlalchemy>=2.0; extra == "admin"
42
+ Requires-Dist: PyJWT>=2.8; extra == "admin"
43
+ Requires-Dist: cryptography>=42.0; extra == "admin"
40
44
  Provides-Extra: websocket
41
45
  Requires-Dist: websockets>=12.0; extra == "websocket"
42
46
  Dynamic: license-file
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantum-framework"
7
- version = "0.11.0"
7
+ version = "0.12.0"
8
8
  description = "Full-stack declarative framework for building web, desktop, and mobile applications"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -50,6 +50,13 @@ jobs = [
50
50
  # q:schedule failed on any clean install.
51
51
  "apscheduler>=3.10.0",
52
52
  ]
53
+ admin = [
54
+ # `quantum admin`: the admin database (SQLAlchemy), its login tokens
55
+ # (PyJWT) and encrypted connector secrets (cryptography).
56
+ "sqlalchemy>=2.0",
57
+ "PyJWT>=2.8",
58
+ "cryptography>=42.0",
59
+ ]
53
60
  websocket = [
54
61
  # The transport behind q:websocket. Without it the tag registers a
55
62
  # connection and opens nothing — and says so, rather than pretending.
@@ -71,7 +78,14 @@ Repository = "https://github.com/danielgregorio/quantum"
71
78
  # do core), e as pastas `quantum-as4`/`quantum-lsp` (projetos irmaos,
72
79
  # hifenados e nem importaveis) — 91 arquivos de peso morto no wheel. O ponto
73
80
  # em `quantum.*` casa subpacotes; sem ele, `quantum_admin` nao entra.
74
- include = ["quantum", "quantum.*"]
81
+ include = ["quantum", "quantum.*",
82
+ # The admin (`quantum admin`, extra [admin]): its screens, its
83
+ # services and the backend library they use. The backend's
84
+ # FastAPI modules ship with it, unused by the screens.
85
+ "quantum_admin", "quantum_admin.services", "quantum_admin.backend"]
86
+
87
+ [tool.setuptools.package-data]
88
+ quantum_admin = ["components/**/*.q", "assets/*"]
75
89
 
76
90
 
77
91
  [tool.black]
@@ -0,0 +1,131 @@
1
+ """`quantum admin` — serve the Quantum Admin (quantum_admin/components/admin).
2
+
3
+ The admin is a Quantum app: .q screens over declared services. This command
4
+ writes its configuration into a data folder and starts the same server as
5
+ `quantum start`, so nothing has to be set up by hand.
6
+
7
+ quantum admin # data in ./.quantum-admin, projects under ./
8
+ quantum admin --data quantum_admin # the repository's own admin data
9
+
10
+ The data folder holds the admin database, its settings (connectors, global
11
+ settings, process PIDs) and the generated config. Paths of projects are
12
+ relative to the folder the command runs in (--root to change it).
13
+ """
14
+
15
+ import os
16
+ import secrets
17
+ from pathlib import Path
18
+
19
+ MODULES = [
20
+ "quantum_admin.services.auth",
21
+ "quantum_admin.services.projects",
22
+ "quantum_admin.services.yaml_import",
23
+ "quantum_admin.services.connectors",
24
+ "quantum_admin.services.settings",
25
+ "quantum_admin.services.components",
26
+ "quantum_admin.services.apps",
27
+ "quantum_admin.services.repository",
28
+ ]
29
+
30
+ INSTALL_HINT = 'pip install "quantum-framework[admin]"'
31
+
32
+
33
+ def create_admin_parser(subparsers):
34
+ parser = subparsers.add_parser('admin', help='Start the Quantum Admin')
35
+ parser.add_argument('--port', type=int, default=8090, help='Port (default 8090)')
36
+ parser.add_argument('--data', default='.quantum-admin',
37
+ help='Folder for the admin database and settings (default ./.quantum-admin)')
38
+ parser.add_argument('--root', default='.',
39
+ help='Folder the project paths are relative to (default: current folder)')
40
+ return parser
41
+
42
+
43
+ def missing_dependencies():
44
+ """Names of the [admin] extra's packages that do not import."""
45
+ faltando = []
46
+ for modulo, pacote in (("sqlalchemy", "SQLAlchemy"), ("jwt", "PyJWT"),
47
+ ("cryptography", "cryptography")):
48
+ try:
49
+ __import__(modulo)
50
+ except ImportError:
51
+ faltando.append(pacote)
52
+ return faltando
53
+
54
+
55
+ def _package() -> Path:
56
+ import quantum_admin
57
+ return Path(quantum_admin.__file__).resolve().parent
58
+
59
+
60
+ def screens_folder() -> Path:
61
+ return _package() / "components"
62
+
63
+
64
+ def assets_folder() -> Path:
65
+ """The screens' CSS and JS, served under /static/."""
66
+ return _package() / "assets"
67
+
68
+
69
+ def _secret(arquivo: Path) -> str:
70
+ """A key kept in the data folder, so sessions survive a restart."""
71
+ if arquivo.exists():
72
+ return arquivo.read_text(encoding="utf-8").strip()
73
+ chave = secrets.token_hex(32)
74
+ arquivo.write_text(chave, encoding="utf-8")
75
+ try:
76
+ arquivo.chmod(0o600)
77
+ except OSError:
78
+ pass
79
+ return chave
80
+
81
+
82
+ def prepare(data: str, root: str, port: int) -> Path:
83
+ """Create the data folder, point the admin at it, write the config. Returns its path."""
84
+ import yaml
85
+
86
+ pasta = Path(data).resolve()
87
+ (pasta / "settings").mkdir(parents=True, exist_ok=True)
88
+
89
+ # Read by quantum_admin.backend at import time, so set before any import.
90
+ os.environ["QUANTUM_ADMIN_DATABASE_URL"] = f"sqlite:///{(pasta / 'quantum_admin.db').as_posix()}"
91
+ os.environ["QUANTUM_ADMIN_SETTINGS_DIR"] = str(pasta / "settings")
92
+ os.environ["QUANTUM_ADMIN_ROOT"] = str(Path(root).resolve())
93
+ os.environ.setdefault("JWT_SECRET_KEY", _secret(pasta / "jwt_secret"))
94
+
95
+ config = pasta / "quantum.config.yaml"
96
+ config.write_text(
97
+ "# Generated by `quantum admin` on every start; edits are overwritten.\n" +
98
+ yaml.safe_dump({
99
+ "server": {"host": "127.0.0.1", "port": port, "reload": False, "debug": False},
100
+ "paths": {"components": screens_folder().as_posix(),
101
+ "static": assets_folder().as_posix(),
102
+ "uploads": (pasta / "uploads").as_posix(),
103
+ "logs": (pasta / "logs").as_posix()},
104
+ "security": {"login_url": "/admin/login", "secret_key": _secret(pasta / "session_secret")},
105
+ "logging": {"level": "WARNING", "console": True, "file": False},
106
+ "services": MODULES,
107
+ }, sort_keys=False),
108
+ encoding="utf-8")
109
+ return config
110
+
111
+
112
+ def handle_admin(args) -> int:
113
+ faltando = missing_dependencies()
114
+ if faltando:
115
+ print(f"[ERROR] The admin needs {', '.join(faltando)}. Install with:\n {INSTALL_HINT}")
116
+ return 1
117
+
118
+ config = prepare(args.data, args.root, args.port)
119
+
120
+ from quantum.runtime.web_server import QuantumWebServer
121
+ from quantum_admin.backend.auth_service import get_auth_service
122
+
123
+ # Created now, not at the first login, so a generated password is printed
124
+ # before the URL instead of in the middle of the request log.
125
+ get_auth_service()
126
+ print(f"\n Quantum Admin: http://127.0.0.1:{args.port}/admin"
127
+ f"\n data: {config.parent}\n")
128
+
129
+ server = QuantumWebServer(str(config))
130
+ server.PID_FILE = str(config.parent / "admin.pid")
131
+ return server.start() or 0
@@ -280,6 +280,7 @@ def main():
280
280
  Examples:
281
281
  quantum start # Start web server (magic!)
282
282
  quantum run hello.q # Execute component
283
+ quantum admin # Start the Quantum Admin
283
284
  quantum run game.q --engine godot # Build Godot 4 project
284
285
  quantum run backup-job.q # Execute job
285
286
  quantum deploy ./my-app # Deploy application
@@ -330,6 +331,10 @@ Examples:
330
331
  # Stop command
331
332
  subparsers.add_parser('stop', help='Stop running web server')
332
333
 
334
+ # Admin command (the screens need the [admin] extra; checked when run)
335
+ from quantum.cli.admin import create_admin_parser
336
+ create_admin_parser(subparsers)
337
+
333
338
  # Deploy commands (if available)
334
339
  deploy_available, create_deploy_parser, create_apps_parser, handle_deploy, handle_apps = _get_deploy_module()
335
340
 
@@ -392,6 +397,10 @@ Examples:
392
397
  traceback.print_exc()
393
398
  sys.exit(1)
394
399
 
400
+ elif args.command == 'admin':
401
+ from quantum.cli.admin import handle_admin
402
+ sys.exit(handle_admin(args))
403
+
395
404
  # Handle 'stop' command
396
405
  elif args.command == 'stop':
397
406
  from quantum.cli.server_process import stop_server
@@ -112,6 +112,7 @@ class ComponentNode(QuantumNode):
112
112
  # Phase G: Authentication & Security
113
113
  self.require_auth = False # Require authentication to access component
114
114
  self.require_role = None # Required role(s) to access (comma-separated: "admin,editor")
115
+ self.login_url = None # AUTH-4: overrides security.login_url for this component
115
116
  self.require_permission = None # Required permission(s)
116
117
 
117
118
  # HTML rendering & interactivity (Phase 1 & future Phase 3)
@@ -423,18 +423,18 @@ class ExpressionEvaluator:
423
423
  return op(operand)
424
424
 
425
425
  if isinstance(node, ast.BoolOp):
426
- values = [self._eval(v, ctx) for v in node.values]
427
- if isinstance(node.op, ast.And):
428
- result = True
429
- for v in values:
430
- if not v:
431
- return v
432
- result = v
433
- return result
434
- for v in values:
435
- if v:
436
- return v
437
- return values[-1] if values else False
426
+ # EXPR-6: short-circuit, as in Python. Every operand used to be
427
+ # evaluated first, so `user and user.name` failed with "variable
428
+ # 'user' is not defined"-style errors exactly when the left side
429
+ # was there to guard the right.
430
+ valor = False
431
+ for operando in node.values:
432
+ valor = self._eval(operando, ctx)
433
+ if isinstance(node.op, ast.And) and not valor:
434
+ return valor
435
+ if isinstance(node.op, ast.Or) and valor:
436
+ return valor
437
+ return valor
438
438
 
439
439
  if isinstance(node, ast.Compare):
440
440
  left = self._eval(node.left, ctx)
@@ -584,6 +584,13 @@ class QuantumParser:
584
584
  require_auth_attr = root.get('require_auth', 'false').lower()
585
585
  component.require_auth = require_auth_attr in ['true', '1', 'yes']
586
586
  component.require_role = root.get('require_role')
587
+ # AUTH-4: where this component sends a visitor without a session.
588
+ component.login_url = root.get('login_url')
589
+ if component.login_url is not None and (not component.login_url.startswith('/')
590
+ or component.login_url.startswith('//')):
591
+ raise QuantumParseError(
592
+ f'<q:component name="{component.name}"> login_url must be a path on this server, '
593
+ f'like /login; got {component.login_url!r}')
587
594
  component.require_permission = root.get('require_permission')
588
595
 
589
596
  # HTML rendering & interactivity (Phase 1 & future Phase 3)
@@ -618,7 +625,108 @@ class QuantumParser:
618
625
  # Parse control flow statements (if, loop, set, dispatchEvent, etc)
619
626
  self._parse_control_flow_statements(root, component)
620
627
 
628
+ self._check_statements_in_markup(component)
629
+
621
630
  return component
631
+
632
+ # Nodes the HTML renderer draws. Anything else inside markup is a statement,
633
+ # and statements run in the execution pass, before the render pass sees
634
+ # the markup around them.
635
+ _RENDERED = (HTMLNode, TextNode, CommentNode, DocTypeNode, LoopNode, IfNode,
636
+ ComponentCallNode, SlotNode, ImportNode)
637
+
638
+ def _check_statements_in_markup(self, component: ComponentNode) -> None:
639
+ """PARSE-2: a statement where it would never run, or run with the wrong values.
640
+
641
+ Execution walks the component's statements and skips markup; the
642
+ renderer walks the markup and skips statements. So a q:set inside a
643
+ <ul> never ran — `<li>{y}</li>` printed the literal `{y}` — and a q:set
644
+ in a q:loop that also renders rows ran once per row in the execution
645
+ pass, leaving every row to render with the LAST value. Both were
646
+ silent.
647
+ """
648
+ def filhos(node):
649
+ if isinstance(node, IfNode):
650
+ corpo = list(node.if_body)
651
+ for bloco in node.elseif_blocks:
652
+ corpo += list(bloco.get('body', []))
653
+ return corpo + list(node.else_body)
654
+ if isinstance(node, LoopNode):
655
+ return list(node.body)
656
+ if isinstance(node, (HTMLNode, ComponentCallNode)):
657
+ return list(node.children or [])
658
+ return []
659
+
660
+ def renderiza(nodes):
661
+ for node in nodes:
662
+ if isinstance(node, TextNode):
663
+ if node.content.strip():
664
+ return True
665
+ elif isinstance(node, (HTMLNode, ComponentCallNode)):
666
+ return True
667
+ elif isinstance(node, (IfNode, LoopNode)) and renderiza(filhos(node)):
668
+ return True
669
+ return False
670
+
671
+ def tag(node):
672
+ nome = type(node).__name__
673
+ nome = nome[:-4] if nome.endswith('Node') else nome
674
+ rotulo = f"q:{nome[:1].lower()}{nome[1:]}"
675
+ atributo = getattr(node, 'name', None)
676
+ return f'<{rotulo} name="{atributo}">' if isinstance(atributo, str) else f'<{rotulo}>'
677
+
678
+ def expressoes(nodes):
679
+ """The text the renderer evaluates under `nodes`: contents, attributes, conditions."""
680
+ partes = []
681
+ for node in nodes:
682
+ if isinstance(node, TextNode):
683
+ partes.append(node.content)
684
+ elif isinstance(node, HTMLNode):
685
+ partes.extend(str(v) for v in (node.attributes or {}).values())
686
+ elif isinstance(node, ComponentCallNode):
687
+ partes.extend(str(v) for v in (node.props or {}).values())
688
+ elif isinstance(node, IfNode):
689
+ partes.append('{%s}' % node.condition)
690
+ partes.extend('{%s}' % b.get('condition', '') for b in node.elseif_blocks)
691
+ elif isinstance(node, LoopNode):
692
+ partes.append('{%s}' % (node.items or ''))
693
+ partes.append(expressoes(filhos(node)))
694
+ return '\n'.join(partes)
695
+
696
+ def usado(nome, texto):
697
+ padrao = re.compile(r'(?<![\w.])' + re.escape(nome) + r'(?![\w])')
698
+ return any(padrao.search(m) for m in re.findall(r'\{([^{}]*)\}', texto))
699
+
700
+ def andar(nodes, onde):
701
+ for node in nodes:
702
+ if not isinstance(node, self._RENDERED):
703
+ if onde and onde[0] == 'markup':
704
+ raise QuantumParseError(
705
+ f'{tag(node)} inside {onde[1]} in <q:component name="{component.name}"> '
706
+ f'never runs: statements run before the page is rendered, and markup '
707
+ f'is only rendered. Move it above the markup (or into a q:action).')
708
+ nome = getattr(node, 'name', None)
709
+ # A running total read AFTER the loop is fine; a value the
710
+ # loop's own rows read is not.
711
+ if onde and isinstance(nome, str) and usado(nome, onde[1]):
712
+ raise QuantumParseError(
713
+ f'{tag(node)} inside a q:loop whose rows use {{{nome}}} in '
714
+ f'<q:component name="{component.name}">: the loop runs for every item '
715
+ f'before any row is rendered, so every row would show the last value. '
716
+ f'Compute it in the expression instead, e.g. {{item.price * item.qty}}.')
717
+ continue
718
+ if isinstance(node, HTMLNode):
719
+ andar(filhos(node), onde if onde and onde[0] == 'markup' else ('markup', f'<{node.tag}>'))
720
+ elif isinstance(node, ComponentCallNode):
721
+ andar(filhos(node), onde if onde and onde[0] == 'markup'
722
+ else ('markup', f'the content of <{node.component_name}>'))
723
+ elif isinstance(node, LoopNode):
724
+ corpo = filhos(node)
725
+ andar(corpo, onde or (('loop', expressoes(corpo)) if renderiza(corpo) else None))
726
+ elif isinstance(node, IfNode):
727
+ andar(filhos(node), onde)
728
+
729
+ andar(component.statements, None)
622
730
 
623
731
  def _parse_control_flow_statements(self, parent: ET.Element, component: ComponentNode):
624
732
  """Parse control flow statements - delegates to modular parser registry"""
@@ -46,7 +46,10 @@ class InvokeParser(BaseTagParser):
46
46
  invoke_node.function = self.get_attr(element, 'function')
47
47
  invoke_node.component = self.get_attr(element, 'component')
48
48
  invoke_node.url = self.get_attr(element, 'url')
49
- invoke_node.endpoint = self.get_attr(element, 'endpoint')
49
+ if self.get_attr(element, 'endpoint') is not None:
50
+ raise ParserError(
51
+ f'<q:invoke name="{name}"> endpoint= is not supported: it was accepted and never '
52
+ f'did anything, and was removed in Quantum 0.12. Use url= or service=.')
50
53
  invoke_node.service = self.get_attr(element, 'service')
51
54
 
52
55
  # HTTP attributes
@@ -46,7 +46,8 @@ class ActionHandler:
46
46
  def handle_action(
47
47
  self,
48
48
  action: ActionNode,
49
- context: Optional[ExecutionContext] = None
49
+ context: Optional[ExecutionContext] = None,
50
+ route_params: Optional[Dict[str, str]] = None
50
51
  ) -> Tuple[Optional[str], int]:
51
52
  """
52
53
  Handle action execution.
@@ -97,6 +98,14 @@ class ActionHandler:
97
98
  if key != 'files' and not param_validation.is_uploaded_file(value)
98
99
  })
99
100
 
101
+ # ROUTE-2: the page's route segments ([nome], [...caminho]) are
102
+ # variables of the action too, set last so a form field with the
103
+ # same name cannot replace them. A POST to /admin/app/loja used to
104
+ # run without `name`, so every form had to repeat it in a hidden
105
+ # field — one the browser can change.
106
+ for key, value in (route_params or {}).items():
107
+ context.set_variable(key, value)
108
+
100
109
  # 4. Execute action body
101
110
  redirect_info = self._execute_action_body(action, context)
102
111
 
@@ -419,18 +428,17 @@ class ActionHandler:
419
428
  context.set_variable(key, value, scope="component")
420
429
 
421
430
  def _resolve_databinding(self, text: str, context: ExecutionContext) -> str:
422
- """Resolve {variable} databinding in text"""
431
+ """Resolve {expressions} in a redirect URL or flash message (ACT-3).
432
+
433
+ This looked each {name} up with get_variable, so anything but a plain
434
+ name failed — `flash="{result.error}"` or `{len(itens)} saved` ended
435
+ the action with "Variable 'result.error' not found in any scope" and a
436
+ 500. It now goes through the same evaluator as every q: attribute.
437
+ """
423
438
  if not text or '{' not in text:
424
439
  return text
425
-
426
- import re
427
-
428
- def replace_var(match):
429
- var_name = match.group(1)
430
- value = context.get_variable(var_name)
431
- return str(value) if value is not None else ''
432
-
433
- return re.sub(r'\{([^}]+)\}', replace_var, text)
440
+ valor = self.component_runtime._apply_databinding(text, context.get_all_variables())
441
+ return '' if valor is None else str(valor)
434
442
 
435
443
  def _set_flash_message(self, message: str, flash_type: str = 'info'):
436
444
  """Set flash message in session"""
@@ -455,10 +455,9 @@ class ComponentRuntime:
455
455
  # assembles code out of data. Same change as HTMLRenderer.
456
456
  stripped = self._databinding_pattern.sub(lambda m: m.group(1), condition).strip()
457
457
 
458
- # A regex quantifier as a whole condition is meaningless; false.
459
- if is_regex_quantifier(stripped):
460
- return False
461
-
458
+ # IF-2: `1` and `{1}` are the number 1, true. There used to be a
459
+ # regex-quantifier check here that made every such condition false;
460
+ # a condition is never a regex, and HTMLRenderer never had the check.
462
461
  try:
463
462
  return bool(self._expressions.evaluate(stripped, context or {}))
464
463
  except UndefinedError:
@@ -0,0 +1,133 @@
1
+ """
2
+ Quantum Component Composer — <Component prop="…">slot content</Component>.
3
+
4
+ COMP-1..4 (SPEC.md). What this used to get wrong, found by building the admin
5
+ layout on it:
6
+
7
+ - the component was searched in ./components relative to the process's
8
+ working directory — paths.components from quantum.config.yaml and the
9
+ `from=` of q:import were ignored;
10
+ - a component that was not found, or failed, became an HTML comment and the
11
+ page answered 200 with the section missing;
12
+ - the child ran as ComponentRuntime() with no configuration, so a q:query
13
+ or q:invoke service= inside a child component had no datasources/services;
14
+ - slot content was grafted into the child's AST and evaluated in the
15
+ CHILD's scope: a q:loop over the page's rows inside a layout saw nothing.
16
+ Worse, the graft mutated the child AST held in the resolver cache, so the
17
+ first request's slot content replaced the q:slot for every later request;
18
+ - props used get_variable only (no expressions) and fell back to the
19
+ placeholder text on failure.
20
+ """
21
+
22
+ import re
23
+ from typing import Any, Dict, List
24
+
25
+ from quantum.core.ast_nodes import ComponentCallNode, ComponentNode, HTMLNode, QuantumNode, SlotNode
26
+ from quantum.core.expressions import ExpressionError
27
+ from quantum.runtime.component import ComponentRuntime
28
+ from quantum.runtime.component_resolver import ComponentNotFoundError, ComponentResolver
29
+
30
+
31
+ class ComponentCompositionError(Exception):
32
+ """A component call could not be composed; the message names the component."""
33
+
34
+
35
+ class RenderedHTML(QuantumNode):
36
+ """Markup the parent already rendered (slot content), inserted as-is."""
37
+
38
+ def __init__(self, html: str):
39
+ self.html = html
40
+
41
+ def to_dict(self) -> Dict[str, Any]:
42
+ return {"type": "rendered_html", "length": len(self.html)}
43
+
44
+ def validate(self) -> List[str]:
45
+ return []
46
+
47
+
48
+ class ComponentComposer:
49
+ def __init__(self, resolver: ComponentResolver):
50
+ self.resolver = resolver
51
+
52
+ def compose(self, call: ComponentCallNode, parent) -> str:
53
+ """Render `call` as a child of the HTMLRenderer `parent`."""
54
+ from quantum.runtime.renderer import HTMLRenderer
55
+
56
+ nome, origem = parent.imports.get(call.component_name, (call.component_name, None))
57
+ try:
58
+ child = self.resolver.resolve(nome, origem).ast
59
+ except ComponentNotFoundError as exc:
60
+ raise ComponentCompositionError(f"<{call.component_name}>: {exc}") from exc
61
+
62
+ props = self._props(child, call, parent)
63
+ # COMP-4: the same configuration and the same session, application and
64
+ # request scopes as the page — a layout showing {session.userName}
65
+ # used to see an empty session.
66
+ props.update(_session_scope=parent.context.session_vars,
67
+ _application_scope=parent.context.application_vars,
68
+ _request_scope=parent.context.request_vars)
69
+ runtime = ComponentRuntime(config=parent.config)
70
+ runtime.execute_component(child, props)
71
+
72
+ # COMP-3: slot content belongs to the page — rendered here, with the
73
+ # page's variables, before the child sees it.
74
+ slot = RenderedHTML(parent.render_all(call.children)) if call.children else None
75
+ preenchido = ComponentNode(name=child.name, component_type=child.component_type)
76
+ preenchido.statements = self._com_slot(child.statements, slot)
77
+ preenchido.params, preenchido.returns, preenchido.functions = child.params, child.returns, child.functions
78
+
79
+ renderer = HTMLRenderer(runtime.execution_context, components_dir=parent.components_dir,
80
+ function_resolver=runtime._resolve_expression_function, config=parent.config)
81
+ return renderer.render(preenchido)
82
+
83
+ def _props(self, child: ComponentNode, call: ComponentCallNode, parent) -> Dict[str, Any]:
84
+ props = {}
85
+ for definicao in child.params:
86
+ if definicao.name in call.props:
87
+ props[definicao.name] = self._avaliar(call.props[definicao.name], definicao.name, call, parent)
88
+ elif definicao.default is not None:
89
+ props[definicao.name] = definicao.default
90
+ elif definicao.required:
91
+ raise ComponentCompositionError(
92
+ f"<{call.component_name}> requires the prop {definicao.name!r}")
93
+ return props
94
+
95
+ @staticmethod
96
+ def _avaliar(valor, prop, call, parent):
97
+ """COMP-2: a prop is an attribute of a tag — an expression that fails is an error."""
98
+ if not isinstance(valor, str) or "{" not in valor:
99
+ return valor
100
+ variaveis = parent.context.get_all_variables()
101
+ try:
102
+ inteira = re.fullmatch(r"\s*\{([^{}]*)\}\s*", valor)
103
+ if inteira:
104
+ return parent._expressions.evaluate(inteira.group(1), variaveis)
105
+ return re.sub(r"\{([^{}]*)\}",
106
+ lambda m: str(parent._expressions.evaluate(m.group(1), variaveis)), valor)
107
+ except ExpressionError as exc:
108
+ raise ComponentCompositionError(
109
+ f"<{call.component_name} {prop}=\"{valor}\">: {exc}") from exc
110
+
111
+ def _com_slot(self, nodes: List[QuantumNode], slot) -> List[QuantumNode]:
112
+ """Copy of `nodes` with each q:slot replaced — the cached child AST is never touched."""
113
+ resultado = []
114
+ for node in nodes:
115
+ if isinstance(node, SlotNode):
116
+ # Only the default slot receives the call's children; named
117
+ # slots keep their default content (named slots are not
118
+ # implemented, as before).
119
+ if slot is not None and (node.name or "default") == "default":
120
+ resultado.append(slot)
121
+ else:
122
+ resultado.extend(node.default_content or [])
123
+ elif isinstance(node, HTMLNode) and self._tem_slot(node.children):
124
+ copia = HTMLNode(tag=node.tag, attributes=dict(node.attributes),
125
+ children=self._com_slot(node.children, slot), self_closing=node.self_closing)
126
+ resultado.append(copia)
127
+ else:
128
+ resultado.append(node)
129
+ return resultado
130
+
131
+ def _tem_slot(self, nodes) -> bool:
132
+ return any(isinstance(n, SlotNode) or (isinstance(n, HTMLNode) and self._tem_slot(n.children))
133
+ for n in nodes)