browser-use 0.2.0 → 0.3.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/README.md +295 -686
- package/dist/actor/element.d.ts +19 -0
- package/dist/actor/element.js +46 -0
- package/dist/actor/index.d.ts +4 -0
- package/dist/actor/index.js +4 -0
- package/dist/actor/mouse.d.ts +19 -0
- package/dist/actor/mouse.js +39 -0
- package/dist/actor/page.d.ts +29 -0
- package/dist/actor/page.js +88 -0
- package/dist/actor/utils.d.ts +4 -0
- package/dist/actor/utils.js +35 -0
- package/dist/agent/cloud-events.d.ts +18 -0
- package/dist/agent/cloud-events.js +65 -2
- package/dist/agent/gif.d.ts +1 -0
- package/dist/agent/gif.js +24 -2
- package/dist/agent/judge.d.ts +17 -0
- package/dist/agent/judge.js +197 -0
- package/dist/agent/message-manager/service.d.ts +12 -4
- package/dist/agent/message-manager/service.js +205 -39
- package/dist/agent/message-manager/utils.js +0 -1
- package/dist/agent/message-manager/views.d.ts +4 -0
- package/dist/agent/message-manager/views.js +11 -7
- package/dist/agent/prompts.d.ts +24 -3
- package/dist/agent/prompts.js +274 -59
- package/dist/agent/service.d.ts +99 -41
- package/dist/agent/service.js +2266 -472
- package/dist/agent/variable-detector.d.ts +12 -0
- package/dist/agent/variable-detector.js +211 -0
- package/dist/agent/views.d.ts +237 -18
- package/dist/agent/views.js +446 -33
- package/dist/browser/cloud/cloud.d.ts +20 -0
- package/dist/browser/cloud/cloud.js +129 -0
- package/dist/browser/cloud/index.d.ts +2 -0
- package/dist/browser/cloud/index.js +2 -0
- package/dist/browser/cloud/views.d.ts +41 -0
- package/dist/browser/cloud/views.js +35 -0
- package/dist/browser/events.d.ts +345 -0
- package/dist/browser/events.js +566 -0
- package/dist/browser/extensions.js +17 -17
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/profile.d.ts +8 -2
- package/dist/browser/profile.js +79 -12
- package/dist/browser/session-manager.d.ts +85 -0
- package/dist/browser/session-manager.js +208 -0
- package/dist/browser/session.d.ts +100 -8
- package/dist/browser/session.js +1097 -58
- package/dist/browser/types.d.ts +0 -2
- package/dist/browser/views.d.ts +39 -0
- package/dist/browser/views.js +32 -0
- package/dist/browser/watchdogs/aboutblank-watchdog.d.ts +12 -0
- package/dist/browser/watchdogs/aboutblank-watchdog.js +131 -0
- package/dist/browser/watchdogs/base.d.ts +21 -0
- package/dist/browser/watchdogs/base.js +81 -0
- package/dist/browser/watchdogs/cdp-session-watchdog.d.ts +14 -0
- package/dist/browser/watchdogs/cdp-session-watchdog.js +177 -0
- package/dist/browser/watchdogs/crash-watchdog.d.ts +38 -0
- package/dist/browser/watchdogs/crash-watchdog.js +296 -0
- package/dist/browser/watchdogs/default-action-watchdog.d.ts +49 -0
- package/dist/browser/watchdogs/default-action-watchdog.js +212 -0
- package/dist/browser/watchdogs/dom-watchdog.d.ts +8 -0
- package/dist/browser/watchdogs/dom-watchdog.js +31 -0
- package/dist/browser/watchdogs/downloads-watchdog.d.ts +77 -0
- package/dist/browser/watchdogs/downloads-watchdog.js +409 -0
- package/dist/browser/watchdogs/har-recording-watchdog.d.ts +19 -0
- package/dist/browser/watchdogs/har-recording-watchdog.js +317 -0
- package/dist/browser/watchdogs/index.d.ts +15 -0
- package/dist/browser/watchdogs/index.js +15 -0
- package/dist/browser/watchdogs/local-browser-watchdog.d.ts +10 -0
- package/dist/browser/watchdogs/local-browser-watchdog.js +32 -0
- package/dist/browser/watchdogs/permissions-watchdog.d.ts +8 -0
- package/dist/browser/watchdogs/permissions-watchdog.js +73 -0
- package/dist/browser/watchdogs/popups-watchdog.d.ts +13 -0
- package/dist/browser/watchdogs/popups-watchdog.js +77 -0
- package/dist/browser/watchdogs/recording-watchdog.d.ts +27 -0
- package/dist/browser/watchdogs/recording-watchdog.js +249 -0
- package/dist/browser/watchdogs/screenshot-watchdog.d.ts +6 -0
- package/dist/browser/watchdogs/screenshot-watchdog.js +13 -0
- package/dist/browser/watchdogs/security-watchdog.d.ts +10 -0
- package/dist/browser/watchdogs/security-watchdog.js +84 -0
- package/dist/browser/watchdogs/storage-state-watchdog.d.ts +24 -0
- package/dist/browser/watchdogs/storage-state-watchdog.js +288 -0
- package/dist/cli.d.ts +7 -2
- package/dist/cli.js +182 -25
- package/dist/code-use/formatting.d.ts +3 -0
- package/dist/code-use/formatting.js +18 -0
- package/dist/code-use/index.d.ts +6 -0
- package/dist/code-use/index.js +6 -0
- package/dist/code-use/namespace.d.ts +5 -0
- package/dist/code-use/namespace.js +81 -0
- package/dist/code-use/notebook-export.d.ts +3 -0
- package/dist/code-use/notebook-export.js +56 -0
- package/dist/code-use/service.d.ts +24 -0
- package/dist/code-use/service.js +104 -0
- package/dist/code-use/utils.d.ts +4 -0
- package/dist/code-use/utils.js +98 -0
- package/dist/code-use/views.d.ts +108 -0
- package/dist/code-use/views.js +165 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +69 -3
- package/dist/controller/registry/service.d.ts +10 -1
- package/dist/controller/registry/service.js +266 -10
- package/dist/controller/registry/views.d.ts +4 -1
- package/dist/controller/registry/views.js +25 -2
- package/dist/controller/service.d.ts +10 -1
- package/dist/controller/service.js +1807 -268
- package/dist/controller/views.d.ts +78 -155
- package/dist/controller/views.js +61 -12
- package/dist/dom/history-tree-processor/service.d.ts +5 -0
- package/dist/dom/history-tree-processor/service.js +169 -14
- package/dist/dom/history-tree-processor/view.d.ts +7 -1
- package/dist/dom/history-tree-processor/view.js +10 -1
- package/dist/dom/markdown-extractor.d.ts +37 -0
- package/dist/dom/markdown-extractor.js +345 -0
- package/dist/dom/service.d.ts +3 -1
- package/dist/dom/service.js +76 -0
- package/dist/dom/views.d.ts +1 -0
- package/dist/dom/views.js +45 -0
- package/dist/event-bus.d.ts +107 -7
- package/dist/event-bus.js +313 -10
- package/dist/exceptions.d.ts +0 -3
- package/dist/exceptions.js +0 -7
- package/dist/filesystem/file-system.d.ts +18 -0
- package/dist/filesystem/file-system.js +503 -42
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/integrations/gmail/actions.d.ts +3 -3
- package/dist/integrations/gmail/actions.js +4 -4
- package/dist/llm/anthropic/chat.d.ts +18 -1
- package/dist/llm/anthropic/chat.js +123 -55
- package/dist/llm/anthropic/serializer.d.ts +2 -0
- package/dist/llm/anthropic/serializer.js +81 -9
- package/dist/llm/aws/chat-anthropic.d.ts +17 -0
- package/dist/llm/aws/chat-anthropic.js +126 -26
- package/dist/llm/aws/chat-bedrock.d.ts +28 -1
- package/dist/llm/aws/chat-bedrock.js +161 -34
- package/dist/llm/aws/serializer.d.ts +13 -1
- package/dist/llm/aws/serializer.js +56 -17
- package/dist/llm/azure/chat.d.ts +53 -2
- package/dist/llm/azure/chat.js +366 -54
- package/dist/llm/base.d.ts +2 -0
- package/dist/llm/browser-use/chat.d.ts +40 -0
- package/dist/llm/browser-use/chat.js +305 -0
- package/dist/llm/browser-use/index.d.ts +1 -0
- package/dist/llm/browser-use/index.js +1 -0
- package/dist/llm/cerebras/chat.d.ts +39 -0
- package/dist/llm/cerebras/chat.js +178 -0
- package/dist/llm/cerebras/index.d.ts +2 -0
- package/dist/llm/cerebras/index.js +2 -0
- package/dist/llm/cerebras/serializer.d.ts +7 -0
- package/dist/llm/cerebras/serializer.js +82 -0
- package/dist/llm/deepseek/chat.d.ts +19 -2
- package/dist/llm/deepseek/chat.js +138 -25
- package/dist/llm/google/chat.d.ts +46 -2
- package/dist/llm/google/chat.js +267 -64
- package/dist/llm/google/serializer.d.ts +9 -1
- package/dist/llm/google/serializer.js +141 -34
- package/dist/llm/groq/chat.d.ts +21 -2
- package/dist/llm/groq/chat.js +125 -26
- package/dist/llm/groq/parser.js +3 -1
- package/dist/llm/mistral/chat.d.ts +43 -0
- package/dist/llm/mistral/chat.js +154 -0
- package/dist/llm/mistral/index.d.ts +2 -0
- package/dist/llm/mistral/index.js +2 -0
- package/dist/llm/mistral/schema.d.ts +8 -0
- package/dist/llm/mistral/schema.js +27 -0
- package/dist/llm/models.d.ts +2 -0
- package/dist/llm/models.js +317 -0
- package/dist/llm/ollama/chat.d.ts +13 -1
- package/dist/llm/ollama/chat.js +110 -19
- package/dist/llm/ollama/serializer.d.ts +1 -0
- package/dist/llm/ollama/serializer.js +34 -12
- package/dist/llm/openai/chat.d.ts +16 -0
- package/dist/llm/openai/chat.js +94 -44
- package/dist/llm/openai/like.d.ts +5 -3
- package/dist/llm/openai/like.js +7 -3
- package/dist/llm/openai/responses-serializer.d.ts +18 -0
- package/dist/llm/openai/responses-serializer.js +72 -0
- package/dist/llm/openrouter/chat.d.ts +28 -2
- package/dist/llm/openrouter/chat.js +115 -29
- package/dist/llm/schema.d.ts +11 -1
- package/dist/llm/schema.js +81 -1
- package/dist/llm/vercel/chat.d.ts +50 -0
- package/dist/llm/vercel/chat.js +276 -0
- package/dist/llm/vercel/index.d.ts +1 -0
- package/dist/llm/vercel/index.js +1 -0
- package/dist/llm/vercel/serializer.d.ts +5 -0
- package/dist/llm/vercel/serializer.js +7 -0
- package/dist/llm/views.d.ts +2 -1
- package/dist/llm/views.js +3 -1
- package/dist/logging-config.d.ts +2 -0
- package/dist/logging-config.js +82 -29
- package/dist/mcp/client.d.ts +10 -5
- package/dist/mcp/client.js +14 -9
- package/dist/mcp/controller.d.ts +42 -3
- package/dist/mcp/controller.js +56 -31
- package/dist/mcp/server.d.ts +14 -0
- package/dist/mcp/server.js +255 -52
- package/dist/observability.js +10 -4
- package/dist/sandbox/index.d.ts +2 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/sandbox/sandbox.d.ts +19 -0
- package/dist/sandbox/sandbox.js +140 -0
- package/dist/sandbox/views.d.ts +67 -0
- package/dist/sandbox/views.js +121 -0
- package/dist/skill-cli/index.d.ts +3 -0
- package/dist/skill-cli/index.js +3 -0
- package/dist/skill-cli/protocol.d.ts +30 -0
- package/dist/skill-cli/protocol.js +48 -0
- package/dist/skill-cli/server.d.ts +11 -0
- package/dist/skill-cli/server.js +85 -0
- package/dist/skill-cli/sessions.d.ts +24 -0
- package/dist/skill-cli/sessions.js +47 -0
- package/dist/skills/index.d.ts +3 -0
- package/dist/skills/index.js +3 -0
- package/dist/skills/service.d.ts +27 -0
- package/dist/skills/service.js +266 -0
- package/dist/skills/utils.d.ts +6 -0
- package/dist/skills/utils.js +53 -0
- package/dist/skills/views.d.ts +40 -0
- package/dist/skills/views.js +10 -0
- package/dist/sync/auth.js +8 -3
- package/dist/sync/service.d.ts +6 -6
- package/dist/sync/service.js +54 -89
- package/dist/telemetry/views.d.ts +20 -6
- package/dist/telemetry/views.js +23 -5
- package/dist/tokens/custom-pricing.d.ts +2 -0
- package/dist/tokens/custom-pricing.js +22 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/mappings.d.ts +1 -0
- package/dist/tokens/mappings.js +3 -0
- package/dist/tokens/service.js +27 -8
- package/dist/tools/extraction/index.d.ts +2 -0
- package/dist/tools/extraction/index.js +2 -0
- package/dist/tools/extraction/schema-utils.d.ts +6 -0
- package/dist/tools/extraction/schema-utils.js +237 -0
- package/dist/tools/extraction/views.d.ts +7 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/registry/index.d.ts +2 -0
- package/dist/tools/registry/index.js +2 -0
- package/dist/tools/registry/service.d.ts +1 -0
- package/dist/tools/registry/service.js +1 -0
- package/dist/tools/registry/views.d.ts +1 -0
- package/dist/tools/registry/views.js +1 -0
- package/dist/tools/service.d.ts +2 -0
- package/dist/tools/service.js +1 -0
- package/dist/tools/utils.d.ts +2 -0
- package/dist/tools/utils.js +57 -0
- package/dist/tools/views.d.ts +1 -0
- package/dist/tools/views.js +1 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +70 -3
- package/package.json +87 -26
- package/dist/dom/playground/process-dom.js +0 -5
- package/dist/dom/playground/test-accessibility.d.ts +0 -44
- package/dist/dom/playground/test-accessibility.js +0 -111
- /package/dist/{dom/playground/process-dom.d.ts → tools/extraction/views.js} +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { BrowserConnectedEvent, BrowserStopEvent, BrowserErrorEvent, BrowserStartEvent, BrowserStoppedEvent, } from '../events.js';
|
|
4
|
+
import { BaseWatchdog } from './base.js';
|
|
5
|
+
const toIsoFromEpochSeconds = (value) => {
|
|
6
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
7
|
+
return new Date().toISOString();
|
|
8
|
+
}
|
|
9
|
+
return new Date(value * 1000).toISOString();
|
|
10
|
+
};
|
|
11
|
+
const normalizeHeaders = (input) => {
|
|
12
|
+
if (!input) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(input)) {
|
|
16
|
+
const out = {};
|
|
17
|
+
for (const header of input) {
|
|
18
|
+
if (header &&
|
|
19
|
+
typeof header === 'object' &&
|
|
20
|
+
'name' in header &&
|
|
21
|
+
'value' in header) {
|
|
22
|
+
out[String(header.name).toLowerCase()] = String(header.value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
if (typeof input === 'object') {
|
|
28
|
+
return Object.fromEntries(Object.entries(input).map(([key, value]) => [
|
|
29
|
+
key.toLowerCase(),
|
|
30
|
+
String(value),
|
|
31
|
+
]));
|
|
32
|
+
}
|
|
33
|
+
return {};
|
|
34
|
+
};
|
|
35
|
+
export class HarRecordingWatchdog extends BaseWatchdog {
|
|
36
|
+
static LISTENS_TO = [
|
|
37
|
+
BrowserStartEvent,
|
|
38
|
+
BrowserConnectedEvent,
|
|
39
|
+
BrowserStopEvent,
|
|
40
|
+
BrowserStoppedEvent,
|
|
41
|
+
];
|
|
42
|
+
_harPath = null;
|
|
43
|
+
_cdpSession = null;
|
|
44
|
+
_listeners = [];
|
|
45
|
+
_entries = new Map();
|
|
46
|
+
async on_BrowserStartEvent() {
|
|
47
|
+
const resolvedPath = this._resolveAndPrepareHarPath();
|
|
48
|
+
if (!resolvedPath) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this._harPath = resolvedPath;
|
|
52
|
+
}
|
|
53
|
+
async on_BrowserConnectedEvent() {
|
|
54
|
+
await this._startCdpCaptureIfNeeded();
|
|
55
|
+
}
|
|
56
|
+
async on_BrowserStopEvent() {
|
|
57
|
+
await this._writeHarFallbackIfNeeded();
|
|
58
|
+
}
|
|
59
|
+
async on_BrowserStoppedEvent() {
|
|
60
|
+
const resolvedPath = this._harPath ?? this._resolveConfiguredHarPath();
|
|
61
|
+
if (!resolvedPath) {
|
|
62
|
+
await this._teardownCapture();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
66
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
67
|
+
error_type: 'HarRecordingMissing',
|
|
68
|
+
message: `HAR file was not created at ${resolvedPath}`,
|
|
69
|
+
details: {
|
|
70
|
+
record_har_path: resolvedPath,
|
|
71
|
+
},
|
|
72
|
+
}));
|
|
73
|
+
await this._teardownCapture();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const stat = fs.statSync(resolvedPath);
|
|
78
|
+
if (stat.size === 0) {
|
|
79
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
80
|
+
error_type: 'HarRecordingEmpty',
|
|
81
|
+
message: `HAR file is empty at ${resolvedPath}`,
|
|
82
|
+
details: {
|
|
83
|
+
record_har_path: resolvedPath,
|
|
84
|
+
},
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
90
|
+
error_type: 'HarRecordingStatFailed',
|
|
91
|
+
message: `Failed to inspect HAR file: ${error.message}`,
|
|
92
|
+
details: {
|
|
93
|
+
record_har_path: resolvedPath,
|
|
94
|
+
},
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await this._teardownCapture();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
onDetached() {
|
|
102
|
+
void this._teardownCapture();
|
|
103
|
+
}
|
|
104
|
+
_resolveConfiguredHarPath() {
|
|
105
|
+
const configuredPath = this.browser_session.browser_profile.config.record_har_path;
|
|
106
|
+
if (typeof configuredPath !== 'string' || configuredPath.trim() === '') {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return path.resolve(configuredPath);
|
|
110
|
+
}
|
|
111
|
+
_resolveAndPrepareHarPath() {
|
|
112
|
+
const resolvedPath = this._resolveConfiguredHarPath();
|
|
113
|
+
if (!resolvedPath) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
|
117
|
+
this.browser_session.browser_profile.config.record_har_path = resolvedPath;
|
|
118
|
+
return resolvedPath;
|
|
119
|
+
}
|
|
120
|
+
async _startCdpCaptureIfNeeded() {
|
|
121
|
+
if (!this._harPath || this._cdpSession) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const cdpSession = (await this.browser_session.get_or_create_cdp_session(null));
|
|
126
|
+
this._cdpSession = cdpSession;
|
|
127
|
+
await cdpSession.send?.('Network.enable');
|
|
128
|
+
const onRequestWillBeSent = (payload) => {
|
|
129
|
+
const requestId = String(payload?.requestId ?? '');
|
|
130
|
+
const request = payload?.request ?? {};
|
|
131
|
+
const url = String(request?.url ?? '');
|
|
132
|
+
if (!requestId || !url.toLowerCase().startsWith('https://')) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const tsRequest = typeof payload?.timestamp === 'number' ? payload.timestamp : null;
|
|
136
|
+
this._entries.set(requestId, {
|
|
137
|
+
request_id: requestId,
|
|
138
|
+
started_date_time: toIsoFromEpochSeconds(typeof payload?.wallTime === 'number'
|
|
139
|
+
? payload.wallTime
|
|
140
|
+
: Date.now() / 1000),
|
|
141
|
+
method: String(request?.method ?? 'GET'),
|
|
142
|
+
url,
|
|
143
|
+
request_headers: normalizeHeaders(request?.headers),
|
|
144
|
+
status: 0,
|
|
145
|
+
status_text: '',
|
|
146
|
+
response_headers: {},
|
|
147
|
+
mime_type: '',
|
|
148
|
+
failed: false,
|
|
149
|
+
ts_request: tsRequest,
|
|
150
|
+
ts_response: null,
|
|
151
|
+
ts_finished: null,
|
|
152
|
+
encoded_data_length: null,
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const onResponseReceived = (payload) => {
|
|
156
|
+
const requestId = String(payload?.requestId ?? '');
|
|
157
|
+
const entry = this._entries.get(requestId);
|
|
158
|
+
if (!entry) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const response = payload?.response ?? {};
|
|
162
|
+
entry.status =
|
|
163
|
+
typeof response?.status === 'number'
|
|
164
|
+
? response.status
|
|
165
|
+
: Number(response?.status ?? 0);
|
|
166
|
+
entry.status_text = String(response?.statusText ?? '');
|
|
167
|
+
entry.response_headers = normalizeHeaders(response?.headers);
|
|
168
|
+
entry.mime_type = String(response?.mimeType ?? '');
|
|
169
|
+
entry.ts_response =
|
|
170
|
+
typeof payload?.timestamp === 'number' ? payload.timestamp : null;
|
|
171
|
+
};
|
|
172
|
+
const onLoadingFinished = (payload) => {
|
|
173
|
+
const requestId = String(payload?.requestId ?? '');
|
|
174
|
+
const entry = this._entries.get(requestId);
|
|
175
|
+
if (!entry) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
entry.ts_finished =
|
|
179
|
+
typeof payload?.timestamp === 'number' ? payload.timestamp : null;
|
|
180
|
+
entry.encoded_data_length =
|
|
181
|
+
typeof payload?.encodedDataLength === 'number'
|
|
182
|
+
? payload.encodedDataLength
|
|
183
|
+
: null;
|
|
184
|
+
};
|
|
185
|
+
const onLoadingFailed = (payload) => {
|
|
186
|
+
const requestId = String(payload?.requestId ?? '');
|
|
187
|
+
const entry = this._entries.get(requestId);
|
|
188
|
+
if (!entry) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
entry.failed = true;
|
|
192
|
+
};
|
|
193
|
+
cdpSession.on?.('Network.requestWillBeSent', onRequestWillBeSent);
|
|
194
|
+
cdpSession.on?.('Network.responseReceived', onResponseReceived);
|
|
195
|
+
cdpSession.on?.('Network.loadingFinished', onLoadingFinished);
|
|
196
|
+
cdpSession.on?.('Network.loadingFailed', onLoadingFailed);
|
|
197
|
+
this._listeners = [
|
|
198
|
+
{ event: 'Network.requestWillBeSent', handler: onRequestWillBeSent },
|
|
199
|
+
{ event: 'Network.responseReceived', handler: onResponseReceived },
|
|
200
|
+
{ event: 'Network.loadingFinished', handler: onLoadingFinished },
|
|
201
|
+
{ event: 'Network.loadingFailed', handler: onLoadingFailed },
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
206
|
+
error_type: 'HarCaptureUnavailable',
|
|
207
|
+
message: `CDP HAR capture is unavailable: ${error.message}`,
|
|
208
|
+
details: {
|
|
209
|
+
record_har_path: this._harPath,
|
|
210
|
+
},
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async _writeHarFallbackIfNeeded() {
|
|
215
|
+
const resolvedPath = this._harPath ?? this._resolveConfiguredHarPath();
|
|
216
|
+
if (!resolvedPath) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (fs.existsSync(resolvedPath)) {
|
|
220
|
+
try {
|
|
221
|
+
if (fs.statSync(resolvedPath).size > 0) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Continue into fallback writer.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const entries = [...this._entries.values()];
|
|
230
|
+
const harEntries = entries.map((entry) => {
|
|
231
|
+
const waitMs = entry.ts_request != null && entry.ts_response != null
|
|
232
|
+
? Math.max(0, Math.round((entry.ts_response - entry.ts_request) * 1000))
|
|
233
|
+
: 0;
|
|
234
|
+
const receiveMs = entry.ts_response != null && entry.ts_finished != null
|
|
235
|
+
? Math.max(0, Math.round((entry.ts_finished - entry.ts_response) * 1000))
|
|
236
|
+
: 0;
|
|
237
|
+
const totalMs = waitMs + receiveMs;
|
|
238
|
+
return {
|
|
239
|
+
startedDateTime: entry.started_date_time,
|
|
240
|
+
time: totalMs,
|
|
241
|
+
request: {
|
|
242
|
+
method: entry.method,
|
|
243
|
+
url: entry.url,
|
|
244
|
+
httpVersion: 'HTTP/1.1',
|
|
245
|
+
headers: Object.entries(entry.request_headers).map(([name, value]) => ({
|
|
246
|
+
name,
|
|
247
|
+
value,
|
|
248
|
+
})),
|
|
249
|
+
queryString: [],
|
|
250
|
+
cookies: [],
|
|
251
|
+
headersSize: -1,
|
|
252
|
+
bodySize: -1,
|
|
253
|
+
},
|
|
254
|
+
response: {
|
|
255
|
+
status: entry.status,
|
|
256
|
+
statusText: entry.status_text,
|
|
257
|
+
httpVersion: 'HTTP/1.1',
|
|
258
|
+
headers: Object.entries(entry.response_headers).map(([name, value]) => ({
|
|
259
|
+
name,
|
|
260
|
+
value,
|
|
261
|
+
})),
|
|
262
|
+
cookies: [],
|
|
263
|
+
content: {
|
|
264
|
+
size: entry.encoded_data_length ?? -1,
|
|
265
|
+
mimeType: entry.mime_type,
|
|
266
|
+
},
|
|
267
|
+
redirectURL: '',
|
|
268
|
+
headersSize: -1,
|
|
269
|
+
bodySize: entry.encoded_data_length ?? -1,
|
|
270
|
+
},
|
|
271
|
+
cache: {},
|
|
272
|
+
timings: {
|
|
273
|
+
dns: -1,
|
|
274
|
+
connect: -1,
|
|
275
|
+
ssl: -1,
|
|
276
|
+
send: 0,
|
|
277
|
+
wait: waitMs,
|
|
278
|
+
receive: receiveMs,
|
|
279
|
+
},
|
|
280
|
+
_failed: entry.failed,
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
const harObject = {
|
|
284
|
+
log: {
|
|
285
|
+
version: '1.2',
|
|
286
|
+
creator: {
|
|
287
|
+
name: 'browser-use-node',
|
|
288
|
+
version: 'dev',
|
|
289
|
+
},
|
|
290
|
+
pages: [],
|
|
291
|
+
entries: harEntries,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
const tempPath = `${resolvedPath}.tmp`;
|
|
295
|
+
fs.writeFileSync(tempPath, JSON.stringify(harObject, null, 2), 'utf-8');
|
|
296
|
+
fs.renameSync(tempPath, resolvedPath);
|
|
297
|
+
}
|
|
298
|
+
async _teardownCapture() {
|
|
299
|
+
if (!this._cdpSession) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
for (const listener of this._listeners) {
|
|
303
|
+
this._cdpSession.off?.(listener.event, listener.handler);
|
|
304
|
+
}
|
|
305
|
+
this._listeners = [];
|
|
306
|
+
try {
|
|
307
|
+
await this._cdpSession.detach?.();
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Ignore CDP detach errors during shutdown.
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
this._cdpSession = null;
|
|
314
|
+
this._entries.clear();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './base.js';
|
|
2
|
+
export * from './aboutblank-watchdog.js';
|
|
3
|
+
export * from './cdp-session-watchdog.js';
|
|
4
|
+
export * from './crash-watchdog.js';
|
|
5
|
+
export * from './default-action-watchdog.js';
|
|
6
|
+
export * from './dom-watchdog.js';
|
|
7
|
+
export * from './downloads-watchdog.js';
|
|
8
|
+
export * from './har-recording-watchdog.js';
|
|
9
|
+
export * from './local-browser-watchdog.js';
|
|
10
|
+
export * from './permissions-watchdog.js';
|
|
11
|
+
export * from './popups-watchdog.js';
|
|
12
|
+
export * from './recording-watchdog.js';
|
|
13
|
+
export * from './screenshot-watchdog.js';
|
|
14
|
+
export * from './security-watchdog.js';
|
|
15
|
+
export * from './storage-state-watchdog.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './base.js';
|
|
2
|
+
export * from './aboutblank-watchdog.js';
|
|
3
|
+
export * from './cdp-session-watchdog.js';
|
|
4
|
+
export * from './crash-watchdog.js';
|
|
5
|
+
export * from './default-action-watchdog.js';
|
|
6
|
+
export * from './dom-watchdog.js';
|
|
7
|
+
export * from './downloads-watchdog.js';
|
|
8
|
+
export * from './har-recording-watchdog.js';
|
|
9
|
+
export * from './local-browser-watchdog.js';
|
|
10
|
+
export * from './permissions-watchdog.js';
|
|
11
|
+
export * from './popups-watchdog.js';
|
|
12
|
+
export * from './recording-watchdog.js';
|
|
13
|
+
export * from './screenshot-watchdog.js';
|
|
14
|
+
export * from './security-watchdog.js';
|
|
15
|
+
export * from './storage-state-watchdog.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BrowserKillEvent, BrowserLaunchEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export declare class LocalBrowserWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO: (typeof BrowserLaunchEvent | typeof BrowserKillEvent)[];
|
|
5
|
+
on_BrowserLaunchEvent(): Promise<{
|
|
6
|
+
cdp_url: string;
|
|
7
|
+
}>;
|
|
8
|
+
on_BrowserKillEvent(): Promise<void>;
|
|
9
|
+
on_BrowserStopEvent(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BrowserKillEvent, BrowserLaunchEvent, BrowserStopEvent, } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export class LocalBrowserWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO = [
|
|
5
|
+
BrowserLaunchEvent,
|
|
6
|
+
BrowserKillEvent,
|
|
7
|
+
BrowserStopEvent,
|
|
8
|
+
];
|
|
9
|
+
async on_BrowserLaunchEvent() {
|
|
10
|
+
await this.browser_session.start();
|
|
11
|
+
return {
|
|
12
|
+
cdp_url: this.browser_session.cdp_url ??
|
|
13
|
+
this.browser_session.wss_url ??
|
|
14
|
+
'playwright',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async on_BrowserKillEvent() {
|
|
18
|
+
if (this.browser_session.is_stopping) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
await this.browser_session.kill();
|
|
22
|
+
}
|
|
23
|
+
on_BrowserStopEvent() {
|
|
24
|
+
if (!this.browser_session._owns_browser_resources) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Fire-and-forget to avoid blocking BrowserStopEvent handler completion.
|
|
28
|
+
void this.event_bus.dispatch(new BrowserKillEvent()).catch(() => {
|
|
29
|
+
// Ignore shutdown re-entrancy errors during stop lifecycle.
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BrowserConnectedEvent, BrowserErrorEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export declare class PermissionsWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO: (typeof BrowserConnectedEvent)[];
|
|
5
|
+
static EMITS: (typeof BrowserErrorEvent)[];
|
|
6
|
+
on_BrowserConnectedEvent(): Promise<void>;
|
|
7
|
+
private _grantPermissionsViaCdp;
|
|
8
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { BrowserConnectedEvent, BrowserErrorEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export class PermissionsWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO = [BrowserConnectedEvent];
|
|
5
|
+
static EMITS = [BrowserErrorEvent];
|
|
6
|
+
async on_BrowserConnectedEvent() {
|
|
7
|
+
const permissions = this.browser_session.browser_profile.config.permissions;
|
|
8
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
let cdpError = null;
|
|
12
|
+
try {
|
|
13
|
+
const grantedWithCdp = await this._grantPermissionsViaCdp(permissions);
|
|
14
|
+
if (grantedWithCdp) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
cdpError = error;
|
|
20
|
+
}
|
|
21
|
+
const context = this.browser_session.browser_context;
|
|
22
|
+
if (!context?.grantPermissions) {
|
|
23
|
+
if (!cdpError) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
27
|
+
error_type: 'PermissionsWatchdogError',
|
|
28
|
+
message: cdpError.message || 'Failed to grant permissions via CDP',
|
|
29
|
+
details: {
|
|
30
|
+
permissions,
|
|
31
|
+
mode: 'cdp',
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
await context.grantPermissions(permissions);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const message = error.message ?? 'Failed to grant permissions';
|
|
41
|
+
await this.event_bus.dispatch(new BrowserErrorEvent({
|
|
42
|
+
error_type: 'PermissionsWatchdogError',
|
|
43
|
+
message,
|
|
44
|
+
details: {
|
|
45
|
+
permissions,
|
|
46
|
+
cdp_error: cdpError?.message ?? null,
|
|
47
|
+
mode: 'playwright',
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async _grantPermissionsViaCdp(permissions) {
|
|
53
|
+
const browser = this.browser_session.browser;
|
|
54
|
+
if (!browser?.newBrowserCDPSession) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const cdpSession = await browser.newBrowserCDPSession();
|
|
58
|
+
try {
|
|
59
|
+
await cdpSession.send?.('Browser.grantPermissions', {
|
|
60
|
+
permissions,
|
|
61
|
+
});
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
try {
|
|
66
|
+
await cdpSession.detach?.();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Ignore detach failures.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BrowserStoppedEvent, TabCreatedEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export declare class PopupsWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO: (typeof BrowserStoppedEvent | typeof TabCreatedEvent)[];
|
|
5
|
+
private _dialogListenersRegistered;
|
|
6
|
+
private _cdpDialogSessions;
|
|
7
|
+
on_TabCreatedEvent(event: TabCreatedEvent): Promise<void>;
|
|
8
|
+
on_BrowserStoppedEvent(): Promise<void>;
|
|
9
|
+
protected onDetached(): void;
|
|
10
|
+
private _attachCdpDialogHandler;
|
|
11
|
+
private _detachCdpDialogHandlers;
|
|
12
|
+
private _handleJavascriptDialog;
|
|
13
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BrowserStoppedEvent, TabCreatedEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export class PopupsWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO = [TabCreatedEvent, BrowserStoppedEvent];
|
|
5
|
+
_dialogListenersRegistered = new Set();
|
|
6
|
+
_cdpDialogSessions = new Map();
|
|
7
|
+
async on_TabCreatedEvent(event) {
|
|
8
|
+
const page = (await this.browser_session.get_current_page());
|
|
9
|
+
if (!page) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const attachDialogHandler = this.browser_session
|
|
13
|
+
?._attachDialogHandler;
|
|
14
|
+
if (typeof attachDialogHandler === 'function') {
|
|
15
|
+
attachDialogHandler.call(this.browser_session, page);
|
|
16
|
+
}
|
|
17
|
+
await this._attachCdpDialogHandler(event.target_id, page);
|
|
18
|
+
}
|
|
19
|
+
async on_BrowserStoppedEvent() {
|
|
20
|
+
await this._detachCdpDialogHandlers();
|
|
21
|
+
}
|
|
22
|
+
onDetached() {
|
|
23
|
+
void this._detachCdpDialogHandlers();
|
|
24
|
+
}
|
|
25
|
+
async _attachCdpDialogHandler(targetId, page) {
|
|
26
|
+
if (this._dialogListenersRegistered.has(targetId)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const session = (await this.browser_session.get_or_create_cdp_session(page));
|
|
31
|
+
await session.send?.('Page.enable');
|
|
32
|
+
const handler = (payload) => {
|
|
33
|
+
void this._handleJavascriptDialog(payload, session);
|
|
34
|
+
};
|
|
35
|
+
session.on?.('Page.javascriptDialogOpening', handler);
|
|
36
|
+
this._dialogListenersRegistered.add(targetId);
|
|
37
|
+
this._cdpDialogSessions.set(targetId, {
|
|
38
|
+
session,
|
|
39
|
+
handler,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this.browser_session.logger.debug(`[PopupsWatchdog] Failed to attach CDP dialog handler: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async _detachCdpDialogHandlers() {
|
|
47
|
+
for (const [targetId, binding] of [...this._cdpDialogSessions.entries()]) {
|
|
48
|
+
binding.session.off?.('Page.javascriptDialogOpening', binding.handler);
|
|
49
|
+
try {
|
|
50
|
+
await binding.session.detach?.();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Ignore detach failures during cleanup.
|
|
54
|
+
}
|
|
55
|
+
this._cdpDialogSessions.delete(targetId);
|
|
56
|
+
}
|
|
57
|
+
this._dialogListenersRegistered.clear();
|
|
58
|
+
}
|
|
59
|
+
async _handleJavascriptDialog(payload, session) {
|
|
60
|
+
const dialogType = typeof payload?.type === 'string' ? payload.type : 'alert';
|
|
61
|
+
const message = typeof payload?.message === 'string' ? payload.message : '';
|
|
62
|
+
const shouldAccept = ['alert', 'confirm', 'beforeunload'].includes(dialogType);
|
|
63
|
+
const captureClosedPopupMessage = this.browser_session
|
|
64
|
+
?._captureClosedPopupMessage;
|
|
65
|
+
if (typeof captureClosedPopupMessage === 'function' && message) {
|
|
66
|
+
captureClosedPopupMessage.call(this.browser_session, dialogType, message);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
await session.send?.('Page.handleJavaScriptDialog', {
|
|
70
|
+
accept: shouldAccept,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.browser_session.logger.debug(`[PopupsWatchdog] Failed to handle JavaScript dialog: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AgentFocusChangedEvent, BrowserConnectedEvent, BrowserStopEvent, BrowserStoppedEvent, TabCreatedEvent } from '../events.js';
|
|
2
|
+
import { BaseWatchdog } from './base.js';
|
|
3
|
+
export declare class RecordingWatchdog extends BaseWatchdog {
|
|
4
|
+
static LISTENS_TO: (typeof BrowserStopEvent | typeof BrowserConnectedEvent | typeof BrowserStoppedEvent | typeof TabCreatedEvent)[];
|
|
5
|
+
private _traceStarted;
|
|
6
|
+
private _videoCloseListeners;
|
|
7
|
+
private _cdpScreencastSession;
|
|
8
|
+
private _cdpScreencastHandler;
|
|
9
|
+
private _cdpScreencastPath;
|
|
10
|
+
private _cdpScreencastStream;
|
|
11
|
+
on_BrowserConnectedEvent(): Promise<void>;
|
|
12
|
+
on_BrowserStopEvent(): Promise<void>;
|
|
13
|
+
on_BrowserStoppedEvent(): Promise<void>;
|
|
14
|
+
on_AgentFocusChangedEvent(event: AgentFocusChangedEvent): Promise<void>;
|
|
15
|
+
on_TabCreatedEvent(): Promise<void>;
|
|
16
|
+
protected onDetached(): void;
|
|
17
|
+
private _prepareVideoDirectory;
|
|
18
|
+
private _startTracingIfConfigured;
|
|
19
|
+
private _stopTracingIfStarted;
|
|
20
|
+
private _attachVideoListenersToKnownPages;
|
|
21
|
+
private _attachVideoListener;
|
|
22
|
+
private _detachVideoListeners;
|
|
23
|
+
private _getKnownPages;
|
|
24
|
+
private _captureVideoArtifact;
|
|
25
|
+
private _startCdpScreencastIfConfigured;
|
|
26
|
+
private _stopCdpScreencastIfStarted;
|
|
27
|
+
}
|