browser-use 0.0.1 → 0.1.0
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/LICENSE +21 -0
- package/README.md +761 -0
- package/dist/agent/cloud-events.d.ts +264 -0
- package/dist/agent/cloud-events.js +318 -0
- package/dist/agent/gif.d.ts +15 -0
- package/dist/agent/gif.js +215 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/message-manager/service.d.ts +30 -0
- package/dist/agent/message-manager/service.js +208 -0
- package/dist/agent/message-manager/utils.d.ts +2 -0
- package/dist/agent/message-manager/utils.js +41 -0
- package/dist/agent/message-manager/views.d.ts +26 -0
- package/dist/agent/message-manager/views.js +73 -0
- package/dist/agent/prompts.d.ts +52 -0
- package/dist/agent/prompts.js +259 -0
- package/dist/agent/service.d.ts +290 -0
- package/dist/agent/service.js +2200 -0
- package/dist/agent/views.d.ts +741 -0
- package/dist/agent/views.js +537 -0
- package/dist/browser/browser.d.ts +7 -0
- package/dist/browser/browser.js +5 -0
- package/dist/browser/context.d.ts +8 -0
- package/dist/browser/context.js +4 -0
- package/dist/browser/dvd-screensaver.d.ts +101 -0
- package/dist/browser/dvd-screensaver.js +270 -0
- package/dist/browser/extensions.d.ts +63 -0
- package/dist/browser/extensions.js +359 -0
- package/dist/browser/index.d.ts +10 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/playwright-manager.d.ts +47 -0
- package/dist/browser/playwright-manager.js +146 -0
- package/dist/browser/profile.d.ts +196 -0
- package/dist/browser/profile.js +815 -0
- package/dist/browser/session.d.ts +505 -0
- package/dist/browser/session.js +3409 -0
- package/dist/browser/types.d.ts +1184 -0
- package/dist/browser/types.js +1 -0
- package/dist/browser/utils.d.ts +1 -0
- package/dist/browser/utils.js +19 -0
- package/dist/browser/views.d.ts +78 -0
- package/dist/browser/views.js +72 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +44 -0
- package/dist/config.d.ts +108 -0
- package/dist/config.js +430 -0
- package/dist/controller/index.d.ts +3 -0
- package/dist/controller/index.js +3 -0
- package/dist/controller/registry/index.d.ts +2 -0
- package/dist/controller/registry/index.js +2 -0
- package/dist/controller/registry/service.d.ts +45 -0
- package/dist/controller/registry/service.js +184 -0
- package/dist/controller/registry/views.d.ts +55 -0
- package/dist/controller/registry/views.js +174 -0
- package/dist/controller/service.d.ts +49 -0
- package/dist/controller/service.js +1176 -0
- package/dist/controller/views.d.ts +241 -0
- package/dist/controller/views.js +88 -0
- package/dist/dom/clickable-element-processor/service.d.ts +11 -0
- package/dist/dom/clickable-element-processor/service.js +60 -0
- package/dist/dom/dom_tree/index.js +1400 -0
- package/dist/dom/history-tree-processor/service.d.ts +14 -0
- package/dist/dom/history-tree-processor/service.js +75 -0
- package/dist/dom/history-tree-processor/view.d.ts +54 -0
- package/dist/dom/history-tree-processor/view.js +56 -0
- package/dist/dom/playground/extraction.d.ts +19 -0
- package/dist/dom/playground/extraction.js +187 -0
- package/dist/dom/playground/process-dom.d.ts +1 -0
- package/dist/dom/playground/process-dom.js +5 -0
- package/dist/dom/playground/test-accessibility.d.ts +44 -0
- package/dist/dom/playground/test-accessibility.js +111 -0
- package/dist/dom/service.d.ts +19 -0
- package/dist/dom/service.js +227 -0
- package/dist/dom/utils.d.ts +1 -0
- package/dist/dom/utils.js +6 -0
- package/dist/dom/views.d.ts +61 -0
- package/dist/dom/views.js +247 -0
- package/dist/event-bus.d.ts +11 -0
- package/dist/event-bus.js +19 -0
- package/dist/exceptions.d.ts +10 -0
- package/dist/exceptions.js +22 -0
- package/dist/filesystem/file-system.d.ts +68 -0
- package/dist/filesystem/file-system.js +412 -0
- package/dist/filesystem/index.d.ts +1 -0
- package/dist/filesystem/index.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +33 -0
- package/dist/integrations/gmail/actions.d.ts +12 -0
- package/dist/integrations/gmail/actions.js +113 -0
- package/dist/integrations/gmail/index.d.ts +2 -0
- package/dist/integrations/gmail/index.js +2 -0
- package/dist/integrations/gmail/service.d.ts +61 -0
- package/dist/integrations/gmail/service.js +260 -0
- package/dist/llm/anthropic/chat.d.ts +28 -0
- package/dist/llm/anthropic/chat.js +126 -0
- package/dist/llm/anthropic/index.d.ts +2 -0
- package/dist/llm/anthropic/index.js +2 -0
- package/dist/llm/anthropic/serializer.d.ts +68 -0
- package/dist/llm/anthropic/serializer.js +285 -0
- package/dist/llm/aws/chat-anthropic.d.ts +61 -0
- package/dist/llm/aws/chat-anthropic.js +176 -0
- package/dist/llm/aws/chat-bedrock.d.ts +15 -0
- package/dist/llm/aws/chat-bedrock.js +80 -0
- package/dist/llm/aws/index.d.ts +3 -0
- package/dist/llm/aws/index.js +3 -0
- package/dist/llm/aws/serializer.d.ts +5 -0
- package/dist/llm/aws/serializer.js +68 -0
- package/dist/llm/azure/chat.d.ts +15 -0
- package/dist/llm/azure/chat.js +83 -0
- package/dist/llm/azure/index.d.ts +1 -0
- package/dist/llm/azure/index.js +1 -0
- package/dist/llm/base.d.ts +16 -0
- package/dist/llm/base.js +1 -0
- package/dist/llm/deepseek/chat.d.ts +15 -0
- package/dist/llm/deepseek/chat.js +51 -0
- package/dist/llm/deepseek/index.d.ts +2 -0
- package/dist/llm/deepseek/index.js +2 -0
- package/dist/llm/deepseek/serializer.d.ts +6 -0
- package/dist/llm/deepseek/serializer.js +57 -0
- package/dist/llm/exceptions.d.ts +10 -0
- package/dist/llm/exceptions.js +18 -0
- package/dist/llm/google/chat.d.ts +20 -0
- package/dist/llm/google/chat.js +144 -0
- package/dist/llm/google/index.d.ts +2 -0
- package/dist/llm/google/index.js +2 -0
- package/dist/llm/google/serializer.d.ts +6 -0
- package/dist/llm/google/serializer.js +64 -0
- package/dist/llm/groq/chat.d.ts +15 -0
- package/dist/llm/groq/chat.js +52 -0
- package/dist/llm/groq/index.d.ts +3 -0
- package/dist/llm/groq/index.js +3 -0
- package/dist/llm/groq/parser.d.ts +32 -0
- package/dist/llm/groq/parser.js +189 -0
- package/dist/llm/groq/serializer.d.ts +6 -0
- package/dist/llm/groq/serializer.js +56 -0
- package/dist/llm/messages.d.ts +77 -0
- package/dist/llm/messages.js +157 -0
- package/dist/llm/ollama/chat.d.ts +15 -0
- package/dist/llm/ollama/chat.js +77 -0
- package/dist/llm/ollama/index.d.ts +2 -0
- package/dist/llm/ollama/index.js +2 -0
- package/dist/llm/ollama/serializer.d.ts +6 -0
- package/dist/llm/ollama/serializer.js +53 -0
- package/dist/llm/openai/chat.d.ts +38 -0
- package/dist/llm/openai/chat.js +174 -0
- package/dist/llm/openai/index.d.ts +3 -0
- package/dist/llm/openai/index.js +3 -0
- package/dist/llm/openai/like.d.ts +17 -0
- package/dist/llm/openai/like.js +19 -0
- package/dist/llm/openai/serializer.d.ts +6 -0
- package/dist/llm/openai/serializer.js +57 -0
- package/dist/llm/openrouter/chat.d.ts +15 -0
- package/dist/llm/openrouter/chat.js +74 -0
- package/dist/llm/openrouter/index.d.ts +2 -0
- package/dist/llm/openrouter/index.js +2 -0
- package/dist/llm/openrouter/serializer.d.ts +3 -0
- package/dist/llm/openrouter/serializer.js +3 -0
- package/dist/llm/schema.d.ts +6 -0
- package/dist/llm/schema.js +77 -0
- package/dist/llm/views.d.ts +15 -0
- package/dist/llm/views.js +12 -0
- package/dist/logging-config.d.ts +25 -0
- package/dist/logging-config.js +89 -0
- package/dist/mcp/client.d.ts +142 -0
- package/dist/mcp/client.js +638 -0
- package/dist/mcp/controller.d.ts +6 -0
- package/dist/mcp/controller.js +38 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/server.d.ts +134 -0
- package/dist/mcp/server.js +759 -0
- package/dist/observability-decorators.d.ts +158 -0
- package/dist/observability-decorators.js +286 -0
- package/dist/observability.d.ts +23 -0
- package/dist/observability.js +58 -0
- package/dist/screenshots/index.d.ts +1 -0
- package/dist/screenshots/index.js +1 -0
- package/dist/screenshots/service.d.ts +6 -0
- package/dist/screenshots/service.js +28 -0
- package/dist/sync/auth.d.ts +27 -0
- package/dist/sync/auth.js +205 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/service.d.ts +21 -0
- package/dist/sync/service.js +146 -0
- package/dist/telemetry/index.d.ts +2 -0
- package/dist/telemetry/index.js +2 -0
- package/dist/telemetry/service.d.ts +12 -0
- package/dist/telemetry/service.js +85 -0
- package/dist/telemetry/views.d.ts +112 -0
- package/dist/telemetry/views.js +112 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/service.d.ts +35 -0
- package/dist/tokens/service.js +423 -0
- package/dist/tokens/views.d.ts +58 -0
- package/dist/tokens/views.js +1 -0
- package/dist/utils.d.ts +128 -0
- package/dist/utils.js +529 -0
- package/package.json +94 -5
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DOMHistoryElement, HashedDomElement } from './view.js';
|
|
2
|
+
import { DOMElementNode } from '../views.js';
|
|
3
|
+
export declare class HistoryTreeProcessor {
|
|
4
|
+
static convert_dom_element_to_history_element(dom_element: DOMElementNode, css_selector?: string | null): DOMHistoryElement;
|
|
5
|
+
static find_history_element_in_tree(dom_history_element: DOMHistoryElement, tree: DOMElementNode): DOMElementNode | null;
|
|
6
|
+
static compare_history_element_and_dom_element(dom_history_element: DOMHistoryElement, dom_element: DOMElementNode): boolean;
|
|
7
|
+
static _hash_dom_history_element(dom_history_element: DOMHistoryElement): HashedDomElement;
|
|
8
|
+
static _hash_dom_element(dom_element: DOMElementNode): HashedDomElement;
|
|
9
|
+
static _get_parent_branch_path(dom_element: DOMElementNode): string[];
|
|
10
|
+
static _parent_branch_path_hash(parent_branch_path: string[]): string;
|
|
11
|
+
static _attributes_hash(attributes: Record<string, string>): string;
|
|
12
|
+
static _xpath_hash(xpath: string): string;
|
|
13
|
+
static _text_hash(dom_element: DOMElementNode): string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { DOMHistoryElement, HashedDomElement } from './view.js';
|
|
3
|
+
import { DOMElementNode } from '../views.js';
|
|
4
|
+
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
|
|
5
|
+
export class HistoryTreeProcessor {
|
|
6
|
+
static convert_dom_element_to_history_element(dom_element, css_selector = null) {
|
|
7
|
+
const parent_branch_path = this._get_parent_branch_path(dom_element);
|
|
8
|
+
return new DOMHistoryElement(dom_element.tag_name, dom_element.xpath, dom_element.highlight_index ?? null, parent_branch_path, dom_element.attributes, dom_element.shadow_root, css_selector, dom_element.page_coordinates, dom_element.viewport_coordinates, dom_element.viewport_info);
|
|
9
|
+
}
|
|
10
|
+
static find_history_element_in_tree(dom_history_element, tree) {
|
|
11
|
+
const hashed = this._hash_dom_history_element(dom_history_element);
|
|
12
|
+
const process_node = (node) => {
|
|
13
|
+
if (node.highlight_index !== null && node.highlight_index !== undefined) {
|
|
14
|
+
const hashed_node = this._hash_dom_element(node);
|
|
15
|
+
if (hashed_node.equals(hashed)) {
|
|
16
|
+
return node;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const child of node.children) {
|
|
20
|
+
if (child instanceof DOMElementNode) {
|
|
21
|
+
const result = process_node(child);
|
|
22
|
+
if (result) {
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
return process_node(tree);
|
|
30
|
+
}
|
|
31
|
+
static compare_history_element_and_dom_element(dom_history_element, dom_element) {
|
|
32
|
+
const hashed_history = this._hash_dom_history_element(dom_history_element);
|
|
33
|
+
const hashed_dom = this._hash_dom_element(dom_element);
|
|
34
|
+
return hashed_history.equals(hashed_dom);
|
|
35
|
+
}
|
|
36
|
+
static _hash_dom_history_element(dom_history_element) {
|
|
37
|
+
const branch_path_hash = this._parent_branch_path_hash(dom_history_element.entire_parent_branch_path);
|
|
38
|
+
const attributes_hash = this._attributes_hash(dom_history_element.attributes);
|
|
39
|
+
const xpath_hash = this._xpath_hash(dom_history_element.xpath);
|
|
40
|
+
return new HashedDomElement(branch_path_hash, attributes_hash, xpath_hash);
|
|
41
|
+
}
|
|
42
|
+
static _hash_dom_element(dom_element) {
|
|
43
|
+
const parent_branch_path = this._get_parent_branch_path(dom_element);
|
|
44
|
+
const branch_path_hash = this._parent_branch_path_hash(parent_branch_path);
|
|
45
|
+
const attributes_hash = this._attributes_hash(dom_element.attributes);
|
|
46
|
+
const xpath_hash = this._xpath_hash(dom_element.xpath);
|
|
47
|
+
return new HashedDomElement(branch_path_hash, attributes_hash, xpath_hash);
|
|
48
|
+
}
|
|
49
|
+
static _get_parent_branch_path(dom_element) {
|
|
50
|
+
const parents = [];
|
|
51
|
+
let current = dom_element;
|
|
52
|
+
while (current && current.parent) {
|
|
53
|
+
parents.push(current);
|
|
54
|
+
current = current.parent;
|
|
55
|
+
}
|
|
56
|
+
parents.reverse();
|
|
57
|
+
return parents.map((parent) => parent.tag_name);
|
|
58
|
+
}
|
|
59
|
+
static _parent_branch_path_hash(parent_branch_path) {
|
|
60
|
+
return sha256(parent_branch_path.join('/'));
|
|
61
|
+
}
|
|
62
|
+
static _attributes_hash(attributes) {
|
|
63
|
+
const attributes_string = Object.entries(attributes)
|
|
64
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
65
|
+
.join('');
|
|
66
|
+
return sha256(attributes_string);
|
|
67
|
+
}
|
|
68
|
+
static _xpath_hash(xpath) {
|
|
69
|
+
return sha256(xpath);
|
|
70
|
+
}
|
|
71
|
+
static _text_hash(dom_element) {
|
|
72
|
+
const text = dom_element.get_all_text_till_next_clickable_element();
|
|
73
|
+
return sha256(text);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare class HashedDomElement {
|
|
2
|
+
branch_path_hash: string;
|
|
3
|
+
attributes_hash: string;
|
|
4
|
+
xpath_hash: string;
|
|
5
|
+
constructor(branch_path_hash: string, attributes_hash: string, xpath_hash: string);
|
|
6
|
+
/**
|
|
7
|
+
* Check equality with another HashedDomElement
|
|
8
|
+
*/
|
|
9
|
+
equals(other: HashedDomElement): boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface Coordinates {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}
|
|
15
|
+
export interface CoordinateSet {
|
|
16
|
+
top_left: Coordinates;
|
|
17
|
+
top_right: Coordinates;
|
|
18
|
+
bottom_left: Coordinates;
|
|
19
|
+
bottom_right: Coordinates;
|
|
20
|
+
center: Coordinates;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ViewportInfo {
|
|
25
|
+
scroll_x?: number | null;
|
|
26
|
+
scroll_y?: number | null;
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class DOMHistoryElement {
|
|
31
|
+
tag_name: string;
|
|
32
|
+
xpath: string;
|
|
33
|
+
highlight_index: number | null;
|
|
34
|
+
entire_parent_branch_path: string[];
|
|
35
|
+
attributes: Record<string, string>;
|
|
36
|
+
shadow_root: boolean;
|
|
37
|
+
css_selector: string | null;
|
|
38
|
+
page_coordinates: CoordinateSet | null;
|
|
39
|
+
viewport_coordinates: CoordinateSet | null;
|
|
40
|
+
viewport_info: ViewportInfo | null;
|
|
41
|
+
constructor(tag_name: string, xpath: string, highlight_index: number | null, entire_parent_branch_path: string[], attributes: Record<string, string>, shadow_root?: boolean, css_selector?: string | null, page_coordinates?: CoordinateSet | null, viewport_coordinates?: CoordinateSet | null, viewport_info?: ViewportInfo | null);
|
|
42
|
+
to_dict(): {
|
|
43
|
+
tag_name: string;
|
|
44
|
+
xpath: string;
|
|
45
|
+
highlight_index: number | null;
|
|
46
|
+
entire_parent_branch_path: string[];
|
|
47
|
+
attributes: Record<string, string>;
|
|
48
|
+
shadow_root: boolean;
|
|
49
|
+
css_selector: string | null;
|
|
50
|
+
page_coordinates: CoordinateSet | null;
|
|
51
|
+
viewport_coordinates: CoordinateSet | null;
|
|
52
|
+
viewport_info: ViewportInfo | null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export class HashedDomElement {
|
|
2
|
+
branch_path_hash;
|
|
3
|
+
attributes_hash;
|
|
4
|
+
xpath_hash;
|
|
5
|
+
constructor(branch_path_hash, attributes_hash, xpath_hash) {
|
|
6
|
+
this.branch_path_hash = branch_path_hash;
|
|
7
|
+
this.attributes_hash = attributes_hash;
|
|
8
|
+
this.xpath_hash = xpath_hash;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check equality with another HashedDomElement
|
|
12
|
+
*/
|
|
13
|
+
equals(other) {
|
|
14
|
+
return (this.branch_path_hash === other.branch_path_hash &&
|
|
15
|
+
this.attributes_hash === other.attributes_hash &&
|
|
16
|
+
this.xpath_hash === other.xpath_hash);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class DOMHistoryElement {
|
|
20
|
+
tag_name;
|
|
21
|
+
xpath;
|
|
22
|
+
highlight_index;
|
|
23
|
+
entire_parent_branch_path;
|
|
24
|
+
attributes;
|
|
25
|
+
shadow_root;
|
|
26
|
+
css_selector;
|
|
27
|
+
page_coordinates;
|
|
28
|
+
viewport_coordinates;
|
|
29
|
+
viewport_info;
|
|
30
|
+
constructor(tag_name, xpath, highlight_index, entire_parent_branch_path, attributes, shadow_root = false, css_selector = null, page_coordinates = null, viewport_coordinates = null, viewport_info = null) {
|
|
31
|
+
this.tag_name = tag_name;
|
|
32
|
+
this.xpath = xpath;
|
|
33
|
+
this.highlight_index = highlight_index;
|
|
34
|
+
this.entire_parent_branch_path = entire_parent_branch_path;
|
|
35
|
+
this.attributes = attributes;
|
|
36
|
+
this.shadow_root = shadow_root;
|
|
37
|
+
this.css_selector = css_selector;
|
|
38
|
+
this.page_coordinates = page_coordinates;
|
|
39
|
+
this.viewport_coordinates = viewport_coordinates;
|
|
40
|
+
this.viewport_info = viewport_info;
|
|
41
|
+
}
|
|
42
|
+
to_dict() {
|
|
43
|
+
return {
|
|
44
|
+
tag_name: this.tag_name,
|
|
45
|
+
xpath: this.xpath,
|
|
46
|
+
highlight_index: this.highlight_index,
|
|
47
|
+
entire_parent_branch_path: this.entire_parent_branch_path,
|
|
48
|
+
attributes: this.attributes,
|
|
49
|
+
shadow_root: this.shadow_root,
|
|
50
|
+
css_selector: this.css_selector,
|
|
51
|
+
page_coordinates: this.page_coordinates,
|
|
52
|
+
viewport_coordinates: this.viewport_coordinates,
|
|
53
|
+
viewport_info: this.viewport_info,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive DOM element testing tool.
|
|
3
|
+
*
|
|
4
|
+
* This playground allows you to:
|
|
5
|
+
* - Navigate to websites
|
|
6
|
+
* - Extract DOM state and clickable elements
|
|
7
|
+
* - Interactively click elements by index
|
|
8
|
+
* - Input text into elements
|
|
9
|
+
* - Copy element JSON to clipboard
|
|
10
|
+
* - Analyze token counts for LLM prompts
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* - Enter an element index to click it
|
|
14
|
+
* - Enter 'index,text' to input text into an element
|
|
15
|
+
* - Enter 'c,index' to copy element JSON to clipboard
|
|
16
|
+
* - Enter 'q' to quit
|
|
17
|
+
*/
|
|
18
|
+
declare function testFocusVsAllElements(): Promise<void>;
|
|
19
|
+
export { testFocusVsAllElements };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
import { BrowserProfile, BrowserSession } from '../../browser/index.js';
|
|
5
|
+
import { DomService } from '../service.js';
|
|
6
|
+
import { DEFAULT_INCLUDE_ATTRIBUTES } from '../views.js';
|
|
7
|
+
import { AgentMessagePrompt } from '../../agent/prompts.js';
|
|
8
|
+
import { FileSystem } from '../../filesystem/file-system.js';
|
|
9
|
+
const TIMEOUT = 60;
|
|
10
|
+
/**
|
|
11
|
+
* Interactive DOM element testing tool.
|
|
12
|
+
*
|
|
13
|
+
* This playground allows you to:
|
|
14
|
+
* - Navigate to websites
|
|
15
|
+
* - Extract DOM state and clickable elements
|
|
16
|
+
* - Interactively click elements by index
|
|
17
|
+
* - Input text into elements
|
|
18
|
+
* - Copy element JSON to clipboard
|
|
19
|
+
* - Analyze token counts for LLM prompts
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* - Enter an element index to click it
|
|
23
|
+
* - Enter 'index,text' to input text into an element
|
|
24
|
+
* - Enter 'c,index' to copy element JSON to clipboard
|
|
25
|
+
* - Enter 'q' to quit
|
|
26
|
+
*/
|
|
27
|
+
async function testFocusVsAllElements() {
|
|
28
|
+
const browserSession = new BrowserSession({
|
|
29
|
+
browser_profile: new BrowserProfile({
|
|
30
|
+
window_size: { width: 1100, height: 1000 },
|
|
31
|
+
disable_security: true,
|
|
32
|
+
wait_for_network_idle_page_load_time: 1,
|
|
33
|
+
headless: false,
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
const websites = [
|
|
37
|
+
'https://google.com',
|
|
38
|
+
'https://www.ycombinator.com/companies',
|
|
39
|
+
'https://kayak.com/flights',
|
|
40
|
+
'https://docs.google.com/spreadsheets/d/1INaIcfpYXlMRWO__de61SHFCaqt1lfHlcvtXZPItlpI/edit',
|
|
41
|
+
'https://www.zeiss.com/career/en/job-search.html?page=1',
|
|
42
|
+
'https://www.mlb.com/yankees/stats/',
|
|
43
|
+
'https://www.amazon.com/s?k=laptop&s=review-rank',
|
|
44
|
+
'https://reddit.com',
|
|
45
|
+
'https://codepen.io/geheimschriftstift/pen/mPLvQz',
|
|
46
|
+
'https://www.google.com/search?q=google+hi',
|
|
47
|
+
'https://amazon.com',
|
|
48
|
+
'https://github.com',
|
|
49
|
+
];
|
|
50
|
+
await browserSession.start();
|
|
51
|
+
const page = await browserSession.getCurrentPage();
|
|
52
|
+
if (!page) {
|
|
53
|
+
throw new Error('No page available');
|
|
54
|
+
}
|
|
55
|
+
const domService = new DomService(page);
|
|
56
|
+
const rl = readline.createInterface({
|
|
57
|
+
input: process.stdin,
|
|
58
|
+
output: process.stdout,
|
|
59
|
+
});
|
|
60
|
+
const question = promisify(rl.question).bind(rl);
|
|
61
|
+
for (const website of websites) {
|
|
62
|
+
await page.goto(website);
|
|
63
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
64
|
+
let lastClickedIndex = null;
|
|
65
|
+
while (true) {
|
|
66
|
+
try {
|
|
67
|
+
console.log(`\n${'='.repeat(50)}\nTesting ${website}\n${'='.repeat(50)}`);
|
|
68
|
+
console.log('\nGetting page state...');
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
const allElementsState = await browserSession.get_state_summary(true);
|
|
71
|
+
const endTime = Date.now();
|
|
72
|
+
console.log(`get_state_summary took ${((endTime - startTime) / 1000).toFixed(2)} seconds`);
|
|
73
|
+
const selectorMap = allElementsState.selector_map;
|
|
74
|
+
const totalElements = Object.keys(selectorMap).length;
|
|
75
|
+
console.log(`Total number of elements: ${totalElements}`);
|
|
76
|
+
const prompt = new AgentMessagePrompt({
|
|
77
|
+
browser_state_summary: allElementsState,
|
|
78
|
+
file_system: new FileSystem('./tmp'),
|
|
79
|
+
include_attributes: DEFAULT_INCLUDE_ATTRIBUTES,
|
|
80
|
+
step_info: null,
|
|
81
|
+
});
|
|
82
|
+
const userMessage = prompt.get_user_message(false).text || '';
|
|
83
|
+
const textToSave = userMessage;
|
|
84
|
+
await fs.mkdir('./tmp', { recursive: true });
|
|
85
|
+
await fs.writeFile('./tmp/user_message.txt', textToSave, 'utf-8');
|
|
86
|
+
await fs.writeFile('./tmp/element_tree.json', JSON.stringify(allElementsState.element_tree.toJSON(), null, 0), 'utf-8');
|
|
87
|
+
try {
|
|
88
|
+
// Optional: tiktoken is not installed by default
|
|
89
|
+
// @ts-ignore - tiktoken is an optional dependency
|
|
90
|
+
const { encoding_for_model } = await import('tiktoken');
|
|
91
|
+
const encoding = encoding_for_model('gpt-4o');
|
|
92
|
+
const tokenCount = encoding.encode(textToSave).length;
|
|
93
|
+
console.log(`Token count: ${tokenCount}`);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.log('Could not calculate token count (tiktoken not installed):', error.message);
|
|
97
|
+
}
|
|
98
|
+
console.log('User message written to ./tmp/user_message.txt');
|
|
99
|
+
console.log('Element tree written to ./tmp/element_tree.json');
|
|
100
|
+
const answer = String(await question("Enter element index to click, 'index,text' to input, 'c,index' to copy element JSON, or 'q' to quit: "));
|
|
101
|
+
if (answer.toLowerCase().trim() === 'q') {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
if (answer.toLowerCase().startsWith('c,')) {
|
|
106
|
+
const parts = answer.split(',', 2);
|
|
107
|
+
if (parts.length === 2) {
|
|
108
|
+
try {
|
|
109
|
+
const targetIndex = parseInt(parts[1].trim(), 10);
|
|
110
|
+
if (targetIndex in selectorMap) {
|
|
111
|
+
const elementNode = selectorMap[targetIndex];
|
|
112
|
+
const elementJson = JSON.stringify(elementNode.toJSON(), null, 2);
|
|
113
|
+
console.log(`Element ${targetIndex} JSON:`);
|
|
114
|
+
console.log(elementJson);
|
|
115
|
+
console.log(`\nElement: ${elementNode.tag_name}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(`Invalid index: ${targetIndex}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
console.log(`Invalid index format: ${parts[1]}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.log("Invalid input format. Use 'c,index'.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (answer.includes(',')) {
|
|
130
|
+
const parts = answer.split(',', 2);
|
|
131
|
+
if (parts.length === 2) {
|
|
132
|
+
try {
|
|
133
|
+
const targetIndex = parseInt(parts[0].trim(), 10);
|
|
134
|
+
const textToInput = parts[1];
|
|
135
|
+
if (targetIndex in selectorMap) {
|
|
136
|
+
const elementNode = selectorMap[targetIndex];
|
|
137
|
+
console.log(`Inputting text '${textToInput}' into element ${targetIndex}: ${elementNode.tag_name}`);
|
|
138
|
+
await browserSession._inputTextElementNode(elementNode, textToInput);
|
|
139
|
+
console.log('Input successful.');
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(`Invalid index: ${targetIndex}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
console.log(`Invalid index format: ${parts[0]}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log("Invalid input format. Use 'index,text'.");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
try {
|
|
155
|
+
const clickedIndex = parseInt(answer, 10);
|
|
156
|
+
if (clickedIndex in selectorMap) {
|
|
157
|
+
const elementNode = selectorMap[clickedIndex];
|
|
158
|
+
console.log(`Clicking element ${clickedIndex}: ${elementNode.tag_name}`);
|
|
159
|
+
await browserSession._clickElementNode(elementNode);
|
|
160
|
+
console.log('Click successful.');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(`Invalid index: ${clickedIndex}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
console.log(`Invalid input: '${answer}'. Enter an index, 'index,text', 'c,index', or 'q'.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (actionError) {
|
|
172
|
+
console.log(`Action failed: ${actionError.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.log(`Error in loop: ${error.message}`);
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
rl.close();
|
|
182
|
+
await browserSession.close();
|
|
183
|
+
}
|
|
184
|
+
if (require.main === module) {
|
|
185
|
+
testFocusVsAllElements().catch(console.error);
|
|
186
|
+
}
|
|
187
|
+
export { testFocusVsAllElements };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility Tree Playground for browser-use
|
|
3
|
+
*
|
|
4
|
+
* - Launches a browser and navigates to a target URL (default: amazon.com)
|
|
5
|
+
* - Extracts both the full and interesting-only accessibility trees using Playwright
|
|
6
|
+
* - Prints and saves both trees to JSON files
|
|
7
|
+
* - Recursively prints relevant info for each node (role, name, value, description, focusable, focused, checked, selected, disabled, children count)
|
|
8
|
+
* - Explains the difference between the accessibility tree and the DOM tree
|
|
9
|
+
* - Notes on React/Vue/SPA apps
|
|
10
|
+
* - Easy to modify for your own experiments
|
|
11
|
+
*
|
|
12
|
+
* Run with: npx tsx src/dom/playground/test-accessibility.ts
|
|
13
|
+
*/
|
|
14
|
+
type AXNode = {
|
|
15
|
+
role?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
value?: string | number;
|
|
18
|
+
description?: string;
|
|
19
|
+
focusable?: boolean;
|
|
20
|
+
focused?: boolean;
|
|
21
|
+
checked?: boolean | 'mixed';
|
|
22
|
+
selected?: boolean;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
children?: AXNode[];
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Helper to recursively print relevant info from the accessibility tree
|
|
29
|
+
*/
|
|
30
|
+
declare function printAxTree(node: AXNode | null, depth?: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* Helper to print all available accessibility node attributes
|
|
33
|
+
* Prints all key-value pairs for each node (except 'children'), then recurses into children
|
|
34
|
+
*/
|
|
35
|
+
declare function printAllFields(node: AXNode | null, depth?: number): void;
|
|
36
|
+
/**
|
|
37
|
+
* Flatten the accessibility tree into a list of "role name" strings
|
|
38
|
+
*/
|
|
39
|
+
declare function flattenAxTree(node: AXNode | null, lines: string[]): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get and analyze the accessibility tree for a given URL
|
|
42
|
+
*/
|
|
43
|
+
declare function getAxTree(targetUrl: string): Promise<void>;
|
|
44
|
+
export { printAxTree, printAllFields, flattenAxTree, getAxTree };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility Tree Playground for browser-use
|
|
3
|
+
*
|
|
4
|
+
* - Launches a browser and navigates to a target URL (default: amazon.com)
|
|
5
|
+
* - Extracts both the full and interesting-only accessibility trees using Playwright
|
|
6
|
+
* - Prints and saves both trees to JSON files
|
|
7
|
+
* - Recursively prints relevant info for each node (role, name, value, description, focusable, focused, checked, selected, disabled, children count)
|
|
8
|
+
* - Explains the difference between the accessibility tree and the DOM tree
|
|
9
|
+
* - Notes on React/Vue/SPA apps
|
|
10
|
+
* - Easy to modify for your own experiments
|
|
11
|
+
*
|
|
12
|
+
* Run with: npx tsx src/dom/playground/test-accessibility.ts
|
|
13
|
+
*/
|
|
14
|
+
import { chromium } from 'playwright';
|
|
15
|
+
/**
|
|
16
|
+
* Helper to recursively print relevant info from the accessibility tree
|
|
17
|
+
*/
|
|
18
|
+
function printAxTree(node, depth = 0) {
|
|
19
|
+
if (!node) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const indent = ' '.repeat(depth);
|
|
23
|
+
const info = [
|
|
24
|
+
`role=${JSON.stringify(node.role)}`,
|
|
25
|
+
node.name ? `name=${JSON.stringify(node.name)}` : null,
|
|
26
|
+
node.value !== undefined ? `value=${JSON.stringify(node.value)}` : null,
|
|
27
|
+
node.description ? `desc=${JSON.stringify(node.description)}` : null,
|
|
28
|
+
'focusable' in node ? `focusable=${JSON.stringify(node.focusable)}` : null,
|
|
29
|
+
'focused' in node ? `focused=${JSON.stringify(node.focused)}` : null,
|
|
30
|
+
'checked' in node ? `checked=${JSON.stringify(node.checked)}` : null,
|
|
31
|
+
'selected' in node ? `selected=${JSON.stringify(node.selected)}` : null,
|
|
32
|
+
'disabled' in node ? `disabled=${JSON.stringify(node.disabled)}` : null,
|
|
33
|
+
node.children ? `children=${node.children.length}` : null,
|
|
34
|
+
];
|
|
35
|
+
console.log('--------------------------------');
|
|
36
|
+
console.log(indent + info.filter((x) => x !== null).join(', '));
|
|
37
|
+
for (const child of node.children || []) {
|
|
38
|
+
printAxTree(child, depth + 1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Helper to print all available accessibility node attributes
|
|
43
|
+
* Prints all key-value pairs for each node (except 'children'), then recurses into children
|
|
44
|
+
*/
|
|
45
|
+
function printAllFields(node, depth = 0) {
|
|
46
|
+
if (!node) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const indent = ' '.repeat(depth);
|
|
50
|
+
for (const [k, v] of Object.entries(node)) {
|
|
51
|
+
if (k !== 'children') {
|
|
52
|
+
console.log(`${indent}${k}: ${JSON.stringify(v)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if ('children' in node && node.children) {
|
|
56
|
+
console.log(`${indent}children: ${node.children.length}`);
|
|
57
|
+
for (const child of node.children) {
|
|
58
|
+
printAllFields(child, depth + 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Flatten the accessibility tree into a list of "role name" strings
|
|
64
|
+
*/
|
|
65
|
+
function flattenAxTree(node, lines) {
|
|
66
|
+
if (!node) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const role = node.role || '';
|
|
70
|
+
const name = node.name || '';
|
|
71
|
+
lines.push(`${role} ${name}`);
|
|
72
|
+
for (const child of node.children || []) {
|
|
73
|
+
flattenAxTree(child, lines);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get and analyze the accessibility tree for a given URL
|
|
78
|
+
*/
|
|
79
|
+
async function getAxTree(targetUrl) {
|
|
80
|
+
const browser = await chromium.launch({ headless: true });
|
|
81
|
+
const page = await browser.newPage();
|
|
82
|
+
console.log(`Navigating to ${targetUrl}`);
|
|
83
|
+
await page.goto(targetUrl, { waitUntil: 'load' });
|
|
84
|
+
const axTreeInteresting = await page.accessibility.snapshot({
|
|
85
|
+
interestingOnly: true,
|
|
86
|
+
});
|
|
87
|
+
const lines = [];
|
|
88
|
+
flattenAxTree(axTreeInteresting, lines);
|
|
89
|
+
console.log(lines);
|
|
90
|
+
console.log(`length of ax_tree_interesting: ${lines.length}`);
|
|
91
|
+
await browser.close();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Main execution
|
|
95
|
+
*/
|
|
96
|
+
async function main() {
|
|
97
|
+
const TARGET_URLS = [
|
|
98
|
+
// 'https://amazon.com/',
|
|
99
|
+
// 'https://www.google.com/',
|
|
100
|
+
// 'https://www.facebook.com/',
|
|
101
|
+
// 'https://platform.openai.com/tokenizer',
|
|
102
|
+
'https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/checkbox',
|
|
103
|
+
];
|
|
104
|
+
for (const url of TARGET_URLS) {
|
|
105
|
+
await getAxTree(url);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (require.main === module) {
|
|
109
|
+
main().catch(console.error);
|
|
110
|
+
}
|
|
111
|
+
export { printAxTree, printAllFields, flattenAxTree, getAxTree };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Page } from '../browser/types.js';
|
|
2
|
+
import { DOMState } from './views.js';
|
|
3
|
+
export declare class DomService {
|
|
4
|
+
private readonly page;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
private readonly jsCode;
|
|
7
|
+
constructor(page: Page, logger?: import("../logging-config.js").Logger);
|
|
8
|
+
get_clickable_elements(highlight_elements?: boolean, focus_element?: number, viewport_expansion?: number): Promise<DOMState>;
|
|
9
|
+
get_cross_origin_iframes(): Promise<any>;
|
|
10
|
+
private _build_dom_tree;
|
|
11
|
+
private _construct_dom_tree;
|
|
12
|
+
private _parse_node;
|
|
13
|
+
private safeHostname;
|
|
14
|
+
private getFrames;
|
|
15
|
+
private getFrameUrl;
|
|
16
|
+
private isAdUrl;
|
|
17
|
+
private getPageUrl;
|
|
18
|
+
private isDebugEnabled;
|
|
19
|
+
}
|