Commit Graph
31 Commits
Author SHA1 Message Date
mikewong23571andClaude Sonnet 4.6 1129a3e7fc fix: restore BrowserManager.navigate() and package entry point (#748)
Root cause: package.json `main` pointed at `dist/daemon.js` (the
internal daemon process), so programmatic consumers of the package
received the daemon module instead of a usable API. Additionally,
`BrowserManager.launch()` required IPC-only fields (`id`, `action`),
and `navigate()` existed only as a private function inside actions.ts.

Changes:
- Add src/index.ts as the public package entry point
- Add BrowserLaunchOptions type (Pick<LaunchCommand> minus id/action/engine)
  to decouple the programmatic API from the IPC wire protocol
- Change launch() signature from LaunchCommand to BrowserLaunchOptions
- Add BrowserManager.navigate(url, options?) — consolidates domain check,
  scoped-header setup, and page.goto() into one reusable method; auto-
  recovers a new page when all pages have been closed (stale session)
- Add BrowserManager.getUrl() and getTitle() convenience methods
- Update package.json: main → ./dist/index.js, add types and exports["."]
- Add tests: navigate() with headers, waitUntil, allowedDomains blocking,
  allowedDomains allow, non-http(s) scheme blocking, getUrl/getTitle, and
  compile-time type assertions verifying the public entry exports the right
  API surface (direct repro of #307)

Fixes #307

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:55:47 -05:00
d019c09bbc feat: add idle timeout to daemon to prevent orphaned Chrome processes (#722)
* feat: add idle timeout to daemon to prevent orphaned Chrome processes

The daemon persists indefinitely after browser sessions are used,
leaving orphaned Chromium processes consuming memory and CPU.

Add a configurable idle timeout (default 15 minutes) that shuts down
the daemon when no commands arrive. Resets on every incoming command,
so active sessions are unaffected.

Set AGENT_BROWSER_IDLE_TIMEOUT_MS=0 to disable (preserves old behavior).

Fixes #721

* fix: save session state before shutdown to prevent silent data loss

The shutdown() function (used by idle timeout, SIGINT, SIGTERM, SIGHUP)
previously closed the browser without saving state, unlike the explicit
`close` command which calls saveStateToFile(). This meant idle timeouts
silently destroyed cookies, localStorage, and login sessions.

Now shutdown() mirrors the close command's auto-save behavior: it calls
saveStateToFile() before manager.close(), preserving session state to
disk. This makes idle timeout functionally equivalent to an explicit
close — users returning after an idle shutdown get their state restored.

Addresses review feedback on #722 by @ctate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Max Stoel <maxalerator@hotmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:39:25 -05:00
mikewong23571 d4f7fbc718 fix: align native daemon port hash with client on Windows (#734)
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
2026-03-12 14:15:35 -05:00
Chris Tate e5fd26eb9e headed mode (#607)
* headed mode

* fixes

* fixes

* docs

* fixes

* fixes

* fixes
2026-03-03 22:34:07 -06:00
neilmixandClaude Opus 4.6 e912f541f2 fix: treat EPERM from kill(pid, 0) as "process exists" in daemon liveness checks (#564)
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>
2026-03-01 10:12:37 -06:00
Chris Tate bc1e917e87 add security hardening features (#543)
* 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
2026-02-25 15:33:20 -06:00
Chris Tate b7665e52b6 v0.14.0 changeset (#534)
* v0.14.0 changeset

* fixes

* improvements
2026-02-23 10:48:07 -06:00
shohuandshohu 16c4ef2da6 fix(daemon): add backpressure control and command serialization to prevent IPC EAGAIN (#529)
- Add AGENT_BROWSER_DEFAULT_TIMEOUT env var to override Playwright's
  default 60s timeout (CDP/recording 10s timeouts unaffected)
- Add backpressure-aware safeWrite() that waits for drain when socket
  buffer is full, preventing data loss under load
- Serialize command execution per socket via queue to prevent concurrent
  writes that cause buffer contention

These daemon-side fixes complement #329 (CLI-side EAGAIN retry) by
addressing the root causes: Playwright operations that outlast the
CLI's IPC timeout, and concurrent socket.write() calls that overflow
the kernel buffer.

Tested with heavy React app (1000+ DOM nodes) — 10 consecutive
snapshot commands complete without os error 35/11.

Refs #322

Co-authored-by: shohu <shohu@users.noreply.github.com>
2026-02-23 10:06:06 -06:00
Chris Tate 12d79e4428 add --color-scheme flag for persistent dark/light mode (#528)
Fixes #519. Playwright defaults `colorScheme` to `light` on all new contexts, overriding the browser/OS dark mode setting. This is especially disruptive in CDP mode, where every reconnection resets the scheme. The `set media dark` command also didn't persist its choice to new tabs or pages.

- Add `--color-scheme <dark|light|no-preference>` flag, config key (`colorScheme`), and env var (`AGENT_BROWSER_COLOR_SCHEME`)
- Store the preference in `BrowserManager` and automatically apply it to all new contexts (via Playwright's context option) and all new pages (via `page.emulateMedia` in `setupPageTracking`)
- `set media dark/light` now also persists its choice for subsequent pages and tabs
2026-02-23 01:50:17 -06:00
Chris Tate 467b830974 fix state load failing when no browser is running (#527)
`state load` always fails with "Cannot load state while browser is running" even when no browser is running, making the command completely unusable (#526).

The daemon's auto-launch logic starts a browser before `state_load` gets to handle the command. This adds `state_load` to the exclusion list alongside `launch` and `close`, so `handleStateLoad` can perform its own launch with the state file.
2026-02-23 00:56:48 -06:00
Aman panditandChris Tate 697b788af0 feat: add session persistence, state management commands, and --new-tab click (#184)
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>
2026-02-13 11:56:20 -06:00
Chris Tate 221d22c14f fix: resolve stale session, ref resolution and cursor-ref collision bugs (#427) 2026-02-12 17:32:58 -06:00
Chris Tate 07c2372766 feat: add --allow-file-access flag for file:// URL support (#375)
* 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.
2026-02-05 00:24:28 -06:00
Chris Tate a1b992411e add iOS support (#358)
* ios

* tests

* docs

* real device

* better list

* fixes
2026-02-03 01:36:19 -06:00
Li Yang e831b07f47 chore(cli): save screenshots to tmp dir when no path provided (#247)
* fix(cli): save screenshots to tmp dir when no path provided

Instead of outputting base64 to stdout (which is not useful for most CLI use cases),
screenshots without a path now save to ~/.agent-browser/tmp/screenshots/ with a
generated filename and return the path.

This makes the behavior more ergonomic for AI agents and CLI users alike.

* cleanup

* cleanup

* just revert the cargo.lock version for now

* refactor: extract getAppDir() from getSocketDir()

* docs: improve screenshot help text consistency
2026-01-26 09:08:39 -06:00
Chris Tate f862e2f7df Security: Reject cross-origin connections to daemon and stream server (#274) 2026-01-26 00:42:00 -06:00
Chris Tate 79863a5180 Fix: CLI: state load / profile persistence not usable in v0.7.6 (#268)
* Fix: CLI: state load / profile persistence not usable in v0.7.6

This PR addresses issue #259

* Fix issues identified in code review
2026-01-25 13:45:17 -06:00
Zhiwei Li 53187a603c feat: add support for ignoring HTTPS certificate errors (#93)
* 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
2026-01-24 23:54:33 -06:00
mmhiyokoandClaude Opus 4.5 946d236d9f fix: use ~/.agent-browser for socket files instead of TMPDIR (#180)
* 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>
2026-01-22 01:15:25 -06:00
OanakiajaandChris Tate 083a946aac feat: add browser launch --args, --user-agent, --proxy-bypass configuration support. (#35)
* feat: add browser launch args, user-agent, and proxy configuration support

* fix: User Agent env need added

* fix: command pass error

---------

Co-authored-by: Chris Tate <chris@ctate.dev>
2026-01-22 00:19:37 -06:00
Sanchay 42879c337a fix: respect AGENT_BROWSER_HEADED env var for headed mode (#92)
The headless option was hardcoded to true in the auto-launch section,
ignoring the AGENT_BROWSER_HEADED environment variable. This fix checks
the env var so users can run the browser in headed mode by setting
AGENT_BROWSER_HEADED=1.

Fixes #90
2026-01-17 19:22:59 -06:00
Chris TateandVercel <vercel[bot]@users.noreply.github.com> 2dc093cd62 add screencast (#67)
* docs

* updates

* Fix: The handleCopy function fails to handle errors from navigator.clipboard.writeText(), causing unhandled exceptions and misleading UI feedback when clipboard operations fail.

Co-authored-by: ctate <chris@ctate.dev>

* Fix: The benchmark file uses emojis (📊, 🚀, 🔨, 📈, 📋, , ⏱️, ⚠) in console output, violating repository guidelines that forbid emojis in code and output.

Co-authored-by: ctate <chris@ctate.dev>

* Remove benchmark/run.ts from PR

* screencast

* update docs

* address comments

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-01-13 14:53:27 -06:00
Shirshak 673e2e266e feat: Add extension support (#48)
* Rebase: Add extension support

* Fix logs
2026-01-13 14:34:59 -06:00
Chris Tate 4f6fd8ec5c support serverless environments (#29)
* add --executable-path

* tests

* test vercel

* fixes
2026-01-12 11:41:25 -06:00
Chris Tate 5a7c7440c0 add ci action (#12)
* add ci action

* pre-commit hook

* format
2026-01-12 00:02:38 -06:00
Chris Tate 3bd331b97f cleanup 2026-01-11 10:08:08 -06:00
Chris Tate 36b1af29f6 windows 2026-01-11 09:54:51 -06:00
Chris Tate 0cd29aa119 agent-browser 2026-01-10 22:51:59 -06:00
Chris Tate 93374638eb scripts 2026-01-10 13:08:22 -06:00
Chris Tate 9d253da8bd session 2026-01-10 11:43:55 -06:00
Chris Tate 2d0520b770 init 2026-01-10 11:33:37 -06:00