diff --git a/src/daemon/spawner.py b/src/daemon/spawner.py index 64cb799..d8acc6a 100644 --- a/src/daemon/spawner.py +++ b/src/daemon/spawner.py @@ -1249,6 +1249,32 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ pass return result + async def _probe_gateway(self, timeout: float = 3.0) -> bool: + """TCP + WebSocket Upgrade 握手探测 Gateway 存活 + + #08: Phase -1 — 在 spawn 前探测 Gateway 进程是否可达。 + 不可达 → AgentBusyError(reason="gateway_down") → 任务留 pending → ticker 30s 后重试。 + """ + try: + reader, writer = await asyncio.wait_for( + asyncio.open_connection(self.gateway_host, self.gateway_port), + timeout=timeout, + ) + writer.write( + b'GET /ws HTTP/1.1\r\n' + b'Host: 127.0.0.1:%d\r\n' + b'Upgrade: websocket\r\n' + b'Connection: Upgrade\r\n' + b'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n' + b'Sec-WebSocket-Version: 13\r\n\r\n' % self.gateway_port + ) + await writer.drain() + resp = await asyncio.wait_for(reader.read(256), timeout=timeout) + writer.close() + return b'101' in resp + except Exception: + return False + @staticmethod def _classify_outcome(exit_code: int, json_result: dict, stderr_text: str, task_status: Optional[str], stdout_text: str = "") -> dict: