提交

提交图

235 次代码提交

作者 SHA1 备注 提交日期
Alezander9 b124b56c42 list agent-workspace contents; clarify SKILL.md scope 2026-04-30 16:16:29 -07:00
Alezander9 f3a011b988 docs: add AGENTS.md with repo orientation for coding agents 2026-04-30 16:13:51 -07:00
Alexander Yue d1209ab99c Merge pull request #261 from browser-use/fix/shopify-admin-skills-location
fix: move shopify-admin skills to agent-workspace/domain-skills/
2026-04-30 00:04:10 -07:00
alex e1eabc4584 fix: move shopify-admin skills to agent-workspace/domain-skills/
PR #247 added these under domain-skills/ at the repo root, but the
canonical location for domain skills is agent-workspace/domain-skills/
(referenced in README.md, SKILL.md, and helpers.py).
2026-04-29 23:39:22 -07:00
Alexander Yue 660827d7d4 Merge pull request #260 from Alezander9/fix/stale-devtools-active-port
fix(daemon): ignore stale DevToolsActivePort path; resolve WS via /json/version
2026-04-29 23:23:38 -07:00
Alezander9 9a87de27f4 fix(daemon): resolve WS via /json/version to avoid stale DevToolsActivePort path
Chrome leaves DevToolsActivePort behind across runs. If the user previously
launched Chrome with one --user-data-dir on a debug port, then later launches
Chrome with a different --user-data-dir on the same port, the file in the
first profile keeps a stale browser UUID. The TCP probe to that port
succeeds against the new Chrome, but the WS upgrade returns HTTP 404
because the cached UUID is dead -- daemon dies with
'CDP WS handshake failed: HTTP 404'.

Resolve the WS URL via /json/version on the port from DevToolsActivePort
instead of trusting the path stored alongside it. /json/version is
authoritative for whatever Chrome is currently bound to that port, so
stale paths no longer matter. The 9222/9223 fallback below already used
this approach -- now both paths agree.

Reproduced on Windows 11 with one Chrome running on port 9222 with a custom
--user-data-dir while a stale DevToolsActivePort sat in
~/AppData/Local/Google/Chrome/User Data from a prior debug session.
2026-04-29 23:21:38 -07:00
Alexander Yue c8f09ebf67 Merge pull request #256 from browser-use/docs/install-profile-sync-c-flag
docs: replace remaining heredoc examples with -c flag
2026-04-29 13:59:50 -07:00
Alezander9 db41106662 docs: replace remaining heredoc examples with -c flag
PR #229 (src layout refactor) updated SKILL.md and run.py to the -c form
when the CLI dropped stdin support, but missed install.md (steps 2 and 7)
and interaction-skills/profile-sync.md. Sync them up.

Picks up the still-relevant hunks from #215 (closes #215, refs #213).

Co-authored-by: Andres Gonzalez <62394570+FVTVLIX@users.noreply.github.com>
2026-04-29 13:55:44 -07:00
Alexander Yue 7ce5ba7ca4 Merge pull request #255 from browser-use/docs/readme-operator-framing-rebase
docs: lead README with first-buyer job (rebased from #170)
2026-04-29 13:53:11 -07:00
Alexander Yue c95a512891 Merge pull request #254 from browser-use/fix/connection-status-error-on-cdp-failure
fix(daemon): report error on stale CDP in connection_status
2026-04-29 13:51:13 -07:00
Matt Van Horn 9e609f2674 docs: lead README with first-buyer job
Rebased from #170 (authored by @mvanhorn) onto current main.
The original branch was based on a stale README; only the two
intentional lines from that PR are reapplied here.
2026-04-29 13:51:12 -07: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
FuLin 1c5a5681bb domain-skills: add Xiaohongshu scraping skill (web search, sorting, and note-opening) (#246)
* Add Xiaohongshu search and note-opening skill

* Clarify Xiaohongshu search URL encoding
2026-04-29 11:46:32 -07:00
playjing 2a478a8ff9 domain-skills/shopify-admin: KB FAQs, embedded apps, Polaris inputs (#247)
Three patterns discovered while populating the Shopify Knowledge Base App
for JING (silent training football brand). Each pattern saved 30+ minutes
of rediscovery; documenting so future agents on any Shopify store benefit.

- README.md: when to use the harness for Shopify admin vs Storefront API
  / Admin API / Shopify CLI; auth notes; Polaris React vs Web Components
  detection.

- embedded-apps.md: every Shopify app surfaces in an iframe at
  *-app.shopify.prod.shopifyapps.com. Use iframe_target() and pass
  target_id to js() calls. Known iframe slugs table (extend as
  discovered).

- polaris-inputs.md: Polaris React text inputs reject the standard
  React-friendly synthetic value setter pattern — fields fill but Save
  button stays disabled. Working pattern is .focus() via JS + CDP-native
  keystrokes via type_text(). Includes full add-FAQ implementation +
  notes on Polaris's web-component migration (polaris-react archived
  Jan 2026).

- knowledge-base.md: canonical automation for the Shopify Knowledge
  Base App (Winter '26 Edition). FAQ-pair flow, batch loop pattern,
  /pairs/<id> URL signal for save success, brand-voice guidance,
  query-log review cadence. App is currently English-only with no
  public API — browser automation is the only path until Shopify
  exposes endpoints.

Tested with 19 successful FAQ saves on the JING store on 2026-04-29.

Co-authored-by: Claude (JING build agent) <build@playjing.local>
2026-04-29 11:31:14 -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
Alexander Yue 997ee457b4 Merge pull request #245 from browser-use/add-comet-arc-profiles
daemon: discover DevToolsActivePort in Comet and Arc profiles on macOS
2026-04-28 22:46:07 -07:00
Alezander9 e4a4478a10 daemon: discover DevToolsActivePort in Comet and Arc profiles on macOS 2026-04-28 22:43:06 -07:00
Alexander Yue 2e0f7ec3a8 Merge pull request #241 from cryptoshrine/fix/windows-os-kill-systemerror
fix(admin): catch SystemError raised by os.kill on Windows
2026-04-28 22:34:29 -07:00
Alexander Yue b1ec1346a2 Merge pull request #244 from browser-use/fix/bh-tmp-dir-short-filenames
fix(_ipc): drop bu-<NAME> filename prefix when BH_TMP_DIR is set
2026-04-28 22:32:57 -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
Alexander Yue 2125cea3a5 Merge pull request #243 from browser-use/fix/ipc-tmp-dir-mkdir
fix(_ipc): mkdir -p _TMP at module load
2026-04-28 21:25:36 -07:00
Alezander9 225abcc9d1 fix(_ipc): mkdir -p _TMP at module load so BH_TMP_DIR can point at a non-existent dir
Without this, a caller passing BH_TMP_DIR=<custom dir> to a directory that
doesn't exist yet causes the first write (sock/port/pid/log/screenshot) to
fail with FileNotFoundError. Default _TMP (/tmp on POSIX, gettempdir() on
Windows) always exists, so this is latent today; it bites the per-session
scratch-dir use case browsercode is about to introduce.

Single root cause -> single fix: cover screenshots, sock, port, pid, and
log paths uniformly.
2026-04-28 21:21:05 -07:00
Alexander Yue 04f7716630 Merge pull request #242 from browser-use/claude/slack-session-sdKmR
Rename skill command from /browser-harness to /browser
2026-04-28 15:48:45 -07:00
Alexander Yue 036bfaf69c Merge pull request #240 from browser-use/fix/ensure-daemon-windows-probe
fix(admin): route ensure_daemon CDP probe through ipc.connect (Windows)
2026-04-28 15:48:30 -07:00
Alezander9 6dc41c1a5b fix(_ipc): suppress empty python.exe console window on Windows daemon spawn
DETACHED_PROCESS overrides CREATE_NO_WINDOW per Win32 docs, so combining
them caused Windows to allocate a fresh console for the daemon. Closing
that window killed the daemon and forced Chrome to re-prompt for remote
debugging permission. Drop DETACHED_PROCESS, keep CREATE_NEW_PROCESS_GROUP
for terminal-close survival.
2026-04-28 15:32:35 -07:00
Alezander9 82ef2a13ab feat(_ipc): BH_TMP_DIR overrides sock/port/pid/log + screenshot dir
Lets callers (e.g. browsercode per-session scratch) redirect all harness
file output via one env var. Default behavior unchanged when unset:
/tmp on POSIX, gettempdir() on Windows.

helpers.capture_screenshot and the debug-click overlay now route through
ipc._TMP so the same knob covers screenshots.
2026-04-28 15:21:48 -07:00
Claude ee1ff81eac Rename skill command from /browser-harness to /browser
Shortens the invocation to a cleaner /browser command.

https://claude.ai/code/session_014BWe8AkViicHviYPP843t5
2026-04-28 22:19:53 +00:00
cryptoshrine 06aad12d91 fix(admin): catch SystemError raised by os.kill on Windows
On Windows, `os.kill(pid, 0)` does not behave like its POSIX
counterpart. Instead of returning silently when the process
exists or raising ProcessLookupError when it doesn't, CPython
on Windows raises:

    SystemError: <built-in function kill> returned a result
                 with an exception set

This happens because the underlying Win32 TerminateProcess API
does not accept signal 0 as an "is alive?" probe — Python's C
implementation hits an internal error path that doesn't set a
proper exception, and the interpreter surfaces SystemError.

`restart_daemon()` already catches `(ProcessLookupError, OSError)`
around both `os.kill(pid, 0)` and `os.kill(pid, signal.SIGTERM)`,
but SystemError isn't a subclass of either, so the harness crashes
on every second invocation on Windows: the daemon's stale pid file
points at a no-longer-running pid, the probe raises SystemError,
and the whole `browser-harness -c '...'` call dies with a stack
trace.

Repro on Windows 11 (Python 3.12, browser-harness 0.1.0):

    browser-harness --reload      # ok
    browser-harness -c 'print(page_info())'   # ok
    browser-harness -c 'print(page_info())'   # CRASH

Fix: add SystemError to the existing except clauses in
restart_daemon(). One-word change in two places, matches the
intent of the existing handlers (treat any "couldn't probe/signal
the pid" failure as "process is gone, move on").
2026-04-28 22:56:48 +01:00
Alezander9 1dd4f60252 fix(admin): route ensure_daemon CDP probe through ipc.connect (Windows)
The stale-daemon probe in ensure_daemon used raw socket.AF_UNIX, which:
1. Doesn't exist on uv-bundled Python on Windows (AttributeError).
2. Would point at a TCP display string '127.0.0.1:<port>', not a socket
   path, even if AF_UNIX existed.

Either way the probe always raised, was swallowed by 'except Exception',
and fell through to restart_daemon — killing and respawning the daemon
on every warm call. Symptom is most visible on Windows where the warm
path is the common case.

Fix: use ipc.connect(name) which already does the right thing per
platform (AF_UNIX on POSIX, TCP loopback on Windows). Same call
daemon_alive and restart_daemon already use.

Also drop the now-unused _paths() helper; restart_daemon only needed
the pid_path half, inline that.
2026-04-28 13:16:28 -07:00
Vlad Rafeev ac71315eda fix: prevent chrome://inspect tab flooding during setup (#232)
When remote-debugging is not enabled, `run_setup` opens chrome://inspect
once, then retries `ensure_daemon` in a loop. But `ensure_daemon` itself
also calls `_open_chrome_inspect()` on failure — opening a new tab every
~7 seconds and flooding the browser.

Add `_open_inspect` parameter to `ensure_daemon` so the retry loop in
`run_setup` can suppress redundant tab opens after the first one.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Saurav Panda <sgp65@cornell.edu>
2026-04-28 12:01:13 -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
Alexander Yue f5f0564757 Merge pull request #225 from browser-use/fix/windows-ipc
Windows support: route IPC through ipc.py (TCP on Windows, AF_UNIX on POSIX)
2026-04-28 01:04:34 -07:00
Alezander9 51ff305bfa Use /tmp on POSIX to stay under AF_UNIX sun_path limit
tempfile.gettempdir() on macOS returns /var/folders/xx/yy.../T/ (~49 chars).
Combined with bu-{64-char-name}.sock that exceeds the 104-byte sun_path
limit on macOS (108 on Linux), causing daemon startup to fail.

Pre-PR upstream hardcoded /tmp; this restores that for POSIX. Windows is
unaffected (uses TCP, not AF_UNIX).
2026-04-28 00:24:18 -07:00
Matt Van Horn a5c973f333 fix(daemon): fire-and-forget mark-title eval so load events don't stall (#171)
* fix(daemon): fire-and-forget mark-title eval so load events don't stall

* fix(daemon): bound mark-title eval to 2s so stalled V8 can't pile up tasks

Fire-and-forget kept the perf win, but dropped the 2s upper bound. On a
discarded renderer or hard-hung V8, the Runtime.evaluate task would
never resolve, leaking one task per navigation. Restore the timeout
inside the _silent wrapper.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Saurav Panda <sgp65@cornell.edu>
2026-04-27 18:44:19 -07:00
Alezander9 b05be93d60 Slim ipc.py and rename to _ipc.py to signal internal
Cut ipc.py from 127 to 68 lines (-46%) by removing restated docstrings and
keeping only load-bearing inline comments (path-traversal guard, uv-Python
AF_UNIX gating, Windows .port-file role). Same logic, same call sites.

Rename ipc -> _ipc per Python convention for internal modules. The IPC
plumbing is only called by daemon/admin/helpers; agents reading helpers.py
should not be pulled into transport details. Callers do 'import _ipc as ipc'
so internal ipc.foo references stay unchanged.
2026-04-27 18:01:11 -07:00
Alezander9 71cf3a87a0 Merge remote-tracking branch 'origin/main' into fix/windows-ipc
# Conflicts:
#	SKILL.md
#	admin.py
#	helpers.py
2026-04-27 17:41:08 -07:00
Alezander9 b749f82350 Address PR review (cubic): docs, serve-task crash detection, name sanitization
- daemon.py:1, SKILL.md:151 — docstring/diagram said named pipe on Windows;
  the implementation is TCP loopback. Updated.
- daemon.py:209 — if ipc.serve() crashes (e.g. bind failure), the prior
  shutdown path could miss it and leave the daemon waiting forever on
  d.stop without a listening endpoint. Now race serve_task and stop.wait()
  via asyncio.wait(FIRST_COMPLETED): if serve finishes first it must have
  raised, so await it to surface the exception. Cleanup cancels both tasks
  unconditionally.
- ipc.py:45 — BU_NAME flowed straight into f-strings building filesystem
  paths, allowing path traversal outside tempdir. Validate via
  ^[A-Za-z0-9_-]{1,64}$ in a single _check() helper that all path
  builders (log_path/pid_path/port_path/_sock_path) call. Bad names raise
  ValueError early with a clear message.
2026-04-27 17:36:08 -07:00
Alezander9 991ab21f80 Windows support: route IPC through ipc.py (TCP on Windows, AF_UNIX on POSIX)
The harness was Linux/macOS-only because daemon IPC hardcoded AF_UNIX sockets
at /tmp/bu-*.sock paths and asyncio.start_unix_server, all of which are
unavailable or invalid on Windows. Worse, uv-managed Python on Windows
(python-build-standalone) ships without socket.AF_UNIX entirely (#124).

New ipc.py centralizes the platform fork:
  - POSIX: AF_UNIX socket at <tempdir>/bu-<NAME>.sock (chmod 0600), unchanged
    semantics from the prior /tmp-hardcoded path.
  - Windows: TCP loopback on 127.0.0.1:<ephemeral>, with the chosen port
    written to <tempdir>/bu-<NAME>.port so clients can find the daemon.
    Uses asyncio.start_server (stdlib, no obscure APIs, no third-party deps).

Path discipline: log/pid/port files all sit under tempfile.gettempdir() so
they land in /tmp on Linux, $TMPDIR on macOS, %TEMP% on Windows. helpers.py
screenshot() default also moves from /tmp/shot.png to tempfile.gettempdir().

subprocess detach uses start_new_session=True on POSIX and
DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP on Windows via ipc.spawn_kwargs().

run.py reconfigures stdout to UTF-8 on Windows so print(page_info()) doesn't
UnicodeEncodeError on the 🟢 marker that helpers prepend to tab titles
(#124 item 4). cp1252 (PowerShell default) can't encode it.

Verified end-to-end on Windows 11 with Chrome remote debugging:
  - daemon spawns, allocates port, writes .port file
  - goto + page_info + screenshot round-trip through TCP loopback
  - restart_daemon cleans up .port and .pid

POSIX path is logically equivalent to the prior code (same AF_UNIX call,
same socket-file semantics, same chmod 0600), routed through ipc.py.

Closes #124 items 1, 2, 4. Item 3 (Chrome 147 user-data-dir) is a separate
concern not addressed here.
2026-04-27 16:40:56 -07:00
Saurav Panda 953aebd07f Merge pull request #221 from browser-use/feat/screenshot-max-dim
feat(helpers): add max_dim to capture_screenshot
2026-04-27 12:18:43 -07:00
Saurav Panda 7d314748ee Merge branch 'main' into feat/screenshot-max-dim 2026-04-27 12:11:01 -07:00
Saurav Panda ef491eae69 Merge pull request #209 from everlastconsulting/loom-folder-enumeration
domain-skills/loom: library folder enumeration
2026-04-27 12:09:55 -07:00
Saurav Panda ffadfe6c51 fix: declare pillow dependency and use TemporaryDirectory in tests
Bot review caught two issues:

1. capture_screenshot's max_dim path imports PIL but Pillow wasn't in
   pyproject.toml dependencies, so a clean install would ImportError as
   soon as the option was used.

2. NamedTemporaryFile + writing to f.name while the handle is open is
   not portable on Windows (file lock). Switched the screenshot tests to
   tempfile.TemporaryDirectory and folded the three near-identical bodies
   into a small _run() helper.
2026-04-27 12:08:40 -07:00
Saurav Panda a2acd7a3d2 Merge pull request #214 from samtucker/add-bu-cdp-url
daemon: add BU_CDP_URL for HTTP DevTools endpoint resolution
2026-04-27 12:06:44 -07:00
Saurav Panda 1886023e8f Merge pull request #173 from drichman1-maker/fix/macos-brave-and-port-fallback
Add macOS Brave profile path + port-9222/9223 CDP probe fallback
2026-04-27 12:04:50 -07:00
Saurav Panda fca4d792ec Merge pull request #211 from hobostay/fix/missing-c-argument-validation
Fix IndexError when -c flag is passed without code argument
2026-04-27 12:03:09 -07:00
Saurav Panda e3685fd667 Merge pull request #172 from mvanhorn/fix/106-websockets-pin-chrome-147
fix(deps): pin websockets==15.0.1 to unblock Chrome 147 CDP handshake
2026-04-27 12:00:56 -07:00