From d23f854c2546e3bd9b66e13c3bf3831f5b997bbd Mon Sep 17 00:00:00 2001 From: Chaim Date: Mon, 8 Jun 2026 09:09:08 +0000 Subject: [PATCH] fix(ops): self-restart/stop of the host bridge returns 200 (detached) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restarting/stopping legal-court-fetch-service from its own /pm2/control kills the process before it can reply — the client got a misleading 502 even though pm2 performed the restart. Detach the self-action (sleep 1; pm2 ...) so the HTTP response flushes first, and report success optimistically. Other targets are unchanged. Own name via COURT_FETCH_SERVICE_PM2_NAME (default legal-court-fetch-service). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/legal_mcp/court_fetch_service/server.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mcp-server/src/legal_mcp/court_fetch_service/server.py b/mcp-server/src/legal_mcp/court_fetch_service/server.py index a7e455a..c1c6c40 100644 --- a/mcp-server/src/legal_mcp/court_fetch_service/server.py +++ b/mcp-server/src/legal_mcp/court_fetch_service/server.py @@ -121,6 +121,10 @@ async def pm2_status(request: web.Request) -> web.Response: # Whitelisted to ``legal-`` names only — never paperclip or arbitrary processes. _PM2_ACTIONS = {"restart", "stop", "start"} +# Our own pm2 process name. Restarting/stopping ourselves kills this process +# mid-reply, so those self-actions are detached (see pm2_control). +_OWN_PM2_NAME = os.environ.get("COURT_FETCH_SERVICE_PM2_NAME", "legal-court-fetch-service") + async def pm2_control(request: web.Request) -> web.Response: """Run ``pm2 `` for a whitelisted legal-* process.""" @@ -143,6 +147,18 @@ async def pm2_control(request: web.Request) -> web.Response: {"error": "name must be a legal-* process"}, status=403 ) + # Self restart/stop kills this process before it can reply (client sees a + # dropped connection / 502) even though pm2 does perform the action. Detach + # it with a brief delay so the HTTP response flushes first, then report + # success optimistically. + if name == _OWN_PM2_NAME and action in ("restart", "stop"): + import asyncio as _asyncio + + await _asyncio.create_subprocess_shell(f"sleep 1; pm2 {action} {name} --silent") + return web.json_response( + {"ok": True, "action": action, "deferred": True, "service": None} + ) + try: rc, out, err = await _pm2_run(action, name, "--silent", timeout=30) if rc != 0: -- 2.49.1