diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index cacb61ae6..9ddb46d74 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -674,6 +674,67 @@ def gateway( console.print(f"[green]✓[/green] Heartbeat: every {hb_cfg.interval_s}s") + async def _health_server(host: str, health_port: int): + """Lightweight HTTP health endpoint on the gateway port.""" + import json as _json + import time + + start_time = time.monotonic() + + async def handle(reader, writer): + try: + data = await asyncio.wait_for(reader.read(4096), timeout=5) + except (asyncio.TimeoutError, ConnectionError): + writer.close() + return + + request_line = data.split(b"\r\n", 1)[0].decode("utf-8", errors="replace") + method, path = "", "" + parts = request_line.split(" ") + if len(parts) >= 2: + method, path = parts[0], parts[1] + + if method == "GET" and path == "/health": + uptime_s = int(time.monotonic() - start_time) + body = _json.dumps({ + "service": "nanobot", + "version": __version__, + "status": "running", + "uptime_seconds": uptime_s, + "channels": channels.enabled_channels, + }) + resp = ( + f"HTTP/1.0 200 OK\r\n" + f"Content-Type: application/json\r\n" + f"Content-Length: {len(body)}\r\n" + f"\r\n{body}" + ) + elif method == "GET" and path == "/": + body = "nanobot" + resp = ( + f"HTTP/1.0 200 OK\r\n" + f"Content-Type: text/plain\r\n" + f"Content-Length: {len(body)}\r\n" + f"\r\n{body}" + ) + else: + body = "Not Found" + resp = ( + f"HTTP/1.0 404 Not Found\r\n" + f"Content-Type: text/plain\r\n" + f"Content-Length: {len(body)}\r\n" + f"\r\n{body}" + ) + + writer.write(resp.encode()) + await writer.drain() + writer.close() + + server = await asyncio.start_server(handle, host, health_port) + console.print(f"[green]✓[/green] Health endpoint: http://{host}:{health_port}/health") + async with server: + await server.serve_forever() + async def run(): try: await cron.start() @@ -681,6 +742,7 @@ def gateway( await asyncio.gather( agent.run(), channels.start_all(), + _health_server(config.gateway.host, port), ) except KeyboardInterrupt: console.print("\nShutting down...")