@fastbrowse/darwin-x64 0.5.20 → 0.5.23
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.
- package/fastbrowse/_internal/base_library.zip +0 -0
- package/fastbrowse/_internal/fastbrowse/browser/capture.js +72 -1
- package/fastbrowse/_internal/fastbrowse/browser/snapshot.js +85 -7
- package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/METADATA +74 -13
- package/fastbrowse/_internal/fastbrowse-0.5.23.dist-info/RECORD +14 -0
- package/fastbrowse/_internal/fastbrowse-0.5.23.dist-info/uv_cache.json +1 -0
- package/fastbrowse/_internal/websockets/speedups.cpython-313-darwin.so +0 -0
- package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/METADATA +2 -2
- package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/RECORD +22 -22
- package/fastbrowse/fastbrowse +0 -0
- package/package.json +1 -1
- package/fastbrowse/_internal/fastbrowse-0.5.20.dist-info/RECORD +0 -14
- package/fastbrowse/_internal/fastbrowse-0.5.20.dist-info/uv_cache.json +0 -1
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/INSTALLER +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/REQUESTED +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/WHEEL +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/direct_url.json +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/entry_points.txt +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/licenses/LICENSE +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/licenses/NOTICE +0 -0
- /package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/uv_build.json +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/INSTALLER +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/REQUESTED +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/WHEEL +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/entry_points.txt +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/licenses/LICENSE +0 -0
- /package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
@@ -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 (!
|
|
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 =
|
|
190
|
-
if (glyph &&
|
|
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 ?
|
|
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:
|
|
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) {
|
package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastbrowse
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.23
|
|
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
|
|
@@ -60,6 +61,9 @@ with methods and run summaries.
|
|
|
60
61
|
|
|
61
62
|
Rows are comparable only at matching task versions. See [eval results and workflow](docs/evals.md).
|
|
62
63
|
|
|
64
|
+
Recorded results - the published head-to-head, the matched-task campaigns and the browser cost basis - are
|
|
65
|
+
summarised with their limitations in [benchmark results](docs/benchmarks.md).
|
|
66
|
+
|
|
63
67
|
### Why fastbrowse, against each kind of agent
|
|
64
68
|
|
|
65
69
|
- **LLM agents that generate actions** (the hosted Browser Use agent and similar): Jev picks each action from the controls
|
|
@@ -97,6 +101,10 @@ automatic selection; see [provider routing](docs/jev.md#provider-failover). The
|
|
|
97
101
|
PATH, and `uv add fastbrowse` puts it in a project. Service keys can live in a `.env` file in the working directory;
|
|
98
102
|
[`.env.example`](.env.example) shows the settings. Values named by `--secret` must be in the process environment.
|
|
99
103
|
|
|
104
|
+
On Arch Linux, the [`fastbrowse` AUR package](https://aur.archlinux.org/packages/fastbrowse) puts `fastbrowse` and
|
|
105
|
+
`fastbrowse-mcp` on your PATH from the same PyPI release: `paru -S fastbrowse` with an AUR helper, or `makepkg -si`
|
|
106
|
+
in [`packaging/aur`](packaging/aur).
|
|
107
|
+
|
|
100
108
|
To work on fastbrowse itself:
|
|
101
109
|
|
|
102
110
|
```sh
|
|
@@ -121,15 +129,16 @@ the quotes behind the answer, and cost by component.
|
|
|
121
129
|
| `--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
130
|
| `--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
131
|
| `--target-match TEXT` | attach to the first window whose title or URL contains `TEXT` (implies `--attach`) |
|
|
132
|
+
| `--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
133
|
| `--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
134
|
| `--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` |
|
|
135
|
+
| `--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
136
|
| `--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
137
|
| `--max-steps N`, `--max-dollars N` | optionally bound steps and model spend; defaults are unlimited. Cloud browser charges are added when it stops |
|
|
129
138
|
| `--downloads DIR` | keep downloaded files |
|
|
130
139
|
| `--json` | full result instead of the answer |
|
|
131
140
|
| `--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 |
|
|
141
|
+
| `--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
142
|
|
|
134
143
|
```sh
|
|
135
144
|
export SAUCE_PASSWORD=secret_sauce
|
|
@@ -147,6 +156,9 @@ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
|
|
|
147
156
|
--start https://www.amazon.com/ --profile ~/.fastbrowse/amazon --headed
|
|
148
157
|
```
|
|
149
158
|
|
|
159
|
+
`--headed` is not needed once the profile is signed in: without it the run is headless and opens no window. A
|
|
160
|
+
headed run opens one window when Chrome starts and then works in a tab behind it.
|
|
161
|
+
|
|
150
162
|
On a cloud browser the profile lives on the [Browser Use Cloud](https://cloud.browser-use.com) account
|
|
151
163
|
rather than on disk, and `--cloud-profile ID` runs as it. Whoever signed that profile in did so once, in a
|
|
152
164
|
browser of their own; the run inherits the cookies and no model is shown a credential:
|
|
@@ -189,6 +201,26 @@ the text; `--attach` alone takes the first window, skipping DevTools. Popups the
|
|
|
189
201
|
Windows that nothing opened, as an Electron app's main process opens them, join only with `--target-match`,
|
|
190
202
|
because in a browser such a window could equally be a tab you opened yourself.
|
|
191
203
|
|
|
204
|
+
### Working in the background
|
|
205
|
+
|
|
206
|
+
A run in a visible window, whether `--headed` or a browser you handed over, opens its tab behind the window's
|
|
207
|
+
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
|
|
208
|
+
are Chrome's own and still bring a window forward: a headed Chrome showing its window as it starts, a popup a
|
|
209
|
+
page opens, and, with `--attach`, a click in a window's front tab.
|
|
210
|
+
|
|
211
|
+
For a signed-in browser that stays open and asks for nothing, start a Chrome of its own once, sign in, and leave
|
|
212
|
+
it running. Each run then opens and closes a background tab in it:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.fastbrowse/chrome"
|
|
216
|
+
uv run fastbrowse "Report my open orders." --start https://www.amazon.com/ --cdp-port 9222
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Chrome ignores `--remote-debugging-port` on its default data directory, so the directory of its own is required.
|
|
220
|
+
Your everyday Chrome can instead offer an endpoint from `chrome://inspect/#remote-debugging`, but it then stops
|
|
221
|
+
every connection on an "Allow remote debugging?" prompt that takes focus and waits for a click, once per run.
|
|
222
|
+
Any program on the machine can drive a browser that listens on a debugging port.
|
|
223
|
+
|
|
192
224
|
A DevTools port gives any program on the machine full control of the app, including its signed-in sessions.
|
|
193
225
|
Close the app, or restart it without the flag, when you are done.
|
|
194
226
|
|
|
@@ -200,10 +232,23 @@ Override with `FASTBROWSE_LLM_MODEL` (every purpose), `FASTBROWSE_LLM_MODEL_<PUR
|
|
|
200
232
|
`READ`, `FIELD_TEXT`, `SHORTCUT`, `RECOVER`, `COMPOSE`, `VERIFY`) and `FASTBROWSE_LLM_REASONING`
|
|
201
233
|
(`low`, `medium`, `high`).
|
|
202
234
|
|
|
235
|
+
`FASTBROWSE_LLM_BASE_URL` sends every LLM call to another OpenAI-compatible chat completions endpoint instead
|
|
236
|
+
of OpenRouter and the gateway, such as a local vLLM server at `http://127.0.0.1:8000/v1`. Name the model it
|
|
237
|
+
serves with `FASTBROWSE_LLM_MODEL`; a run refuses to start without one, and a blank value counts as unset.
|
|
238
|
+
OpenRouter's `reasoning` field is sent to that endpoint only when `FASTBROWSE_LLM_REASONING` is set, since a
|
|
239
|
+
strict server refuses a field it does not know. `FASTBROWSE_LLM_API_KEY` is its bearer
|
|
240
|
+
token, sent only when set, and that endpoint never receives `OPENROUTER_API_KEY` or `AI_GATEWAY_API_KEY`. Two
|
|
241
|
+
settings shape its requests: `FASTBROWSE_LLM_STRICT=0` sends each schema as the model defines it rather than
|
|
242
|
+
in strict form, for a server whose structured output breaks under strict mode, and `FASTBROWSE_LLM_EXTRA_BODY`
|
|
243
|
+
is a JSON object added to every request, such as `{"chat_template_kwargs":{"enable_thinking":false}}`. It may
|
|
244
|
+
not set a field fastbrowse sends itself (`model`, `messages`, `max_tokens`, `response_format`, `provider`,
|
|
245
|
+
`reasoning`). Jev still needs its own key. An endpoint that reports no price leaves those calls' cost unknown
|
|
246
|
+
rather than zero, so `--max-dollars` stops the run after the first such call.
|
|
247
|
+
|
|
203
248
|
### Results
|
|
204
249
|
|
|
205
250
|
The exit code identifies the run status. Configuration refusals exit 1; invalid command syntax exits 2.
|
|
206
|
-
|
|
251
|
+
Only `complete` exits 0, so a program can also check zero versus nonzero.
|
|
207
252
|
|
|
208
253
|
| Status | Exit | Meaning |
|
|
209
254
|
|:--|--:|:--|
|
|
@@ -225,6 +270,7 @@ optional object, so callers can identify the affected limit without parsing an e
|
|
|
225
270
|
|
|
226
271
|
A bot check can block a cloud browser before any task steps execute. Retry with an attached local browser
|
|
227
272
|
or choose another source for the research. Credentials do not resolve a CAPTCHA.
|
|
273
|
+
The `blocked` result's `evidence` quotes the opening text of the challenge page, with its address.
|
|
228
274
|
|
|
229
275
|
If a model completion omits its cost, fastbrowse looks up the matching OpenRouter or Vercel generation
|
|
230
276
|
receipt. If that receipt is still unavailable, a run with a dollar cap stops with `budget_exceeded`;
|
|
@@ -235,6 +281,9 @@ A browser command that remains unanswered for 120 seconds returns `unavailable`,
|
|
|
235
281
|
still answers health checks. Step and dollar limits do not bound total elapsed time. Set `Limits(max_seconds=...)` for a run deadline.
|
|
236
282
|
When investigating a stalled run, enable trace logging and retain the last event alongside the result.
|
|
237
283
|
|
|
284
|
+
[Troubleshooting](docs/troubleshooting.md) takes each status a run can stop with: what it means, its common
|
|
285
|
+
causes and the next step.
|
|
286
|
+
|
|
238
287
|
## How it works
|
|
239
288
|
|
|
240
289
|
<picture>
|
|
@@ -299,7 +348,8 @@ asyncio.run(main())
|
|
|
299
348
|
the run opens its own tab and closes the tabs it owns. It leaves the browser and pre-existing tabs open;
|
|
300
349
|
cookies and other changes made by the task can persist. `cdp_port=` finds the same browser from its DevTools
|
|
301
350
|
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
|
|
351
|
+
`--attach` and `--target-match` do, and `foreground=True` brings the run's tab to the front as `--foreground`
|
|
352
|
+
does. Pass `browser_api_key=` to start a cloud browser;
|
|
303
353
|
with neither argument, it runs local Chrome. Passing both is an error. `cloud_extensions=[...]` loads up to
|
|
304
354
|
three of your account's ready Browser Use Cloud extensions, by ID, into that cloud browser; it is an error with
|
|
305
355
|
local or attached browsers.
|
|
@@ -318,6 +368,17 @@ async def main() -> None:
|
|
|
318
368
|
|
|
319
369
|
`resolve_cdp_port(port)` returns the `ws://` URL behind a DevTools port, for a caller that passes `cdp_url=`.
|
|
320
370
|
|
|
371
|
+
`run_task` and `connect_cdp` take two access controls that the command line, the MCP server and the JavaScript
|
|
372
|
+
SDK do not offer. `allowed_origins=["https://app.example.com"]` limits the documents a run may read and control
|
|
373
|
+
to those exact origins: scheme, host and optional port, with no wildcard or path. A navigation, redirect, frame
|
|
374
|
+
or popup outside the list is refused before it is sent, and a window already open outside it is never read.
|
|
375
|
+
A sign-in that redirects through another origin needs that origin listed too. The list scopes documents, not
|
|
376
|
+
the network: images, scripts and requests a listed page makes to other hosts still load. A scoped run delivers
|
|
377
|
+
no live frames or screenshots, and combining it with `record=` is an error. `check_access=` is an async callback
|
|
378
|
+
awaited before the browser starts and before every read, navigation and action. When it raises, `connect_cdp`
|
|
379
|
+
raises a `BrowserError` and `run_task` returns a `RunResult` with status `error`; either way the error names only
|
|
380
|
+
the exception's type, never its text.
|
|
381
|
+
|
|
321
382
|
`RunResult.citations` is a tuple of `Citation` objects, also importable from `fastbrowse`. Each has `id`
|
|
322
383
|
(the number in the answer), `text` (the Notes fact), `requirement_id` (or `None`), `url`, `quote` and
|
|
323
384
|
`deep_link`. Each claim in `result.answer` carries numbered Markdown links to its supporting facts.
|
|
@@ -357,7 +418,7 @@ package is a TypeScript SDK with no dependencies. With it npm installs one of fi
|
|
|
357
418
|
as a native binary, the one for your platform: `@fastbrowse/darwin-arm64`, `darwin-x64`, `linux-arm64`,
|
|
358
419
|
`linux-x64` or `win32-x64`. No install script runs and nothing is downloaded on first use, so it installs under
|
|
359
420
|
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
|
|
421
|
+
the PyPI package's version and are published by the same tag.
|
|
361
422
|
|
|
362
423
|
The binary brings no browser. It finds local Chrome, starts a Browser Use Cloud browser, or drives one already
|
|
363
424
|
running, as the command line does. It reads the same keys from the environment of your process
|
|
@@ -412,7 +473,7 @@ the options are camelCase. The structured data is in `data`, as in Python. The S
|
|
|
412
473
|
schema's output type. Data the schema refuses rejects with `OutputValidationError`, which carries the result.
|
|
413
474
|
A JSON Schema object is accepted too; `output` is then `data`, typed `unknown`.
|
|
414
475
|
|
|
415
|
-
What a run can fill
|
|
476
|
+
What a run can fill is narrower than what a schema can say. The server accepts nested objects, arrays,
|
|
416
477
|
`enum`, `const` and optional values, and refuses any other keyword by name before a browser opens. A run
|
|
417
478
|
fills a flat object of required string, number, integer and boolean fields, as the example has. With any
|
|
418
479
|
other field the run ends `unverified` with no data.
|
|
@@ -433,11 +494,11 @@ await fb.run('Log in as standard_user with the saved password and add the backpa
|
|
|
433
494
|
|
|
434
495
|
`secrets.resolve` is called each time a value is typed and never for an origin its ref does not cover, so the
|
|
435
496
|
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
|
-
|
|
438
|
-
|
|
439
|
-
`
|
|
440
|
-
`Fastbrowse.start` also takes as defaults for every run. A run passes `null` for one of them to go without
|
|
497
|
+
and anything but `true` keeps the run from `complete`. `onFrame` receives JPEG frames of the active tab; without
|
|
498
|
+
it no frame is sent. A callback that throws ends the run with status `error`. The other options are `run_task`'s:
|
|
499
|
+
`inputs`, `attachments` as bytes, `downloads`, `record`, and the browser choices `chrome`, `cloudProfile`,
|
|
500
|
+
`cdpUrl`, `cdpPort`, `attach`, `targetMatch`, `foreground`, `proxyCountry`, `viewport` and `cloudAllowResizing`,
|
|
501
|
+
which `Fastbrowse.start` also takes as defaults for every run. A run passes `null` for one of them to go without
|
|
441
502
|
that default.
|
|
442
503
|
|
|
443
504
|
There is no binary for Alpine or another musl system, and none for a platform outside the five. There
|
|
@@ -489,7 +550,7 @@ The server's flags decide what a calling model may do; a call can ask for less,
|
|
|
489
550
|
|
|
490
551
|
| Flag | Effect |
|
|
491
552
|
|:--|:--|
|
|
492
|
-
| `--local`, `--headed`, `--profile DIR`, `--downloads DIR`, `--cursor` | as for the CLI, fixed for every call |
|
|
553
|
+
| `--local`, `--headed`, `--profile DIR`, `--foreground`, `--downloads DIR`, `--cursor` | as for the CLI, fixed for every call |
|
|
493
554
|
| `--cloud-profile ID` | every call runs signed in as that cloud profile; a calling model cannot choose it |
|
|
494
555
|
| `--allow-authorize` | let a call pass `authorize` to go through irreversible actions; without it they always stop at `needs_confirmation` |
|
|
495
556
|
| `--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.23.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
|
5
|
+
fastbrowse-0.5.23.dist-info/METADATA,sha256=_tHtsDCpIq1LtYvyl1hsqA-XHnZB86idiCg6iqO6IMw,36424
|
|
6
|
+
fastbrowse-0.5.23.dist-info/RECORD,,
|
|
7
|
+
fastbrowse-0.5.23.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
fastbrowse-0.5.23.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
9
|
+
fastbrowse-0.5.23.dist-info/direct_url.json,sha256=cZGZY9ovdib3X-RhjHdlyO_0_EMUe0tW6AXsSyMbNE4,86
|
|
10
|
+
fastbrowse-0.5.23.dist-info/entry_points.txt,sha256=SlQAK1_4_oywTAMckx8BYuCQJva0F-QuShQPcxtUm-g,95
|
|
11
|
+
fastbrowse-0.5.23.dist-info/licenses/LICENSE,sha256=gcUazG_j5a2blKPmfKqRKE98KOqX2kPbWY-EOtQZiNU,1067
|
|
12
|
+
fastbrowse-0.5.23.dist-info/licenses/NOTICE,sha256=qEO0Az_Tk98vRVL0xjnGYgMvexlMCA92lWl4JBwYRzg,1854
|
|
13
|
+
fastbrowse-0.5.23.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
|
|
14
|
+
fastbrowse-0.5.23.dist-info/uv_cache.json,sha256=RHhZDSEpPRq1C_MkD2kEEppnHuQ4UybkBcfjm8erpLo,194
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"timestamp":{"secs_since_epoch":1792329370,"nanos_since_epoch":625328192},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1792329370,"nanos_since_epoch":634544741}}}
|
|
Binary file
|
package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: websockets
|
|
3
|
-
Version: 17.
|
|
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://
|
|
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.
|
|
3
|
-
websockets-17.
|
|
4
|
-
websockets-17.
|
|
5
|
-
websockets-17.
|
|
6
|
-
websockets-17.
|
|
7
|
-
websockets-17.
|
|
8
|
-
websockets-17.
|
|
9
|
-
websockets-17.
|
|
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=j0ILfIDPYGKSxi38ezJw4f9sIlVrNBnqDQjDYMVPBMs,138
|
|
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=
|
|
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=
|
|
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=
|
|
27
|
-
websockets/frames.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
45
|
+
websockets/speedups.cpython-313-darwin.so,sha256=cQQscid5qO4S83zZAT_jYiOM1gecdZ5Sw_fS7eW15KU,9856
|
|
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=
|
|
50
|
-
websockets/sync/connection.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
63
|
+
websockets/uri.py,sha256=TNFgDvNDogfESw-78X8UDkh9pJF03ae-dujtpxE8BOs,3068
|
|
64
64
|
websockets/utils.py,sha256=Wdtsh2qQiHLrXUBEnp3LatMLJTZjB2CErbeOFvrdXvE,2022
|
|
65
|
-
websockets/version.py,sha256
|
|
65
|
+
websockets/version.py,sha256=FXV-IiFoGtln5A_i5aYSpGvbTBGiWUgRSsorpNv83ho,3202
|
package/fastbrowse/fastbrowse
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -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=NQkJ10qj0a2tOAG1n-E8bx-QNcOOfzHTWIFROAfMSS0,194
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"timestamp":{"secs_since_epoch":1791608647,"nanos_since_epoch":711603669},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1791608647,"nanos_since_epoch":722459116}}}
|
/package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/INSTALLER
RENAMED
|
File without changes
|
/package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/REQUESTED
RENAMED
|
File without changes
|
/package/fastbrowse/_internal/{fastbrowse-0.5.20.dist-info → fastbrowse-0.5.23.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/INSTALLER
RENAMED
|
File without changes
|
/package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/REQUESTED
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/fastbrowse/_internal/{websockets-17.1.dist-info → websockets-17.2.dist-info}/top_level.txt
RENAMED
|
File without changes
|