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
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Package Registry
|
|
3
|
+
|
|
4
|
+
Local registry for storing and managing installed packages.
|
|
5
|
+
|
|
6
|
+
Registry structure:
|
|
7
|
+
~/.quantum/
|
|
8
|
+
├── packages/
|
|
9
|
+
│ ├── my-component/
|
|
10
|
+
│ │ ├── 1.0.0/ # Version directory
|
|
11
|
+
│ │ │ ├── quantum.yaml
|
|
12
|
+
│ │ │ ├── src/
|
|
13
|
+
│ │ │ └── ...
|
|
14
|
+
│ │ └── 1.0.1/
|
|
15
|
+
│ └── other-package/
|
|
16
|
+
├── registry.json # Package index
|
|
17
|
+
└── config.yaml # User config
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import shutil
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Dict, List, Optional, Any
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
|
|
27
|
+
from .manifest import PackageManifest, ManifestError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RegistryError(Exception):
|
|
31
|
+
"""Error in package registry operations."""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PackageRegistry:
|
|
36
|
+
"""
|
|
37
|
+
Local package registry manager.
|
|
38
|
+
|
|
39
|
+
Handles:
|
|
40
|
+
- Package installation/removal
|
|
41
|
+
- Version management
|
|
42
|
+
- Package index maintenance
|
|
43
|
+
- Package search
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, registry_path: Optional[Path] = None):
|
|
47
|
+
"""Initialize registry at given path or default ~/.quantum."""
|
|
48
|
+
if registry_path is None:
|
|
49
|
+
# Default to ~/.quantum/
|
|
50
|
+
home = Path.home()
|
|
51
|
+
registry_path = home / '.quantum'
|
|
52
|
+
|
|
53
|
+
self.registry_path = Path(registry_path)
|
|
54
|
+
self.packages_path = self.registry_path / 'packages'
|
|
55
|
+
self.index_path = self.registry_path / 'registry.json'
|
|
56
|
+
|
|
57
|
+
# Ensure directories exist
|
|
58
|
+
self._init_registry()
|
|
59
|
+
|
|
60
|
+
def _init_registry(self) -> None:
|
|
61
|
+
"""Initialize registry directories and files."""
|
|
62
|
+
self.registry_path.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
self.packages_path.mkdir(exist_ok=True)
|
|
64
|
+
|
|
65
|
+
if not self.index_path.exists():
|
|
66
|
+
self._save_index({})
|
|
67
|
+
|
|
68
|
+
def _load_index(self) -> Dict[str, Any]:
|
|
69
|
+
"""Load registry index."""
|
|
70
|
+
try:
|
|
71
|
+
with open(self.index_path, 'r', encoding='utf-8') as f:
|
|
72
|
+
return json.load(f)
|
|
73
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
74
|
+
return {}
|
|
75
|
+
|
|
76
|
+
def _save_index(self, index: Dict[str, Any]) -> None:
|
|
77
|
+
"""Save registry index."""
|
|
78
|
+
with open(self.index_path, 'w', encoding='utf-8') as f:
|
|
79
|
+
json.dump(index, f, indent=2)
|
|
80
|
+
|
|
81
|
+
def install_package(
|
|
82
|
+
self,
|
|
83
|
+
source_path: Path,
|
|
84
|
+
manifest: Optional[PackageManifest] = None
|
|
85
|
+
) -> PackageManifest:
|
|
86
|
+
"""
|
|
87
|
+
Install a package from source directory.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
source_path: Path to package directory (with quantum.yaml)
|
|
91
|
+
manifest: Pre-loaded manifest (optional)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Installed package manifest
|
|
95
|
+
"""
|
|
96
|
+
source_path = Path(source_path)
|
|
97
|
+
|
|
98
|
+
# Load manifest if not provided
|
|
99
|
+
if manifest is None:
|
|
100
|
+
manifest_path = source_path / 'quantum.yaml'
|
|
101
|
+
if not manifest_path.exists():
|
|
102
|
+
raise RegistryError(f"No quantum.yaml found in {source_path}")
|
|
103
|
+
manifest = PackageManifest.load(manifest_path)
|
|
104
|
+
|
|
105
|
+
package_name = manifest.name
|
|
106
|
+
version = manifest.version
|
|
107
|
+
|
|
108
|
+
# Create package version directory
|
|
109
|
+
version_dir = self.packages_path / package_name / version
|
|
110
|
+
if version_dir.exists():
|
|
111
|
+
# Remove existing version
|
|
112
|
+
shutil.rmtree(version_dir)
|
|
113
|
+
|
|
114
|
+
version_dir.mkdir(parents=True)
|
|
115
|
+
|
|
116
|
+
# Copy package contents
|
|
117
|
+
for item in source_path.iterdir():
|
|
118
|
+
if item.name.startswith('.'):
|
|
119
|
+
continue # Skip hidden files
|
|
120
|
+
|
|
121
|
+
dest = version_dir / item.name
|
|
122
|
+
if item.is_dir():
|
|
123
|
+
shutil.copytree(item, dest)
|
|
124
|
+
else:
|
|
125
|
+
shutil.copy2(item, dest)
|
|
126
|
+
|
|
127
|
+
# Update registry index
|
|
128
|
+
index = self._load_index()
|
|
129
|
+
if package_name not in index:
|
|
130
|
+
index[package_name] = {
|
|
131
|
+
'versions': {},
|
|
132
|
+
'latest': None,
|
|
133
|
+
'installed_at': None,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
index[package_name]['versions'][version] = {
|
|
137
|
+
'path': str(version_dir),
|
|
138
|
+
'installed_at': datetime.now().isoformat(),
|
|
139
|
+
'description': manifest.description,
|
|
140
|
+
'author': manifest.author,
|
|
141
|
+
'keywords': manifest.keywords,
|
|
142
|
+
}
|
|
143
|
+
index[package_name]['latest'] = version
|
|
144
|
+
index[package_name]['installed_at'] = datetime.now().isoformat()
|
|
145
|
+
|
|
146
|
+
self._save_index(index)
|
|
147
|
+
|
|
148
|
+
# Update manifest path
|
|
149
|
+
manifest._path = version_dir
|
|
150
|
+
|
|
151
|
+
return manifest
|
|
152
|
+
|
|
153
|
+
def remove_package(
|
|
154
|
+
self,
|
|
155
|
+
package_name: str,
|
|
156
|
+
version: Optional[str] = None
|
|
157
|
+
) -> bool:
|
|
158
|
+
"""
|
|
159
|
+
Remove a package or specific version.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
package_name: Name of package to remove
|
|
163
|
+
version: Specific version to remove (None = all versions)
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
True if removed, False if not found
|
|
167
|
+
"""
|
|
168
|
+
index = self._load_index()
|
|
169
|
+
|
|
170
|
+
if package_name not in index:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
package_dir = self.packages_path / package_name
|
|
174
|
+
|
|
175
|
+
if version:
|
|
176
|
+
# Remove specific version
|
|
177
|
+
version_dir = package_dir / version
|
|
178
|
+
if version_dir.exists():
|
|
179
|
+
shutil.rmtree(version_dir)
|
|
180
|
+
|
|
181
|
+
if version in index[package_name]['versions']:
|
|
182
|
+
del index[package_name]['versions'][version]
|
|
183
|
+
|
|
184
|
+
# Update latest version
|
|
185
|
+
remaining_versions = list(index[package_name]['versions'].keys())
|
|
186
|
+
if remaining_versions:
|
|
187
|
+
# Sort by version and get highest
|
|
188
|
+
remaining_versions.sort(key=self._version_key, reverse=True)
|
|
189
|
+
index[package_name]['latest'] = remaining_versions[0]
|
|
190
|
+
else:
|
|
191
|
+
# No versions left, remove package entirely
|
|
192
|
+
del index[package_name]
|
|
193
|
+
if package_dir.exists():
|
|
194
|
+
shutil.rmtree(package_dir)
|
|
195
|
+
else:
|
|
196
|
+
# Remove all versions
|
|
197
|
+
if package_dir.exists():
|
|
198
|
+
shutil.rmtree(package_dir)
|
|
199
|
+
del index[package_name]
|
|
200
|
+
|
|
201
|
+
self._save_index(index)
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
def get_package(
|
|
205
|
+
self,
|
|
206
|
+
package_name: str,
|
|
207
|
+
version: Optional[str] = None
|
|
208
|
+
) -> Optional[PackageManifest]:
|
|
209
|
+
"""
|
|
210
|
+
Get installed package manifest.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
package_name: Package name
|
|
214
|
+
version: Specific version (None = latest)
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Package manifest or None if not found
|
|
218
|
+
"""
|
|
219
|
+
index = self._load_index()
|
|
220
|
+
|
|
221
|
+
if package_name not in index:
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
pkg_info = index[package_name]
|
|
225
|
+
|
|
226
|
+
if version is None:
|
|
227
|
+
version = pkg_info.get('latest')
|
|
228
|
+
|
|
229
|
+
if not version or version not in pkg_info['versions']:
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
version_info = pkg_info['versions'][version]
|
|
233
|
+
version_dir = Path(version_info['path'])
|
|
234
|
+
|
|
235
|
+
manifest_path = version_dir / 'quantum.yaml'
|
|
236
|
+
if not manifest_path.exists():
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
return PackageManifest.load(manifest_path)
|
|
241
|
+
except ManifestError:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
def get_package_path(
|
|
245
|
+
self,
|
|
246
|
+
package_name: str,
|
|
247
|
+
version: Optional[str] = None
|
|
248
|
+
) -> Optional[Path]:
|
|
249
|
+
"""
|
|
250
|
+
Get path to installed package directory.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
package_name: Package name
|
|
254
|
+
version: Specific version (None = latest)
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Path to package directory or None
|
|
258
|
+
"""
|
|
259
|
+
index = self._load_index()
|
|
260
|
+
|
|
261
|
+
if package_name not in index:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
pkg_info = index[package_name]
|
|
265
|
+
|
|
266
|
+
if version is None:
|
|
267
|
+
version = pkg_info.get('latest')
|
|
268
|
+
|
|
269
|
+
if not version or version not in pkg_info['versions']:
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
version_info = pkg_info['versions'][version]
|
|
273
|
+
return Path(version_info['path'])
|
|
274
|
+
|
|
275
|
+
def list_packages(self) -> List[Dict[str, Any]]:
|
|
276
|
+
"""
|
|
277
|
+
List all installed packages.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List of package info dictionaries
|
|
281
|
+
"""
|
|
282
|
+
index = self._load_index()
|
|
283
|
+
packages = []
|
|
284
|
+
|
|
285
|
+
for name, info in index.items():
|
|
286
|
+
pkg = {
|
|
287
|
+
'name': name,
|
|
288
|
+
'latest': info.get('latest'),
|
|
289
|
+
'versions': list(info.get('versions', {}).keys()),
|
|
290
|
+
'installed_at': info.get('installed_at'),
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# Get description from latest version
|
|
294
|
+
if info.get('latest') and info['latest'] in info.get('versions', {}):
|
|
295
|
+
version_info = info['versions'][info['latest']]
|
|
296
|
+
pkg['description'] = version_info.get('description', '')
|
|
297
|
+
pkg['author'] = version_info.get('author', '')
|
|
298
|
+
pkg['keywords'] = version_info.get('keywords', [])
|
|
299
|
+
|
|
300
|
+
packages.append(pkg)
|
|
301
|
+
|
|
302
|
+
return packages
|
|
303
|
+
|
|
304
|
+
def search_packages(self, query: str) -> List[Dict[str, Any]]:
|
|
305
|
+
"""
|
|
306
|
+
Search installed packages by name or keywords.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
query: Search query
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
List of matching package info dictionaries
|
|
313
|
+
"""
|
|
314
|
+
query = query.lower()
|
|
315
|
+
packages = self.list_packages()
|
|
316
|
+
results = []
|
|
317
|
+
|
|
318
|
+
for pkg in packages:
|
|
319
|
+
# Match by name
|
|
320
|
+
if query in pkg['name'].lower():
|
|
321
|
+
results.append(pkg)
|
|
322
|
+
continue
|
|
323
|
+
|
|
324
|
+
# Match by keywords
|
|
325
|
+
keywords = pkg.get('keywords', [])
|
|
326
|
+
if any(query in kw.lower() for kw in keywords):
|
|
327
|
+
results.append(pkg)
|
|
328
|
+
continue
|
|
329
|
+
|
|
330
|
+
# Match by description
|
|
331
|
+
description = pkg.get('description', '')
|
|
332
|
+
if query in description.lower():
|
|
333
|
+
results.append(pkg)
|
|
334
|
+
|
|
335
|
+
return results
|
|
336
|
+
|
|
337
|
+
def has_package(self, package_name: str, version: Optional[str] = None) -> bool:
|
|
338
|
+
"""Check if package is installed."""
|
|
339
|
+
index = self._load_index()
|
|
340
|
+
|
|
341
|
+
if package_name not in index:
|
|
342
|
+
return False
|
|
343
|
+
|
|
344
|
+
if version:
|
|
345
|
+
return version in index[package_name].get('versions', {})
|
|
346
|
+
|
|
347
|
+
return True
|
|
348
|
+
|
|
349
|
+
def get_all_versions(self, package_name: str) -> List[str]:
|
|
350
|
+
"""Get all installed versions of a package."""
|
|
351
|
+
index = self._load_index()
|
|
352
|
+
|
|
353
|
+
if package_name not in index:
|
|
354
|
+
return []
|
|
355
|
+
|
|
356
|
+
versions = list(index[package_name].get('versions', {}).keys())
|
|
357
|
+
versions.sort(key=self._version_key, reverse=True)
|
|
358
|
+
return versions
|
|
359
|
+
|
|
360
|
+
@staticmethod
|
|
361
|
+
def _version_key(version: str) -> tuple:
|
|
362
|
+
"""Convert version string to sortable tuple."""
|
|
363
|
+
# Remove prerelease suffix for comparison
|
|
364
|
+
base_version = version.split('-')[0]
|
|
365
|
+
try:
|
|
366
|
+
parts = [int(p) for p in base_version.split('.')]
|
|
367
|
+
# Pad to 3 parts
|
|
368
|
+
while len(parts) < 3:
|
|
369
|
+
parts.append(0)
|
|
370
|
+
return tuple(parts)
|
|
371
|
+
except ValueError:
|
|
372
|
+
return (0, 0, 0)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class RemoteRegistry:
|
|
376
|
+
"""
|
|
377
|
+
Remote package registry client.
|
|
378
|
+
|
|
379
|
+
TODO: Implement when remote registry server is available.
|
|
380
|
+
For now, packages are shared via local paths or git URLs.
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
def __init__(self, registry_url: str = "https://packages.quantum.dev"):
|
|
384
|
+
self.registry_url = registry_url
|
|
385
|
+
|
|
386
|
+
def search(self, query: str) -> List[Dict[str, Any]]:
|
|
387
|
+
"""Search remote registry."""
|
|
388
|
+
# TODO: Implement HTTP search
|
|
389
|
+
raise NotImplementedError("Remote registry not yet implemented")
|
|
390
|
+
|
|
391
|
+
def download(self, package_name: str, version: Optional[str] = None) -> Path:
|
|
392
|
+
"""Download package from remote registry."""
|
|
393
|
+
# TODO: Implement HTTP download
|
|
394
|
+
raise NotImplementedError("Remote registry not yet implemented")
|
|
395
|
+
|
|
396
|
+
def publish(self, manifest: PackageManifest, archive_path: Path) -> bool:
|
|
397
|
+
"""Publish package to remote registry."""
|
|
398
|
+
# TODO: Implement HTTP publish
|
|
399
|
+
raise NotImplementedError("Remote registry not yet implemented")
|