文件历史

提交图

24 次代码提交

作者 SHA1 备注 提交日期
Saurav Panda 2a9c64548e set_session: run disable+enables in parallel; background cosmetic title prefix
codex flagged that set_session was now performing up to four sequential
domain enables (5s timeout each) plus a 2s Network.disable, so a slow
or remote daemon could block the synchronous IPC reply for ~22s while
the helper's _send() socket has only a 5s read timeout. Old code only
awaited Page.enable (3s) on this path.

Three changes to keep the reply under the IPC deadline:

1. _enable_default_domains now awaits asyncio.gather over the four
   Domain.enable coroutines instead of looping sequentially. Per-call
   timeout reduced from 5s to 4s. Worst case is bounded by a single
   CDP round trip rather than four.

2. set_session schedules the old-session Network.disable in the same
   gather as the four enables on the new session — independent CDP
   sessions, no ordering required for correctness (the consumer-side
   filter in wait_for_network_idle is the actual correctness gate).

3. The 🟢 tab-marker title-prefix Runtime.evaluate is now fire-and-forget
   via asyncio.create_task (+ _silent). It's purely cosmetic; agents
   shouldn't wait on it.

Worst-case set_session reply time: max(2s disable, 4s parallel enables)
≈ 4s, comfortably under the 5s IPC timeout. Normal case on a remote
daemon drops from ~800ms (4 sequential round trips) to ~200ms (1 round
trip in parallel).

Two new tests in tests/unit/test_daemon.py use a fake CDP whose
send_raw blocks on an asyncio.Event, then assert the peak in-flight
call count:
- with a previous session: peak == 5 (1 disable + 4 enables)
- first attach: peak == 4 (4 enables, disable skipped)
Sequential await would peak at 1 on both. Full suite: 65 passed.
2026-05-04 18:48:36 -07:00
Saurav Panda 45b75e62c4 wait_for_network_idle: filter by session_id; set_session: disable old Network
codex flagged a P2 follow-on to the just-added Network.enable: my fix
reliably enables Network on every fresh session, but old sessions
never get Network disabled (helpers.switch_tab attaches without
detaching), and wait_for_network_idle reads from the daemon's global
drain_events stream without filtering by session_id. Net effect: an
agent that visited a polling/SSE tab and switched away would observe
that background tab's traffic in a later wait, either timing out or
waiting on the wrong tab's requests.

Two narrow fixes:

1. helpers.wait_for_network_idle now captures the active session at
   the start of the wait and skips events whose session_id doesn't
   match. That's the consumer-side root-cause fix.

2. daemon set_session now calls Network.disable on the previous
   session before enabling on the new one. Defense in depth — keeps
   the daemon's event buffer from filling with background-tab noise
   in the first place. Best-effort with its own 2s timeout; failure
   doesn't abort the rest of the handler.

Tests added:
- tests/unit/test_helpers.py: events from a background session are
  ignored; the active session can reach idle even when the
  background session is busy.
- tests/unit/test_daemon.py: set_session disables Network on the old
  session; first set_session call (no prior session) does not call
  Network.disable.

Full suite: 63 passed (60 -> 63).
2026-05-04 18:34:18 -07:00
Saurav Panda c789a9fb9c set_session: enable Page/DOM/Runtime/Network (parity with initial attach)
Prior to this change, the daemon's set_session meta-handler only called
Page.enable on the new CDP session. The initial-attach path enabled all
four of Page/DOM/Runtime/Network. set_session is what backs switch_tab()
and new_tab() in helpers.py, so any helper that depends on Network
events — most notably the wait_for_network_idle() that just landed in
PR #258 — silently stopped receiving events after a tab switch.

Refactored: extracted the domain-enable loop into a private
_enable_default_domains(session_id) helper used by both attach_first_page
and the set_session handler. Each domain is enabled with its own
timeout, and a single failure does not abort the others.

Also tightened the target_id fallback semantics: if a caller passes
target_id=None on set_session, the daemon keeps its existing target_id
rather than overwriting with None (preserves the existing 'or' fallback,
just covered by a test now).

Tests in new tests/unit/test_daemon.py drive Daemon.handle() directly
with a fake CDP client and assert:
- set_session enables all four default domains on the new session
  (the regression — would fail against the old single-Page.enable code)
- target_id is preserved when caller passes None
- _enable_default_domains attempts every domain even when one raises

Identified via codex review (P1). Full suite: 60 passed.
2026-05-04 18:26:08 -07:00
Alexander Yue 59a166f0d8 Merge pull request #280 from browser-use/docs/canonical-browser-connection
docs: standardize harness docs against canonical browser connection reference
2026-05-02 18:12:21 -07:00
Alezander9 a2443d1d51 docs: standardize harness docs against canonical browser connection reference
Cross-checked AGENTS.md, README.md, SKILL.md, install.md, and profile-sync.md for accuracy. Tightened the sync_local_profile docstring and the chrome://inspect stderr message to match. Cloud-bootstrap test updated to set BU_AUTOSPAWN. All unit tests pass.
2026-05-02 18:02:36 -07:00
Saurav Panda 12f5973fb1 Merge pull request #258 from wdeveloper16/feat/spa-form-helpers
fix(helpers): add fill_input, wait_for_element, wait_for_network_idle
2026-05-02 15:11:26 -07:00
Saurav Panda 4da0684eb2 fill_input: dispatch select-all without char event so Cmd/Ctrl+A actually fires
press_key emits a Input.dispatchKeyEvent('char', text='a') after every
single-character keyDown. With Cmd/Ctrl held, that char event makes Chrome
treat the input as a printable letter "a" rather than firing the
select-all shortcut, so the field never gets cleared (Backspace would
then delete the literal "a" and leave the original value untouched).

Dispatch the rawKeyDown/keyUp pair directly with the modifier set and no
text/char event. Tightened the test to assert (a) the modifier is on the
'a' event with the platform-correct value and (b) no char event with
text='a' is emitted.
2026-05-02 12:54:43 -07:00
Saurav Panda 41c23013d1 wait_for_element(visible=True): prefer checkVisibility, fall back to computed style
The computed-style check measures the element itself, so an element nested
inside a display:none or visibility:hidden ancestor is reported as visible
(getComputedStyle returns the descendant's own non-none value, not the
inherited "is rendered" state). checkVisibility walks the ancestor chain
and is the right primitive on modern Chrome. Kept the per-element CSS
check as a fallback for older Chrome that lacks checkVisibility.
2026-05-02 12:26:23 -07:00
Alezander9 63b876cad3 harden Windows IPC: ping handshake, token auth, atomic port file
Three improvements to the cross-platform IPC layer, lifted from #104:

- meta:'ping' handshake replaces bare TCP connect in daemon_alive() and already_running(). A connect-only check on Windows can succeed against an unrelated process that grabbed our ephemeral port after a daemon crash; the ping/pong response confirms the listener is actually our daemon.

- Per-daemon random token (secrets.token_hex(32)) gates every request on Windows. AF_UNIX + chmod 600 is the boundary on POSIX, but TCP loopback has no chmod-equivalent; without a token any local process could connect and issue CDP commands.

- Atomic .port write (write .port.tmp, os.replace) so a concurrent reader never sees a half-written file.

Adds rohitdutt108 to VOUCHED.td.

Co-authored-by: Rohit Dutt <rohit.dutt@iyc.ishafoundation.org>
2026-05-01 20:57:46 -07:00
Alezander9 84da63313d Soften skill-toggle wording; drop redundant =0 test 2026-05-01 20:44:14 -07:00
Alezander9 7e9a7db8c5 feat: gate domain skills behind BH_DOMAIN_SKILLS env (default off)
Domain skills auto-injected by goto_url() are community-contributed and quality varies; defaulting them off avoids polluting the average run while preserving the contribution loop. Set BH_DOMAIN_SKILLS=1 to opt in.
2026-05-01 20:34:01 -07:00
Alezander9 49355ba7a8 probe /json/version instead of bare TCP; trim redundant gate tests
Cubic flagged that the original socket.create_connection probe matches any process on 9222/9223, not just Chrome. Mirror daemon.py's fallback by hitting /json/version, so a stale or unrelated listener does not skip the cloud bootstrap.

Drop the three boolean-table tests that mocked every collaborator and re-asserted the literal if-condition. Add a focused test for _local_chrome_listening that covers the false-positive case directly.
2026-04-30 15:34:35 -07:00
Shaun Jackson 79e1ce9ff6 fix: auto-bootstrap cloud daemon on headless servers when BROWSER_USE_API_KEY is set
On headless servers (VPS, Docker) with no local Chrome, ensure_daemon() fires
before any user script runs and raises immediately — start_remote_daemon() can
never be reached from within a -c script.

Add a pre-check in main(): if no daemon is alive, Chrome is not listening on
known debugging ports (9222/9223), and BROWSER_USE_API_KEY is set, auto-
provision a Browser Use cloud browser before falling through to ensure_daemon().

_local_chrome_listening() probes ports 9222/9223 with a 0.3s timeout rather
than relying on _is_local_chrome_mode(), which only checks for absence of
BU_CDP_WS and would incorrectly trigger cloud bootstrap on a local machine
where Chrome is running but BROWSER_USE_API_KEY is also set (e.g. for profile
sync).

Fixes the behaviour reported in issues #181 and #183.

Tested on a headless Hostinger VPS running hermes-agent in Docker — browser-
harness -c '...' now works without any manual daemon setup when BROWSER_USE_API_KEY
is set.
2026-04-30 22:27:18 +10:00
wdeveloper16 878cfa9d59 fix(fill_input): raise on missing element, add timeout param for SPA rendering 2026-04-30 10:49:58 +02:00
wdeveloper16 2229e91eee fix: address copilot review — macOS Cmd+A, fixed visibility check, inflight tracking, stronger test 2026-04-30 00:01:00 +02:00
wdeveloper16 da708c42e3 feat(helpers): add fill_input, wait_for_element, wait_for_network_idle 2026-04-29 23:42:35 +02:00
Alezander9 e52992ad80 fix(daemon): report cdp_disconnected on stale CDP probe in connection_status
When the browser dies but the daemon process keeps running, target_id and session stay cached on the Bridge. Pre-fix, connection_status swallowed the Target.getTargetInfo failure and returned the cached IDs with page=null, so admin counted the daemon as healthy. Now it returns {error: cdp_disconnected} and admin's existing error-check skips it. Also returns {error: not_attached} when target_id is unset.
2026-04-29 13:45:16 -07:00
BitToby d2410828ec Fix remote browser cleanup when daemon startup fails (#251)
* Fix remote startup cleanup

* Fix cloud browser cleanup on startup interruption
2026-04-29 11:09:30 -07:00
Alezander9 51d62fbbe7 fix(_ipc): drop bu-<NAME> filename prefix when BH_TMP_DIR is set
When BH_TMP_DIR is set, the caller owns the directory and is expected to
give each daemon its own — so the bu-<NAME> filename prefix is redundant.
Dropping it cuts ~30 chars off the AF_UNIX path on POSIX, which matters
because sun_path is 104 bytes on macOS / 108 on Linux and a long BU_NAME
(e.g. an opencode-style session id) can blow past it.

Concretely the failing path on macOS for a 30-char session id under
~/.local/share/bcode/sessions/<sessionID>/bu-<sessionID>.sock was 117
bytes; with this change it becomes ~83 bytes.

When BH_TMP_DIR is unset, _TMP is the shared default (/tmp on POSIX,
gettempdir() on Windows) and the bu-<NAME> prefix is kept to disambiguate
multiple daemons sharing the dir. Backward compatible by default.

admin._daemon_endpoint_names: when BH_TMP_DIR is set, the dir is per-daemon
by construction, so glob discovery doesn't apply — just check whether our
local endpoint exists. Without BH_TMP_DIR, fall back to the existing
shared-dir glob.

Validation (_check on BU_NAME) still runs in both modes to catch garbage
names early.
2026-04-28 22:30:57 -07:00
Sarath S Menon fefca43ab5 feat(doctor): show live browser connections and attached pages in run_doctor (#234)
Add browser_connections() / active_browser_connections() to admin, which probe
each daemon socket via a new connection_status meta message. The daemon tracks
target_id (set on attach and on set_session) and returns the live page title/URL
via Target.getTargetInfo. switch_tab now forwards target_id in set_session so the
daemon stays in sync after tab switches. run_doctor shows the count, each daemon
name, and the truncated active-page title and URL.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 17:42:47 +05:30
Sarath S Menon 64dafa2805 refactor(js): proper return-statement parsing, unserializable value decoding, unified eval helpers (#231)
Replace naive `"return " in expression` check with a character-level parser that
ignores strings, line comments, and block comments. Add `_decode_unserializable_js_value`
to handle NaN, ±Infinity, -0, and BigInt. Extract `_runtime_value` and
`_runtime_evaluate` to unify error handling across `js()` and `page_info()`.
Wrap Runtime.evaluate TimeoutErrors in RuntimeError with expression context.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:33:28 +05:30
Sarath S Menon 5a77ea5525 fix(js): raise RuntimeError on JS evaluation errors instead of returning None (#230)
Previously js() silently returned None when Chrome reported a syntax or
runtime error. Now it raises RuntimeError with the error description,
line/column location, and a snippet of the failing expression.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:04:33 +05:30
Sarath S Menon fb1a51dd9b refactor: move to src layout, agent-workspace, and fix SKILL.md invocation format (#229)
* refactor(tests): reorganize into tests/unit and tests/integration

Moves all root-level test_*.py files into a structured tests/ directory:
- tests/unit/ — admin, helpers (was test_screenshot), run
- tests/integration/ — js expression tests
- tests/conftest.py — shared fake_png pytest fixture, eliminating duplication

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

* refactor: move to src layout, agent-workspace, and fix SKILL.md invocation format

- Move package to src/browser_harness/ and domain-skills/interaction-skills to agent-workspace/
- Fix all browser-harness <<'PY' heredoc examples in SKILL.md and run.py HELP string to use the correct -c '...' flag format (heredoc was never supported by the CLI)
- Update SKILL.md path references from domain-skills/ to agent-workspace/domain-skills/

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 15:38:11 +05:30
Sarath S Menon cae52ce916 refactor(tests): reorganize into tests/unit and tests/integration (#228)
Moves all root-level test_*.py files into a structured tests/ directory:
- tests/unit/ — admin, helpers (was test_screenshot), run
- tests/integration/ — js expression tests
- tests/conftest.py — shared fake_png pytest fixture, eliminating duplication

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 14:42:52 +05:30