browser-use 0.4.0 → 0.6.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 (58) hide show
  1. package/dist/agent/service.js +2 -0
  2. package/dist/agent/system_prompt.md +269 -0
  3. package/dist/agent/system_prompt_anthropic_flash.md +240 -0
  4. package/dist/agent/system_prompt_browser_use.md +18 -0
  5. package/dist/agent/system_prompt_browser_use_flash.md +15 -0
  6. package/dist/agent/system_prompt_browser_use_no_thinking.md +17 -0
  7. package/dist/agent/system_prompt_flash.md +16 -0
  8. package/dist/agent/system_prompt_flash_anthropic.md +30 -0
  9. package/dist/agent/system_prompt_no_thinking.md +245 -0
  10. package/dist/browser/cloud/index.d.ts +1 -0
  11. package/dist/browser/cloud/index.js +1 -0
  12. package/dist/browser/cloud/management.d.ts +130 -0
  13. package/dist/browser/cloud/management.js +140 -0
  14. package/dist/browser/events.d.ts +61 -3
  15. package/dist/browser/events.js +66 -0
  16. package/dist/browser/profile.d.ts +1 -0
  17. package/dist/browser/profile.js +25 -8
  18. package/dist/browser/session.d.ts +59 -2
  19. package/dist/browser/session.js +943 -131
  20. package/dist/browser/watchdogs/base.js +34 -1
  21. package/dist/browser/watchdogs/captcha-watchdog.d.ts +26 -0
  22. package/dist/browser/watchdogs/captcha-watchdog.js +151 -0
  23. package/dist/browser/watchdogs/index.d.ts +1 -0
  24. package/dist/browser/watchdogs/index.js +1 -0
  25. package/dist/browser/watchdogs/screenshot-watchdog.js +4 -3
  26. package/dist/cli.d.ts +120 -0
  27. package/dist/cli.js +1816 -4
  28. package/dist/controller/service.js +106 -362
  29. package/dist/controller/views.d.ts +9 -6
  30. package/dist/controller/views.js +8 -5
  31. package/dist/dom/dom_tree/index.js +24 -11
  32. package/dist/filesystem/file-system.js +1 -1
  33. package/dist/llm/litellm/chat.d.ts +11 -0
  34. package/dist/llm/litellm/chat.js +16 -0
  35. package/dist/llm/litellm/index.d.ts +1 -0
  36. package/dist/llm/litellm/index.js +1 -0
  37. package/dist/llm/models.js +29 -3
  38. package/dist/llm/oci-raw/chat.d.ts +64 -0
  39. package/dist/llm/oci-raw/chat.js +350 -0
  40. package/dist/llm/oci-raw/index.d.ts +2 -0
  41. package/dist/llm/oci-raw/index.js +2 -0
  42. package/dist/llm/oci-raw/serializer.d.ts +12 -0
  43. package/dist/llm/oci-raw/serializer.js +128 -0
  44. package/dist/mcp/server.d.ts +1 -0
  45. package/dist/mcp/server.js +62 -13
  46. package/dist/skill-cli/direct.d.ts +100 -0
  47. package/dist/skill-cli/direct.js +984 -0
  48. package/dist/skill-cli/index.d.ts +2 -0
  49. package/dist/skill-cli/index.js +2 -0
  50. package/dist/skill-cli/server.d.ts +2 -0
  51. package/dist/skill-cli/server.js +472 -11
  52. package/dist/skill-cli/tunnel.d.ts +61 -0
  53. package/dist/skill-cli/tunnel.js +257 -0
  54. package/dist/sync/auth.d.ts +8 -0
  55. package/dist/sync/auth.js +12 -0
  56. package/dist/utils.d.ts +1 -1
  57. package/dist/utils.js +2 -1
  58. package/package.json +22 -4
@@ -0,0 +1,257 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawn, spawnSync } from 'node:child_process';
5
+ const TUNNEL_URL_PATTERN = /(https:\/\/\S+\.trycloudflare\.com)/;
6
+ const DEFAULT_TUNNELS_DIR = path.join(os.homedir(), '.browser-use', 'tunnels');
7
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
+ const findSystemBinary = (binary) => {
9
+ const command = process.platform === 'win32' ? 'where' : 'which';
10
+ const result = spawnSync(command, [binary], {
11
+ encoding: 'utf8',
12
+ stdio: ['ignore', 'pipe', 'ignore'],
13
+ });
14
+ if (result.status !== 0) {
15
+ return null;
16
+ }
17
+ return (result.stdout
18
+ .split(/\r?\n/)
19
+ .map((line) => line.trim())
20
+ .find(Boolean) ?? null);
21
+ };
22
+ export class TunnelManager {
23
+ tunnel_dir;
24
+ binary_resolver;
25
+ spawn_impl;
26
+ sleep_impl;
27
+ is_process_alive_impl;
28
+ kill_process_impl;
29
+ binary_path = null;
30
+ constructor(options = {}) {
31
+ this.tunnel_dir = options.tunnel_dir ?? DEFAULT_TUNNELS_DIR;
32
+ this.binary_resolver = options.binary_resolver ?? findSystemBinary;
33
+ this.spawn_impl = options.spawn_impl ?? spawn;
34
+ this.sleep_impl = options.sleep_impl ?? sleep;
35
+ this.is_process_alive_impl =
36
+ options.is_process_alive ?? default_is_process_alive;
37
+ this.kill_process_impl = options.kill_process ?? default_kill_process;
38
+ }
39
+ get_tunnel_file(port) {
40
+ return path.join(this.tunnel_dir, `${port}.json`);
41
+ }
42
+ get_tunnel_log_file(port) {
43
+ return path.join(this.tunnel_dir, `${port}.log`);
44
+ }
45
+ save_tunnel_info(port, pid, url) {
46
+ fs.mkdirSync(this.tunnel_dir, { recursive: true });
47
+ fs.writeFileSync(this.get_tunnel_file(port), JSON.stringify({ port, pid, url }), 'utf-8');
48
+ }
49
+ load_tunnel_info(port) {
50
+ const filePath = this.get_tunnel_file(port);
51
+ if (!fs.existsSync(filePath)) {
52
+ return null;
53
+ }
54
+ try {
55
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
56
+ if (!parsed ||
57
+ typeof parsed.port !== 'number' ||
58
+ typeof parsed.pid !== 'number' ||
59
+ typeof parsed.url !== 'string') {
60
+ fs.rmSync(filePath, { force: true });
61
+ return null;
62
+ }
63
+ if (!this.is_process_alive_impl(parsed.pid)) {
64
+ fs.rmSync(filePath, { force: true });
65
+ fs.rmSync(this.get_tunnel_log_file(port), { force: true });
66
+ return null;
67
+ }
68
+ return {
69
+ port: parsed.port,
70
+ pid: parsed.pid,
71
+ url: parsed.url,
72
+ };
73
+ }
74
+ catch {
75
+ fs.rmSync(filePath, { force: true });
76
+ return null;
77
+ }
78
+ }
79
+ get_binary_path() {
80
+ if (this.binary_path) {
81
+ return this.binary_path;
82
+ }
83
+ const systemBinary = this.binary_resolver('cloudflared');
84
+ if (systemBinary) {
85
+ this.binary_path = systemBinary;
86
+ return systemBinary;
87
+ }
88
+ throw new Error('cloudflared not installed.\n\nInstall cloudflared:\n macOS: brew install cloudflared\n Linux: curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o ~/.local/bin/cloudflared && chmod +x ~/.local/bin/cloudflared\n Windows: winget install Cloudflare.cloudflared');
89
+ }
90
+ is_available() {
91
+ if (this.binary_path) {
92
+ return true;
93
+ }
94
+ return Boolean(this.binary_resolver('cloudflared'));
95
+ }
96
+ get_status() {
97
+ const systemBinary = this.binary_resolver('cloudflared');
98
+ if (systemBinary) {
99
+ return {
100
+ available: true,
101
+ source: 'system',
102
+ path: systemBinary,
103
+ note: 'cloudflared installed',
104
+ };
105
+ }
106
+ return {
107
+ available: false,
108
+ source: null,
109
+ path: null,
110
+ note: 'cloudflared not installed - install it manually before using tunnel',
111
+ };
112
+ }
113
+ async start_tunnel(port) {
114
+ const existing = this.load_tunnel_info(port);
115
+ if (existing) {
116
+ return { url: existing.url, port, existing: true };
117
+ }
118
+ let binaryPath;
119
+ try {
120
+ binaryPath = this.get_binary_path();
121
+ }
122
+ catch (error) {
123
+ return { error: error.message };
124
+ }
125
+ fs.mkdirSync(this.tunnel_dir, { recursive: true });
126
+ const logPath = this.get_tunnel_log_file(port);
127
+ const logFd = fs.openSync(logPath, 'w');
128
+ try {
129
+ const child = this.spawn_impl(binaryPath, ['tunnel', '--url', `http://localhost:${port}`], {
130
+ detached: true,
131
+ stdio: ['ignore', 'ignore', logFd],
132
+ });
133
+ child.unref?.();
134
+ const deadline = Date.now() + 15_000;
135
+ while (Date.now() < deadline) {
136
+ const pid = child.pid;
137
+ if (typeof pid === 'number' && !this.is_process_alive_impl(pid)) {
138
+ const content = fs.existsSync(logPath)
139
+ ? fs.readFileSync(logPath, 'utf-8')
140
+ : '';
141
+ return {
142
+ error: `cloudflared exited unexpectedly: ${content.slice(0, 500)}`,
143
+ };
144
+ }
145
+ const content = fs.existsSync(logPath)
146
+ ? fs.readFileSync(logPath, 'utf-8')
147
+ : '';
148
+ const match = content.match(TUNNEL_URL_PATTERN);
149
+ if (match?.[1] && typeof child.pid === 'number') {
150
+ this.save_tunnel_info(port, child.pid, match[1]);
151
+ return { url: match[1], port };
152
+ }
153
+ await this.sleep_impl(200);
154
+ }
155
+ if (typeof child.pid === 'number') {
156
+ await this.kill_process_impl(child.pid);
157
+ }
158
+ return { error: 'Timed out waiting for cloudflare tunnel URL (15s)' };
159
+ }
160
+ finally {
161
+ fs.closeSync(logFd);
162
+ }
163
+ }
164
+ list_tunnels() {
165
+ const tunnels = [];
166
+ if (!fs.existsSync(this.tunnel_dir)) {
167
+ return { tunnels, count: 0 };
168
+ }
169
+ for (const entry of fs.readdirSync(this.tunnel_dir)) {
170
+ if (!entry.endsWith('.json')) {
171
+ continue;
172
+ }
173
+ const port = Number.parseInt(path.basename(entry, '.json'), 10);
174
+ if (!Number.isFinite(port)) {
175
+ continue;
176
+ }
177
+ const info = this.load_tunnel_info(port);
178
+ if (info) {
179
+ tunnels.push({ port: info.port, url: info.url });
180
+ }
181
+ }
182
+ return { tunnels, count: tunnels.length };
183
+ }
184
+ async stop_tunnel(port) {
185
+ const info = this.load_tunnel_info(port);
186
+ if (!info) {
187
+ return { error: `No tunnel running on port ${port}` };
188
+ }
189
+ await this.kill_process_impl(info.pid);
190
+ fs.rmSync(this.get_tunnel_file(port), { force: true });
191
+ fs.rmSync(this.get_tunnel_log_file(port), { force: true });
192
+ return {
193
+ stopped: port,
194
+ url: info.url,
195
+ };
196
+ }
197
+ async stop_all_tunnels() {
198
+ const stopped = [];
199
+ if (!fs.existsSync(this.tunnel_dir)) {
200
+ return { stopped, count: 0 };
201
+ }
202
+ for (const entry of fs.readdirSync(this.tunnel_dir)) {
203
+ if (!entry.endsWith('.json')) {
204
+ continue;
205
+ }
206
+ const port = Number.parseInt(path.basename(entry, '.json'), 10);
207
+ if (!Number.isFinite(port)) {
208
+ continue;
209
+ }
210
+ const result = await this.stop_tunnel(port);
211
+ if ('stopped' in result) {
212
+ stopped.push(result.stopped);
213
+ }
214
+ }
215
+ return {
216
+ stopped,
217
+ count: stopped.length,
218
+ };
219
+ }
220
+ }
221
+ const default_is_process_alive = (pid) => {
222
+ try {
223
+ process.kill(pid, 0);
224
+ return true;
225
+ }
226
+ catch {
227
+ return false;
228
+ }
229
+ };
230
+ const default_kill_process = async (pid) => {
231
+ try {
232
+ process.kill(pid, 'SIGTERM');
233
+ }
234
+ catch {
235
+ return false;
236
+ }
237
+ for (let attempt = 0; attempt < 10; attempt += 1) {
238
+ if (!default_is_process_alive(pid)) {
239
+ return true;
240
+ }
241
+ await sleep(100);
242
+ }
243
+ try {
244
+ process.kill(pid, 'SIGKILL');
245
+ return true;
246
+ }
247
+ catch {
248
+ return false;
249
+ }
250
+ };
251
+ let tunnel_manager = null;
252
+ export const get_tunnel_manager = () => {
253
+ if (!tunnel_manager) {
254
+ tunnel_manager = new TunnelManager();
255
+ }
256
+ return tunnel_manager;
257
+ };
@@ -1,5 +1,12 @@
1
1
  import { type AxiosInstance } from 'axios';
2
2
  export declare const TEMP_USER_ID = "99999999-9999-9999-9999-999999999999";
3
+ interface CloudAuthConfigData {
4
+ api_token: string | null;
5
+ user_id: string | null;
6
+ authorized_at: string | null;
7
+ }
8
+ export declare const load_cloud_auth_config: () => CloudAuthConfigData;
9
+ export declare const save_cloud_api_token: (api_token: string, user_id?: string | null) => void;
3
10
  export declare class DeviceAuthClient {
4
11
  private readonly baseUrl;
5
12
  private readonly clientId;
@@ -25,3 +32,4 @@ export declare class DeviceAuthClient {
25
32
  };
26
33
  clear_auth(): void;
27
34
  }
35
+ export {};
package/dist/sync/auth.js CHANGED
@@ -36,6 +36,18 @@ const saveAuthConfig = (config) => {
36
36
  /* noop */
37
37
  }
38
38
  };
39
+ export const load_cloud_auth_config = () => loadAuthConfig();
40
+ export const save_cloud_api_token = (api_token, user_id = TEMP_USER_ID) => {
41
+ const normalized = api_token.trim();
42
+ if (!normalized) {
43
+ throw new Error('API token cannot be empty');
44
+ }
45
+ saveAuthConfig({
46
+ api_token: normalized,
47
+ user_id,
48
+ authorized_at: new Date().toISOString(),
49
+ });
50
+ };
39
51
  const getOrCreateDeviceId = () => {
40
52
  ensureDir();
41
53
  try {
package/dist/utils.d.ts CHANGED
@@ -116,7 +116,7 @@ export declare function createSemaphore(maxConcurrent: number): {
116
116
  getQueueLength(): number;
117
117
  };
118
118
  /**
119
- * Check if a URL is a new tab page (about:blank, chrome://new-tab-page, or chrome://newtab).
119
+ * Check if a URL is a new tab page (about:blank/about:newtab/chrome://new-tab-page/chrome://newtab).
120
120
  */
121
121
  export declare function is_new_tab_page(url: string): boolean;
122
122
  /**
package/dist/utils.js CHANGED
@@ -488,10 +488,11 @@ export function createSemaphore(maxConcurrent) {
488
488
  };
489
489
  }
490
490
  /**
491
- * Check if a URL is a new tab page (about:blank, chrome://new-tab-page, or chrome://newtab).
491
+ * Check if a URL is a new tab page (about:blank/about:newtab/chrome://new-tab-page/chrome://newtab).
492
492
  */
493
493
  export function is_new_tab_page(url) {
494
494
  return (url === 'about:blank' ||
495
+ url === 'about:newtab' ||
495
496
  url === 'chrome://new-tab-page/' ||
496
497
  url === 'chrome://new-tab-page' ||
497
498
  url === 'chrome://newtab/' ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-use",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "A TypeScript-first library for programmatic browser control, designed for building AI-powered web agents.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -101,6 +101,16 @@
101
101
  "import": "./dist/llm/google/index.js",
102
102
  "default": "./dist/llm/google/index.js"
103
103
  },
104
+ "./llm/litellm": {
105
+ "types": "./dist/llm/litellm/index.d.ts",
106
+ "import": "./dist/llm/litellm/index.js",
107
+ "default": "./dist/llm/litellm/index.js"
108
+ },
109
+ "./llm/oci-raw": {
110
+ "types": "./dist/llm/oci-raw/index.d.ts",
111
+ "import": "./dist/llm/oci-raw/index.js",
112
+ "default": "./dist/llm/oci-raw/index.js"
113
+ },
104
114
  "./llm/groq": {
105
115
  "types": "./dist/llm/groq/index.d.ts",
106
116
  "import": "./dist/llm/groq/index.js",
@@ -171,6 +181,11 @@
171
181
  "import": "./dist/skill-cli/index.js",
172
182
  "default": "./dist/skill-cli/index.js"
173
183
  },
184
+ "./skill-cli/direct": {
185
+ "types": "./dist/skill-cli/direct.d.ts",
186
+ "import": "./dist/skill-cli/direct.js",
187
+ "default": "./dist/skill-cli/direct.js"
188
+ },
174
189
  "./skills": {
175
190
  "types": "./dist/skills/index.d.ts",
176
191
  "import": "./dist/skills/index.js",
@@ -235,7 +250,8 @@
235
250
  "./dist/*": "./dist/*"
236
251
  },
237
252
  "bin": {
238
- "browser-use": "./dist/cli.js"
253
+ "browser-use": "./dist/cli.js",
254
+ "browser-use-direct": "./dist/skill-cli/direct.js"
239
255
  },
240
256
  "type": "module",
241
257
  "files": [
@@ -264,10 +280,10 @@
264
280
  "license": "MIT",
265
281
  "dependencies": {
266
282
  "@anthropic-ai/sdk": "^0.74.0",
267
- "@aws-sdk/client-bedrock-runtime": "^3.995.0",
283
+ "@aws-sdk/client-bedrock-runtime": "^3.1012.0",
268
284
  "@cfworker/json-schema": "^4.1.1",
269
285
  "@google/genai": "^1.40.0",
270
- "@modelcontextprotocol/sdk": "^1.26.0",
286
+ "@modelcontextprotocol/sdk": "^1.27.1",
271
287
  "adm-zip": "^0.5.16",
272
288
  "axios": "^1.13.4",
273
289
  "canvas": "^3.2.1",
@@ -279,6 +295,8 @@
279
295
  "gpt-tokenizer": "^3.4.0",
280
296
  "groq-sdk": "^0.37.0",
281
297
  "minimatch": "^10.2.3",
298
+ "oci-common": "^2.126.2",
299
+ "oci-generativeaiinference": "^2.126.2",
282
300
  "ollama": "^0.6.3",
283
301
  "openai": "^6.18.0",
284
302
  "pdf-parse": "^2.4.5",