Cherry-picks upstream agent-browser #1396. Adds a configurable
--hide-scrollbars flag (AGENT_BROWSER_HIDE_SCROLLBARS env, hideScrollbars
config key, default true) that appends Chrome's --hide-scrollbars launch arg
for headless (non-extension) launches so native scrollbars aren't painted into
screenshots. Plumbed through flags.rs, connection.rs, main.rs, native/actions.rs
and native/cdp/chrome.rs; help text in output.rs + skill-data.
Fork adaptation:
- the arg lands in the headless && !has_extensions block, separate from the
stealth base args — no interaction with anti-detection.
- dropped upstream docs/, agent-browser.schema.json and README hunks (removed
or rewritten in this fork).
Verified: cargo check --tests passes.
Before: after `npm i -g` upgrade, the next agent-browser command would
detect daemon version mismatch, kill the old daemon, spawn a fresh one,
and connect to a brand-new about:blank tab. The user's previous
navigation state was silently lost — `get url` returned about:blank
even though the user's Chrome was still on the same page.
Now: before killing the old daemon, the CLI synchronously asks it for
its current URL via the existing socket. If non-empty and not
about:blank, it's persisted to a `.restore-url` sidecar in the socket
dir. After the new daemon spawns and auto-connects, it reads the
sidecar (read-and-delete), navigates the fresh tab to the saved URL,
and prints `⚠ Restored previous URL: <url>`.
Manual `agent-browser close` does NOT write the sidecar, so a clean
shutdown won't trigger surprise navigation. The sidecar is consumed on
read regardless of whether navigation succeeded, so a stale entry
can't haunt later auto-launches.
- Auto-connect is now ON by default (was opt-in via --auto-connect)
- Added --launch/--new flags to explicitly start a fresh browser
- CI environments (CI env var) automatically use --launch mode
- Friendly error message with platform-specific Chrome relaunch guide
- Mentions Chrome 144+ runtime CDP toggle (chrome://inspect)
- --cdp and --provider flags implicitly disable auto-connect
- AGENT_BROWSER_NO_AUTO_CONNECT=1 to disable, AGENT_BROWSER_FORCE_LAUNCH=1 to force
Track 3 of native-stealth migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(react): first-class React introspection, Web Vitals, and nextjs skill
Add React-general and web-universal features as first-class agent-browser verbs
(react tree/inspect/renders/suspense, vitals, pushstate). Genuinely Next.js-specific
workflows (PPR cookie protocol, /_next/mcp bridge, dev-server endpoints) ship as
a new `nextjs` skill that composes the primitives. No new runtime dependencies -
the React DevTools installHook.js is vendored (MIT) and include_str!'d into the
binary.
New commands:
react tree Full React component tree (depth id parent name)
react inspect <fiberId> Props, hooks, state, source for one fiber
react renders start|stop Fiber profiler with Insts/Mounts/Re-renders/Self/DOM
+ prev->next change details
react suspense Suspense boundaries + classifier (client-hook,
request-api, server-fetch, cache, stream, framework)
+ root-cause grouping + recommendations
vitals [url] LCP/CLS/TTFB/FCP/INP + React hydration phases
pushstate <url> Generic SPA client-side navigation
removeinitscript <id> Remove a script registered via addinitscript
New launch flags:
--init-script <path> Register init scripts before first navigation
(repeatable; env AGENT_BROWSER_INIT_SCRIPTS)
--enable <feature> Built-in init scripts; currently react-devtools
(repeatable; env AGENT_BROWSER_ENABLE)
Other primitives:
network route ... --resource-type <csv> Filter by CDP resource type
cookies set --curl <file> Auto-detects JSON/cURL/Cookie-header
* fixes
* fixes
* fixes
* fix(cli): honor AGENT_BROWSER_DEFAULT_TIMEOUT env var for wait commands
The `AGENT_BROWSER_DEFAULT_TIMEOUT` environment variable was being ignored by CLI wait commands, causing them to use hardcoded 30-second timeouts instead of the configured default.
## Changes Made
- **Centralized timeout injection**: Modified `parse_command()` to automatically inject `flags.default_timeout` into any wait-family command that doesn't already have an explicit `--timeout` flag
- **Environment variable parsing**: Added `default_timeout` field to `Flags` struct that reads from `AGENT_BROWSER_DEFAULT_TIMEOUT` env var
- **Daemon propagation**: Updated daemon spawning to pass through the default timeout via environment variables
- **Unified timeout handling**: Added `timeout_ms()` helper method in `DaemonState` that all wait handlers now use instead of scattered `unwrap_or()` calls
- **Comprehensive test coverage**: Added 10 regression tests covering all wait command variants and edge cases
## Implementation Details
The fix uses a two-stage approach:
1. CLI parses the env var and injects timeout values into command JSON for any `wait*` action
2. Daemon reads the env var and provides a centralized fallback via `timeout_ms()` helper
This ensures new wait variants automatically inherit the default timeout without requiring per-variant wiring.
Fixes#1147
* fix: preserve 30s default timeout for backward compatibility
The default_timeout_ms fallback was set to 25_000ms, which silently
changes the existing 30_000ms behavior for users who haven't set
AGENT_BROWSER_DEFAULT_TIMEOUT. Restore the original 30s default.
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
After upgrading agent-browser, the old daemon process keeps running.
ensure_daemon() only checks socket connectivity, not version, so the
new CLI silently reuses the old daemon — causing broken CDP behavior
with no error or warning.
Add a version sidecar file (.version) written by the daemon on startup.
ensure_daemon() now compares it against the CLI's compiled version and
automatically kills/restarts on mismatch. Missing version files (from
pre-fix or Node.js-era daemons) are treated as mismatches so the first
upgrade to this version also benefits.
Fixes#1127
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
When a daemon is killed or crashes without cleaning up, stale .sock/.pid
files are left behind. Previously, `close --all` would fail to connect to
these zombie daemons and simply report an error, leaving the stale files
in place and poisoning all future sessions.
Three fixes:
1. `close --all` now force-kills unreachable daemon processes and removes
all stale files (pid, sock, stream) instead of reporting failure. It
also cleans up dead-but-lingering PID files during enumeration and
scans for orphaned .sock files without corresponding .pid files.
2. `ensure_daemon` handles concurrent startup races: when a spawned
daemon exits with "Address already in use" (another instance won the
bind race), it checks whether the winner is accepting connections and
piggybacks on it instead of failing.
3. `cleanup_stale_files` is now public so `close --all` can reuse it.
Fixes#1118
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* Add auto-dismissal for alert and beforeunload dialogs
This PR adds automatic handling of JavaScript dialogs to prevent the agent from blocking indefinitely when `alert()` or `beforeunload` dialogs appear on web pages.
## Summary
Previously, when a website displayed native browser confirmation dialogs (like alerts or "Are you sure you want to leave?" prompts), agent-browser would hang waiting for manual intervention. This is a common issue since many websites use these dialogs for notifications or navigation warnings.
## Changes Made
- **Auto-dismiss functionality**: Added a background task that automatically accepts `alert` and `beforeunload` dialogs while leaving `confirm` and `prompt` dialogs for explicit handling
- **New flag**: Added `--no-auto-dialog` flag to disable automatic handling when needed
- **Environment variable**: Added `AGENT_BROWSER_NO_AUTO_DIALOG` for configuration
- **Documentation**: Updated README and docs with usage examples and configuration details
- **Tests**: Added comprehensive test coverage for flag parsing and dialog handling logic
## Implementation Details
- Only `alert` (notification-only) and `beforeunload` (navigation warning) dialogs are auto-handled for safety
- `confirm` and `prompt` dialogs still require explicit `dialog accept/dismiss` commands to ensure agents make deliberate choices for destructive actions
- The feature is enabled by default since these dialog types rarely require user decision-making
- Uses Chrome DevTools Protocol's `Page.handleJavaScriptDialog` for reliable dialog dismissal
Fixes#1070
* Log dialog type and message before auto-dismissal
Without this, auto-dismissed alert/beforeunload dialogs are silently
swallowed and the agent has no way to see what the dialog said. Adding
an eprintln before the CDP call makes the dismissal visible in stderr
for debugging.
* Log dialog dismissal errors instead of silently discarding them
- Remove premature "accepted" from log message since it fires before
the CDP command executes
- Replace `let _ =` with `if let Err(e)` to log failures when
Page.handleJavaScriptDialog fails
- Apply rustfmt to auto-dialog tests
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
On Windows the daemon derives a TCP port from the session name via a
djb2 hash (e.g. "default" → 50838). On many machines this port falls
inside Hyper-V's excluded port range (winnat), causing EACCES on bind
and preventing the daemon from starting.
Changes:
- daemon: try the hash-derived port first; on failure, bind to port 0
(OS-assigned) and write the actual port to the .port file
- client (connection.rs, stream.rs): read the .port file to discover the
daemon's actual port, falling back to the hash if the file is absent
- run_daemon: guard .sock file operations with #[cfg(unix)] and add
.port file cleanup for #[cfg(windows)]
Fixes#390
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
`relay_command_to_daemon` in stream.rs used `tokio::net::UnixStream`
unconditionally, which doesn't compile on Windows. Add platform-
conditional code matching the existing pattern in daemon.rs and
connection.rs: Unix sockets on unix, TCP on Windows.
* dashboard
* fix: re-apply download behavior on recording context (#1019)
* fix: re-apply download behavior on recording context
record start creates a new browser context via Target.createBrowserContext.
Browser.setDownloadBehavior called at launch only applies to the default
context, so downloads in the recording context are silently dropped.
Fix:
1. Store download_path on BrowserManager (from LaunchOptions)
2. After creating the recording context, call Browser.setDownloadBehavior
with the new browserContextId
This ensures downloads work during recording.
Fixes#1018
* fix: add download_path to third BrowserManager constructor (auto_connect_cdp)
* fix: reap zombie Chrome process and fast-detect crash for auto-restart (#1023)
When Chrome crashes (e.g. SIGTRAP from CHECK() assertion), the daemon
now:
1. Reaps the zombie immediately via a SIGCHLD handler in the event loop
that calls waitpid(-1, WNOHANG)
2. Detects the crash instantly on the next command via a non-blocking
try_wait() check (has_process_exited), avoiding the 3-second CDP
timeout that is_connection_alive() would incur
3. Auto-relaunches Chrome transparently for the caller
Fixes#1017
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* fix: route keyboard type through text input (#1014)
* fix: handle --clear flag in console command (#1015)
The console and errors commands parsed --clear from CLI args but the
action handlers silently ignored the flag. The handlers did not accept
the cmd parameter so they had no way to read the clear field.
Changes:
- Add clear_console() method to EventTracker in network.rs
- Update handle_console to accept cmd, read the clear field, and clear
the buffer when --clear is passed (returns {cleared: true})
- Update call site in execute_command to pass cmd
Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>
* chore: patch release - ### Bug Fixes
- **Re-apply download behavior on r... (#1025)
* Add runtime stream enable/disable/status commands (#951)
* Add runtime stream management commands
* Run rustfmt and satisfy clippy
* Fix stream disable cleanup semantics
* Format stream disable regression tests
* fix: retain radio/checkbox elements in compact snapshot tree (#1008)
compact_tree() checked for "[ref=" to identify lines worth keeping, but
radio and checkbox elements render as e.g. [checked=false, ref=e1] where
the "[" opens before "checked=", not "ref=". Dropping the leading bracket
so the check is just "ref=" fixes the match for all elements with refs.
Fixes#1006
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* chore: version packages (#1027)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fixes
* dashboard
* fixes
* remove observe
* fmt
* fixes
* fixes
* jotai
* fmt
* upload dashboard
---------
Co-authored-by: Stefan Smiljkovic <stefan@vanila.io>
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: zhanba <c5e1856@gmail.com>
Co-authored-by: xuyongliang <478439790@qq.com>
Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>
Co-authored-by: Thomas Kosiewski <thoma471@googlemail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: handle proxy authentication via CDP Fetch.authRequired
Chrome's --proxy-server flag does not support credentials embedded in
the URL. When a proxy requires authentication, Chrome receives a 407
from the proxy but has no way to respond with credentials, resulting
in net::ERR_INVALID_AUTH_CREDENTIALS.
Fix by:
1. Parsing credentials from the proxy URL (already done by parse_proxy)
2. Storing them in DaemonState.proxy_credentials
3. Enabling Fetch.enable with handleAuthRequests: true
4. Responding to Fetch.authRequired events with Fetch.continueWithAuth
5. Passing only the server URL (without credentials) to --proxy-server
6. Forwarding credentials to the daemon via dedicated env vars
Also adds fallback to standard proxy env vars (HTTP_PROXY, HTTPS_PROXY,
ALL_PROXY, NO_PROXY) when AGENT_BROWSER_PROXY is not set.
Fixes#990
* refactor: use typed struct for parse_proxy, fix double Fetch.enable and username-only auth
- Replace serde_json::Value return from parse_proxy with a typed ParsedProxy struct
- Fix double Fetch.enable call when both proxy auth and domain filter are active
(the second call could overwrite handleAuthRequests from the first)
- Allow username-only proxy auth (some proxies don't require a password)
- Handle empty username/password in parse_proxy as None instead of Some("")
- Use install_domain_filter_fetch in auto_launch for consistency
- Update unit tests to use typed struct fields
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Fixes#992
When a JavaScript dialog (alert/confirm/prompt) blocks the page, agents
had no way to detect it — all commands just timed out with generic errors.
- Add `dialog status` command to check for pending dialogs
- Track dialog state via CDP Page.javascriptDialogOpening/Closed events
- Auto-inject `warning` field into all command responses when a dialog is
pending, so agents can distinguish dialog-blocked timeouts from other issues
- Document dialog commands in SKILL.md (was missing entirely), README.md,
docs site, and --help output
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* fix: Windows auto-connect profiling (#835)
Fix three interrelated bugs causing `--auto-connect` to fail on Windows,
plus a UX issue where auto-connect hijacked existing tabs:
1. Stale DevToolsActivePort — add TCP port liveness check before returning
M144+ WebSocket URL; remove stale files when port is dead.
2. Missing Windows error codes — add os error 10061 (WSAECONNREFUSED) and
10054 (WSAECONNRESET) to is_transient_error() so daemon startup races
are retried on Windows.
3. --auto-connect not propagated to daemon — add auto_connect to
DaemonOptions, set AGENT_BROWSER_AUTO_CONNECT env var via
apply_daemon_env(), and guard the headed launch block so it doesn't
send a second launch that overrides the auto-connect.
4. Auto-connect opens a fresh tab — after connecting to an existing
Chrome, create a new about:blank tab and bring it to front so
navigations don't hijack the user's existing tabs.
Made-with: Cursor
* fix: address review feedback — cargo fmt, shared helper, Windows tests
- Run cargo fmt on is_port_reachable() formatting
- Extract duplicated auto-connect-with-fresh-tab logic into
connect_auto_with_fresh_tab() helper used by both handle_launch()
and auto_launch()
- Add unit tests for Windows WSAECONNREFUSED (os error 10061) and
WSAECONNRESET (os error 10054) in is_transient_error()
---------
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* fix: propagate --cdp flag to daemon via AGENT_BROWSER_CDP env var
The --cdp flag was not being passed to the daemon process as an environment
variable, causing auto-reconnection to fail. The daemon's auto_launch()
function checks for AGENT_BROWSER_CDP, but this was never set when spawning
the daemon.
This commit adds:
- cdp field to DaemonOptions struct
- AGENT_BROWSER_CDP env var setting in apply_daemon_env()
- flags.cdp propagation in main.rs
This ensures reliable CDP connection recovery when using --cdp with external
browsers like Lightpanda, Electron apps, or remote Chrome instances.
Fixes reconnection issues with --cdp flag after connection drops.
* chore: remove changeset
---------
Co-authored-by: Jake Shore <jakeshore@Jakes-Mac-mini.local>
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
* feat: add --idle-timeout CLI flag for daemon auto-shutdown
Add user-friendly --idle-timeout flag that converts time strings
to milliseconds. Supports formats like '10s', '3m', '1h', or raw ms.
This addresses a common need for ephemeral/CI environments where
daemon processes can be orphaned if not explicitly closed, leading
to resource consumption from zombie chrome-headless-shell processes.
Co-authored-by: Hermes (via claude-sonnet-4-20250520) <agent@hermes.ai>
* fix: address idle-timeout review feedback
* fix: normalize idle-timeout parsing
---------
Co-authored-by: Merlin <merlin@rbeckner.com>
Co-authored-by: Hermes (via claude-sonnet-4-20250520) <agent@hermes.ai>
The client (connection.rs) and native daemon (native/daemon.rs) used
different get_port_for_session() implementations on Windows:
- Client: i32, .chars(), djb2 — (hash << 5) - hash + c
- Daemon: i64, .bytes(), Java hashCode — hash * 31 + b
For session name "default", client computes port 50838 while the
daemon binds on 51174, causing a 5-second timeout and startup failure.
Fix: align native/daemon.rs to use the identical djb2 algorithm from
connection.rs (i32, chars, djb2), so both sides agree on the port.
Unix is unaffected (uses Unix domain sockets, no port hashing).
Tests: add port hash regression tests to all three implementations
(native/daemon.rs, connection.rs, daemon.ts) to prevent future drift.
Fixes#705
TypeScript CI has prettier --check but Rust CI only runs cargo test.
Add cargo fmt --check to catch formatting issues early, and fix the
8 pre-existing formatting violations on main.
When the daemon process crashes during startup (e.g., missing
Playwright), stderr was discarded via Stdio::null(), so users only
saw "Daemon failed to start (port: ...)" with no diagnostic info.
Now captures daemon stderr via Stdio::piped() and detects early process
exit with try_wait() during the startup polling loop. If the daemon
crashes, the actual error from stderr is shown to the user.
Also forwards --debug flag to the daemon process as AGENT_BROWSER_DEBUG
so debug logging works end-to-end.
Closes#56
* fix(windows): resolve daemon startup failures and Git Bash compatibility
Three root causes behind 27 open Windows issues:
1. Path::canonicalize() returns \\?\ prefixed paths on Windows that
Node.js cannot parse, preventing daemon startup. Strip the prefix
before passing to Node. (fixes#522, #390, #56, #25, #37, #89)
2. Git Bash/MSYS2 translates Unix-style paths and resolves node to
a shell wrapper script. Use node.exe explicitly and set
MSYS_NO_PATHCONV/MSYS2_ARG_CONV_EXCL to prevent argument mangling.
(fixes#148, #108, #171)
3. postinstall fixWindowsShims() hardcoded x64 arch and did not verify
the native binary exists before rewriting shims. Now detects arch
dynamically and validates the binary path. (fixes#262)
Also:
- Error messages now show TCP port on Windows instead of Unix socket path
- Windows CI expanded to test full daemon lifecycle (open, snapshot, close)
* fix(windows): strip \\?\ prefix in auth-cli path (fixes#579)
Same canonicalize() issue as the daemon spawn path, but in
run_auth_cli() which passes the script path to Node.js.
Per POSIX, kill(pid, 0) returns EPERM when the process exists but the
caller lacks permission to signal it, and ESRCH when it does not exist.
The daemon liveness checks in both the Rust CLI and TypeScript daemon
treated any kill failure as "not running", which is incorrect when
running inside a macOS sandbox that restricts signal delivery to
(target self). This caused the CLI to delete the real daemon's socket
and PID files, then spawn a duplicate daemon.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* add security hardening features
- Add authentication vault (`auth save/login/list/show/delete`) so credentials are stored locally and never exposed to the LLM (fixes Snyk W007)
- Add `--content-boundaries` flag to wrap page-sourced output in structural markers, helping LLMs distinguish tool output from untrusted page content (fixes Snyk W011)
- Add `--allowed-domains` flag to restrict browser navigation to trusted domains
- Add `--action-policy` for static allow/deny gating of action categories, with opt-in `--confirm-actions`/`--confirm-interactive` for orchestrator or human-in-the-loop confirmation
- Add `--max-output` flag to truncate large page outputs, preventing context flooding
- New docs page at /security, updated README, SKILL.md, CLI help text, and templates
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* fixes
* docs
* feat: add --download-path option
Adds a `--download-path` flag (and `AGENT_BROWSER_DOWNLOAD_PATH` env / `downloadPath` config key) to set a default download directory for browser downloads.
Without this, Playwright stores downloads in a temp directory that is deleted when the browser closes. The new option passes through to Playwright's `downloadsPath` on `launch()` and `launchPersistentContext()`.
Fixes#507
* improvements
* fixes
* fixes
Rebased and fixed implementation of PR #184 features on current main:
Session persistence:
- --session-name flag and AGENT_BROWSER_SESSION_NAME env var auto-save/restore
cookies and localStorage across browser restarts
- State files stored in ~/.agent-browser/sessions/ with owner-only permissions
- AES-256-GCM encryption via AGENT_BROWSER_ENCRYPTION_KEY env var
- Auto-expiration of old state files (AGENT_BROWSER_STATE_EXPIRE_DAYS, default 30)
State management commands:
- state list: list saved state files with metadata
- state show <file>: display state summary (cookies, origins, domains)
- state rename <old> <new>: rename state files
- state clear [name] [--all]: clear saved states
- state clean --older-than <days>: delete expired states
New --new-tab flag for click command:
- Opens link href in a new tab instead of navigating the current tab
Security hardening:
- Session name validation prevents path traversal (CLI + daemon)
- safeHeaderMerge prevents prototype pollution in header merging
- WebSocket stream server binds to 127.0.0.1 only
- State files written with 0o600 permissions
Fixes applied over the original PR:
- Use color.rs module instead of hardcoded ANSI escape codes
- Align CLI output field names with daemon response format
- Add CLI-level --session-name validation (not just daemon-side)
- Avoid adding "DOM" to tsconfig.json lib (use proper typing in evaluate)
- Keep version at 0.9.3 (matches current main)
- Centralize session name validation in daemon.ts helper
- Update all documentation (README, SKILL.md, docs site, --help output)
Co-authored-by: Chris Tate <chris@ctate.dev>
* cargo fmt
* fix: remove redundant `use libc` import (clippy::single_component_path_imports)
* fix: use `.first()` instead of `.get(0)` (clippy::get_first)
* fix: use `.copied()` instead of `.map(|s| *s)` (clippy::map_clone)
* fix: allow too_many_arguments on ensure_daemon (clippy::too_many_arguments)
* fix: use `then_some` instead of `then` with closure (clippy::unnecessary_lazy_evaluations)
* fix: use pattern match instead of redundant guard (clippy::redundant_guards)
* fix: use pattern match instead of redundant guard in commands.rs (clippy::redundant_guards)
* fix: use `contains()` instead of `iter().any()` for simple equality (clippy::manual_contains)
* Add changeset
* feat: add --allow-file-access flag for file:// URL support
Adds the ability to open and interact with local files using file:// URLs.
This enables use cases like viewing local PDFs, testing local HTML files,
and allowing JavaScript to access other local files via XHR.
The flag adds Chromium's --allow-file-access-from-files and --allow-file-access
launch arguments. Only supported in Chromium browsers.
Fixes#345
* fix: add cli_allow_file_access tracking to prevent spurious warning
When --allow-file-access is set via AGENT_BROWSER_ALLOW_FILE_ACCESS env var
(not CLI), don't warn about the flag being ignored when daemon is already running.
* fix: add retry logic for transient socket errors
Fixes race condition when rapidly closing and opening browser sessions.
The daemon has a 100ms shutdown delay, which caused the CLI to detect
stale daemons as "running" and fail with EAGAIN errors.
Changes:
- Add retry logic (5 attempts, exponential backoff) for transient errors
including EAGAIN, EOF, connection reset, and connection refused
- Add 150ms verification delay in ensure_daemon to detect shutting-down daemons
- Add cleanup_stale_files to remove leftover socket/PID files before starting
a new daemon
Tested with 20 rapid close/open cycles and 100+ parallel commands.
* test: add unit tests for transient error detection
Extracts is_transient_error() function and adds 14 unit tests covering:
- EAGAIN errors (macOS os error 35, Linux os error 11)
- WouldBlock and Resource temporarily unavailable
- EOF and empty JSON response errors
- Connection reset (macOS os error 54, Linux os error 104)
- Broken pipe errors
- Socket not found (os error 2)
- Connection refused (macOS os error 61, Linux os error 111)
- Non-transient errors (verifies they are NOT retried)
* feat: add support for ignoring HTTPS certificate errors
* fix: update warning message for already running daemon to include ignore HTTPS errors option
* docs: add documentation for --ignore-https-errors option in README and SKILL.md
* feat: initialize ignore_https_errors flag in command context
* fix: change launch_cmd to mutable for cdp value handling
* fix: use ~/.agent-browser for socket files instead of TMPDIR
This fixes issue #163 where different TMPDIR values (common with
tmux/screen/VSCode/IntelliJ) caused the CLI and daemon to use
different socket paths.
Socket directory priority:
1. AGENT_BROWSER_SOCKET_DIR (explicit override)
2. $XDG_RUNTIME_DIR/agent-browser (Linux standard)
3. ~/.agent-browser (fallback, like Docker Desktop)
Both CLI (Rust) and daemon (Node.js) now use the same logic.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: session list now looks in correct socket directory
- Make get_socket_dir() public in connection.rs
- Update session list to use get_socket_dir() instead of temp_dir()
- Update pid file pattern from agent-browser-{session}.pid to {session}.pid
- Add tmpdir fallback to daemon.ts when homedir is unavailable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add unit tests for socket directory resolution
Add comprehensive tests for get_socket_dir/getSocketDir to verify:
- AGENT_BROWSER_SOCKET_DIR takes priority
- Empty strings are ignored (fixes Rust/TypeScript consistency)
- XDG_RUNTIME_DIR fallback works correctly
- Home directory fallback when env vars unset
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add --proxy flag for browser proxy support
Add CLI flag to configure HTTP/SOCKS proxy for Playwright browser context.
Supports URL format with optional credentials: http://user:pass@host:port
* fix: improve proxy parsing error handling
- Handle malformed credentials (@ without :) by ignoring incomplete creds
- Replace unwrap() with expect() for better error messages
- Addresses Vercel bot code review suggestions
* Restaura cambios locales: soporte AGENT_BROWSER_HOME y timeout aumentado
- Agrega soporte para variable de entorno AGENT_BROWSER_HOME en connection.rs
- Aumenta timeout por defecto de 10s a 60s para conexiones más lentas
* feat: add --proxy flag for browser proxy configuration
Implements proxy support based on PR #16 with reviewer feedback:
Features:
- Parse proxy URLs: http://[user:pass@]host:port
- Support for HTTP, HTTPS, and SOCKS5 protocols
- Handle username-only auth (preserves username with empty password)
- Apply proxy to both standard and persistent contexts
Changes:
- cli/src/flags.rs: Add proxy flag parsing
- cli/src/main.rs: Add parse_proxy() with comprehensive tests
- cli/src/output.rs: Add --proxy to help output
- cli/src/commands.rs: Fix test helper to include proxy field
- src/types.ts: Add proxy to LaunchCommand interface
- src/protocol.ts: Add proxy validation schema
- src/browser.ts: Apply proxy to context creation
Tests:
- 7 unit tests for parse_proxy() covering all edge cases
- All Rust tests passing (69 tests)
- All TypeScript tests passing (168 tests)
- TypeScript typecheck passing
Resolves feedback from PR #16:
- Fixed username-only proxy handling (issue #2681046975)
- Added comprehensive unit tests
- Added --proxy to help documentation
- Used expect() instead of unwrap() for better error messages
* refactor: simplify parse_proxy function
- Remove redundant comments
- Extract server variable to reduce duplication
- Inline trivial username/password variables
All 7 proxy tests still passing.