browser-use 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
package/dist/config.js ADDED
@@ -0,0 +1,430 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { config as loadEnv } from 'dotenv';
6
+ import { createLogger } from './logging-config.js';
7
+ loadEnv();
8
+ const logger = createLogger('browser_use.config');
9
+ const expand_user = (value) => value.replace(/^~(?=$|\/|\\)/, os.homedir());
10
+ const resolve_path = (value) => path.resolve(expand_user(value));
11
+ const string_to_bool = (value, defaultValue = false) => {
12
+ if (value === undefined || value === null || value === '') {
13
+ return defaultValue;
14
+ }
15
+ return ['true', '1', 't', 'y', 'yes'].includes(value.toLowerCase());
16
+ };
17
+ let docker_cache = null;
18
+ export const is_running_in_docker = () => {
19
+ if (docker_cache !== null) {
20
+ return docker_cache;
21
+ }
22
+ try {
23
+ if (fs.existsSync('/.dockerenv')) {
24
+ docker_cache = true;
25
+ return true;
26
+ }
27
+ }
28
+ catch {
29
+ /* no-op */
30
+ }
31
+ try {
32
+ const cgroup = fs.readFileSync('/proc/1/cgroup', 'utf-8').toLowerCase();
33
+ if (cgroup.includes('docker') || cgroup.includes('kubepods')) {
34
+ docker_cache = true;
35
+ return true;
36
+ }
37
+ }
38
+ catch {
39
+ /* no-op */
40
+ }
41
+ try {
42
+ const cmdline = fs.readFileSync('/proc/1/cmdline', 'utf-8').toLowerCase();
43
+ if (cmdline.includes('py') ||
44
+ cmdline.includes('uv') ||
45
+ cmdline.includes('app')) {
46
+ docker_cache = true;
47
+ return true;
48
+ }
49
+ }
50
+ catch {
51
+ /* no-op */
52
+ }
53
+ try {
54
+ const processes = fs
55
+ .readdirSync('/proc')
56
+ .filter((entry) => /^\d+$/.test(entry));
57
+ if (processes.length > 0 && processes.length < 10) {
58
+ docker_cache = true;
59
+ return true;
60
+ }
61
+ }
62
+ catch {
63
+ /* no-op */
64
+ }
65
+ docker_cache = false;
66
+ return false;
67
+ };
68
+ const ensure_dir = (target) => fs.mkdirSync(target, { recursive: true });
69
+ class OldConfig {
70
+ _dirs_created = false;
71
+ get BROWSER_USE_LOGGING_LEVEL() {
72
+ return (process.env.BROWSER_USE_LOGGING_LEVEL ?? 'info').toLowerCase();
73
+ }
74
+ get ANONYMIZED_TELEMETRY() {
75
+ return string_to_bool(process.env.ANONYMIZED_TELEMETRY, true);
76
+ }
77
+ get BROWSER_USE_CLOUD_SYNC() {
78
+ const value = process.env.BROWSER_USE_CLOUD_SYNC;
79
+ return value ? string_to_bool(value) : this.ANONYMIZED_TELEMETRY;
80
+ }
81
+ get BROWSER_USE_CLOUD_API_URL() {
82
+ const url = process.env.BROWSER_USE_CLOUD_API_URL ?? 'https://api.browser-use.com';
83
+ if (!url.includes('://')) {
84
+ throw new Error('BROWSER_USE_CLOUD_API_URL must be a valid URL');
85
+ }
86
+ return url;
87
+ }
88
+ get BROWSER_USE_CLOUD_UI_URL() {
89
+ const url = process.env.BROWSER_USE_CLOUD_UI_URL ?? '';
90
+ if (url && !url.includes('://')) {
91
+ throw new Error('BROWSER_USE_CLOUD_UI_URL must be a valid URL if set');
92
+ }
93
+ return url;
94
+ }
95
+ get XDG_CACHE_HOME() {
96
+ return resolve_path(process.env.XDG_CACHE_HOME ?? '~/.cache');
97
+ }
98
+ get XDG_CONFIG_HOME() {
99
+ return resolve_path(process.env.XDG_CONFIG_HOME ?? '~/.config');
100
+ }
101
+ get BROWSER_USE_CONFIG_DIR() {
102
+ const configured = process.env.BROWSER_USE_CONFIG_DIR;
103
+ const dir = configured
104
+ ? resolve_path(configured)
105
+ : path.join(this.XDG_CONFIG_HOME, 'browseruse');
106
+ this._ensure_dirs(dir);
107
+ return dir;
108
+ }
109
+ get BROWSER_USE_CONFIG_FILE() {
110
+ return path.join(this.BROWSER_USE_CONFIG_DIR, 'config.json');
111
+ }
112
+ get BROWSER_USE_PROFILES_DIR() {
113
+ const dir = path.join(this.BROWSER_USE_CONFIG_DIR, 'profiles');
114
+ this._ensure_dirs(dir);
115
+ return dir;
116
+ }
117
+ get BROWSER_USE_DEFAULT_USER_DATA_DIR() {
118
+ return path.join(this.BROWSER_USE_PROFILES_DIR, 'default');
119
+ }
120
+ get BROWSER_USE_EXTENSIONS_DIR() {
121
+ const dir = path.join(this.BROWSER_USE_CONFIG_DIR, 'extensions');
122
+ this._ensure_dirs(dir);
123
+ return dir;
124
+ }
125
+ get OPENAI_API_KEY() {
126
+ return process.env.OPENAI_API_KEY ?? '';
127
+ }
128
+ get ANTHROPIC_API_KEY() {
129
+ return process.env.ANTHROPIC_API_KEY ?? '';
130
+ }
131
+ get GOOGLE_API_KEY() {
132
+ return process.env.GOOGLE_API_KEY ?? '';
133
+ }
134
+ get DEEPSEEK_API_KEY() {
135
+ return process.env.DEEPSEEK_API_KEY ?? '';
136
+ }
137
+ get GROK_API_KEY() {
138
+ return process.env.GROK_API_KEY ?? '';
139
+ }
140
+ get NOVITA_API_KEY() {
141
+ return process.env.NOVITA_API_KEY ?? '';
142
+ }
143
+ get AZURE_OPENAI_ENDPOINT() {
144
+ return process.env.AZURE_OPENAI_ENDPOINT ?? '';
145
+ }
146
+ get AZURE_OPENAI_KEY() {
147
+ return process.env.AZURE_OPENAI_KEY ?? '';
148
+ }
149
+ get SKIP_LLM_API_KEY_VERIFICATION() {
150
+ return string_to_bool(process.env.SKIP_LLM_API_KEY_VERIFICATION, false);
151
+ }
152
+ get IN_DOCKER() {
153
+ return (string_to_bool(process.env.IN_DOCKER, false) || is_running_in_docker());
154
+ }
155
+ get IS_IN_EVALS() {
156
+ return string_to_bool(process.env.IS_IN_EVALS, false);
157
+ }
158
+ get WIN_FONT_DIR() {
159
+ return process.env.WIN_FONT_DIR ?? 'C:\\Windows\\Fonts';
160
+ }
161
+ _ensure_dirs(base_dir) {
162
+ if (!this._dirs_created) {
163
+ const config_dir = base_dir ??
164
+ (process.env.BROWSER_USE_CONFIG_DIR
165
+ ? resolve_path(process.env.BROWSER_USE_CONFIG_DIR)
166
+ : path.join(this.XDG_CONFIG_HOME, 'browseruse'));
167
+ ensure_dir(config_dir);
168
+ ensure_dir(path.join(config_dir, 'profiles'));
169
+ ensure_dir(path.join(config_dir, 'extensions'));
170
+ this._dirs_created = true;
171
+ }
172
+ }
173
+ }
174
+ class FlatEnvConfig {
175
+ get BROWSER_USE_LOGGING_LEVEL() {
176
+ return process.env.BROWSER_USE_LOGGING_LEVEL ?? 'info';
177
+ }
178
+ get ANONYMIZED_TELEMETRY() {
179
+ return string_to_bool(process.env.ANONYMIZED_TELEMETRY, true);
180
+ }
181
+ get BROWSER_USE_CLOUD_SYNC() {
182
+ const value = process.env.BROWSER_USE_CLOUD_SYNC;
183
+ return value ? string_to_bool(value) : null;
184
+ }
185
+ get BROWSER_USE_CLOUD_API_URL() {
186
+ return (process.env.BROWSER_USE_CLOUD_API_URL ?? 'https://api.browser-use.com');
187
+ }
188
+ get BROWSER_USE_CLOUD_UI_URL() {
189
+ return process.env.BROWSER_USE_CLOUD_UI_URL ?? '';
190
+ }
191
+ get XDG_CACHE_HOME() {
192
+ return resolve_path(process.env.XDG_CACHE_HOME ?? '~/.cache');
193
+ }
194
+ get XDG_CONFIG_HOME() {
195
+ return resolve_path(process.env.XDG_CONFIG_HOME ?? '~/.config');
196
+ }
197
+ get BROWSER_USE_CONFIG_DIR() {
198
+ return process.env.BROWSER_USE_CONFIG_DIR
199
+ ? resolve_path(process.env.BROWSER_USE_CONFIG_DIR)
200
+ : null;
201
+ }
202
+ get OPENAI_API_KEY() {
203
+ return process.env.OPENAI_API_KEY ?? '';
204
+ }
205
+ get ANTHROPIC_API_KEY() {
206
+ return process.env.ANTHROPIC_API_KEY ?? '';
207
+ }
208
+ get GOOGLE_API_KEY() {
209
+ return process.env.GOOGLE_API_KEY ?? '';
210
+ }
211
+ get DEEPSEEK_API_KEY() {
212
+ return process.env.DEEPSEEK_API_KEY ?? '';
213
+ }
214
+ get GROK_API_KEY() {
215
+ return process.env.GROK_API_KEY ?? '';
216
+ }
217
+ get NOVITA_API_KEY() {
218
+ return process.env.NOVITA_API_KEY ?? '';
219
+ }
220
+ get AZURE_OPENAI_ENDPOINT() {
221
+ return process.env.AZURE_OPENAI_ENDPOINT ?? '';
222
+ }
223
+ get AZURE_OPENAI_KEY() {
224
+ return process.env.AZURE_OPENAI_KEY ?? '';
225
+ }
226
+ get SKIP_LLM_API_KEY_VERIFICATION() {
227
+ return string_to_bool(process.env.SKIP_LLM_API_KEY_VERIFICATION, false);
228
+ }
229
+ get IN_DOCKER() {
230
+ const value = process.env.IN_DOCKER;
231
+ return value === undefined ? null : string_to_bool(value);
232
+ }
233
+ get IS_IN_EVALS() {
234
+ return string_to_bool(process.env.IS_IN_EVALS, false);
235
+ }
236
+ get WIN_FONT_DIR() {
237
+ return process.env.WIN_FONT_DIR ?? 'C:\\Windows\\Fonts';
238
+ }
239
+ get BROWSER_USE_CONFIG_PATH() {
240
+ return process.env.BROWSER_USE_CONFIG_PATH
241
+ ? resolve_path(process.env.BROWSER_USE_CONFIG_PATH)
242
+ : null;
243
+ }
244
+ get BROWSER_USE_HEADLESS() {
245
+ const value = process.env.BROWSER_USE_HEADLESS;
246
+ return value === undefined ? null : string_to_bool(value);
247
+ }
248
+ get BROWSER_USE_ALLOWED_DOMAINS() {
249
+ return process.env.BROWSER_USE_ALLOWED_DOMAINS ?? null;
250
+ }
251
+ get BROWSER_USE_LLM_MODEL() {
252
+ return process.env.BROWSER_USE_LLM_MODEL ?? null;
253
+ }
254
+ }
255
+ const create_default_config = () => {
256
+ logger.info('Creating fresh default config.json');
257
+ const profile_id = randomUUID();
258
+ const llm_id = randomUUID();
259
+ const agent_id = randomUUID();
260
+ return {
261
+ browser_profile: {
262
+ [profile_id]: {
263
+ id: profile_id,
264
+ default: true,
265
+ created_at: new Date().toISOString(),
266
+ headless: false,
267
+ user_data_dir: null,
268
+ allowed_domains: null,
269
+ downloads_path: null,
270
+ },
271
+ },
272
+ llm: {
273
+ [llm_id]: {
274
+ id: llm_id,
275
+ default: true,
276
+ created_at: new Date().toISOString(),
277
+ model: 'gpt-4o',
278
+ api_key: 'your-openai-api-key-here',
279
+ temperature: null,
280
+ max_tokens: null,
281
+ },
282
+ },
283
+ agent: {
284
+ [agent_id]: {
285
+ id: agent_id,
286
+ default: true,
287
+ created_at: new Date().toISOString(),
288
+ max_steps: null,
289
+ use_vision: null,
290
+ system_prompt: null,
291
+ },
292
+ },
293
+ };
294
+ };
295
+ const looks_like_new_format = (data) => data &&
296
+ typeof data === 'object' &&
297
+ ['browser_profile', 'llm', 'agent'].every((key) => typeof data[key] === 'object') &&
298
+ Object.values(data.browser_profile || {}).every((entry) => typeof entry === 'object' && 'id' in entry);
299
+ const load_and_migrate_config = (config_path) => {
300
+ if (!fs.existsSync(config_path)) {
301
+ const parent = path.dirname(config_path);
302
+ ensure_dir(parent);
303
+ const fresh = create_default_config();
304
+ fs.writeFileSync(config_path, JSON.stringify(fresh, null, 2), 'utf-8');
305
+ return fresh;
306
+ }
307
+ try {
308
+ const raw = JSON.parse(fs.readFileSync(config_path, 'utf-8'));
309
+ if (looks_like_new_format(raw)) {
310
+ return raw;
311
+ }
312
+ logger.info(`Old config format detected at ${config_path}, creating fresh config`);
313
+ const fresh = create_default_config();
314
+ fs.writeFileSync(config_path, JSON.stringify(fresh, null, 2), 'utf-8');
315
+ return fresh;
316
+ }
317
+ catch (error) {
318
+ logger.error(`Failed to load config from ${config_path}: ${error.message}, creating fresh config`);
319
+ const fresh = create_default_config();
320
+ try {
321
+ fs.writeFileSync(config_path, JSON.stringify(fresh, null, 2), 'utf-8');
322
+ }
323
+ catch (write_error) {
324
+ logger.error(`Failed to write fresh config: ${write_error.message}`);
325
+ }
326
+ return fresh;
327
+ }
328
+ };
329
+ class ConfigCore {
330
+ _get_config_path() {
331
+ const env = new FlatEnvConfig();
332
+ if (env.BROWSER_USE_CONFIG_PATH) {
333
+ return env.BROWSER_USE_CONFIG_PATH;
334
+ }
335
+ if (env.BROWSER_USE_CONFIG_DIR) {
336
+ return path.join(env.BROWSER_USE_CONFIG_DIR, 'config.json');
337
+ }
338
+ return path.join(env.XDG_CONFIG_HOME, 'browseruse', 'config.json');
339
+ }
340
+ _get_db_config() {
341
+ return load_and_migrate_config(this._get_config_path());
342
+ }
343
+ _get_default_entry(records) {
344
+ for (const entry of Object.values(records)) {
345
+ if (entry.default) {
346
+ return { ...entry };
347
+ }
348
+ }
349
+ const [first] = Object.values(records);
350
+ return first ? { ...first } : {};
351
+ }
352
+ _get_default_profile() {
353
+ return this._get_default_entry(this._get_db_config().browser_profile);
354
+ }
355
+ _get_default_llm() {
356
+ return this._get_default_entry(this._get_db_config().llm);
357
+ }
358
+ _get_default_agent() {
359
+ return this._get_default_entry(this._get_db_config().agent);
360
+ }
361
+ _load_config() {
362
+ const config = {
363
+ browser_profile: this._get_default_profile(),
364
+ llm: this._get_default_llm(),
365
+ agent: this._get_default_agent(),
366
+ };
367
+ const env = new FlatEnvConfig();
368
+ if (env.BROWSER_USE_HEADLESS !== null) {
369
+ config.browser_profile.headless = env.BROWSER_USE_HEADLESS;
370
+ }
371
+ if (env.BROWSER_USE_ALLOWED_DOMAINS) {
372
+ config.browser_profile.allowed_domains =
373
+ env.BROWSER_USE_ALLOWED_DOMAINS.split(',')
374
+ .map((domain) => domain.trim())
375
+ .filter(Boolean);
376
+ }
377
+ if (env.OPENAI_API_KEY) {
378
+ config.llm.api_key = env.OPENAI_API_KEY;
379
+ }
380
+ if (env.BROWSER_USE_LLM_MODEL) {
381
+ config.llm.model = env.BROWSER_USE_LLM_MODEL;
382
+ }
383
+ return config;
384
+ }
385
+ _ensure_dirs() {
386
+ new OldConfig()._ensure_dirs();
387
+ }
388
+ load_config() {
389
+ return this._load_config();
390
+ }
391
+ get_default_profile() {
392
+ return this._get_default_profile();
393
+ }
394
+ get_default_llm() {
395
+ return this._get_default_llm();
396
+ }
397
+ get_default_agent() {
398
+ return this._get_default_agent();
399
+ }
400
+ }
401
+ const config_handler = {
402
+ get(target, prop, receiver) {
403
+ if (typeof prop !== 'string') {
404
+ return Reflect.get(target, prop, receiver);
405
+ }
406
+ const old = new OldConfig();
407
+ if (prop in old) {
408
+ const value = old[prop];
409
+ return typeof value === 'function'
410
+ ? value.bind(old)
411
+ : value;
412
+ }
413
+ const env = new FlatEnvConfig();
414
+ if (prop in env) {
415
+ const value = env[prop];
416
+ return typeof value === 'function'
417
+ ? value.bind(env)
418
+ : value;
419
+ }
420
+ const coreValue = target[prop];
421
+ if (typeof coreValue === 'function') {
422
+ return coreValue.bind(target);
423
+ }
424
+ return coreValue;
425
+ },
426
+ };
427
+ export const CONFIG = new Proxy(new ConfigCore(), config_handler);
428
+ export const load_browser_use_config = () => CONFIG.load_config();
429
+ export const get_default_profile = (config) => config.browser_profile ?? {};
430
+ export const get_default_llm = (config) => config.llm ?? {};
@@ -0,0 +1,3 @@
1
+ export * from './service.js';
2
+ export * from './views.js';
3
+ export * from './registry/index.js';
@@ -0,0 +1,3 @@
1
+ export * from './service.js';
2
+ export * from './views.js';
3
+ export * from './registry/index.js';
@@ -0,0 +1,2 @@
1
+ export * from './service.js';
2
+ export * from './views.js';
@@ -0,0 +1,2 @@
1
+ export * from './service.js';
2
+ export * from './views.js';
@@ -0,0 +1,45 @@
1
+ import { type ZodTypeAny } from 'zod';
2
+ import type { Page } from '../../browser/types.js';
3
+ import { FileSystem } from '../../filesystem/file-system.js';
4
+ import { ActionModel, RegisteredAction } from './views.js';
5
+ import type { BrowserSession } from '../../browser/session.js';
6
+ type BaseChatModel = unknown;
7
+ export interface SensitiveDataMap {
8
+ [key: string]: string | Record<string, string>;
9
+ }
10
+ export interface ExecuteActionContext<Context> {
11
+ context?: Context;
12
+ browser_session?: BrowserSession | null;
13
+ page_extraction_llm?: BaseChatModel | null;
14
+ file_system?: FileSystem | null;
15
+ available_file_paths?: string[] | null;
16
+ sensitive_data?: SensitiveDataMap | null;
17
+ signal?: AbortSignal | null;
18
+ }
19
+ export type RegistryActionHandler<Params = any, Context = unknown> = (params: Params, ctx: ExecuteActionContext<Context> & {
20
+ page?: Page | null;
21
+ has_sensitive_data?: boolean;
22
+ }) => Promise<unknown> | unknown;
23
+ export interface ActionOptions {
24
+ param_model?: ZodTypeAny;
25
+ domains?: string[] | null;
26
+ allowed_domains?: string[] | null;
27
+ page_filter?: ((page: Page) => boolean) | null;
28
+ }
29
+ export declare class Registry<Context = unknown> {
30
+ private registry;
31
+ private excludeActions;
32
+ constructor(exclude_actions?: string[] | null);
33
+ action(description: string, options?: ActionOptions): <Params = any>(handler: RegistryActionHandler<Params, Context>) => RegistryActionHandler<Params, Context>;
34
+ get_action(action_name: string): RegisteredAction | null;
35
+ get_all_actions(): Map<string, RegisteredAction>;
36
+ execute_action: (...args: any[]) => any;
37
+ private replace_sensitive_data;
38
+ private log_sensitive_data_usage;
39
+ create_action_model(options?: {
40
+ include_actions?: string[] | null;
41
+ page?: Page | null;
42
+ }): typeof ActionModel;
43
+ get_prompt_description(page?: Page | null): string;
44
+ }
45
+ export {};
@@ -0,0 +1,184 @@
1
+ import { z } from 'zod';
2
+ import { createLogger } from '../../logging-config.js';
3
+ import { observe_debug } from '../../observability.js';
4
+ import { time_execution_async } from '../../utils.js';
5
+ import { is_new_tab_page, match_url_with_domain_pattern } from '../../utils.js';
6
+ import { ActionModel, ActionRegistry, RegisteredAction } from './views.js';
7
+ const logger = createLogger('browser_use.controller.registry');
8
+ const isAbortError = (error) => error instanceof Error && error.name === 'AbortError';
9
+ const createAbortError = (reason) => {
10
+ if (isAbortError(reason)) {
11
+ return reason;
12
+ }
13
+ const message = reason instanceof Error ? reason.message : 'Operation aborted';
14
+ const error = new Error(message);
15
+ error.name = 'AbortError';
16
+ if (reason !== undefined) {
17
+ error.cause = reason;
18
+ }
19
+ return error;
20
+ };
21
+ const wrapActionExecutionError = (actionName, error) => {
22
+ const message = error instanceof Error ? error.message : String(error);
23
+ const wrapped = new Error(`Error executing action ${actionName}: ${message}`);
24
+ if (error !== undefined) {
25
+ wrapped.cause = error;
26
+ }
27
+ return wrapped;
28
+ };
29
+ export class Registry {
30
+ registry = new ActionRegistry();
31
+ excludeActions;
32
+ constructor(exclude_actions = null) {
33
+ this.excludeActions = new Set(exclude_actions ?? []);
34
+ }
35
+ action(description, options = {}) {
36
+ const schema = options.param_model ?? z.object({}).strict();
37
+ const domains = options.allowed_domains ?? options.domains ?? null;
38
+ const pageFilter = options.page_filter ?? null;
39
+ return (handler) => {
40
+ if (this.excludeActions.has(handler.name)) {
41
+ return handler;
42
+ }
43
+ const action = new RegisteredAction(handler.name, description, handler, schema, domains, pageFilter);
44
+ this.registry.register(action);
45
+ return handler;
46
+ };
47
+ }
48
+ get_action(action_name) {
49
+ return this.registry.get(action_name);
50
+ }
51
+ get_all_actions() {
52
+ return this.registry.actionsMap;
53
+ }
54
+ execute_action = observe_debug({
55
+ name: 'execute_action',
56
+ ignore_input: true,
57
+ ignore_output: true,
58
+ })(time_execution_async('--execute_action')(async (action_name, params, { browser_session = null, page_extraction_llm = null, file_system = null, sensitive_data = null, available_file_paths = null, signal = null, context = null, } = {}) => {
59
+ const action = this.registry.get(action_name);
60
+ if (!action) {
61
+ throw new Error(`Action ${action_name} not found`);
62
+ }
63
+ const parsed = action.paramSchema.safeParse(params);
64
+ if (!parsed.success) {
65
+ throw new Error(`Invalid parameters for action ${action_name}: ${parsed.error.message}`);
66
+ }
67
+ let validatedParams = parsed.data;
68
+ let currentUrl = null;
69
+ if (browser_session?.agent_current_page?.url) {
70
+ currentUrl = browser_session.agent_current_page.url();
71
+ }
72
+ else if (browser_session?.get_current_page) {
73
+ const currentPage = await browser_session.get_current_page();
74
+ currentUrl = currentPage?.url() ?? null;
75
+ }
76
+ if (sensitive_data) {
77
+ validatedParams = this.replace_sensitive_data(validatedParams, sensitive_data, currentUrl);
78
+ }
79
+ let page = null;
80
+ if (browser_session?.get_current_page) {
81
+ page = await browser_session.get_current_page();
82
+ }
83
+ const ctx = {
84
+ context,
85
+ browser_session,
86
+ browser: browser_session,
87
+ browser_context: browser_session,
88
+ page,
89
+ page_extraction_llm,
90
+ file_system,
91
+ available_file_paths,
92
+ signal,
93
+ has_sensitive_data: action_name === 'input_text' && Boolean(sensitive_data),
94
+ };
95
+ if (signal?.aborted) {
96
+ throw createAbortError(signal.reason);
97
+ }
98
+ try {
99
+ return await action.handler(validatedParams, ctx);
100
+ }
101
+ catch (error) {
102
+ if (signal?.aborted || isAbortError(error)) {
103
+ throw createAbortError(signal?.reason ?? error);
104
+ }
105
+ throw wrapActionExecutionError(action_name, error);
106
+ }
107
+ }));
108
+ replace_sensitive_data(params, sensitiveData, currentUrl) {
109
+ const secretPattern = /<secret>(.*?)<\/secret>/g;
110
+ const applicableSecrets = {};
111
+ for (const [domainOrKey, content] of Object.entries(sensitiveData)) {
112
+ if (content && typeof content === 'object' && !Array.isArray(content)) {
113
+ if (currentUrl &&
114
+ !is_new_tab_page(currentUrl) &&
115
+ match_url_with_domain_pattern(currentUrl, domainOrKey)) {
116
+ Object.assign(applicableSecrets, content);
117
+ }
118
+ }
119
+ else if (typeof content === 'string') {
120
+ applicableSecrets[domainOrKey] = content;
121
+ }
122
+ }
123
+ const cloneValue = (value) => {
124
+ if (Array.isArray(value)) {
125
+ return value.map((item) => cloneValue(item));
126
+ }
127
+ if (value && typeof value === 'object') {
128
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cloneValue(v)]));
129
+ }
130
+ return value;
131
+ };
132
+ const processed = cloneValue(params);
133
+ const replaced = new Set();
134
+ const missing = new Set();
135
+ const traverse = (value) => {
136
+ if (typeof value === 'string') {
137
+ return value.replace(secretPattern, (_, placeholder) => {
138
+ if (placeholder in applicableSecrets) {
139
+ replaced.add(placeholder);
140
+ return applicableSecrets[placeholder];
141
+ }
142
+ missing.add(placeholder);
143
+ return `<secret>${placeholder}</secret>`;
144
+ });
145
+ }
146
+ if (Array.isArray(value)) {
147
+ return value.map(traverse);
148
+ }
149
+ if (value && typeof value === 'object') {
150
+ for (const [key, val] of Object.entries(value)) {
151
+ value[key] = traverse(val);
152
+ }
153
+ }
154
+ return value;
155
+ };
156
+ traverse(processed);
157
+ this.log_sensitive_data_usage(replaced, currentUrl);
158
+ if (missing.size > 0) {
159
+ logger.warning(`Missing or empty keys in sensitive_data dictionary: ${Array.from(missing).join(', ')}`);
160
+ }
161
+ return processed;
162
+ }
163
+ log_sensitive_data_usage(placeholders, currentUrl) {
164
+ if (!placeholders.size) {
165
+ return;
166
+ }
167
+ const urlInfo = currentUrl && !is_new_tab_page(currentUrl) ? ` on ${currentUrl}` : '';
168
+ logger.info(`🔒 Using sensitive data placeholders: ${Array.from(placeholders).join(', ')}${urlInfo}`);
169
+ }
170
+ create_action_model(options = {}) {
171
+ const { include_actions = null, page = null } = options;
172
+ const availableActions = this.registry.getAvailableActions(page, include_actions);
173
+ class DynamicActionModel extends ActionModel {
174
+ static available_actions = availableActions.map((action) => action.name);
175
+ }
176
+ Object.defineProperty(DynamicActionModel, 'name', {
177
+ value: 'ActionModel',
178
+ });
179
+ return DynamicActionModel;
180
+ }
181
+ get_prompt_description(page) {
182
+ return this.registry.get_prompt_description(page ?? undefined);
183
+ }
184
+ }