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>
- Forward all cloud-only flags in _handle_remote_run_with_wait
Previously only forwarded 6 params; now includes session_id, vision,
metadata, secrets, allowed_domains, skill_ids, structured_output,
judge, judge_ground_truth, plus session creation for profile/proxy
- Use asyncio.to_thread for blocking SDK call in poll_until_complete
Prevents client.tasks.get_task from blocking the event loop while
polling for task completion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace custom HTTP client with browser-use-sdk for tasks and sessions,
completing the SDK migration started in 8a992229.
## SDK Migration
- Add `sdk.py` with singleton client factory (`get_sdk_client()`)
- Rewrite `cloud_task.py` to use SDK (-300 lines)
- All functions return typed SDK models (TaskView, SessionView, etc.)
- Add `_filter_none()` to omit None values (SDK sends null, API rejects)
## No Client-Side Defaults
- Remove hardcoded defaults from CLI (max_steps, proxy_country, etc.)
- Let API use its own defaults (documented at docs.cloud.browser-use.com)
- Only pass values user explicitly sets
## New CLI Features
- `session create` - create session without task
- `session share <id>` - create/delete public share URLs
- `task list --session <id>` - filter tasks by session
- New task flags: --metadata, --secret, --allowed-domain, --skill-id,
--structured-output, --judge, --judge-ground-truth, --start-url
## Async by Default
- Change `--no-wait` to `--wait` (flip default behavior)
- Tasks return immediately with task_id by default
- Use `--wait` to block until completion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace raw urllib HTTP calls with the official browser-use-sdk client
for all cloud profile API operations. This provides:
- Type-safe responses (ProfileView, ProfileListResponse)
- Cleaner error handling via SDK exceptions
- Consistent SDK usage pattern (matches skills/service.py)
- Removes 40 lines of manual HTTP/auth handling from main.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use CREATE_NO_WINDOW instead of DETACHED_PROCESS to prevent
a blank console window from appearing when the server starts.
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>
Previously doctor said "See details above" but never printed the actual
check results. Now prints each check with status icon and details.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The setup command parser was changed to use --mode but the handler was
still passing args.profile and reading result["profile"]. Fixed both
to use mode consistently.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Default task status shows only latest step (token efficient for agents)
- Add --compact/-c flag to show all steps with reasoning
- Add --verbose/-v flag to show all steps with URLs + actions
- Add --last/-n, --reverse/-r, --step/-s flags for navigating long tasks
- Display duration for tasks (e.g., "18s", "5m 13s") and sessions
- Format proxy cost to 2 decimal places in session output
- Update SKILL.md docs with recommended monitoring workflow
- Spawn cloudflared as daemon with start_new_session=True
- Track tunnels via PID files in ~/.browser-use/tunnels/ instead of memory
- Properly add --all flag to tunnel argparser (fixes unrecognized args error)
- Clean up stale PID files when tunnel process has died
This fixes the issue where cloudflared would become a zombie/defunct process
immediately after browser-use tunnel returned, causing 530 errors.
Previously, `browser-use tunnel 3000` would start a session server with
the default browser mode (chromium), which poisoned subsequent
`--browser remote` commands with a mode mismatch error.
Now tunnel commands are handled directly in main.py without starting a
browser session:
npm run dev &
browser-use tunnel 3000 # No session created
browser-use --browser remote open <tunnel-url> # Works!
Changes:
- Move tunnel logic to standalone functions in tunnel_manager.py
- Handle tunnel commands in main.py before ensure_server()
- Tunnels persist across browser-use close (independent lifecycle)
- Add `browser-use tunnel stop --all` command
Breaking behavior change:
- `browser-use close` no longer stops tunnels
- Use `browser-use tunnel stop <port>` or `tunnel stop --all` explicitly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a session is started without --browser remote and a subsequent command
uses --browser remote, the CLI now errors with guidance instead of silently
using the local browser (which loses cloud features like live_url).
Code changes:
- Add session metadata file to track browser_mode per session
- Error only when remote requested but session is local
- Allow local-on-remote (user gets more features than requested)
- Clean up metadata on session close
SKILL.md improvements:
- Clarify that --browser remote only needed on FIRST command
- Add table showing session modes
- Add "What happens if you forget" section with error example
- Add troubleshooting entry for mode mismatch error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a session is started with --browser chromium and a subsequent command
uses --browser remote (or vice versa), the CLI now detects the mismatch and
provides actionable error guidance instead of silently using the wrong mode.
Changes:
- Add get_session_metadata_path() to track session config in temp files
- Write browser_mode, headed, profile to metadata on server start
- Check metadata in ensure_server() and error if modes don't match
- Clean up metadata file on session close
Example error:
Error: Session 'default' is running with --browser chromium,
but --browser remote was requested.
Options:
1. Use existing browser: browser-use --browser chromium <command>
2. Use different session: browser-use --browser remote --session other <command>
3. Close existing session: browser-use close
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>
- Remove invalid cdp_url parameter from create_browser_session calls
- Replace deleted GetCookiesEvent with direct _cdp_get_cookies() call
- Add type assertion for cookies_file after error check
- Use unique variable names for CDP results to avoid type conflicts
- Add type ignores for CDP typed return values used as dicts
PR #3911 changed the browser-use entry point to skill_cli/main.py but
didn't port the early command interception for install, init, --template,
and --mcp. This broke:
- `browser-use install` (installs Chromium via playwright)
- `browser-use init` (generates template files)
- `browser-use --template` (direct template generation)
- `browser-use --mcp` (MCP server mode)
This fix adds early command interception to skill_cli/main.py, matching
the behavior from browser_use/cli.py. Commands are handled before argparse
to avoid loading heavy dependencies for simple setup tasks.
Also adds tests to prevent regression.
- Add cloud profile commands: list, create, get, update, delete
- Add profile sync command to sync local Chrome cookies to cloud
- Add profile cookies command to list cookies by domain
- Add cookies export/import commands for bulk transfer
- Optimize cookie import using Network.setCookies (bulk API)
- Real browser now runs headless by default (use --headed for visible)
- Support --domain flag for syncing specific domain cookies only
- Update SKILL.md with cloud profile workflow documentation
- Add `profile list-local` command to discover local Chrome profiles
- Fix real browser mode to properly pass `profile_directory` to BrowserSession
- Document profile management workflow in SKILL.md
Adds information retrieval commands to the browser-use CLI:
- get title: page title
- get html: full page or element HTML via selector
- get text: text content of element by index
- get value: input/textarea value by index
- get attributes: all attributes of element by index
- get bbox: bounding box (x, y, width, height) of element
Adds mouse interaction commands to the browser-use CLI:
- hover: move mouse over element (triggers CSS :hover effects)
- dblclick: double-click element
- rightclick: right-click element (context menu)
Adds wait conditions to the browser-use CLI with two subcommands:
- wait selector: wait for CSS selector with state options (visible, hidden, attached, detached)
- wait text: wait for text to appear on the page
Both support custom timeout in milliseconds.
Adds cookie management to the browser-use CLI with three subcommands:
- cookies get: retrieve all cookies or filter by URL
- cookies set: create cookies with domain, path, secure, httpOnly options
- cookies clear: remove all cookies or filter by URL
This prevents conflicts when switching between browser modes. Session parameters
(browser mode, cdp_url, etc.) are set at creation time and cannot be changed.
Without this fix, an existing 'default' session using chromium mode would be
silently reused when the user specifies --cdp-url, causing the CDP connection
to be ignored.
The same issue would occur switching between any browser modes (e.g., --browser
chromium to --browser real), but CDP is the most common case where users expect
to connect to a specific existing browser instance.
The session remap is placed early in main() before any command handling (server,
sessions, close --all) so those commands also target the correct session.
Users can still override with explicit --session flag if needed.
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>
- State command outputs same format that browser-use agents see
- Added aliases: bu, browser (all work the same)
- Headed mode now correctly shows browser window