提交

提交图

348 次代码提交

作者 SHA1 备注 提交日期
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
Saurav Panda 71cb1f3581 feat(helpers): add max_dim to capture_screenshot
Long agent sessions on 2× displays bust the 2000px-per-side limit some
image-aware LLMs enforce — a 2296×1143 CSS viewport produces a 4592×2286
PNG. Passing max_dim=1800 downscales the file before save (only when the
image actually exceeds max_dim), keeping callers that don't pass it
unchanged.
2026-04-27 12:00:36 -07:00
Saurav Panda aa70de78e9 Merge pull request #217 from sontianye/feat/substack-domain-skill
feat(domain-skills): add Substack scraping skill
2026-04-27 11:34:43 -07:00
Saurav Panda 767ed0cd57 Merge pull request #218 from NandiniMurali/domain-skill/expedia
Add Expedia domain skill: hotel search automation
2026-04-27 11:16:54 -07:00
Nandini Muralidharan d0ebf8637c Add Expedia domain skill: hotel search automation
Covers URL-based search (dates, destination, travellers), traveller widget
JS interaction, child age dropdowns, and price filter usage. Documents that
the date picker is unreliable with coordinate clicks and should be bypassed
via URL parameters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 14:06:41 +05:30
Tianye Song 190fe686fd fix: remove contradictory body_html=null claims for paywalled posts
Three places still described paywalled post body_html as null/None:
- TL;DR limitations list
- Approach 2 section header
- substack_get_post docstring

All updated to correctly state that body_html is a truncated HTML preview
(not null) for paywalled posts, consistent with the Gotchas section and
empirical verification.
2026-04-27 15:08:56 +08:00
Tianye Song c58bdc37f2 feat(domain-skills): add Substack scraping skill
Adds domain-skills/substack/scraping.md with four field-tested approaches
for extracting data from any Substack publication without authentication
or a browser session. All approaches verified live on 2026-04-27.

Approach 1 — /api/v1/posts: paginated post list with title, slug, audience,
wordcount, reactions, and post_id. Supports offset pagination.

Approach 2 — /api/v1/posts/{slug}: full post content. Returns complete
body_html (~40KB) for free posts; truncated HTML preview for paywalled posts.
Use audience == "everyone" as the reliable signal for full content.

Approach 3 — /api/v1/post/{id}/comments: comment list with author, body,
date, and reaction counts. Uses integer post_id (not slug).

Approach 4 — /feed: lightweight RSS metadata (title/link/pubDate/description)
without JSON parsing overhead.

Covers both URL formats (native subdomain and custom domain), pagination,
paywalled post handling, and gotchas: reactions is an emoji-keyed dict not
an integer, comments endpoint uses post_id not slug, body_html is a truncated
preview (not null) for paid posts, no unauthenticated cross-publication search.

Co-Authored-By: Tianye Song <songtianye1997@gmail.com>
2026-04-27 14:59:22 +08:00
Sam Tucker 94897c7ae3 daemon: add BU_CDP_URL for HTTP DevTools endpoint resolution
BU_CDP_URL takes a Chrome DevTools HTTP endpoint (e.g.
http://127.0.0.1:9333) and resolves it to the WS URL via /json/version,
mirroring how start_remote_daemon already handles cloud browsers.

The motivating use case is running a dedicated automation Chrome on a
non-default --user-data-dir to avoid both the Chrome 136 default-profile
lockdown and the Chrome 144+ "Allow remote debugging" per-connection
consent dialog. Pointing BU_CDP_URL at that instance lets the harness
attach without prompting the user.

Falls back to the existing default-profile DevToolsActivePort discovery
when neither BU_CDP_WS nor BU_CDP_URL is set, so existing setups are
unaffected.
2026-04-26 19:30:44 +01:00
Test User 263de47192 Fix IndexError when -c flag is passed without code argument
Running `browser-harness -c` without a code argument crashed with an
unhandled IndexError at `exec(args[1])`. Added a length check to
produce a proper usage message instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 19:58:12 +08:00
Leonard Schmedding cba31dfec0 domain-skills/loom: library folder enumeration
Adds a domain skill for enumerating Loom library folders
(loom.com/looms/videos/<slug>-<id>) — the private workspace variant
that requires an authenticated session.

yt-dlp's existing LoomFolderIE covers public-shared folders
(loom.com/share/folder/<id>) but the underlying /v1/folders/<id>
endpoint returns Forbidden for library IDs even with cookies.
That makes browser-harness with the user's open Chrome tab the
only practical programmatic route for private workspace content.

Field-tested on a 78-video folder; documents the data-videoid
selector, the scrollIntoView mechanic that beats scrollTop's
silent cap, dead-end endpoints, and the pipe-to-yt-dlp setup.
2026-04-26 14:39:50 +03:00
Saurav Panda 142e426568 Merge pull request #204 from browser-use/fix/chrome-handshake-timeout
fix(setup): retry Chrome handshake timeouts
2026-04-25 19:28:28 -07:00
Saurav Panda a53071551c fix(setup): retry Chrome handshake timeouts 2026-04-25 19:26:02 -07:00
Saurav Panda c0e7253797 Merge pull request #121 from mvanhorn/fix/108-cdp-handshake-error-message
fix: disambiguate CDP handshake error between local Chrome and cloud remote
2026-04-25 19:10:16 -07:00
Saurav Panda 837b2516a9 Merge pull request #176 from iskyiskyisky/domain-skills/gmail-compose
domain-skills: gmail — compose.md
2026-04-25 17:49:49 -07:00
Saurav Panda b31cfb458e Merge pull request #202 from anandvc/fix/switch-tab-accept-dict
fix(helpers): switch_tab accepts dict from current_tab()/list_tabs()
2026-04-25 17:45:32 -07:00
Saurav Panda 76ee7fb7c2 Merge pull request #197 from YiannisDermitzakis/youtube/watch-page-hydration-note
docs(youtube): document watch-page DOM hydration wait
2026-04-25 17:44:29 -07:00
Anand Chhatpar 192f99251b fix(helpers): switch_tab accepts dict from current_tab()/list_tabs()
current_tab() and list_tabs() both return dicts shaped {"targetId": ...,
"url": ..., "title": ...}, but switch_tab() only accepted the bare string.
This made the natural pattern

    original = current_tab()
    new_tab(...)
    switch_tab(original)

raise InvalidParameters from the CDP layer ("string value expected at
position 17"). Callers had to remember to pull ["targetId"] out manually,
which contradicts the harness's "obvious shapes work" ergonomic.

Make switch_tab tolerant of either shape: pull targetId from the dict if
one is passed, otherwise treat the input as the id string.
2026-04-25 16:31:31 -07:00
Sarath S Menon 216a2c9653 feat(cli): add --reload flag to restart the daemon (#200)
* fix(js): don't double-wrap IIFEs that contain return

The substring check `"return " in expression` incorrectly matched
expressions that already contained an IIFE with an internal return,
causing double-wrapping and a silent None result. Guard with
`not expression.strip().startswith("(")` so pre-wrapped expressions
pass through unchanged.

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

* feat(cli): add --reload flag to restart the daemon

Kills the running daemon so the next call picks up code changes,
replacing the manual pkill workflow after editing helpers.py.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:32:22 +05:30
Sarath S Menon 3b5395947a fix(js): don't double-wrap IIFEs that contain return (#199)
The substring check `"return " in expression` incorrectly matched
expressions that already contained an IIFE with an internal return,
causing double-wrapping and a silent None result. Guard with
`not expression.strip().startswith("(")` so pre-wrapped expressions
pass through unchanged.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:19:42 +05:30
Yiannis Dermitzakis dafe832b6e docs(youtube): document watch-page DOM hydration wait
When falling through to the browser path for watch-page DOM (instead of
the http_get + ytInitialPlayerResponse blob), wait_for_load() is not
enough. The load event fires before YouTube's Polymer components
hydrate — h1.ytd-watch-metadata yt-formatted-string,
ytd-video-owner-renderer #channel-name a, and ytd-watch-info-text all
return null for ~2s after load. A wait(3) after wait_for_load() is
required before querying any watch-page selector.

Field-tested 2026-04-24 on Brave; same behavior observed on
ungoogled-chromium. The HTTP path remains the recommended approach for
metadata; this note exists for the cases that genuinely need the
rendered DOM (live UI state, etc.).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:00:10 +02:00
Saurav Panda f8adad28e0 Merge pull request #184 from b00833647-cmd/feat/imdb-domain-skill
domain-skills/imdb: charts, search, and More Like This
2026-04-24 18:27:14 -07:00
Saurav Panda 1c8ce43ffa Merge pull request #194 from ambeidetic/domain-skills/polymarket
domain-skills: add Polymarket scraping skill
2026-04-24 18:21:28 -07:00
Abraham 74afacf28c domain-skills: add Polymarket scraping skill
Markdown-style domain skill for Polymarket scraping via Gamma API
(api-first per repo doctrine) with DOM leaf-div-disambiguation fallback
for CSS-module SPAs. Covers market outcomes, metadata, and comments.

Live-tested against gamma-api.polymarket.com and a live event page:
  - 9 outcomes extracted (e.g. April 7: YES 99.95 / NO 0.05, vol $45.7M)
  - Metadata: title, end_date, total_volume, category, market_count
  - 38 comments fetched (40 raw, 2 deleted skipped)

Gotcha documented: Gamma API comment envelopes for deleted comments
preserve id/createdAt/profile/media/parentCommentID but drop the body
field entirely — naive dict access throws KeyError. Guard with
'if "body" not in c: continue'.

DOM fallback pattern documented (not primary path): Polymarket has
zero data-testid attributes and CSS-module-hashed classes. Leaf-div
disambiguation (children.length === 0 + nearest-common-ancestor
grouping) is the only robust approach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:33:17 -03:00
Muqsit 106075e2ea domain-skills: handle trailing-slash URLs in perplexity id extraction 2026-04-24 10:11:13 -07:00
Luka Secilmis 3b8b2b9e91 Merge pull request #190 from browser-use/cursor/update-readme-footer-links-19e8
Update README footer Bitter Lesson and Skills links
2026-04-24 14:41:46 +02:00
Cursor Agent ce35175b78 Update README bitter lesson and skills footer links
Co-authored-by: Luka Secilmis <lukasec@users.noreply.github.com>
2026-04-24 12:36:11 +00:00
Muqsit 01fdbd7a13 domain-skills: address cubic round-2 review
- manus/tasks.md: ProseMirror composer can mount late; wrap the
  getBoundingClientRect lookup in a bounded retry + assertion so the
  helper doesn't null-deref and block submission.
- perplexity/computer.md: id extraction must parse the URL pathname
  before slicing — raw-href slicing corrupts when the URL carries
  ?view=thread or a hash fragment.
- perplexity/computer.md: all_todo_done() returned false on hydrate
  while the doc claimed None; unify on None so "can't tell" states
  (closed panel, empty panel) are distinguishable from "done=false".
2026-04-24 04:56:16 -07:00
Sarath S Menon 5918ea7e4b feat(debug): add --debug-clicks mode with DPR-aware overlay (#189)
When BH_DEBUG_CLICKS=1 (or --debug-clicks CLI flag), click_at_xy
captures a screenshot before each click and draws a red crosshair
at the exact click location, scaled by window.devicePixelRatio so
the marker aligns correctly on HiDPI/Retina displays.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:54:50 +05:30
Sarath S Menon 05d6eb8c3e refactor(run): replace heredoc stdin with -c flag (#188)
* fix(js): auto-wrap top-level return expressions in an IIFE

Agents naturally write js() calls with top-level `return`, which is a
syntax error in Runtime.evaluate. js() now detects `return ` and wraps
the expression in an IIFE so both styles work without callers needing
to know the CDP constraint.

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

* refactor(run): replace heredoc stdin with -c flag

Drops the <<'PY' heredoc pattern entirely. Agents now pass code via
`browser-harness -c "..."`, which is simpler to construct programmatically
and avoids heredoc quoting pitfalls.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:53:37 +05:30
Sarath S Menon 932347c7a2 fix(js): auto-wrap top-level return expressions in an IIFE (#187)
Agents naturally write js() calls with top-level `return`, which is a
syntax error in Runtime.evaluate. js() now detects `return ` and wraps
the expression in an IIFE so both styles work without callers needing
to know the CDP constraint.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:48:19 +05:30
Sarath S Menon 0c4af63b1b docs: move setup/maintenance content from SKILL.md to install.md (#186)
SKILL.md now covers day-to-day usage only. Maintenance commands
(--doctor, --setup, --update) and the architecture section move to
install.md where setup and break-fix content belongs.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:19:50 +05:30
Sarath S Menon 724c1c7a16 docs(SKILL.md): remove inline backticks; fix install.md goto_url rename (#185)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 14:45:28 +05:30
Faraz Ghodratizadeh b0e1925ac7 domain-skills/imdb: charts, search, and More Like This
Adds domain-skills/imdb/scraping.md covering four field-tested paths:
- /chart/top (Top 250)
- /chart/moviemeter (100 trending)
- /find/?s=tt&q=KEYWORD (title search)
- /title/tt{id}/ "More Like This" panel (requires scroll-triggered hydration)

Highlights the shared li.ipc-metadata-list-summary-item row primitive
(reused across charts and search), rank-prefix parsing in
h3.ipc-title__text, abbreviated-vote parsing, and the
heading-regex-then-walk-up pattern for locating containers whose class
hashes churn.

Verified 2026-04-24.
2026-04-24 10:50:20 +02:00
Muqsit 253887bb07 domain-skills: review feedback + SKILL.md compliance fixes
manus/tasks.md
- Correct the Connect RPC transport description: the JS client wraps the
  JSON payload in a Uint8Array before fetch, so a naive fetch-hook logs
  it as <<bytes:N>> — the wire content matches content-type (JSON both
  ways), not a separate binary codec.
- Replace the raw click(750, 505) example with a computed rect from
  div.ProseMirror[contenteditable="true"] (SKILL.md: no pixel coords).
- Drop the specific-task prompt example ('reply with just the word PONG'
  → 'Reply with PONG') for a generic description of the auto-summary.
- Remove a real taskId from the URL-pattern docstring.

perplexity/computer.md
- Scope Todo-panel queries to the Radix popover. The Todo button has
  data-state / aria-expanded / aria-controls; the open panel is mounted
  with id === button.aria-controls. A global 'svg use' query across the
  whole document picks up tool-invocation icons, sidebar icons, and top-
  bar icons — scope-less completion detection is unreliable.
- Rewrite all_todo_done() to return None when the panel is closed or
  hydrating and True only when every row's icon is #pplx-icon-check.
- Remove a real slug and taskId from the URL-pattern docstring.
2026-04-23 23:20:58 -07:00
Muqsit 5b3807719b domain-skills: manus + perplexity — task workflows
- manus/tasks.md: Connect RPC endpoints at api.manus.im, TipTap composer,
  task URL pattern /app/<id>, share URL pattern /share/<id>, task-completion
  selectors, share popover + copy-link flow (execCommand path).
- perplexity/computer.md: dashboard vs detail URLs under /computer/tasks,
  Lexical composer with [data-ask-input-container] + [data-lexical-editor]
  markers, ARIA grid task list (role=row / role=cell), Todo + Usage panels,
  tool-invocation rows (group/tool-wrapper), artifact URLs (pplx-res
  Cloudinary vs presigned CloudFront), share popover with
  data-testid=access-level-{private,specific-people,public} and the
  ?view=thread share URL pattern.
2026-04-23 23:08:42 -07:00
Sarath S Menon 361c90e0a7 Merge pull request #178 from browser-use/refactor/remove-playwright-name-overlap
refactor: rename goto/click/screenshot to avoid Playwright name overlap
2026-04-24 11:26:22 +05:30
Sarath Suresh fbd9146df5 refactor: rename goto/click/screenshot to avoid Playwright name overlap
- goto()       → goto_url()
- click()      → click_at_xy()
- screenshot() → capture_screenshot()

These three shared exact names with Playwright but different argument
shapes, causing agent confusion. Updated helpers.py and all markdown
skill files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:26:07 +05:30
Sarath S Menon d08ca526bf Merge pull request #177 from browser-use/docs/remove-bold-formatting
docs(SKILL.md): remove bold formatting
2026-04-24 10:53:29 +05:30
Sarath Suresh 3dfb08a73d docs(SKILL.md): remove bold formatting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 10:52:30 +05:30
Isky cf6a7322da domain-skills: gmail — compose.md
Covers open-compose shortcut, multiple-dialog stacking, the Tab-inserts-literal-tab trap, attachments via DOM.setFileInputFiles on the visible dialog's input, and stable selectors for To/Subject/Body/Send.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:55:16 -07:00
drichman1-maker 704b1d41b7 Add macOS Brave profile path + port-9222/9223 CDP probe fallback
Two small fixes in daemon.py for macOS users running Brave (or any
Chromium variant launched with --remote-debugging-port):

1. PROFILES list was missing the macOS path for Brave
   (~/Library/Application Support/BraveSoftware/Brave-Browser).
   The Linux Flatpak path was the only Brave entry, so macOS Brave
   users got "DevToolsActivePort not found" even after enabling
   remote debugging.

2. When Chromium is launched with an explicit --remote-debugging-port
   flag, the DevToolsActivePort file is not always written to the
   profile dir. CDP is fully live, but get_ws_url() can't find it.
   Added a final fallback that probes 127.0.0.1:9222 and :9223 via
   /json/version and uses the returned webSocketDebuggerUrl. The
   loop is gated to the standard debugging ports and only runs after
   the existing profile-dir scan fails, so it doesn't interfere with
   the normal sticky-checkbox flow.

Both changes are additive — no existing behavior is altered.
2026-04-23 14:31:50 -04:00
Matt Van Horn 4faf1d7414 fix(deps): pin websockets==15.0.1 to unblock Chrome 147 CDP handshake 2026-04-23 07:46:36 -07:00
Isky 17e88b4e46 domain-skills: amazon — cart.md, orders.md
Two new domain skills for amazon.com derived from a logged-in session:

- cart.md: documents the empty-cart trap where `[data-asin]` selectors
  match recommendation widgets even on an empty cart, plus the stable
  subtotal selectors and the price-change banner format.

- orders.md: covers the order-list card structure, the order-search
  quirk (the `?search=` URL param doesn't filter — must submit the
  form or hit the post-submit `/your-orders/search/` URL), and the
  tracking page (whose `data-test-id` selectors are all null — extract
  from `body.innerText` anchored on the "Arriving" line).
2026-04-23 01:40:43 -07:00
Gregor Žunič ba8b22f1ff fix(run): exec scoping for comprehensions + sharpen click guidance (#133)
* fix(run): pass globals to exec so comprehensions resolve free vars

exec(sys.stdin.read()) inside main() passes different dicts for globals
and locals. Python comprehensions and generator expressions compile to a
nested function whose free-variable lookups can only see the globals
dict, so code like

    for it in items:
        low = it['text'].lower()
        hit = any(k in low for k in KEYS)   # NameError: 'low'

fails at module level under exec. `low` is stored in the exec locals
dict which the generator's implicit function cannot see.

Passing globals() as the only extra arg makes exec use the same dict
for both globals and locals, and comprehensions resolve cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(skill): eyeball click coords from screenshots; no getBoundingClientRect

The previous Clicking bullet said "screenshot -> look -> click(x,y)" but
did not rule out roundtripping through js("...getBoundingClientRect()")
to compute coords. Agents coming from Playwright / Selenium habits tend
to locate-first-click-second even when the screenshot already shows the
target, which is slower and more brittle (hidden inputs placed at
x=-9999, CSS-transformed elements, pseudo-elements) than just reading
the pixel off the image.

Rewrite the bullet to explicitly suppress that reflex and scope the
DOM-fallback to elements with no visible geometry. Also replace the
loose "compositor level" phrasing with the actual mechanism: Chrome's
browser-process hit-testing, which is why clicks pass through iframes
/ shadow DOM / cross-origin without extra work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:06:16 -07:00
Gregor Žunič e8941a5f35 fix(admin): ensure_daemon self-heals cold-start failures (#161)
Reuses the existing `_open_chrome_inspect()` helper so a bare
`browser-harness <<PY ... PY` invocation recovers from the three
cold-start failures that previously forced a manual restart_daemon()
or a separate `browser-harness --setup` run:

  1. Stale daemon — previous run's Chrome was killed but the daemon
     still listens on the unix socket. Probe with a real CDP call
     (`Target.getTargets`) and require "result" in the reply. A
     `{"meta":"session"}` probe would not work because the daemon
     answers meta requests from a cached Python dict even when its
     CDP WebSocket to Chrome is dead.
  2. Cold Chrome — log tail says "DevToolsActivePort not found" or
     "not live yet". `_open_chrome_inspect()` uses AppleScript
     `activate` which launches Chrome if absent, then opens the
     inspect page.
  3. Missing Allow on chrome://inspect — log tail says
     "WS handshake failed ... 403". Same recovery: open the inspect
     page, print one stderr hint ("click Allow..."), retry spawn once.

Net: +37/-22 lines, one function touched, no new helpers.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:00:41 -07:00