Spaces:
Sleeping
Sleeping
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>
- .openclaw/health_monitor.py +14 -0
- app.py +19 -0
- 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
|