@fastbrowse/darwin-arm64 0.5.20 → 0.5.25

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 (27) hide show
  1. package/fastbrowse/_internal/base_library.zip +0 -0
  2. package/fastbrowse/_internal/fastbrowse/browser/capture.js +72 -1
  3. package/fastbrowse/_internal/fastbrowse/browser/snapshot.js +85 -7
  4. package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/METADATA +85 -16
  5. package/fastbrowse/_internal/fastbrowse-0.5.25.dist-info/RECORD +14 -0
  6. package/fastbrowse/_internal/fastbrowse-0.5.25.dist-info/uv_cache.json +1 -0
  7. package/fastbrowse/_internal/websockets/speedups.cpython-313-darwin.so +0 -0
  8. package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/METADATA +2 -2
  9. package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/RECORD +22 -22
  10. package/fastbrowse/fastbrowse +0 -0
  11. package/package.json +1 -1
  12. package/fastbrowse/_internal/fastbrowse-0.5.20.dist-info/RECORD +0 -14
  13. package/fastbrowse/_internal/fastbrowse-0.5.20.dist-info/uv_cache.json +0 -1
  14. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/INSTALLER +0 -0
  15. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/REQUESTED +0 -0
  16. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/WHEEL +0 -0
  17. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/direct_url.json +0 -0
  18. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/entry_points.txt +0 -0
  19. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/licenses/LICENSE +0 -0
  20. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/licenses/NOTICE +0 -0
  21. /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.25.dist-info}/uv_build.json +0 -0
  22. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/INSTALLER +0 -0
  23. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/REQUESTED +0 -0
  24. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/WHEEL +0 -0
  25. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/entry_points.txt +0 -0
  26. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/licenses/LICENSE +0 -0
  27. /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/top_level.txt +0 -0
@@ -16,6 +16,8 @@
16
16
  let controlObservations = 0;
17
17
  let imageObservations = 0;
18
18
  let imagesOmitted = false;
19
+ let linkObservations = 0;
20
+ let linksOmitted = false;
19
21
  let controlsOmitted = false;
20
22
  const imageSource = source => {
21
23
  if (!source) return source;
@@ -80,9 +82,46 @@
80
82
  }
81
83
  return found ? registry.hoverContexts.get(found).control : null;
82
84
  };
85
+ const listIds = new WeakMap();
86
+ const listFrameless = new WeakMap();
87
+ let nextListId = 0;
88
+ const listContext = nodes => {
89
+ const lists = new Set();
90
+ for (const node of nodes) {
91
+ let element = node.nodeType === 1 ? node : node.parentElement;
92
+ let list = null;
93
+ for (let parent = element; parent; parent = parent.parentElement) {
94
+ if (parent.matches('nav,[role="navigation"]')) return {};
95
+ if (parent.matches('ul,ol,[role="list"]')) list = parent;
96
+ }
97
+ if (!list) {
98
+ const nested = [...(element?.querySelectorAll('ul,ol,[role="list"]') ?? [])];
99
+ const outer = nested.filter(candidate => !nested.some(other => other !== candidate && other.contains(candidate)));
100
+ if (outer.length === 1) list = outer[0];
101
+ }
102
+ if (list) lists.add(list);
103
+ }
104
+ if (lists.size !== 1) return {};
105
+ const list = [...lists][0];
106
+ if (!listIds.has(list)) {
107
+ listIds.set(list, String(nextListId++));
108
+ // A frame the capture cannot open elsewhere on the page, such as an advertisement, cannot hold a member of a
109
+ // list whose element contains no frame; one inside the list, or behind a shadow root the list or anything in it
110
+ // holds, could.
111
+ const framed = e => e.tagName === 'IFRAME' || e.tagName === 'FRAME' || !!e.shadowRoot;
112
+ listFrameless.set(list, !framed(list) && ![...list.querySelectorAll('*')].some(framed));
113
+ }
114
+ const members = [...list.querySelectorAll('li,[role="listitem"]')].filter(member => {
115
+ for (let parent = member; parent && parent !== list; parent = parent.parentElement) {
116
+ if (hidden(parent)) return false;
117
+ }
118
+ return !!textOf(member);
119
+ });
120
+ return { list_id: listIds.get(list), list_count: members.length, list_frameless: listFrameless.get(list) };
121
+ };
83
122
  const push = (kind, text, extra = {}, nodes = []) => {
84
123
  if (text)
85
- blocks.push({ kind, text, heading_path: [...path], frame_path: framePath, source_path: sourcePath, control_context: controlOf(nodes), ...extra });
124
+ blocks.push({ kind, text, heading_path: [...path], frame_path: framePath, source_path: sourcePath, control_context: controlOf(nodes), ...listContext(nodes), ...extra });
86
125
  };
87
126
 
88
127
  // A cell can say what it says with an image or an icon alone: a flag marking the winner, a tick for "yes".
@@ -408,6 +447,38 @@
408
447
  src: imageSource(image.getAttribute('src') ?? image.getAttribute('srcset')) };
409
448
  push('observation', `Image element DOM metadata: ${JSON.stringify(metadata)}. Pixels are not described.`);
410
449
  }
450
+ // A link drawn only as an icon has no text, so its destination, the one thing it says, was nowhere a reader
451
+ // could quote: asked for a release's download URL beside an icon-only download link, a run read every other
452
+ // field of the row and then clicked the link again and again looking for the address. The row's text names
453
+ // which item the link belongs to, since the observation is not read where the link stands.
454
+ for (const link of [...root.querySelectorAll('a[href]')].filter(visible)) {
455
+ if (clean(link.innerText ?? '')) continue;
456
+ // An icon button written as `href="#"` or a script URL runs code here and leads nowhere to report. A bare
457
+ // `#` parses to an empty hash, so the raw attribute says it is a fragment link, not the parsed URL.
458
+ let target;
459
+ try {
460
+ target = new URL(link.getAttribute('href'), location.href);
461
+ } catch {
462
+ // One malformed address must not cost the page its whole capture.
463
+ continue;
464
+ }
465
+ const page = location.href.split('#')[0];
466
+ const fragment = link.getAttribute('href').trim().startsWith('#') || (target.hash && target.href.split('#')[0] === page);
467
+ if (target.protocol === 'javascript:' || fragment) continue;
468
+ if (linkObservations++ >= 32) {
469
+ if (!linksOmitted) push('observation', 'Additional link destinations omitted.');
470
+ linksOmitted = true;
471
+ break;
472
+ }
473
+ let row = link.parentElement;
474
+ while (row && row !== root && !clean(row.innerText ?? '')) row = row.parentElement;
475
+ const metadata = {
476
+ label: (nameOf(link) || drawn(link)).slice(0, 256) || null,
477
+ href: imageSource(link.href),
478
+ near: row ? textOf(row).replace(/\s+/g, ' ').slice(0, 256) || null : null,
479
+ };
480
+ push('observation', `Link element DOM metadata: ${JSON.stringify(metadata)}. The link has no text.`);
481
+ }
411
482
  const h1 = [...root.querySelectorAll('h1,[role="heading"][aria-level="1"]')].filter(visible);
412
483
  visibleH1 ||= h1.length > 0;
413
484
  if (root === document.body) {
@@ -41,6 +41,10 @@
41
41
  return true;
42
42
  };
43
43
  const registry = (window.__fastbrowse ||= { ids: new WeakMap(), nodes: new Map(), next: 1 });
44
+ // Keep the page's own timer overrides from holding a browser-side frame wait open.
45
+ registry.nativeSetTimeout ||= window.setTimeout.bind(window);
46
+ registry.nativeClearTimeout ||= window.clearTimeout.bind(window);
47
+ registry.nativeRequestAnimationFrame ||= window.requestAnimationFrame.bind(window);
44
48
  // What was offered to be acted on; `ids` also names hover targets and fingerprinted nodes.
45
49
  registry.controls ||= new WeakSet();
46
50
  // A visible loading indicator means the page is still fetching what it will draw, which network idle and a
@@ -180,17 +184,51 @@
180
184
 
181
185
  // Their text is code, not a name: Amazon nests a <style> inside a result card's link.
182
186
  const CODE = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE']);
183
- // Icon-only buttons hide their glyph from accessibility text, but the icon library still names it.
187
+ // Icon-only buttons hide their glyph from accessibility text, but the icon library still names it: in a class,
188
+ // a sprite reference, a data attribute, an svg title or a ligature. A gallery's trash control otherwise reads
189
+ // "button" beside an edit link that reads "link", and the choice cannot tell removing an image from editing it.
190
+ const ICON_CLASS =
191
+ /^(?:icon-tabler|tabler-icon|lucide|glyphicon|fa|fas|far|bi|mdi|icon|ti|la|ri|ion|feather)-([a-z][a-z0-9-]{0,47})$/;
192
+ // Sizes, weights and effects share an icon library's prefix without naming a glyph, as does the library itself.
193
+ const ICON_STYLE =
194
+ /^(?:icon|tabler|solid|regular|light|thin|duotone|brands|xs|sm|lg|xl|\d+x|fw|spin|pulse|border|inverse|stack.*|pull-.*|rotate-.*|flip-.*)$/;
195
+ const glyphName = text => {
196
+ const name = (text || '').trim().toLowerCase().replace(/^(?:icons?|ico|ic|svg|sprite)[-_]/, '');
197
+ return /^[a-z][a-z0-9_-]{0,47}$/.test(name) && !ICON_STYLE.test(name) ? name : '';
198
+ };
184
199
  const iconLabel = e => {
185
200
  const glyphs = new Set();
186
- for (const icon of e.querySelectorAll('svg')) {
187
- if (!showing(icon)) continue;
201
+ for (const icon of e.querySelectorAll('svg,i,span,[role="img"]')) {
202
+ if (!icon.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })) continue;
203
+ const named = [];
188
204
  for (const token of icon.classList) {
189
- const glyph = /^(?:icon-tabler|tabler-icon|lucide)-([a-z][a-z0-9-]{0,47})$/.exec(token);
190
- if (glyph && !/^(?:icon|xs|sm|lg|xl|\d+x|spin|pulse)$/.test(glyph[1])) glyphs.add(glyph[1]);
205
+ const glyph = ICON_CLASS.exec(token);
206
+ if (glyph && !ICON_STYLE.test(glyph[1])) named.push(glyph[1]);
207
+ }
208
+ const use = icon.localName === 'svg' && icon.querySelector('use');
209
+ const sprite = use && (use.getAttribute('href') || use.getAttribute('xlink:href') || '');
210
+ if (sprite) named.push(glyphName(sprite.split('#').pop().split('/').pop().replace(/\.svg$/, '')));
211
+ named.push(glyphName(icon.getAttribute('data-icon')));
212
+ // A ligature font draws the glyph its text names, and the icon is hidden so readers skip that word.
213
+ if ([...icon.classList].some(token => /^material-(?:icons|symbols)/.test(token))) {
214
+ named.push(glyphName(icon.textContent.replace(/\s+/g, '_')));
191
215
  }
216
+ const title =
217
+ icon.getAttribute('aria-label') || (icon.localName === 'svg' ? icon.querySelector(':scope > title')?.textContent : '');
218
+ if (title?.trim()) glyphs.add(title.replace(/\s+/g, ' ').trim());
219
+ else for (const name of named) if (name) glyphs.add(`${name.replace(/[-_]/g, ' ')} icon`);
192
220
  }
193
- return glyphs.size === 1 ? `${[...glyphs][0].replace(/-/g, ' ')} icon` : '';
221
+ return glyphs.size === 1 ? [...glyphs][0] : '';
222
+ };
223
+ // An accessible description is what an unnamed control has left that says what it does.
224
+ const describedBy = e => {
225
+ const root = typeof e.getRootNode === 'function' ? e.getRootNode() : document;
226
+ return (e.getAttribute('aria-describedby') || '')
227
+ .split(/\s+/)
228
+ .map(id => (id && (root.getElementById ? root.getElementById(id) : document.getElementById(id))?.textContent) || '')
229
+ .map(text => text.replace(/\s+/g, ' ').trim())
230
+ .filter(Boolean)
231
+ .join(' ');
194
232
  };
195
233
  const labelOf = (e, seen = new Set()) => {
196
234
  if (!e || seen.has(e)) return '';
@@ -373,6 +411,8 @@
373
411
  };
374
412
 
375
413
  const controls = [];
414
+ // Controls with no accessible name of their own, whose label is an icon's or only their role.
415
+ const unnamed = new Set();
376
416
  const fieldScope = e => {
377
417
  if (!e.matches('input,textarea,select')) return null;
378
418
  const form = e.form || e.closest('form,fieldset,[role="form"]');
@@ -412,10 +452,12 @@
412
452
 
413
453
  const id = identity(e);
414
454
  registry.controls.add(e);
455
+ const named = labelOf(e);
456
+ if (!named) unnamed.add(id);
415
457
  const base = {
416
458
  id,
417
459
  role: rname,
418
- label: labelOf(e) || (rname === 'button' ? iconLabel(e) : '') || rname,
460
+ label: named || iconLabel(e) || describedBy(e) || rname,
419
461
  offscreen: y < 0 || y >= innerHeight,
420
462
  distance: y < 0 || y >= innerHeight ? 1 + Math.abs(y - innerHeight / 2) : 0,
421
463
  sensitive: secret(source),
@@ -432,6 +474,8 @@
432
474
  base.href = u.origin === location.origin ? u.pathname + u.search : u.host + u.pathname;
433
475
  // The page's own word for "the next page of this list", which survives an icon label or another language.
434
476
  if (e.rel && e.rel.split(/\s+/).includes('next')) base.next_page = true;
477
+ // The page's own word that this link saves a file, which a task ruling out downloads has to see before a click.
478
+ if (e.hasAttribute('download')) base.download = true;
435
479
  }
436
480
  for (const key of ['checked', 'selected', 'expanded']) {
437
481
  const value = e.getAttribute('aria-' + key);
@@ -705,6 +749,40 @@
705
749
  }
706
750
  }
707
751
 
752
+ // An unnamed control the page draws once per card, a trash icon beside each image, is a twin only while another
753
+ // card shows the same icon: one left alone, or drawn by another icon set, would name no card and leave the choice
754
+ // guessing which image it removes. It is named by the card that holds it, or failing one by the nearest ancestor
755
+ // that says anything besides it.
756
+ // A card is an item, a row, or an element the page repeats beside itself with the same tag and class.
757
+ const isCard = e =>
758
+ e.matches('article,li,[role="listitem"],tr,[role="row"]') ||
759
+ (!!e.getAttribute('class') &&
760
+ [...(e.parentElement?.children || [])].some(
761
+ s => s !== e && s.localName === e.localName && s.getAttribute('class') === e.getAttribute('class'),
762
+ ));
763
+ const nearestName = (element, label) => {
764
+ let first = '';
765
+ for (let e = element.parentElement, path = element; e && e !== e.ownerDocument.body; path = e, e = e.parentElement) {
766
+ first ||= nameOf(e, element, label, true);
767
+ if (!isCard(e)) continue;
768
+ // An action row's own caption ("Actions") is the first text the climb meets and names no item, so the card
769
+ // is named by what it holds outside the row that holds the control.
770
+ const aria = (e.getAttribute('aria-label') || '').trim();
771
+ const outside = [...e.children]
772
+ .filter(child => child !== path)
773
+ .map(child => firstLine(child.innerText))
774
+ .find(line => line && line !== label && !repeated.has(line));
775
+ return excerpt((aria !== label && aria) || outside || first, CONTROL_CONTEXT_CHARS);
776
+ }
777
+ return excerpt(first, CONTROL_CONTEXT_CHARS);
778
+ };
779
+ for (const c of controls) {
780
+ if (!c.context && unnamed.has(c.id)) {
781
+ const context = nearestName(registry.nodes.get(c.id), c.label);
782
+ if (context) c.context = context;
783
+ }
784
+ }
785
+
708
786
  // A revealed caption needs its actual container association, not merely the most recent action.
709
787
  registry.hoverContexts = new WeakMap();
710
788
  for (const c of controls) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastbrowse
3
- Version: 0.5.20
3
+ Version: 0.5.25
4
4
  Summary: Browser automation with cited answers, structured output and code-controlled credentials and authorization.
5
5
  Project-URL: Homepage, https://github.com/agent-labs-dev/fastbrowse
6
6
  Project-URL: Repository, https://github.com/agent-labs-dev/fastbrowse
@@ -9,6 +9,7 @@ Author: Agent Labs
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
11
11
  License-File: NOTICE
12
+ Classifier: Development Status :: 4 - Beta
12
13
  Requires-Python: >=3.13
13
14
  Requires-Dist: cdp-use==1.4.5
14
15
  Requires-Dist: httpx[http2]<1,>=0.28
@@ -55,10 +56,20 @@ Recorded on local Chrome: 13 steps, 20.9s and $0.0034 in model calls. Playback i
55
56
 
56
57
  ### Against Browser Use agents
57
58
 
58
- The [benchmark page](https://fastbrowse.ai/benchmarks) reports task success, speed and cost,
59
- with methods and run summaries.
59
+ Benchmarking compares exactly three things:
60
60
 
61
- Rows are comparable only at matching task versions. See [eval results and workflow](docs/evals.md).
61
+ | | Comparison | Arms | Recorded |
62
+ |:--|:--|:--|:--|
63
+ | C1 | agent against agent | fastbrowse against the hosted Browser Use agent | 40 of 42 passed against 14 of 42, median 17.0s against 27.5s, $0.011 against $0.400 a task |
64
+ | C2 | backend against backend | fastbrowse on its own browser pool against fastbrowse on a Browser Use Cloud browser | not yet measured |
65
+ | C3 | fastbrowse and its backend against [Browser Use Ultrafast](https://github.com/browser-use/jev-ultrafast) | fastbrowse on its own browser pool against `jev-ultrafast`, 6 tasks | not yet measured |
66
+
67
+ C1's figures are the recorded head-to-head on 14 answer tasks, three passes each, on identical cloud browsers
68
+ under identical limits. [Benchmark results](docs/benchmarks.md) gives each comparison's sources, the internal
69
+ matched-task campaigns behind C1, the browser cost basis and the limitations. The
70
+ [benchmark page](https://fastbrowse.ai/benchmarks) renders only figures the maintainer approved. Rows are
71
+ comparable only at matching task versions; the upstream suites and the Codex with Cua Driver arm are diagnostics,
72
+ described in [eval results and workflow](docs/evals.md).
62
73
 
63
74
  ### Why fastbrowse, against each kind of agent
64
75
 
@@ -97,6 +108,10 @@ automatic selection; see [provider routing](docs/jev.md#provider-failover). The
97
108
  PATH, and `uv add fastbrowse` puts it in a project. Service keys can live in a `.env` file in the working directory;
98
109
  [`.env.example`](.env.example) shows the settings. Values named by `--secret` must be in the process environment.
99
110
 
111
+ On Arch Linux, the [`fastbrowse` AUR package](https://aur.archlinux.org/packages/fastbrowse) puts `fastbrowse` and
112
+ `fastbrowse-mcp` on your PATH from the same PyPI release: `paru -S fastbrowse` with an AUR helper, or `makepkg -si`
113
+ in [`packaging/aur`](packaging/aur).
114
+
100
115
  To work on fastbrowse itself:
101
116
 
102
117
  ```sh
@@ -121,15 +136,16 @@ the quotes behind the answer, and cost by component.
121
136
  | `--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` |
122
137
  | `--attach` | with `--cdp-url` or `--cdp-port`, drive a window already open instead of opening a tab, and leave it open after the run |
123
138
  | `--target-match TEXT` | attach to the first window whose title or URL contains `TEXT` (implies `--attach`) |
139
+ | `--foreground` | bring the run's tab and its browser window to the front, to watch it. Without it a run in a visible window works behind the window's current tab; see [working in the background](#working-in-the-background) for what Chrome still brings forward |
124
140
  | `--proxy-country CC` | browse from that country (Browser Use's codes: `uk`, `de`, ...; default `us`), so a shop shows its local delivery and prices |
125
141
  | `--authorize` | allow submit, pay, delete and send; without it the run stops at `needs_confirmation` first |
126
- | `--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` |
142
+ | `--secret NAME=ENV_VAR[@ORIGIN]` | let the agent type `$ENV_VAR` on the declared origin (`https://*.site.com` covers its hosts), or the `--start` origin if omitted; models only see `NAME`. An explicit origin needs no `--start` |
127
143
  | `--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 |
128
144
  | `--max-steps N`, `--max-dollars N` | optionally bound steps and model spend; defaults are unlimited. Cloud browser charges are added when it stops |
129
145
  | `--downloads DIR` | keep downloaded files |
130
146
  | `--json` | full result instead of the answer |
131
147
  | `--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 |
132
- | `--cursor` | draw the agent's cursor over a visible Chrome (`--headed`, or one you attach to) with [Cua Driver](https://github.com/trycua/cua), so you can watch where it acts. Off by default. It needs `cua-driver` on `PATH` and an X11 display on Linux, and does nothing without them |
148
+ | `--cursor` | draw the agent's cursor over a visible Chrome (`--headed`, or one you attach to) with [Cua Driver](https://github.com/trycua/cua), so you can watch where it acts. Off by default. It needs `cua-driver` on `PATH` and an X11 display on Linux, and does nothing without them. It brings the run's tab to the front as `--foreground` does, since the cursor lies over the window's front tab |
133
149
 
134
150
  ```sh
135
151
  export SAUCE_PASSWORD=secret_sauce
@@ -147,6 +163,9 @@ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
147
163
  --start https://www.amazon.com/ --profile ~/.fastbrowse/amazon --headed
148
164
  ```
149
165
 
166
+ `--headed` is not needed once the profile is signed in: without it the run is headless and opens no window. A
167
+ headed run opens one window when Chrome starts and then works in a tab behind it.
168
+
150
169
  On a cloud browser the profile lives on the [Browser Use Cloud](https://cloud.browser-use.com) account
151
170
  rather than on disk, and `--cloud-profile ID` runs as it. Whoever signed that profile in did so once, in a
152
171
  browser of their own; the run inherits the cookies and no model is shown a credential:
@@ -189,6 +208,26 @@ the text; `--attach` alone takes the first window, skipping DevTools. Popups the
189
208
  Windows that nothing opened, as an Electron app's main process opens them, join only with `--target-match`,
190
209
  because in a browser such a window could equally be a tab you opened yourself.
191
210
 
211
+ ### Working in the background
212
+
213
+ A run in a visible window, whether `--headed` or a browser you handed over, opens its tab behind the window's
214
+ current tab and renders it there under focus emulation. Nothing the run sends raises or focuses the window. `--foreground` brings the tab and its window to the front instead, to watch the run. Three things
215
+ are Chrome's own and still bring a window forward: a headed Chrome showing its window as it starts, a popup a
216
+ page opens, and, with `--attach`, a click in a window's front tab.
217
+
218
+ For a signed-in browser that stays open and asks for nothing, start a Chrome of its own once, sign in, and leave
219
+ it running. Each run then opens and closes a background tab in it:
220
+
221
+ ```sh
222
+ google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.fastbrowse/chrome"
223
+ uv run fastbrowse "Report my open orders." --start https://www.amazon.com/ --cdp-port 9222
224
+ ```
225
+
226
+ Chrome ignores `--remote-debugging-port` on its default data directory, so the directory of its own is required.
227
+ Your everyday Chrome can instead offer an endpoint from `chrome://inspect/#remote-debugging`, but it then stops
228
+ every connection on an "Allow remote debugging?" prompt that takes focus and waits for a click, once per run.
229
+ Any program on the machine can drive a browser that listens on a debugging port.
230
+
192
231
  A DevTools port gives any program on the machine full control of the app, including its signed-in sessions.
193
232
  Close the app, or restart it without the flag, when you are done.
194
233
 
@@ -200,10 +239,23 @@ Override with `FASTBROWSE_LLM_MODEL` (every purpose), `FASTBROWSE_LLM_MODEL_<PUR
200
239
  `READ`, `FIELD_TEXT`, `SHORTCUT`, `RECOVER`, `COMPOSE`, `VERIFY`) and `FASTBROWSE_LLM_REASONING`
201
240
  (`low`, `medium`, `high`).
202
241
 
242
+ `FASTBROWSE_LLM_BASE_URL` sends every LLM call to another OpenAI-compatible chat completions endpoint instead
243
+ of OpenRouter and the gateway, such as a local vLLM server at `http://127.0.0.1:8000/v1`. Name the model it
244
+ serves with `FASTBROWSE_LLM_MODEL`; a run refuses to start without one, and a blank value counts as unset.
245
+ OpenRouter's `reasoning` field is sent to that endpoint only when `FASTBROWSE_LLM_REASONING` is set, since a
246
+ strict server refuses a field it does not know. `FASTBROWSE_LLM_API_KEY` is its bearer
247
+ token, sent only when set, and that endpoint never receives `OPENROUTER_API_KEY` or `AI_GATEWAY_API_KEY`. Two
248
+ settings shape its requests: `FASTBROWSE_LLM_STRICT=0` sends each schema as the model defines it rather than
249
+ in strict form, for a server whose structured output breaks under strict mode, and `FASTBROWSE_LLM_EXTRA_BODY`
250
+ is a JSON object added to every request, such as `{"chat_template_kwargs":{"enable_thinking":false}}`. It may
251
+ not set a field fastbrowse sends itself (`model`, `messages`, `max_tokens`, `response_format`, `provider`,
252
+ `reasoning`). Jev still needs its own key. An endpoint that reports no price leaves those calls' cost unknown
253
+ rather than zero, so `--max-dollars` stops the run after the first such call.
254
+
203
255
  ### Results
204
256
 
205
257
  The exit code identifies the run status. Configuration refusals exit 1; invalid command syntax exits 2.
206
- Programs that only check zero versus nonzero continue to work.
258
+ Only `complete` exits 0, so a program can also check zero versus nonzero.
207
259
 
208
260
  | Status | Exit | Meaning |
209
261
  |:--|--:|:--|
@@ -225,6 +277,7 @@ optional object, so callers can identify the affected limit without parsing an e
225
277
 
226
278
  A bot check can block a cloud browser before any task steps execute. Retry with an attached local browser
227
279
  or choose another source for the research. Credentials do not resolve a CAPTCHA.
280
+ The `blocked` result's `evidence` quotes the opening text of the challenge page, with its address.
228
281
 
229
282
  If a model completion omits its cost, fastbrowse looks up the matching OpenRouter or Vercel generation
230
283
  receipt. If that receipt is still unavailable, a run with a dollar cap stops with `budget_exceeded`;
@@ -235,6 +288,9 @@ A browser command that remains unanswered for 120 seconds returns `unavailable`,
235
288
  still answers health checks. Step and dollar limits do not bound total elapsed time. Set `Limits(max_seconds=...)` for a run deadline.
236
289
  When investigating a stalled run, enable trace logging and retain the last event alongside the result.
237
290
 
291
+ [Troubleshooting](docs/troubleshooting.md) takes each status a run can stop with: what it means, its common
292
+ causes and the next step.
293
+
238
294
  ## How it works
239
295
 
240
296
  <picture>
@@ -256,6 +312,7 @@ More in [docs/design.md](docs/design.md).
256
312
  | Signing in | yes | password fields excluded | `--secret` or a Bitwarden vault item; models see names only |
257
313
  | Irreversible actions | explicit task instructions in our fixture tests | no authorization parameter in Agent | classifier plus a code-enforced authorization gate |
258
314
  | Browser | cloud | Chrome through Browser Harness; cloud in these evals | cloud by default, or local Chrome with `--local` |
315
+ | Benchmarked in | C1 | C3 | C1 on a cloud browser; C2 and C3 on its own browser pool |
259
316
 
260
317
  Browser Use Ultrafast (`jev-ultrafast`) is Browser Use's navigation agent, separate from its hosted
261
318
  LLM agent; fastbrowse shares its core techniques. Its column describes
@@ -299,7 +356,8 @@ asyncio.run(main())
299
356
  the run opens its own tab and closes the tabs it owns. It leaves the browser and pre-existing tabs open;
300
357
  cookies and other changes made by the task can persist. `cdp_port=` finds the same browser from its DevTools
301
358
  port on `127.0.0.1`. `attach=True` and `target_match=` drive a window already open and leave it open, as
302
- `--attach` and `--target-match` do. Pass `browser_api_key=` to start a cloud browser;
359
+ `--attach` and `--target-match` do, and `foreground=True` brings the run's tab to the front as `--foreground`
360
+ does. Pass `browser_api_key=` to start a cloud browser;
303
361
  with neither argument, it runs local Chrome. Passing both is an error. `cloud_extensions=[...]` loads up to
304
362
  three of your account's ready Browser Use Cloud extensions, by ID, into that cloud browser; it is an error with
305
363
  local or attached browsers.
@@ -318,6 +376,17 @@ async def main() -> None:
318
376
 
319
377
  `resolve_cdp_port(port)` returns the `ws://` URL behind a DevTools port, for a caller that passes `cdp_url=`.
320
378
 
379
+ `run_task` and `connect_cdp` take two access controls that the command line, the MCP server and the JavaScript
380
+ SDK do not offer. `allowed_origins=["https://app.example.com"]` limits the documents a run may read and control
381
+ to those exact origins: scheme, host and optional port, with no wildcard or path. A navigation, redirect, frame
382
+ or popup outside the list is refused before it is sent, and a window already open outside it is never read.
383
+ A sign-in that redirects through another origin needs that origin listed too. The list scopes documents, not
384
+ the network: images, scripts and requests a listed page makes to other hosts still load. A scoped run delivers
385
+ no live frames or screenshots, and combining it with `record=` is an error. `check_access=` is an async callback
386
+ awaited before the browser starts and before every read, navigation and action. When it raises, `connect_cdp`
387
+ raises a `BrowserError` and `run_task` returns a `RunResult` with status `error`; either way the error names only
388
+ the exception's type, never its text.
389
+
321
390
  `RunResult.citations` is a tuple of `Citation` objects, also importable from `fastbrowse`. Each has `id`
322
391
  (the number in the answer), `text` (the Notes fact), `requirement_id` (or `None`), `url`, `quote` and
323
392
  `deep_link`. Each claim in `result.answer` carries numbered Markdown links to its supporting facts.
@@ -357,7 +426,7 @@ package is a TypeScript SDK with no dependencies. With it npm installs one of fi
357
426
  as a native binary, the one for your platform: `@fastbrowse/darwin-arm64`, `darwin-x64`, `linux-arm64`,
358
427
  `linux-x64` or `win32-x64`. No install script runs and nothing is downloaded on first use, so it installs under
359
428
  pnpm and bun with scripts blocked, and it starts offline. It needs Node.js 20 or newer. The npm packages carry
360
- the PyPI package's version and are published by the same tag, starting with the release after 0.5.18.
429
+ the PyPI package's version and are published by the same tag.
361
430
 
362
431
  The binary brings no browser. It finds local Chrome, starts a Browser Use Cloud browser, or drives one already
363
432
  running, as the command line does. It reads the same keys from the environment of your process
@@ -412,7 +481,7 @@ the options are camelCase. The structured data is in `data`, as in Python. The S
412
481
  schema's output type. Data the schema refuses rejects with `OutputValidationError`, which carries the result.
413
482
  A JSON Schema object is accepted too; `output` is then `data`, typed `unknown`.
414
483
 
415
- What a run can fill today is narrower than what a schema can say. The server accepts nested objects, arrays,
484
+ What a run can fill is narrower than what a schema can say. The server accepts nested objects, arrays,
416
485
  `enum`, `const` and optional values, and refuses any other keyword by name before a browser opens. A run
417
486
  fills a flat object of required string, number, integer and boolean fields, as the example has. With any
418
487
  other field the run ends `unverified` with no data.
@@ -433,11 +502,11 @@ await fb.run('Log in as standard_user with the saved password and add the backpa
433
502
 
434
503
  `secrets.resolve` is called each time a value is typed and never for an origin its ref does not cover, so the
435
504
  value leaves your vault at that moment and a one-time code is fresh. `until` gets the address the run ended on,
436
- and anything but `true` keeps the run from `complete`. `onFrame` receives JPEG frames of the active tab;
437
- without it no frame is sent. A callback that throws ends the run with status `error`. The other options are
438
- the command line's: `inputs`, `attachments` as bytes, `downloads`, `record`, and the browser choices
439
- `chrome`, `cloudProfile`, `cdpUrl`, `cdpPort`, `attach`, `targetMatch` and `proxyCountry`, which
440
- `Fastbrowse.start` also takes as defaults for every run. A run passes `null` for one of them to go without
505
+ and anything but `true` keeps the run from `complete`. `onFrame` receives JPEG frames of the active tab; without
506
+ it no frame is sent. A callback that throws ends the run with status `error`. The other options are `run_task`'s:
507
+ `inputs`, `attachments` as bytes, `downloads`, `record`, and the browser choices `chrome`, `cloudProfile`,
508
+ `cdpUrl`, `cdpPort`, `attach`, `targetMatch`, `foreground`, `proxyCountry`, `viewport` and `cloudAllowResizing`,
509
+ which `Fastbrowse.start` also takes as defaults for every run. A run passes `null` for one of them to go without
441
510
  that default.
442
511
 
443
512
  There is no binary for Alpine or another musl system, and none for a platform outside the five. There
@@ -489,7 +558,7 @@ The server's flags decide what a calling model may do; a call can ask for less,
489
558
 
490
559
  | Flag | Effect |
491
560
  |:--|:--|
492
- | `--local`, `--headed`, `--profile DIR`, `--downloads DIR`, `--cursor` | as for the CLI, fixed for every call |
561
+ | `--local`, `--headed`, `--profile DIR`, `--foreground`, `--downloads DIR`, `--cursor` | as for the CLI, fixed for every call |
493
562
  | `--cloud-profile ID` | every call runs signed in as that cloud profile; a calling model cannot choose it |
494
563
  | `--allow-authorize` | let a call pass `authorize` to go through irreversible actions; without it they always stop at `needs_confirmation` |
495
564
  | `--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 |
@@ -0,0 +1,14 @@
1
+ ../../../bin/fastbrowse,sha256=k_VHUJGUyyMLHeLCiSHLkBTLNP1m86q6e4YkyAHdlZM,337
2
+ ../../../bin/fastbrowse-mcp,sha256=1CAFjtHrbGFIoAzljudiwUZ-Qrz8WFq5CLTPaDEvGRE,344
3
+ _editable_impl_fastbrowse.pth,sha256=Ft6bxdkTegROr5p9hZSxsnUSfEHrZYCfXWKjtpA4_DU,44
4
+ fastbrowse-0.5.25.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
5
+ fastbrowse-0.5.25.dist-info/METADATA,sha256=PviO3CI9ERI53eHfNlnFhSL0Sn9CNuF-Oe-vEJqhBHg,37292
6
+ fastbrowse-0.5.25.dist-info/RECORD,,
7
+ fastbrowse-0.5.25.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ fastbrowse-0.5.25.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
+ fastbrowse-0.5.25.dist-info/direct_url.json,sha256=cZGZY9ovdib3X-RhjHdlyO_0_EMUe0tW6AXsSyMbNE4,86
10
+ fastbrowse-0.5.25.dist-info/entry_points.txt,sha256=SlQAK1_4_oywTAMckx8BYuCQJva0F-QuShQPcxtUm-g,95
11
+ fastbrowse-0.5.25.dist-info/licenses/LICENSE,sha256=gcUazG_j5a2blKPmfKqRKE98KOqX2kPbWY-EOtQZiNU,1067
12
+ fastbrowse-0.5.25.dist-info/licenses/NOTICE,sha256=qEO0Az_Tk98vRVL0xjnGYgMvexlMCA92lWl4JBwYRzg,1854
13
+ fastbrowse-0.5.25.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
14
+ fastbrowse-0.5.25.dist-info/uv_cache.json,sha256=9V7wf1peUAIFzJ6pXv0Se3RK71iedjoSz-A-l4WlvPQ,190
@@ -0,0 +1 @@
1
+ {"timestamp":{"secs_since_epoch":1792479829,"nanos_since_epoch":1789416},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1792479829,"nanos_since_epoch":3558374}}}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: websockets
3
- Version: 17.1
3
+ Version: 17.2
4
4
  Summary: An implementation of the WebSocket Protocol (RFC 6455 & 7692)
5
5
  Author-email: Aymeric Augustin <aymeric.augustin@m4x.org>
6
6
  License-Expression: BSD-3-Clause
@@ -143,7 +143,7 @@ Why shouldn't I use ``websockets``?
143
143
  that build on top of ``websockets`` to support WebSocket connections, like
144
144
  uvicorn_ or Sanic_.
145
145
 
146
- .. _uvicorn: https://www.uvicorn.org/
146
+ .. _uvicorn: https://uvicorn.dev/
147
147
  .. _Sanic: https://sanic.dev/en/
148
148
 
149
149
  What else?
@@ -1,20 +1,20 @@
1
1
  ../../../bin/websockets,sha256=DVn8K_GAmc_NaykY80NWs5Rqx1AeOpPtmypr9Uuy4GQ,337
2
- websockets-17.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
3
- websockets-17.1.dist-info/METADATA,sha256=F8cPnGo0ogTyqWnHPkfrp3KJ7yopkUIIqxCuSZc-Gto,6340
4
- websockets-17.1.dist-info/RECORD,,
5
- websockets-17.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- websockets-17.1.dist-info/WHEEL,sha256=W9vNsxHtuPjBar6tWy08iHBg_jNK-VgyOpoyoNPwHv0,136
7
- websockets-17.1.dist-info/entry_points.txt,sha256=Dnhn4dm5EsI4ZMAsHldGF6CwBXZrGXnR7cnK2-XR7zY,51
8
- websockets-17.1.dist-info/licenses/LICENSE,sha256=PWoMBQ2L7FL6utUC5F-yW9ArytvXDeo01Ee2oP9Obag,1514
9
- websockets-17.1.dist-info/top_level.txt,sha256=CMpdKklxKsvZgCgyltxUWOHibZXZ1uYIVpca9xsQ8Hk,11
2
+ websockets-17.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
3
+ websockets-17.2.dist-info/METADATA,sha256=DV8SedCV57qE7yFhSvCCLcUAzLLlG3w6Ge2nFZstreQ,6336
4
+ websockets-17.2.dist-info/RECORD,,
5
+ websockets-17.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ websockets-17.2.dist-info/WHEEL,sha256=W9vNsxHtuPjBar6tWy08iHBg_jNK-VgyOpoyoNPwHv0,136
7
+ websockets-17.2.dist-info/entry_points.txt,sha256=Dnhn4dm5EsI4ZMAsHldGF6CwBXZrGXnR7cnK2-XR7zY,51
8
+ websockets-17.2.dist-info/licenses/LICENSE,sha256=PWoMBQ2L7FL6utUC5F-yW9ArytvXDeo01Ee2oP9Obag,1514
9
+ websockets-17.2.dist-info/top_level.txt,sha256=CMpdKklxKsvZgCgyltxUWOHibZXZ1uYIVpca9xsQ8Hk,11
10
10
  websockets/__init__.py,sha256=zmBCJj7EIa6ZJEQDp4dIdznfzAJUA8dW41-j7Em-QDQ,7450
11
11
  websockets/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
12
12
  websockets/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- websockets/asyncio/client.py,sha256=ds1rHzlGQyUoGSVSm0Ztc2QL5cLOiUK9957bO0K6-OY,30554
13
+ websockets/asyncio/client.py,sha256=WBGS7GrjeBrElotuw45kUC_xrP449yuip9TCBm1yFrE,30666
14
14
  websockets/asyncio/connection.py,sha256=8utq1WNx-rM4OaetJpTktWEL6oOb-Srh925tCmPx40Y,49740
15
15
  websockets/asyncio/messages.py,sha256=vBPd93IuL3LE0OSxeaICIjzvyoht53kmh-_Zho-bGzU,11096
16
16
  websockets/asyncio/router.py,sha256=JFNUNILkAB5P9ft6PLogNaZq6p92Wz19vIlxfx1DTlg,7783
17
- websockets/asyncio/server.py,sha256=34Rtp3g3nAWLDaBScxubofczkNoDIMMzMPAwdLFKxvw,34126
17
+ websockets/asyncio/server.py,sha256=9aq5Fv-b9gTHfIY1h56A4iNIm6Ber5IQAkcdVCExY3M,34434
18
18
  websockets/auth.py,sha256=U_Jwmn59ZRQ6EecpOvMizQCG_ZbAvgUf1ik7haZRC3c,568
19
19
  websockets/cli.py,sha256=fYf0YqrWIx_iJDA-RWdczh5vjO9p6ZxZvmE761bUQg0,6892
20
20
  websockets/client.py,sha256=6vTmYiCpIpe8eUrcmElOY-oKriH6OUxyp-hGpTWvPMc,16140
@@ -23,8 +23,8 @@ websockets/datastructures.py,sha256=XGTvvgFdZSevNV_T6-Lo-Dxm2VHxJcgKl4l1DNQ6fU8,
23
23
  websockets/exceptions.py,sha256=tp0_EyqQfd7CYNOHmxAieOH8Ed9PSQFt18e-CuReTOY,14308
24
24
  websockets/extensions/__init__.py,sha256=QkZsxaJVllVSp1uhdD5uPGibdbx_091GrVVfS5LXcpw,98
25
25
  websockets/extensions/base.py,sha256=JNfyk543C7VuPH0QOobiqKoGrzjJILje6sz5ILvOPl4,2903
26
- websockets/extensions/permessage_deflate.py,sha256=IAXdCGY60zfdjatKd6fDfTMizOeQYsnh0zBtL9vPuzA,25840
27
- websockets/frames.py,sha256=Twiqn_buvQiblERugcT-MDmnPZ7kt-FMXESe2DhOT9o,16709
26
+ websockets/extensions/permessage_deflate.py,sha256=2X1f3T5HuOQXbYnmHX-Mk3q1t21n-U_FhfjS6oHBibw,26569
27
+ websockets/frames.py,sha256=YssQbowj263xm7XnQAahjcRlA4Hdy1WSBYthsfx8EPs,17486
28
28
  websockets/headers.py,sha256=yQnPljVZwV1_V-pOSRKNLG_u827wFC1h72cciojcQ8M,16046
29
29
  websockets/http11.py,sha256=tLVuNvNADwt8d1ui5KMFKZCZMTtJ6CqXZveogLaPbf0,18245
30
30
  websockets/imports.py,sha256=T_B9TUmHoceKMQ-PNphdQQAH2XdxAxwSQNeQEgqILkE,2795
@@ -34,32 +34,32 @@ websockets/legacy/client.py,sha256=5oJurUczPddXi8Rde5fA2hD3V4pnv__Z64pRRXP6HBs,2
34
34
  websockets/legacy/exceptions.py,sha256=ViEjpoT09fzx_Zqf0aNGDVtRDNjXaOw0gdCta3LkjFc,1924
35
35
  websockets/legacy/framing.py,sha256=b0wMx83XTIXVZoO60pl3AibKHbQlDb7rBAyhFEkaMCk,6374
36
36
  websockets/legacy/handshake.py,sha256=2Nzr5AN2xvDC5EdNP-kB3lOcrAaUNlYuj_-hr_jv7pM,5285
37
- websockets/legacy/http.py,sha256=cOCQmDWhIKQmm8UWGXPW7CDZg03wjogCsb0LP9oetNQ,7061
37
+ websockets/legacy/http.py,sha256=5Ru5qH1dwJ9EDAh5-CCJaI7KAhrk5j7fVU_63y5KfeI,7151
38
38
  websockets/legacy/protocol.py,sha256=3MTh1-0ktWStRCX00lLV69gLzF_9gAn8YLFE_bZkzYw,63645
39
39
  websockets/legacy/server.py,sha256=hSk4nFTsI3eMRbu5JcI4gXei9Ubw07RSrSjtL4eM2gc,44835
40
40
  websockets/protocol.py,sha256=3HuCBMLTJMxp9JHd5JMBqtTF84SsY29iuHH0yznuD_Q,27204
41
- websockets/proxy.py,sha256=oFrbEYtasYWv-WDcniObD9nBR5Q5qkHpyCVLngx7WMQ,4969
41
+ websockets/proxy.py,sha256=gydvdaoJctxyGRZukd1vKimuxl4EYhT1ee9aPHqjkZM,4904
42
42
  websockets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  websockets/server.py,sha256=7kEJL1QEFO8pTB90wP4fUbkzYGDcbkTa7jTvdiSZtQk,24679
44
44
  websockets/speedups.c,sha256=u_dncR4M38EX6He_fzb1TY6D3Hke67ZpoHLLhZZ0hvQ,5920
45
- websockets/speedups.cpython-313-darwin.so,sha256=spMKIv_HCDJ1u5Xi-8pHtZHbXA0aB6JZqT9yZZr2aQE,51376
45
+ websockets/speedups.cpython-313-darwin.so,sha256=QZP9rzuQIu9v2K02dH6sNOJdrJtnp0mNNuyODpMY9Ms,51376
46
46
  websockets/speedups.pyi,sha256=unjvBNg-uW4c7z-9OW4WiSzZk_QH2bLEcjYAMuoSgBI,102
47
47
  websockets/streams.py,sha256=rJrDNSL9hy3EJYc1PFHjYx4rdd1vELCua0CwdFiHN_8,4610
48
48
  websockets/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- websockets/sync/client.py,sha256=WyD943lK7j4NQLI7PqoxWEEdd9mMIJjpa0NExx08Pj4,39916
50
- websockets/sync/connection.py,sha256=AZqADNZOE3ecTiqT7WNq1nsUbuojaDFpzNicEN9wIFQ,48680
49
+ websockets/sync/client.py,sha256=AmXZikxVg1m39yfl1Ovm9KY3xaTfzXeXNQ36yvYMHqQ,39972
50
+ websockets/sync/connection.py,sha256=3ZxMhZ9PzIzKzNFo8C78JLJWfjnh0QzPeMk6h_lP4EQ,49326
51
51
  websockets/sync/messages.py,sha256=q8K1vxdrqVk1bvn3zW1Jx09TlakIWSyYcOHC7ueFQmk,12826
52
52
  websockets/sync/router.py,sha256=wOGAvGbKwXyqmNEFBCWZP3faHvy_2MKN7O2XeEL8nGI,7212
53
- websockets/sync/server.py,sha256=VTT6TsR-J-NSWW5le8UVMTNNFNzPp6wbZWuVQUh-jYk,34422
53
+ websockets/sync/server.py,sha256=oxbJ9bs18SZOY2uSVrThdtLO8GOGyIJ5g4yFQcq-WFg,34882
54
54
  websockets/sync/utils.py,sha256=TtW-ncYFvJmiSW2gO86ngE2BVsnnBdL-4H88kWNDYbg,1107
55
55
  websockets/trio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- websockets/trio/client.py,sha256=m2N5IaFBrPYcUtp1VCPUo8tJ2UFi2fQ4G-95M4JCOxU,30197
56
+ websockets/trio/client.py,sha256=lR4uIlZ5Ak1gJ8Cbcg83G3p3PbkO00AMJQCun52FBbY,30256
57
57
  websockets/trio/connection.py,sha256=1XHDkVbK3e_M91pOGGeYeT0oazUM5NjqHOow0FkNrgY,49248
58
58
  websockets/trio/messages.py,sha256=SmbA1k_ljEGuEHZNJ0HPDe8-ojHtMTiXGR20HrPJuKk,10476
59
59
  websockets/trio/router.py,sha256=qZL9-44mi9SOOTGU3CLBPuaCNzafQGqZ-fJl9hVpv-s,7196
60
- websockets/trio/server.py,sha256=jRtrfZf3hHp1aQo6ffNMN7siaWzvZVhqC1iq1_Pj0sA,25805
60
+ websockets/trio/server.py,sha256=AnXSoUJceAyFTKqTcGDbajrEBt2vkDMokwWvWSXUu38,25934
61
61
  websockets/trio/utils.py,sha256=A12NYUPOjQaiGusak43oWzPIcCxhikKTkAJLXtZJvcY,956
62
62
  websockets/typing.py,sha256=cu89n_XQXno_g9qPpFmhLbbznE-MKVazUAows2u5JqU,1840
63
- websockets/uri.py,sha256=2fFMw-AbKJ5HVHNCuw1Rx1MnkCkNWRpogxWhhM30EU4,3125
63
+ websockets/uri.py,sha256=TNFgDvNDogfESw-78X8UDkh9pJF03ae-dujtpxE8BOs,3068
64
64
  websockets/utils.py,sha256=Wdtsh2qQiHLrXUBEnp3LatMLJTZjB2CErbeOFvrdXvE,2022
65
- websockets/version.py,sha256=-oLWt_FTqI6kCSgzMRlK9II17zZe7OnLw1nW2ZvqXEs,3202
65
+ websockets/version.py,sha256=FXV-IiFoGtln5A_i5aYSpGvbTBGiWUgRSsorpNv83ho,3202
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastbrowse/darwin-arm64",
3
- "version": "0.5.20",
3
+ "version": "0.5.25",
4
4
  "description": "The fastbrowse binary for macOS on arm64.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,14 +0,0 @@
1
- ../../../bin/fastbrowse,sha256=k_VHUJGUyyMLHeLCiSHLkBTLNP1m86q6e4YkyAHdlZM,337
2
- ../../../bin/fastbrowse-mcp,sha256=1CAFjtHrbGFIoAzljudiwUZ-Qrz8WFq5CLTPaDEvGRE,344
3
- _editable_impl_fastbrowse.pth,sha256=Ft6bxdkTegROr5p9hZSxsnUSfEHrZYCfXWKjtpA4_DU,44
4
- fastbrowse-0.5.20.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
5
- fastbrowse-0.5.20.dist-info/METADATA,sha256=TRP1vQPXRmVjUohcVe1cPzAiSIAyyu5deui0sw5RpmA,31373
6
- fastbrowse-0.5.20.dist-info/RECORD,,
7
- fastbrowse-0.5.20.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- fastbrowse-0.5.20.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
- fastbrowse-0.5.20.dist-info/direct_url.json,sha256=cZGZY9ovdib3X-RhjHdlyO_0_EMUe0tW6AXsSyMbNE4,86
10
- fastbrowse-0.5.20.dist-info/entry_points.txt,sha256=SlQAK1_4_oywTAMckx8BYuCQJva0F-QuShQPcxtUm-g,95
11
- fastbrowse-0.5.20.dist-info/licenses/LICENSE,sha256=gcUazG_j5a2blKPmfKqRKE98KOqX2kPbWY-EOtQZiNU,1067
12
- fastbrowse-0.5.20.dist-info/licenses/NOTICE,sha256=qEO0Az_Tk98vRVL0xjnGYgMvexlMCA92lWl4JBwYRzg,1854
13
- fastbrowse-0.5.20.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
14
- fastbrowse-0.5.20.dist-info/uv_cache.json,sha256=GWjVYY9Vp6-ng3PbDCtLXRAAV5WI5xeIq-BkZ9dZnZ0,194
@@ -1 +0,0 @@
1
- {"timestamp":{"secs_since_epoch":1791608646,"nanos_since_epoch":209709708},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1791608646,"nanos_since_epoch":211240749}}}