From b50771dd97d884c68081d01cf91bc06dc266ceaf Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Fri, 22 May 2026 17:04:48 +0200 Subject: [PATCH] fix(mcp): tear down dashboard server on cli show --kill The --kill handler in dashboardApp.ts only called gracefullyProcessExitDoNotHang and never tore down the dashboard's HTTP/WS server. As a result DashboardConnection.onclose never fired, AttachedPage.dispose never ran, and the active screencast pipeline to the daemon-side browser was still up when the daemon then tried to gracefully close it. On Windows that race let the 30s gracefullyProcessExitDoNotHang safety timer win every time, making cli show --kill consistently take ~30s in MCP CI jobs. - DashboardServer.close() now closes WS connections before stopping the HTTP server (matches the WSServer.close() / CDPRelay.stop() pattern). - The --kill branch awaits dashboard.close() before scheduling exit. - socket.end() moves into gracefullyProcessExitDoNotHang's onExit so the cli client only sees the IPC FIN at actual process exit. --- .../src/tools/dashboard/dashboardApp.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/tools/dashboard/dashboardApp.ts b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts index cf338ec2af472..fb35e817467cb 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardApp.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts @@ -107,7 +107,11 @@ async function startDashboardServer(provider: SessionProvider, options: Dashboar return await Promise.race([...connections].map(c => c.emitAnnotate({ signal: cancellation }))); }; - const close = () => httpServer.stop(); + const close = async () => { + for (const c of connections) + c.close?.(); + await httpServer.stop(); + }; return { url: httpServer.urlPrefix('human-readable'), reveal, triggerAnnotate, close }; } @@ -355,8 +359,8 @@ async function startApp(server: net.Server, options: DashboardOptions) { socket.end(e); } } else if (parsed.kill) { - socket.end(); - gracefullyProcessExitDoNotHang(0); + await dashboard.close().catch(() => {}); + gracefullyProcessExitDoNotHang(0, () => new Promise(r => socket.end(r))); } else { try { await page?.bringToFront();