The session server had a race condition where concurrent CLI calls could
spawn multiple server processes. The old server would be orphaned when
the new one overwrote the PID file and deleted its socket.
Changes:
- Add flock-based locking using portalocker (already a dependency)
- Server acquires exclusive lock before writing PID file
- CLI checks both is_server_running() AND is_session_locked()
- Add kill_orphaned_server() to clean up servers with PID but no lock
- Increase socket timeout from 0.1s to 0.5s for reliability
- cleanup_session_files() now removes .lock and .meta files
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename tunnel_manager.py to tunnel.py
- Remove dead code: commands/tunnel.py (session-scoped implementation never reached)
- Remove TunnelInfo dataclass and tunnels field from sessions.py
- Remove tunnel routing from server.py (unreachable code path)
- Update imports in main.py, doctor.py, setup.py, and tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use ctypes.OpenProcess on Windows for process existence check
(os.kill raises WinError 87 or SystemError on Windows)
- Use 127.0.0.1 instead of localhost to avoid IPv6 binding issues
- Add reuse_address=True to asyncio.start_server for TCP port reuse
Fixes issues reported in:
- #3912 (os.kill fails on Windows)
- #3918 (WinError 87 in is_server_running)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New `tunnel` command: expose localhost ports via Cloudflare quick tunnels
(`bu tunnel 3000` → trycloudflare.com URL, idempotent, auto-cleanup on session close)
- New `skills/remote-browser/SKILL.md`: guides sandboxed agents through
cloud browser + tunnel workflow
- TunnelInfo dataclass on SessionInfo for tunnel lifecycle management
- Tunnel dispatch in both SessionServer and CLI arg parser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This adds support for connecting to an existing browser instance via
Chrome DevTools Protocol (CDP) URL in the skill_cli commands.
## Why
The current skill_cli only supports three browser modes:
- chromium: Launches a new Playwright-managed Chromium
- real: Launches users Chrome with profile
- remote: Uses Browser-Use Cloud
However, many use cases require connecting to an already-running browser:
- Remote browser automation via SSH tunnels
- Connecting to browsers in Docker containers
- Reusing authenticated browser sessions
- Integration with Claude Code on remote servers
The BrowserSession class already supports cdp_url, but it was not
exposed through the skill_cli. The TUI mode has this option, but the
simple CLI commands did not.
## Changes
- main.py: Added --cdp-url argument and pass-through to server
- server.py: Accept and store cdp_url, pass to session registry
- sessions.py: Handle cdp_url in session creation (takes precedence)
## Usage
browser-use --cdp-url http://localhost:9222 open https://example.com
browser-use --cdp-url http://localhost:9222 state
browser-use --cdp-url http://localhost:9222 click 5
## Testing
Tested with Chrome remote debugging over SSH tunnel - successfully
controlled a local macOS Chrome browser from a remote Linux server.
Vibe coded with Claude Opus 4.5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>