mcp-proxy-adapter 6.9.16__py3-none-any.whl → 6.9.18__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.
- mcp_proxy_adapter/api/app.py +57 -55
- mcp_proxy_adapter/api/handlers.py +5 -5
- mcp_proxy_adapter/api/middleware/__init__.py +8 -8
- mcp_proxy_adapter/api/middleware/base.py +14 -14
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +7 -7
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -9
- mcp_proxy_adapter/api/middleware/factory.py +17 -17
- mcp_proxy_adapter/api/middleware/logging.py +6 -6
- mcp_proxy_adapter/api/middleware/performance.py +3 -3
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +19 -19
- mcp_proxy_adapter/api/middleware/transport_middleware.py +3 -3
- mcp_proxy_adapter/api/middleware/unified_security.py +11 -11
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +21 -21
- mcp_proxy_adapter/api/tool_integration.py +3 -2
- mcp_proxy_adapter/api/tools.py +4 -3
- mcp_proxy_adapter/commands/auth_validation_command.py +6 -5
- mcp_proxy_adapter/commands/base.py +10 -10
- mcp_proxy_adapter/commands/builtin_commands.py +6 -6
- mcp_proxy_adapter/commands/catalog_manager.py +74 -74
- mcp_proxy_adapter/commands/cert_monitor_command.py +13 -12
- mcp_proxy_adapter/commands/certificate_management_command.py +20 -19
- mcp_proxy_adapter/commands/command_registry.py +88 -80
- mcp_proxy_adapter/commands/config_command.py +3 -1
- mcp_proxy_adapter/commands/dependency_manager.py +10 -10
- mcp_proxy_adapter/commands/help_command.py +21 -20
- mcp_proxy_adapter/commands/hooks.py +27 -27
- mcp_proxy_adapter/commands/key_management_command.py +19 -18
- mcp_proxy_adapter/commands/plugins_command.py +2 -1
- mcp_proxy_adapter/commands/protocol_management_command.py +6 -6
- mcp_proxy_adapter/commands/proxy_registration_command.py +9 -9
- mcp_proxy_adapter/commands/registration_status_command.py +4 -4
- mcp_proxy_adapter/commands/reload_command.py +5 -5
- mcp_proxy_adapter/commands/role_test_command.py +2 -1
- mcp_proxy_adapter/commands/roles_management_command.py +9 -8
- mcp_proxy_adapter/commands/security_command.py +3 -2
- mcp_proxy_adapter/commands/ssl_setup_command.py +7 -6
- mcp_proxy_adapter/commands/token_management_command.py +12 -11
- mcp_proxy_adapter/commands/transport_management_command.py +2 -2
- mcp_proxy_adapter/config.py +3 -3
- mcp_proxy_adapter/core/__init__.py +1 -1
- mcp_proxy_adapter/core/app_factory.py +1 -1
- mcp_proxy_adapter/core/app_runner.py +3 -3
- mcp_proxy_adapter/core/auth_validator.py +9 -9
- mcp_proxy_adapter/core/certificate_utils.py +27 -27
- mcp_proxy_adapter/core/client_manager.py +13 -13
- mcp_proxy_adapter/core/client_security.py +26 -26
- mcp_proxy_adapter/core/config_converter.py +18 -18
- mcp_proxy_adapter/core/config_validator.py +5 -1
- mcp_proxy_adapter/core/crl_utils.py +22 -22
- mcp_proxy_adapter/core/logging.py +21 -13
- mcp_proxy_adapter/core/mtls_asgi.py +7 -7
- mcp_proxy_adapter/core/mtls_asgi_app.py +9 -9
- mcp_proxy_adapter/core/mtls_proxy.py +9 -9
- mcp_proxy_adapter/core/mtls_server.py +18 -18
- mcp_proxy_adapter/core/protocol_manager.py +29 -29
- mcp_proxy_adapter/core/proxy_registration.py +67 -67
- mcp_proxy_adapter/core/security_adapter.py +18 -18
- mcp_proxy_adapter/core/security_factory.py +16 -16
- mcp_proxy_adapter/core/security_integration.py +6 -6
- mcp_proxy_adapter/core/server_adapter.py +12 -12
- mcp_proxy_adapter/core/server_engine.py +17 -17
- mcp_proxy_adapter/core/signal_handler.py +12 -12
- mcp_proxy_adapter/core/ssl_utils.py +12 -12
- mcp_proxy_adapter/core/transport_manager.py +14 -14
- mcp_proxy_adapter/core/unified_config_adapter.py +6 -6
- mcp_proxy_adapter/core/utils.py +5 -5
- mcp_proxy_adapter/custom_openapi.py +7 -7
- mcp_proxy_adapter/examples/cert_manager_bugfix.py +2 -2
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +6 -5
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +44 -0
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +66 -0
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +64 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +21 -21
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +6 -6
- mcp_proxy_adapter/examples/full_application/main.py +28 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +38 -38
- mcp_proxy_adapter/examples/run_proxy_server.py +20 -10
- mcp_proxy_adapter/examples/test_framework_complete.py +35 -35
- mcp_proxy_adapter/examples/test_mcp_server.py +2 -2
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +386 -0
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +248 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.9.18.dist-info/RECORD +149 -0
- mcp_proxy_adapter-6.9.16.dist-info/RECORD +0 -144
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.9.16.dist-info → mcp_proxy_adapter-6.9.18.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@ from mcp_proxy_adapter.core.errors import (
|
|
16
16
|
NotFoundError,
|
17
17
|
ValidationError,
|
18
18
|
)
|
19
|
-
from mcp_proxy_adapter.core.logging import
|
19
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
20
20
|
|
21
21
|
|
22
22
|
T = TypeVar("T", bound=CommandResult)
|
@@ -131,7 +131,7 @@ class Command(ABC):
|
|
131
131
|
|
132
132
|
# Log warning about invalid parameters
|
133
133
|
if invalid_params:
|
134
|
-
|
134
|
+
get_global_logger().warning(
|
135
135
|
f"Command {self.__class__.__name__} received invalid parameters: {invalid_params}. "
|
136
136
|
f"Allowed parameters: {list(allowed_properties)}"
|
137
137
|
)
|
@@ -168,7 +168,7 @@ class Command(ABC):
|
|
168
168
|
context = kwargs.pop("context", {}) if "context" in kwargs else {}
|
169
169
|
|
170
170
|
try:
|
171
|
-
|
171
|
+
get_global_logger().debug(f"Running command {cls.__name__} with params: {kwargs}")
|
172
172
|
|
173
173
|
# Import registry here to avoid circular imports
|
174
174
|
from mcp_proxy_adapter.commands.command_registry import registry
|
@@ -197,31 +197,31 @@ class Command(ABC):
|
|
197
197
|
# Execute command with validated parameters and context
|
198
198
|
result = await command.execute(**validated_params, context=context)
|
199
199
|
|
200
|
-
|
200
|
+
get_global_logger().debug(f"Command {cls.__name__} executed successfully")
|
201
201
|
return result
|
202
202
|
except ValidationError as e:
|
203
203
|
# Ошибка валидации параметров
|
204
|
-
|
204
|
+
get_global_logger().error(f"Validation error in command {cls.__name__}: {e}")
|
205
205
|
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
206
206
|
except InvalidParamsError as e:
|
207
207
|
# Ошибка в параметрах команды
|
208
|
-
|
208
|
+
get_global_logger().error(f"Invalid parameters error in command {cls.__name__}: {e}")
|
209
209
|
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
210
210
|
except NotFoundError as e:
|
211
211
|
# Ресурс не найден
|
212
|
-
|
212
|
+
get_global_logger().error(f"Resource not found error in command {cls.__name__}: {e}")
|
213
213
|
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
214
214
|
except TimeoutError as e:
|
215
215
|
# Превышено время ожидания
|
216
|
-
|
216
|
+
get_global_logger().error(f"Timeout error in command {cls.__name__}: {e}")
|
217
217
|
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
218
218
|
except CommandError as e:
|
219
219
|
# Ошибка выполнения команды
|
220
|
-
|
220
|
+
get_global_logger().error(f"Command error in {cls.__name__}: {e}")
|
221
221
|
return ErrorResult(message=str(e), code=e.code, details=e.data)
|
222
222
|
except Exception as e:
|
223
223
|
# Непредвиденная ошибка
|
224
|
-
|
224
|
+
get_global_logger().exception(f"Unexpected error executing command {cls.__name__}: {e}")
|
225
225
|
internal_error = InternalError(f"Command execution error: {str(e)}")
|
226
226
|
return ErrorResult(
|
227
227
|
message=internal_error.message,
|
@@ -23,7 +23,7 @@ from mcp_proxy_adapter.commands.proxy_registration_command import (
|
|
23
23
|
)
|
24
24
|
from mcp_proxy_adapter.commands.echo_command import EchoCommand
|
25
25
|
from mcp_proxy_adapter.commands.role_test_command import RoleTestCommand
|
26
|
-
from mcp_proxy_adapter.core.logging import
|
26
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
27
27
|
|
28
28
|
|
29
29
|
def register_builtin_commands() -> int:
|
@@ -33,7 +33,7 @@ def register_builtin_commands() -> int:
|
|
33
33
|
Returns:
|
34
34
|
Number of built-in commands registered.
|
35
35
|
"""
|
36
|
-
|
36
|
+
get_global_logger().debug("Registering built-in framework commands...")
|
37
37
|
|
38
38
|
builtin_commands = [
|
39
39
|
HelpCommand,
|
@@ -63,7 +63,7 @@ def register_builtin_commands() -> int:
|
|
63
63
|
|
64
64
|
# Check if command already exists (should not happen for built-in)
|
65
65
|
if registry.command_exists(command_name):
|
66
|
-
|
66
|
+
get_global_logger().warning(
|
67
67
|
f"Built-in command '{command_name}' already exists, skipping"
|
68
68
|
)
|
69
69
|
continue
|
@@ -71,14 +71,14 @@ def register_builtin_commands() -> int:
|
|
71
71
|
# Register the command
|
72
72
|
registry.register_builtin(command_class)
|
73
73
|
registered_count += 1
|
74
|
-
|
74
|
+
get_global_logger().debug(f"Registered built-in command: {command_name}")
|
75
75
|
|
76
76
|
except Exception as e:
|
77
|
-
|
77
|
+
get_global_logger().error(
|
78
78
|
f"Failed to register built-in command {command_class.__name__}: {e}"
|
79
79
|
)
|
80
80
|
|
81
|
-
|
81
|
+
get_global_logger().info(f"Registered {registered_count} built-in framework commands")
|
82
82
|
return registered_count
|
83
83
|
|
84
84
|
|
@@ -16,7 +16,7 @@ from pathlib import Path
|
|
16
16
|
from typing import Dict, List, Optional, Any
|
17
17
|
from packaging import version as pkg_version
|
18
18
|
|
19
|
-
from mcp_proxy_adapter.core.logging import
|
19
|
+
from mcp_proxy_adapter.core.logging import get_global_logger
|
20
20
|
from mcp_proxy_adapter.commands.dependency_manager import dependency_manager
|
21
21
|
from mcp_proxy_adapter.config import config
|
22
22
|
|
@@ -27,7 +27,7 @@ try:
|
|
27
27
|
REQUESTS_AVAILABLE = True
|
28
28
|
except ImportError:
|
29
29
|
REQUESTS_AVAILABLE = False
|
30
|
-
|
30
|
+
get_global_logger().warning(
|
31
31
|
"requests library not available, HTTP/HTTPS functionality will be limited"
|
32
32
|
)
|
33
33
|
|
@@ -128,13 +128,13 @@ class CatalogManager:
|
|
128
128
|
|
129
129
|
def _load_catalog(self) -> None:
|
130
130
|
"""DEPRECATED: Catalog is not cached locally."""
|
131
|
-
|
131
|
+
get_global_logger().warning(
|
132
132
|
"_load_catalog() is deprecated - catalog is always fetched fresh from servers"
|
133
133
|
)
|
134
134
|
|
135
135
|
def _save_catalog(self) -> None:
|
136
136
|
"""DEPRECATED: Catalog is not cached locally."""
|
137
|
-
|
137
|
+
get_global_logger().warning(
|
138
138
|
"_save_catalog() is deprecated - catalog is always fetched fresh from servers"
|
139
139
|
)
|
140
140
|
|
@@ -152,7 +152,7 @@ class CatalogManager:
|
|
152
152
|
Dictionary of parsed CommandCatalog objects
|
153
153
|
"""
|
154
154
|
if not isinstance(server_catalog, dict):
|
155
|
-
|
155
|
+
get_global_logger().error(
|
156
156
|
f"Invalid catalog format: expected dict, got {type(server_catalog)}"
|
157
157
|
)
|
158
158
|
return {}
|
@@ -165,7 +165,7 @@ class CatalogManager:
|
|
165
165
|
commands_data = server_catalog.get("commands", [])
|
166
166
|
|
167
167
|
if not isinstance(commands_data, list):
|
168
|
-
|
168
|
+
get_global_logger().error(
|
169
169
|
f"Invalid commands format: expected list, got {type(commands_data)}"
|
170
170
|
)
|
171
171
|
return {}
|
@@ -173,24 +173,24 @@ class CatalogManager:
|
|
173
173
|
for cmd_data in commands_data:
|
174
174
|
try:
|
175
175
|
if not isinstance(cmd_data, dict):
|
176
|
-
|
176
|
+
get_global_logger().warning(
|
177
177
|
f"Skipping invalid command data: expected dict, got {type(cmd_data)}"
|
178
178
|
)
|
179
179
|
continue
|
180
180
|
|
181
181
|
name = cmd_data.get("name")
|
182
182
|
if not name or not isinstance(name, str):
|
183
|
-
|
183
|
+
get_global_logger().warning(f"Skipping command without valid name")
|
184
184
|
continue
|
185
185
|
|
186
186
|
version = cmd_data.get("version", "0.1")
|
187
187
|
if not isinstance(version, str):
|
188
|
-
|
188
|
+
get_global_logger().warning(f"Invalid version format for {name}: {version}")
|
189
189
|
version = "0.1"
|
190
190
|
|
191
191
|
source_url = cmd_data.get("source_url", "")
|
192
192
|
if not isinstance(source_url, str):
|
193
|
-
|
193
|
+
get_global_logger().warning(f"Invalid source_url for {name}: {source_url}")
|
194
194
|
source_url = ""
|
195
195
|
|
196
196
|
catalog = CommandCatalog(
|
@@ -208,14 +208,14 @@ class CatalogManager:
|
|
208
208
|
result[name] = catalog
|
209
209
|
|
210
210
|
except Exception as e:
|
211
|
-
|
211
|
+
get_global_logger().error(f"Error processing command data: {e}")
|
212
212
|
continue
|
213
213
|
else:
|
214
214
|
# New format: {"id": {"plugin": "...", "descr": "...", ...}}
|
215
215
|
for command_id, cmd_data in server_catalog.items():
|
216
216
|
try:
|
217
217
|
if not isinstance(cmd_data, dict):
|
218
|
-
|
218
|
+
get_global_logger().warning(
|
219
219
|
f"Skipping invalid command data for {command_id}: expected dict, got {type(cmd_data)}"
|
220
220
|
)
|
221
221
|
continue
|
@@ -223,14 +223,14 @@ class CatalogManager:
|
|
223
223
|
# Use command_id as name if name is not provided
|
224
224
|
name = cmd_data.get("name", command_id)
|
225
225
|
if not isinstance(name, str):
|
226
|
-
|
226
|
+
get_global_logger().warning(
|
227
227
|
f"Skipping command {command_id} without valid name"
|
228
228
|
)
|
229
229
|
continue
|
230
230
|
|
231
231
|
version = cmd_data.get("version", "0.1")
|
232
232
|
if not isinstance(version, str):
|
233
|
-
|
233
|
+
get_global_logger().warning(f"Invalid version format for {name}: {version}")
|
234
234
|
version = "0.1"
|
235
235
|
|
236
236
|
# For new format, construct source_url from server_url and plugin filename
|
@@ -269,7 +269,7 @@ class CatalogManager:
|
|
269
269
|
result[name] = catalog
|
270
270
|
|
271
271
|
except Exception as e:
|
272
|
-
|
272
|
+
get_global_logger().error(f"Error processing command {command_id}: {e}")
|
273
273
|
continue
|
274
274
|
|
275
275
|
return result
|
@@ -285,7 +285,7 @@ class CatalogManager:
|
|
285
285
|
Dictionary of commands available on the server
|
286
286
|
"""
|
287
287
|
if not REQUESTS_AVAILABLE:
|
288
|
-
|
288
|
+
get_global_logger().error(
|
289
289
|
"requests library not available, cannot fetch catalog from remote server"
|
290
290
|
)
|
291
291
|
return {}
|
@@ -293,55 +293,55 @@ class CatalogManager:
|
|
293
293
|
try:
|
294
294
|
# Validate URL format
|
295
295
|
if not server_url.startswith(("http://", "https://")):
|
296
|
-
|
296
|
+
get_global_logger().error(f"Invalid server URL format: {server_url}")
|
297
297
|
return {}
|
298
298
|
|
299
299
|
# Fetch catalog from server (use URL as-is)
|
300
|
-
|
300
|
+
get_global_logger().debug(f"Fetching catalog from: {server_url}")
|
301
301
|
|
302
302
|
response = requests.get(server_url, timeout=30)
|
303
303
|
response.raise_for_status()
|
304
304
|
|
305
305
|
# Validate response content
|
306
306
|
if not response.content:
|
307
|
-
|
307
|
+
get_global_logger().error(f"Empty response from {server_url}")
|
308
308
|
return {}
|
309
309
|
|
310
310
|
try:
|
311
311
|
server_catalog = response.json()
|
312
312
|
except json.JSONDecodeError as e:
|
313
|
-
|
313
|
+
get_global_logger().error(f"Invalid JSON response from {server_url}: {e}")
|
314
314
|
return {}
|
315
315
|
|
316
316
|
if not isinstance(server_catalog, dict):
|
317
|
-
|
317
|
+
get_global_logger().error(
|
318
318
|
f"Invalid catalog format from {server_url}: expected dict, got {type(server_catalog)}"
|
319
319
|
)
|
320
320
|
return {}
|
321
321
|
|
322
322
|
result = self._parse_catalog_data(server_catalog, server_url)
|
323
323
|
|
324
|
-
|
324
|
+
get_global_logger().info(
|
325
325
|
f"Successfully fetched catalog from {server_url}: {len(result)} commands"
|
326
326
|
)
|
327
327
|
return result
|
328
328
|
|
329
329
|
except requests.exceptions.Timeout:
|
330
|
-
|
330
|
+
get_global_logger().error(f"Timeout while fetching catalog from {server_url}")
|
331
331
|
return {}
|
332
332
|
except requests.exceptions.ConnectionError as e:
|
333
|
-
|
333
|
+
get_global_logger().error(
|
334
334
|
f"Connection error while fetching catalog from {server_url}: {e}"
|
335
335
|
)
|
336
336
|
return {}
|
337
337
|
except requests.exceptions.HTTPError as e:
|
338
|
-
|
338
|
+
get_global_logger().error(f"HTTP error while fetching catalog from {server_url}: {e}")
|
339
339
|
return {}
|
340
340
|
except requests.exceptions.RequestException as e:
|
341
|
-
|
341
|
+
get_global_logger().error(f"Request error while fetching catalog from {server_url}: {e}")
|
342
342
|
return {}
|
343
343
|
except Exception as e:
|
344
|
-
|
344
|
+
get_global_logger().error(
|
345
345
|
f"Unexpected error while fetching catalog from {server_url}: {e}"
|
346
346
|
)
|
347
347
|
return {}
|
@@ -367,13 +367,13 @@ class CatalogManager:
|
|
367
367
|
)
|
368
368
|
|
369
369
|
if not all_satisfied:
|
370
|
-
|
370
|
+
get_global_logger().warning(
|
371
371
|
f"Command {command_name} has missing dependencies: {missing_deps}"
|
372
372
|
)
|
373
|
-
|
373
|
+
get_global_logger().info(f"Installed dependencies: {installed_deps}")
|
374
374
|
return False
|
375
375
|
|
376
|
-
|
376
|
+
get_global_logger().debug(
|
377
377
|
f"All dependencies satisfied for command {command_name}: {server_cmd.depends}"
|
378
378
|
)
|
379
379
|
return True
|
@@ -405,22 +405,22 @@ class CatalogManager:
|
|
405
405
|
)
|
406
406
|
|
407
407
|
if all_satisfied:
|
408
|
-
|
408
|
+
get_global_logger().info(
|
409
409
|
f"All dependencies already satisfied for {command_name}: {server_cmd.depends}"
|
410
410
|
)
|
411
411
|
return True
|
412
412
|
|
413
413
|
if not auto_install:
|
414
|
-
|
414
|
+
get_global_logger().warning(
|
415
415
|
f"Command {command_name} has missing dependencies: {missing_deps}"
|
416
416
|
)
|
417
|
-
|
417
|
+
get_global_logger().info(
|
418
418
|
f"Auto-install is disabled. Please install manually: pip install {' '.join(missing_deps)}"
|
419
419
|
)
|
420
420
|
return False
|
421
421
|
|
422
422
|
# Try to install missing dependencies
|
423
|
-
|
423
|
+
get_global_logger().info(
|
424
424
|
f"Installing missing dependencies for {command_name}: {missing_deps}"
|
425
425
|
)
|
426
426
|
|
@@ -429,7 +429,7 @@ class CatalogManager:
|
|
429
429
|
)
|
430
430
|
|
431
431
|
if success:
|
432
|
-
|
432
|
+
get_global_logger().info(
|
433
433
|
f"Successfully installed all dependencies for {command_name}: {installed_deps}"
|
434
434
|
)
|
435
435
|
|
@@ -438,18 +438,18 @@ class CatalogManager:
|
|
438
438
|
server_cmd.depends
|
439
439
|
)
|
440
440
|
if all_verified:
|
441
|
-
|
441
|
+
get_global_logger().info(f"All dependencies verified for {command_name}")
|
442
442
|
return True
|
443
443
|
else:
|
444
|
-
|
444
|
+
get_global_logger().error(
|
445
445
|
f"Failed to verify dependencies for {command_name}: {failed_verifications}"
|
446
446
|
)
|
447
447
|
return False
|
448
448
|
else:
|
449
|
-
|
449
|
+
get_global_logger().error(
|
450
450
|
f"Failed to install dependencies for {command_name}: {failed_deps}"
|
451
451
|
)
|
452
|
-
|
452
|
+
get_global_logger().error(
|
453
453
|
f"Please install manually: pip install {' '.join(failed_deps)}"
|
454
454
|
)
|
455
455
|
return False
|
@@ -471,7 +471,7 @@ class CatalogManager:
|
|
471
471
|
|
472
472
|
# If local file doesn't exist, download
|
473
473
|
if not local_file.exists():
|
474
|
-
|
474
|
+
get_global_logger().info(f"New command found: {command_name} v{server_cmd.version}")
|
475
475
|
return True
|
476
476
|
|
477
477
|
# Try to extract version from local file
|
@@ -484,18 +484,18 @@ class CatalogManager:
|
|
484
484
|
local_ver = pkg_version.parse(local_version)
|
485
485
|
|
486
486
|
if server_ver > local_ver:
|
487
|
-
|
487
|
+
get_global_logger().info(
|
488
488
|
f"Newer version available for {command_name}: {local_ver} -> {server_ver}"
|
489
489
|
)
|
490
490
|
return True
|
491
491
|
else:
|
492
|
-
|
492
|
+
get_global_logger().debug(
|
493
493
|
f"Local version {local_ver} is same or newer than server version {server_ver}"
|
494
494
|
)
|
495
495
|
return False
|
496
496
|
|
497
497
|
except Exception as e:
|
498
|
-
|
498
|
+
get_global_logger().warning(f"Failed to compare versions for {command_name}: {e}")
|
499
499
|
# If version comparison fails, download anyway
|
500
500
|
return True
|
501
501
|
|
@@ -512,7 +512,7 @@ class CatalogManager:
|
|
512
512
|
Returns:
|
513
513
|
True if command was downloaded, False otherwise
|
514
514
|
"""
|
515
|
-
|
515
|
+
get_global_logger().warning(
|
516
516
|
"update_command() is deprecated - always downloading fresh from server (use _download_command directly)"
|
517
517
|
)
|
518
518
|
|
@@ -534,14 +534,14 @@ class CatalogManager:
|
|
534
534
|
True if download successful, False otherwise
|
535
535
|
"""
|
536
536
|
if not REQUESTS_AVAILABLE:
|
537
|
-
|
537
|
+
get_global_logger().error(
|
538
538
|
"requests library not available, cannot download command from remote server"
|
539
539
|
)
|
540
540
|
return False
|
541
541
|
|
542
542
|
# Step 1: Check and install dependencies
|
543
543
|
if not self._install_dependencies(command_name, server_cmd):
|
544
|
-
|
544
|
+
get_global_logger().error(
|
545
545
|
f"Cannot download {command_name}: failed to install dependencies"
|
546
546
|
)
|
547
547
|
return False
|
@@ -549,20 +549,20 @@ class CatalogManager:
|
|
549
549
|
try:
|
550
550
|
# Validate source URL
|
551
551
|
if not server_cmd.source_url.startswith(("http://", "https://")):
|
552
|
-
|
552
|
+
get_global_logger().error(
|
553
553
|
f"Invalid source URL for {command_name}: {server_cmd.source_url}"
|
554
554
|
)
|
555
555
|
return False
|
556
556
|
|
557
557
|
# Download command file (use source_url as-is)
|
558
|
-
|
558
|
+
get_global_logger().debug(f"Downloading {command_name} from: {server_cmd.source_url}")
|
559
559
|
|
560
560
|
response = requests.get(server_cmd.source_url, timeout=30)
|
561
561
|
response.raise_for_status()
|
562
562
|
|
563
563
|
# Validate response content
|
564
564
|
if not response.content:
|
565
|
-
|
565
|
+
get_global_logger().error(
|
566
566
|
f"Empty response when downloading {command_name} from {server_cmd.source_url}"
|
567
567
|
)
|
568
568
|
return False
|
@@ -570,7 +570,7 @@ class CatalogManager:
|
|
570
570
|
# Validate Python file content
|
571
571
|
content = response.text
|
572
572
|
if not content.strip():
|
573
|
-
|
573
|
+
get_global_logger().error(
|
574
574
|
f"Empty file content for {command_name} from {server_cmd.source_url}"
|
575
575
|
)
|
576
576
|
return False
|
@@ -587,7 +587,7 @@ class CatalogManager:
|
|
587
587
|
for keyword in ["class", "def", "import", "from", '"""', "'''", "#"]
|
588
588
|
)
|
589
589
|
):
|
590
|
-
|
590
|
+
get_global_logger().warning(
|
591
591
|
f"File {command_name}_command.py doesn't look like a valid Python file"
|
592
592
|
)
|
593
593
|
|
@@ -616,12 +616,12 @@ class CatalogManager:
|
|
616
616
|
hasattr(module, attr) and attr.endswith("Command")
|
617
617
|
for attr in dir(module)
|
618
618
|
):
|
619
|
-
|
619
|
+
get_global_logger().warning(
|
620
620
|
f"Module {command_name}_command.py doesn't contain a Command class"
|
621
621
|
)
|
622
622
|
|
623
623
|
except Exception as e:
|
624
|
-
|
624
|
+
get_global_logger().error(f"Failed to validate {command_name}_command.py: {e}")
|
625
625
|
return False
|
626
626
|
|
627
627
|
# If validation passed, move to final location
|
@@ -659,15 +659,15 @@ class CatalogManager:
|
|
659
659
|
# Update full metadata
|
660
660
|
server_cmd.metadata.update(metadata)
|
661
661
|
|
662
|
-
|
662
|
+
get_global_logger().debug(
|
663
663
|
f"Extracted metadata for {command_name}: {metadata}"
|
664
664
|
)
|
665
665
|
except Exception as e:
|
666
|
-
|
666
|
+
get_global_logger().warning(
|
667
667
|
f"Failed to extract metadata from {command_name}: {e}"
|
668
668
|
)
|
669
669
|
|
670
|
-
|
670
|
+
get_global_logger().info(
|
671
671
|
f"Successfully downloaded and validated {command_name} v{server_cmd.version}"
|
672
672
|
)
|
673
673
|
return True
|
@@ -678,35 +678,35 @@ class CatalogManager:
|
|
678
678
|
try:
|
679
679
|
os.unlink(temp_file.name)
|
680
680
|
except Exception as e:
|
681
|
-
|
681
|
+
get_global_logger().warning(
|
682
682
|
f"Failed to clean up temporary file {temp_file.name}: {e}"
|
683
683
|
)
|
684
684
|
|
685
685
|
except requests.exceptions.Timeout:
|
686
|
-
|
686
|
+
get_global_logger().error(
|
687
687
|
f"Timeout while downloading {command_name} from {server_cmd.source_url}"
|
688
688
|
)
|
689
689
|
return False
|
690
690
|
except requests.exceptions.ConnectionError as e:
|
691
|
-
|
691
|
+
get_global_logger().error(
|
692
692
|
f"Connection error while downloading {command_name} from {server_cmd.source_url}: {e}"
|
693
693
|
)
|
694
694
|
return False
|
695
695
|
except requests.exceptions.HTTPError as e:
|
696
|
-
|
696
|
+
get_global_logger().error(
|
697
697
|
f"HTTP error while downloading {command_name} from {server_cmd.source_url}: {e}"
|
698
698
|
)
|
699
699
|
return False
|
700
700
|
except requests.exceptions.RequestException as e:
|
701
|
-
|
701
|
+
get_global_logger().error(
|
702
702
|
f"Request error while downloading {command_name} from {server_cmd.source_url}: {e}"
|
703
703
|
)
|
704
704
|
return False
|
705
705
|
except OSError as e:
|
706
|
-
|
706
|
+
get_global_logger().error(f"File system error while downloading {command_name}: {e}")
|
707
707
|
return False
|
708
708
|
except Exception as e:
|
709
|
-
|
709
|
+
get_global_logger().error(f"Unexpected error while downloading {command_name}: {e}")
|
710
710
|
return False
|
711
711
|
|
712
712
|
def sync_with_servers(self, server_urls: List[str]) -> Dict[str, Any]:
|
@@ -719,7 +719,7 @@ class CatalogManager:
|
|
719
719
|
Returns:
|
720
720
|
Dictionary with sync results
|
721
721
|
"""
|
722
|
-
|
722
|
+
get_global_logger().info(f"Loading fresh catalog from {len(server_urls)} servers")
|
723
723
|
|
724
724
|
# Clear local catalog - always start fresh
|
725
725
|
self.catalog = {}
|
@@ -750,16 +750,16 @@ class CatalogManager:
|
|
750
750
|
self.catalog[command_name] = server_cmd
|
751
751
|
else:
|
752
752
|
# Command already exists with same or newer version
|
753
|
-
|
753
|
+
get_global_logger().debug(
|
754
754
|
f"Command {command_name} already exists with same or newer version"
|
755
755
|
)
|
756
756
|
|
757
757
|
except Exception as e:
|
758
758
|
error_msg = f"Failed to sync with {server_url}: {e}"
|
759
759
|
results["errors"].append(error_msg)
|
760
|
-
|
760
|
+
get_global_logger().error(error_msg)
|
761
761
|
|
762
|
-
|
762
|
+
get_global_logger().info(f"Fresh catalog loaded: {results}")
|
763
763
|
return results
|
764
764
|
|
765
765
|
def get_local_commands(self) -> List[str]:
|
@@ -773,7 +773,7 @@ class CatalogManager:
|
|
773
773
|
for file_path in self.commands_dir.glob("*_command.py"):
|
774
774
|
commands.append(str(file_path))
|
775
775
|
|
776
|
-
|
776
|
+
get_global_logger().debug(
|
777
777
|
f"Found {len(commands)} local command files: {[Path(c).name for c in commands]}"
|
778
778
|
)
|
779
779
|
return commands
|
@@ -813,11 +813,11 @@ class CatalogManager:
|
|
813
813
|
del self.catalog[command_name]
|
814
814
|
self._save_catalog()
|
815
815
|
|
816
|
-
|
816
|
+
get_global_logger().info(f"Removed command: {command_name}")
|
817
817
|
return True
|
818
818
|
|
819
819
|
except Exception as e:
|
820
|
-
|
820
|
+
get_global_logger().error(f"Failed to remove command {command_name}: {e}")
|
821
821
|
return False
|
822
822
|
|
823
823
|
def extract_metadata_from_file(self, file_path: str) -> Dict[str, Any]:
|
@@ -909,11 +909,11 @@ class CatalogManager:
|
|
909
909
|
except json.JSONDecodeError:
|
910
910
|
continue
|
911
911
|
|
912
|
-
|
912
|
+
get_global_logger().debug(f"Extracted metadata from {file_path}: {metadata}")
|
913
913
|
return metadata
|
914
914
|
|
915
915
|
except Exception as e:
|
916
|
-
|
916
|
+
get_global_logger().error(f"Failed to extract metadata from {file_path}: {e}")
|
917
917
|
return {}
|
918
918
|
|
919
919
|
def update_local_command_metadata(self, command_name: str) -> bool:
|
@@ -957,11 +957,11 @@ class CatalogManager:
|
|
957
957
|
# Save catalog
|
958
958
|
self._save_catalog()
|
959
959
|
|
960
|
-
|
960
|
+
get_global_logger().info(f"Updated metadata for {command_name}")
|
961
961
|
return True
|
962
962
|
|
963
963
|
return False
|
964
964
|
|
965
965
|
except Exception as e:
|
966
|
-
|
966
|
+
get_global_logger().error(f"Failed to update metadata for {command_name}: {e}")
|
967
967
|
return False
|