browser-use 0.0.1 → 0.0.2

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 (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +761 -0
  3. package/dist/agent/cloud-events.d.ts +264 -0
  4. package/dist/agent/cloud-events.js +318 -0
  5. package/dist/agent/gif.d.ts +15 -0
  6. package/dist/agent/gif.js +215 -0
  7. package/dist/agent/index.d.ts +8 -0
  8. package/dist/agent/index.js +8 -0
  9. package/dist/agent/message-manager/service.d.ts +30 -0
  10. package/dist/agent/message-manager/service.js +208 -0
  11. package/dist/agent/message-manager/utils.d.ts +2 -0
  12. package/dist/agent/message-manager/utils.js +41 -0
  13. package/dist/agent/message-manager/views.d.ts +26 -0
  14. package/dist/agent/message-manager/views.js +73 -0
  15. package/dist/agent/prompts.d.ts +52 -0
  16. package/dist/agent/prompts.js +259 -0
  17. package/dist/agent/service.d.ts +290 -0
  18. package/dist/agent/service.js +2200 -0
  19. package/dist/agent/views.d.ts +741 -0
  20. package/dist/agent/views.js +537 -0
  21. package/dist/browser/browser.d.ts +7 -0
  22. package/dist/browser/browser.js +5 -0
  23. package/dist/browser/context.d.ts +8 -0
  24. package/dist/browser/context.js +4 -0
  25. package/dist/browser/dvd-screensaver.d.ts +101 -0
  26. package/dist/browser/dvd-screensaver.js +270 -0
  27. package/dist/browser/extensions.d.ts +63 -0
  28. package/dist/browser/extensions.js +359 -0
  29. package/dist/browser/index.d.ts +10 -0
  30. package/dist/browser/index.js +9 -0
  31. package/dist/browser/playwright-manager.d.ts +47 -0
  32. package/dist/browser/playwright-manager.js +146 -0
  33. package/dist/browser/profile.d.ts +196 -0
  34. package/dist/browser/profile.js +815 -0
  35. package/dist/browser/session.d.ts +505 -0
  36. package/dist/browser/session.js +3409 -0
  37. package/dist/browser/types.d.ts +1184 -0
  38. package/dist/browser/types.js +1 -0
  39. package/dist/browser/utils.d.ts +1 -0
  40. package/dist/browser/utils.js +19 -0
  41. package/dist/browser/views.d.ts +78 -0
  42. package/dist/browser/views.js +72 -0
  43. package/dist/cli.d.ts +2 -0
  44. package/dist/cli.js +44 -0
  45. package/dist/config.d.ts +108 -0
  46. package/dist/config.js +430 -0
  47. package/dist/controller/index.d.ts +3 -0
  48. package/dist/controller/index.js +3 -0
  49. package/dist/controller/registry/index.d.ts +2 -0
  50. package/dist/controller/registry/index.js +2 -0
  51. package/dist/controller/registry/service.d.ts +45 -0
  52. package/dist/controller/registry/service.js +184 -0
  53. package/dist/controller/registry/views.d.ts +55 -0
  54. package/dist/controller/registry/views.js +174 -0
  55. package/dist/controller/service.d.ts +49 -0
  56. package/dist/controller/service.js +1176 -0
  57. package/dist/controller/views.d.ts +241 -0
  58. package/dist/controller/views.js +88 -0
  59. package/dist/dom/clickable-element-processor/service.d.ts +11 -0
  60. package/dist/dom/clickable-element-processor/service.js +60 -0
  61. package/dist/dom/dom_tree/index.js +1400 -0
  62. package/dist/dom/history-tree-processor/service.d.ts +14 -0
  63. package/dist/dom/history-tree-processor/service.js +75 -0
  64. package/dist/dom/history-tree-processor/view.d.ts +54 -0
  65. package/dist/dom/history-tree-processor/view.js +56 -0
  66. package/dist/dom/playground/extraction.d.ts +19 -0
  67. package/dist/dom/playground/extraction.js +187 -0
  68. package/dist/dom/playground/process-dom.d.ts +1 -0
  69. package/dist/dom/playground/process-dom.js +5 -0
  70. package/dist/dom/playground/test-accessibility.d.ts +44 -0
  71. package/dist/dom/playground/test-accessibility.js +111 -0
  72. package/dist/dom/service.d.ts +19 -0
  73. package/dist/dom/service.js +227 -0
  74. package/dist/dom/utils.d.ts +1 -0
  75. package/dist/dom/utils.js +6 -0
  76. package/dist/dom/views.d.ts +61 -0
  77. package/dist/dom/views.js +247 -0
  78. package/dist/event-bus.d.ts +11 -0
  79. package/dist/event-bus.js +19 -0
  80. package/dist/exceptions.d.ts +10 -0
  81. package/dist/exceptions.js +22 -0
  82. package/dist/filesystem/file-system.d.ts +68 -0
  83. package/dist/filesystem/file-system.js +412 -0
  84. package/dist/filesystem/index.d.ts +1 -0
  85. package/dist/filesystem/index.js +1 -0
  86. package/dist/index.d.ts +31 -0
  87. package/dist/index.js +33 -0
  88. package/dist/integrations/gmail/actions.d.ts +12 -0
  89. package/dist/integrations/gmail/actions.js +113 -0
  90. package/dist/integrations/gmail/index.d.ts +2 -0
  91. package/dist/integrations/gmail/index.js +2 -0
  92. package/dist/integrations/gmail/service.d.ts +61 -0
  93. package/dist/integrations/gmail/service.js +260 -0
  94. package/dist/llm/anthropic/chat.d.ts +28 -0
  95. package/dist/llm/anthropic/chat.js +126 -0
  96. package/dist/llm/anthropic/index.d.ts +2 -0
  97. package/dist/llm/anthropic/index.js +2 -0
  98. package/dist/llm/anthropic/serializer.d.ts +68 -0
  99. package/dist/llm/anthropic/serializer.js +285 -0
  100. package/dist/llm/aws/chat-anthropic.d.ts +61 -0
  101. package/dist/llm/aws/chat-anthropic.js +176 -0
  102. package/dist/llm/aws/chat-bedrock.d.ts +15 -0
  103. package/dist/llm/aws/chat-bedrock.js +80 -0
  104. package/dist/llm/aws/index.d.ts +3 -0
  105. package/dist/llm/aws/index.js +3 -0
  106. package/dist/llm/aws/serializer.d.ts +5 -0
  107. package/dist/llm/aws/serializer.js +68 -0
  108. package/dist/llm/azure/chat.d.ts +15 -0
  109. package/dist/llm/azure/chat.js +83 -0
  110. package/dist/llm/azure/index.d.ts +1 -0
  111. package/dist/llm/azure/index.js +1 -0
  112. package/dist/llm/base.d.ts +16 -0
  113. package/dist/llm/base.js +1 -0
  114. package/dist/llm/deepseek/chat.d.ts +15 -0
  115. package/dist/llm/deepseek/chat.js +51 -0
  116. package/dist/llm/deepseek/index.d.ts +2 -0
  117. package/dist/llm/deepseek/index.js +2 -0
  118. package/dist/llm/deepseek/serializer.d.ts +6 -0
  119. package/dist/llm/deepseek/serializer.js +57 -0
  120. package/dist/llm/exceptions.d.ts +10 -0
  121. package/dist/llm/exceptions.js +18 -0
  122. package/dist/llm/google/chat.d.ts +20 -0
  123. package/dist/llm/google/chat.js +144 -0
  124. package/dist/llm/google/index.d.ts +2 -0
  125. package/dist/llm/google/index.js +2 -0
  126. package/dist/llm/google/serializer.d.ts +6 -0
  127. package/dist/llm/google/serializer.js +64 -0
  128. package/dist/llm/groq/chat.d.ts +15 -0
  129. package/dist/llm/groq/chat.js +52 -0
  130. package/dist/llm/groq/index.d.ts +3 -0
  131. package/dist/llm/groq/index.js +3 -0
  132. package/dist/llm/groq/parser.d.ts +32 -0
  133. package/dist/llm/groq/parser.js +189 -0
  134. package/dist/llm/groq/serializer.d.ts +6 -0
  135. package/dist/llm/groq/serializer.js +56 -0
  136. package/dist/llm/messages.d.ts +77 -0
  137. package/dist/llm/messages.js +157 -0
  138. package/dist/llm/ollama/chat.d.ts +15 -0
  139. package/dist/llm/ollama/chat.js +77 -0
  140. package/dist/llm/ollama/index.d.ts +2 -0
  141. package/dist/llm/ollama/index.js +2 -0
  142. package/dist/llm/ollama/serializer.d.ts +6 -0
  143. package/dist/llm/ollama/serializer.js +53 -0
  144. package/dist/llm/openai/chat.d.ts +38 -0
  145. package/dist/llm/openai/chat.js +174 -0
  146. package/dist/llm/openai/index.d.ts +3 -0
  147. package/dist/llm/openai/index.js +3 -0
  148. package/dist/llm/openai/like.d.ts +17 -0
  149. package/dist/llm/openai/like.js +19 -0
  150. package/dist/llm/openai/serializer.d.ts +6 -0
  151. package/dist/llm/openai/serializer.js +57 -0
  152. package/dist/llm/openrouter/chat.d.ts +15 -0
  153. package/dist/llm/openrouter/chat.js +74 -0
  154. package/dist/llm/openrouter/index.d.ts +2 -0
  155. package/dist/llm/openrouter/index.js +2 -0
  156. package/dist/llm/openrouter/serializer.d.ts +3 -0
  157. package/dist/llm/openrouter/serializer.js +3 -0
  158. package/dist/llm/schema.d.ts +6 -0
  159. package/dist/llm/schema.js +77 -0
  160. package/dist/llm/views.d.ts +15 -0
  161. package/dist/llm/views.js +12 -0
  162. package/dist/logging-config.d.ts +25 -0
  163. package/dist/logging-config.js +89 -0
  164. package/dist/mcp/client.d.ts +142 -0
  165. package/dist/mcp/client.js +638 -0
  166. package/dist/mcp/controller.d.ts +6 -0
  167. package/dist/mcp/controller.js +38 -0
  168. package/dist/mcp/index.d.ts +3 -0
  169. package/dist/mcp/index.js +3 -0
  170. package/dist/mcp/server.d.ts +134 -0
  171. package/dist/mcp/server.js +759 -0
  172. package/dist/observability-decorators.d.ts +158 -0
  173. package/dist/observability-decorators.js +286 -0
  174. package/dist/observability.d.ts +23 -0
  175. package/dist/observability.js +58 -0
  176. package/dist/screenshots/index.d.ts +1 -0
  177. package/dist/screenshots/index.js +1 -0
  178. package/dist/screenshots/service.d.ts +6 -0
  179. package/dist/screenshots/service.js +28 -0
  180. package/dist/sync/auth.d.ts +27 -0
  181. package/dist/sync/auth.js +205 -0
  182. package/dist/sync/index.d.ts +2 -0
  183. package/dist/sync/index.js +2 -0
  184. package/dist/sync/service.d.ts +21 -0
  185. package/dist/sync/service.js +146 -0
  186. package/dist/telemetry/index.d.ts +2 -0
  187. package/dist/telemetry/index.js +2 -0
  188. package/dist/telemetry/service.d.ts +12 -0
  189. package/dist/telemetry/service.js +85 -0
  190. package/dist/telemetry/views.d.ts +112 -0
  191. package/dist/telemetry/views.js +112 -0
  192. package/dist/tokens/index.d.ts +2 -0
  193. package/dist/tokens/index.js +2 -0
  194. package/dist/tokens/service.d.ts +35 -0
  195. package/dist/tokens/service.js +423 -0
  196. package/dist/tokens/views.d.ts +58 -0
  197. package/dist/tokens/views.js +1 -0
  198. package/dist/utils.d.ts +128 -0
  199. package/dist/utils.js +529 -0
  200. package/package.json +94 -5
@@ -0,0 +1,128 @@
1
+ type Callback = (() => void) | undefined;
2
+ export interface SignalHandlerOptions {
3
+ pause_callback?: Callback;
4
+ resume_callback?: Callback;
5
+ custom_exit_callback?: Callback;
6
+ exit_on_second_int?: boolean;
7
+ interruptible_task_patterns?: string[];
8
+ }
9
+ export declare class SignalHandler {
10
+ loop: NodeJS.EventEmitter | null;
11
+ pause_callback?: Callback;
12
+ resume_callback?: Callback;
13
+ custom_exit_callback?: Callback;
14
+ exit_on_second_int: boolean;
15
+ interruptible_task_patterns: string[];
16
+ is_windows: boolean;
17
+ private ctrl_c_pressed;
18
+ private waiting_for_input;
19
+ private bound_sigint;
20
+ private bound_sigterm;
21
+ constructor(options?: SignalHandlerOptions);
22
+ register(): void;
23
+ unregister(): void;
24
+ private _handle_second_ctrl_c;
25
+ private _cancel_interruptible_tasks;
26
+ wait_for_resume(): Promise<void>;
27
+ reset(): void;
28
+ private sigint_handler;
29
+ private sigterm_handler;
30
+ }
31
+ export declare const time_execution_sync: (additional_text?: string) => <T extends (...args: any[]) => any>(func: T) => T;
32
+ export declare const time_execution_async: (additional_text?: string) => <T extends (...args: any[]) => Promise<any>>(func: T) => T;
33
+ export declare const singleton: <T extends (...args: any[]) => any>(cls: T) => (...args: Parameters<T>) => ReturnType<T>;
34
+ export declare const check_env_variables: (keys: string[], predicate?: (values: string[]) => boolean) => boolean;
35
+ export declare const is_unsafe_pattern: (pattern: string) => boolean;
36
+ export declare const merge_dicts: (a: Record<string, any>, b: Record<string, any>, path?: (string | number)[]) => Record<string, any>;
37
+ export declare const get_browser_use_version: () => string;
38
+ export declare const get_git_info: () => Record<string, string> | null;
39
+ export declare const _log_pretty_path: (input: unknown) => string;
40
+ export declare const _log_pretty_url: (value: string, max_len?: number | null) => string;
41
+ export declare const log_pretty_path: (input: unknown) => string;
42
+ export declare const log_pretty_url: (value: string, max_len?: number | null) => string;
43
+ export declare const uuid7str: () => string;
44
+ /**
45
+ * Retry configuration options
46
+ */
47
+ export interface RetryOptions {
48
+ /** Maximum number of retry attempts (default: 3) */
49
+ maxAttempts?: number;
50
+ /** Delay between retries in milliseconds (default: 1000) */
51
+ delayMs?: number;
52
+ /** Exponential backoff multiplier (default: 1 = no backoff) */
53
+ backoffMultiplier?: number;
54
+ /** Maximum delay in milliseconds for exponential backoff (default: 30000) */
55
+ maxDelayMs?: number;
56
+ /** Function to determine if error is retryable (default: all errors retryable) */
57
+ shouldRetry?: (error: Error, attempt: number) => boolean;
58
+ /** Callback called on each retry attempt */
59
+ onRetry?: (error: Error, attempt: number, nextDelayMs: number) => void;
60
+ }
61
+ /**
62
+ * Retry an async function with configurable attempts and delays
63
+ * Implements exponential backoff with jitter
64
+ *
65
+ * @param fn - The async function to retry
66
+ * @param options - Retry configuration
67
+ * @returns The result of the function
68
+ * @throws The last error if all retries fail
69
+ *
70
+ * @example
71
+ * const result = await retryAsync(
72
+ * async () => await fetchData(),
73
+ * { maxAttempts: 3, delayMs: 1000, backoffMultiplier: 2 }
74
+ * );
75
+ */
76
+ export declare function retryAsync<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
77
+ /**
78
+ * Create a semaphore for limiting concurrent operations
79
+ *
80
+ * @example
81
+ * const semaphore = createSemaphore(3); // Allow max 3 concurrent operations
82
+ * await semaphore.acquire();
83
+ * try {
84
+ * await doWork();
85
+ * } finally {
86
+ * semaphore.release();
87
+ * }
88
+ */
89
+ export declare function createSemaphore(maxConcurrent: number): {
90
+ /**
91
+ * Acquire a semaphore slot
92
+ * Waits if max concurrent operations are already running
93
+ */
94
+ acquire(): Promise<void>;
95
+ /**
96
+ * Release a semaphore slot
97
+ * Allows next queued operation to proceed
98
+ */
99
+ release(): void;
100
+ /**
101
+ * Get current active count
102
+ */
103
+ getActiveCount(): number;
104
+ /**
105
+ * Get queue length
106
+ */
107
+ getQueueLength(): number;
108
+ };
109
+ /**
110
+ * Check if a URL is a new tab page (about:blank or chrome://new-tab-page).
111
+ */
112
+ export declare function is_new_tab_page(url: string): boolean;
113
+ /**
114
+ * Check if a URL matches a domain pattern. SECURITY CRITICAL.
115
+ *
116
+ * Supports optional glob patterns and schemes:
117
+ * - *.example.com will match sub.example.com and example.com
118
+ * - *google.com will match google.com, agoogle.com, and www.google.com
119
+ * - http*://example.com will match http://example.com, https://example.com
120
+ * - chrome-extension://* will match chrome-extension://aaaaaaaaaaaa and chrome-extension://bbbbbbbbbbbbb
121
+ *
122
+ * When no scheme is specified, https is used by default for security.
123
+ * For example, 'example.com' will match 'https://example.com' but not 'http://example.com'.
124
+ *
125
+ * Note: New tab pages (about:blank, chrome://new-tab-page) must be handled at the callsite, not inside this function.
126
+ */
127
+ export declare function match_url_with_domain_pattern(url: string, domain_pattern: string, log_warnings?: boolean): boolean;
128
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,529 @@
1
+ import { execSync } from 'node:child_process';
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import readline from 'node:readline';
7
+ import { stderr } from 'node:process';
8
+ import { performance } from 'node:perf_hooks';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { config as loadEnv } from 'dotenv';
11
+ import { createLogger } from './logging-config.js';
12
+ loadEnv();
13
+ const logger = createLogger('browser_use.utils');
14
+ let _exiting = false;
15
+ export class SignalHandler {
16
+ loop = null;
17
+ pause_callback;
18
+ resume_callback;
19
+ custom_exit_callback;
20
+ exit_on_second_int;
21
+ interruptible_task_patterns;
22
+ is_windows;
23
+ ctrl_c_pressed = false;
24
+ waiting_for_input = false;
25
+ bound_sigint = this.sigint_handler.bind(this);
26
+ bound_sigterm = this.sigterm_handler.bind(this);
27
+ constructor(options = {}) {
28
+ this.pause_callback = options.pause_callback;
29
+ this.resume_callback = options.resume_callback;
30
+ this.custom_exit_callback = options.custom_exit_callback;
31
+ this.exit_on_second_int = options.exit_on_second_int ?? true;
32
+ this.interruptible_task_patterns = options.interruptible_task_patterns ?? [
33
+ 'step',
34
+ 'multi_act',
35
+ 'get_next_action',
36
+ ];
37
+ this.is_windows = os.platform() === 'win32';
38
+ }
39
+ register() {
40
+ process.on('SIGINT', this.bound_sigint);
41
+ process.on('SIGTERM', this.bound_sigterm);
42
+ }
43
+ unregister() {
44
+ process.off('SIGINT', this.bound_sigint);
45
+ process.off('SIGTERM', this.bound_sigterm);
46
+ }
47
+ _handle_second_ctrl_c() {
48
+ if (!_exiting) {
49
+ _exiting = true;
50
+ if (this.custom_exit_callback) {
51
+ try {
52
+ this.custom_exit_callback();
53
+ }
54
+ catch (error) {
55
+ logger.error(`Error in exit callback: ${error.message}`);
56
+ }
57
+ }
58
+ }
59
+ stderr.write('\n\n🛑 Got second Ctrl+C. Exiting immediately...\n');
60
+ stderr.write('\x1b[?25h\x1b[0m\x1b[?1l\x1b[?2004l\r');
61
+ process.exit(0);
62
+ }
63
+ _cancel_interruptible_tasks() {
64
+ // Node.js does not provide asyncio-style task cancellation.
65
+ // Users should manage their own interruptible work via pause/resume callbacks.
66
+ }
67
+ async wait_for_resume() {
68
+ this.waiting_for_input = true;
69
+ const green = '\x1b[32;1m';
70
+ const red = '\x1b[31m';
71
+ const blink = '\x1b[33;5m';
72
+ const unblink = '\x1b[0m';
73
+ const reset = '\x1b[0m';
74
+ stderr.write(`➡️ Press ${green}[Enter]${reset} to resume or ${red}[Ctrl+C]${reset} again to exit${blink}...${unblink} `);
75
+ await new Promise((resolve) => {
76
+ const rl = readline.createInterface({
77
+ input: process.stdin,
78
+ output: stderr,
79
+ });
80
+ const cleanup = () => {
81
+ this.waiting_for_input = false;
82
+ rl.close();
83
+ resolve();
84
+ };
85
+ rl.once('line', () => {
86
+ if (this.resume_callback) {
87
+ try {
88
+ this.resume_callback();
89
+ }
90
+ catch (error) {
91
+ logger.error(`Error in resume callback: ${error.message}`);
92
+ }
93
+ }
94
+ cleanup();
95
+ });
96
+ rl.once('SIGINT', () => {
97
+ this._handle_second_ctrl_c();
98
+ cleanup();
99
+ });
100
+ });
101
+ }
102
+ reset() {
103
+ this.ctrl_c_pressed = false;
104
+ this.waiting_for_input = false;
105
+ }
106
+ sigint_handler() {
107
+ if (_exiting) {
108
+ process.exit(0);
109
+ }
110
+ if (this.ctrl_c_pressed) {
111
+ if (this.waiting_for_input) {
112
+ return;
113
+ }
114
+ if (this.exit_on_second_int) {
115
+ this._handle_second_ctrl_c();
116
+ }
117
+ }
118
+ this.ctrl_c_pressed = true;
119
+ this._cancel_interruptible_tasks();
120
+ if (this.pause_callback) {
121
+ try {
122
+ this.pause_callback();
123
+ }
124
+ catch (error) {
125
+ logger.error(`Error in pause callback: ${error.message}`);
126
+ }
127
+ }
128
+ stderr.write('----------------------------------------------------------------------\n');
129
+ }
130
+ sigterm_handler() {
131
+ if (!_exiting) {
132
+ _exiting = true;
133
+ stderr.write('\n\n🛑 SIGTERM received. Exiting immediately...\n\n');
134
+ if (this.custom_exit_callback) {
135
+ this.custom_exit_callback();
136
+ }
137
+ }
138
+ process.exit(0);
139
+ }
140
+ }
141
+ const strip_hyphen = (value) => value.replace(/^-+|-+$/g, '').trim();
142
+ const pick_logger = (args) => {
143
+ if (args.length > 0) {
144
+ const candidate = args[0];
145
+ if (candidate && candidate.logger) {
146
+ return candidate.logger;
147
+ }
148
+ }
149
+ return logger;
150
+ };
151
+ export const time_execution_sync = (additional_text = '') => (func) => {
152
+ const label = strip_hyphen(additional_text);
153
+ const wrapper = function (...args) {
154
+ const start = performance.now();
155
+ const result = func.apply(this, args);
156
+ const execution_time = (performance.now() - start) / 1000;
157
+ if (execution_time > 0.25) {
158
+ pick_logger(args).debug(`⏳ ${label}() took ${execution_time.toFixed(2)}s`);
159
+ }
160
+ return result;
161
+ };
162
+ return wrapper;
163
+ };
164
+ export const time_execution_async = (additional_text = '') => (func) => {
165
+ const label = strip_hyphen(additional_text);
166
+ const wrapper = async function (...args) {
167
+ const start = performance.now();
168
+ const result = await func.apply(this, args);
169
+ const execution_time = (performance.now() - start) / 1000;
170
+ if (execution_time > 0.25) {
171
+ pick_logger(args).debug(`⏳ ${label}() took ${execution_time.toFixed(2)}s`);
172
+ }
173
+ return result;
174
+ };
175
+ return wrapper;
176
+ };
177
+ export const singleton = (cls) => {
178
+ let instance;
179
+ return (...args) => {
180
+ if (instance === undefined) {
181
+ instance = cls(...args);
182
+ }
183
+ return instance;
184
+ };
185
+ };
186
+ export const check_env_variables = (keys, predicate = (values) => values.every((value) => value.trim().length > 0)) => {
187
+ const values = keys.map((key) => process.env[key] ?? '');
188
+ return predicate(values);
189
+ };
190
+ export const is_unsafe_pattern = (pattern) => {
191
+ if (pattern.includes('://')) {
192
+ const [, ...rest] = pattern.split('://');
193
+ pattern = rest.join('://');
194
+ }
195
+ const bare_domain = pattern.replace('.*', '').replace('*.', '');
196
+ return bare_domain.includes('*');
197
+ };
198
+ export const merge_dicts = (a, b, path = []) => {
199
+ for (const key of Object.keys(b)) {
200
+ if (key in a) {
201
+ if (typeof a[key] === 'object' &&
202
+ !Array.isArray(a[key]) &&
203
+ typeof b[key] === 'object' &&
204
+ !Array.isArray(b[key])) {
205
+ merge_dicts(a[key], b[key], [...path, key]);
206
+ }
207
+ else if (Array.isArray(a[key]) && Array.isArray(b[key])) {
208
+ a[key] = [...a[key], ...b[key]];
209
+ }
210
+ else if (a[key] !== b[key]) {
211
+ throw new Error(`Conflict at ${[...path, key].join('.')}`);
212
+ }
213
+ }
214
+ else {
215
+ a[key] = b[key];
216
+ }
217
+ }
218
+ return a;
219
+ };
220
+ const __filename = fileURLToPath(import.meta.url);
221
+ const __dirname = path.dirname(__filename);
222
+ const package_root = path.resolve(__dirname, '..');
223
+ let cached_version = null;
224
+ export const get_browser_use_version = () => {
225
+ if (cached_version) {
226
+ return cached_version;
227
+ }
228
+ try {
229
+ const package_json = JSON.parse(fs.readFileSync(path.join(package_root, 'package.json'), 'utf-8'));
230
+ if (package_json?.version) {
231
+ const version = String(package_json.version);
232
+ cached_version = version;
233
+ process.env.LIBRARY_VERSION = version;
234
+ return version;
235
+ }
236
+ }
237
+ catch (error) {
238
+ logger.debug(`Error detecting browser-use version: ${error.message}`);
239
+ }
240
+ return 'unknown';
241
+ };
242
+ let cached_git_info;
243
+ export const get_git_info = () => {
244
+ if (cached_git_info !== undefined) {
245
+ return cached_git_info;
246
+ }
247
+ try {
248
+ const git_dir = path.join(package_root, '.git');
249
+ if (!fs.existsSync(git_dir)) {
250
+ cached_git_info = null;
251
+ return null;
252
+ }
253
+ const commit_hash = execSync('git rev-parse HEAD', {
254
+ cwd: package_root,
255
+ stdio: ['ignore', 'pipe', 'ignore'],
256
+ })
257
+ .toString()
258
+ .trim();
259
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
260
+ cwd: package_root,
261
+ stdio: ['ignore', 'pipe', 'ignore'],
262
+ })
263
+ .toString()
264
+ .trim();
265
+ const remote_url = execSync('git config --get remote.origin.url', {
266
+ cwd: package_root,
267
+ stdio: ['ignore', 'pipe', 'ignore'],
268
+ })
269
+ .toString()
270
+ .trim();
271
+ const commit_timestamp = execSync('git show -s --format=%ci HEAD', {
272
+ cwd: package_root,
273
+ stdio: ['ignore', 'pipe', 'ignore'],
274
+ })
275
+ .toString()
276
+ .trim();
277
+ cached_git_info = { commit_hash, branch, remote_url, commit_timestamp };
278
+ return cached_git_info;
279
+ }
280
+ catch (error) {
281
+ logger.debug(`Error getting git info: ${error.message}`);
282
+ cached_git_info = null;
283
+ return null;
284
+ }
285
+ };
286
+ export const _log_pretty_path = (input) => {
287
+ if (!input) {
288
+ return '';
289
+ }
290
+ if (typeof input !== 'string') {
291
+ return `<${input.constructor?.name || typeof input}>`;
292
+ }
293
+ const normalized = input.trim();
294
+ if (!normalized) {
295
+ return '';
296
+ }
297
+ let pretty_path = normalized.replace(os.homedir(), '~');
298
+ pretty_path = pretty_path.replace(process.cwd(), '.');
299
+ return pretty_path.includes(' ') ? `"${pretty_path}"` : pretty_path;
300
+ };
301
+ export const _log_pretty_url = (value, max_len = 22) => {
302
+ let sanitized = value
303
+ .replace('https://', '')
304
+ .replace('http://', '')
305
+ .replace('www.', '');
306
+ if (max_len !== null && sanitized.length > max_len) {
307
+ sanitized = `${sanitized.slice(0, max_len)}…`;
308
+ }
309
+ return sanitized;
310
+ };
311
+ export const log_pretty_path = _log_pretty_path;
312
+ export const log_pretty_url = _log_pretty_url;
313
+ export const uuid7str = () => {
314
+ const timestamp = Buffer.alloc(6);
315
+ const now = Date.now();
316
+ timestamp.writeUIntBE(now, 0, 6);
317
+ const random = crypto.randomBytes(10);
318
+ const bytes = Buffer.concat([timestamp, random]);
319
+ bytes[6] = (bytes[6] & 0x0f) | 0x70;
320
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
321
+ const hex = bytes.toString('hex');
322
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
323
+ };
324
+ /**
325
+ * Retry an async function with configurable attempts and delays
326
+ * Implements exponential backoff with jitter
327
+ *
328
+ * @param fn - The async function to retry
329
+ * @param options - Retry configuration
330
+ * @returns The result of the function
331
+ * @throws The last error if all retries fail
332
+ *
333
+ * @example
334
+ * const result = await retryAsync(
335
+ * async () => await fetchData(),
336
+ * { maxAttempts: 3, delayMs: 1000, backoffMultiplier: 2 }
337
+ * );
338
+ */
339
+ export async function retryAsync(fn, options = {}) {
340
+ const { maxAttempts = 3, delayMs = 1000, backoffMultiplier = 1, maxDelayMs = 30000, shouldRetry = () => true, onRetry, } = options;
341
+ let lastError = null;
342
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
343
+ try {
344
+ return await fn();
345
+ }
346
+ catch (error) {
347
+ lastError = error;
348
+ // Check if we should retry
349
+ const isLastAttempt = attempt === maxAttempts;
350
+ if (isLastAttempt || !shouldRetry(lastError, attempt)) {
351
+ throw lastError;
352
+ }
353
+ // Calculate delay with exponential backoff and jitter
354
+ const baseDelay = delayMs * Math.pow(backoffMultiplier, attempt - 1);
355
+ const jitter = Math.random() * 0.3 * baseDelay; // Add up to 30% jitter
356
+ const nextDelay = Math.min(baseDelay + jitter, maxDelayMs);
357
+ // Notify about retry
358
+ if (onRetry) {
359
+ onRetry(lastError, attempt, nextDelay);
360
+ }
361
+ // Wait before next attempt
362
+ await new Promise((resolve) => setTimeout(resolve, nextDelay));
363
+ }
364
+ }
365
+ // This should never be reached, but TypeScript requires it
366
+ throw lastError || new Error('Retry failed with unknown error');
367
+ }
368
+ /**
369
+ * Create a semaphore for limiting concurrent operations
370
+ *
371
+ * @example
372
+ * const semaphore = createSemaphore(3); // Allow max 3 concurrent operations
373
+ * await semaphore.acquire();
374
+ * try {
375
+ * await doWork();
376
+ * } finally {
377
+ * semaphore.release();
378
+ * }
379
+ */
380
+ export function createSemaphore(maxConcurrent) {
381
+ let activeCount = 0;
382
+ const queue = [];
383
+ return {
384
+ /**
385
+ * Acquire a semaphore slot
386
+ * Waits if max concurrent operations are already running
387
+ */
388
+ async acquire() {
389
+ if (activeCount < maxConcurrent) {
390
+ activeCount++;
391
+ return;
392
+ }
393
+ await new Promise((resolve) => {
394
+ queue.push(resolve);
395
+ });
396
+ },
397
+ /**
398
+ * Release a semaphore slot
399
+ * Allows next queued operation to proceed
400
+ */
401
+ release() {
402
+ const next = queue.shift();
403
+ if (next) {
404
+ next();
405
+ }
406
+ else {
407
+ activeCount--;
408
+ }
409
+ },
410
+ /**
411
+ * Get current active count
412
+ */
413
+ getActiveCount() {
414
+ return activeCount;
415
+ },
416
+ /**
417
+ * Get queue length
418
+ */
419
+ getQueueLength() {
420
+ return queue.length;
421
+ },
422
+ };
423
+ }
424
+ /**
425
+ * Check if a URL is a new tab page (about:blank or chrome://new-tab-page).
426
+ */
427
+ export function is_new_tab_page(url) {
428
+ return (url === 'about:blank' ||
429
+ url === 'chrome://new-tab-page/' ||
430
+ url === 'chrome://new-tab-page');
431
+ }
432
+ /**
433
+ * Check if a URL matches a domain pattern. SECURITY CRITICAL.
434
+ *
435
+ * Supports optional glob patterns and schemes:
436
+ * - *.example.com will match sub.example.com and example.com
437
+ * - *google.com will match google.com, agoogle.com, and www.google.com
438
+ * - http*://example.com will match http://example.com, https://example.com
439
+ * - chrome-extension://* will match chrome-extension://aaaaaaaaaaaa and chrome-extension://bbbbbbbbbbbbb
440
+ *
441
+ * When no scheme is specified, https is used by default for security.
442
+ * For example, 'example.com' will match 'https://example.com' but not 'http://example.com'.
443
+ *
444
+ * Note: New tab pages (about:blank, chrome://new-tab-page) must be handled at the callsite, not inside this function.
445
+ */
446
+ export function match_url_with_domain_pattern(url, domain_pattern, log_warnings = false) {
447
+ try {
448
+ // Note: new tab pages should be handled at the callsite, not here
449
+ if (is_new_tab_page(url)) {
450
+ return false;
451
+ }
452
+ const parsed_url = new URL(url);
453
+ // Extract only the hostname and scheme components
454
+ const scheme = parsed_url.protocol.replace(':', '').toLowerCase();
455
+ const domain = parsed_url.hostname.toLowerCase();
456
+ if (!scheme || !domain) {
457
+ return false;
458
+ }
459
+ // Normalize the domain pattern
460
+ const normalizedPattern = domain_pattern.toLowerCase();
461
+ // Handle pattern with scheme
462
+ let pattern_scheme;
463
+ let pattern_domain;
464
+ if (normalizedPattern.includes('://')) {
465
+ const parts = normalizedPattern.split('://');
466
+ pattern_scheme = parts[0];
467
+ pattern_domain = parts[1];
468
+ }
469
+ else {
470
+ pattern_scheme = 'https'; // Default to matching only https for security
471
+ pattern_domain = normalizedPattern;
472
+ }
473
+ // Handle port in pattern (we strip ports from patterns since we already extracted only the hostname from the URL)
474
+ if (pattern_domain.includes(':') && !pattern_domain.startsWith(':')) {
475
+ pattern_domain = pattern_domain.split(':')[0];
476
+ }
477
+ // If scheme doesn't match using minimatch, return false
478
+ const minimatch = require('minimatch');
479
+ if (!minimatch(scheme, pattern_scheme)) {
480
+ return false;
481
+ }
482
+ // Check for exact match
483
+ if (pattern_domain === '*' || domain === pattern_domain) {
484
+ return true;
485
+ }
486
+ // Handle glob patterns
487
+ if (pattern_domain.includes('*')) {
488
+ // Check for unsafe glob patterns
489
+ // First, check for patterns like *.*.domain which are unsafe
490
+ if ((pattern_domain.match(/\*\./g) || []).length > 1 ||
491
+ (pattern_domain.match(/\.\*/g) || []).length > 1) {
492
+ if (log_warnings) {
493
+ console.error(`⛔️ Multiple wildcards in pattern=[${domain_pattern}] are not supported`);
494
+ }
495
+ return false; // Don't match unsafe patterns
496
+ }
497
+ // Check for wildcards in TLD part (example.*)
498
+ if (pattern_domain.endsWith('.*')) {
499
+ if (log_warnings) {
500
+ console.error(`⛔️ Wildcard TLDs like in pattern=[${domain_pattern}] are not supported for security`);
501
+ }
502
+ return false; // Don't match unsafe patterns
503
+ }
504
+ // Then check for embedded wildcards
505
+ const bare_domain = pattern_domain.replace('*.', '');
506
+ if (bare_domain.includes('*')) {
507
+ if (log_warnings) {
508
+ console.error(`⛔️ Only *.domain style patterns are supported, ignoring pattern=[${domain_pattern}]`);
509
+ }
510
+ return false; // Don't match unsafe patterns
511
+ }
512
+ // Special handling so that *.google.com also matches bare google.com
513
+ if (pattern_domain.startsWith('*.')) {
514
+ const base = pattern_domain.slice(2); // Remove '*.'
515
+ if (domain === base || domain.endsWith('.' + base)) {
516
+ return true;
517
+ }
518
+ }
519
+ // Use minimatch for pattern matching
520
+ return minimatch(domain, pattern_domain);
521
+ }
522
+ // No match
523
+ return false;
524
+ }
525
+ catch (error) {
526
+ // Invalid URL or pattern
527
+ return false;
528
+ }
529
+ }