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.

Files changed (198) 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 +4 -13
  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 +104 -204
  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 +171 -174
  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 +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -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 +17 -16
  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 +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  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 +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +605 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.1.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  import time
4
+ import typing as t
4
5
  import uuid
5
6
  from contextlib import suppress
6
7
  from pathlib import Path
@@ -17,7 +18,6 @@ console = Console()
17
18
 
18
19
  class JobManager:
19
20
  def __init__(self, progress_dir: Path) -> None:
20
- # Validate and secure the progress directory path
21
21
  self.progress_dir = SecurePathValidator.validate_safe_path(progress_dir)
22
22
  self.active_connections: dict[str, set[Any]] = {}
23
23
  self.known_jobs: set[str] = set()
@@ -26,16 +26,13 @@ class JobManager:
26
26
  self.progress_dir.mkdir(exist_ok=True)
27
27
 
28
28
  def validate_job_id(self, job_id: str) -> bool:
29
- """Validate job ID using secure input validator."""
30
29
  if not job_id:
31
30
  return False
32
31
 
33
- # First check if it's a valid UUID
34
32
  with suppress(ValueError):
35
33
  uuid.UUID(job_id)
36
34
  return True
37
35
 
38
- # Use secure input validator for additional validation
39
36
  result = get_input_validator().validate_job_id(job_id)
40
37
  return result.valid
41
38
 
@@ -50,47 +47,43 @@ class JobManager:
50
47
  if not self.active_connections[job_id]:
51
48
  del self.active_connections[job_id]
52
49
 
53
- async def broadcast_to_job(self, job_id: str, data: dict) -> None:
50
+ async def broadcast_to_job(self, job_id: str, data: dict[str, t.Any]) -> None:
54
51
  if job_id not in self.active_connections:
55
52
  return
56
53
 
57
54
  timeout_manager = get_timeout_manager()
58
55
  connections = self.active_connections[job_id].copy()
59
56
 
60
- # Create websocket send tasks
61
57
  send_tasks = self._create_broadcast_tasks(connections, timeout_manager, data)
62
58
 
63
- # Execute broadcast with timeout handling
64
59
  if send_tasks:
65
60
  await self._execute_broadcast_tasks(job_id, send_tasks)
66
61
 
67
62
  def _create_broadcast_tasks(
68
- self, connections: set, timeout_manager, data: dict
69
- ) -> list:
70
- """Create tasks for all websocket sends with timeout."""
63
+ self, connections: set[t.Any], timeout_manager: t.Any, data: dict[str, t.Any]
64
+ ) -> list[tuple[t.Any, asyncio.Task[t.Any]]]:
71
65
  send_tasks = []
72
66
  for websocket in connections:
73
67
  task = asyncio.create_task(
74
68
  timeout_manager.with_timeout(
75
69
  "websocket_broadcast",
76
70
  websocket.send_json(data),
77
- timeout=2.0, # Quick broadcast timeout
71
+ timeout=2.0,
78
72
  )
79
73
  )
80
74
  send_tasks.append((websocket, task))
81
75
  return send_tasks
82
76
 
83
- async def _execute_broadcast_tasks(self, job_id: str, send_tasks: list) -> None:
84
- """Execute broadcast tasks with timeout and error handling."""
77
+ async def _execute_broadcast_tasks(
78
+ self, job_id: str, send_tasks: list[t.Any]
79
+ ) -> None:
85
80
  try:
86
- # Use asyncio.wait with timeout for batch sending
87
81
  done, pending = await asyncio.wait(
88
82
  [task for _, task in send_tasks],
89
- timeout=5.0, # Overall timeout for all broadcasts
83
+ timeout=5.0,
90
84
  return_when=asyncio.ALL_COMPLETED,
91
85
  )
92
86
 
93
- # Handle completed and pending tasks
94
87
  await self._handle_broadcast_results(job_id, send_tasks, done, pending)
95
88
 
96
89
  except Exception as e:
@@ -98,10 +91,12 @@ class JobManager:
98
91
  await self._cleanup_failed_broadcast(job_id, send_tasks)
99
92
 
100
93
  async def _handle_broadcast_results(
101
- self, job_id: str, send_tasks: list, done: set, pending: set
94
+ self,
95
+ job_id: str,
96
+ send_tasks: list[t.Any],
97
+ done: set[t.Any],
98
+ pending: set[t.Any],
102
99
  ) -> None:
103
- """Handle results of broadcast tasks."""
104
- # Cancel any pending tasks and remove failed connections
105
100
  for websocket, task in send_tasks:
106
101
  if task in pending:
107
102
  task.cancel()
@@ -112,12 +107,12 @@ class JobManager:
112
107
  except Exception:
113
108
  self.remove_connection(job_id, websocket)
114
109
 
115
- # Wait for cancelled tasks to complete
116
110
  if pending:
117
111
  await asyncio.gather(*pending, return_exceptions=True)
118
112
 
119
- async def _cleanup_failed_broadcast(self, job_id: str, send_tasks: list) -> None:
120
- """Clean up connections after broadcast failure."""
113
+ async def _cleanup_failed_broadcast(
114
+ self, job_id: str, send_tasks: list[t.Any]
115
+ ) -> None:
121
116
  for websocket, task in send_tasks:
122
117
  if not task.done():
123
118
  task.cancel()
@@ -127,7 +122,7 @@ class JobManager:
127
122
  if not self.progress_dir.exists():
128
123
  return None
129
124
 
130
- progress_files = list(self.progress_dir.glob("job-*.json"))
125
+ progress_files = list[t.Any](self.progress_dir.glob("job-*.json"))
131
126
  if not progress_files:
132
127
  return None
133
128
 
@@ -139,11 +134,10 @@ class JobManager:
139
134
  progress_file.stem[4:] if progress_file.stem.startswith("job -") else None
140
135
  )
141
136
 
142
- def get_job_progress(self, job_id: str) -> dict | None:
137
+ def get_job_progress(self, job_id: str) -> dict[str, t.Any] | None:
143
138
  if not self.validate_job_id(job_id):
144
139
  return None
145
140
 
146
- # Use secure path joining to create progress file path
147
141
  try:
148
142
  progress_file = SecurePathValidator.secure_path_join(
149
143
  self.progress_dir, f"job-{job_id}.json"
@@ -151,21 +145,18 @@ class JobManager:
151
145
  if not progress_file.exists():
152
146
  return None
153
147
 
154
- # Validate file size before reading
155
148
  SecurePathValidator.validate_file_size(progress_file)
156
149
 
157
- return json.loads(progress_file.read_text())
150
+ return json.loads(progress_file.read_text()) # type: ignore[no-any-return]
158
151
  except (json.JSONDecodeError, OSError):
159
152
  return None
160
153
 
161
154
  async def _process_progress_file(self, progress_file: Path) -> None:
162
- # Validate the progress file path is within our allowed directory
163
155
  try:
164
156
  validated_file = SecurePathValidator.validate_safe_path(
165
157
  progress_file, self.progress_dir
166
158
  )
167
159
  except Exception:
168
- # If path validation fails, skip processing this file
169
160
  return
170
161
 
171
162
  job_id = self.extract_job_id_from_file(validated_file)
@@ -185,20 +176,18 @@ class JobManager:
185
176
 
186
177
  while self.is_running:
187
178
  try:
188
- # Monitor directory changes with timeout protection
189
179
  async with timeout_manager.timeout_context(
190
180
  "file_operations",
191
- timeout=10.0, # Timeout for directory monitoring cycle
181
+ timeout=10.0,
192
182
  strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
193
183
  ):
194
184
  if self.progress_dir.exists():
195
- # Process files with individual timeouts
196
185
  for progress_file in self.progress_dir.glob("job-*.json"):
197
186
  try:
198
187
  await timeout_manager.with_timeout(
199
188
  "file_operations",
200
189
  self._process_progress_file(progress_file),
201
- timeout=5.0, # Per-file timeout
190
+ timeout=5.0,
202
191
  )
203
192
  except Exception as e:
204
193
  console.print(
@@ -206,7 +195,6 @@ class JobManager:
206
195
  )
207
196
  continue
208
197
 
209
- # Reset error count on successful cycle
210
198
  consecutive_errors = 0
211
199
  await asyncio.sleep(1)
212
200
 
@@ -214,14 +202,12 @@ class JobManager:
214
202
  consecutive_errors += 1
215
203
  console.print(f"[red]Progress monitoring error: {e}[/red]")
216
204
 
217
- # Implement exponential backoff for repeated errors
218
205
  if consecutive_errors >= max_consecutive_errors:
219
206
  console.print(
220
207
  f"[red]Too many consecutive errors ({consecutive_errors}), stopping monitor[/red]"
221
208
  )
222
209
  break
223
210
 
224
- # Exponential backoff with max delay
225
211
  delay = min(5 * (2 ** (consecutive_errors - 1)), 60)
226
212
  await asyncio.sleep(delay)
227
213
 
@@ -232,19 +218,20 @@ class JobManager:
232
218
  timeout_manager = get_timeout_manager()
233
219
 
234
220
  try:
235
- # Start file monitor with timeout protection
236
221
  async with timeout_manager.timeout_context(
237
222
  "file_operations",
238
- timeout=30.0, # Monitor startup timeout
223
+ timeout=30.0,
239
224
  strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
240
225
  ):
241
226
  monitor = create_progress_monitor(self.progress_dir)
242
227
  await monitor.start()
243
228
 
244
- def on_progress_update(job_id: str, progress_data: dict) -> None:
229
+ def on_progress_update(
230
+ job_id: str, progress_data: dict[str, t.Any]
231
+ ) -> None:
245
232
  if job_id and self.validate_job_id(job_id):
246
- # Create broadcast task with timeout handling
247
- async def safe_broadcast():
233
+
234
+ async def safe_broadcast() -> None:
248
235
  try:
249
236
  await timeout_manager.with_timeout(
250
237
  "websocket_broadcast",
@@ -256,13 +243,12 @@ class JobManager:
256
243
  f"[yellow]Broadcast failed for job {job_id}: {e}[/yellow]"
257
244
  )
258
245
 
259
- asyncio.create_task(safe_broadcast())
246
+ asyncio.create_task(safe_broadcast()) # type: ignore[no-untyped-call]
260
247
 
261
248
  if job_id not in self.known_jobs:
262
249
  self.known_jobs.add(job_id)
263
250
  console.print(f"[green]New job detected: {job_id}[/green]")
264
251
 
265
- # Start directory monitoring with proper timeout handling
266
252
  await self._monitor_directory_changes()
267
253
 
268
254
  except Exception as e:
@@ -273,18 +259,17 @@ class JobManager:
273
259
 
274
260
  while self.is_running:
275
261
  try:
276
- # Cleanup cycle with timeout protection
277
262
  await timeout_manager.with_timeout(
278
263
  "file_operations",
279
264
  self._perform_cleanup_cycle(),
280
- timeout=30.0, # Cleanup timeout
265
+ timeout=30.0,
281
266
  strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
282
267
  )
283
- await asyncio.sleep(3600) # 1 hour between cleanups
268
+ await asyncio.sleep(3600)
284
269
  except Exception as e:
285
270
  console.print(f"[red]Cleanup error: {e}[/red]")
286
- # Shorter sleep on error to retry sooner
287
- await asyncio.sleep(1800) # 30 minutes on error
271
+
272
+ await asyncio.sleep(1800)
288
273
 
289
274
  async def _perform_cleanup_cycle(self) -> None:
290
275
  if not self.progress_dir.exists():
@@ -318,17 +303,16 @@ class JobManager:
318
303
 
319
304
  while self.is_running:
320
305
  try:
321
- # Timeout check with its own timeout protection
322
306
  await timeout_manager.with_timeout(
323
307
  "file_operations",
324
308
  self._check_and_timeout_stuck_jobs(),
325
- timeout=60.0, # Timeout check timeout
309
+ timeout=60.0,
326
310
  strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
327
311
  )
328
- await asyncio.sleep(300) # 5 minutes between checks
312
+ await asyncio.sleep(300)
329
313
  except Exception as e:
330
314
  console.print(f"[red]Timeout check error: {e}[/red]")
331
- # Continue checking even on errors
315
+
332
316
  await asyncio.sleep(300)
333
317
 
334
318
  async def _check_and_timeout_stuck_jobs(self) -> None:
@@ -352,12 +336,10 @@ class JobManager:
352
336
  timeout_seconds: int,
353
337
  ) -> None:
354
338
  try:
355
- # Validate the progress file path is secure
356
339
  validated_file = SecurePathValidator.validate_safe_path(
357
340
  progress_file, self.progress_dir
358
341
  )
359
342
 
360
- # Validate file size before reading
361
343
  SecurePathValidator.validate_file_size(validated_file)
362
344
 
363
345
  progress_data = json.loads(validated_file.read_text())
@@ -371,12 +353,11 @@ class JobManager:
371
353
  self._timeout_job(progress_data, validated_file)
372
354
 
373
355
  except (json.JSONDecodeError, OSError, Exception):
374
- # Catch validation errors as well as file errors
375
356
  pass
376
357
 
377
358
  def _should_timeout_job(
378
359
  self,
379
- progress_data: dict,
360
+ progress_data: dict[str, t.Any],
380
361
  progress_file: Path,
381
362
  current_time: float,
382
363
  timeout_seconds: int,
@@ -386,7 +367,9 @@ class JobManager:
386
367
  and current_time - progress_file.stat().st_mtime > timeout_seconds
387
368
  )
388
369
 
389
- def _timeout_job(self, progress_data: dict, progress_file: Path) -> None:
370
+ def _timeout_job(
371
+ self, progress_data: dict[str, t.Any], progress_file: Path
372
+ ) -> None:
390
373
  progress_data["status"] = "failed"
391
374
  progress_data["message"] = "Job timed out (no updates for 30 minutes)"
392
375