browser-use 0.0.2 β†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,6 +47,15 @@ We are committed to:
47
47
 
48
48
  This is **not** a fork or competing projectβ€”it's a respectful port to serve a different programming language community.
49
49
 
50
+ ### Upstream Parity Status
51
+
52
+ This Node.js/TypeScript implementation is currently **strictly aligned** with the Python `browser-use` release
53
+ [`v0.5.11`](https://github.com/browser-use/browser-use/releases/tag/0.5.11), published on **August 10, 2025**.
54
+
55
+ - πŸ“¦ Core features and behavior are aligned against that upstream tag baseline.
56
+ - βœ… Our test strategy is maintained to be as equivalent as practical to the Python coverage and behavior checks.
57
+ - πŸ”„ We expect to move this parity baseline forward to the Python **January 2026** release line very soon.
58
+
50
59
  ## Features
51
60
 
52
61
  - πŸ€– **AI-Powered**: Built specifically for LLM-driven web automation with structured output support
@@ -74,6 +83,9 @@ pnpm add browser-use
74
83
 
75
84
  Playwright browsers will be installed automatically via postinstall hook.
76
85
 
86
+ Use only documented public entrypoints such as `browser-use` and
87
+ `browser-use/llm/openai`. Avoid deep imports like `browser-use/dist/...`.
88
+
77
89
  ### Basic Usage with Agent
78
90
 
79
91
  ```typescript
@@ -145,6 +157,47 @@ const history = await agent.run(10);
145
157
  console.log(history.final_result());
146
158
  ```
147
159
 
160
+ ### CLI Usage
161
+
162
+ ```bash
163
+ # Interactive mode (when running in a TTY)
164
+ npx browser-use
165
+
166
+ # One-shot task
167
+ npx browser-use -p "Go to example.com and extract the page title"
168
+
169
+ # Positional task mode
170
+ npx browser-use "Search for TypeScript browser automation"
171
+
172
+ # Pick model/provider by model name
173
+ npx browser-use --model claude-sonnet-4-20250514 -p "Summarize latest AI news"
174
+
175
+ # Pick provider explicitly (uses provider default model)
176
+ npx browser-use --provider anthropic -p "Summarize latest AI news"
177
+
178
+ # Headless + custom browser profile settings
179
+ npx browser-use --headless --window-width 1440 --window-height 900 -p "Check dashboard status"
180
+
181
+ # Restrict navigation to trusted domains (recommended with secrets)
182
+ npx browser-use --allowed-domains "example.com,*.example.org" -p "Log in and fetch account info"
183
+
184
+ # Connect to existing Chromium via CDP
185
+ npx browser-use --cdp-url http://localhost:9222 -p "Inspect the active tab"
186
+
187
+ # MCP server mode
188
+ npx browser-use --mcp
189
+ ```
190
+
191
+ Interactive mode commands:
192
+
193
+ - `help`: show interactive usage
194
+ - `exit`: quit interactive mode
195
+
196
+ Security notes:
197
+
198
+ - Prefer `--allowed-domains` whenever tasks involve credentials or sensitive data.
199
+ - `--allow-insecure` disables domain-lockdown enforcement for sensitive data and is unsafe for production.
200
+
148
201
  ## Advanced Usage
149
202
 
150
203
  ### Vision/Multimodal Support
@@ -565,6 +618,9 @@ yarn test test/integration-advanced.test.ts
565
618
 
566
619
  # Watch mode
567
620
  yarn test:watch
621
+
622
+ # Validate published package exports
623
+ yarn test:pack
568
624
  ```
569
625
 
570
626
  ### Code Quality
@@ -577,7 +633,7 @@ yarn lint
577
633
  yarn prettier
578
634
 
579
635
  # Type check
580
- yarn build
636
+ yarn typecheck
581
637
  ```
582
638
 
583
639
  ## Architecture
@@ -585,28 +641,28 @@ yarn build
585
641
  The library follows a modular, layered architecture:
586
642
 
587
643
  ```
588
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
644
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
589
645
  β”‚ Agent (Orchestrator) β”‚
590
646
  β”‚ - Task execution & planning β”‚
591
647
  β”‚ - LLM message management β”‚
592
648
  β”‚ - Step execution loop β”‚
593
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
649
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
594
650
  β”‚
595
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
651
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
596
652
  β”‚ Controller (Actions) β”‚
597
653
  β”‚ - Action registry & execution β”‚
598
654
  β”‚ - Built-in actions (30+) β”‚
599
655
  β”‚ - Custom action support β”‚
600
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
656
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
601
657
  β”‚
602
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
658
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
603
659
  β”‚ BrowserSession (Browser) β”‚
604
660
  β”‚ - Playwright integration β”‚
605
661
  β”‚ - Tab & page management β”‚
606
662
  β”‚ - Navigation & interaction β”‚
607
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
663
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
608
664
  β”‚
609
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
665
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
610
666
  β”‚ DOMService (DOM Analysis) β”‚
611
667
  β”‚ - Element extraction β”‚
612
668
  β”‚ - Clickable element detection β”‚
@@ -69,6 +69,7 @@ interface AgentConstructorParams<Context, AgentStructuredOutput> {
69
69
  include_tool_call_examples?: boolean;
70
70
  vision_detail_level?: AgentSettings['vision_detail_level'];
71
71
  session_attachment_mode?: AgentSettings['session_attachment_mode'];
72
+ allow_insecure_sensitive_data?: boolean;
72
73
  llm_timeout?: number;
73
74
  step_timeout?: number;
74
75
  }
@@ -7,14 +7,15 @@ import { z } from 'zod';
7
7
  import { createLogger } from '../logging-config.js';
8
8
  import { CONFIG } from '../config.js';
9
9
  import { EventBus } from '../event-bus.js';
10
- import { uuid7str, SignalHandler, get_browser_use_version, } from '../utils.js';
10
+ import { uuid7str, SignalHandler, get_browser_use_version } from '../utils.js';
11
11
  import { Controller as DefaultController } from '../controller/service.js';
12
12
  import { FileSystem as AgentFileSystem, DEFAULT_FILE_SYSTEM_PATH, } from '../filesystem/file-system.js';
13
13
  import { SystemPrompt } from './prompts.js';
14
14
  import { MessageManager } from './message-manager/service.js';
15
15
  import { BrowserStateHistory } from '../browser/views.js';
16
16
  import { BrowserSession } from '../browser/session.js';
17
- import { BrowserProfile, DEFAULT_BROWSER_PROFILE, } from '../browser/profile.js';
17
+ import { BrowserProfile, DEFAULT_BROWSER_PROFILE } from '../browser/profile.js';
18
+ import { InsecureSensitiveDataError } from '../exceptions.js';
18
19
  import { HistoryTreeProcessor } from '../dom/history-tree-processor/service.js';
19
20
  import { DOMHistoryElement } from '../dom/history-tree-processor/view.js';
20
21
  import { UserMessage } from '../llm/messages.js';
@@ -128,6 +129,7 @@ const defaultAgentOptions = () => ({
128
129
  display_files_in_done_text: true,
129
130
  include_tool_call_examples: false,
130
131
  session_attachment_mode: 'copy',
132
+ allow_insecure_sensitive_data: false,
131
133
  vision_detail_level: 'auto',
132
134
  llm_timeout: 60,
133
135
  step_timeout: 180,
@@ -137,7 +139,11 @@ const AgentLLMOutputSchema = z.object({
137
139
  evaluation_previous_goal: z.string().optional().nullable(),
138
140
  memory: z.string().optional().nullable(),
139
141
  next_goal: z.string().optional().nullable(),
140
- action: z.array(z.record(z.string(), z.any())).optional().nullable().default([]),
142
+ action: z
143
+ .array(z.record(z.string(), z.any()))
144
+ .optional()
145
+ .nullable()
146
+ .default([]),
141
147
  });
142
148
  const AgentLLMOutputFormat = AgentLLMOutputSchema;
143
149
  AgentLLMOutputFormat.schema = AgentLLMOutputSchema;
@@ -198,7 +204,7 @@ export class Agent {
198
204
  DoneActionModel = ActionModel;
199
205
  DoneAgentOutput = AgentOutput;
200
206
  constructor(params) {
201
- const { task, llm, page = null, browser = null, browser_context = null, browser_profile = null, browser_session = null, controller = null, sensitive_data = null, initial_actions = null, register_new_step_callback = null, register_done_callback = null, register_external_agent_status_raise_error_callback = null, output_model_schema = null, use_vision = true, save_conversation_path = null, save_conversation_path_encoding = 'utf-8', max_failures = 3, retry_delay = 10, override_system_message = null, extend_system_message = null, validate_output = false, generate_gif = false, available_file_paths = [], include_attributes, max_actions_per_step = 10, use_thinking = true, flash_mode = false, max_history_items = null, page_extraction_llm = null, context = null, source = null, file_system_path = null, task_id = null, cloud_sync = null, calculate_cost = false, display_files_in_done_text = true, include_tool_call_examples = false, vision_detail_level = 'auto', session_attachment_mode = 'copy', llm_timeout = 60, step_timeout = 180, } = { ...defaultAgentOptions(), ...params };
207
+ const { task, llm, page = null, browser = null, browser_context = null, browser_profile = null, browser_session = null, controller = null, sensitive_data = null, initial_actions = null, register_new_step_callback = null, register_done_callback = null, register_external_agent_status_raise_error_callback = null, output_model_schema = null, use_vision = true, save_conversation_path = null, save_conversation_path_encoding = 'utf-8', max_failures = 3, retry_delay = 10, override_system_message = null, extend_system_message = null, validate_output = false, generate_gif = false, available_file_paths = [], include_attributes, max_actions_per_step = 10, use_thinking = true, flash_mode = false, max_history_items = null, page_extraction_llm = null, context = null, source = null, file_system_path = null, task_id = null, cloud_sync = null, calculate_cost = false, display_files_in_done_text = true, include_tool_call_examples = false, vision_detail_level = 'auto', session_attachment_mode = 'copy', allow_insecure_sensitive_data = false, llm_timeout = 60, step_timeout = 180, } = { ...defaultAgentOptions(), ...params };
202
208
  if (!llm) {
203
209
  throw new Error('Invalid llm, must be provided');
204
210
  }
@@ -249,6 +255,7 @@ export class Agent {
249
255
  calculate_cost,
250
256
  include_tool_call_examples,
251
257
  session_attachment_mode,
258
+ allow_insecure_sensitive_data,
252
259
  llm_timeout,
253
260
  step_timeout,
254
261
  };
@@ -489,7 +496,7 @@ export class Agent {
489
496
  Agent._sharedSessionStepLocks.delete(browser_session.id);
490
497
  }
491
498
  _init_browser_session(init) {
492
- let { page, browser, browser_context, browser_profile, browser_session, } = init;
499
+ let { page, browser, browser_context, browser_profile, browser_session } = init;
493
500
  if (browser instanceof BrowserSession) {
494
501
  browser_session = browser_session ?? browser;
495
502
  browser = null;
@@ -617,6 +624,9 @@ export class Agent {
617
624
  : Boolean(allowedDomainsConfig);
618
625
  // If no allowed_domains are configured, show a security warning
619
626
  if (!hasAllowedDomains) {
627
+ if (!this.settings.allow_insecure_sensitive_data) {
628
+ throw new InsecureSensitiveDataError();
629
+ }
620
630
  this.logger.error('⚠️⚠️⚠️ Agent(sensitive_data=β€’β€’β€’β€’β€’β€’β€’β€’) was provided but BrowserSession(allowed_domains=[...]) is not locked down! ⚠️⚠️⚠️\n' +
621
631
  ' ☠️ If the agent visits a malicious website and encounters a prompt-injection attack, your sensitive_data may be exposed!\n\n' +
622
632
  ' https://docs.browser-use.com/customize/browser-settings#restrict-urls\n' +
@@ -627,7 +637,7 @@ export class Agent {
627
637
  // User can still abort process with Ctrl+C.
628
638
  this._sleep_blocking(10_000);
629
639
  }
630
- this.logger.warning('‼️ Continuing with insecure settings for now... but this will become a hard error in the future!');
640
+ this.logger.warning('‼️ Continuing with insecure settings because allow_insecure_sensitive_data=true is enabled.');
631
641
  }
632
642
  // If we're using domain-specific credentials, validate domain patterns
633
643
  else if (hasDomainSpecificCredentials) {
@@ -1701,7 +1711,7 @@ export class Agent {
1701
1711
  throw new Error('Agent paused');
1702
1712
  }
1703
1713
  }
1704
- async _handle_post_llm_processing(browser_state_summary, input_messages, actions = []) {
1714
+ async _handle_post_llm_processing(browser_state_summary, input_messages, _actions = []) {
1705
1715
  if (this.register_new_step_callback && this.state.last_model_output) {
1706
1716
  await this.register_new_step_callback(browser_state_summary, this.state.last_model_output, this.state.n_steps);
1707
1717
  }
@@ -1939,7 +1949,9 @@ export class Agent {
1939
1949
  typeof entry.model_dump === 'function'
1940
1950
  ? entry.model_dump()
1941
1951
  : entry;
1942
- if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
1952
+ if (!candidate ||
1953
+ typeof candidate !== 'object' ||
1954
+ Array.isArray(candidate)) {
1943
1955
  return false;
1944
1956
  }
1945
1957
  return Object.keys(candidate).length === 0;
@@ -2018,7 +2030,9 @@ export class Agent {
2018
2030
  typeof entry.model_dump === 'function'
2019
2031
  ? entry.model_dump()
2020
2032
  : entry;
2021
- if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
2033
+ if (!candidate ||
2034
+ typeof candidate !== 'object' ||
2035
+ Array.isArray(candidate)) {
2022
2036
  throw new Error(`Invalid action at index ${i}: expected an object with exactly one action key`);
2023
2037
  }
2024
2038
  const actionObject = candidate;
@@ -55,6 +55,7 @@ export declare class ActionResult {
55
55
  }
56
56
  export interface AgentSettings {
57
57
  session_attachment_mode: 'copy' | 'strict' | 'shared';
58
+ allow_insecure_sensitive_data: boolean;
58
59
  use_vision: boolean;
59
60
  vision_detail_level: 'auto' | 'low' | 'high';
60
61
  use_vision_for_planner: boolean;
@@ -65,6 +65,7 @@ export class ActionResult {
65
65
  }
66
66
  export const defaultAgentSettings = () => ({
67
67
  session_attachment_mode: 'copy',
68
+ allow_insecure_sensitive_data: false,
68
69
  use_vision: true,
69
70
  vision_detail_level: 'auto',
70
71
  use_vision_for_planner: false,
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs';
2
+ import os from 'node:os';
2
3
  import path from 'node:path';
3
4
  import { exec } from 'node:child_process';
4
5
  import { promisify } from 'node:util';
@@ -52,7 +53,7 @@ export class BrowserSession {
52
53
  ? typeof structuredClone === 'function'
53
54
  ? structuredClone(init.browser_profile.config)
54
55
  : JSON.parse(JSON.stringify(init.browser_profile.config))
55
- : init.profile ?? {};
56
+ : (init.profile ?? {});
56
57
  this.browser_profile = new BrowserProfile(sourceProfileConfig);
57
58
  this.id = init.id ?? uuid7str();
58
59
  this.browser = init.browser ?? null;
@@ -493,8 +494,7 @@ export class BrowserSession {
493
494
  }
494
495
  catch (error) {
495
496
  const sandboxEnabled = this.browser_profile.config.chromium_sandbox;
496
- if (!sandboxEnabled ||
497
- !this._isSandboxLaunchError(error)) {
497
+ if (!sandboxEnabled || !this._isSandboxLaunchError(error)) {
498
498
  throw error;
499
499
  }
500
500
  this.logger.warning('Chromium sandbox is unavailable in this environment. Retrying launch with chromium_sandbox=false (--no-sandbox).');
@@ -3094,7 +3094,7 @@ export class BrowserSession {
3094
3094
  */
3095
3095
  _escapeSelector(selector) {
3096
3096
  // Escape special CSS characters
3097
- return selector.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
3097
+ return selector.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, '\\$&');
3098
3098
  }
3099
3099
  // endregion
3100
3100
  // region - User Data Directory Management
@@ -3181,7 +3181,7 @@ export class BrowserSession {
3181
3181
  * Create a temporary user data directory
3182
3182
  */
3183
3183
  async _createTempUserDataDir() {
3184
- const osTempDir = require('os').tmpdir();
3184
+ const osTempDir = os.tmpdir();
3185
3185
  const tempDir = path.join(osTempDir, `browser-use-${Date.now()}-${Math.random().toString(36).slice(2)}`);
3186
3186
  fs.mkdirSync(tempDir, { recursive: true });
3187
3187
  return tempDir;
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,38 @@
1
1
  #!/usr/bin/env node
2
+ import type { BaseChatModel } from './llm/base.js';
3
+ type CliModelProvider = 'openai' | 'anthropic' | 'google' | 'deepseek' | 'groq' | 'openrouter' | 'azure' | 'aws-anthropic' | 'aws' | 'ollama';
4
+ export interface ParsedCliArgs {
5
+ help: boolean;
6
+ version: boolean;
7
+ debug: boolean;
8
+ allow_insecure: boolean;
9
+ headless: boolean | null;
10
+ window_width: number | null;
11
+ window_height: number | null;
12
+ user_data_dir: string | null;
13
+ profile_directory: string | null;
14
+ allowed_domains: string[] | null;
15
+ cdp_url: string | null;
16
+ model: string | null;
17
+ provider: CliModelProvider | null;
18
+ prompt: string | null;
19
+ mcp: boolean;
20
+ positional: string[];
21
+ }
22
+ export declare const CLI_HISTORY_LIMIT = 100;
23
+ export declare const parseCliArgs: (argv: string[]) => ParsedCliArgs;
24
+ export declare const isInteractiveExitCommand: (value: string) => boolean;
25
+ export declare const isInteractiveHelpCommand: (value: string) => boolean;
26
+ export declare const normalizeCliHistory: (history: unknown[], maxLength?: number) => string[];
27
+ export declare const getCliHistoryPath: (configDir?: string | null) => string;
28
+ export declare const loadCliHistory: (historyPath?: string) => Promise<string[]>;
29
+ export declare const saveCliHistory: (history: string[], historyPath?: string) => Promise<void>;
30
+ export declare const shouldStartInteractiveMode: (task: string | null, options?: {
31
+ forceInteractive?: boolean;
32
+ inputIsTTY?: boolean;
33
+ outputIsTTY?: boolean;
34
+ }) => boolean;
35
+ export declare const getLlmFromCliArgs: (args: ParsedCliArgs) => BaseChatModel;
36
+ export declare const getCliUsage: () => string;
37
+ export declare function main(argv?: string[]): Promise<void>;
2
38
  export {};