Claude Code Claude Opus 4.6 commited on
Commit
aa7928a
·
1 Parent(s): da336f0

fix: Add timeout wrapper for uvicorn launch and health monitor stage reader

Browse files

- Add SIGALRM timeout wrapper (30s) to prevent indefinite hanging on port bind failure
- Add websockets>=12.0 to requirements.txt
- Add read_current_stage() function to health_monitor.py for Cain stage detection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. .openclaw/health_monitor.py +14 -0
  2. app.py +19 -0
  3. requirements.txt +1 -0
.openclaw/health_monitor.py CHANGED
@@ -806,6 +806,20 @@ def reset_health_monitor():
806
  _global_monitor = None
807
 
808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
  if __name__ == "__main__":
810
  # Test the health monitor
811
  print("=== Health Monitor Test ===\n")
 
806
  _global_monitor = None
807
 
808
 
809
+ def read_current_stage() -> str:
810
+ """
811
+ Read Cain's current stage from cain_status.json.
812
+ Returns a simple status string.
813
+ """
814
+ try:
815
+ status_file = Path.home() / ".openclaw" / "agents" / "cain_status.json"
816
+ with open(status_file) as f:
817
+ data = json.load(f)
818
+ return data.get('stage', data.get('current_state', 'UNKNOWN'))
819
+ except Exception:
820
+ return 'UNKNOWN'
821
+
822
+
823
  if __name__ == "__main__":
824
  # Test the health monitor
825
  print("=== Health Monitor Test ===\n")
app.py CHANGED
@@ -2286,6 +2286,22 @@ if __name__ == "__main__":
2286
  }
2287
 
2288
  # Explicit crash log for uvicorn launch failures
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2289
  try:
2290
  uvicorn.run(
2291
  app,
@@ -2294,8 +2310,11 @@ if __name__ == "__main__":
2294
  log_config=log_config,
2295
  log_level="debug"
2296
  )
 
 
2297
  log_startup("Uvicorn server stopped normally")
2298
  except Exception as launch_error:
 
2299
  # Write to crash.log immediately
2300
  crash_msg = f"[{datetime.utcnow().isoformat()}] FATAL: uvicorn.run() failed: {launch_error}\n"
2301
  with open(_crash_log_path, "a") as f:
 
2286
  }
2287
 
2288
  # Explicit crash log for uvicorn launch failures
2289
+ # Add timeout wrapper to prevent indefinite hanging on port binding failure
2290
+ import signal
2291
+ _server_started = False
2292
+ _launch_timeout = 30 # seconds
2293
+
2294
+ def timeout_handler(signum, frame):
2295
+ if not _server_started:
2296
+ crash_msg = f"[{datetime.utcnow().isoformat()}] FATAL: uvicorn launch timeout after {_launch_timeout}s - likely port bind failure\n"
2297
+ with open(_crash_log_path, "a") as f:
2298
+ f.write(crash_msg)
2299
+ print(f"[FATAL] Launch timeout - port {server_port} may be in use", file=sys.stderr)
2300
+ sys.exit(1)
2301
+
2302
+ signal.signal(signal.SIGALRM, timeout_handler)
2303
+ signal.alarm(_launch_timeout)
2304
+
2305
  try:
2306
  uvicorn.run(
2307
  app,
 
2310
  log_config=log_config,
2311
  log_level="debug"
2312
  )
2313
+ _server_started = True
2314
+ signal.alarm(0) # Cancel timeout
2315
  log_startup("Uvicorn server stopped normally")
2316
  except Exception as launch_error:
2317
+ signal.alarm(0) # Cancel timeout
2318
  # Write to crash.log immediately
2319
  crash_msg = f"[{datetime.utcnow().isoformat()}] FATAL: uvicorn.run() failed: {launch_error}\n"
2320
  with open(_crash_log_path, "a") as f:
requirements.txt CHANGED
@@ -4,3 +4,4 @@ uvicorn[standard]>=0.24.0
4
  requests>=2.31.0
5
  gradio>=4.0.0
6
  psutil>=5.9.0
 
 
4
  requests>=2.31.0
5
  gradio>=4.0.0
6
  psutil>=5.9.0
7
+ websockets>=12.0