@@ -20,7 +20,8 @@ class PlaywrightServer extends BaseHandler {
2020 this . dialogs = new Map ( ) ;
2121 this . elementHandles = new Map ( ) ;
2222 this . contextThrottling = new Map ( ) ;
23- this . counters = { browser : 0 , context : 0 , page : 0 , response : 0 , route : 0 , element : 0 } ;
23+ this . servers = new Map ( ) ;
24+ this . counters = { browser : 0 , context : 0 , page : 0 , response : 0 , route : 0 , element : 0 , server : 0 } ;
2425 }
2526
2627 initHandlers ( ) {
@@ -67,6 +68,9 @@ class PlaywrightServer extends BaseHandler {
6768 response : ( ) => this . handleResponse ( command , actionMethod ) ,
6869 mouse : ( ) => this . interactionHandler . handleMouse ( command , actionMethod ) ,
6970 keyboard : ( ) => this . interactionHandler . handleKeyboard ( command , actionMethod ) ,
71+ touchscreen : ( ) => this . interactionHandler . handleTouchscreen ( command , actionMethod ) ,
72+ frame : ( ) => this . frameHandler . handle ( command , actionMethod ) ,
73+ browserServer : ( ) => this . handleBrowserServer ( command , actionMethod )
7074 frame : ( ) = > this . frameHandler . handle ( command , actionMethod ) ,
7175 selectors : ( ) => this . selectorsHandler . handle ( command , actionMethod )
7276 } ) ;
@@ -77,9 +81,13 @@ class PlaywrightServer extends BaseHandler {
7781
7882 const directRegistry = CommandRegistry . create ( {
7983 launch : ( ) => this . launchBrowser ( command ) ,
84+ // Attach to existing browser servers/endpoints
85+ connect : ( ) => this . connect ( command ) ,
86+ connectOverCDP : ( ) => this . connectOverCDP ( command ) ,
8087 newContext : ( ) => this . newContext ( command ) ,
8188 close : ( ) => this . closeBrowser ( command ) ,
82- exit : ( ) => this . exit ( )
89+ exit : ( ) => this . exit ( ) ,
90+ launchServer : ( ) => this . launchServer ( command )
8391 } ) ;
8492
8593 return await ErrorHandler . safeExecute ( ( ) => this . executeWithRegistry ( directRegistry , command . action ) , { action : command . action } ) ;
@@ -241,6 +249,83 @@ class PlaywrightServer extends BaseHandler {
241249 }
242250 }
243251
252+ async handleBrowserServer ( command , method ) {
253+ const server = this . servers . get ( command . serverId ) ;
254+ if ( ! server ) throw new Error ( `Unknown BrowserServer: ${ command . serverId } ` ) ;
255+ const registry = CommandRegistry . create ( {
256+ close : async ( ) => { await server . close ( ) ; this . servers . delete ( command . serverId ) ; } ,
257+ kill : async ( ) => { try { await server . kill ( ) ; } finally { this . servers . delete ( command . serverId ) ; } }
258+ } ) ;
259+ logger . info ( `BROWSER_SERVER ${ method . toUpperCase ( ) } ` , { serverId : command . serverId } ) ;
260+ return await ErrorHandler . safeExecute ( ( ) => this . executeWithRegistry ( registry , method ) , { method, serverId : command . serverId } ) ;
261+ }
262+
263+ async launchServer ( command ) {
264+ try {
265+ const browserType = this . getBrowserType ( command . browser ) ;
266+ const launchOptions = this . buildLaunchOptions ( command ) ;
267+ const server = await browserType . launchServer ( launchOptions ) ;
268+ const serverId = this . generateId ( 'server' ) ;
269+ this . servers . set ( serverId , server ) ;
270+
271+ // Auto-cleanup on server close and notify
272+ try {
273+ server . on ?. ( 'close' , ( ) => {
274+ if ( this . servers . has ( serverId ) ) this . servers . delete ( serverId ) ;
275+ try { sendFramedResponse ( { objectId : serverId , event : 'close' , params : { } } ) ; } catch { }
276+ } ) ;
277+ } catch { }
278+
279+ const wsEndpoint = server . wsEndpoint ( ) ;
280+ let pid = null ;
281+ try { pid = server . process ( ) ? server . process ( ) . pid : null ; } catch { }
282+ return { serverId, wsEndpoint, pid } ;
283+ } catch ( error ) {
284+ logger . error ( 'launchServer error' , { message : error . message } ) ;
285+ throw error ;
286+ }
287+ }
288+
289+ // Attach to an existing BrowserServer (WebSocket endpoint)
290+ async connect ( command ) {
291+ try {
292+ const { browser, wsEndpoint, options } = command ;
293+ if ( ! wsEndpoint || typeof wsEndpoint !== 'string' ) throw new Error ( 'connect requires a wsEndpoint string' ) ;
294+ const browserType = this . getBrowserType ( browser ) ;
295+ const attached = await browserType . connect ( wsEndpoint , options || { } ) ;
296+ const browserId = this . generateId ( 'browser' ) ;
297+ this . browsers . set ( browserId , attached ) ;
298+ // Create a fresh context to align with launch return shape
299+ const context = await attached . newContext ( ) ;
300+ const contextId = this . generateId ( 'context' ) ;
301+ this . contexts . set ( contextId , { context, browserId, id : contextId } ) ;
302+ return { browserId, defaultContextId : contextId , version : attached . version ( ) } ;
303+ } catch ( error ) {
304+ logger . error ( 'connect error' , { message : error . message } ) ;
305+ throw error ;
306+ }
307+ }
308+
309+ // Attach over CDP (Chromium only)
310+ async connectOverCDP ( command ) {
311+ try {
312+ const { endpointURL, options, browser } = command ;
313+ if ( ! endpointURL || typeof endpointURL !== 'string' ) throw new Error ( 'connectOverCDP requires an endpointURL string' ) ;
314+ const browserName = browser || 'chromium' ;
315+ if ( browserName !== 'chromium' ) throw new Error ( 'connectOverCDP is only supported for chromium' ) ;
316+ const attached = await chromium . connectOverCDP ( endpointURL , options || { } ) ;
317+ const browserId = this . generateId ( 'browser' ) ;
318+ this . browsers . set ( browserId , attached ) ;
319+ const context = await attached . newContext ( ) ;
320+ const contextId = this . generateId ( 'context' ) ;
321+ this . contexts . set ( contextId , { context, browserId, id : contextId } ) ;
322+ return { browserId, defaultContextId : contextId , version : attached . version ( ) } ;
323+ } catch ( error ) {
324+ logger . error ( 'connectOverCDP error' , { message : error . message } ) ;
325+ throw error ;
326+ }
327+ }
328+
244329 async exit ( ) {
245330 logger . info ( 'Shutting down server' ) ;
246331 for ( const browser of this . browsers . values ( ) ) {
0 commit comments