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.
Files changed (259) hide show
  1. package/README.md +295 -686
  2. package/dist/actor/element.d.ts +19 -0
  3. package/dist/actor/element.js +46 -0
  4. package/dist/actor/index.d.ts +4 -0
  5. package/dist/actor/index.js +4 -0
  6. package/dist/actor/mouse.d.ts +19 -0
  7. package/dist/actor/mouse.js +39 -0
  8. package/dist/actor/page.d.ts +29 -0
  9. package/dist/actor/page.js +88 -0
  10. package/dist/actor/utils.d.ts +4 -0
  11. package/dist/actor/utils.js +35 -0
  12. package/dist/agent/cloud-events.d.ts +18 -0
  13. package/dist/agent/cloud-events.js +65 -2
  14. package/dist/agent/gif.d.ts +1 -0
  15. package/dist/agent/gif.js +24 -2
  16. package/dist/agent/judge.d.ts +17 -0
  17. package/dist/agent/judge.js +197 -0
  18. package/dist/agent/message-manager/service.d.ts +12 -4
  19. package/dist/agent/message-manager/service.js +205 -39
  20. package/dist/agent/message-manager/utils.js +0 -1
  21. package/dist/agent/message-manager/views.d.ts +4 -0
  22. package/dist/agent/message-manager/views.js +11 -7
  23. package/dist/agent/prompts.d.ts +24 -3
  24. package/dist/agent/prompts.js +274 -59
  25. package/dist/agent/service.d.ts +99 -41
  26. package/dist/agent/service.js +2266 -472
  27. package/dist/agent/variable-detector.d.ts +12 -0
  28. package/dist/agent/variable-detector.js +211 -0
  29. package/dist/agent/views.d.ts +237 -18
  30. package/dist/agent/views.js +446 -33
  31. package/dist/browser/cloud/cloud.d.ts +20 -0
  32. package/dist/browser/cloud/cloud.js +129 -0
  33. package/dist/browser/cloud/index.d.ts +2 -0
  34. package/dist/browser/cloud/index.js +2 -0
  35. package/dist/browser/cloud/views.d.ts +41 -0
  36. package/dist/browser/cloud/views.js +35 -0
  37. package/dist/browser/events.d.ts +345 -0
  38. package/dist/browser/events.js +566 -0
  39. package/dist/browser/extensions.js +17 -17
  40. package/dist/browser/index.d.ts +4 -0
  41. package/dist/browser/index.js +4 -0
  42. package/dist/browser/profile.d.ts +8 -2
  43. package/dist/browser/profile.js +79 -12
  44. package/dist/browser/session-manager.d.ts +85 -0
  45. package/dist/browser/session-manager.js +208 -0
  46. package/dist/browser/session.d.ts +100 -8
  47. package/dist/browser/session.js +1097 -58
  48. package/dist/browser/types.d.ts +0 -2
  49. package/dist/browser/views.d.ts +39 -0
  50. package/dist/browser/views.js +32 -0
  51. package/dist/browser/watchdogs/aboutblank-watchdog.d.ts +12 -0
  52. package/dist/browser/watchdogs/aboutblank-watchdog.js +131 -0
  53. package/dist/browser/watchdogs/base.d.ts +21 -0
  54. package/dist/browser/watchdogs/base.js +81 -0
  55. package/dist/browser/watchdogs/cdp-session-watchdog.d.ts +14 -0
  56. package/dist/browser/watchdogs/cdp-session-watchdog.js +177 -0
  57. package/dist/browser/watchdogs/crash-watchdog.d.ts +38 -0
  58. package/dist/browser/watchdogs/crash-watchdog.js +296 -0
  59. package/dist/browser/watchdogs/default-action-watchdog.d.ts +49 -0
  60. package/dist/browser/watchdogs/default-action-watchdog.js +212 -0
  61. package/dist/browser/watchdogs/dom-watchdog.d.ts +8 -0
  62. package/dist/browser/watchdogs/dom-watchdog.js +31 -0
  63. package/dist/browser/watchdogs/downloads-watchdog.d.ts +77 -0
  64. package/dist/browser/watchdogs/downloads-watchdog.js +409 -0
  65. package/dist/browser/watchdogs/har-recording-watchdog.d.ts +19 -0
  66. package/dist/browser/watchdogs/har-recording-watchdog.js +317 -0
  67. package/dist/browser/watchdogs/index.d.ts +15 -0
  68. package/dist/browser/watchdogs/index.js +15 -0
  69. package/dist/browser/watchdogs/local-browser-watchdog.d.ts +10 -0
  70. package/dist/browser/watchdogs/local-browser-watchdog.js +32 -0
  71. package/dist/browser/watchdogs/permissions-watchdog.d.ts +8 -0
  72. package/dist/browser/watchdogs/permissions-watchdog.js +73 -0
  73. package/dist/browser/watchdogs/popups-watchdog.d.ts +13 -0
  74. package/dist/browser/watchdogs/popups-watchdog.js +77 -0
  75. package/dist/browser/watchdogs/recording-watchdog.d.ts +27 -0
  76. package/dist/browser/watchdogs/recording-watchdog.js +249 -0
  77. package/dist/browser/watchdogs/screenshot-watchdog.d.ts +6 -0
  78. package/dist/browser/watchdogs/screenshot-watchdog.js +13 -0
  79. package/dist/browser/watchdogs/security-watchdog.d.ts +10 -0
  80. package/dist/browser/watchdogs/security-watchdog.js +84 -0
  81. package/dist/browser/watchdogs/storage-state-watchdog.d.ts +24 -0
  82. package/dist/browser/watchdogs/storage-state-watchdog.js +288 -0
  83. package/dist/cli.d.ts +7 -2
  84. package/dist/cli.js +182 -25
  85. package/dist/code-use/formatting.d.ts +3 -0
  86. package/dist/code-use/formatting.js +18 -0
  87. package/dist/code-use/index.d.ts +6 -0
  88. package/dist/code-use/index.js +6 -0
  89. package/dist/code-use/namespace.d.ts +5 -0
  90. package/dist/code-use/namespace.js +81 -0
  91. package/dist/code-use/notebook-export.d.ts +3 -0
  92. package/dist/code-use/notebook-export.js +56 -0
  93. package/dist/code-use/service.d.ts +24 -0
  94. package/dist/code-use/service.js +104 -0
  95. package/dist/code-use/utils.d.ts +4 -0
  96. package/dist/code-use/utils.js +98 -0
  97. package/dist/code-use/views.d.ts +108 -0
  98. package/dist/code-use/views.js +165 -0
  99. package/dist/config.d.ts +13 -0
  100. package/dist/config.js +69 -3
  101. package/dist/controller/registry/service.d.ts +10 -1
  102. package/dist/controller/registry/service.js +266 -10
  103. package/dist/controller/registry/views.d.ts +4 -1
  104. package/dist/controller/registry/views.js +25 -2
  105. package/dist/controller/service.d.ts +10 -1
  106. package/dist/controller/service.js +1807 -268
  107. package/dist/controller/views.d.ts +78 -155
  108. package/dist/controller/views.js +61 -12
  109. package/dist/dom/history-tree-processor/service.d.ts +5 -0
  110. package/dist/dom/history-tree-processor/service.js +169 -14
  111. package/dist/dom/history-tree-processor/view.d.ts +7 -1
  112. package/dist/dom/history-tree-processor/view.js +10 -1
  113. package/dist/dom/markdown-extractor.d.ts +37 -0
  114. package/dist/dom/markdown-extractor.js +345 -0
  115. package/dist/dom/service.d.ts +3 -1
  116. package/dist/dom/service.js +76 -0
  117. package/dist/dom/views.d.ts +1 -0
  118. package/dist/dom/views.js +45 -0
  119. package/dist/event-bus.d.ts +107 -7
  120. package/dist/event-bus.js +313 -10
  121. package/dist/exceptions.d.ts +0 -3
  122. package/dist/exceptions.js +0 -7
  123. package/dist/filesystem/file-system.d.ts +18 -0
  124. package/dist/filesystem/file-system.js +503 -42
  125. package/dist/index.d.ts +7 -0
  126. package/dist/index.js +6 -0
  127. package/dist/integrations/gmail/actions.d.ts +3 -3
  128. package/dist/integrations/gmail/actions.js +4 -4
  129. package/dist/llm/anthropic/chat.d.ts +18 -1
  130. package/dist/llm/anthropic/chat.js +123 -55
  131. package/dist/llm/anthropic/serializer.d.ts +2 -0
  132. package/dist/llm/anthropic/serializer.js +81 -9
  133. package/dist/llm/aws/chat-anthropic.d.ts +17 -0
  134. package/dist/llm/aws/chat-anthropic.js +126 -26
  135. package/dist/llm/aws/chat-bedrock.d.ts +28 -1
  136. package/dist/llm/aws/chat-bedrock.js +161 -34
  137. package/dist/llm/aws/serializer.d.ts +13 -1
  138. package/dist/llm/aws/serializer.js +56 -17
  139. package/dist/llm/azure/chat.d.ts +53 -2
  140. package/dist/llm/azure/chat.js +366 -54
  141. package/dist/llm/base.d.ts +2 -0
  142. package/dist/llm/browser-use/chat.d.ts +40 -0
  143. package/dist/llm/browser-use/chat.js +305 -0
  144. package/dist/llm/browser-use/index.d.ts +1 -0
  145. package/dist/llm/browser-use/index.js +1 -0
  146. package/dist/llm/cerebras/chat.d.ts +39 -0
  147. package/dist/llm/cerebras/chat.js +178 -0
  148. package/dist/llm/cerebras/index.d.ts +2 -0
  149. package/dist/llm/cerebras/index.js +2 -0
  150. package/dist/llm/cerebras/serializer.d.ts +7 -0
  151. package/dist/llm/cerebras/serializer.js +82 -0
  152. package/dist/llm/deepseek/chat.d.ts +19 -2
  153. package/dist/llm/deepseek/chat.js +138 -25
  154. package/dist/llm/google/chat.d.ts +46 -2
  155. package/dist/llm/google/chat.js +267 -64
  156. package/dist/llm/google/serializer.d.ts +9 -1
  157. package/dist/llm/google/serializer.js +141 -34
  158. package/dist/llm/groq/chat.d.ts +21 -2
  159. package/dist/llm/groq/chat.js +125 -26
  160. package/dist/llm/groq/parser.js +3 -1
  161. package/dist/llm/mistral/chat.d.ts +43 -0
  162. package/dist/llm/mistral/chat.js +154 -0
  163. package/dist/llm/mistral/index.d.ts +2 -0
  164. package/dist/llm/mistral/index.js +2 -0
  165. package/dist/llm/mistral/schema.d.ts +8 -0
  166. package/dist/llm/mistral/schema.js +27 -0
  167. package/dist/llm/models.d.ts +2 -0
  168. package/dist/llm/models.js +317 -0
  169. package/dist/llm/ollama/chat.d.ts +13 -1
  170. package/dist/llm/ollama/chat.js +110 -19
  171. package/dist/llm/ollama/serializer.d.ts +1 -0
  172. package/dist/llm/ollama/serializer.js +34 -12
  173. package/dist/llm/openai/chat.d.ts +16 -0
  174. package/dist/llm/openai/chat.js +94 -44
  175. package/dist/llm/openai/like.d.ts +5 -3
  176. package/dist/llm/openai/like.js +7 -3
  177. package/dist/llm/openai/responses-serializer.d.ts +18 -0
  178. package/dist/llm/openai/responses-serializer.js +72 -0
  179. package/dist/llm/openrouter/chat.d.ts +28 -2
  180. package/dist/llm/openrouter/chat.js +115 -29
  181. package/dist/llm/schema.d.ts +11 -1
  182. package/dist/llm/schema.js +81 -1
  183. package/dist/llm/vercel/chat.d.ts +50 -0
  184. package/dist/llm/vercel/chat.js +276 -0
  185. package/dist/llm/vercel/index.d.ts +1 -0
  186. package/dist/llm/vercel/index.js +1 -0
  187. package/dist/llm/vercel/serializer.d.ts +5 -0
  188. package/dist/llm/vercel/serializer.js +7 -0
  189. package/dist/llm/views.d.ts +2 -1
  190. package/dist/llm/views.js +3 -1
  191. package/dist/logging-config.d.ts +2 -0
  192. package/dist/logging-config.js +82 -29
  193. package/dist/mcp/client.d.ts +10 -5
  194. package/dist/mcp/client.js +14 -9
  195. package/dist/mcp/controller.d.ts +42 -3
  196. package/dist/mcp/controller.js +56 -31
  197. package/dist/mcp/server.d.ts +14 -0
  198. package/dist/mcp/server.js +255 -52
  199. package/dist/observability.js +10 -4
  200. package/dist/sandbox/index.d.ts +2 -0
  201. package/dist/sandbox/index.js +2 -0
  202. package/dist/sandbox/sandbox.d.ts +19 -0
  203. package/dist/sandbox/sandbox.js +140 -0
  204. package/dist/sandbox/views.d.ts +67 -0
  205. package/dist/sandbox/views.js +121 -0
  206. package/dist/skill-cli/index.d.ts +3 -0
  207. package/dist/skill-cli/index.js +3 -0
  208. package/dist/skill-cli/protocol.d.ts +30 -0
  209. package/dist/skill-cli/protocol.js +48 -0
  210. package/dist/skill-cli/server.d.ts +11 -0
  211. package/dist/skill-cli/server.js +85 -0
  212. package/dist/skill-cli/sessions.d.ts +24 -0
  213. package/dist/skill-cli/sessions.js +47 -0
  214. package/dist/skills/index.d.ts +3 -0
  215. package/dist/skills/index.js +3 -0
  216. package/dist/skills/service.d.ts +27 -0
  217. package/dist/skills/service.js +266 -0
  218. package/dist/skills/utils.d.ts +6 -0
  219. package/dist/skills/utils.js +53 -0
  220. package/dist/skills/views.d.ts +40 -0
  221. package/dist/skills/views.js +10 -0
  222. package/dist/sync/auth.js +8 -3
  223. package/dist/sync/service.d.ts +6 -6
  224. package/dist/sync/service.js +54 -89
  225. package/dist/telemetry/views.d.ts +20 -6
  226. package/dist/telemetry/views.js +23 -5
  227. package/dist/tokens/custom-pricing.d.ts +2 -0
  228. package/dist/tokens/custom-pricing.js +22 -0
  229. package/dist/tokens/index.d.ts +2 -0
  230. package/dist/tokens/index.js +2 -0
  231. package/dist/tokens/mappings.d.ts +1 -0
  232. package/dist/tokens/mappings.js +3 -0
  233. package/dist/tokens/service.js +27 -8
  234. package/dist/tools/extraction/index.d.ts +2 -0
  235. package/dist/tools/extraction/index.js +2 -0
  236. package/dist/tools/extraction/schema-utils.d.ts +6 -0
  237. package/dist/tools/extraction/schema-utils.js +237 -0
  238. package/dist/tools/extraction/views.d.ts +7 -0
  239. package/dist/tools/index.d.ts +5 -0
  240. package/dist/tools/index.js +5 -0
  241. package/dist/tools/registry/index.d.ts +2 -0
  242. package/dist/tools/registry/index.js +2 -0
  243. package/dist/tools/registry/service.d.ts +1 -0
  244. package/dist/tools/registry/service.js +1 -0
  245. package/dist/tools/registry/views.d.ts +1 -0
  246. package/dist/tools/registry/views.js +1 -0
  247. package/dist/tools/service.d.ts +2 -0
  248. package/dist/tools/service.js +1 -0
  249. package/dist/tools/utils.d.ts +2 -0
  250. package/dist/tools/utils.js +57 -0
  251. package/dist/tools/views.d.ts +1 -0
  252. package/dist/tools/views.js +1 -0
  253. package/dist/utils.d.ts +10 -1
  254. package/dist/utils.js +70 -3
  255. package/package.json +87 -26
  256. package/dist/dom/playground/process-dom.js +0 -5
  257. package/dist/dom/playground/test-accessibility.d.ts +0 -44
  258. package/dist/dom/playground/test-accessibility.js +0 -111
  259. /package/dist/{dom/playground/process-dom.d.ts → tools/extraction/views.js} +0 -0
@@ -0,0 +1,296 @@
1
+ import { BrowserConnectedEvent, BrowserErrorEvent, BrowserStoppedEvent, TabClosedEvent, TabCreatedEvent, TargetCrashedEvent, } from '../events.js';
2
+ import { BaseWatchdog } from './base.js';
3
+ export class CrashWatchdog extends BaseWatchdog {
4
+ static LISTENS_TO = [
5
+ BrowserConnectedEvent,
6
+ BrowserStoppedEvent,
7
+ TabCreatedEvent,
8
+ TabClosedEvent,
9
+ ];
10
+ static EMITS = [TargetCrashedEvent, BrowserErrorEvent];
11
+ _pageListeners = new Map();
12
+ _pendingRequests = new Map();
13
+ _requestIds = new WeakMap();
14
+ _requestCounter = 0;
15
+ _healthInterval = null;
16
+ _networkTimeoutMs = 10_000;
17
+ _healthCheckIntervalMs = 5_000;
18
+ _consecutiveUnresponsiveChecks = 0;
19
+ _unresponsiveThreshold = 2;
20
+ _monitoringInProgress = false;
21
+ async on_BrowserConnectedEvent() {
22
+ this._attachToKnownPages();
23
+ this._startHealthMonitor();
24
+ }
25
+ async on_TabCreatedEvent() {
26
+ this._attachToKnownPages();
27
+ }
28
+ async on_TabClosedEvent() {
29
+ this._dropDetachedPages();
30
+ }
31
+ async on_BrowserStoppedEvent() {
32
+ this._detachAllPages();
33
+ this._stopHealthMonitor();
34
+ this._pendingRequests.clear();
35
+ this._consecutiveUnresponsiveChecks = 0;
36
+ }
37
+ onDetached() {
38
+ this._detachAllPages();
39
+ this._stopHealthMonitor();
40
+ this._pendingRequests.clear();
41
+ this._consecutiveUnresponsiveChecks = 0;
42
+ }
43
+ _attachToKnownPages() {
44
+ for (const page of this._getKnownPages()) {
45
+ this._attachPage(page);
46
+ }
47
+ }
48
+ _dropDetachedPages() {
49
+ const livePages = new Set(this._getKnownPages());
50
+ for (const [page, listeners] of [...this._pageListeners.entries()]) {
51
+ if (livePages.has(page)) {
52
+ continue;
53
+ }
54
+ this._detachPageListeners(page, listeners);
55
+ this._pageListeners.delete(page);
56
+ }
57
+ }
58
+ _detachAllPages() {
59
+ for (const [page, listeners] of [...this._pageListeners.entries()]) {
60
+ this._detachPageListeners(page, listeners);
61
+ }
62
+ this._pageListeners.clear();
63
+ }
64
+ _attachPage(page) {
65
+ if (this._pageListeners.has(page)) {
66
+ return;
67
+ }
68
+ if (typeof page?.on !== 'function') {
69
+ return;
70
+ }
71
+ const crashListener = (payload) => {
72
+ void this._handlePageCrash(page, payload);
73
+ };
74
+ const requestListener = (payload) => {
75
+ this._trackRequestStart(payload);
76
+ };
77
+ const requestFinishedListener = (payload) => {
78
+ this._trackRequestDone(payload);
79
+ };
80
+ const requestFailedListener = (payload) => {
81
+ this._trackRequestDone(payload);
82
+ };
83
+ const responseListener = () => {
84
+ // Keep hook for future detailed response-based crash heuristics.
85
+ };
86
+ page.on('crash', crashListener);
87
+ page.on('request', requestListener);
88
+ page.on('requestfinished', requestFinishedListener);
89
+ page.on('requestfailed', requestFailedListener);
90
+ page.on('response', responseListener);
91
+ this._pageListeners.set(page, {
92
+ crash: crashListener,
93
+ request: requestListener,
94
+ requestfinished: requestFinishedListener,
95
+ requestfailed: requestFailedListener,
96
+ response: responseListener,
97
+ });
98
+ }
99
+ _detachPageListeners(page, listeners) {
100
+ if (typeof page.off === 'function') {
101
+ page.off('crash', listeners.crash);
102
+ page.off('request', listeners.request);
103
+ page.off('requestfinished', listeners.requestfinished);
104
+ page.off('requestfailed', listeners.requestfailed);
105
+ page.off('response', listeners.response);
106
+ return;
107
+ }
108
+ if (typeof page.removeListener === 'function') {
109
+ page.removeListener('crash', listeners.crash);
110
+ page.removeListener('request', listeners.request);
111
+ page.removeListener('requestfinished', listeners.requestfinished);
112
+ page.removeListener('requestfailed', listeners.requestfailed);
113
+ page.removeListener('response', listeners.response);
114
+ }
115
+ }
116
+ _getKnownPages() {
117
+ const pagesFromContext = typeof this.browser_session.browser_context?.pages === 'function'
118
+ ? this.browser_session.browser_context.pages()
119
+ : [];
120
+ const activePage = this.browser_session.agent_current_page;
121
+ if (!activePage) {
122
+ return pagesFromContext;
123
+ }
124
+ if (pagesFromContext.includes(activePage)) {
125
+ return pagesFromContext;
126
+ }
127
+ return [...pagesFromContext, activePage];
128
+ }
129
+ async _handlePageCrash(page, payload) {
130
+ const target_id = this._resolveTargetId(page);
131
+ const url = this._safePageUrl(page) ?? this.browser_session.active_tab?.url ?? '';
132
+ const errorMessage = this._normalizeCrashError(payload);
133
+ await this.event_bus.dispatch(new TargetCrashedEvent({
134
+ target_id,
135
+ error: errorMessage,
136
+ }));
137
+ await this.event_bus.dispatch(new BrowserErrorEvent({
138
+ error_type: 'TargetCrash',
139
+ message: errorMessage,
140
+ details: {
141
+ target_id,
142
+ url,
143
+ },
144
+ }));
145
+ }
146
+ _resolveTargetId(page) {
147
+ const pageUrl = this._safePageUrl(page);
148
+ if (pageUrl) {
149
+ const tabByUrl = this.browser_session.tabs.find((tab) => tab.url === pageUrl && tab.target_id);
150
+ if (tabByUrl?.target_id) {
151
+ return tabByUrl.target_id;
152
+ }
153
+ }
154
+ const activeTargetId = this.browser_session.active_tab?.target_id;
155
+ if (activeTargetId) {
156
+ return activeTargetId;
157
+ }
158
+ return (this.browser_session.session_manager.get_focused_target_id() ??
159
+ 'unknown_target');
160
+ }
161
+ _safePageUrl(page) {
162
+ try {
163
+ return typeof page.url === 'function' ? page.url() : null;
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ }
169
+ _normalizeCrashError(payload) {
170
+ if (payload instanceof Error) {
171
+ return payload.message || 'Target crashed';
172
+ }
173
+ if (typeof payload === 'string' && payload.trim().length > 0) {
174
+ return payload.trim();
175
+ }
176
+ if (payload && typeof payload === 'object' && 'message' in payload) {
177
+ const candidate = payload.message;
178
+ if (typeof candidate === 'string' && candidate.trim().length > 0) {
179
+ return candidate.trim();
180
+ }
181
+ }
182
+ return 'Target crashed';
183
+ }
184
+ _trackRequestStart(request) {
185
+ const requestObject = request;
186
+ if (!requestObject || typeof requestObject !== 'object') {
187
+ return;
188
+ }
189
+ const requestId = `req-${this._requestCounter++}`;
190
+ const url = typeof request?.url === 'function'
191
+ ? request.url()
192
+ : (this.browser_session.active_tab?.url ?? '');
193
+ const method = typeof request?.method === 'function' ? request.method() : 'GET';
194
+ this._requestIds.set(requestObject, requestId);
195
+ this._pendingRequests.set(requestId, {
196
+ url: typeof url === 'string' ? url : '',
197
+ method: typeof method === 'string' ? method : 'GET',
198
+ started_at: Date.now(),
199
+ });
200
+ }
201
+ _trackRequestDone(request) {
202
+ const requestObject = request;
203
+ if (!requestObject || typeof requestObject !== 'object') {
204
+ return;
205
+ }
206
+ const requestId = this._requestIds.get(requestObject);
207
+ if (!requestId) {
208
+ return;
209
+ }
210
+ this._requestIds.delete(requestObject);
211
+ this._pendingRequests.delete(requestId);
212
+ }
213
+ _startHealthMonitor() {
214
+ if (this._healthInterval) {
215
+ return;
216
+ }
217
+ this._healthInterval = setInterval(() => {
218
+ void this._runHealthCheck();
219
+ }, this._healthCheckIntervalMs);
220
+ }
221
+ _stopHealthMonitor() {
222
+ if (!this._healthInterval) {
223
+ return;
224
+ }
225
+ clearInterval(this._healthInterval);
226
+ this._healthInterval = null;
227
+ }
228
+ async _runHealthCheck() {
229
+ if (this._monitoringInProgress) {
230
+ return;
231
+ }
232
+ this._monitoringInProgress = true;
233
+ try {
234
+ await this._checkNetworkTimeouts();
235
+ await this._checkPageResponsiveness();
236
+ }
237
+ finally {
238
+ this._monitoringInProgress = false;
239
+ }
240
+ }
241
+ async _checkNetworkTimeouts() {
242
+ const now = Date.now();
243
+ for (const [requestId, metadata] of [...this._pendingRequests.entries()]) {
244
+ const ageMs = now - metadata.started_at;
245
+ if (ageMs < this._networkTimeoutMs) {
246
+ continue;
247
+ }
248
+ this._pendingRequests.delete(requestId);
249
+ await this.event_bus.dispatch(new BrowserErrorEvent({
250
+ error_type: 'NetworkTimeout',
251
+ message: `Request timed out after ${Math.round(ageMs / 1000)}s`,
252
+ details: {
253
+ request_id: requestId,
254
+ url: metadata.url,
255
+ method: metadata.method,
256
+ timeout_ms: this._networkTimeoutMs,
257
+ },
258
+ }));
259
+ }
260
+ }
261
+ async _checkPageResponsiveness() {
262
+ const page = (await this.browser_session.get_current_page());
263
+ if (!page?.evaluate) {
264
+ return;
265
+ }
266
+ const timeoutMs = 2_000;
267
+ try {
268
+ await Promise.race([
269
+ page.evaluate(() => document.readyState),
270
+ new Promise((_, reject) => {
271
+ setTimeout(() => {
272
+ reject(new Error('Page responsiveness check timed out'));
273
+ }, timeoutMs);
274
+ }),
275
+ ]);
276
+ this._consecutiveUnresponsiveChecks = 0;
277
+ }
278
+ catch (error) {
279
+ this._consecutiveUnresponsiveChecks += 1;
280
+ if (this._consecutiveUnresponsiveChecks < this._unresponsiveThreshold) {
281
+ return;
282
+ }
283
+ this._consecutiveUnresponsiveChecks = 0;
284
+ await this.event_bus.dispatch(new BrowserErrorEvent({
285
+ error_type: 'TargetUnresponsive',
286
+ message: error.message || 'Target became unresponsive',
287
+ details: {
288
+ url: this._safePageUrl(page) ??
289
+ this.browser_session.active_tab?.url ??
290
+ '',
291
+ timeout_ms: timeoutMs,
292
+ },
293
+ }));
294
+ }
295
+ }
296
+ }
@@ -0,0 +1,49 @@
1
+ import { ClickCoordinateEvent, ClickElementEvent, CloseTabEvent, GetDropdownOptionsEvent, GoBackEvent, NavigateToUrlEvent, ScrollEvent, ScrollToTextEvent, SelectDropdownOptionEvent, SendKeysEvent, SwitchTabEvent, TypeTextEvent, UploadFileEvent, WaitEvent } from '../events.js';
2
+ import { BaseWatchdog } from './base.js';
3
+ export declare class DefaultActionWatchdog extends BaseWatchdog {
4
+ static LISTENS_TO: (typeof ClickElementEvent | typeof ClickCoordinateEvent | typeof TypeTextEvent | typeof SwitchTabEvent | typeof GoBackEvent | typeof GetDropdownOptionsEvent)[];
5
+ on_NavigateToUrlEvent(event: NavigateToUrlEvent): Promise<void>;
6
+ on_SwitchTabEvent(event: SwitchTabEvent): Promise<string | null | undefined>;
7
+ on_CloseTabEvent(event: CloseTabEvent): Promise<void>;
8
+ on_GoBackEvent(): Promise<void>;
9
+ on_GoForwardEvent(): Promise<void>;
10
+ on_RefreshEvent(): Promise<void>;
11
+ on_WaitEvent(event: WaitEvent): Promise<void>;
12
+ on_SendKeysEvent(event: SendKeysEvent): Promise<void>;
13
+ on_ScrollEvent(event: ScrollEvent): Promise<void>;
14
+ on_ScrollToTextEvent(event: ScrollToTextEvent): Promise<void>;
15
+ on_ClickElementEvent(event: ClickElementEvent): Promise<string | {
16
+ validation_error: string;
17
+ } | null>;
18
+ on_ClickCoordinateEvent(event: ClickCoordinateEvent): Promise<{
19
+ coordinate_x: number;
20
+ coordinate_y: number;
21
+ }>;
22
+ on_TypeTextEvent(event: TypeTextEvent): Promise<void>;
23
+ on_UploadFileEvent(event: UploadFileEvent): Promise<void>;
24
+ on_GetDropdownOptionsEvent(event: GetDropdownOptionsEvent): Promise<{
25
+ type: string;
26
+ options: string;
27
+ formatted_options: any;
28
+ message: any;
29
+ short_term_memory: any;
30
+ long_term_memory: string;
31
+ }>;
32
+ on_SelectDropdownOptionEvent(event: SelectDropdownOptionEvent): Promise<{
33
+ message: string;
34
+ short_term_memory: string;
35
+ long_term_memory: string;
36
+ matched_text: string;
37
+ matched_value: string;
38
+ } | {
39
+ message: string;
40
+ short_term_memory: string;
41
+ long_term_memory: string;
42
+ matched_text: string;
43
+ matched_value?: undefined;
44
+ }>;
45
+ private _isPrintRelatedElement;
46
+ private _handlePrintButtonClick;
47
+ private _getUniqueFilename;
48
+ private _sanitizeFilename;
49
+ }
@@ -0,0 +1,212 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { ClickCoordinateEvent, ClickElementEvent, CloseTabEvent, FileDownloadedEvent, GetDropdownOptionsEvent, GoBackEvent, GoForwardEvent, NavigateToUrlEvent, RefreshEvent, ScrollEvent, ScrollToTextEvent, SelectDropdownOptionEvent, SendKeysEvent, SwitchTabEvent, TypeTextEvent, UploadFileEvent, WaitEvent, } from '../events.js';
5
+ import { BaseWatchdog } from './base.js';
6
+ export class DefaultActionWatchdog extends BaseWatchdog {
7
+ static LISTENS_TO = [
8
+ NavigateToUrlEvent,
9
+ SwitchTabEvent,
10
+ CloseTabEvent,
11
+ GoBackEvent,
12
+ GoForwardEvent,
13
+ RefreshEvent,
14
+ WaitEvent,
15
+ SendKeysEvent,
16
+ ScrollEvent,
17
+ ScrollToTextEvent,
18
+ ClickElementEvent,
19
+ TypeTextEvent,
20
+ UploadFileEvent,
21
+ ClickCoordinateEvent,
22
+ GetDropdownOptionsEvent,
23
+ SelectDropdownOptionEvent,
24
+ ];
25
+ async on_NavigateToUrlEvent(event) {
26
+ if (event.new_tab) {
27
+ await this.browser_session.create_new_tab(event.url, {
28
+ wait_until: event.wait_until,
29
+ timeout_ms: event.timeout_ms,
30
+ });
31
+ return;
32
+ }
33
+ await this.browser_session.navigate_to(event.url, {
34
+ wait_until: event.wait_until,
35
+ timeout_ms: event.timeout_ms,
36
+ });
37
+ }
38
+ async on_SwitchTabEvent(event) {
39
+ let identifier;
40
+ if (event.target_id) {
41
+ identifier = event.target_id;
42
+ }
43
+ else {
44
+ const tabs = this.browser_session.tabs;
45
+ if (tabs.length === 0) {
46
+ await this.browser_session.create_new_tab('about:blank');
47
+ return (this.browser_session.active_tab?.target_id ??
48
+ this.browser_session.active_tab?.tab_id ??
49
+ 'unknown_target');
50
+ }
51
+ const latestTab = tabs[tabs.length - 1];
52
+ identifier = latestTab.target_id ?? latestTab.tab_id ?? latestTab.page_id;
53
+ }
54
+ const page = await this.browser_session.switch_to_tab(identifier);
55
+ const activeTargetId = this.browser_session.active_tab?.target_id ?? null;
56
+ return (activeTargetId ?? (page ? this.browser_session.active_tab?.tab_id : null));
57
+ }
58
+ async on_CloseTabEvent(event) {
59
+ await this.browser_session.close_tab(event.target_id);
60
+ }
61
+ async on_GoBackEvent() {
62
+ await this.browser_session.go_back();
63
+ }
64
+ async on_GoForwardEvent() {
65
+ await this.browser_session.go_forward();
66
+ }
67
+ async on_RefreshEvent() {
68
+ await this.browser_session.refresh();
69
+ }
70
+ async on_WaitEvent(event) {
71
+ const seconds = Math.min(Math.max(event.seconds, 0), event.max_seconds);
72
+ await this.browser_session.wait(seconds);
73
+ }
74
+ async on_SendKeysEvent(event) {
75
+ await this.browser_session.send_keys(event.keys);
76
+ }
77
+ async on_ScrollEvent(event) {
78
+ await this.browser_session.scroll(event.direction, event.amount, {
79
+ node: event.node ?? null,
80
+ });
81
+ }
82
+ async on_ScrollToTextEvent(event) {
83
+ await this.browser_session.scroll_to_text(event.text, {
84
+ direction: event.direction,
85
+ });
86
+ }
87
+ async on_ClickElementEvent(event) {
88
+ if (this.browser_session.is_file_input(event.node)) {
89
+ return {
90
+ validation_error: 'The target element is a file input. Use upload_file action instead of click.',
91
+ };
92
+ }
93
+ if (this._isPrintRelatedElement(event.node)) {
94
+ const pdfPath = await this._handlePrintButtonClick();
95
+ if (pdfPath) {
96
+ return pdfPath;
97
+ }
98
+ }
99
+ return this.browser_session._click_element_node(event.node);
100
+ }
101
+ async on_ClickCoordinateEvent(event) {
102
+ await this.browser_session.click_coordinates(event.coordinate_x, event.coordinate_y, {
103
+ button: event.button,
104
+ });
105
+ return {
106
+ coordinate_x: event.coordinate_x,
107
+ coordinate_y: event.coordinate_y,
108
+ };
109
+ }
110
+ async on_TypeTextEvent(event) {
111
+ return this.browser_session._input_text_element_node(event.node, event.text, {
112
+ clear: event.clear,
113
+ });
114
+ }
115
+ async on_UploadFileEvent(event) {
116
+ await this.browser_session.upload_file(event.node, event.file_path);
117
+ }
118
+ async on_GetDropdownOptionsEvent(event) {
119
+ return this.browser_session.get_dropdown_options(event.node);
120
+ }
121
+ async on_SelectDropdownOptionEvent(event) {
122
+ return this.browser_session.select_dropdown_option(event.node, event.text);
123
+ }
124
+ _isPrintRelatedElement(node) {
125
+ if (!node || typeof node !== 'object') {
126
+ return false;
127
+ }
128
+ const attributes = 'attributes' in node && node.attributes
129
+ ? node.attributes
130
+ : {};
131
+ const onclick = String(attributes.onclick ?? '').toLowerCase();
132
+ if (onclick.includes('print')) {
133
+ return true;
134
+ }
135
+ const textMethods = [
136
+ node.get_all_text_till_next_clickable_element,
137
+ node.get_all_children_text,
138
+ ];
139
+ for (const textMethod of textMethods) {
140
+ if (typeof textMethod !== 'function') {
141
+ continue;
142
+ }
143
+ try {
144
+ const text = String(textMethod.call(node, 2) ?? '').toLowerCase();
145
+ if (text.includes('print')) {
146
+ return true;
147
+ }
148
+ }
149
+ catch {
150
+ // Ignore text extraction failures.
151
+ }
152
+ }
153
+ return false;
154
+ }
155
+ async _handlePrintButtonClick() {
156
+ const page = await this.browser_session.get_current_page();
157
+ if (!page) {
158
+ return null;
159
+ }
160
+ try {
161
+ const cdpSession = await this.browser_session.get_or_create_cdp_session(page);
162
+ const result = await cdpSession.send?.('Page.printToPDF', {
163
+ printBackground: true,
164
+ preferCSSPageSize: true,
165
+ });
166
+ const pdfBase64 = result && typeof result.data === 'string' ? result.data : null;
167
+ if (!pdfBase64) {
168
+ return null;
169
+ }
170
+ const downloadsPath = this.browser_session.browser_profile.downloads_path || os.tmpdir();
171
+ fs.mkdirSync(downloadsPath, { recursive: true });
172
+ const title = typeof page.title === 'function' ? await page.title() : 'document';
173
+ const suggestedName = this._sanitizeFilename(`${title || 'document'}.pdf`);
174
+ const uniqueFilename = await this._getUniqueFilename(downloadsPath, suggestedName);
175
+ const finalPath = path.join(downloadsPath, uniqueFilename);
176
+ const content = Buffer.from(pdfBase64, 'base64');
177
+ fs.writeFileSync(finalPath, content);
178
+ await this.event_bus.dispatch(new FileDownloadedEvent({
179
+ guid: null,
180
+ url: typeof page.url === 'function' ? page.url() : '',
181
+ path: finalPath,
182
+ file_name: uniqueFilename,
183
+ file_size: content.length,
184
+ file_type: 'pdf',
185
+ mime_type: 'application/pdf',
186
+ auto_download: false,
187
+ }));
188
+ return finalPath;
189
+ }
190
+ catch (error) {
191
+ this.browser_session.logger.debug(`[DefaultActionWatchdog] Print-to-PDF failed: ${error.message}`);
192
+ return null;
193
+ }
194
+ }
195
+ async _getUniqueFilename(directory, filename) {
196
+ const parsed = path.parse(filename);
197
+ let candidate = filename;
198
+ let counter = 1;
199
+ while (fs.existsSync(path.join(directory, candidate))) {
200
+ candidate = `${parsed.name} (${counter})${parsed.ext}`;
201
+ counter += 1;
202
+ }
203
+ return candidate;
204
+ }
205
+ _sanitizeFilename(filename) {
206
+ const sanitized = filename
207
+ .replace(/[\\/:*?"<>|]+/g, '_')
208
+ .replace(/\s+/g, ' ')
209
+ .trim();
210
+ return sanitized || 'document.pdf';
211
+ }
212
+ }
@@ -0,0 +1,8 @@
1
+ import { BrowserErrorEvent, BrowserStateRequestEvent, TabCreatedEvent } from '../events.js';
2
+ import { BaseWatchdog } from './base.js';
3
+ export declare class DOMWatchdog extends BaseWatchdog {
4
+ static LISTENS_TO: (typeof BrowserStateRequestEvent | typeof TabCreatedEvent)[];
5
+ static EMITS: (typeof BrowserErrorEvent)[];
6
+ on_TabCreatedEvent(): Promise<null>;
7
+ on_BrowserStateRequestEvent(event: BrowserStateRequestEvent): Promise<import("../views.js").BrowserStateSummary>;
8
+ }
@@ -0,0 +1,31 @@
1
+ import { BrowserErrorEvent, BrowserStateRequestEvent, TabCreatedEvent, } from '../events.js';
2
+ import { BaseWatchdog } from './base.js';
3
+ export class DOMWatchdog extends BaseWatchdog {
4
+ static LISTENS_TO = [TabCreatedEvent, BrowserStateRequestEvent];
5
+ static EMITS = [BrowserErrorEvent];
6
+ async on_TabCreatedEvent() {
7
+ // Placeholder hook kept for parity with Python watchdog lifecycle.
8
+ return null;
9
+ }
10
+ async on_BrowserStateRequestEvent(event) {
11
+ try {
12
+ return await this.browser_session.get_browser_state_with_recovery({
13
+ cache_clickable_elements_hashes: true,
14
+ include_screenshot: event.include_screenshot,
15
+ include_recent_events: event.include_recent_events,
16
+ });
17
+ }
18
+ catch (error) {
19
+ await this.event_bus.dispatch(new BrowserErrorEvent({
20
+ error_type: 'BrowserStateRequestFailed',
21
+ message: `DOM state request failed: ${error.message}`,
22
+ details: {
23
+ include_screenshot: event.include_screenshot,
24
+ include_recent_events: event.include_recent_events,
25
+ },
26
+ event_parent_id: event.event_id,
27
+ }));
28
+ throw error;
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,77 @@
1
+ import { BrowserConnectedEvent, BrowserLaunchEvent, BrowserStateRequestEvent, BrowserStoppedEvent, DownloadProgressEvent, DownloadStartedEvent, FileDownloadedEvent, TabClosedEvent } from '../events.js';
2
+ import { BaseWatchdog } from './base.js';
3
+ type DownloadStartInfo = {
4
+ guid: string;
5
+ url: string;
6
+ suggested_filename: string;
7
+ auto_download: boolean;
8
+ };
9
+ type DownloadProgressInfo = {
10
+ guid: string;
11
+ received_bytes: number;
12
+ total_bytes: number;
13
+ state: string;
14
+ };
15
+ type DownloadCompleteInfo = {
16
+ guid: string | null;
17
+ url: string;
18
+ path: string;
19
+ file_name: string;
20
+ file_size: number;
21
+ file_type: string | null;
22
+ mime_type: string | null;
23
+ auto_download: boolean;
24
+ };
25
+ export declare class DownloadsWatchdog extends BaseWatchdog {
26
+ static LISTENS_TO: (typeof BrowserStateRequestEvent | typeof BrowserLaunchEvent | typeof BrowserConnectedEvent | typeof BrowserStoppedEvent | typeof TabClosedEvent | typeof DownloadStartedEvent | typeof DownloadProgressEvent | typeof FileDownloadedEvent)[];
27
+ static EMITS: (typeof DownloadStartedEvent | typeof DownloadProgressEvent | typeof FileDownloadedEvent)[];
28
+ private _activeDownloads;
29
+ private _downloadStartCallbacks;
30
+ private _downloadProgressCallbacks;
31
+ private _downloadCompleteCallbacks;
32
+ private _cdpSession;
33
+ private _cdpListeners;
34
+ private _networkDownloads;
35
+ private _detectedDownloadUrls;
36
+ on_BrowserConnectedEvent(): Promise<void>;
37
+ on_BrowserLaunchEvent(): void;
38
+ on_BrowserStateRequestEvent(event: BrowserStateRequestEvent): Promise<void>;
39
+ on_BrowserStoppedEvent(): void;
40
+ on_TabCreatedEvent(): null;
41
+ on_TabClosedEvent(): null;
42
+ on_NavigationCompleteEvent(): null;
43
+ on_DownloadStartedEvent(event: DownloadStartedEvent): void;
44
+ on_DownloadProgressEvent(event: DownloadProgressEvent): void;
45
+ on_FileDownloadedEvent(event: FileDownloadedEvent): string;
46
+ get_active_downloads(): {
47
+ url: string;
48
+ suggested_filename: string;
49
+ started_at: string;
50
+ received_bytes: number;
51
+ total_bytes: number;
52
+ state: string;
53
+ guid: string;
54
+ }[];
55
+ register_download_callbacks(on_start_or_options?: ((info: DownloadStartInfo) => void) | {
56
+ on_start?: ((info: DownloadStartInfo) => void) | null;
57
+ on_progress?: ((info: DownloadProgressInfo) => void) | null;
58
+ on_complete?: ((info: DownloadCompleteInfo) => void) | null;
59
+ } | null, on_progress?: ((info: DownloadProgressInfo) => void) | null, on_complete?: ((info: DownloadCompleteInfo) => void) | null): void;
60
+ unregister_download_callbacks(on_start_or_options?: ((info: DownloadStartInfo) => void) | {
61
+ on_start?: ((info: DownloadStartInfo) => void) | null;
62
+ on_progress?: ((info: DownloadProgressInfo) => void) | null;
63
+ on_complete?: ((info: DownloadCompleteInfo) => void) | null;
64
+ } | null, on_progress?: ((info: DownloadProgressInfo) => void) | null, on_complete?: ((info: DownloadCompleteInfo) => void) | null): void;
65
+ protected onDetached(): void;
66
+ private _normalizeCallbackRegistration;
67
+ private _startCdpDownloadMonitoring;
68
+ private _stopCdpDownloadMonitoring;
69
+ private _handleNetworkResponse;
70
+ private _handleNetworkLoadingFinished;
71
+ private _normalizeHeaders;
72
+ private _resolveSuggestedFilename;
73
+ private _sanitizeFilename;
74
+ private _inferFileType;
75
+ private _getUniqueFilename;
76
+ }
77
+ export {};