crackerjack 0.32.0__py3-none-any.whl → 0.33.1__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.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +64 -6
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +257 -218
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +558 -240
- crackerjack/docs/INDEX.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/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +66 -13
- crackerjack/managers/test_command_builder.py +5 -17
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +109 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +161 -32
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +174 -33
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +15 -12
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +3 -0
- crackerjack/mixins/error_handling.py +145 -0
- crackerjack/models/config.py +21 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +176 -107
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/models/task.py +3 -0
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +90 -105
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +18 -11
- crackerjack/services/config_merge.py +30 -85
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +41 -17
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +41 -45
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +30 -33
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +409 -0
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +416 -0
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +250 -576
- crackerjack/services/performance_cache.py +382 -0
- crackerjack/services/performance_monitor.py +565 -0
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +61 -30
- crackerjack/services/security_logger.py +18 -22
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.32.0.dist-info/RECORD +0 -180
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import typing as t
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from fastapi import FastAPI
|
|
@@ -11,8 +12,10 @@ from ...services.secure_status_formatter import (
|
|
|
11
12
|
from .jobs import JobManager
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def _build_job_list(
|
|
15
|
-
|
|
15
|
+
def _build_job_list(
|
|
16
|
+
job_manager: JobManager, progress_dir: Path
|
|
17
|
+
) -> list[dict[str, t.Any]]:
|
|
18
|
+
jobs: list[dict[str, t.Any]] = []
|
|
16
19
|
if not progress_dir.exists():
|
|
17
20
|
return jobs
|
|
18
21
|
for progress_file in progress_dir.glob("job-*.json"):
|
|
@@ -34,13 +37,15 @@ def _build_job_list(job_manager: JobManager, progress_dir: Path) -> list[dict]:
|
|
|
34
37
|
return jobs
|
|
35
38
|
|
|
36
39
|
|
|
37
|
-
def _build_status_response(
|
|
40
|
+
def _build_status_response(
|
|
41
|
+
job_manager: JobManager, jobs: list[dict[str, t.Any]]
|
|
42
|
+
) -> dict[str, t.Any]:
|
|
38
43
|
return {
|
|
39
44
|
"status": "running",
|
|
40
45
|
"message": "Crackerjack WebSocket Server",
|
|
41
46
|
"active_connections": len(job_manager.active_connections),
|
|
42
47
|
"jobs": jobs[:10],
|
|
43
|
-
"websocket_url": "ws
|
|
48
|
+
"websocket_url": "ws: //[INTERNAL_URL]/ws/progress/{job_id}",
|
|
44
49
|
"endpoints": {
|
|
45
50
|
"status": "/",
|
|
46
51
|
"latest_job": "/latest",
|
|
@@ -52,367 +57,362 @@ def _build_status_response(job_manager: JobManager, jobs: list[dict]) -> dict:
|
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
def _get_monitor_html(job_id: str) -> str:
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
<div class="
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
<h1>🚀 Crackerjack Job Monitor</h1>
|
|
131
|
-
<p>Job ID: <span class="job-id">{job_id}</span></p>
|
|
132
|
-
</div>
|
|
133
|
-
<div class="status connecting" id="status">
|
|
134
|
-
Status: Connecting to job...
|
|
135
|
-
</div>
|
|
136
|
-
<div class="log" id="log">
|
|
137
|
-
<div>Connecting to WebSocket...</div>
|
|
138
|
-
</div>
|
|
60
|
+
return f"""<!DOCTYPE html>
|
|
61
|
+
<html>
|
|
62
|
+
<head>
|
|
63
|
+
<title>Job Monitor - {job_id}</title>
|
|
64
|
+
<meta charset="UTF-8">
|
|
65
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
66
|
+
<style>
|
|
67
|
+
body {{
|
|
68
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
69
|
+
margin: 0;
|
|
70
|
+
padding: 20px;
|
|
71
|
+
background-color: #f5f5f5;
|
|
72
|
+
}}
|
|
73
|
+
.container {{
|
|
74
|
+
max-width: 800px;
|
|
75
|
+
margin: 0 auto;
|
|
76
|
+
background: white;
|
|
77
|
+
padding: 30px;
|
|
78
|
+
border-radius: 10px;
|
|
79
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
80
|
+
}}
|
|
81
|
+
.header {{
|
|
82
|
+
text-align: center;
|
|
83
|
+
margin-bottom: 30px;
|
|
84
|
+
padding-bottom: 20px;
|
|
85
|
+
border-bottom: 2px solid #eee;
|
|
86
|
+
}}
|
|
87
|
+
.job-id {{
|
|
88
|
+
font-family: 'Courier New', monospace;
|
|
89
|
+
background: #f0f0f0;
|
|
90
|
+
padding: 5px 10px;
|
|
91
|
+
border-radius: 5px;
|
|
92
|
+
color: #333;
|
|
93
|
+
}}
|
|
94
|
+
.status {{
|
|
95
|
+
margin: 20px 0;
|
|
96
|
+
padding: 15px;
|
|
97
|
+
border-radius: 5px;
|
|
98
|
+
font-weight: bold;
|
|
99
|
+
}}
|
|
100
|
+
.status.running {{ background-color: #e3f2fd; color: #1976d2; }}
|
|
101
|
+
.status.completed {{ background-color: #e8f5e8; color: #388e3c; }}
|
|
102
|
+
.status.failed {{ background-color: #ffebee; color: #d32f2f; }}
|
|
103
|
+
.status.connecting {{ background-color: #fff3e0; color: #f57c00; }}
|
|
104
|
+
.log {{
|
|
105
|
+
margin: 20px 0;
|
|
106
|
+
padding: 15px;
|
|
107
|
+
background: #1a1a1a;
|
|
108
|
+
color: #00ff00;
|
|
109
|
+
font-family: 'Courier New', monospace;
|
|
110
|
+
border-radius: 5px;
|
|
111
|
+
max-height: 300px;
|
|
112
|
+
overflow-y: auto;
|
|
113
|
+
font-size: 12px;
|
|
114
|
+
line-height: 1.4;
|
|
115
|
+
}}
|
|
116
|
+
.connection-status {{
|
|
117
|
+
position: fixed;
|
|
118
|
+
top: 10px;
|
|
119
|
+
right: 10px;
|
|
120
|
+
padding: 5px 10px;
|
|
121
|
+
border-radius: 15px;
|
|
122
|
+
font-size: 12px;
|
|
123
|
+
font-weight: bold;
|
|
124
|
+
}}
|
|
125
|
+
.connected {{ background: #4caf50; color: white; }}
|
|
126
|
+
.disconnected {{ background: #f44336; color: white; }}
|
|
127
|
+
</style>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
<div class="connection-status" id="connectionStatus">Connecting...</div>
|
|
131
|
+
<div class="container">
|
|
132
|
+
<div class="header">
|
|
133
|
+
<h1>🚀 Crackerjack Job Monitor</h1>
|
|
134
|
+
<p>Job ID: <span class="job-id">{job_id}</span></p>
|
|
139
135
|
</div>
|
|
136
|
+
<div class="status connecting" id="status">
|
|
137
|
+
Status: Connecting to job...
|
|
138
|
+
</div>
|
|
139
|
+
<div class="log" id="log">
|
|
140
|
+
<div>Connecting to WebSocket...</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<script>
|
|
145
|
+
const jobId = '{job_id}';
|
|
146
|
+
const wsUrl = `ws://[INTERNAL_URL]/ws/progress/${{jobId}}`;
|
|
147
|
+
let ws = null;
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// Start connection
|
|
190
|
-
connect();
|
|
191
|
-
</script>
|
|
192
|
-
</body>
|
|
193
|
-
</html>
|
|
194
|
-
"""
|
|
149
|
+
function updateConnectionStatus(status) {{
|
|
150
|
+
const elem = document.getElementById('connectionStatus');
|
|
151
|
+
elem.className = 'connection-status ' + status;
|
|
152
|
+
elem.textContent = status === 'connected' ? '🟢 Connected' : '🔴 Disconnected';
|
|
153
|
+
}}
|
|
154
|
+
|
|
155
|
+
function addLogEntry(message) {{
|
|
156
|
+
const log = document.getElementById('log');
|
|
157
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
158
|
+
const entry = document.createElement('div');
|
|
159
|
+
entry.textContent = `[${{timestamp}}] ${{message}}`;
|
|
160
|
+
log.appendChild(entry);
|
|
161
|
+
log.scrollTop = log.scrollHeight;
|
|
162
|
+
}}
|
|
163
|
+
|
|
164
|
+
function connect() {{
|
|
165
|
+
ws = new WebSocket(wsUrl);
|
|
166
|
+
|
|
167
|
+
ws.onopen = function() {{
|
|
168
|
+
updateConnectionStatus('connected');
|
|
169
|
+
addLogEntry('Connected to WebSocket');
|
|
170
|
+
}};
|
|
171
|
+
|
|
172
|
+
ws.onmessage = function(event) {{
|
|
173
|
+
try {{
|
|
174
|
+
const data = JSON.parse(event.data);
|
|
175
|
+
addLogEntry(data.message || 'Progress update');
|
|
176
|
+
}} catch (e) {{
|
|
177
|
+
addLogEntry('Received: ' + event.data);
|
|
178
|
+
}}
|
|
179
|
+
}};
|
|
180
|
+
|
|
181
|
+
ws.onclose = function() {{
|
|
182
|
+
updateConnectionStatus('disconnected');
|
|
183
|
+
addLogEntry('WebSocket connection closed');
|
|
184
|
+
}};
|
|
185
|
+
|
|
186
|
+
ws.onerror = function() {{
|
|
187
|
+
updateConnectionStatus('disconnected');
|
|
188
|
+
addLogEntry('WebSocket error occurred');
|
|
189
|
+
}};
|
|
190
|
+
}}
|
|
191
|
+
|
|
192
|
+
// Start connection
|
|
193
|
+
connect();
|
|
194
|
+
</script>
|
|
195
|
+
</body>
|
|
196
|
+
</html>"""
|
|
195
197
|
|
|
196
198
|
|
|
197
199
|
def _get_test_html() -> str:
|
|
198
|
-
return """
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
#
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
<
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<div id="serverStatus"></div>
|
|
282
|
-
</div>
|
|
283
|
-
|
|
284
|
-
<div class="test-section">
|
|
285
|
-
<h3>2. Latest Job</h3>
|
|
286
|
-
<button onclick="checkLatestJob()">Get Latest Job</button>
|
|
287
|
-
<div id="latestJob"></div>
|
|
288
|
-
</div>
|
|
289
|
-
|
|
290
|
-
<div class="test-section">
|
|
291
|
-
<h3>3. WebSocket Test</h3>
|
|
292
|
-
<input type="text" id="testJobId" placeholder="Enter job ID (e.g., test-123)" value="test-123">
|
|
293
|
-
<button onclick="testWebSocket()">Test WebSocket</button>
|
|
294
|
-
<button onclick="disconnectWebSocket()">Disconnect</button>
|
|
295
|
-
<div id="wsStatus"></div>
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
<div id="log">
|
|
299
|
-
<div>Ready to test...</div>
|
|
300
|
-
</div>
|
|
200
|
+
return """<!DOCTYPE html>
|
|
201
|
+
<html>
|
|
202
|
+
<head>
|
|
203
|
+
<title>WebSocket Test Page</title>
|
|
204
|
+
<meta charset="UTF-8">
|
|
205
|
+
<style>
|
|
206
|
+
body {
|
|
207
|
+
font-family: Arial, sans-serif;
|
|
208
|
+
max-width: 800px;
|
|
209
|
+
margin: 50px auto;
|
|
210
|
+
padding: 20px;
|
|
211
|
+
background-color: #f5f5f5;
|
|
212
|
+
}
|
|
213
|
+
.container {
|
|
214
|
+
background: white;
|
|
215
|
+
padding: 30px;
|
|
216
|
+
border-radius: 10px;
|
|
217
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
218
|
+
}
|
|
219
|
+
.test-section {
|
|
220
|
+
margin: 20px 0;
|
|
221
|
+
padding: 15px;
|
|
222
|
+
border: 1px solid #ddd;
|
|
223
|
+
border-radius: 5px;
|
|
224
|
+
}
|
|
225
|
+
button {
|
|
226
|
+
background: #007cba;
|
|
227
|
+
color: white;
|
|
228
|
+
border: none;
|
|
229
|
+
padding: 10px 20px;
|
|
230
|
+
border-radius: 5px;
|
|
231
|
+
cursor: pointer;
|
|
232
|
+
margin: 5px;
|
|
233
|
+
}
|
|
234
|
+
button:hover {
|
|
235
|
+
background: #005a87;
|
|
236
|
+
}
|
|
237
|
+
input[type="text"] {
|
|
238
|
+
padding: 8px;
|
|
239
|
+
border: 1px solid #ccc;
|
|
240
|
+
border-radius: 3px;
|
|
241
|
+
margin: 5px;
|
|
242
|
+
width: 200px;
|
|
243
|
+
}
|
|
244
|
+
.status {
|
|
245
|
+
margin: 10px 0;
|
|
246
|
+
padding: 10px;
|
|
247
|
+
border-radius: 5px;
|
|
248
|
+
font-weight: bold;
|
|
249
|
+
}
|
|
250
|
+
.success {
|
|
251
|
+
background: #d4edda;
|
|
252
|
+
color: #155724;
|
|
253
|
+
}
|
|
254
|
+
.error {
|
|
255
|
+
background: #f8d7da;
|
|
256
|
+
color: #721c24;
|
|
257
|
+
}
|
|
258
|
+
.info {
|
|
259
|
+
background: #d1ecf1;
|
|
260
|
+
color: #0c5460;
|
|
261
|
+
}
|
|
262
|
+
#log {
|
|
263
|
+
background: #1a1a1a;
|
|
264
|
+
color: #00ff00;
|
|
265
|
+
padding: 15px;
|
|
266
|
+
border-radius: 5px;
|
|
267
|
+
font-family: 'Courier New', monospace;
|
|
268
|
+
max-height: 300px;
|
|
269
|
+
overflow-y: auto;
|
|
270
|
+
margin-top: 20px;
|
|
271
|
+
}
|
|
272
|
+
</style>
|
|
273
|
+
</head>
|
|
274
|
+
<body>
|
|
275
|
+
<div class="container">
|
|
276
|
+
<h1>🧪 WebSocket Test Page</h1>
|
|
277
|
+
<p>Test WebSocket connectivity and server functionality</p>
|
|
278
|
+
|
|
279
|
+
<div class="test-section">
|
|
280
|
+
<h3>1. Server Status</h3>
|
|
281
|
+
<button onclick="checkServerStatus()">Check Status</button>
|
|
282
|
+
<div id="serverStatus"></div>
|
|
301
283
|
</div>
|
|
302
284
|
|
|
303
|
-
<
|
|
304
|
-
|
|
305
|
-
|
|
285
|
+
<div class="test-section">
|
|
286
|
+
<h3>2. Latest Job</h3>
|
|
287
|
+
<button onclick="checkLatestJob()">Get Latest Job</button>
|
|
288
|
+
<div id="latestJob"></div>
|
|
289
|
+
</div>
|
|
306
290
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
log.scrollTop = log.scrollHeight;
|
|
315
|
-
}
|
|
291
|
+
<div class="test-section">
|
|
292
|
+
<h3>3. WebSocket Test</h3>
|
|
293
|
+
<input type="text" id="testJobId" placeholder="Enter job ID (e.g., test-123)" value="test-123">
|
|
294
|
+
<button onclick="testWebSocket()">Test WebSocket</button>
|
|
295
|
+
<button onclick="disconnectWebSocket()">Disconnect</button>
|
|
296
|
+
<div id="wsStatus"></div>
|
|
297
|
+
</div>
|
|
316
298
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
299
|
+
<div id="log">
|
|
300
|
+
<div>Ready to test...</div>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<script>
|
|
305
|
+
let testWs = null;
|
|
306
|
+
const log = document.getElementById('log');
|
|
307
|
+
|
|
308
|
+
function addLog(message, type = 'info') {
|
|
309
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
310
|
+
const div = document.createElement('div');
|
|
311
|
+
div.textContent = `[${timestamp}] ${message}`;
|
|
312
|
+
if (type === 'error') div.style.color = '#ff6b6b';
|
|
313
|
+
if (type === 'success') div.style.color = '#51cf66';
|
|
314
|
+
log.appendChild(div);
|
|
315
|
+
log.scrollTop = log.scrollHeight;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function showStatus(elementId, message, type = 'info') {
|
|
319
|
+
const element = document.getElementById(elementId);
|
|
320
|
+
element.innerHTML = `<div class="status ${type}">${message}</div>`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function checkServerStatus() {
|
|
324
|
+
try {
|
|
325
|
+
addLog('Checking server status...');
|
|
326
|
+
const response = await fetch('/');
|
|
327
|
+
const data = await response.json();
|
|
328
|
+
|
|
329
|
+
showStatus('serverStatus',
|
|
330
|
+
`Status: ${data.status}<br>` +
|
|
331
|
+
`Active connections: ${data.active_connections}<br>` +
|
|
332
|
+
`Jobs: ${data.jobs?.length || 0}`, 'success');
|
|
333
|
+
addLog('Server status retrieved successfully', 'success');
|
|
334
|
+
} catch (error) {
|
|
335
|
+
showStatus('serverStatus', `Error: ${error.message}`, 'error');
|
|
336
|
+
addLog(`Server status error: ${error.message}`, 'error');
|
|
320
337
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
`
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function checkLatestJob() {
|
|
341
|
+
try {
|
|
342
|
+
addLog('Getting latest job...');
|
|
343
|
+
const response = await fetch('/latest');
|
|
344
|
+
const data = await response.json();
|
|
345
|
+
|
|
346
|
+
if (data.job_id) {
|
|
347
|
+
showStatus('latestJob',
|
|
348
|
+
`Job ID: ${data.job_id}<br>` +
|
|
349
|
+
`Status: ${data.progress?.status || 'unknown'}<br>` +
|
|
350
|
+
`Progress: ${data.progress?.overall_progress || 0}%`, 'success');
|
|
351
|
+
addLog(`Latest job: ${data.job_id}`, 'success');
|
|
352
|
+
} else {
|
|
353
|
+
showStatus('latestJob', data.message, 'info');
|
|
354
|
+
addLog(data.message);
|
|
336
355
|
}
|
|
356
|
+
} catch (error) {
|
|
357
|
+
showStatus('latestJob', `Error: ${error.message}`, 'error');
|
|
358
|
+
addLog(`Latest job error: ${error.message}`, 'error');
|
|
337
359
|
}
|
|
360
|
+
}
|
|
338
361
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (data.job_id) {
|
|
346
|
-
showStatus('latestJob',
|
|
347
|
-
`Job ID: ${data.job_id}<br>` +
|
|
348
|
-
`Status: ${data.progress?.status || 'unknown'}<br>` +
|
|
349
|
-
`Progress: ${data.progress?.overall_progress || 0}%`, 'success');
|
|
350
|
-
addLog(`Latest job: ${data.job_id}`, 'success');
|
|
351
|
-
} else {
|
|
352
|
-
showStatus('latestJob', data.message, 'info');
|
|
353
|
-
addLog(data.message);
|
|
354
|
-
}
|
|
355
|
-
} catch (error) {
|
|
356
|
-
showStatus('latestJob', `Error: ${error.message}`, 'error');
|
|
357
|
-
addLog(`Latest job error: ${error.message}`, 'error');
|
|
358
|
-
}
|
|
362
|
+
function testWebSocket() {
|
|
363
|
+
const jobId = document.getElementById('testJobId').value;
|
|
364
|
+
if (!jobId) {
|
|
365
|
+
showStatus('wsStatus', 'Please enter a job ID', 'error');
|
|
366
|
+
return;
|
|
359
367
|
}
|
|
360
368
|
|
|
361
|
-
|
|
362
|
-
const jobId = document.getElementById('testJobId').value;
|
|
363
|
-
if (!jobId) {
|
|
364
|
-
showStatus('wsStatus', 'Please enter a job ID', 'error');
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
369
|
+
disconnectWebSocket();
|
|
367
370
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const wsUrl = `ws://localhost:8675/ws/progress/${jobId}`;
|
|
372
|
-
testWs = new WebSocket(wsUrl);
|
|
373
|
-
|
|
374
|
-
testWs.onopen = function() {
|
|
375
|
-
showStatus('wsStatus', `Connected to ${jobId}`, 'success');
|
|
376
|
-
addLog('WebSocket connected successfully', 'success');
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
testWs.onmessage = function(event) {
|
|
380
|
-
try {
|
|
381
|
-
const data = JSON.parse(event.data);
|
|
382
|
-
addLog(`Received: ${data.message || 'Progress update'}`);
|
|
383
|
-
} catch (e) {
|
|
384
|
-
addLog(`Raw message: ${event.data}`);
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
testWs.onclose = function() {
|
|
389
|
-
showStatus('wsStatus', 'WebSocket closed', 'info');
|
|
390
|
-
addLog('WebSocket connection closed');
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
testWs.onerror = function(error) {
|
|
394
|
-
showStatus('wsStatus', `WebSocket error: ${error.message}`, 'error');
|
|
395
|
-
addLog(`WebSocket error: ${error.message}`, 'error');
|
|
396
|
-
};
|
|
397
|
-
}
|
|
371
|
+
addLog(`Connecting to WebSocket for job: ${jobId}`);
|
|
372
|
+
const wsUrl = `ws://localhost:8675/ws/progress/${jobId}`;
|
|
373
|
+
testWs = new WebSocket(wsUrl);
|
|
398
374
|
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
375
|
+
testWs.onopen = function() {
|
|
376
|
+
showStatus('wsStatus', `Connected to ${jobId}`, 'success');
|
|
377
|
+
addLog('WebSocket connected successfully', 'success');
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
testWs.onmessage = function(event) {
|
|
381
|
+
try {
|
|
382
|
+
const data = JSON.parse(event.data);
|
|
383
|
+
addLog(`Received: ${data.message || 'Progress update'}`);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
addLog(`Raw message: ${event.data}`);
|
|
405
386
|
}
|
|
406
|
-
}
|
|
387
|
+
};
|
|
407
388
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
389
|
+
testWs.onclose = function() {
|
|
390
|
+
showStatus('wsStatus', 'WebSocket closed', 'info');
|
|
391
|
+
addLog('WebSocket connection closed');
|
|
411
392
|
};
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
393
|
+
|
|
394
|
+
testWs.onerror = function(error) {
|
|
395
|
+
showStatus('wsStatus', `WebSocket error: ${error.message}`, 'error');
|
|
396
|
+
addLog(`WebSocket error: ${error.message}`, 'error');
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function disconnectWebSocket() {
|
|
401
|
+
if (testWs) {
|
|
402
|
+
testWs.close();
|
|
403
|
+
testWs = null;
|
|
404
|
+
showStatus('wsStatus', 'Disconnected', 'info');
|
|
405
|
+
addLog('WebSocket disconnected');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Auto-check server status on load
|
|
410
|
+
window.onload = function() {
|
|
411
|
+
checkServerStatus();
|
|
412
|
+
};
|
|
413
|
+
</script>
|
|
414
|
+
</body>
|
|
415
|
+
</html>"""
|
|
416
416
|
|
|
417
417
|
|
|
418
418
|
def register_endpoints(
|
|
@@ -421,13 +421,11 @@ def register_endpoints(
|
|
|
421
421
|
progress_dir: Path,
|
|
422
422
|
) -> None:
|
|
423
423
|
@app.get("/")
|
|
424
|
-
async def get_status():
|
|
424
|
+
async def get_status() -> dict[str, t.Any]:
|
|
425
425
|
try:
|
|
426
|
-
# Build raw status response
|
|
427
426
|
jobs = _build_job_list(job_manager, progress_dir)
|
|
428
427
|
raw_status = _build_status_response(job_manager, jobs)
|
|
429
428
|
|
|
430
|
-
# Apply secure formatting
|
|
431
429
|
secure_status = format_secure_status(
|
|
432
430
|
raw_status,
|
|
433
431
|
project_root=progress_dir.parent,
|
|
@@ -436,7 +434,6 @@ def register_endpoints(
|
|
|
436
434
|
|
|
437
435
|
return secure_status
|
|
438
436
|
except Exception as e:
|
|
439
|
-
# Use secure error formatting
|
|
440
437
|
formatter = get_secure_status_formatter()
|
|
441
438
|
error_response = formatter.format_error_response(
|
|
442
439
|
str(e),
|
|
@@ -444,7 +441,7 @@ def register_endpoints(
|
|
|
444
441
|
return error_response
|
|
445
442
|
|
|
446
443
|
@app.get("/latest")
|
|
447
|
-
async def get_latest_job():
|
|
444
|
+
async def get_latest_job() -> dict[str, t.Any]:
|
|
448
445
|
try:
|
|
449
446
|
latest_job_id = job_manager.get_latest_job_id()
|
|
450
447
|
|
|
@@ -461,12 +458,11 @@ def register_endpoints(
|
|
|
461
458
|
"status": "success",
|
|
462
459
|
"message": f"Latest job: {latest_job_id}",
|
|
463
460
|
"job_id": latest_job_id,
|
|
464
|
-
"progress": progress_data,
|
|
465
|
-
"websocket_url": f"ws
|
|
466
|
-
"monitor_url": f"http
|
|
461
|
+
"progress": progress_data or {},
|
|
462
|
+
"websocket_url": f"ws: //[INTERNAL_URL]/ws/progress/{latest_job_id}",
|
|
463
|
+
"monitor_url": f"http: //[INTERNAL_URL]/monitor/{latest_job_id}",
|
|
467
464
|
}
|
|
468
465
|
|
|
469
|
-
# Apply secure formatting
|
|
470
466
|
secure_response = format_secure_status(
|
|
471
467
|
raw_response,
|
|
472
468
|
project_root=progress_dir.parent,
|
|
@@ -476,7 +472,6 @@ def register_endpoints(
|
|
|
476
472
|
return secure_response
|
|
477
473
|
|
|
478
474
|
except Exception as e:
|
|
479
|
-
# Use secure error formatting
|
|
480
475
|
formatter = get_secure_status_formatter()
|
|
481
476
|
error_response = formatter.format_error_response(
|
|
482
477
|
f"Failed to get latest job: {e}",
|
|
@@ -484,7 +479,7 @@ def register_endpoints(
|
|
|
484
479
|
return error_response
|
|
485
480
|
|
|
486
481
|
@app.get("/monitor/{job_id}")
|
|
487
|
-
async def get_job_monitor_page(job_id: str):
|
|
482
|
+
async def get_job_monitor_page(job_id: str) -> HTMLResponse:
|
|
488
483
|
if not job_manager.validate_job_id(job_id):
|
|
489
484
|
return HTMLResponse(
|
|
490
485
|
content="<h1>Error</h1><p>Invalid job ID</p>",
|
|
@@ -493,5 +488,5 @@ def register_endpoints(
|
|
|
493
488
|
return HTMLResponse(content=_get_monitor_html(job_id))
|
|
494
489
|
|
|
495
490
|
@app.get("/test")
|
|
496
|
-
async def get_test_page():
|
|
491
|
+
async def get_test_page() -> HTMLResponse:
|
|
497
492
|
return HTMLResponse(content=_get_test_html())
|