@@ -21,19 +21,20 @@ import path from 'path';
2121
2222import { calculateSha1 } from '../../utils' ;
2323import { debug } from '../../utilsBundle' ;
24+
2425import { decorateServer } from '../../server/utils/network' ;
2526import { gracefullyProcessExitDoNotHang } from '../../server/utils/processLauncher' ;
2627
2728import { BrowserServerBackend } from '../../tools/browserServerBackend' ;
2829import { browserTools } from '../../tools/tools' ;
29- import { SocketConnection } from '../client/socketConnection' ;
30- import { commands } from './commands' ;
3130import { parseCommand } from './command' ;
31+ import { commands } from './commands' ;
32+
33+ import { SocketConnection } from '../client/socketConnection' ;
3234import { createClientInfo } from '../client/registry' ;
3335
3436import type * as playwright from '../../..' ;
3537import type * as tools from '../../tools/exports' ;
36- import type * as mcp from '../../mcp/exports' ;
3738import type { SessionConfig , ClientInfo } from '../client/registry' ;
3839import type { BrowserContext } from '../../client/browserContext' ;
3940
@@ -49,14 +50,17 @@ async function socketExists(socketPath: string): Promise<boolean> {
4950 return false ;
5051}
5152
52- export async function startMcpDaemonServer (
53+ export async function startCliDaemonServer (
5354 sessionName : string ,
5455 browserContext : playwright . BrowserContext ,
55- mcpConfig : tools . ContextConfig ,
56+ contextConfig : tools . ContextConfig = { } ,
5657 clientInfo = createClientInfo ( ) ,
57- persistent ?: boolean ,
58+ options ?: {
59+ persistent ?: boolean ,
60+ exitOnClose ?: boolean ,
61+ }
5862) : Promise < string > {
59- const sessionConfig = createSessionConfig ( clientInfo , sessionName , browserContext , persistent ) ;
63+ const sessionConfig = createSessionConfig ( clientInfo , sessionName , browserContext , options ) ;
6064 const { socketPath } = sessionConfig ;
6165
6266 // Clean up existing socket file on Unix
@@ -70,11 +74,14 @@ export async function startMcpDaemonServer(
7074 }
7175 }
7276
73- const backend = new BrowserServerBackend ( mcpConfig , browserContext , browserTools ) ;
77+ const backend = new BrowserServerBackend ( contextConfig , browserContext , browserTools ) ;
7478 await backend . initialize ( { cwd : process . cwd ( ) } ) ;
7579
7680 await fs . promises . mkdir ( path . dirname ( socketPath ) , { recursive : true } ) ;
7781
82+ if ( ( browserContext as BrowserContext ) . _closingStatus !== 'none' )
83+ throw new Error ( 'Browser context was closed before the daemon could start' ) ;
84+
7885 const server = net . createServer ( socket => {
7986 daemonDebug ( 'new client connection' ) ;
8087 const connection = new SocketConnection ( socket ) ;
@@ -87,15 +94,12 @@ export async function startMcpDaemonServer(
8794 daemonDebug ( 'received command' , method ) ;
8895 if ( method === 'stop' ) {
8996 daemonDebug ( 'stop command received, shutting down' ) ;
90- if ( process . platform !== 'win32' )
91- await fs . promises . unlink ( sessionConfig . socketPath ) . catch ( ( ) => { } ) ;
92- if ( ! sessionConfig . cli . persistent )
93- await deleteSessionFile ( clientInfo , sessionConfig ) ;
94-
95- gracefullyProcessExitDoNotHang ( 0 , async ( ) => {
96- await connection . send ( { id, result : 'ok' } ) . catch ( ( ) => { } ) ;
97- server . close ( ) ;
98- } ) ;
97+ await deleteSessionFile ( clientInfo , sessionConfig ) ;
98+ const sendAck = async ( ) => connection . send ( { id, result : 'ok' } ) . catch ( ( ) => { } ) ;
99+ if ( options ?. exitOnClose )
100+ gracefullyProcessExitDoNotHang ( 0 , ( ) => sendAck ( ) ) ;
101+ else
102+ await sendAck ( ) ;
99103 } else if ( method === 'run' ) {
100104 const { toolName, toolParams } = parseCliCommand ( params . args ) ;
101105 if ( params . cwd )
@@ -112,7 +116,13 @@ export async function startMcpDaemonServer(
112116 }
113117 } ;
114118 } ) ;
119+
115120 decorateServer ( server ) ;
121+ browserContext . on ( 'close' , ( ) => Promise . resolve ( ) . then ( async ( ) => {
122+ await deleteSessionFile ( clientInfo , sessionConfig ) ;
123+ if ( options ?. exitOnClose )
124+ gracefullyProcessExitDoNotHang ( 0 ) ;
125+ } ) ) ;
116126
117127 await new Promise < void > ( ( resolve , reject ) => {
118128 server . on ( 'error' , ( error : NodeJS . ErrnoException ) => {
@@ -133,17 +143,20 @@ async function saveSessionFile(clientInfo: ClientInfo, sessionConfig: SessionCon
133143}
134144
135145async function deleteSessionFile ( clientInfo : ClientInfo , sessionConfig : SessionConfig ) {
136- const sessionFile = path . join ( clientInfo . daemonProfilesDir , `${ sessionConfig . name } .session` ) ;
137- await fs . promises . rm ( sessionFile ) . catch ( ( ) => { } ) ;
146+ await fs . promises . unlink ( sessionConfig . socketPath ) . catch ( ( ) => { } ) ;
147+ if ( ! sessionConfig . cli . persistent ) {
148+ const sessionFile = path . join ( clientInfo . daemonProfilesDir , `${ sessionConfig . name } .session` ) ;
149+ await fs . promises . rm ( sessionFile ) . catch ( ( ) => { } ) ;
150+ }
138151}
139152
140- function formatResult ( result : mcp . CallToolResult ) {
153+ function formatResult ( result : tools . CallToolResult ) {
141154 const isError = result . isError ;
142155 const text = result . content [ 0 ] . type === 'text' ? result . content [ 0 ] . text : undefined ;
143156 return { isError, text } ;
144157}
145158
146- function parseCliCommand ( args : Record < string , string > & { _ : string [ ] } ) : { toolName : string , toolParams : NonNullable < mcp . CallToolRequest [ 'params' ] [ 'arguments' ] > } {
159+ function parseCliCommand ( args : Record < string , string > & { _ : string [ ] } ) : { toolName : string , toolParams : NonNullable < tools . CallToolRequest [ 'params' ] [ 'arguments' ] > } {
147160 const command = commands [ args . _ [ 0 ] ] ;
148161 if ( ! command )
149162 throw new Error ( 'Command is required' ) ;
@@ -159,15 +172,18 @@ function daemonSocketPath(clientInfo: ClientInfo, sessionName: string): string {
159172 return path . join ( socketsDir , clientInfo . workspaceDirHash , socketName ) ;
160173}
161174
162- function createSessionConfig ( clientInfo : ClientInfo , sessionName : string , browserContext : playwright . BrowserContext , persistent ?: boolean ) : SessionConfig {
175+ function createSessionConfig ( clientInfo : ClientInfo , sessionName : string , browserContext : playwright . BrowserContext , options : {
176+ persistent ?: boolean ,
177+ exitOnStop ?: boolean ,
178+ } = { } ) : SessionConfig {
163179 const bc = browserContext as BrowserContext ;
164180 return {
165181 name : sessionName ,
166182 version : clientInfo . version ,
167183 timestamp : Date . now ( ) ,
168184 socketPath : daemonSocketPath ( clientInfo , sessionName ) ,
169185 workspaceDir : clientInfo . workspaceDir ,
170- cli : { persistent } ,
186+ cli : { persistent : options . persistent } ,
171187 browser : {
172188 browserName : bc . browser ( ) ! . browserType ( ) . name ( ) ,
173189 launchOptions : bc . browser ( ) ! . _options ,
0 commit comments