crackerjack 0.33.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 +4 -13
- 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 +104 -204
- 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 +171 -174
- 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 +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -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 +17 -16
- 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 +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- 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 +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- 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 +6 -35
- 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 +15 -9
- crackerjack/services/config_merge.py +19 -80
- 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 +27 -25
- 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 +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- 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 +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- 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 +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- 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.33.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +196 -25
- 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.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
crackerjack/mcp/file_monitor.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
+
import typing as t
|
|
4
5
|
from collections.abc import Callable
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
@@ -10,9 +11,10 @@ try:
|
|
|
10
11
|
|
|
11
12
|
WATCHDOG_AVAILABLE = True
|
|
12
13
|
except ImportError:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# Type stubs for when watchdog is not available
|
|
15
|
+
FileSystemEvent = t.Any
|
|
16
|
+
FileSystemEventHandler = t.Any
|
|
17
|
+
Observer = t.Any
|
|
16
18
|
WATCHDOG_AVAILABLE = False
|
|
17
19
|
|
|
18
20
|
import contextlib
|
|
@@ -28,7 +30,7 @@ if WATCHDOG_AVAILABLE:
|
|
|
28
30
|
|
|
29
31
|
class ProgressFileHandler(FileSystemEventHandler):
|
|
30
32
|
def __init__(
|
|
31
|
-
self, callback: Callable[[str, dict], None], progress_dir: Path
|
|
33
|
+
self, callback: Callable[[str, dict[str, t.Any]], None], progress_dir: Path
|
|
32
34
|
) -> None:
|
|
33
35
|
super().__init__()
|
|
34
36
|
self.callback = callback
|
|
@@ -43,7 +45,6 @@ if WATCHDOG_AVAILABLE:
|
|
|
43
45
|
try:
|
|
44
46
|
file_path = Path(event.src_path)
|
|
45
47
|
|
|
46
|
-
# Validate that the file path is within our allowed progress directory
|
|
47
48
|
validated_path = SecurePathValidator.validate_safe_path(
|
|
48
49
|
file_path, self.progress_dir
|
|
49
50
|
)
|
|
@@ -66,11 +67,9 @@ if WATCHDOG_AVAILABLE:
|
|
|
66
67
|
|
|
67
68
|
job_id = validated_path.stem.replace("job-", "")
|
|
68
69
|
except Exception:
|
|
69
|
-
# If path validation fails, skip processing this file
|
|
70
70
|
return
|
|
71
71
|
|
|
72
72
|
try:
|
|
73
|
-
# Validate file size before reading
|
|
74
73
|
SecurePathValidator.validate_file_size(validated_path)
|
|
75
74
|
|
|
76
75
|
with validated_path.open() as f:
|
|
@@ -88,7 +87,7 @@ if WATCHDOG_AVAILABLE:
|
|
|
88
87
|
else:
|
|
89
88
|
|
|
90
89
|
class ProgressFileHandler:
|
|
91
|
-
def __init__(self, callback: Callable[[str, dict], None]) -> None:
|
|
90
|
+
def __init__(self, callback: Callable[[str, dict[str, t.Any]], None]) -> None:
|
|
92
91
|
pass
|
|
93
92
|
|
|
94
93
|
|
|
@@ -96,7 +95,7 @@ class AsyncProgressMonitor:
|
|
|
96
95
|
def __init__(self, progress_dir: Path) -> None:
|
|
97
96
|
self.progress_dir = SecurePathValidator.validate_safe_path(progress_dir)
|
|
98
97
|
self.observer: Observer | None = None
|
|
99
|
-
self.subscribers: dict[str, set[Callable[[dict], None]]] = {}
|
|
98
|
+
self.subscribers: dict[str, set[Callable[[dict[str, t.Any]], None]]] = {}
|
|
100
99
|
self._running = False
|
|
101
100
|
|
|
102
101
|
self.progress_dir.mkdir(exist_ok=True)
|
|
@@ -132,14 +131,18 @@ class AsyncProgressMonitor:
|
|
|
132
131
|
|
|
133
132
|
console.print("[yellow]📁 Stopped progress directory monitoring[/ yellow]")
|
|
134
133
|
|
|
135
|
-
def subscribe(
|
|
134
|
+
def subscribe(
|
|
135
|
+
self, job_id: str, callback: Callable[[dict[str, t.Any]], None]
|
|
136
|
+
) -> None:
|
|
136
137
|
if job_id not in self.subscribers:
|
|
137
138
|
self.subscribers[job_id] = set()
|
|
138
139
|
|
|
139
140
|
self.subscribers[job_id].add(callback)
|
|
140
141
|
console.print(f"[cyan]📋 Subscribed to job updates: {job_id}[/ cyan]")
|
|
141
142
|
|
|
142
|
-
def unsubscribe(
|
|
143
|
+
def unsubscribe(
|
|
144
|
+
self, job_id: str, callback: Callable[[dict[str, t.Any]], None]
|
|
145
|
+
) -> None:
|
|
143
146
|
if job_id in self.subscribers:
|
|
144
147
|
self.subscribers[job_id].discard(callback)
|
|
145
148
|
|
|
@@ -148,7 +151,7 @@ class AsyncProgressMonitor:
|
|
|
148
151
|
|
|
149
152
|
console.print(f"[cyan]📋 Unsubscribed from job updates: {job_id}[/ cyan]")
|
|
150
153
|
|
|
151
|
-
def _on_file_changed(self, job_id: str, progress_data: dict) -> None:
|
|
154
|
+
def _on_file_changed(self, job_id: str, progress_data: dict[str, t.Any]) -> None:
|
|
152
155
|
if job_id in self.subscribers:
|
|
153
156
|
for callback in self.subscribers[job_id].copy():
|
|
154
157
|
try:
|
|
@@ -160,7 +163,7 @@ class AsyncProgressMonitor:
|
|
|
160
163
|
|
|
161
164
|
self.subscribers[job_id].discard(callback)
|
|
162
165
|
|
|
163
|
-
async def get_current_progress(self, job_id: str) -> dict | None:
|
|
166
|
+
async def get_current_progress(self, job_id: str) -> dict[str, t.Any] | None:
|
|
164
167
|
progress_file = self.progress_dir / f"job-{job_id}.json"
|
|
165
168
|
|
|
166
169
|
if not progress_file.exists():
|
|
@@ -168,7 +171,8 @@ class AsyncProgressMonitor:
|
|
|
168
171
|
|
|
169
172
|
try:
|
|
170
173
|
with progress_file.open() as f:
|
|
171
|
-
|
|
174
|
+
json_result = json.load(f)
|
|
175
|
+
return t.cast(dict[str, t.Any] | None, json_result)
|
|
172
176
|
except (json.JSONDecodeError, OSError):
|
|
173
177
|
return None
|
|
174
178
|
|
|
@@ -208,9 +212,9 @@ class AsyncProgressMonitor:
|
|
|
208
212
|
class PollingProgressMonitor:
|
|
209
213
|
def __init__(self, progress_dir: Path) -> None:
|
|
210
214
|
self.progress_dir = SecurePathValidator.validate_safe_path(progress_dir)
|
|
211
|
-
self.subscribers: dict[str, set[Callable[[dict], None]]] = {}
|
|
215
|
+
self.subscribers: dict[str, set[Callable[[dict[str, t.Any]], None]]] = {}
|
|
212
216
|
self._running = False
|
|
213
|
-
self._poll_task: asyncio.Task | None = None
|
|
217
|
+
self._poll_task: asyncio.Task[None] | None = None
|
|
214
218
|
self._file_mtimes: dict[str, float] = {}
|
|
215
219
|
|
|
216
220
|
self.progress_dir.mkdir(exist_ok=True)
|
|
@@ -252,7 +256,6 @@ class PollingProgressMonitor:
|
|
|
252
256
|
|
|
253
257
|
for progress_file in self.progress_dir.glob("job-*.json"):
|
|
254
258
|
try:
|
|
255
|
-
# Validate file path is within our allowed directory
|
|
256
259
|
validated_file = SecurePathValidator.validate_safe_path(
|
|
257
260
|
progress_file, self.progress_dir
|
|
258
261
|
)
|
|
@@ -267,7 +270,6 @@ class PollingProgressMonitor:
|
|
|
267
270
|
job_id = validated_file.stem.replace("job-", "")
|
|
268
271
|
|
|
269
272
|
try:
|
|
270
|
-
# Validate file size before reading
|
|
271
273
|
SecurePathValidator.validate_file_size(validated_file)
|
|
272
274
|
with validated_file.open() as f:
|
|
273
275
|
progress_data = json.load(f)
|
|
@@ -284,7 +286,7 @@ class PollingProgressMonitor:
|
|
|
284
286
|
|
|
285
287
|
self._file_mtimes = current_files
|
|
286
288
|
|
|
287
|
-
def _notify_subscribers(self, job_id: str, progress_data: dict) -> None:
|
|
289
|
+
def _notify_subscribers(self, job_id: str, progress_data: dict[str, t.Any]) -> None:
|
|
288
290
|
if job_id in self.subscribers:
|
|
289
291
|
for callback in self.subscribers[job_id].copy():
|
|
290
292
|
try:
|
|
@@ -295,14 +297,18 @@ class PollingProgressMonitor:
|
|
|
295
297
|
)
|
|
296
298
|
self.subscribers[job_id].discard(callback)
|
|
297
299
|
|
|
298
|
-
def subscribe(
|
|
300
|
+
def subscribe(
|
|
301
|
+
self, job_id: str, callback: Callable[[dict[str, t.Any]], None]
|
|
302
|
+
) -> None:
|
|
299
303
|
if job_id not in self.subscribers:
|
|
300
304
|
self.subscribers[job_id] = set()
|
|
301
305
|
|
|
302
306
|
self.subscribers[job_id].add(callback)
|
|
303
307
|
console.print(f"[cyan]📋 Subscribed to job updates: {job_id} (polling)[/ cyan]")
|
|
304
308
|
|
|
305
|
-
def unsubscribe(
|
|
309
|
+
def unsubscribe(
|
|
310
|
+
self, job_id: str, callback: Callable[[dict[str, t.Any]], None]
|
|
311
|
+
) -> None:
|
|
306
312
|
if job_id in self.subscribers:
|
|
307
313
|
self.subscribers[job_id].discard(callback)
|
|
308
314
|
|
|
@@ -313,7 +319,7 @@ class PollingProgressMonitor:
|
|
|
313
319
|
f"[cyan]📋 Unsubscribed from job updates: {job_id} (polling)[/ cyan]",
|
|
314
320
|
)
|
|
315
321
|
|
|
316
|
-
async def get_current_progress(self, job_id: str) -> dict | None:
|
|
322
|
+
async def get_current_progress(self, job_id: str) -> dict[str, t.Any] | None:
|
|
317
323
|
progress_file = self.progress_dir / f"job-{job_id}.json"
|
|
318
324
|
|
|
319
325
|
if not progress_file.exists():
|
|
@@ -321,7 +327,8 @@ class PollingProgressMonitor:
|
|
|
321
327
|
|
|
322
328
|
try:
|
|
323
329
|
with progress_file.open() as f:
|
|
324
|
-
|
|
330
|
+
json_result = json.load(f)
|
|
331
|
+
return t.cast(dict[str, t.Any] | None, json_result)
|
|
325
332
|
except (json.JSONDecodeError, OSError):
|
|
326
333
|
return None
|
|
327
334
|
|
|
@@ -109,7 +109,7 @@ class JobDataCollector:
|
|
|
109
109
|
status = data.get("status", "unknown")
|
|
110
110
|
stage = data.get("current_stage", "Unknown")
|
|
111
111
|
iteration = data.get("iteration", 0)
|
|
112
|
-
max_iterations = data.get("max_iterations",
|
|
112
|
+
max_iterations = data.get("max_iterations", 5)
|
|
113
113
|
|
|
114
114
|
status_emoji = {
|
|
115
115
|
"running": "🚀 Running",
|
|
@@ -138,7 +138,7 @@ class JobDataCollector:
|
|
|
138
138
|
)
|
|
139
139
|
|
|
140
140
|
async def _discover_jobs_websocket(self) -> dict[str, Any]:
|
|
141
|
-
jobs_data = {
|
|
141
|
+
jobs_data: dict[str, Any] = {
|
|
142
142
|
"active": 0,
|
|
143
143
|
"completed": 0,
|
|
144
144
|
"failed": 0,
|
|
@@ -155,11 +155,13 @@ class JobDataCollector:
|
|
|
155
155
|
with suppress(Exception):
|
|
156
156
|
async with timeout_manager.timeout_context(
|
|
157
157
|
"network_operations",
|
|
158
|
-
timeout=5.0,
|
|
158
|
+
timeout=5.0,
|
|
159
159
|
):
|
|
160
|
-
websocket_base = self.websocket_url.replace(
|
|
161
|
-
"
|
|
162
|
-
|
|
160
|
+
websocket_base = self.websocket_url.replace(
|
|
161
|
+
"ws: //", "http: //"
|
|
162
|
+
).replace(
|
|
163
|
+
"wss: //",
|
|
164
|
+
"https: //",
|
|
163
165
|
)
|
|
164
166
|
|
|
165
167
|
async with (
|
|
@@ -239,13 +241,13 @@ class ServiceHealthChecker:
|
|
|
239
241
|
try:
|
|
240
242
|
async with timeout_manager.timeout_context(
|
|
241
243
|
"network_operations",
|
|
242
|
-
timeout=3.0,
|
|
244
|
+
timeout=3.0,
|
|
243
245
|
):
|
|
244
246
|
async with (
|
|
245
247
|
aiohttp.ClientSession(
|
|
246
248
|
timeout=aiohttp.ClientTimeout(total=2),
|
|
247
249
|
) as session,
|
|
248
|
-
session.get("http
|
|
250
|
+
session.get("http: //localhost: 8675/") as response,
|
|
249
251
|
):
|
|
250
252
|
if response.status == 200:
|
|
251
253
|
data = await response.json()
|
|
@@ -267,7 +269,7 @@ class ServiceHealthChecker:
|
|
|
267
269
|
check=False,
|
|
268
270
|
capture_output=True,
|
|
269
271
|
text=True,
|
|
270
|
-
timeout=5.0,
|
|
272
|
+
timeout=5.0,
|
|
271
273
|
)
|
|
272
274
|
if result.returncode == 0:
|
|
273
275
|
return ("MCP Server", "🟢 Process Active", "0")
|
|
@@ -284,7 +286,7 @@ class ServiceHealthChecker:
|
|
|
284
286
|
check=False,
|
|
285
287
|
capture_output=True,
|
|
286
288
|
text=True,
|
|
287
|
-
timeout=5.0,
|
|
289
|
+
timeout=5.0,
|
|
288
290
|
)
|
|
289
291
|
if result.returncode == 0:
|
|
290
292
|
return ("Service Watchdog", "🟢 Active", "0")
|
|
@@ -300,7 +302,7 @@ class ErrorCollector:
|
|
|
300
302
|
self.console = Console()
|
|
301
303
|
|
|
302
304
|
async def collect_recent_errors(self) -> list[tuple[str, str, str, str]]:
|
|
303
|
-
errors = []
|
|
305
|
+
errors: list[tuple[str, str, str, str]] = []
|
|
304
306
|
|
|
305
307
|
errors.extend(self._check_debug_logs())
|
|
306
308
|
|
|
@@ -320,7 +322,7 @@ class ErrorCollector:
|
|
|
320
322
|
return errors[-5:]
|
|
321
323
|
|
|
322
324
|
def _check_debug_logs(self) -> list[tuple[str, str, str, str]]:
|
|
323
|
-
errors = []
|
|
325
|
+
errors: list[tuple[str, str, str, str]] = []
|
|
324
326
|
|
|
325
327
|
with suppress(Exception):
|
|
326
328
|
debug_log = Path(tempfile.gettempdir()) / "tui_debug.log"
|
|
@@ -333,7 +335,7 @@ class ErrorCollector:
|
|
|
333
335
|
self,
|
|
334
336
|
debug_log: Path,
|
|
335
337
|
) -> list[tuple[str, str, str, str]]:
|
|
336
|
-
errors = []
|
|
338
|
+
errors: list[tuple[str, str, str, str]] = []
|
|
337
339
|
|
|
338
340
|
with debug_log.open() as f:
|
|
339
341
|
lines = f.readlines()[-10:]
|
|
@@ -362,7 +364,7 @@ class ErrorCollector:
|
|
|
362
364
|
return message[:40] + "..." if len(message) > 40 else message
|
|
363
365
|
|
|
364
366
|
def _check_crackerjack_logs(self) -> list[tuple[str, str, str, str]]:
|
|
365
|
-
errors = []
|
|
367
|
+
errors: list[tuple[str, str, str, str]] = []
|
|
366
368
|
|
|
367
369
|
with suppress(Exception):
|
|
368
370
|
for log_file in Path(tempfile.gettempdir()).glob(
|
|
@@ -380,7 +382,7 @@ class ErrorCollector:
|
|
|
380
382
|
self,
|
|
381
383
|
log_file: Path,
|
|
382
384
|
) -> list[tuple[str, str, str, str]]:
|
|
383
|
-
errors = []
|
|
385
|
+
errors: list[tuple[str, str, str, str]] = []
|
|
384
386
|
|
|
385
387
|
with log_file.open() as f:
|
|
386
388
|
lines = f.readlines()[-5:]
|
|
@@ -415,7 +417,7 @@ class ErrorCollector:
|
|
|
415
417
|
|
|
416
418
|
class ServiceManager:
|
|
417
419
|
def __init__(self) -> None:
|
|
418
|
-
self.started_services: list[tuple[str, subprocess.Popen]] = []
|
|
420
|
+
self.started_services: list[tuple[str, subprocess.Popen[bytes]]] = []
|
|
419
421
|
self.console = Console()
|
|
420
422
|
|
|
421
423
|
async def ensure_services_running(self) -> None:
|
|
@@ -446,12 +448,12 @@ class ServiceManager:
|
|
|
446
448
|
with suppress(Exception):
|
|
447
449
|
async with timeout_manager.timeout_context(
|
|
448
450
|
"network_operations",
|
|
449
|
-
timeout=3.0,
|
|
451
|
+
timeout=3.0,
|
|
450
452
|
):
|
|
451
453
|
async with aiohttp.ClientSession(
|
|
452
454
|
timeout=aiohttp.ClientTimeout(total=2),
|
|
453
455
|
) as session:
|
|
454
|
-
async with session.get("http
|
|
456
|
+
async with session.get("http: //localhost: 8675/") as response:
|
|
455
457
|
return response.status == 200
|
|
456
458
|
return False
|
|
457
459
|
|
|
@@ -462,11 +464,19 @@ class ServiceManager:
|
|
|
462
464
|
check=False,
|
|
463
465
|
capture_output=True,
|
|
464
466
|
text=True,
|
|
465
|
-
timeout=5.0,
|
|
467
|
+
timeout=5.0,
|
|
466
468
|
)
|
|
467
469
|
return result.returncode == 0
|
|
468
470
|
return False
|
|
469
471
|
|
|
472
|
+
def collect_services_data(self) -> list[tuple[str, str, str]]:
|
|
473
|
+
"""Check all services and return their status information."""
|
|
474
|
+
mcp_status = "running" if self._check_mcp_server() else "stopped"
|
|
475
|
+
return [
|
|
476
|
+
("mcp_server", mcp_status, "localhost:8675"),
|
|
477
|
+
("websocket_server", "unknown", "localhost:8676"),
|
|
478
|
+
]
|
|
479
|
+
|
|
470
480
|
async def _start_websocket_server(self) -> None:
|
|
471
481
|
with suppress(Exception):
|
|
472
482
|
process = subprocess.Popen(
|
|
@@ -496,7 +506,7 @@ class ServiceManager:
|
|
|
496
506
|
check=False,
|
|
497
507
|
capture_output=True,
|
|
498
508
|
text=True,
|
|
499
|
-
timeout=5.0,
|
|
509
|
+
timeout=5.0,
|
|
500
510
|
)
|
|
501
511
|
if result.returncode == 0:
|
|
502
512
|
return
|
|
@@ -514,7 +524,7 @@ class ServiceManager:
|
|
|
514
524
|
self._cleanup_single_service(process)
|
|
515
525
|
self.started_services.clear()
|
|
516
526
|
|
|
517
|
-
def _cleanup_single_service(self, process: subprocess.Popen) -> None:
|
|
527
|
+
def _cleanup_single_service(self, process: subprocess.Popen[bytes]) -> None:
|
|
518
528
|
with suppress(Exception):
|
|
519
529
|
if process.poll() is not None:
|
|
520
530
|
return
|