feat: add heartbeat/ping to terminal WebSocket

- Backend: Send ping every 30s from WebSocket endpoint
- Frontend: Respond to pings with pongs, detect missed pings (60s timeout)
- Update type definitions to include 'resetting' status

Refs: persistent-terminal-sessions task 6.4
This commit is contained in:
2026-05-24 12:45:50 +00:00
parent d117047711
commit 073013bc61
13 changed files with 468 additions and 24 deletions
+17 -2
View File
@@ -86,13 +86,14 @@ async def terminal_websocket(
# Send connected status
await websocket.send_json({"type": "status", "status": "connected"})
# Start I/O loops
# Start I/O loops and heartbeat
read_task = asyncio.create_task(_read_loop(session, websocket))
write_task = asyncio.create_task(_write_loop(session, websocket))
heartbeat_task = asyncio.create_task(_heartbeat_loop(websocket))
# Wait for either task to complete (indicating disconnect or error)
done, pending = await asyncio.wait(
[read_task, write_task],
[read_task, write_task, heartbeat_task],
return_when=asyncio.FIRST_COMPLETED,
)
@@ -181,6 +182,20 @@ async def _write_loop(session, websocket) -> None:
pass
async def _heartbeat_loop(websocket: WebSocket) -> None:
"""Send periodic ping messages to detect disconnections."""
try:
while True:
await asyncio.sleep(30) # Ping every 30 seconds
try:
await websocket.send_json({"type": "ping"})
except Exception:
# WebSocket is closed or broken
break
except Exception:
pass
@router.post(
"/projects/{project_id}/repositories/{repo_id}/instances/{instance_id}/terminal/reset",
summary="Reset terminal session",