diff --git a/browser_use/browser/session.py b/browser_use/browser/session.py index 8ebddac20..b6dbb88e2 100644 --- a/browser_use/browser/session.py +++ b/browser_use/browser/session.py @@ -721,6 +721,10 @@ class BrowserSession(BaseModel): # Create fresh event bus self.event_bus = EventBus() + async def close(self) -> None: + """Alias for stop().""" + await self.stop() + @observe_debug(ignore_input=True, ignore_output=True, name='browser_start_event_handler') async def on_BrowserStartEvent(self, event: BrowserStartEvent) -> dict[str, str]: """Handle browser start request. diff --git a/browser_use/browser/watchdogs/local_browser_watchdog.py b/browser_use/browser/watchdogs/local_browser_watchdog.py index 9f2c3d30c..de56f7cb1 100644 --- a/browser_use/browser/watchdogs/local_browser_watchdog.py +++ b/browser_use/browser/watchdogs/local_browser_watchdog.py @@ -126,7 +126,7 @@ class LocalBrowserWatchdog(BaseWatchdog): self.logger.debug(f'[LocalBrowserWatchdog] 📦 Using custom local browser executable_path= {browser_path}') else: # self.logger.debug('[LocalBrowserWatchdog] 🔍 Looking for local browser binary path...') - # Try fallback paths first (system browsers preferred) + # Try fallback paths first (Playwright's Chromium preferred by default) browser_path = self._find_installed_browser_path(channel=profile.channel) if not browser_path: self.logger.error( @@ -224,9 +224,9 @@ class LocalBrowserWatchdog(BaseWatchdog): Falls back to all known browser paths if the channel-specific search fails. Prioritizes: - 1. Channel-specific paths (if channel is set) - 2. System Chrome stable - 3. Playwright chromium + 1. Channel-specific paths (if channel is set to a non-default value) + 2. Playwright bundled Chromium (when no channel or default channel specified) + 3. System Chrome stable 4. Other system native browsers (Chromium -> Chrome Canary/Dev -> Brave -> Edge) 5. Playwright headless-shell fallback @@ -313,14 +313,14 @@ class LocalBrowserWatchdog(BaseWatchdog): BrowserChannel.MSEDGE_CANARY: 'msedge', } - # If a non-default channel is specified, put matching patterns first, then the rest as fallback + # Prioritize the target browser group, then fall back to the rest. if channel and channel != BROWSERUSE_DEFAULT_CHANNEL and channel in _channel_to_group: target_group = _channel_to_group[channel] - prioritized = [p for g, p in all_patterns if g == target_group] - rest = [p for g, p in all_patterns if g != target_group] - patterns = prioritized + rest else: - patterns = [p for _, p in all_patterns] + target_group = _channel_to_group[BROWSERUSE_DEFAULT_CHANNEL] + prioritized = [p for g, p in all_patterns if g == target_group] + rest = [p for g, p in all_patterns if g != target_group] + patterns = prioritized + rest for pattern in patterns: # Expand user home directory @@ -362,7 +362,7 @@ class LocalBrowserWatchdog(BaseWatchdog): import platform # Build command - only use --with-deps on Linux (it fails on Windows/macOS) - cmd = ['uvx', 'playwright', 'install', 'chrome'] + cmd = ['uvx', 'playwright', 'install', 'chromium'] if platform.system() == 'Linux': cmd.append('--with-deps') @@ -380,7 +380,7 @@ class LocalBrowserWatchdog(BaseWatchdog): if browser_path: return browser_path self.logger.error(f'[LocalBrowserWatchdog] ❌ Playwright local browser installation error: \n{stdout}\n{stderr}') - raise RuntimeError('No local browser path found after: uvx playwright install chrome') + raise RuntimeError('No local browser path found after: uvx playwright install chromium') except TimeoutError: # Kill the subprocess if it times out process.kill() diff --git a/browser_use/mcp/server.py b/browser_use/mcp/server.py index bb4e9be7b..d27d1f40c 100644 --- a/browser_use/mcp/server.py +++ b/browser_use/mcp/server.py @@ -1227,18 +1227,21 @@ class BrowserUseServer: raise RuntimeError('MCP stdio transport requires stdin, but this process was launched without one.') async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): - await self.server.run( - read_stream, - write_stream, - InitializationOptions( - server_name='browser-use', - server_version='0.1.0', - capabilities=self.server.get_capabilities( - notification_options=NotificationOptions(), - experimental_capabilities={}, + try: + await self.server.run( + read_stream, + write_stream, + InitializationOptions( + server_name='browser-use', + server_version='0.1.0', + capabilities=self.server.get_capabilities( + notification_options=NotificationOptions(), + experimental_capabilities={}, + ), ), - ), - ) + ) + except BrokenPipeError: + logger.warning('MCP client disconnected while writing to stdio; shutting down server cleanly.') async def main(session_timeout_minutes: int = 10):