claude-mpm 4.1.0__py3-none-any.whl → 4.1.2__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.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +133 -58
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +271 -268
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +192 -141
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +120 -84
- claude_mpm/cli/commands/run_config_checker.py +4 -5
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +204 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +20 -23
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +581 -280
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -15
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
- claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +10 -6
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
- claude_mpm-4.1.2.dist-info/RECORD +498 -0
- claude_mpm-4.1.0.dist-info/RECORD +0 -494
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -10,14 +10,12 @@ to create focused, maintainable modules.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import asyncio
|
|
13
|
-
import logging
|
|
14
|
-
import os
|
|
15
13
|
import threading
|
|
16
14
|
import time
|
|
17
15
|
from collections import deque
|
|
18
16
|
from datetime import datetime
|
|
19
17
|
from pathlib import Path
|
|
20
|
-
from typing import Any, Dict,
|
|
18
|
+
from typing import Any, Dict, Set
|
|
21
19
|
|
|
22
20
|
try:
|
|
23
21
|
import aiohttp
|
|
@@ -32,16 +30,15 @@ except ImportError:
|
|
|
32
30
|
web = None
|
|
33
31
|
|
|
34
32
|
# Import VersionService for dynamic version retrieval
|
|
33
|
+
import contextlib
|
|
34
|
+
|
|
35
35
|
from claude_mpm.services.version_service import VersionService
|
|
36
36
|
|
|
37
37
|
from ....core.constants import (
|
|
38
|
-
NetworkConfig,
|
|
39
|
-
PerformanceConfig,
|
|
40
38
|
SystemLimits,
|
|
41
39
|
TimeoutConfig,
|
|
42
40
|
)
|
|
43
|
-
from ....core.
|
|
44
|
-
from ....core.logging_config import get_logger, log_operation, log_performance_context
|
|
41
|
+
from ....core.logging_config import get_logger
|
|
45
42
|
from ....core.unified_paths import get_project_root, get_scripts_dir
|
|
46
43
|
from ...exceptions import SocketIOServerError as MPMConnectionError
|
|
47
44
|
|
|
@@ -87,7 +84,7 @@ class SocketIOServerCore:
|
|
|
87
84
|
|
|
88
85
|
# Static files path
|
|
89
86
|
self.static_path = None
|
|
90
|
-
|
|
87
|
+
|
|
91
88
|
# Heartbeat task
|
|
92
89
|
self.heartbeat_task = None
|
|
93
90
|
self.heartbeat_interval = 60 # seconds
|
|
@@ -145,7 +142,7 @@ class SocketIOServerCore:
|
|
|
145
142
|
# condition window where other threads might try to access it.
|
|
146
143
|
self.loop = asyncio.new_event_loop()
|
|
147
144
|
asyncio.set_event_loop(self.loop)
|
|
148
|
-
|
|
145
|
+
|
|
149
146
|
self.logger.debug("Event loop created and set for background thread")
|
|
150
147
|
|
|
151
148
|
# Run the server
|
|
@@ -167,7 +164,7 @@ class SocketIOServerCore:
|
|
|
167
164
|
logger=False, # Disable Socket.IO's own logging
|
|
168
165
|
engineio_logger=False,
|
|
169
166
|
ping_interval=25, # Send ping every 25 seconds
|
|
170
|
-
ping_timeout=60,
|
|
167
|
+
ping_timeout=60, # Wait 60 seconds for pong response
|
|
171
168
|
max_http_buffer_size=1e8, # 100MB max buffer
|
|
172
169
|
)
|
|
173
170
|
|
|
@@ -215,10 +212,8 @@ class SocketIOServerCore:
|
|
|
215
212
|
# Cancel heartbeat task
|
|
216
213
|
if self.heartbeat_task and not self.heartbeat_task.done():
|
|
217
214
|
self.heartbeat_task.cancel()
|
|
218
|
-
|
|
215
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
219
216
|
await self.heartbeat_task
|
|
220
|
-
except asyncio.CancelledError:
|
|
221
|
-
pass
|
|
222
217
|
self.logger.info("Stopped system heartbeat task")
|
|
223
218
|
|
|
224
219
|
if self.site:
|
|
@@ -240,43 +235,48 @@ class SocketIOServerCore:
|
|
|
240
235
|
# Add debug logging for deployment context
|
|
241
236
|
try:
|
|
242
237
|
from ....core.unified_paths import PathContext
|
|
238
|
+
|
|
243
239
|
deployment_context = PathContext.detect_deployment_context()
|
|
244
|
-
self.logger.debug(
|
|
240
|
+
self.logger.debug(
|
|
241
|
+
f"Setting up static files in {deployment_context.value} mode"
|
|
242
|
+
)
|
|
245
243
|
except Exception as e:
|
|
246
244
|
self.logger.debug(f"Could not detect deployment context: {e}")
|
|
247
|
-
|
|
245
|
+
|
|
248
246
|
self.dashboard_path = self._find_static_path()
|
|
249
247
|
|
|
250
248
|
if self.dashboard_path and self.dashboard_path.exists():
|
|
251
249
|
self.logger.info(f"✅ Dashboard found at: {self.dashboard_path}")
|
|
252
|
-
|
|
250
|
+
|
|
253
251
|
# Serve index.html at root
|
|
254
252
|
async def index_handler(request):
|
|
255
253
|
index_file = self.dashboard_path / "index.html"
|
|
256
254
|
if index_file.exists():
|
|
257
255
|
self.logger.debug(f"Serving dashboard index from: {index_file}")
|
|
258
256
|
return web.FileResponse(index_file)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
self.logger.warning(
|
|
258
|
+
f"Dashboard index.html not found at: {index_file}"
|
|
259
|
+
)
|
|
260
|
+
return web.Response(text="Dashboard not available", status=404)
|
|
262
261
|
|
|
263
262
|
self.app.router.add_get("/", index_handler)
|
|
264
|
-
|
|
263
|
+
|
|
265
264
|
# Serve version.json from dashboard directory
|
|
266
265
|
async def version_handler(request):
|
|
267
266
|
version_file = self.dashboard_path / "version.json"
|
|
268
267
|
if version_file.exists():
|
|
269
268
|
self.logger.debug(f"Serving version.json from: {version_file}")
|
|
270
269
|
return web.FileResponse(version_file)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
270
|
+
# Return default version info if file doesn't exist
|
|
271
|
+
return web.json_response(
|
|
272
|
+
{
|
|
274
273
|
"version": "1.0.0",
|
|
275
274
|
"build": 1,
|
|
276
275
|
"formatted_build": "0001",
|
|
277
|
-
"full_version": "v1.0.0-0001"
|
|
278
|
-
}
|
|
279
|
-
|
|
276
|
+
"full_version": "v1.0.0-0001",
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
|
|
280
280
|
self.app.router.add_get("/version.json", version_handler)
|
|
281
281
|
|
|
282
282
|
# Serve static assets (CSS, JS) from the dashboard static directory
|
|
@@ -287,12 +287,17 @@ class SocketIOServerCore:
|
|
|
287
287
|
self.app.router.add_static(
|
|
288
288
|
"/static/", dashboard_static_path, name="dashboard_static"
|
|
289
289
|
)
|
|
290
|
-
self.logger.info(
|
|
290
|
+
self.logger.info(
|
|
291
|
+
f"✅ Static assets available at: {dashboard_static_path}"
|
|
292
|
+
)
|
|
291
293
|
else:
|
|
292
|
-
self.logger.warning(
|
|
294
|
+
self.logger.warning(
|
|
295
|
+
f"⚠️ Static assets directory not found at: {dashboard_static_path}"
|
|
296
|
+
)
|
|
293
297
|
|
|
294
298
|
else:
|
|
295
299
|
self.logger.warning("⚠️ No dashboard found, serving fallback response")
|
|
300
|
+
|
|
296
301
|
# Fallback handler
|
|
297
302
|
async def fallback_handler(request):
|
|
298
303
|
return web.Response(
|
|
@@ -301,18 +306,20 @@ class SocketIOServerCore:
|
|
|
301
306
|
)
|
|
302
307
|
|
|
303
308
|
self.app.router.add_get("/", fallback_handler)
|
|
304
|
-
|
|
309
|
+
|
|
305
310
|
except Exception as e:
|
|
306
311
|
self.logger.error(f"❌ Error setting up static files: {e}")
|
|
307
312
|
import traceback
|
|
313
|
+
|
|
308
314
|
self.logger.debug(f"Static file setup traceback: {traceback.format_exc()}")
|
|
309
|
-
|
|
315
|
+
|
|
310
316
|
# Ensure we always have a basic handler
|
|
311
317
|
async def error_handler(request):
|
|
312
318
|
return web.Response(
|
|
313
319
|
text="Socket.IO server running - Static files unavailable",
|
|
314
320
|
status=200,
|
|
315
321
|
)
|
|
322
|
+
|
|
316
323
|
self.app.router.add_get("/", error_handler)
|
|
317
324
|
|
|
318
325
|
def _find_static_path(self):
|
|
@@ -324,16 +331,17 @@ class SocketIOServerCore:
|
|
|
324
331
|
# Get deployment-context-aware paths
|
|
325
332
|
try:
|
|
326
333
|
from ....core.unified_paths import get_path_manager
|
|
334
|
+
|
|
327
335
|
path_manager = get_path_manager()
|
|
328
|
-
|
|
336
|
+
|
|
329
337
|
# Use package root for installed packages (including pipx)
|
|
330
338
|
package_root = path_manager.package_root
|
|
331
339
|
self.logger.debug(f"Package root: {package_root}")
|
|
332
|
-
|
|
340
|
+
|
|
333
341
|
# Use project root for development
|
|
334
342
|
project_root = get_project_root()
|
|
335
343
|
self.logger.debug(f"Project root: {project_root}")
|
|
336
|
-
|
|
344
|
+
|
|
337
345
|
except Exception as e:
|
|
338
346
|
self.logger.debug(f"Could not get path manager: {e}")
|
|
339
347
|
package_root = None
|
|
@@ -345,7 +353,6 @@ class SocketIOServerCore:
|
|
|
345
353
|
package_root / "dashboard" / "templates" if package_root else None,
|
|
346
354
|
package_root / "services" / "socketio" / "static" if package_root else None,
|
|
347
355
|
package_root / "static" if package_root else None,
|
|
348
|
-
|
|
349
356
|
# Project-based paths (for development)
|
|
350
357
|
project_root / "src" / "claude_mpm" / "dashboard" / "templates",
|
|
351
358
|
project_root / "dashboard" / "templates",
|
|
@@ -353,15 +360,12 @@ class SocketIOServerCore:
|
|
|
353
360
|
project_root / "src" / "claude_mpm" / "services" / "socketio" / "static",
|
|
354
361
|
project_root / "static",
|
|
355
362
|
project_root / "src" / "static",
|
|
356
|
-
|
|
357
363
|
# Package installation locations (fallback)
|
|
358
364
|
Path(__file__).parent.parent / "static",
|
|
359
365
|
Path(__file__).parent / "static",
|
|
360
|
-
|
|
361
366
|
# Scripts directory (for standalone installations)
|
|
362
367
|
get_scripts_dir() / "static",
|
|
363
368
|
get_scripts_dir() / "socketio" / "static",
|
|
364
|
-
|
|
365
369
|
# Current working directory
|
|
366
370
|
Path.cwd() / "static",
|
|
367
371
|
Path.cwd() / "socketio" / "static",
|
|
@@ -369,7 +373,9 @@ class SocketIOServerCore:
|
|
|
369
373
|
|
|
370
374
|
# Filter out None values
|
|
371
375
|
possible_paths = [p for p in possible_paths if p is not None]
|
|
372
|
-
self.logger.debug(
|
|
376
|
+
self.logger.debug(
|
|
377
|
+
f"Searching {len(possible_paths)} possible static file locations"
|
|
378
|
+
)
|
|
373
379
|
|
|
374
380
|
for path in possible_paths:
|
|
375
381
|
self.logger.debug(f"Checking for static files at: {path}")
|
|
@@ -379,14 +385,15 @@ class SocketIOServerCore:
|
|
|
379
385
|
if (path / "index.html").exists():
|
|
380
386
|
self.logger.info(f"✅ Found static files at: {path}")
|
|
381
387
|
return path
|
|
382
|
-
|
|
383
|
-
self.logger.debug(f"Directory exists but no index.html: {path}")
|
|
388
|
+
self.logger.debug(f"Directory exists but no index.html: {path}")
|
|
384
389
|
else:
|
|
385
390
|
self.logger.debug(f"Path does not exist: {path}")
|
|
386
391
|
except Exception as e:
|
|
387
392
|
self.logger.debug(f"Error checking path {path}: {e}")
|
|
388
393
|
|
|
389
|
-
self.logger.warning(
|
|
394
|
+
self.logger.warning(
|
|
395
|
+
"⚠️ Static files not found - dashboard will not be available"
|
|
396
|
+
)
|
|
390
397
|
self.logger.debug(f"Searched paths: {[str(p) for p in possible_paths]}")
|
|
391
398
|
return None
|
|
392
399
|
|
|
@@ -409,10 +416,10 @@ class SocketIOServerCore:
|
|
|
409
416
|
True if server is active
|
|
410
417
|
"""
|
|
411
418
|
return self.running
|
|
412
|
-
|
|
419
|
+
|
|
413
420
|
async def _heartbeat_loop(self):
|
|
414
421
|
"""Send periodic heartbeat events to connected clients.
|
|
415
|
-
|
|
422
|
+
|
|
416
423
|
WHY: This provides a way to verify the event flow is working and
|
|
417
424
|
track server health and active sessions without relying on hook events.
|
|
418
425
|
"""
|
|
@@ -420,23 +427,27 @@ class SocketIOServerCore:
|
|
|
420
427
|
try:
|
|
421
428
|
# Wait for the interval
|
|
422
429
|
await asyncio.sleep(self.heartbeat_interval)
|
|
423
|
-
|
|
430
|
+
|
|
424
431
|
if not self.sio:
|
|
425
432
|
continue
|
|
426
|
-
|
|
433
|
+
|
|
427
434
|
# Calculate uptime
|
|
428
435
|
uptime_seconds = 0
|
|
429
436
|
if self.stats.get("start_time"):
|
|
430
|
-
uptime_seconds = int(
|
|
431
|
-
|
|
437
|
+
uptime_seconds = int(
|
|
438
|
+
(datetime.now() - self.stats["start_time"]).total_seconds()
|
|
439
|
+
)
|
|
440
|
+
|
|
432
441
|
# Get active sessions from main server if available
|
|
433
442
|
active_sessions = []
|
|
434
|
-
if self.main_server and hasattr(
|
|
443
|
+
if self.main_server and hasattr(
|
|
444
|
+
self.main_server, "get_active_sessions"
|
|
445
|
+
):
|
|
435
446
|
try:
|
|
436
447
|
active_sessions = self.main_server.get_active_sessions()
|
|
437
448
|
except Exception as e:
|
|
438
449
|
self.logger.debug(f"Could not get active sessions: {e}")
|
|
439
|
-
|
|
450
|
+
|
|
440
451
|
# Prepare heartbeat data (using new schema)
|
|
441
452
|
heartbeat_data = {
|
|
442
453
|
"type": "system",
|
|
@@ -454,20 +465,20 @@ class SocketIOServerCore:
|
|
|
454
465
|
},
|
|
455
466
|
},
|
|
456
467
|
}
|
|
457
|
-
|
|
468
|
+
|
|
458
469
|
# Add to event history if main server is available
|
|
459
|
-
if self.main_server and hasattr(self.main_server,
|
|
470
|
+
if self.main_server and hasattr(self.main_server, "event_history"):
|
|
460
471
|
self.main_server.event_history.append(heartbeat_data)
|
|
461
|
-
|
|
472
|
+
|
|
462
473
|
# Emit heartbeat to all connected clients (already using new schema)
|
|
463
474
|
await self.sio.emit("system_event", heartbeat_data)
|
|
464
|
-
|
|
475
|
+
|
|
465
476
|
self.logger.info(
|
|
466
477
|
f"System heartbeat sent - clients: {len(self.connected_clients)}, "
|
|
467
478
|
f"uptime: {uptime_seconds}s, events: {self.stats.get('events_sent', 0)}, "
|
|
468
479
|
f"sessions: {len(active_sessions)}"
|
|
469
480
|
)
|
|
470
|
-
|
|
481
|
+
|
|
471
482
|
except asyncio.CancelledError:
|
|
472
483
|
# Task was cancelled, exit gracefully
|
|
473
484
|
break
|
|
@@ -7,7 +7,6 @@ WHY this integration module:
|
|
|
7
7
|
- Provides clean separation of concerns
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
import asyncio
|
|
11
10
|
import logging
|
|
12
11
|
from typing import Optional
|
|
13
12
|
|
|
@@ -19,17 +18,17 @@ logger = logging.getLogger(__name__)
|
|
|
19
18
|
|
|
20
19
|
class EventBusIntegration:
|
|
21
20
|
"""Integrates EventBus relay with Socket.IO server.
|
|
22
|
-
|
|
21
|
+
|
|
23
22
|
WHY integration class:
|
|
24
23
|
- Encapsulates EventBus setup and teardown
|
|
25
24
|
- Provides lifecycle management for relay
|
|
26
25
|
- Handles configuration and error cases
|
|
27
26
|
- Can be easily added to existing server
|
|
28
27
|
"""
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
def __init__(self, server_instance=None):
|
|
31
30
|
"""Initialize EventBus integration.
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
Args:
|
|
34
33
|
server_instance: Optional Socket.IO server instance
|
|
35
34
|
"""
|
|
@@ -38,43 +37,78 @@ class EventBusIntegration:
|
|
|
38
37
|
self.event_bus: Optional[EventBus] = None
|
|
39
38
|
self.config = get_config()
|
|
40
39
|
self.enabled = self.config.enabled and self.config.relay_enabled
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
def setup(self, port: Optional[int] = None) -> bool:
|
|
43
42
|
"""Set up EventBus and relay.
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
Args:
|
|
46
45
|
port: Optional Socket.IO server port
|
|
47
|
-
|
|
46
|
+
|
|
48
47
|
Returns:
|
|
49
48
|
bool: True if setup successful
|
|
50
49
|
"""
|
|
50
|
+
from datetime import datetime
|
|
51
|
+
|
|
52
|
+
print(
|
|
53
|
+
f"[{datetime.now().isoformat()}] EventBusIntegration.setup() called",
|
|
54
|
+
flush=True,
|
|
55
|
+
)
|
|
56
|
+
|
|
51
57
|
if not self.enabled:
|
|
52
58
|
logger.info("EventBus integration disabled by configuration")
|
|
59
|
+
print(
|
|
60
|
+
f"[{datetime.now().isoformat()}] EventBus integration disabled by configuration",
|
|
61
|
+
flush=True,
|
|
62
|
+
)
|
|
53
63
|
return False
|
|
54
|
-
|
|
64
|
+
|
|
55
65
|
try:
|
|
56
66
|
# Get EventBus instance
|
|
67
|
+
print(
|
|
68
|
+
f"[{datetime.now().isoformat()}] Getting EventBus instance...",
|
|
69
|
+
flush=True,
|
|
70
|
+
)
|
|
57
71
|
self.event_bus = EventBus.get_instance()
|
|
58
|
-
|
|
72
|
+
print(
|
|
73
|
+
f"[{datetime.now().isoformat()}] EventBus instance obtained", flush=True
|
|
74
|
+
)
|
|
75
|
+
|
|
59
76
|
# Apply configuration
|
|
60
77
|
self.config.apply_to_eventbus(self.event_bus)
|
|
61
|
-
|
|
78
|
+
|
|
62
79
|
# Create and configure relay
|
|
63
80
|
relay_port = port or self.config.relay_port
|
|
81
|
+
print(
|
|
82
|
+
f"[{datetime.now().isoformat()}] Creating SocketIORelay on port {relay_port}...",
|
|
83
|
+
flush=True,
|
|
84
|
+
)
|
|
64
85
|
self.relay = SocketIORelay(relay_port)
|
|
65
86
|
self.config.apply_to_relay(self.relay)
|
|
66
|
-
|
|
87
|
+
|
|
67
88
|
# Start the relay
|
|
89
|
+
print(f"[{datetime.now().isoformat()}] Starting relay...", flush=True)
|
|
68
90
|
self.relay.start()
|
|
69
|
-
|
|
91
|
+
print(f"[{datetime.now().isoformat()}] Relay started", flush=True)
|
|
92
|
+
|
|
70
93
|
logger.info(f"EventBus integration setup complete (port: {relay_port})")
|
|
94
|
+
print(
|
|
95
|
+
f"[{datetime.now().isoformat()}] EventBus integration setup complete",
|
|
96
|
+
flush=True,
|
|
97
|
+
)
|
|
71
98
|
return True
|
|
72
|
-
|
|
99
|
+
|
|
73
100
|
except Exception as e:
|
|
74
101
|
logger.error(f"Failed to setup EventBus integration: {e}")
|
|
102
|
+
print(
|
|
103
|
+
f"[{datetime.now().isoformat()}] Failed to setup EventBus integration: {e}",
|
|
104
|
+
flush=True,
|
|
105
|
+
)
|
|
106
|
+
import traceback
|
|
107
|
+
|
|
108
|
+
traceback.print_exc()
|
|
75
109
|
self.enabled = False
|
|
76
110
|
return False
|
|
77
|
-
|
|
111
|
+
|
|
78
112
|
def teardown(self) -> None:
|
|
79
113
|
"""Tear down EventBus integration."""
|
|
80
114
|
if self.relay:
|
|
@@ -85,105 +119,110 @@ class EventBusIntegration:
|
|
|
85
119
|
logger.error(f"Error stopping relay: {e}")
|
|
86
120
|
finally:
|
|
87
121
|
self.relay = None
|
|
88
|
-
|
|
122
|
+
|
|
89
123
|
def is_active(self) -> bool:
|
|
90
124
|
"""Check if integration is active.
|
|
91
|
-
|
|
125
|
+
|
|
92
126
|
Returns:
|
|
93
127
|
bool: True if relay is active and connected
|
|
94
128
|
"""
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
self.relay is not None and
|
|
98
|
-
self.relay.enabled
|
|
99
|
-
)
|
|
100
|
-
|
|
129
|
+
return self.enabled and self.relay is not None and self.relay.enabled
|
|
130
|
+
|
|
101
131
|
def get_stats(self) -> dict:
|
|
102
132
|
"""Get integration statistics.
|
|
103
|
-
|
|
133
|
+
|
|
104
134
|
Returns:
|
|
105
135
|
dict: Combined stats from EventBus and relay
|
|
106
136
|
"""
|
|
107
|
-
stats = {
|
|
108
|
-
|
|
109
|
-
"active": self.is_active()
|
|
110
|
-
}
|
|
111
|
-
|
|
137
|
+
stats = {"enabled": self.enabled, "active": self.is_active()}
|
|
138
|
+
|
|
112
139
|
if self.event_bus:
|
|
113
140
|
stats["eventbus"] = self.event_bus.get_stats()
|
|
114
|
-
|
|
141
|
+
|
|
115
142
|
if self.relay:
|
|
116
143
|
stats["relay"] = self.relay.get_stats()
|
|
117
|
-
|
|
144
|
+
|
|
118
145
|
return stats
|
|
119
146
|
|
|
120
147
|
|
|
121
|
-
def integrate_with_server(
|
|
148
|
+
def integrate_with_server(
|
|
149
|
+
server_instance, port: Optional[int] = None
|
|
150
|
+
) -> EventBusIntegration:
|
|
122
151
|
"""Helper function to integrate EventBus with a Socket.IO server.
|
|
123
|
-
|
|
152
|
+
|
|
124
153
|
Args:
|
|
125
154
|
server_instance: Socket.IO server instance
|
|
126
155
|
port: Optional server port
|
|
127
|
-
|
|
156
|
+
|
|
128
157
|
Returns:
|
|
129
158
|
EventBusIntegration: The integration instance
|
|
130
159
|
"""
|
|
131
160
|
integration = EventBusIntegration(server_instance)
|
|
132
|
-
integration.setup(port or getattr(server_instance,
|
|
161
|
+
integration.setup(port or getattr(server_instance, "port", 8765))
|
|
133
162
|
return integration
|
|
134
163
|
|
|
135
164
|
|
|
136
165
|
# Monkey-patch helper for existing server
|
|
137
166
|
def patch_socketio_server(server_class):
|
|
138
167
|
"""Monkey-patch an existing Socket.IO server class to add EventBus.
|
|
139
|
-
|
|
168
|
+
|
|
140
169
|
WHY monkey-patching:
|
|
141
170
|
- Allows integration without modifying existing code
|
|
142
171
|
- Can be applied selectively based on configuration
|
|
143
172
|
- Easy to remove or disable
|
|
144
|
-
|
|
173
|
+
|
|
145
174
|
Args:
|
|
146
175
|
server_class: The server class to patch
|
|
147
176
|
"""
|
|
148
177
|
original_init = server_class.__init__
|
|
149
|
-
original_start = getattr(server_class,
|
|
150
|
-
|
|
151
|
-
|
|
178
|
+
original_start = getattr(server_class, "start_sync", None) or getattr(
|
|
179
|
+
server_class, "start", None
|
|
180
|
+
)
|
|
181
|
+
original_stop = getattr(server_class, "stop_sync", None) or getattr(
|
|
182
|
+
server_class, "stop", None
|
|
183
|
+
)
|
|
184
|
+
|
|
152
185
|
def patched_init(self, *args, **kwargs):
|
|
153
186
|
"""Patched __init__ that adds EventBus integration."""
|
|
154
187
|
original_init(self, *args, **kwargs)
|
|
155
188
|
self._eventbus_integration = EventBusIntegration(self)
|
|
156
|
-
|
|
189
|
+
|
|
157
190
|
def patched_start(self, *args, **kwargs):
|
|
158
191
|
"""Patched start method that sets up EventBus."""
|
|
159
192
|
# Call original start
|
|
160
|
-
if original_start
|
|
161
|
-
|
|
162
|
-
else:
|
|
163
|
-
result = None
|
|
164
|
-
|
|
193
|
+
result = original_start(self, *args, **kwargs) if original_start else None
|
|
194
|
+
|
|
165
195
|
# Setup EventBus integration
|
|
166
|
-
if hasattr(self,
|
|
167
|
-
port = getattr(self,
|
|
196
|
+
if hasattr(self, "_eventbus_integration"):
|
|
197
|
+
port = getattr(self, "port", 8765)
|
|
168
198
|
self._eventbus_integration.setup(port)
|
|
169
|
-
|
|
199
|
+
|
|
170
200
|
return result
|
|
171
|
-
|
|
201
|
+
|
|
172
202
|
def patched_stop(self, *args, **kwargs):
|
|
173
203
|
"""Patched stop method that tears down EventBus."""
|
|
174
204
|
# Teardown EventBus first
|
|
175
|
-
if hasattr(self,
|
|
205
|
+
if hasattr(self, "_eventbus_integration"):
|
|
176
206
|
self._eventbus_integration.teardown()
|
|
177
|
-
|
|
207
|
+
|
|
178
208
|
# Call original stop
|
|
179
209
|
if original_stop:
|
|
180
210
|
return original_stop(self, *args, **kwargs)
|
|
181
|
-
|
|
211
|
+
return None
|
|
212
|
+
|
|
182
213
|
# Apply patches
|
|
183
214
|
server_class.__init__ = patched_init
|
|
184
215
|
if original_start:
|
|
185
|
-
setattr(
|
|
216
|
+
setattr(
|
|
217
|
+
server_class,
|
|
218
|
+
"start_sync" if hasattr(server_class, "start_sync") else "start",
|
|
219
|
+
patched_start,
|
|
220
|
+
)
|
|
186
221
|
if original_stop:
|
|
187
|
-
setattr(
|
|
188
|
-
|
|
189
|
-
|
|
222
|
+
setattr(
|
|
223
|
+
server_class,
|
|
224
|
+
"stop_sync" if hasattr(server_class, "stop_sync") else "stop",
|
|
225
|
+
patched_stop,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
logger.info(f"Patched {server_class.__name__} with EventBus integration")
|