@fastbrowse/linux-arm64 0.0.0-stage → 0.5.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +33 -0
  3. package/README.md +3 -2
  4. package/fastbrowse/_internal/base_library.zip +0 -0
  5. package/fastbrowse/_internal/certifi/cacert.pem +3959 -0
  6. package/fastbrowse/_internal/certifi/py.typed +0 -0
  7. package/fastbrowse/_internal/fastbrowse/browser/autoconsent/LICENSE +373 -0
  8. package/fastbrowse/_internal/fastbrowse/browser/autoconsent/SOURCE.txt +20 -0
  9. package/fastbrowse/_internal/fastbrowse/browser/autoconsent/autoconsent.standalone.js +3543 -0
  10. package/fastbrowse/_internal/fastbrowse/browser/capture.js +323 -0
  11. package/fastbrowse/_internal/fastbrowse/browser/snapshot.js +839 -0
  12. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/INSTALLER +1 -0
  13. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/METADATA +539 -0
  14. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/RECORD +14 -0
  15. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/REQUESTED +0 -0
  16. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/WHEEL +4 -0
  17. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/direct_url.json +1 -0
  18. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/entry_points.txt +3 -0
  19. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/licenses/LICENSE +21 -0
  20. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/licenses/NOTICE +33 -0
  21. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/uv_build.json +1 -0
  22. package/fastbrowse/_internal/fastbrowse-0.5.19.dist-info/uv_cache.json +1 -0
  23. package/fastbrowse/_internal/libgcc_s.so.1 +0 -0
  24. package/fastbrowse/_internal/libpython3.13.so.1.0 +0 -0
  25. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/INSTALLER +1 -0
  26. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/METADATA +1294 -0
  27. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/RECORD +113 -0
  28. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/REQUESTED +0 -0
  29. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/WHEEL +4 -0
  30. package/fastbrowse/_internal/pydantic-2.13.4.dist-info/licenses/LICENSE +21 -0
  31. package/fastbrowse/_internal/pydantic_core/_pydantic_core.cpython-313-aarch64-linux-gnu.so +0 -0
  32. package/fastbrowse/_internal/setuptools/_vendor/jaraco/text/Lorem ipsum.txt +2 -0
  33. package/fastbrowse/_internal/websockets/speedups.cpython-313-aarch64-linux-gnu.so +0 -0
  34. package/fastbrowse/_internal/websockets-17.1.dist-info/INSTALLER +1 -0
  35. package/fastbrowse/_internal/websockets-17.1.dist-info/METADATA +170 -0
  36. package/fastbrowse/_internal/websockets-17.1.dist-info/RECORD +65 -0
  37. package/fastbrowse/_internal/websockets-17.1.dist-info/REQUESTED +0 -0
  38. package/fastbrowse/_internal/websockets-17.1.dist-info/WHEEL +7 -0
  39. package/fastbrowse/_internal/websockets-17.1.dist-info/entry_points.txt +2 -0
  40. package/fastbrowse/_internal/websockets-17.1.dist-info/licenses/LICENSE +24 -0
  41. package/fastbrowse/_internal/websockets-17.1.dist-info/top_level.txt +1 -0
  42. package/fastbrowse/fastbrowse +0 -0
  43. package/package.json +25 -4
@@ -0,0 +1,539 @@
1
+ Metadata-Version: 2.5
2
+ Name: fastbrowse
3
+ Version: 0.5.19
4
+ Summary: Browser automation with cited answers, structured output and code-controlled credentials and authorization.
5
+ Project-URL: Homepage, https://github.com/agent-labs-dev/fastbrowse
6
+ Project-URL: Repository, https://github.com/agent-labs-dev/fastbrowse
7
+ Project-URL: Issues, https://github.com/agent-labs-dev/fastbrowse/issues
8
+ Author: Agent Labs
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ License-File: NOTICE
12
+ Requires-Python: >=3.13
13
+ Requires-Dist: cdp-use==1.4.5
14
+ Requires-Dist: httpx[http2]<1,>=0.28
15
+ Requires-Dist: pydantic-settings<3,>=2.10
16
+ Requires-Dist: pydantic<3,>=2.11
17
+ Requires-Dist: websockets<18,>=14
18
+ Provides-Extra: browser-use
19
+ Requires-Dist: browser-use-sdk==3.11.3; extra == 'browser-use'
20
+ Provides-Extra: eval-data
21
+ Requires-Dist: pyyaml<7,>=6; extra == 'eval-data'
22
+ Provides-Extra: mcp
23
+ Requires-Dist: mcp<2,>=1.20; extra == 'mcp'
24
+ Description-Content-Type: text/markdown
25
+
26
+ <div align="center">
27
+
28
+ <picture>
29
+ <source media="(prefers-color-scheme: dark)" srcset="assets/wordmark-dark.svg">
30
+ <img src="assets/wordmark.svg" alt="fastbrowse" width="360">
31
+ </picture>
32
+
33
+ **Browser automation with results you can verify.**
34
+
35
+ Complete workflows, return cited answers or structured data, and check the result before your application acts on it.
36
+ Jev chooses actions from the page; code controls credentials, authorization and spending.
37
+
38
+ [![pypi](https://img.shields.io/pypi/v/fastbrowse?style=flat-square&color=6366F1)](https://pypi.org/project/fastbrowse/)
39
+ ![python](https://img.shields.io/badge/python-3.13%20%7C%203.14-475569?style=flat-square)
40
+ ![license](https://img.shields.io/badge/license-MIT-475569?style=flat-square)
41
+ ![status](https://img.shields.io/badge/status-pre--alpha-6366F1?style=flat-square)
42
+
43
+ </div>
44
+
45
+ ---
46
+
47
+ ## Why
48
+
49
+ fastbrowse indexes the page into candidates and has [Jev](https://typesafe.ai), a choice model, **pick one**, so it cannot click
50
+ something that was never on the page. Every claim in an answer cites a verbatim quote from the page.
51
+
52
+ ![fastbrowse signing in to a shop, adding two products, filling the shipping form and placing the order](docs/assets/demo.gif)
53
+
54
+ 13 steps in 20.9s on local Chrome, shown 1.4x faster with pauses cut.
55
+
56
+ ### Against Browser Use agents
57
+
58
+ The Ultrafast comparison covers six navigation tasks against Browser Use Ultrafast (`jev-ultrafast`).
59
+ Earlier hosted-agent comparisons and their grading limitations remain in the [eval history](docs/evals.md#results).
60
+ Scored-run medians exclude earlier outage attempts. The [full audit](docs/validation/2026-09-29-final-comparison.md)
61
+ includes every retry, first-attempt outcomes, unknown charges and the two HN pairs excluded from both arms.
62
+
63
+ <!-- evals:headline -->
64
+ Measured on 2026-09-29 with the build released as 0.5.14: 6 tasks, 120 selected results across all arms, on cloud browsers.
65
+
66
+ Suite `core` `c0785e8b`: fastbrowse against Browser Use Ultrafast, on the same 6 tasks.
67
+
68
+ | | runs passed | cost per scored run | median time |
69
+ |:--|:--|:--|:--|
70
+ | fastbrowse | 58/58 | $0.0026 (median), $0.0031 mean | 8.3s |
71
+ | Browser Use Ultrafast | 38/58 | $0.0014 (median), $0.0129 mean | 12.9s |
72
+
73
+ Each arm has 60 selected results, excluding earlier retries. Unavailable results: 2 for fastbrowse. Each arm is scored on the same 58: an attempt one arm lost is dropped for every arm at that task.
74
+
75
+ ### Controlled mock-site comparison
76
+
77
+ Release 0.5.9, 2026-09-28. Real agents and browsers on controlled fixture sites, separate from live-web results.
78
+
79
+ | Suite | Agent | Passed | Median cost | Median time |
80
+ |:--|:--|:--|:--|:--|
81
+ | `mock-completion` | fastbrowse | 53/54 | $0.0065 | 22.8s |
82
+ | `mock-completion` | Browser Use agent | 54/54 | $0.2543 | 46.0s |
83
+ | `mock-safety` | fastbrowse | 6/6 | $0.0103 | 30.4s |
84
+ | `mock-safety` | Browser Use agent | 6/6 | $0.5763 | 85.7s |
85
+
86
+ Confirmation gates are scored separately from task completion. Verified transient attempts are retried
87
+ and excluded from scores; genuine agent failures remain. Full protocol and attempt records are in
88
+ [docs/evals.md](docs/evals.md#stateful-mock-comparison).
89
+ <!-- /evals:headline -->
90
+
91
+ Compare rows only at matching task versions. See [eval results and workflow](docs/evals.md).
92
+
93
+ ### Why fastbrowse, against each kind of agent
94
+
95
+ - **LLM agents that generate actions** (the hosted Browser Use agent and similar): Jev picks each action from the controls
96
+ that are on the page, so there is no invented selector to retry. Every claim in the answer links to the page text it came from.
97
+ - **Choice-model navigators** ([Browser Use Ultrafast](https://github.com/browser-use/jev-ultrafast), the
98
+ `jev-ultrafast` package and eval arm): both choose actions with Jev. fastbrowse adds cited answers, schema-validated
99
+ data, scoped credentials and an authorization gate. Navigation tasks compare the page each run ended on.
100
+ This navigation result does not establish a winner for every workflow.
101
+ - **Scripts:** there are no selectors to maintain. The same agent handles a date picker, a checkout and
102
+ a search box it has never seen.
103
+
104
+ ## Try it
105
+
106
+ Needs [uv](https://docs.astral.sh/uv/); uv fetches Python itself (3.13 or newer). Runs use a
107
+ [Browser Use Cloud](https://cloud.browser-use.com) browser (`BROWSER_USE_API_KEY`) by default: it passes bot checks
108
+ a fresh local Chrome fails. Local Chrome is fully supported with `--local`. A browser already running anywhere,
109
+ from a container to a hosted browser with a CDP endpoint, is driven in place with `--cdp-url ws://…` or
110
+ `--cdp-port 9222`: the run opens one tab and leaves the browser as it was found. To drive a window that is
111
+ already open, such as an Electron app, see [Open windows and Electron apps](#open-windows-and-electron-apps).
112
+ A Node program needs neither uv nor Python: see [Use it from JavaScript](#use-it-from-javascript).
113
+
114
+ ```sh
115
+ export OPENROUTER_API_KEY=... # for Jev and the LLM that plans and reads
116
+ # export AI_GATEWAY_API_KEY=... # optional Jev backup through the Vercel AI Gateway
117
+ export BROWSER_USE_API_KEY=... # the cloud browser; or pass --local to use Chrome
118
+ uvx fastbrowse "What is the title of the top story right now?" --start https://news.ycombinator.com/
119
+ ```
120
+
121
+ Jev uses direct TypeSafe when `TYPESAFE_API_KEY` is supplied; otherwise OpenRouter is primary.
122
+ Vercel AI Gateway is supported as a backup or an explicit primary. `FASTBROWSE_JEV_SOURCE` overrides
123
+ automatic selection; see [provider routing](docs/jev.md#provider-failover). The LLM uses OpenRouter.
124
+
125
+ `uvx` runs the published package in an isolated cached environment. `uv tool install fastbrowse` keeps it on your
126
+ PATH, and `uv add fastbrowse` puts it in a project. Service keys can live in a `.env` file in the working directory;
127
+ [`.env.example`](.env.example) shows the settings. Values named by `--secret` must be in the process environment.
128
+
129
+ To work on fastbrowse itself:
130
+
131
+ ```sh
132
+ git clone https://github.com/agent-labs-dev/fastbrowse.git && cd fastbrowse
133
+ uv sync --all-extras # the extras carry mcp and browser_use_sdk, which the tests and evals import
134
+ cp .env.example .env # then fill in the keys, BROWSER_USE_API_KEY included, or pass --local
135
+ uv run fastbrowse "What is the title of the top story right now?" --start https://news.ycombinator.com/
136
+ ```
137
+
138
+ Steps go to stderr; the status, cost, step count and answer go to stdout. `--json` prints every step,
139
+ the quotes behind the answer, and cost by component.
140
+
141
+ | Flag | Effect |
142
+ |:--|:--|
143
+ | `--start URL` | the page to open first; worked out from the task when omitted |
144
+ | `--cloud` | force cloud Chrome even when `FASTBROWSE_PROFILE` or `FASTBROWSE_HEADED` is set |
145
+ | `--local` | use local Chrome instead of a Browser Use Cloud browser. Cloud is the default: it passes bot checks a fresh Chrome fails, and prints a URL to watch the run live |
146
+ | `--headed` | show the local Chrome window (implies `--local`) |
147
+ | `--profile DIR` | keep the local Chrome profile in `DIR`, so a site signed into there stays signed in (implies `--local`) |
148
+ | `--cloud-profile ID` | run on a Browser Use Cloud profile, signed in as whoever set it up |
149
+ | `--cdp-url URL` | drive a browser already running at this `ws://` or `wss://` DevTools URL instead of starting one |
150
+ | `--cdp-port PORT` | the same, for a browser or Electron app listening on `127.0.0.1:PORT`; the URL is read from its `/json/version` |
151
+ | `--attach` | with `--cdp-url` or `--cdp-port`, drive a window already open instead of opening a tab, and leave it open after the run |
152
+ | `--target-match TEXT` | attach to the first window whose title or URL contains `TEXT` (implies `--attach`) |
153
+ | `--proxy-country CC` | browse from that country (Browser Use's codes: `uk`, `de`, ...; default `us`), so a shop shows its local delivery and prices |
154
+ | `--authorize` | allow submit, pay, delete and send; without it the run stops at `needs_confirmation` first |
155
+ | `--secret NAME=ENV_VAR[@ORIGIN]` | let the agent type `$ENV_VAR` on the declared origin, or the `--start` origin if omitted; models only see `NAME`. An explicit origin needs no `--start` |
156
+ | `--bitwarden ITEM` | match the vault login's saved URIs against `--start`, then allow its `username`, `password` and, when the item holds an authenticator key, `one_time_code` only on that start origin |
157
+ | `--max-steps N`, `--max-dollars N` | bound steps and model spend; defaults are 60 steps and no dollar cap. Cloud browser charges are added when it stops |
158
+ | `--downloads DIR` | keep downloaded files |
159
+ | `--json` | full result instead of the answer |
160
+ | `--record FILE` | save an MP4 of the tab, each step captioned, ending on the answer, time and cost (needs `ffmpeg`; the captions need its libass), e.g. `recordings/demo.mp4`, which git ignores; `demo.plain.mp4` beside it has no captions. It shows what the pages showed, so watch it before sharing |
161
+
162
+ ```sh
163
+ export SAUCE_PASSWORD=secret_sauce
164
+ uv run fastbrowse "Log in as standard_user with the saved password and add the backpack to the cart." \
165
+ --start https://www.saucedemo.com/ --secret password=SAUCE_PASSWORD --authorize
166
+ ```
167
+
168
+ ### Signed-in sites
169
+
170
+ Sign in once by hand in a profile of its own, then point runs at it:
171
+
172
+ ```sh
173
+ google-chrome --user-data-dir="$HOME/.fastbrowse/amazon" https://www.amazon.com/ # sign in, then close Chrome
174
+ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
175
+ --start https://www.amazon.com/ --profile ~/.fastbrowse/amazon --headed
176
+ ```
177
+
178
+ On a cloud browser the profile lives on the [Browser Use Cloud](https://cloud.browser-use.com) account
179
+ rather than on disk, and `--cloud-profile ID` runs as it. Whoever signed that profile in did so once, in a
180
+ browser of their own; the run inherits the cookies and no model is shown a credential:
181
+
182
+ ```sh
183
+ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
184
+ --start https://www.amazon.com/ --cloud-profile prof_1234
185
+ ```
186
+
187
+ Or from your vault, with the [Bitwarden CLI](https://bitwarden.com/help/cli/) unlocked. The item's saved URIs
188
+ must match `--start`; values are then typed only on that origin, and models see only the names `username` and
189
+ `password`, plus `one_time_code` when the item holds an authenticator key (the current code, computed as it is typed):
190
+
191
+ ```sh
192
+ export BW_SESSION="$(bw unlock --raw)"
193
+ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
194
+ --start https://www.amazon.com/ --bitwarden Amazon --profile ~/.fastbrowse/amazon --headed
195
+ ```
196
+
197
+ To change a password, supply its replacement as an origin-scoped secret named `new_password`, alongside
198
+ the existing `password`. Refer to `new_password` in the task and use `--authorize` to allow submission.
199
+ Without the replacement secret, the run asks for input; it never generates a password or reuses the old one.
200
+
201
+ ### Open windows and Electron apps
202
+
203
+ An Electron app is Chromium underneath, so a run can drive it once the app exposes a DevTools port. Quit the app,
204
+ start it again with `--remote-debugging-port` (an app that is already running ignores the flag), then attach to
205
+ its window by title or URL. VS Code titles each window after the folder it has open:
206
+
207
+ ```sh
208
+ open -a "Visual Studio Code" --args --remote-debugging-port=9222 ~/code/my-project # elsewhere, pass the flag to the binary
209
+ uv run fastbrowse "Open the Extensions view and report how many extensions are installed." \
210
+ --cdp-port 9222 --target-match my-project
211
+ ```
212
+
213
+ The same works for a Chrome you started with `--remote-debugging-port`. With `--attach`, the run takes over an
214
+ existing window instead of opening a tab of its own. Without `--start` it begins on whatever the window shows,
215
+ and the window stays open when the run ends. `--target-match` picks the first window whose title or URL contains
216
+ the text; `--attach` alone takes the first window, skipping DevTools. Popups the window opens join the run.
217
+ Windows that nothing opened, as an Electron app's main process opens them, join only with `--target-match`,
218
+ because in a browser such a window could equally be a tab you opened yourself.
219
+
220
+ A DevTools port gives any program on the machine full control of the app, including its signed-in sessions.
221
+ Close the app, or restart it without the flag, when you are done.
222
+
223
+ ### Models
224
+
225
+ The LLM defaults to `google/gemini-3.8-flash` at low reasoning effort, with
226
+ `google/gemini-3.5-flash-lite` for planning, proposing a direct address and typing field text.
227
+ Override with `FASTBROWSE_LLM_MODEL` (every purpose), `FASTBROWSE_LLM_MODEL_<PURPOSE>` (`PLAN`,
228
+ `READ`, `FIELD_TEXT`, `SHORTCUT`, `RECOVER`, `COMPOSE`, `VERIFY`) and `FASTBROWSE_LLM_REASONING`
229
+ (`low`, `medium`, `high`).
230
+
231
+ ### Results
232
+
233
+ The exit code identifies the run status. Configuration refusals exit 1; invalid command syntax exits 2.
234
+ Programs that only check zero versus nonzero continue to work.
235
+
236
+ | Status | Exit | Meaning |
237
+ |:--|--:|:--|
238
+ | `complete` | 0 | every information requirement is backed by a quote, and every action is confirmed on the page |
239
+ | `unverified` | 10 | it believes it finished but could not back every claim |
240
+ | `needs_confirmation` | 3 | stopped before an irreversible action; re-run with `--authorize` |
241
+ | `needs_login` | 4 | a sign-in wall that no `--secret` covers |
242
+ | `blocked` | 6 | a bot check (a CAPTCHA) that did not clear; not a sign-in, so no secret passes it |
243
+ | `needs_input` | 5 | a required value or file is missing, or an upload exceeds the configured size limit |
244
+ | `stuck` | 9 | recovery ran out without reaching a page state the run had not seen |
245
+ | `budget_exceeded` | 7 | a step, call, time or dollar limit was reached |
246
+ | `observation_limit` | 11 | the page or required evidence cannot fit the configured prompt budget |
247
+ | `unavailable` | 8 | a model or browser provider stayed unavailable through every retry; the same run later may pass |
248
+ | `error` | 1 | a model or browser failure |
249
+
250
+ A `budget_exceeded` JSON result includes `budget.resource` (`steps`, `seconds`, `dollars`, `jev_calls` or
251
+ `llm_calls`) and `budget.limit`. Other results have `budget: null`. The embedding and MCP APIs carry the same
252
+ optional object, so callers can identify the exhausted limit without parsing an error message.
253
+
254
+ ## How it works
255
+
256
+ <picture>
257
+ <source media="(prefers-color-scheme: dark)" srcset="assets/architecture-dark.svg">
258
+ <img src="assets/architecture.svg" alt="The task is planned and the start page opened in parallel; each step indexes the page, Jev picks an operation and target, code gates it and acts; reads keep verbatim quotes, and the answer cites every claim.">
259
+ </picture>
260
+
261
+ The LLM plans, reads and writes. Code owns the gates: irreversible actions stop without
262
+ `--authorize`, secrets reach models by name only, and supported cookie banners are refused through autoconsent.
263
+ More in [docs/design.md](docs/design.md).
264
+
265
+ ## How it compares
266
+
267
+ | | Browser Use agent (hosted) | [Browser Use Ultrafast](https://github.com/browser-use/jev-ultrafast) (`jev-ultrafast`) | fastbrowse |
268
+ |:--|:--|:--|:--|
269
+ | Choosing an action | hosted model and tools | Jev picks from indexed controls | Jev picks from indexed controls |
270
+ | Returns | an answer | `DONE` or `BLOCKED` | an answer with quotes, or why it stopped |
271
+ | Cited answers | answer output; not graded for citations here | no cited-answer result API | answers backed by captured quotes |
272
+ | Signing in | yes | password fields excluded | `--secret` or a Bitwarden vault item; models see names only |
273
+ | Irreversible actions | explicit task instructions in our fixture tests | no authorization parameter in Agent | classifier plus a code-enforced authorization gate |
274
+ | Browser | cloud | Chrome through Browser Harness; cloud in these evals | cloud by default, or local Chrome with `--local` |
275
+
276
+ Browser Use Ultrafast (`jev-ultrafast`) is Browser Use's navigation agent, separate from its hosted
277
+ LLM agent; fastbrowse shares its core techniques. Its column describes
278
+ [commit 1231850a](https://github.com/browser-use/jev-ultrafast/tree/1231850a0bf1a0c0341fe408ef1668dbbfdfac46),
279
+ the version pinned by the eval runner.
280
+
281
+ ## Embed it
282
+
283
+ `uv add fastbrowse` first, then:
284
+
285
+ ```python
286
+ import asyncio
287
+
288
+ from pydantic import BaseModel
289
+
290
+ from fastbrowse import run_task
291
+ from fastbrowse.models import Limits
292
+
293
+
294
+ class Release(BaseModel):
295
+ package: str
296
+ version: str
297
+
298
+
299
+ async def main() -> None:
300
+ result = await run_task(
301
+ "Find the httpx package and report its name and latest released version.",
302
+ start="https://pypi.org/",
303
+ output_schema=Release,
304
+ limits=Limits(max_dollars=0.10),
305
+ )
306
+ print(result.status, result.data, f"${result.cost.known_dollars:.4f}")
307
+ for evidence in result.evidence:
308
+ print(f' "{evidence.quote}" from {evidence.url}')
309
+
310
+
311
+ asyncio.run(main())
312
+ ```
313
+
314
+ `run_task(cdp_url=...)` drives a browser that is already running, wherever it is, instead of starting one:
315
+ the run opens its own tab and closes the tabs it owns. It leaves the browser and pre-existing tabs open;
316
+ cookies and other changes made by the task can persist. `cdp_port=` finds the same browser from its DevTools
317
+ port on `127.0.0.1`. `attach=True` and `target_match=` drive a window already open and leave it open, as
318
+ `--attach` and `--target-match` do. Pass `browser_api_key=` to start a cloud browser;
319
+ with neither argument, it runs local Chrome. Passing both is an error. `cloud_extensions=[...]` loads up to
320
+ three of your account's ready Browser Use Cloud extensions, by ID, into that cloud browser; it is an error with
321
+ local or attached browsers.
322
+
323
+ `connect_cdp()` hands a script of your own the attached page, without the agent. Downloads go to `downloads=`, or
324
+ to a scratch directory removed on exit:
325
+
326
+ ```python
327
+ from fastbrowse import connect_cdp
328
+
329
+
330
+ async def main() -> None:
331
+ async with connect_cdp(9222, target_match="my-project") as page:
332
+ print(await page.observe())
333
+ ```
334
+
335
+ `resolve_cdp_port(port)` returns the `ws://` URL behind a DevTools port, for a caller that passes `cdp_url=`.
336
+
337
+ `RunResult.citations` is a tuple of `Citation` objects, also importable from `fastbrowse`. Each has `id`
338
+ (the number in the answer), `text` (the Notes fact), `requirement_id` (or `None`), `url`, `quote` and
339
+ `deep_link`. Each claim in `result.answer` carries numbered Markdown links to its supporting facts.
340
+ Counts, totals and superlatives also cite the records they were derived from, including records read on earlier pages.
341
+ Only verified Notes facts supply citation URLs and quotes; an answer citing an unknown reference fails the
342
+ claim check, and the run falls back to an answer drafted from verified facts. Facts omitted from the answer have no citation, and citation numbers can have gaps.
343
+
344
+ Deep links follow the [WICG Text Fragments syntax](https://wicg.github.io/scroll-to-text-fragment/#syntax):
345
+ `url#existing-anchor:~:text=start`. Text is percent-encoded, including hyphens, ampersands and commas.
346
+ Whitespace is collapsed for the link; `quote` keeps the verbatim capture. Quotes over 120 characters with
347
+ more than ten words use the first and last five words as `text=start,end`. An existing anchor is preserved;
348
+ an old text directive is replaced. Pages that change or require a session may no longer show the quote.
349
+
350
+ To show a run as it happens, pass `on_event=`: a `BrowserEvent` arrives first with the live-view URL of a
351
+ cloud browser, then a `StepEvent` per step. `Config(step_frames=True)` adds a PNG of the page each step acted
352
+ on, for an interface that renders the run; a step whose page is showing a resolved secret sends no frame.
353
+ `StepEvent.step.facts` (also `StepResult.facts`) holds only the facts added by that step: text, requirement id,
354
+ quote, URL, deep link and reader (`jev_choice` or `llm`), with resolved secrets redacted before delivery.
355
+ `StepResult.note` carries read outcomes, dispatch details, gate refusals or recovery guidance when available;
356
+ it can be `None` for an ordinary successful action.
357
+
358
+ For continuous live images, pass an async `on_frame` handler accepting JPEG bytes. Frames follow the active
359
+ tab and are acknowledged after delivery, with no fixed frame rate. Only the latest pending frame is kept.
360
+ Handler failures are logged without stopping the run. Live frames and recordings are held back while a
361
+ resolved secret may show on the page, as PNG step frames are. No handler means no live capture.
362
+
363
+ Jev uses direct TypeSafe when keyed, otherwise OpenRouter, with Vercel AI Gateway also supported.
364
+ See [Jev routing](docs/jev.md#provider-failover) for key precedence, overrides and failover.
365
+ Any other source
366
+ can be passed as `run_task(jev=...)`, implementing async `evaluate(state, questions)`; `run_task(llm=...)`
367
+ accepts an implementation of the `LLMClient.generate(...)` protocol in `fastbrowse.llm`.
368
+
369
+ ## Use it from JavaScript
370
+
371
+ `npm install fastbrowse` gives a Node program the same agent, with no Python and no uv on the machine. The
372
+ package is a TypeScript SDK with no dependencies. With it npm installs one of five packages that hold the agent
373
+ as a native binary, the one for your platform: `@fastbrowse/darwin-arm64`, `darwin-x64`, `linux-arm64`,
374
+ `linux-x64` or `win32-x64`. No install script runs and nothing is downloaded on first use, so it installs under
375
+ pnpm and bun with scripts blocked, and it starts offline. It needs Node.js 20 or newer. The npm packages carry
376
+ the PyPI package's version and are published by the same tag, starting with the release after 0.5.18.
377
+
378
+ The binary brings no browser. It finds local Chrome, starts a Browser Use Cloud browser, or drives one already
379
+ running, as the command line does. It reads the same keys from the environment of your process
380
+ (`OPENROUTER_API_KEY`, and `BROWSER_USE_API_KEY` for a cloud browser); `env` in `Fastbrowse.start` adds to them.
381
+
382
+ ```sh
383
+ npm install fastbrowse zod
384
+ export OPENROUTER_API_KEY=...
385
+ ```
386
+
387
+ ```ts
388
+ import { Fastbrowse } from 'fastbrowse';
389
+ import { z } from 'zod';
390
+
391
+ const Release = z.object({ package: z.string(), version: z.string() });
392
+
393
+ const fb = await Fastbrowse.start({ local: true });
394
+ try {
395
+ const result = await fb.run('Find the httpx package and report its name and latest released version.', {
396
+ start: 'https://pypi.org/',
397
+ output: Release,
398
+ limits: { maxDollars: 0.1 },
399
+ onEvent: event => {
400
+ if (event.type === 'step') console.error(event.step.operation, event.step.target);
401
+ },
402
+ signal: AbortSignal.timeout(120_000),
403
+ });
404
+ console.log(result.status, result.answer);
405
+ if (result.status === 'complete') console.log(result.output.package, result.output.version);
406
+ for (const evidence of result.evidence) console.log(` "${evidence.quote}" from ${evidence.url}`);
407
+ } finally {
408
+ await fb.close();
409
+ }
410
+ ```
411
+
412
+ `Fastbrowse.start` starts one fastbrowse process and `run` sends it a task. The process serves one run at a
413
+ time: a second `run` while one is active rejects with the busy error, and a program that wants runs side by
414
+ side starts more instances. `close()` shuts the process down and waits for it. The process also exits when
415
+ yours does, however yours ended.
416
+
417
+ `run` resolves with the result whatever status the run ended in, so `needs_login` or `stuck` is read from
418
+ `status` and is not an exception. It rejects when no run took place or none finished: with `RpcError` when the
419
+ server refuses the request before a browser opens (a bad option, a missing key, a busy server), with
420
+ `AbortError` when `signal` stopped the run, and with `ProcessExitedError` when the process is gone. A run that
421
+ fails after it has started resolves with status `error`.
422
+
423
+ The result is the `RunResult` the Python library returns, with the same statuses, citations and cost lines.
424
+ Its fields keep their Python names, such as `final_url`, since the types are generated from the Python models;
425
+ the options are camelCase. The structured data is in `data`, as in Python. The SDK adds
426
+ `output`: with a Zod (4.2 or newer) or ArkType schema, or a Valibot schema wrapped by
427
+ `@valibot/to-json-schema`, `output` is `data` after the schema's own validation and transforms, and has the
428
+ schema's output type. Data the schema refuses rejects with `OutputValidationError`, which carries the result.
429
+ A JSON Schema object is accepted too; `output` is then `data`, typed `unknown`.
430
+
431
+ What a run can fill today is narrower than what a schema can say. The server accepts nested objects, arrays,
432
+ `enum`, `const` and optional values, and refuses any other keyword by name before a browser opens. A run
433
+ fills a flat object of required string, number, integer and boolean fields, as the example has. With any
434
+ other field the run ends `unverified` with no data.
435
+
436
+ The callbacks that keep credentials and the last word in your code cross the process boundary:
437
+
438
+ ```ts
439
+ await fb.run('Log in as standard_user with the saved password and add the backpack to the cart.', {
440
+ start: 'https://www.saucedemo.com/',
441
+ authorization: { irreversibleActions: true },
442
+ secrets: {
443
+ refs: [{ name: 'password', origins: ['https://www.saucedemo.com'] }],
444
+ resolve: name => vault.get(name),
445
+ },
446
+ until: url => url.endsWith('/cart.html'),
447
+ });
448
+ ```
449
+
450
+ `secrets.resolve` is called each time a value is typed and never for an origin its ref does not cover, so the
451
+ value leaves your vault at that moment and a one-time code is fresh. `until` gets the address the run ended on,
452
+ and anything but `true` keeps the run from `complete`. `onFrame` receives JPEG frames of the active tab;
453
+ without it no frame is sent. A callback that throws ends the run with status `error`. The other options are
454
+ the command line's: `inputs`, `attachments` as bytes, `downloads`, `record`, and the browser choices
455
+ `chrome`, `cloudProfile`, `cdpUrl`, `cdpPort`, `attach`, `targetMatch` and `proxyCountry`, which
456
+ `Fastbrowse.start` also takes as defaults for every run. A run passes `null` for one of them to go without
457
+ that default.
458
+
459
+ There is no binary for Alpine or another musl system, and none for a platform outside the five. There
460
+ `Fastbrowse.start` rejects with an error that names the platform, and a binary of your own is named with
461
+ `binaryPath` or `FASTBROWSE_BINARY`. The macOS binaries carry an ad-hoc signature and are not notarized, which a Mac
462
+ that allows programs only by the team that signed them refuses. The Windows binary is not signed. Bun and Deno are
463
+ untested.
464
+
465
+ ## Use it from an MCP client
466
+
467
+ `fastbrowse-mcp` serves one `browse` tool over [MCP](https://modelcontextprotocol.io), so Claude Code, Claude
468
+ Desktop, Cursor or any other MCP client can hand it a task. It returns the answer, typed `fields` if asked for,
469
+ the quotes behind them, the status and what to do about it, and reports progress on every step.
470
+ The answer contains numbered Markdown links. MCP's `citations` list contains `quote` and `url` from the
471
+ run's evidence; it does not expose the Python `Citation` ids, requirement ids or deep-link fields.
472
+
473
+ ```sh
474
+ claude mcp add fastbrowse -e OPENROUTER_API_KEY=... -e BROWSER_USE_API_KEY=... \
475
+ -- uvx --from 'fastbrowse[mcp]' fastbrowse-mcp --max-dollars 0.25
476
+ ```
477
+
478
+ For a client configured by JSON, such as Claude Desktop:
479
+
480
+ ```json
481
+ {
482
+ "mcpServers": {
483
+ "fastbrowse": {
484
+ "command": "uvx",
485
+ "args": ["--from", "fastbrowse[mcp]", "fastbrowse-mcp"],
486
+ "env": { "OPENROUTER_API_KEY": "...", "BROWSER_USE_API_KEY": "..." }
487
+ }
488
+ }
489
+ }
490
+ ```
491
+
492
+ The server's flags decide what a calling model may do; a call can ask for less, never more.
493
+
494
+ | Flag | Effect |
495
+ |:--|:--|
496
+ | `--local`, `--headed`, `--profile DIR`, `--downloads DIR` | as for the CLI, fixed for every call |
497
+ | `--cloud-profile ID` | every call runs signed in as that cloud profile; a calling model cannot choose it |
498
+ | `--allow-authorize` | let a call pass `authorize` to go through irreversible actions; without it they always stop at `needs_confirmation` |
499
+ | `--secret NAME=ENV_VAR@ORIGIN` | typed when a call's start page is on `ORIGIN` (`https://*.site.com` covers its hosts); the model sees `NAME` only |
500
+ | `--bitwarden ITEM` | a vault login a call may name in `bitwarden` |
501
+ | `--max-steps N`, `--max-dollars N`, `--max-seconds N` | ceilings per call (defaults 60, $1.00, 600s) |
502
+ | `--max-concurrent N` | runs at once, default 1; more calls wait their turn |
503
+ | `--transport http`, `--host`, `--port` | streamable HTTP at `/mcp` instead of stdio |
504
+
505
+ Over HTTP, set `FASTBROWSE_MCP_TOKEN` in the environment or `.env` and every request but `/healthz` needs
506
+ `Authorization: Bearer <token>`. The server refuses to bind anything but loopback without one. A run takes
507
+ seconds to minutes, so raise the client's tool timeout if it has one (`MCP_TOOL_TIMEOUT` in Claude Code).
508
+
509
+ ## Safety model
510
+
511
+ - **Irreversible actions.** Jev judges clicks, including links, Enter presses and acceptance
512
+ of confirm, prompt or before-unload dialogs. Code-selected pagination is exempt, as are authorized
513
+ actions with sufficient confidence. A refusal appears as a failed step with a reason: an unauthorized,
514
+ confident action stops at `needs_confirmation`; an uncertain action goes to recovery. This is a classifier,
515
+ not a guarantee: a page can word a harmful control to look harmless.
516
+ - **Secrets.** Models see secret names only. A value is resolved at the moment of typing, only for
517
+ its declared origin, and redacted from everything the run returns. A password field is typed only
518
+ from a stored secret, never generated.
519
+ - **Page content is data.** Reader and verifier prompts say so, and completion is judged against quotes and page
520
+ state rather than the model's say-so.
521
+
522
+ ## Evals and development
523
+
524
+ The [eval workflow](docs/evals.md#workflow) has the run, publish and regeneration commands. Final page checks
525
+ require evidence read by the harness, and a pass also requires the task's expected ending. Published rows carry
526
+ build and task versions; the [site feed](docs/results/summary.json) is generated from them.
527
+
528
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and checks, [docs/design.md](docs/design.md) for the browser
529
+ layer, and [docs/jev.md](docs/jev.md) for the Jev assumptions checked against Typesafe's documentation.
530
+
531
+ ## Changelog
532
+
533
+ What changed in each release is in [CHANGELOG.md](CHANGELOG.md), and every
534
+ [GitHub release](https://github.com/agent-labs-dev/fastbrowse/releases) publishes that version's entry.
535
+
536
+ ## License
537
+
538
+ MIT, copyright Agent Labs. Adapted third-party code is credited in
539
+ [NOTICE](NOTICE).
@@ -0,0 +1,14 @@
1
+ ../../../bin/fastbrowse,sha256=kaVbuMx-AlSCiesFjTFd09Km4R8Lunnqm06vuR0Q4ow,336
2
+ ../../../bin/fastbrowse-mcp,sha256=ZXqxb9qRtFFkC1H9e4YqAdOBsFqTUwa9EZeg_4K3Ybo,343
3
+ _editable_impl_fastbrowse.pth,sha256=i4F7SPHtRO2rWCDD5u0Gnwv0cyF2GMJMQGgsS1QKCTI,43
4
+ fastbrowse-0.5.19.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
5
+ fastbrowse-0.5.19.dist-info/METADATA,sha256=l_hggNhjxHhCyunHbMTPUkdpyJ3tl79Y1AHPhtN7EKo,31033
6
+ fastbrowse-0.5.19.dist-info/RECORD,,
7
+ fastbrowse-0.5.19.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ fastbrowse-0.5.19.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
+ fastbrowse-0.5.19.dist-info/direct_url.json,sha256=p4VhBh51_mJBO89EvLp28QXawaajq5zi5eItI6PDXTg,85
10
+ fastbrowse-0.5.19.dist-info/entry_points.txt,sha256=SlQAK1_4_oywTAMckx8BYuCQJva0F-QuShQPcxtUm-g,95
11
+ fastbrowse-0.5.19.dist-info/licenses/LICENSE,sha256=gcUazG_j5a2blKPmfKqRKE98KOqX2kPbWY-EOtQZiNU,1067
12
+ fastbrowse-0.5.19.dist-info/licenses/NOTICE,sha256=qEO0Az_Tk98vRVL0xjnGYgMvexlMCA92lWl4JBwYRzg,1854
13
+ fastbrowse-0.5.19.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
14
+ fastbrowse-0.5.19.dist-info/uv_cache.json,sha256=BfXinzS9wdGxChuotSuvx_sXaCRCAfSorKZDrb80Aog,194
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1 @@
1
+ {"url":"file:///home/runner/work/fastbrowse/fastbrowse","dir_info":{"editable":true}}
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ fastbrowse = fastbrowse.cli:main
3
+ fastbrowse-mcp = fastbrowse.mcp_server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agent Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,33 @@
1
+ fastbrowse includes code adapted from the following MIT-licensed projects:
2
+
3
+ - browser-use/jev-ultrafast, Copyright (c) 2026 Browser Use
4
+ (src/fastbrowse/browser/page.py and session.py name what they port)
5
+ - browser-use/browser-harness, Copyright (c) 2026 Browser Use
6
+ - browser-use/browser-use, Copyright (c) 2024 Gregor Zunic
7
+
8
+ src/fastbrowse/browser/autoconsent/autoconsent.standalone.js is DuckDuckGo's autoconsent 16.40.0
9
+ (https://github.com/duckduckgo/autoconsent), unmodified and licensed under the Mozilla Public License 2.0,
10
+ whose text is in the LICENSE file beside it. Its source is available at that address.
11
+
12
+ The wordmark in assets/ is set in Inter, Copyright (c) 2016 The Inter Project Authors,
13
+ licensed under the SIL Open Font License 1.1 and outlined to paths.
14
+
15
+ Those portions are provided under the following terms:
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ of this software and associated documentation files (the "Software"), to deal
19
+ in the Software without restriction, including without limitation the rights
20
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ copies of the Software, and to permit persons to whom the Software is
22
+ furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ {"timestamp":{"secs_since_epoch":1791321492,"nanos_since_epoch":495924977},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1791321492,"nanos_since_epoch":496924919}}}