atlas-chat 0.1.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.
Files changed (250) hide show
  1. atlas/__init__.py +40 -0
  2. atlas/application/__init__.py +7 -0
  3. atlas/application/chat/__init__.py +7 -0
  4. atlas/application/chat/agent/__init__.py +10 -0
  5. atlas/application/chat/agent/act_loop.py +179 -0
  6. atlas/application/chat/agent/factory.py +142 -0
  7. atlas/application/chat/agent/protocols.py +46 -0
  8. atlas/application/chat/agent/react_loop.py +338 -0
  9. atlas/application/chat/agent/think_act_loop.py +171 -0
  10. atlas/application/chat/approval_manager.py +151 -0
  11. atlas/application/chat/elicitation_manager.py +191 -0
  12. atlas/application/chat/events/__init__.py +1 -0
  13. atlas/application/chat/events/agent_event_relay.py +112 -0
  14. atlas/application/chat/modes/__init__.py +1 -0
  15. atlas/application/chat/modes/agent.py +125 -0
  16. atlas/application/chat/modes/plain.py +74 -0
  17. atlas/application/chat/modes/rag.py +81 -0
  18. atlas/application/chat/modes/tools.py +179 -0
  19. atlas/application/chat/orchestrator.py +213 -0
  20. atlas/application/chat/policies/__init__.py +1 -0
  21. atlas/application/chat/policies/tool_authorization.py +99 -0
  22. atlas/application/chat/preprocessors/__init__.py +1 -0
  23. atlas/application/chat/preprocessors/message_builder.py +92 -0
  24. atlas/application/chat/preprocessors/prompt_override_service.py +104 -0
  25. atlas/application/chat/service.py +454 -0
  26. atlas/application/chat/utilities/__init__.py +6 -0
  27. atlas/application/chat/utilities/error_handler.py +367 -0
  28. atlas/application/chat/utilities/event_notifier.py +546 -0
  29. atlas/application/chat/utilities/file_processor.py +613 -0
  30. atlas/application/chat/utilities/tool_executor.py +789 -0
  31. atlas/atlas_chat_cli.py +347 -0
  32. atlas/atlas_client.py +238 -0
  33. atlas/core/__init__.py +0 -0
  34. atlas/core/auth.py +205 -0
  35. atlas/core/authorization_manager.py +27 -0
  36. atlas/core/capabilities.py +123 -0
  37. atlas/core/compliance.py +215 -0
  38. atlas/core/domain_whitelist.py +147 -0
  39. atlas/core/domain_whitelist_middleware.py +82 -0
  40. atlas/core/http_client.py +28 -0
  41. atlas/core/log_sanitizer.py +102 -0
  42. atlas/core/metrics_logger.py +59 -0
  43. atlas/core/middleware.py +131 -0
  44. atlas/core/otel_config.py +242 -0
  45. atlas/core/prompt_risk.py +200 -0
  46. atlas/core/rate_limit.py +0 -0
  47. atlas/core/rate_limit_middleware.py +64 -0
  48. atlas/core/security_headers_middleware.py +51 -0
  49. atlas/domain/__init__.py +37 -0
  50. atlas/domain/chat/__init__.py +1 -0
  51. atlas/domain/chat/dtos.py +85 -0
  52. atlas/domain/errors.py +96 -0
  53. atlas/domain/messages/__init__.py +12 -0
  54. atlas/domain/messages/models.py +160 -0
  55. atlas/domain/rag_mcp_service.py +664 -0
  56. atlas/domain/sessions/__init__.py +7 -0
  57. atlas/domain/sessions/models.py +36 -0
  58. atlas/domain/unified_rag_service.py +371 -0
  59. atlas/infrastructure/__init__.py +10 -0
  60. atlas/infrastructure/app_factory.py +135 -0
  61. atlas/infrastructure/events/__init__.py +1 -0
  62. atlas/infrastructure/events/cli_event_publisher.py +140 -0
  63. atlas/infrastructure/events/websocket_publisher.py +140 -0
  64. atlas/infrastructure/sessions/in_memory_repository.py +56 -0
  65. atlas/infrastructure/transport/__init__.py +7 -0
  66. atlas/infrastructure/transport/websocket_connection_adapter.py +33 -0
  67. atlas/init_cli.py +226 -0
  68. atlas/interfaces/__init__.py +15 -0
  69. atlas/interfaces/events.py +134 -0
  70. atlas/interfaces/llm.py +54 -0
  71. atlas/interfaces/rag.py +40 -0
  72. atlas/interfaces/sessions.py +75 -0
  73. atlas/interfaces/tools.py +57 -0
  74. atlas/interfaces/transport.py +24 -0
  75. atlas/main.py +564 -0
  76. atlas/mcp/api_key_demo/README.md +76 -0
  77. atlas/mcp/api_key_demo/main.py +172 -0
  78. atlas/mcp/api_key_demo/run.sh +56 -0
  79. atlas/mcp/basictable/main.py +147 -0
  80. atlas/mcp/calculator/main.py +149 -0
  81. atlas/mcp/code-executor/execution_engine.py +98 -0
  82. atlas/mcp/code-executor/execution_environment.py +95 -0
  83. atlas/mcp/code-executor/main.py +528 -0
  84. atlas/mcp/code-executor/result_processing.py +276 -0
  85. atlas/mcp/code-executor/script_generation.py +195 -0
  86. atlas/mcp/code-executor/security_checker.py +140 -0
  87. atlas/mcp/corporate_cars/main.py +437 -0
  88. atlas/mcp/csv_reporter/main.py +545 -0
  89. atlas/mcp/duckduckgo/main.py +182 -0
  90. atlas/mcp/elicitation_demo/README.md +171 -0
  91. atlas/mcp/elicitation_demo/main.py +262 -0
  92. atlas/mcp/env-demo/README.md +158 -0
  93. atlas/mcp/env-demo/main.py +199 -0
  94. atlas/mcp/file_size_test/main.py +284 -0
  95. atlas/mcp/filesystem/main.py +348 -0
  96. atlas/mcp/image_demo/main.py +113 -0
  97. atlas/mcp/image_demo/requirements.txt +4 -0
  98. atlas/mcp/logging_demo/README.md +72 -0
  99. atlas/mcp/logging_demo/main.py +103 -0
  100. atlas/mcp/many_tools_demo/main.py +50 -0
  101. atlas/mcp/order_database/__init__.py +0 -0
  102. atlas/mcp/order_database/main.py +369 -0
  103. atlas/mcp/order_database/signal_data.csv +1001 -0
  104. atlas/mcp/pdfbasic/main.py +394 -0
  105. atlas/mcp/pptx_generator/main.py +760 -0
  106. atlas/mcp/pptx_generator/requirements.txt +13 -0
  107. atlas/mcp/pptx_generator/run_test.sh +1 -0
  108. atlas/mcp/pptx_generator/test_pptx_generator_security.py +169 -0
  109. atlas/mcp/progress_demo/main.py +167 -0
  110. atlas/mcp/progress_updates_demo/QUICKSTART.md +273 -0
  111. atlas/mcp/progress_updates_demo/README.md +120 -0
  112. atlas/mcp/progress_updates_demo/main.py +497 -0
  113. atlas/mcp/prompts/main.py +222 -0
  114. atlas/mcp/public_demo/main.py +189 -0
  115. atlas/mcp/sampling_demo/README.md +169 -0
  116. atlas/mcp/sampling_demo/main.py +234 -0
  117. atlas/mcp/thinking/main.py +77 -0
  118. atlas/mcp/tool_planner/main.py +240 -0
  119. atlas/mcp/ui-demo/badmesh.png +0 -0
  120. atlas/mcp/ui-demo/main.py +383 -0
  121. atlas/mcp/ui-demo/templates/button_demo.html +32 -0
  122. atlas/mcp/ui-demo/templates/data_visualization.html +32 -0
  123. atlas/mcp/ui-demo/templates/form_demo.html +28 -0
  124. atlas/mcp/username-override-demo/README.md +320 -0
  125. atlas/mcp/username-override-demo/main.py +308 -0
  126. atlas/modules/__init__.py +0 -0
  127. atlas/modules/config/__init__.py +34 -0
  128. atlas/modules/config/cli.py +231 -0
  129. atlas/modules/config/config_manager.py +1096 -0
  130. atlas/modules/file_storage/__init__.py +22 -0
  131. atlas/modules/file_storage/cli.py +330 -0
  132. atlas/modules/file_storage/content_extractor.py +290 -0
  133. atlas/modules/file_storage/manager.py +295 -0
  134. atlas/modules/file_storage/mock_s3_client.py +402 -0
  135. atlas/modules/file_storage/s3_client.py +417 -0
  136. atlas/modules/llm/__init__.py +19 -0
  137. atlas/modules/llm/caller.py +287 -0
  138. atlas/modules/llm/litellm_caller.py +675 -0
  139. atlas/modules/llm/models.py +19 -0
  140. atlas/modules/mcp_tools/__init__.py +17 -0
  141. atlas/modules/mcp_tools/client.py +2123 -0
  142. atlas/modules/mcp_tools/token_storage.py +556 -0
  143. atlas/modules/prompts/prompt_provider.py +130 -0
  144. atlas/modules/rag/__init__.py +24 -0
  145. atlas/modules/rag/atlas_rag_client.py +336 -0
  146. atlas/modules/rag/client.py +129 -0
  147. atlas/routes/admin_routes.py +865 -0
  148. atlas/routes/config_routes.py +484 -0
  149. atlas/routes/feedback_routes.py +361 -0
  150. atlas/routes/files_routes.py +274 -0
  151. atlas/routes/health_routes.py +40 -0
  152. atlas/routes/mcp_auth_routes.py +223 -0
  153. atlas/server_cli.py +164 -0
  154. atlas/tests/conftest.py +20 -0
  155. atlas/tests/integration/test_mcp_auth_integration.py +152 -0
  156. atlas/tests/manual_test_sampling.py +87 -0
  157. atlas/tests/modules/mcp_tools/test_client_auth.py +226 -0
  158. atlas/tests/modules/mcp_tools/test_client_env.py +191 -0
  159. atlas/tests/test_admin_mcp_server_management_routes.py +141 -0
  160. atlas/tests/test_agent_roa.py +135 -0
  161. atlas/tests/test_app_factory_smoke.py +47 -0
  162. atlas/tests/test_approval_manager.py +439 -0
  163. atlas/tests/test_atlas_client.py +188 -0
  164. atlas/tests/test_atlas_rag_client.py +447 -0
  165. atlas/tests/test_atlas_rag_integration.py +224 -0
  166. atlas/tests/test_attach_file_flow.py +287 -0
  167. atlas/tests/test_auth_utils.py +165 -0
  168. atlas/tests/test_backend_public_url.py +185 -0
  169. atlas/tests/test_banner_logging.py +287 -0
  170. atlas/tests/test_capability_tokens_and_injection.py +203 -0
  171. atlas/tests/test_compliance_level.py +54 -0
  172. atlas/tests/test_compliance_manager.py +253 -0
  173. atlas/tests/test_config_manager.py +617 -0
  174. atlas/tests/test_config_manager_paths.py +12 -0
  175. atlas/tests/test_core_auth.py +18 -0
  176. atlas/tests/test_core_utils.py +190 -0
  177. atlas/tests/test_docker_env_sync.py +202 -0
  178. atlas/tests/test_domain_errors.py +329 -0
  179. atlas/tests/test_domain_whitelist.py +359 -0
  180. atlas/tests/test_elicitation_manager.py +408 -0
  181. atlas/tests/test_elicitation_routing.py +296 -0
  182. atlas/tests/test_env_demo_server.py +88 -0
  183. atlas/tests/test_error_classification.py +113 -0
  184. atlas/tests/test_error_flow_integration.py +116 -0
  185. atlas/tests/test_feedback_routes.py +333 -0
  186. atlas/tests/test_file_content_extraction.py +1134 -0
  187. atlas/tests/test_file_extraction_routes.py +158 -0
  188. atlas/tests/test_file_library.py +107 -0
  189. atlas/tests/test_file_manager_unit.py +18 -0
  190. atlas/tests/test_health_route.py +49 -0
  191. atlas/tests/test_http_client_stub.py +8 -0
  192. atlas/tests/test_imports_smoke.py +30 -0
  193. atlas/tests/test_interfaces_llm_response.py +9 -0
  194. atlas/tests/test_issue_access_denied_fix.py +136 -0
  195. atlas/tests/test_llm_env_expansion.py +836 -0
  196. atlas/tests/test_log_level_sensitive_data.py +285 -0
  197. atlas/tests/test_mcp_auth_routes.py +341 -0
  198. atlas/tests/test_mcp_client_auth.py +331 -0
  199. atlas/tests/test_mcp_data_injection.py +270 -0
  200. atlas/tests/test_mcp_get_authorized_servers.py +95 -0
  201. atlas/tests/test_mcp_hot_reload.py +512 -0
  202. atlas/tests/test_mcp_image_content.py +424 -0
  203. atlas/tests/test_mcp_logging.py +172 -0
  204. atlas/tests/test_mcp_progress_updates.py +313 -0
  205. atlas/tests/test_mcp_prompt_override_system_prompt.py +102 -0
  206. atlas/tests/test_mcp_prompts_server.py +39 -0
  207. atlas/tests/test_mcp_tool_result_parsing.py +296 -0
  208. atlas/tests/test_metrics_logger.py +56 -0
  209. atlas/tests/test_middleware_auth.py +379 -0
  210. atlas/tests/test_prompt_risk_and_acl.py +141 -0
  211. atlas/tests/test_rag_mcp_aggregator.py +204 -0
  212. atlas/tests/test_rag_mcp_service.py +224 -0
  213. atlas/tests/test_rate_limit_middleware.py +45 -0
  214. atlas/tests/test_routes_config_smoke.py +60 -0
  215. atlas/tests/test_routes_files_download_token.py +41 -0
  216. atlas/tests/test_routes_files_health.py +18 -0
  217. atlas/tests/test_runtime_imports.py +53 -0
  218. atlas/tests/test_sampling_integration.py +482 -0
  219. atlas/tests/test_security_admin_routes.py +61 -0
  220. atlas/tests/test_security_capability_tokens.py +65 -0
  221. atlas/tests/test_security_file_stats_scope.py +21 -0
  222. atlas/tests/test_security_header_injection.py +191 -0
  223. atlas/tests/test_security_headers_and_filename.py +63 -0
  224. atlas/tests/test_shared_session_repository.py +101 -0
  225. atlas/tests/test_system_prompt_loading.py +181 -0
  226. atlas/tests/test_token_storage.py +505 -0
  227. atlas/tests/test_tool_approval_config.py +93 -0
  228. atlas/tests/test_tool_approval_utils.py +356 -0
  229. atlas/tests/test_tool_authorization_group_filtering.py +223 -0
  230. atlas/tests/test_tool_details_in_config.py +108 -0
  231. atlas/tests/test_tool_planner.py +300 -0
  232. atlas/tests/test_unified_rag_service.py +398 -0
  233. atlas/tests/test_username_override_in_approval.py +258 -0
  234. atlas/tests/test_websocket_auth_header.py +168 -0
  235. atlas/version.py +6 -0
  236. atlas_chat-0.1.0.data/data/.env.example +253 -0
  237. atlas_chat-0.1.0.data/data/config/defaults/compliance-levels.json +44 -0
  238. atlas_chat-0.1.0.data/data/config/defaults/domain-whitelist.json +123 -0
  239. atlas_chat-0.1.0.data/data/config/defaults/file-extractors.json +74 -0
  240. atlas_chat-0.1.0.data/data/config/defaults/help-config.json +198 -0
  241. atlas_chat-0.1.0.data/data/config/defaults/llmconfig-buggy.yml +11 -0
  242. atlas_chat-0.1.0.data/data/config/defaults/llmconfig.yml +19 -0
  243. atlas_chat-0.1.0.data/data/config/defaults/mcp.json +138 -0
  244. atlas_chat-0.1.0.data/data/config/defaults/rag-sources.json +17 -0
  245. atlas_chat-0.1.0.data/data/config/defaults/splash-config.json +16 -0
  246. atlas_chat-0.1.0.dist-info/METADATA +236 -0
  247. atlas_chat-0.1.0.dist-info/RECORD +250 -0
  248. atlas_chat-0.1.0.dist-info/WHEEL +5 -0
  249. atlas_chat-0.1.0.dist-info/entry_points.txt +4 -0
  250. atlas_chat-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,231 @@
1
+ """CLI interface for configuration management.
2
+
3
+ This CLI allows you to:
4
+ - Validate configuration files
5
+ - List available models
6
+ - Inspect configuration values
7
+ - Test configuration loading
8
+ """
9
+
10
+ import argparse
11
+ import json
12
+ import logging
13
+ import sys
14
+
15
+ from .config_manager import ConfigManager
16
+
17
+ # Set up logging for CLI
18
+ logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def validate_config(args) -> None:
23
+ """Validate all configuration files."""
24
+ print("šŸ” Validating configuration files...")
25
+
26
+ config_manager = ConfigManager()
27
+ status = config_manager.validate_config()
28
+
29
+ print("\nšŸ“‹ Validation Results:")
30
+ for config_type, is_valid in status.items():
31
+ status_icon = "āœ…" if is_valid else "āŒ"
32
+ print(f" {status_icon} {config_type}: {'Valid' if is_valid else 'Invalid'}")
33
+
34
+ if all(status.values()):
35
+ print("\nšŸŽ‰ All configurations are valid!")
36
+ sys.exit(0)
37
+ else:
38
+ print("\nšŸ’„ Some configurations have issues. Check logs above.")
39
+ sys.exit(1)
40
+
41
+
42
+ def list_models(args) -> None:
43
+ """List all available LLM models."""
44
+ config_manager = ConfigManager()
45
+ llm_config = config_manager.llm_config
46
+
47
+ if not llm_config.models:
48
+ print("āŒ No models configured")
49
+ return
50
+
51
+ print(f"šŸ“š Found {len(llm_config.models)} configured models:\n")
52
+
53
+ for name, model in llm_config.models.items():
54
+ print(f"šŸ¤– {name}")
55
+ print(f" Model: {model.model_name}")
56
+ print(f" URL: {model.model_url}")
57
+ print(f" Max Tokens: {model.max_tokens}")
58
+ print(f" Temperature: {model.temperature}")
59
+ if model.description:
60
+ print(f" Description: {model.description}")
61
+ if model.extra_headers:
62
+ print(f" Extra Headers: {list(model.extra_headers.keys())}")
63
+ print()
64
+
65
+
66
+ def list_servers(args) -> None:
67
+ """List all configured MCP servers."""
68
+ config_manager = ConfigManager()
69
+ mcp_config = config_manager.mcp_config
70
+
71
+ if not mcp_config.servers:
72
+ print("āŒ No MCP servers configured")
73
+ return
74
+
75
+ print(f"šŸ”§ Found {len(mcp_config.servers)} configured MCP servers:\n")
76
+
77
+ for name, server in mcp_config.servers.items():
78
+ print(f"šŸ› ļø {name}")
79
+ print(f" Enabled: {'āœ…' if server.enabled else 'āŒ'}")
80
+ if server.description:
81
+ print(f" Description: {server.description}")
82
+ if server.command:
83
+ print(f" Command: {' '.join(server.command)}")
84
+ if server.url:
85
+ print(f" URL: {server.url}")
86
+ if server.groups:
87
+ print(f" Groups: {', '.join(server.groups)}")
88
+ print()
89
+
90
+
91
+ def inspect_settings(args) -> None:
92
+ """Inspect application settings."""
93
+ config_manager = ConfigManager()
94
+ settings = config_manager.app_settings
95
+
96
+ print("āš™ļø Application Settings:\n")
97
+
98
+ # Group settings by category
99
+ categories = {
100
+ "Application": ["app_name", "port", "debug_mode", "log_level"],
101
+ "Features": [attr for attr in dir(settings) if attr.startswith("feature_")],
102
+ "Banner": ["banner_enabled"],
103
+ "Agent": ["feature_agent_mode_available", "agent_max_steps"],
104
+ "Health": ["llm_health_check_interval", "mcp_health_check_interval"],
105
+ "S3": ["s3_endpoint", "s3_bucket_name", "s3_access_key", "s3_secret_key", "s3_region", "s3_timeout", "s3_use_ssl"],
106
+ "Admin": ["admin_group"]
107
+ }
108
+
109
+ for category, attrs in categories.items():
110
+ print(f"šŸ“‚ {category}:")
111
+ for attr in attrs:
112
+ if hasattr(settings, attr):
113
+ value = getattr(settings, attr)
114
+ # Hide sensitive values
115
+ if "key" in attr.lower() or "password" in attr.lower():
116
+ value = "***" if value else "(not set)"
117
+ print(f" {attr}: {value}")
118
+ print()
119
+
120
+
121
+ def show_config_paths(args) -> None:
122
+ """Show where configuration files are being loaded from."""
123
+ config_manager = ConfigManager()
124
+
125
+ print("šŸ“ Configuration file search paths:\n")
126
+
127
+ # Show search paths for each config type
128
+ config_files = {
129
+ "LLM Config": "llmconfig.yml",
130
+ "MCP Config": "mcp.json"
131
+ }
132
+
133
+ for config_type, filename in config_files.items():
134
+ print(f"šŸ” {config_type} ({filename}):")
135
+ search_paths = config_manager._search_paths(filename)
136
+ for path in search_paths:
137
+ exists = "āœ…" if path.exists() else "āŒ"
138
+ print(f" {exists} {path}")
139
+ print()
140
+
141
+
142
+ def reload_config(args) -> None:
143
+ """Reload configuration from files."""
144
+ config_manager = ConfigManager()
145
+ config_manager.reload_configs()
146
+ print("šŸ”„ Configuration reloaded successfully!")
147
+
148
+
149
+ def export_config(args) -> None:
150
+ """Export current configuration as JSON."""
151
+ config_manager = ConfigManager()
152
+
153
+ config_data = {
154
+ "app_settings": config_manager.app_settings.model_dump(),
155
+ "llm_config": config_manager.llm_config.model_dump(),
156
+ "mcp_config": config_manager.mcp_config.model_dump()
157
+ }
158
+
159
+ if args.output:
160
+ with open(args.output, 'w') as f:
161
+ json.dump(config_data, f, indent=2, default=str)
162
+ print(f"šŸ“„ Configuration exported to {args.output}")
163
+ else:
164
+ print(json.dumps(config_data, indent=2, default=str))
165
+
166
+
167
+ def main():
168
+ """Main CLI entry point."""
169
+ parser = argparse.ArgumentParser(
170
+ description="Configuration management CLI",
171
+ formatter_class=argparse.RawDescriptionHelpFormatter,
172
+ epilog="""
173
+ Examples:
174
+ python -m backend.modules.config.cli validate
175
+ python -m backend.modules.config.cli list-models
176
+ python -m backend.modules.config.cli list-servers
177
+ python -m backend.modules.config.cli inspect
178
+ python -m backend.modules.config.cli paths
179
+ python -m backend.modules.config.cli export --output config.json
180
+ """
181
+ )
182
+
183
+ subparsers = parser.add_subparsers(dest='command', help='Available commands')
184
+
185
+ # Validate command
186
+ validate_parser = subparsers.add_parser('validate', help='Validate all configuration files')
187
+ validate_parser.set_defaults(func=validate_config)
188
+
189
+ # List models command
190
+ list_models_parser = subparsers.add_parser('list-models', help='List all configured LLM models')
191
+ list_models_parser.set_defaults(func=list_models)
192
+
193
+ # List servers command
194
+ list_servers_parser = subparsers.add_parser('list-servers', help='List all configured MCP servers')
195
+ list_servers_parser.set_defaults(func=list_servers)
196
+
197
+ # Inspect settings command
198
+ inspect_parser = subparsers.add_parser('inspect', help='Inspect application settings')
199
+ inspect_parser.set_defaults(func=inspect_settings)
200
+
201
+ # Show paths command
202
+ paths_parser = subparsers.add_parser('paths', help='Show configuration file search paths')
203
+ paths_parser.set_defaults(func=show_config_paths)
204
+
205
+ # Reload command
206
+ reload_parser = subparsers.add_parser('reload', help='Reload configuration from files')
207
+ reload_parser.set_defaults(func=reload_config)
208
+
209
+ # Export command
210
+ export_parser = subparsers.add_parser('export', help='Export current configuration as JSON')
211
+ export_parser.add_argument('--output', '-o', help='Output file (default: stdout)')
212
+ export_parser.set_defaults(func=export_config)
213
+
214
+ args = parser.parse_args()
215
+
216
+ if not args.command:
217
+ parser.print_help()
218
+ return
219
+
220
+ try:
221
+ args.func(args)
222
+ except KeyboardInterrupt:
223
+ print("\nāš ļø Operation cancelled by user")
224
+ sys.exit(1)
225
+ except Exception as e:
226
+ logger.error(f"Error: {e}")
227
+ sys.exit(1)
228
+
229
+
230
+ if __name__ == '__main__':
231
+ main()