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.

Files changed (200) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +64 -6
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +257 -218
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +558 -240
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +66 -13
  74. crackerjack/managers/test_command_builder.py +5 -17
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +109 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +161 -32
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +174 -33
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +15 -12
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +3 -0
  109. crackerjack/mixins/error_handling.py +145 -0
  110. crackerjack/models/config.py +21 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +176 -107
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/models/task.py +3 -0
  115. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  116. crackerjack/monitoring/metrics_collector.py +426 -0
  117. crackerjack/monitoring/regression_prevention.py +8 -8
  118. crackerjack/monitoring/websocket_server.py +643 -0
  119. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  120. crackerjack/orchestration/coverage_improvement.py +3 -3
  121. crackerjack/orchestration/execution_strategies.py +26 -6
  122. crackerjack/orchestration/test_progress_streamer.py +8 -5
  123. crackerjack/plugins/base.py +2 -2
  124. crackerjack/plugins/hooks.py +7 -0
  125. crackerjack/plugins/managers.py +11 -8
  126. crackerjack/security/__init__.py +0 -1
  127. crackerjack/security/audit.py +90 -105
  128. crackerjack/services/anomaly_detector.py +392 -0
  129. crackerjack/services/api_extractor.py +615 -0
  130. crackerjack/services/backup_service.py +2 -2
  131. crackerjack/services/bounded_status_operations.py +15 -152
  132. crackerjack/services/cache.py +127 -1
  133. crackerjack/services/changelog_automation.py +395 -0
  134. crackerjack/services/config.py +18 -11
  135. crackerjack/services/config_merge.py +30 -85
  136. crackerjack/services/config_template.py +506 -0
  137. crackerjack/services/contextual_ai_assistant.py +48 -22
  138. crackerjack/services/coverage_badge_service.py +171 -0
  139. crackerjack/services/coverage_ratchet.py +41 -17
  140. crackerjack/services/debug.py +3 -3
  141. crackerjack/services/dependency_analyzer.py +460 -0
  142. crackerjack/services/dependency_monitor.py +14 -11
  143. crackerjack/services/documentation_generator.py +491 -0
  144. crackerjack/services/documentation_service.py +675 -0
  145. crackerjack/services/enhanced_filesystem.py +6 -5
  146. crackerjack/services/enterprise_optimizer.py +865 -0
  147. crackerjack/services/error_pattern_analyzer.py +676 -0
  148. crackerjack/services/file_hasher.py +1 -1
  149. crackerjack/services/git.py +41 -45
  150. crackerjack/services/health_metrics.py +10 -8
  151. crackerjack/services/heatmap_generator.py +735 -0
  152. crackerjack/services/initialization.py +30 -33
  153. crackerjack/services/input_validator.py +5 -97
  154. crackerjack/services/intelligent_commit.py +327 -0
  155. crackerjack/services/log_manager.py +15 -12
  156. crackerjack/services/logging.py +4 -3
  157. crackerjack/services/lsp_client.py +628 -0
  158. crackerjack/services/memory_optimizer.py +409 -0
  159. crackerjack/services/metrics.py +42 -33
  160. crackerjack/services/parallel_executor.py +416 -0
  161. crackerjack/services/pattern_cache.py +1 -1
  162. crackerjack/services/pattern_detector.py +6 -6
  163. crackerjack/services/performance_benchmarks.py +250 -576
  164. crackerjack/services/performance_cache.py +382 -0
  165. crackerjack/services/performance_monitor.py +565 -0
  166. crackerjack/services/predictive_analytics.py +510 -0
  167. crackerjack/services/quality_baseline.py +234 -0
  168. crackerjack/services/quality_baseline_enhanced.py +646 -0
  169. crackerjack/services/quality_intelligence.py +785 -0
  170. crackerjack/services/regex_patterns.py +605 -524
  171. crackerjack/services/regex_utils.py +43 -123
  172. crackerjack/services/secure_path_utils.py +5 -164
  173. crackerjack/services/secure_status_formatter.py +30 -141
  174. crackerjack/services/secure_subprocess.py +11 -92
  175. crackerjack/services/security.py +61 -30
  176. crackerjack/services/security_logger.py +18 -22
  177. crackerjack/services/server_manager.py +124 -16
  178. crackerjack/services/status_authentication.py +16 -159
  179. crackerjack/services/status_security_manager.py +4 -131
  180. crackerjack/services/terminal_utils.py +0 -0
  181. crackerjack/services/thread_safe_status_collector.py +19 -125
  182. crackerjack/services/unified_config.py +21 -13
  183. crackerjack/services/validation_rate_limiter.py +5 -54
  184. crackerjack/services/version_analyzer.py +459 -0
  185. crackerjack/services/version_checker.py +1 -1
  186. crackerjack/services/websocket_resource_limiter.py +10 -144
  187. crackerjack/services/zuban_lsp_service.py +390 -0
  188. crackerjack/slash_commands/__init__.py +2 -7
  189. crackerjack/slash_commands/run.md +2 -2
  190. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  191. crackerjack/tools/validate_regex_patterns.py +19 -48
  192. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
  193. crackerjack-0.33.1.dist-info/RECORD +229 -0
  194. crackerjack/CLAUDE.md +0 -207
  195. crackerjack/RULES.md +0 -380
  196. crackerjack/py313.py +0 -234
  197. crackerjack-0.32.0.dist-info/RECORD +0 -180
  198. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
  199. {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  200. {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(job_manager: JobManager, progress_dir: Path) -> list[dict]:
15
- jobs = []
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(job_manager: JobManager, jobs: list[dict]) -> dict:
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://[INTERNAL_URL]/ws/progress/{job_id}",
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
- """Generate secure HTML for job monitoring page."""
56
- return f"""
57
- <!DOCTYPE html>
58
- <html>
59
- <head>
60
- <title>Job Monitor - {job_id}</title>
61
- <meta charset="UTF-8">
62
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
63
- <style>
64
- body {{
65
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
66
- margin: 0;
67
- padding: 20px;
68
- background-color: #f5f5f5;
69
- }}
70
- .container {{
71
- max-width: 800px;
72
- margin: 0 auto;
73
- background: white;
74
- padding: 30px;
75
- border-radius: 10px;
76
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
77
- }}
78
- .header {{
79
- text-align: center;
80
- margin-bottom: 30px;
81
- padding-bottom: 20px;
82
- border-bottom: 2px solid #eee;
83
- }}
84
- .job-id {{
85
- font-family: 'Courier New', monospace;
86
- background: #f0f0f0;
87
- padding: 5px 10px;
88
- border-radius: 5px;
89
- color: #666;
90
- }}
91
- .status {{
92
- margin: 20px 0;
93
- padding: 15px;
94
- border-radius: 5px;
95
- font-weight: bold;
96
- }}
97
- .status.running {{ background-color: #e3f2fd; color: #1976d2; }}
98
- .status.completed {{ background-color: #e8f5e8; color: #388e3c; }}
99
- .status.failed {{ background-color: #ffebee; color: #d32f2f; }}
100
- .status.connecting {{ background-color: #fff3e0; color: #f57c00; }}
101
- .log {{
102
- margin: 20px 0;
103
- padding: 15px;
104
- background: #1e1e1e;
105
- color: #fff;
106
- font-family: 'Courier New', monospace;
107
- border-radius: 5px;
108
- max-height: 300px;
109
- overflow-y: auto;
110
- font-size: 12px;
111
- line-height: 1.4;
112
- }}
113
- .connection-status {{
114
- position: fixed;
115
- top: 10px;
116
- right: 10px;
117
- padding: 5px 10px;
118
- border-radius: 15px;
119
- font-size: 12px;
120
- font-weight: bold;
121
- }}
122
- .connected {{ background: #4caf50; color: white; }}
123
- .disconnected {{ background: #f44336; color: white; }}
124
- </style>
125
- </head>
126
- <body>
127
- <div class="connection-status" id="connectionStatus">Connecting...</div>
128
- <div class="container">
129
- <div class="header">
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
- <script>
142
- const jobId = '{job_id}';
143
- const wsUrl = `ws://[INTERNAL_URL]/ws/progress/${{jobId}}`;
144
- let ws = null;
145
-
146
- function updateConnectionStatus(status) {{
147
- const elem = document.getElementById('connectionStatus');
148
- elem.className = 'connection-status ' + status;
149
- elem.textContent = status === 'connected' ? '🟢 Connected' : '🔴 Disconnected';
150
- }}
151
-
152
- function addLogEntry(message) {{
153
- const log = document.getElementById('log');
154
- const timestamp = new Date().toLocaleTimeString();
155
- const entry = document.createElement('div');
156
- entry.textContent = `[${{timestamp}}] ${{message}}`;
157
- log.appendChild(entry);
158
- log.scrollTop = log.scrollHeight;
159
- }}
160
-
161
- function connect() {{
162
- ws = new WebSocket(wsUrl);
163
-
164
- ws.onopen = function() {{
165
- updateConnectionStatus('connected');
166
- addLogEntry('Connected to WebSocket');
167
- }};
168
-
169
- ws.onmessage = function(event) {{
170
- try {{
171
- const data = JSON.parse(event.data);
172
- addLogEntry(data.message || 'Progress update');
173
- }} catch (e) {{
174
- addLogEntry('Received: ' + event.data);
175
- }}
176
- }};
177
-
178
- ws.onclose = function() {{
179
- updateConnectionStatus('disconnected');
180
- addLogEntry('WebSocket connection closed');
181
- }};
182
-
183
- ws.onerror = function() {{
184
- updateConnectionStatus('disconnected');
185
- addLogEntry('WebSocket error occurred');
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
- <!DOCTYPE html>
200
- <html>
201
- <head>
202
- <title>WebSocket Test Page</title>
203
- <meta charset="UTF-8">
204
- <style>
205
- body {
206
- font-family: Arial, sans-serif;
207
- max-width: 800px;
208
- margin: 50px auto;
209
- padding: 20px;
210
- background-color: #f5f5f5;
211
- }
212
- .container {
213
- background: white;
214
- padding: 30px;
215
- border-radius: 10px;
216
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
217
- }
218
- .test-section {
219
- margin: 20px 0;
220
- padding: 15px;
221
- border: 1px solid #ddd;
222
- border-radius: 5px;
223
- }
224
- button {
225
- background: #007cba;
226
- color: white;
227
- border: none;
228
- padding: 10px 20px;
229
- border-radius: 5px;
230
- cursor: pointer;
231
- margin: 5px;
232
- }
233
- button:hover {
234
- background: #005a8b;
235
- }
236
- input[type="text"] {
237
- padding: 8px;
238
- border: 1px solid #ddd;
239
- border-radius: 3px;
240
- margin: 5px;
241
- width: 200px;
242
- }
243
- .status {
244
- margin: 10px 0;
245
- padding: 10px;
246
- border-radius: 5px;
247
- font-weight: bold;
248
- }
249
- .success {
250
- background: #d4edda;
251
- color: #155724;
252
- }
253
- .error {
254
- background: #f8d7da;
255
- color: #721c24;
256
- }
257
- .info {
258
- background: #d1ecf1;
259
- color: #0c5460;
260
- }
261
- #log {
262
- background: #1e1e1e;
263
- color: #fff;
264
- padding: 15px;
265
- border-radius: 5px;
266
- font-family: 'Courier New', monospace;
267
- max-height: 300px;
268
- overflow-y: auto;
269
- margin-top: 20px;
270
- }
271
- </style>
272
- </head>
273
- <body>
274
- <div class="container">
275
- <h1>🧪 WebSocket Test Page</h1>
276
- <p>Test WebSocket connectivity and server functionality</p>
277
-
278
- <div class="test-section">
279
- <h3>1. Server Status</h3>
280
- <button onclick="checkServerStatus()">Check Status</button>
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
- <script>
304
- let testWs = null;
305
- const log = document.getElementById('log');
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
- function addLog(message, type = 'info') {
308
- const timestamp = new Date().toLocaleTimeString();
309
- const div = document.createElement('div');
310
- div.textContent = `[${timestamp}] ${message}`;
311
- if (type === 'error') div.style.color = '#ff6b6b';
312
- if (type === 'success') div.style.color = '#51cf66';
313
- log.appendChild(div);
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
- function showStatus(elementId, message, type = 'info') {
318
- const element = document.getElementById(elementId);
319
- element.innerHTML = `<div class="status ${type}">${message}</div>`;
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
- async function checkServerStatus() {
323
- try {
324
- addLog('Checking server status...');
325
- const response = await fetch('/');
326
- const data = await response.json();
327
-
328
- showStatus('serverStatus',
329
- `Status: ${data.status}<br>` +
330
- `Active connections: ${data.active_connections}<br>` +
331
- `Jobs: ${data.jobs?.length || 0}`, 'success');
332
- addLog('Server status retrieved successfully', 'success');
333
- } catch (error) {
334
- showStatus('serverStatus', `Error: ${error.message}`, 'error');
335
- addLog(`Server status error: ${error.message}`, 'error');
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
- async function checkLatestJob() {
340
- try {
341
- addLog('Getting latest job...');
342
- const response = await fetch('/latest');
343
- const data = await response.json();
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
- function testWebSocket() {
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
- disconnectWebSocket();
369
-
370
- addLog(`Connecting to WebSocket for job: ${jobId}`);
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 disconnectWebSocket() {
400
- if (testWs) {
401
- testWs.close();
402
- testWs = null;
403
- showStatus('wsStatus', 'Disconnected', 'info');
404
- addLog('WebSocket disconnected');
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
- // Auto-check server status on load
409
- window.onload = function() {
410
- checkServerStatus();
389
+ testWs.onclose = function() {
390
+ showStatus('wsStatus', 'WebSocket closed', 'info');
391
+ addLog('WebSocket connection closed');
411
392
  };
412
- </script>
413
- </body>
414
- </html>
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://[INTERNAL_URL]/ws/progress/{latest_job_id}",
466
- "monitor_url": f"http://[INTERNAL_URL]/monitor/{latest_job_id}",
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())