quantum-framework 0.9.0__py3-none-any.whl
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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
quantum/cli/runner.py
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Quantum CLI Runner - Main orchestrator refactored
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Fix encoding on Windows
|
|
13
|
+
if os.name == 'nt': # Windows
|
|
14
|
+
try:
|
|
15
|
+
os.system('chcp 65001 >nul 2>&1') # UTF-8
|
|
16
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
17
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
18
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
19
|
+
except:
|
|
20
|
+
pass # Silent fallback
|
|
21
|
+
|
|
22
|
+
# Fix imports - use absolute imports
|
|
23
|
+
|
|
24
|
+
from quantum.core.parser import QuantumParser, QuantumParseError
|
|
25
|
+
from quantum.core.ast_nodes import ComponentNode, ApplicationNode, JobNode
|
|
26
|
+
from quantum.runtime.component import ComponentRuntime, ComponentExecutionError
|
|
27
|
+
from quantum.runtime.web_server import QuantumWebServer
|
|
28
|
+
from quantum.runtime.api_server import QuantumAPIServer
|
|
29
|
+
|
|
30
|
+
# Import deploy commands (lazy load to avoid import errors if requests not installed)
|
|
31
|
+
def _get_deploy_module():
|
|
32
|
+
try:
|
|
33
|
+
from quantum.cli.deploy import (
|
|
34
|
+
create_deploy_parser, create_apps_parser,
|
|
35
|
+
handle_deploy, handle_apps
|
|
36
|
+
)
|
|
37
|
+
return True, create_deploy_parser, create_apps_parser, handle_deploy, handle_apps
|
|
38
|
+
except ImportError as e:
|
|
39
|
+
return False, None, None, None, str(e)
|
|
40
|
+
|
|
41
|
+
# Import package commands
|
|
42
|
+
def _get_pkg_module():
|
|
43
|
+
try:
|
|
44
|
+
from quantum.cli.pkg import create_pkg_parser, handle_pkg
|
|
45
|
+
return True, create_pkg_parser, handle_pkg
|
|
46
|
+
except ImportError as e:
|
|
47
|
+
return False, None, str(e)
|
|
48
|
+
|
|
49
|
+
# Import jobs commands
|
|
50
|
+
def _get_jobs_module():
|
|
51
|
+
try:
|
|
52
|
+
from quantum.cli.jobs import create_jobs_parser, handle_jobs
|
|
53
|
+
return True, create_jobs_parser, handle_jobs
|
|
54
|
+
except ImportError as e:
|
|
55
|
+
return False, None, str(e)
|
|
56
|
+
|
|
57
|
+
# Import message queue commands
|
|
58
|
+
def _get_mq_module():
|
|
59
|
+
try:
|
|
60
|
+
from quantum.cli.mq import create_mq_parser, handle_mq
|
|
61
|
+
return True, create_mq_parser, handle_mq
|
|
62
|
+
except ImportError as e:
|
|
63
|
+
return False, None, str(e)
|
|
64
|
+
|
|
65
|
+
# Import migration commands
|
|
66
|
+
def _get_migrate_module():
|
|
67
|
+
try:
|
|
68
|
+
from quantum.cli.commands.migrate import MigrationRunner
|
|
69
|
+
return True, MigrationRunner
|
|
70
|
+
except ImportError as e:
|
|
71
|
+
return False, str(e)
|
|
72
|
+
|
|
73
|
+
def load_config(config_path: str = 'quantum.config.yaml') -> dict:
|
|
74
|
+
"""Load quantum.config.yaml if present.
|
|
75
|
+
|
|
76
|
+
The `run` command used to build a ComponentRuntime with no config at all,
|
|
77
|
+
so `datasources:` declared in quantum.config.yaml were never seen and
|
|
78
|
+
every q:query fell through to the optional Quantum Admin API.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
import yaml
|
|
82
|
+
path = Path(config_path)
|
|
83
|
+
if path.exists():
|
|
84
|
+
with open(path, 'r', encoding='utf-8') as fh:
|
|
85
|
+
return yaml.safe_load(fh) or {}
|
|
86
|
+
except Exception as exc:
|
|
87
|
+
print(f"[WARN] Could not read {config_path}: {exc}")
|
|
88
|
+
return {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class QuantumRunner:
|
|
92
|
+
"""Main Quantum Runner - Clean orchestration"""
|
|
93
|
+
|
|
94
|
+
def __init__(self, config: dict = None):
|
|
95
|
+
self.parser = QuantumParser()
|
|
96
|
+
self.config = config if config is not None else load_config()
|
|
97
|
+
self.component_runtime = ComponentRuntime(config=self.config)
|
|
98
|
+
|
|
99
|
+
def run(self, file_path: str, debug: bool = False) -> int:
|
|
100
|
+
"""Execute .q file"""
|
|
101
|
+
try:
|
|
102
|
+
if debug:
|
|
103
|
+
print(f"[DEBUG] Parsing file: {Path(file_path).name}")
|
|
104
|
+
|
|
105
|
+
# Store source directory for asset resolution
|
|
106
|
+
self._source_dir = str(Path(file_path).resolve().parent)
|
|
107
|
+
|
|
108
|
+
# Parse file → AST
|
|
109
|
+
ast_node = self.parser.parse_file(file_path)
|
|
110
|
+
|
|
111
|
+
if debug:
|
|
112
|
+
print(f"[DEBUG] AST generated: {type(ast_node).__name__}")
|
|
113
|
+
print(f"[DEBUG] Validating AST...")
|
|
114
|
+
|
|
115
|
+
# Validate AST
|
|
116
|
+
validation_errors = ast_node.validate()
|
|
117
|
+
if validation_errors:
|
|
118
|
+
print("[ERROR] Validation errors:")
|
|
119
|
+
for error in validation_errors:
|
|
120
|
+
print(f" - {error}")
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
# Execute based on type
|
|
124
|
+
return self._execute_ast(ast_node, debug)
|
|
125
|
+
|
|
126
|
+
except QuantumParseError as e:
|
|
127
|
+
print(f"[ERROR] Parse error: {e}")
|
|
128
|
+
return 1
|
|
129
|
+
except Exception as e:
|
|
130
|
+
print(f"[ERROR] Unexpected error: {e}")
|
|
131
|
+
if debug:
|
|
132
|
+
import traceback
|
|
133
|
+
traceback.print_exc()
|
|
134
|
+
return 1
|
|
135
|
+
|
|
136
|
+
def _execute_ast(self, ast_node, debug: bool = False) -> int:
|
|
137
|
+
"""Execute AST based on type"""
|
|
138
|
+
if isinstance(ast_node, ComponentNode):
|
|
139
|
+
return self._execute_component(ast_node, debug)
|
|
140
|
+
elif isinstance(ast_node, ApplicationNode):
|
|
141
|
+
return self._execute_application(ast_node, debug)
|
|
142
|
+
elif isinstance(ast_node, JobNode):
|
|
143
|
+
return self._execute_job(ast_node, debug)
|
|
144
|
+
else:
|
|
145
|
+
print(f"[ERROR] Unsupported AST type: {type(ast_node)}")
|
|
146
|
+
return 1
|
|
147
|
+
|
|
148
|
+
def _execute_component(self, component: ComponentNode, debug: bool = False) -> int:
|
|
149
|
+
"""Execute q:component"""
|
|
150
|
+
print(f"[EXEC] Executing component: {component.name}")
|
|
151
|
+
if debug:
|
|
152
|
+
print(f" Type: {component.component_type}")
|
|
153
|
+
print(f" Params: {len(component.params)}")
|
|
154
|
+
print(f" Returns: {len(component.returns)}")
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
result = self.component_runtime.execute_component(component)
|
|
158
|
+
if result is not None:
|
|
159
|
+
print(f"[SUCCESS] Result: {result}")
|
|
160
|
+
else:
|
|
161
|
+
print("[WARN] Component executed without return")
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
except ComponentExecutionError as e:
|
|
165
|
+
print(f"[ERROR] Execution error: {e}")
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
def _execute_application(self, app: ApplicationNode, debug: bool = False) -> int:
|
|
169
|
+
"""Execute q:application"""
|
|
170
|
+
print(f"[EXEC] Executing application: {app.app_id}")
|
|
171
|
+
print(f"[INFO] Type: {app.app_type}")
|
|
172
|
+
|
|
173
|
+
if debug:
|
|
174
|
+
print(f" Routes: {len(app.routes)}")
|
|
175
|
+
for route in app.routes:
|
|
176
|
+
print(f" {route.method} {route.path}")
|
|
177
|
+
|
|
178
|
+
if app.app_type == 'game':
|
|
179
|
+
return self._build_game(app, debug)
|
|
180
|
+
elif app.app_type == 'terminal':
|
|
181
|
+
return self._build_terminal(app, debug)
|
|
182
|
+
elif app.app_type == 'testing':
|
|
183
|
+
return self._build_tests(app, debug)
|
|
184
|
+
elif app.app_type == 'ui':
|
|
185
|
+
target = getattr(self, '_ui_target', 'html')
|
|
186
|
+
return self._build_ui(app, target, debug)
|
|
187
|
+
elif app.app_type == 'html':
|
|
188
|
+
return self._start_web_server(app)
|
|
189
|
+
elif app.app_type in ('microservices', 'api'):
|
|
190
|
+
return self._start_api_server(app)
|
|
191
|
+
else:
|
|
192
|
+
print(f"[ERROR] Application type '{app.app_type}' not supported")
|
|
193
|
+
return 1
|
|
194
|
+
|
|
195
|
+
def _build_game(self, app: ApplicationNode, debug: bool = False) -> int:
|
|
196
|
+
"""Build game from game application using selected engine backend."""
|
|
197
|
+
from quantum.runtime.game_builder import GameBuilder, GameBuildError
|
|
198
|
+
engine = getattr(self, '_game_engine', 'pixi')
|
|
199
|
+
try:
|
|
200
|
+
source_dir = getattr(self, '_source_dir', None)
|
|
201
|
+
builder = GameBuilder(engine=engine, source_dir=source_dir)
|
|
202
|
+
output_path = builder.build_to_file(app)
|
|
203
|
+
engine_label = 'Godot 4 project' if engine == 'godot' else 'HTML game'
|
|
204
|
+
print(f"[SUCCESS] {engine_label} built: {output_path}")
|
|
205
|
+
if debug:
|
|
206
|
+
print(f" Engine: {engine}")
|
|
207
|
+
print(f" Scenes: {len(getattr(app, 'scenes', []))}")
|
|
208
|
+
print(f" Behaviors: {len(getattr(app, 'behaviors', []))}")
|
|
209
|
+
print(f" Prefabs: {len(getattr(app, 'prefabs', []))}")
|
|
210
|
+
return 0
|
|
211
|
+
except GameBuildError as e:
|
|
212
|
+
print(f"[ERROR] Game build error: {e}")
|
|
213
|
+
return 1
|
|
214
|
+
|
|
215
|
+
def _build_terminal(self, app: ApplicationNode, debug: bool = False) -> int:
|
|
216
|
+
"""Build standalone Python TUI app from terminal application."""
|
|
217
|
+
from quantum.runtime.terminal_builder import TerminalBuilder, TerminalBuildError
|
|
218
|
+
try:
|
|
219
|
+
builder = TerminalBuilder()
|
|
220
|
+
output_path = builder.build_to_file(app)
|
|
221
|
+
print(f"[SUCCESS] Terminal app built: {output_path}")
|
|
222
|
+
if debug:
|
|
223
|
+
print(f" Screens: {len(getattr(app, 'screens', []))}")
|
|
224
|
+
print(f" Keybindings: {len(getattr(app, 'keybindings', []))}")
|
|
225
|
+
return 0
|
|
226
|
+
except TerminalBuildError as e:
|
|
227
|
+
print(f"[ERROR] Terminal build error: {e}")
|
|
228
|
+
return 1
|
|
229
|
+
|
|
230
|
+
def _build_tests(self, app: ApplicationNode, debug: bool = False) -> int:
|
|
231
|
+
"""Build standalone pytest file from testing application."""
|
|
232
|
+
from quantum.runtime.testing_builder import TestingBuilder, TestingBuildError
|
|
233
|
+
try:
|
|
234
|
+
builder = TestingBuilder()
|
|
235
|
+
output_path = builder.build_to_file(app)
|
|
236
|
+
print(f"[SUCCESS] Test file built: {output_path}")
|
|
237
|
+
if debug:
|
|
238
|
+
print(f" Suites: {len(getattr(app, 'test_suites', []))}")
|
|
239
|
+
print(f" Fixtures: {len(getattr(app, 'test_fixtures', []))}")
|
|
240
|
+
print(f" Mocks: {len(getattr(app, 'test_mocks', []))}")
|
|
241
|
+
return 0
|
|
242
|
+
except TestingBuildError as e:
|
|
243
|
+
print(f"[ERROR] Test build error: {e}")
|
|
244
|
+
return 1
|
|
245
|
+
|
|
246
|
+
def _build_ui(self, app: ApplicationNode, target: str = 'html', debug: bool = False) -> int:
|
|
247
|
+
"""Build multi-target UI app from UI application."""
|
|
248
|
+
from quantum.runtime.ui_builder import UIBuilder, UIBuildError
|
|
249
|
+
try:
|
|
250
|
+
builder = UIBuilder()
|
|
251
|
+
output_path = builder.build_to_file(app, target=target)
|
|
252
|
+
print(f"[SUCCESS] UI app built ({target}): {output_path}")
|
|
253
|
+
if debug:
|
|
254
|
+
print(f" Windows: {len(getattr(app, 'ui_windows', []))}")
|
|
255
|
+
print(f" UI children: {len(getattr(app, 'ui_children', []))}")
|
|
256
|
+
print(f" Target: {target}")
|
|
257
|
+
return 0
|
|
258
|
+
except UIBuildError as e:
|
|
259
|
+
print(f"[ERROR] UI build error: {e}")
|
|
260
|
+
return 1
|
|
261
|
+
|
|
262
|
+
def _execute_job(self, job: JobNode, debug: bool = False) -> int:
|
|
263
|
+
"""Execute q:job"""
|
|
264
|
+
print(f"[EXEC] Executing job: {job.job_id}")
|
|
265
|
+
if job.schedule:
|
|
266
|
+
print(f"[INFO] Schedule: {job.schedule}")
|
|
267
|
+
|
|
268
|
+
# TODO: Implement real job execution
|
|
269
|
+
print("[SUCCESS] Job executed successfully")
|
|
270
|
+
return 0
|
|
271
|
+
|
|
272
|
+
def _start_web_server(self, app: ApplicationNode) -> int:
|
|
273
|
+
"""Start web server"""
|
|
274
|
+
try:
|
|
275
|
+
server = QuantumWebServer(port=8080)
|
|
276
|
+
server.configure_from_ast(app)
|
|
277
|
+
# Propagate the server's exit code: it returns non-zero when the
|
|
278
|
+
# port is taken or the bind fails, and swallowing that made
|
|
279
|
+
# `quantum run` report success with nothing listening.
|
|
280
|
+
return server.start() or 0
|
|
281
|
+
except Exception as e:
|
|
282
|
+
print(f"[ERROR] Web server error: {e}")
|
|
283
|
+
return 1
|
|
284
|
+
|
|
285
|
+
def _start_api_server(self, app: ApplicationNode) -> int:
|
|
286
|
+
"""Start API server"""
|
|
287
|
+
try:
|
|
288
|
+
server = QuantumAPIServer(port=8080)
|
|
289
|
+
server.configure_from_ast(app)
|
|
290
|
+
# Propagate the server's exit code: it returns non-zero when the
|
|
291
|
+
# port is taken or the bind fails, and swallowing that made
|
|
292
|
+
# `quantum run` report success with nothing listening.
|
|
293
|
+
return server.start() or 0
|
|
294
|
+
except Exception as e:
|
|
295
|
+
print(f"[ERROR] API server error: {e}")
|
|
296
|
+
return 1
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def main():
|
|
300
|
+
"""CLI entry point"""
|
|
301
|
+
parser = argparse.ArgumentParser(
|
|
302
|
+
description='Quantum CLI - Execute .q files, start servers, and deploy applications',
|
|
303
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
304
|
+
epilog="""
|
|
305
|
+
Examples:
|
|
306
|
+
quantum start # Start web server (magic!)
|
|
307
|
+
quantum run hello.q # Execute component
|
|
308
|
+
quantum run webapp.q # Start web server from .q file
|
|
309
|
+
quantum run api.q # Start API server
|
|
310
|
+
quantum run game.q --engine godot # Build Godot 4 project
|
|
311
|
+
quantum run backup-job.q # Execute job
|
|
312
|
+
quantum deploy ./my-app # Deploy application
|
|
313
|
+
quantum apps # List deployed apps
|
|
314
|
+
quantum apps logs my-app # View app logs
|
|
315
|
+
quantum pkg init ./my-component # Initialize new package
|
|
316
|
+
quantum pkg install ./package # Install package
|
|
317
|
+
quantum pkg list # List installed packages
|
|
318
|
+
quantum jobs list # List scheduled jobs
|
|
319
|
+
quantum jobs run my-job # Run a job manually
|
|
320
|
+
quantum jobs worker start # Start job worker
|
|
321
|
+
quantum mq queues list # List message queues
|
|
322
|
+
quantum mq publish topic message # Publish to topic
|
|
323
|
+
quantum mq worker --queues q1,q2 # Start message worker
|
|
324
|
+
"""
|
|
325
|
+
)
|
|
326
|
+
# --version: le a versao do metadata do pacote instalado (funciona quando
|
|
327
|
+
# veio de `pip install`), com fallback para quando roda da arvore fonte.
|
|
328
|
+
try:
|
|
329
|
+
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
330
|
+
try:
|
|
331
|
+
_versao = _pkg_version("quantum-framework")
|
|
332
|
+
except PackageNotFoundError:
|
|
333
|
+
_versao = "dev (nao instalado via pip)"
|
|
334
|
+
except Exception:
|
|
335
|
+
_versao = "unknown"
|
|
336
|
+
parser.add_argument('--version', action='version',
|
|
337
|
+
version=f'quantum {_versao}')
|
|
338
|
+
|
|
339
|
+
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
|
|
340
|
+
|
|
341
|
+
# Run command
|
|
342
|
+
run_parser = subparsers.add_parser('run', help='Execute a .q file')
|
|
343
|
+
run_parser.add_argument('file', help='.q file to execute')
|
|
344
|
+
run_parser.add_argument('--debug', action='store_true', help='Debug mode')
|
|
345
|
+
run_parser.add_argument('--config', default='quantum.config.yaml', help='Config file')
|
|
346
|
+
run_parser.add_argument('--target', choices=['html', 'textual', 'desktop', 'mobile'], default='html',
|
|
347
|
+
help='UI target (for type="ui" apps): html, textual, desktop, or mobile')
|
|
348
|
+
run_parser.add_argument('--engine', choices=['pixi', 'godot'], default='pixi',
|
|
349
|
+
help='Game engine backend: pixi (default, HTML5) or godot (Godot 4 project)')
|
|
350
|
+
|
|
351
|
+
# Start command
|
|
352
|
+
start_parser = subparsers.add_parser('start', help='Start web server')
|
|
353
|
+
start_parser.add_argument('--port', type=int, help='Port (overrides config)')
|
|
354
|
+
start_parser.add_argument('--config', default='quantum.config.yaml', help='Config file')
|
|
355
|
+
start_parser.add_argument('--debug', action='store_true', help='Debug mode')
|
|
356
|
+
|
|
357
|
+
# Stop command
|
|
358
|
+
subparsers.add_parser('stop', help='Stop running web server')
|
|
359
|
+
|
|
360
|
+
# Deploy commands (if available)
|
|
361
|
+
deploy_available, create_deploy_parser, create_apps_parser, handle_deploy, handle_apps = _get_deploy_module()
|
|
362
|
+
|
|
363
|
+
if deploy_available:
|
|
364
|
+
create_deploy_parser(subparsers)
|
|
365
|
+
create_apps_parser(subparsers)
|
|
366
|
+
|
|
367
|
+
# Package commands (if available)
|
|
368
|
+
pkg_available, create_pkg_parser, handle_pkg = _get_pkg_module()
|
|
369
|
+
|
|
370
|
+
if pkg_available:
|
|
371
|
+
create_pkg_parser(subparsers)
|
|
372
|
+
|
|
373
|
+
# Jobs commands (if available)
|
|
374
|
+
jobs_available, create_jobs_parser, handle_jobs = _get_jobs_module()
|
|
375
|
+
|
|
376
|
+
if jobs_available:
|
|
377
|
+
create_jobs_parser(subparsers)
|
|
378
|
+
|
|
379
|
+
# Message queue commands (if available)
|
|
380
|
+
mq_available, create_mq_parser, handle_mq = _get_mq_module()
|
|
381
|
+
|
|
382
|
+
if mq_available:
|
|
383
|
+
create_mq_parser(subparsers)
|
|
384
|
+
|
|
385
|
+
# Migration commands
|
|
386
|
+
migrate_parser = subparsers.add_parser('migrate', help='Database migration commands')
|
|
387
|
+
migrate_subparsers = migrate_parser.add_subparsers(dest='migrate_command')
|
|
388
|
+
|
|
389
|
+
migrate_subparsers.add_parser('status', help='Show migration status')
|
|
390
|
+
|
|
391
|
+
migrate_up_parser = migrate_subparsers.add_parser('up', help='Apply pending migrations')
|
|
392
|
+
migrate_up_parser.add_argument('-n', '--count', type=int, help='Number of migrations to apply')
|
|
393
|
+
|
|
394
|
+
migrate_down_parser = migrate_subparsers.add_parser('down', help='Rollback migrations')
|
|
395
|
+
migrate_down_parser.add_argument('-n', '--count', type=int, default=1, help='Number of migrations to rollback')
|
|
396
|
+
|
|
397
|
+
migrate_create_parser = migrate_subparsers.add_parser('create', help='Create a new migration')
|
|
398
|
+
migrate_create_parser.add_argument('name', help='Migration name')
|
|
399
|
+
|
|
400
|
+
# Parse arguments
|
|
401
|
+
args = parser.parse_args()
|
|
402
|
+
|
|
403
|
+
if not args.command:
|
|
404
|
+
parser.print_help()
|
|
405
|
+
sys.exit(0)
|
|
406
|
+
|
|
407
|
+
# Handle 'start' command
|
|
408
|
+
if args.command == 'start':
|
|
409
|
+
from quantum.runtime.web_server import start_server
|
|
410
|
+
try:
|
|
411
|
+
# Exit with whatever the server reports: a taken port or a failed
|
|
412
|
+
# bind must not look like a clean start to a supervisor or CI.
|
|
413
|
+
sys.exit(start_server(args.config, port=getattr(args, 'port', None)) or 0)
|
|
414
|
+
except Exception as e:
|
|
415
|
+
print(f"[ERROR] Failed to start server: {e}")
|
|
416
|
+
if getattr(args, 'debug', False):
|
|
417
|
+
import traceback
|
|
418
|
+
traceback.print_exc()
|
|
419
|
+
sys.exit(1)
|
|
420
|
+
|
|
421
|
+
# Handle 'stop' command
|
|
422
|
+
elif args.command == 'stop':
|
|
423
|
+
from quantum.cli.server_process import stop_server
|
|
424
|
+
sys.exit(stop_server())
|
|
425
|
+
|
|
426
|
+
# Handle 'run' command
|
|
427
|
+
elif args.command == 'run':
|
|
428
|
+
if not args.file:
|
|
429
|
+
print("[ERROR] File argument is required for 'run' command")
|
|
430
|
+
print("Usage: quantum run <file.q>")
|
|
431
|
+
sys.exit(1)
|
|
432
|
+
|
|
433
|
+
runner = QuantumRunner(config=load_config(getattr(args, 'config', 'quantum.config.yaml')))
|
|
434
|
+
runner._ui_target = getattr(args, 'target', 'html')
|
|
435
|
+
runner._game_engine = getattr(args, 'engine', 'pixi')
|
|
436
|
+
exit_code = runner.run(args.file, getattr(args, 'debug', False))
|
|
437
|
+
sys.exit(exit_code)
|
|
438
|
+
|
|
439
|
+
# Handle 'deploy' command
|
|
440
|
+
elif args.command == 'deploy':
|
|
441
|
+
if not deploy_available:
|
|
442
|
+
print("[ERROR] Deploy functionality not available.")
|
|
443
|
+
print(f"Missing dependency: {handle_apps}")
|
|
444
|
+
print("Install with: pip install requests")
|
|
445
|
+
sys.exit(1)
|
|
446
|
+
sys.exit(handle_deploy(args))
|
|
447
|
+
|
|
448
|
+
# Handle 'apps' command
|
|
449
|
+
elif args.command == 'apps':
|
|
450
|
+
if not deploy_available:
|
|
451
|
+
print("[ERROR] Apps functionality not available.")
|
|
452
|
+
print(f"Missing dependency: {handle_apps}")
|
|
453
|
+
print("Install with: pip install requests")
|
|
454
|
+
sys.exit(1)
|
|
455
|
+
sys.exit(handle_apps(args))
|
|
456
|
+
|
|
457
|
+
# Handle 'jobs' command
|
|
458
|
+
elif args.command == 'jobs':
|
|
459
|
+
if not jobs_available:
|
|
460
|
+
print("[ERROR] Jobs functionality not available.")
|
|
461
|
+
print(f"Error: {handle_jobs}")
|
|
462
|
+
sys.exit(1)
|
|
463
|
+
sys.exit(handle_jobs(args))
|
|
464
|
+
|
|
465
|
+
# Handle 'mq' command
|
|
466
|
+
elif args.command == 'mq':
|
|
467
|
+
if not mq_available:
|
|
468
|
+
print("[ERROR] Message queue functionality not available.")
|
|
469
|
+
print(f"Error: {handle_mq}")
|
|
470
|
+
sys.exit(1)
|
|
471
|
+
sys.exit(handle_mq(args))
|
|
472
|
+
|
|
473
|
+
# Handle 'pkg' command
|
|
474
|
+
elif args.command == 'pkg':
|
|
475
|
+
if not pkg_available:
|
|
476
|
+
print("[ERROR] Package functionality not available.")
|
|
477
|
+
print(f"Error: {handle_pkg}")
|
|
478
|
+
sys.exit(1)
|
|
479
|
+
sys.exit(handle_pkg(args))
|
|
480
|
+
|
|
481
|
+
# Handle 'migrate' command
|
|
482
|
+
elif args.command == 'migrate':
|
|
483
|
+
migrate_available, result = _get_migrate_module()
|
|
484
|
+
if not migrate_available:
|
|
485
|
+
print(f"[ERROR] Migration functionality not available: {result}")
|
|
486
|
+
sys.exit(1)
|
|
487
|
+
|
|
488
|
+
MigrationRunner = result
|
|
489
|
+
|
|
490
|
+
try:
|
|
491
|
+
runner = MigrationRunner()
|
|
492
|
+
|
|
493
|
+
if args.migrate_command == 'status':
|
|
494
|
+
import json
|
|
495
|
+
status = runner.status()
|
|
496
|
+
print(f"\nDatabase: {status['database_type']}")
|
|
497
|
+
print(f"Applied migrations: {status['total_applied']}")
|
|
498
|
+
print(f"Pending migrations: {status['total_pending']}")
|
|
499
|
+
|
|
500
|
+
if status['applied']:
|
|
501
|
+
print("\nApplied:")
|
|
502
|
+
for m in status['applied']:
|
|
503
|
+
print(f" {m['version']}: {m['name']} (applied: {m['applied_at']})")
|
|
504
|
+
|
|
505
|
+
if status['pending']:
|
|
506
|
+
print("\nPending:")
|
|
507
|
+
for m in status['pending']:
|
|
508
|
+
print(f" {m['version']}: {m['name']}")
|
|
509
|
+
|
|
510
|
+
elif args.migrate_command == 'up':
|
|
511
|
+
results = runner.up(count=getattr(args, 'count', None))
|
|
512
|
+
applied = sum(1 for r in results if r.get('status') == 'applied')
|
|
513
|
+
failed = sum(1 for r in results if r.get('status') == 'failed')
|
|
514
|
+
print(f"\nApplied: {applied}, Failed: {failed}")
|
|
515
|
+
if failed:
|
|
516
|
+
sys.exit(1)
|
|
517
|
+
|
|
518
|
+
elif args.migrate_command == 'down':
|
|
519
|
+
results = runner.down(count=getattr(args, 'count', 1))
|
|
520
|
+
rolled_back = sum(1 for r in results if r.get('status') == 'rolled_back')
|
|
521
|
+
failed = sum(1 for r in results if r.get('status') == 'failed')
|
|
522
|
+
print(f"\nRolled back: {rolled_back}, Failed: {failed}")
|
|
523
|
+
if failed:
|
|
524
|
+
sys.exit(1)
|
|
525
|
+
|
|
526
|
+
elif args.migrate_command == 'create':
|
|
527
|
+
filepath = runner.create(args.name)
|
|
528
|
+
print(f"Created migration: {filepath}")
|
|
529
|
+
print(f"Also created rollback: {filepath.with_suffix('.down.sql')}")
|
|
530
|
+
|
|
531
|
+
else:
|
|
532
|
+
print("Usage: quantum migrate <status|up|down|create> [options]")
|
|
533
|
+
sys.exit(1)
|
|
534
|
+
|
|
535
|
+
sys.exit(0)
|
|
536
|
+
|
|
537
|
+
except Exception as e:
|
|
538
|
+
print(f"[ERROR] Migration failed: {e}")
|
|
539
|
+
sys.exit(1)
|
|
540
|
+
|
|
541
|
+
else:
|
|
542
|
+
parser.print_help()
|
|
543
|
+
sys.exit(1)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
if __name__ == '__main__':
|
|
547
|
+
main()
|