crackerjack 0.18.2__py3-none-any.whl → 0.45.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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""WebSocket endpoints for metrics streaming.
|
|
2
|
+
|
|
3
|
+
This module handles real-time metrics streaming, historical data,
|
|
4
|
+
alerts, and dashboard overview WebSocket connections.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import typing as t
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
from acb.depends import depends
|
|
13
|
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
|
14
|
+
|
|
15
|
+
from crackerjack.events import WorkflowEventTelemetry
|
|
16
|
+
from crackerjack.services.quality.quality_baseline_enhanced import (
|
|
17
|
+
EnhancedQualityBaselineService,
|
|
18
|
+
TrendDirection,
|
|
19
|
+
UnifiedMetrics,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from ..metrics import get_monitoring_current_metrics
|
|
23
|
+
from ..websocket_manager import MonitoringWebSocketManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def register_metrics_websockets(
|
|
27
|
+
app: FastAPI,
|
|
28
|
+
job_manager: t.Any,
|
|
29
|
+
ws_manager: MonitoringWebSocketManager,
|
|
30
|
+
quality_service: EnhancedQualityBaselineService,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Register metrics-related WebSocket endpoints."""
|
|
33
|
+
|
|
34
|
+
@app.websocket("/ws/metrics/live")
|
|
35
|
+
async def websocket_metrics_live(websocket: WebSocket) -> None:
|
|
36
|
+
"""WebSocket endpoint for live metrics streaming."""
|
|
37
|
+
await _handle_live_metrics_websocket(
|
|
38
|
+
websocket, ws_manager, quality_service, job_manager
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@app.websocket("/ws/metrics/historical/{days}")
|
|
42
|
+
async def websocket_metrics_historical(websocket: WebSocket, days: int) -> None:
|
|
43
|
+
"""WebSocket endpoint for historical metrics data."""
|
|
44
|
+
await _handle_historical_metrics_websocket(
|
|
45
|
+
websocket, ws_manager, quality_service, days
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@app.websocket("/ws/alerts/subscribe")
|
|
49
|
+
async def websocket_alerts_subscribe(websocket: WebSocket) -> None:
|
|
50
|
+
"""WebSocket endpoint for alert subscriptions."""
|
|
51
|
+
await _handle_alerts_websocket(websocket, ws_manager)
|
|
52
|
+
|
|
53
|
+
@app.websocket("/ws/dashboard/overview")
|
|
54
|
+
async def websocket_dashboard_overview(websocket: WebSocket) -> None:
|
|
55
|
+
"""WebSocket endpoint for comprehensive dashboard data."""
|
|
56
|
+
await _handle_dashboard_websocket(
|
|
57
|
+
websocket, ws_manager, quality_service, job_manager
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def _handle_live_metrics_websocket(
|
|
62
|
+
websocket: WebSocket,
|
|
63
|
+
ws_manager: MonitoringWebSocketManager,
|
|
64
|
+
quality_service: EnhancedQualityBaselineService,
|
|
65
|
+
job_manager: t.Any,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Handle live metrics WebSocket connection."""
|
|
68
|
+
client_id = f"metrics_{datetime.now().timestamp()}"
|
|
69
|
+
await ws_manager.connect_metrics(websocket, client_id)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
# Send initial metrics
|
|
73
|
+
current_metrics = await get_monitoring_current_metrics(
|
|
74
|
+
quality_service, job_manager
|
|
75
|
+
)
|
|
76
|
+
await websocket.send_text(
|
|
77
|
+
json.dumps(
|
|
78
|
+
{
|
|
79
|
+
"type": "initial_metrics",
|
|
80
|
+
"data": current_metrics.to_dict(),
|
|
81
|
+
"timestamp": datetime.now().isoformat(),
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Keep connection alive and handle client messages
|
|
87
|
+
while True:
|
|
88
|
+
try:
|
|
89
|
+
message = await asyncio.wait_for(websocket.receive_text(), timeout=30.0)
|
|
90
|
+
data = json.loads(message)
|
|
91
|
+
|
|
92
|
+
if data.get("type") == "request_update":
|
|
93
|
+
metrics = await get_monitoring_current_metrics(
|
|
94
|
+
quality_service, job_manager
|
|
95
|
+
)
|
|
96
|
+
await websocket.send_text(
|
|
97
|
+
json.dumps(
|
|
98
|
+
{
|
|
99
|
+
"type": "metrics_update",
|
|
100
|
+
"data": metrics.to_dict(),
|
|
101
|
+
"timestamp": datetime.now().isoformat(),
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
except TimeoutError:
|
|
107
|
+
metrics = await get_monitoring_current_metrics(
|
|
108
|
+
quality_service, job_manager
|
|
109
|
+
)
|
|
110
|
+
await ws_manager.broadcast_metrics(metrics)
|
|
111
|
+
|
|
112
|
+
except WebSocketDisconnect:
|
|
113
|
+
ws_manager.disconnect(websocket, client_id)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def _handle_historical_metrics_websocket(
|
|
117
|
+
websocket: WebSocket,
|
|
118
|
+
ws_manager: MonitoringWebSocketManager,
|
|
119
|
+
quality_service: EnhancedQualityBaselineService,
|
|
120
|
+
days: int,
|
|
121
|
+
) -> None:
|
|
122
|
+
"""Handle historical metrics WebSocket connection."""
|
|
123
|
+
if days > 365:
|
|
124
|
+
await websocket.close(code=1008, reason="Days parameter too large")
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
client_id = f"historical_{datetime.now().timestamp()}"
|
|
128
|
+
await ws_manager.connect_metrics(websocket, client_id)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
baselines = await quality_service.aget_recent_baselines(limit=days)
|
|
132
|
+
historical_data = _convert_baselines_to_metrics(baselines)
|
|
133
|
+
|
|
134
|
+
await _send_historical_data_chunks(websocket, historical_data)
|
|
135
|
+
|
|
136
|
+
# Keep connection open for updates
|
|
137
|
+
while True:
|
|
138
|
+
await websocket.receive_text()
|
|
139
|
+
|
|
140
|
+
except WebSocketDisconnect:
|
|
141
|
+
ws_manager.disconnect(websocket, client_id)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _convert_baselines_to_metrics(
|
|
145
|
+
baselines: list[t.Any],
|
|
146
|
+
) -> list[UnifiedMetrics]:
|
|
147
|
+
"""Convert quality baselines to UnifiedMetrics objects."""
|
|
148
|
+
return [
|
|
149
|
+
UnifiedMetrics(
|
|
150
|
+
timestamp=baseline.timestamp,
|
|
151
|
+
quality_score=baseline.quality_score,
|
|
152
|
+
test_coverage=baseline.coverage_percent,
|
|
153
|
+
hook_duration=0.0,
|
|
154
|
+
active_jobs=0,
|
|
155
|
+
error_count=baseline.hook_failures
|
|
156
|
+
+ baseline.security_issues
|
|
157
|
+
+ baseline.type_errors
|
|
158
|
+
+ baseline.linting_issues,
|
|
159
|
+
trend_direction=TrendDirection.STABLE,
|
|
160
|
+
predictions={},
|
|
161
|
+
)
|
|
162
|
+
for baseline in baselines
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def _send_historical_data_chunks(
|
|
167
|
+
websocket: WebSocket, historical_data: list[UnifiedMetrics]
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Send historical data in chunks to avoid overwhelming the client."""
|
|
170
|
+
chunk_size = 100
|
|
171
|
+
|
|
172
|
+
for i in range(0, len(historical_data), chunk_size):
|
|
173
|
+
chunk = historical_data[i : i + chunk_size]
|
|
174
|
+
await websocket.send_text(
|
|
175
|
+
json.dumps(
|
|
176
|
+
{
|
|
177
|
+
"type": "historical_chunk",
|
|
178
|
+
"data": [m.to_dict() for m in chunk],
|
|
179
|
+
"chunk_index": i // chunk_size,
|
|
180
|
+
"total_chunks": (len(historical_data) + chunk_size - 1)
|
|
181
|
+
// chunk_size,
|
|
182
|
+
"timestamp": datetime.now().isoformat(),
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
await asyncio.sleep(0.1)
|
|
187
|
+
|
|
188
|
+
# Send completion signal
|
|
189
|
+
await websocket.send_text(
|
|
190
|
+
json.dumps(
|
|
191
|
+
{
|
|
192
|
+
"type": "historical_complete",
|
|
193
|
+
"total_records": len(historical_data),
|
|
194
|
+
"timestamp": datetime.now().isoformat(),
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
async def _handle_alerts_websocket(
|
|
201
|
+
websocket: WebSocket, ws_manager: MonitoringWebSocketManager
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Handle alerts WebSocket connection."""
|
|
204
|
+
client_id = f"alerts_{datetime.now().timestamp()}"
|
|
205
|
+
await ws_manager.connect_alerts(websocket, client_id)
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
# Send current active alerts - would need to track these separately
|
|
209
|
+
active_alerts = [] # For now, empty list
|
|
210
|
+
await websocket.send_text(
|
|
211
|
+
json.dumps(
|
|
212
|
+
{
|
|
213
|
+
"type": "active_alerts",
|
|
214
|
+
"data": [alert.to_dict() for alert in active_alerts],
|
|
215
|
+
"timestamp": datetime.now().isoformat(),
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Keep connection alive
|
|
221
|
+
while True:
|
|
222
|
+
await websocket.receive_text()
|
|
223
|
+
|
|
224
|
+
except WebSocketDisconnect:
|
|
225
|
+
ws_manager.disconnect(websocket, client_id)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
async def _handle_dashboard_websocket(
|
|
229
|
+
websocket: WebSocket,
|
|
230
|
+
ws_manager: MonitoringWebSocketManager,
|
|
231
|
+
quality_service: EnhancedQualityBaselineService,
|
|
232
|
+
job_manager: t.Any,
|
|
233
|
+
) -> None:
|
|
234
|
+
"""Handle dashboard overview WebSocket connection."""
|
|
235
|
+
client_id = f"dashboard_{datetime.now().timestamp()}"
|
|
236
|
+
await ws_manager.connect_metrics(websocket, client_id)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
telemetry: WorkflowEventTelemetry | None
|
|
240
|
+
try:
|
|
241
|
+
telemetry = depends.get_sync(WorkflowEventTelemetry)
|
|
242
|
+
except Exception:
|
|
243
|
+
telemetry = None
|
|
244
|
+
|
|
245
|
+
while True:
|
|
246
|
+
current_metrics = await get_monitoring_current_metrics(
|
|
247
|
+
quality_service, job_manager
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
metrics_dict = _create_dashboard_metrics_dict(current_metrics)
|
|
251
|
+
|
|
252
|
+
dashboard_state = await asyncio.to_thread(
|
|
253
|
+
quality_service.create_dashboard_state,
|
|
254
|
+
metrics_dict,
|
|
255
|
+
len(job_manager.active_connections),
|
|
256
|
+
7,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
telemetry_snapshot: dict[str, t.Any] | None = None
|
|
260
|
+
if telemetry is not None:
|
|
261
|
+
telemetry_snapshot = await telemetry.snapshot()
|
|
262
|
+
|
|
263
|
+
await websocket.send_text(
|
|
264
|
+
json.dumps(
|
|
265
|
+
{
|
|
266
|
+
"type": "dashboard_update",
|
|
267
|
+
"data": dashboard_state.to_dict(),
|
|
268
|
+
"telemetry": telemetry_snapshot,
|
|
269
|
+
"timestamp": datetime.now().isoformat(),
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
await asyncio.sleep(10)
|
|
275
|
+
|
|
276
|
+
except WebSocketDisconnect:
|
|
277
|
+
ws_manager.disconnect(websocket, client_id)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _create_dashboard_metrics_dict(current_metrics: UnifiedMetrics) -> dict[str, t.Any]:
|
|
281
|
+
"""Create basic metrics dict for dashboard state."""
|
|
282
|
+
return {
|
|
283
|
+
"coverage_percent": current_metrics.test_coverage,
|
|
284
|
+
"test_count": 0,
|
|
285
|
+
"test_pass_rate": 100.0,
|
|
286
|
+
"hook_failures": 0,
|
|
287
|
+
"complexity_violations": 0,
|
|
288
|
+
"security_issues": 0,
|
|
289
|
+
"type_errors": 0,
|
|
290
|
+
"linting_issues": 0,
|
|
291
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Backward compatibility wrapper for refactored monitoring endpoints.
|
|
2
|
+
|
|
3
|
+
This module re-exports the main factory function from the new monitoring/ directory
|
|
4
|
+
to maintain backward compatibility with existing code that imports from
|
|
5
|
+
crackerjack.mcp.websocket.monitoring_endpoints.
|
|
6
|
+
|
|
7
|
+
The actual implementation has been split into organized modules:
|
|
8
|
+
- monitoring/models.py - Pydantic data models
|
|
9
|
+
- monitoring/websocket_manager.py - WebSocket connection management
|
|
10
|
+
- monitoring/utils.py - Utility functions
|
|
11
|
+
- monitoring/dashboard.py - Dashboard HTML rendering
|
|
12
|
+
- monitoring/websockets/ - WebSocket endpoint modules (metrics, intelligence, dependencies, heatmap)
|
|
13
|
+
- monitoring/api/ - REST API endpoint modules (telemetry, metrics, intelligence, dependencies, heatmap)
|
|
14
|
+
- monitoring/factory.py - Endpoint registration orchestration
|
|
15
|
+
|
|
16
|
+
All imports should work exactly as before via the main factory function.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .monitoring import MonitoringWebSocketManager, create_monitoring_endpoints
|
|
20
|
+
|
|
21
|
+
__all__ = ["create_monitoring_endpoints", "MonitoringWebSocketManager"]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
import signal
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
import time
|
|
7
|
+
import typing as t
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import uvicorn
|
|
11
|
+
from acb import console
|
|
12
|
+
|
|
13
|
+
from crackerjack.core.timeout_manager import get_timeout_manager
|
|
14
|
+
|
|
15
|
+
from .app import create_websocket_app
|
|
16
|
+
from .jobs import JobManager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WebSocketServer:
|
|
20
|
+
def __init__(self, port: int = 8675) -> None:
|
|
21
|
+
self.port = port
|
|
22
|
+
self.progress_dir = Path(tempfile.gettempdir()) / "crackerjack-mcp-progress"
|
|
23
|
+
self.is_running = True
|
|
24
|
+
self.job_manager: JobManager | None = None
|
|
25
|
+
self.app: t.Any = None
|
|
26
|
+
self.timeout_manager = get_timeout_manager()
|
|
27
|
+
self.server_task: asyncio.Task[t.Any] | None = None
|
|
28
|
+
|
|
29
|
+
def setup(self) -> None:
|
|
30
|
+
self.progress_dir.mkdir(exist_ok=True)
|
|
31
|
+
|
|
32
|
+
self.job_manager = JobManager(self.progress_dir)
|
|
33
|
+
|
|
34
|
+
self.app = create_websocket_app(self.job_manager, self.progress_dir)
|
|
35
|
+
|
|
36
|
+
signal.signal(signal.SIGINT, self._signal_handler)
|
|
37
|
+
signal.signal(signal.SIGTERM, self._signal_handler)
|
|
38
|
+
|
|
39
|
+
def _signal_handler(self, _signum: int, _frame: t.Any) -> None:
|
|
40
|
+
console.print("\n[yellow]Shutting down WebSocket server...[/yellow]")
|
|
41
|
+
self.is_running = False
|
|
42
|
+
|
|
43
|
+
if self.server_task and not self.server_task.done():
|
|
44
|
+
self.server_task.cancel()
|
|
45
|
+
|
|
46
|
+
if self.job_manager:
|
|
47
|
+
with contextlib.suppress(Exception):
|
|
48
|
+
asyncio.create_task(self._graceful_shutdown())
|
|
49
|
+
|
|
50
|
+
async def _graceful_shutdown(self) -> None:
|
|
51
|
+
if self.job_manager:
|
|
52
|
+
try:
|
|
53
|
+
await asyncio.sleep(2.0)
|
|
54
|
+
|
|
55
|
+
console.print(
|
|
56
|
+
"[yellow]Forcing remaining WebSocket connections to close[/yellow]"
|
|
57
|
+
)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
console.print(f"[red]Error during graceful shutdown: {e}[/red]")
|
|
60
|
+
|
|
61
|
+
def run(self) -> None:
|
|
62
|
+
try:
|
|
63
|
+
self.setup()
|
|
64
|
+
console.print(
|
|
65
|
+
f"[green]Starting WebSocket server on port {self.port}[/green]",
|
|
66
|
+
)
|
|
67
|
+
console.print(f"Progress directory: {self.progress_dir}")
|
|
68
|
+
console.print("Press Ctrl+C to stop")
|
|
69
|
+
|
|
70
|
+
config = uvicorn.Config(
|
|
71
|
+
app=self.app,
|
|
72
|
+
port=self.port,
|
|
73
|
+
host="127.0.0.1",
|
|
74
|
+
log_level="info",
|
|
75
|
+
timeout_keep_alive=30,
|
|
76
|
+
timeout_graceful_shutdown=30,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
server = uvicorn.Server(config)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
asyncio.run(self._run_with_timeout(server))
|
|
83
|
+
except KeyboardInterrupt:
|
|
84
|
+
console.print("\n[yellow]Server interrupted by user[/yellow]")
|
|
85
|
+
|
|
86
|
+
except KeyboardInterrupt:
|
|
87
|
+
console.print("\n[yellow]Server stopped by user[/yellow]")
|
|
88
|
+
except Exception as e:
|
|
89
|
+
console.print(f"[red]Server error: {e}[/red]")
|
|
90
|
+
finally:
|
|
91
|
+
console.print("[green]WebSocket server shutdown complete[/green]")
|
|
92
|
+
|
|
93
|
+
async def _run_with_timeout(self, server: uvicorn.Server) -> None:
|
|
94
|
+
try:
|
|
95
|
+
self.server_task = asyncio.create_task(server.serve())
|
|
96
|
+
|
|
97
|
+
while self.is_running and not self.server_task.done():
|
|
98
|
+
try:
|
|
99
|
+
await asyncio.sleep(5.0)
|
|
100
|
+
|
|
101
|
+
except asyncio.CancelledError:
|
|
102
|
+
console.print("[yellow]Server monitoring cancelled[/yellow]")
|
|
103
|
+
break
|
|
104
|
+
except Exception as e:
|
|
105
|
+
console.print(f"[red]Server monitoring error: {e}[/red]")
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
if self.server_task and not self.server_task.done():
|
|
109
|
+
try:
|
|
110
|
+
await asyncio.wait_for(self.server_task, timeout=30.0)
|
|
111
|
+
except TimeoutError:
|
|
112
|
+
console.print(
|
|
113
|
+
"[yellow]Server shutdown timeout, forcing termination[/yellow]"
|
|
114
|
+
)
|
|
115
|
+
self.server_task.cancel()
|
|
116
|
+
try:
|
|
117
|
+
await self.server_task
|
|
118
|
+
except asyncio.CancelledError:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
console.print(f"[red]Server runtime error: {e}[/red]")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def handle_websocket_server_command(
|
|
126
|
+
start: bool = False,
|
|
127
|
+
stop: bool = False,
|
|
128
|
+
restart: bool = False,
|
|
129
|
+
port: int = 8675,
|
|
130
|
+
) -> None:
|
|
131
|
+
if stop or restart:
|
|
132
|
+
console.print("[yellow]Stopping WebSocket servers...[/yellow]")
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
result = subprocess.run(
|
|
136
|
+
["pkill", "-f", f"uvicorn.*: {port}"],
|
|
137
|
+
check=False,
|
|
138
|
+
capture_output=True,
|
|
139
|
+
text=True,
|
|
140
|
+
timeout=10,
|
|
141
|
+
)
|
|
142
|
+
if result.returncode == 0:
|
|
143
|
+
console.print("[green]✅ WebSocket servers stopped[/green]")
|
|
144
|
+
else:
|
|
145
|
+
console.print("[dim]No WebSocket servers were running[/dim]")
|
|
146
|
+
except subprocess.TimeoutExpired:
|
|
147
|
+
console.print("[red]Timeout stopping WebSocket servers[/red]")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
console.print(f"[red]Error stopping WebSocket servers: {e}[/red]")
|
|
150
|
+
|
|
151
|
+
if stop:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
time.sleep(2)
|
|
155
|
+
|
|
156
|
+
if start or restart:
|
|
157
|
+
console.print(f"[green]Starting WebSocket server on port {port}...[/green]")
|
|
158
|
+
try:
|
|
159
|
+
server = WebSocketServer(port)
|
|
160
|
+
server.run()
|
|
161
|
+
except Exception as e:
|
|
162
|
+
console.print(f"[red]Failed to start WebSocket server: {e}[/red]")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def main(port: int = 8675) -> None:
|
|
166
|
+
server = WebSocketServer(port)
|
|
167
|
+
server.run()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
import sys
|
|
172
|
+
|
|
173
|
+
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8675
|
|
174
|
+
main(port)
|