optexity-browser-use 0.9.5__py3-none-any.whl
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.
- browser_use/__init__.py +157 -0
- browser_use/actor/__init__.py +11 -0
- browser_use/actor/element.py +1175 -0
- browser_use/actor/mouse.py +134 -0
- browser_use/actor/page.py +561 -0
- browser_use/actor/playground/flights.py +41 -0
- browser_use/actor/playground/mixed_automation.py +54 -0
- browser_use/actor/playground/playground.py +236 -0
- browser_use/actor/utils.py +176 -0
- browser_use/agent/cloud_events.py +282 -0
- browser_use/agent/gif.py +424 -0
- browser_use/agent/judge.py +170 -0
- browser_use/agent/message_manager/service.py +473 -0
- browser_use/agent/message_manager/utils.py +52 -0
- browser_use/agent/message_manager/views.py +98 -0
- browser_use/agent/prompts.py +413 -0
- browser_use/agent/service.py +2316 -0
- browser_use/agent/system_prompt.md +185 -0
- browser_use/agent/system_prompt_flash.md +10 -0
- browser_use/agent/system_prompt_no_thinking.md +183 -0
- browser_use/agent/views.py +743 -0
- browser_use/browser/__init__.py +41 -0
- browser_use/browser/cloud/cloud.py +203 -0
- browser_use/browser/cloud/views.py +89 -0
- browser_use/browser/events.py +578 -0
- browser_use/browser/profile.py +1158 -0
- browser_use/browser/python_highlights.py +548 -0
- browser_use/browser/session.py +3225 -0
- browser_use/browser/session_manager.py +399 -0
- browser_use/browser/video_recorder.py +162 -0
- browser_use/browser/views.py +200 -0
- browser_use/browser/watchdog_base.py +260 -0
- browser_use/browser/watchdogs/__init__.py +0 -0
- browser_use/browser/watchdogs/aboutblank_watchdog.py +253 -0
- browser_use/browser/watchdogs/crash_watchdog.py +335 -0
- browser_use/browser/watchdogs/default_action_watchdog.py +2729 -0
- browser_use/browser/watchdogs/dom_watchdog.py +817 -0
- browser_use/browser/watchdogs/downloads_watchdog.py +1277 -0
- browser_use/browser/watchdogs/local_browser_watchdog.py +461 -0
- browser_use/browser/watchdogs/permissions_watchdog.py +43 -0
- browser_use/browser/watchdogs/popups_watchdog.py +143 -0
- browser_use/browser/watchdogs/recording_watchdog.py +126 -0
- browser_use/browser/watchdogs/screenshot_watchdog.py +62 -0
- browser_use/browser/watchdogs/security_watchdog.py +280 -0
- browser_use/browser/watchdogs/storage_state_watchdog.py +335 -0
- browser_use/cli.py +2359 -0
- browser_use/code_use/__init__.py +16 -0
- browser_use/code_use/formatting.py +192 -0
- browser_use/code_use/namespace.py +665 -0
- browser_use/code_use/notebook_export.py +276 -0
- browser_use/code_use/service.py +1340 -0
- browser_use/code_use/system_prompt.md +574 -0
- browser_use/code_use/utils.py +150 -0
- browser_use/code_use/views.py +171 -0
- browser_use/config.py +505 -0
- browser_use/controller/__init__.py +3 -0
- browser_use/dom/enhanced_snapshot.py +161 -0
- browser_use/dom/markdown_extractor.py +169 -0
- browser_use/dom/playground/extraction.py +312 -0
- browser_use/dom/playground/multi_act.py +32 -0
- browser_use/dom/serializer/clickable_elements.py +200 -0
- browser_use/dom/serializer/code_use_serializer.py +287 -0
- browser_use/dom/serializer/eval_serializer.py +478 -0
- browser_use/dom/serializer/html_serializer.py +212 -0
- browser_use/dom/serializer/paint_order.py +197 -0
- browser_use/dom/serializer/serializer.py +1170 -0
- browser_use/dom/service.py +825 -0
- browser_use/dom/utils.py +129 -0
- browser_use/dom/views.py +906 -0
- browser_use/exceptions.py +5 -0
- browser_use/filesystem/__init__.py +0 -0
- browser_use/filesystem/file_system.py +619 -0
- browser_use/init_cmd.py +376 -0
- browser_use/integrations/gmail/__init__.py +24 -0
- browser_use/integrations/gmail/actions.py +115 -0
- browser_use/integrations/gmail/service.py +225 -0
- browser_use/llm/__init__.py +155 -0
- browser_use/llm/anthropic/chat.py +242 -0
- browser_use/llm/anthropic/serializer.py +312 -0
- browser_use/llm/aws/__init__.py +36 -0
- browser_use/llm/aws/chat_anthropic.py +242 -0
- browser_use/llm/aws/chat_bedrock.py +289 -0
- browser_use/llm/aws/serializer.py +257 -0
- browser_use/llm/azure/chat.py +91 -0
- browser_use/llm/base.py +57 -0
- browser_use/llm/browser_use/__init__.py +3 -0
- browser_use/llm/browser_use/chat.py +201 -0
- browser_use/llm/cerebras/chat.py +193 -0
- browser_use/llm/cerebras/serializer.py +109 -0
- browser_use/llm/deepseek/chat.py +212 -0
- browser_use/llm/deepseek/serializer.py +109 -0
- browser_use/llm/exceptions.py +29 -0
- browser_use/llm/google/__init__.py +3 -0
- browser_use/llm/google/chat.py +542 -0
- browser_use/llm/google/serializer.py +120 -0
- browser_use/llm/groq/chat.py +229 -0
- browser_use/llm/groq/parser.py +158 -0
- browser_use/llm/groq/serializer.py +159 -0
- browser_use/llm/messages.py +238 -0
- browser_use/llm/models.py +271 -0
- browser_use/llm/oci_raw/__init__.py +10 -0
- browser_use/llm/oci_raw/chat.py +443 -0
- browser_use/llm/oci_raw/serializer.py +229 -0
- browser_use/llm/ollama/chat.py +97 -0
- browser_use/llm/ollama/serializer.py +143 -0
- browser_use/llm/openai/chat.py +264 -0
- browser_use/llm/openai/like.py +15 -0
- browser_use/llm/openai/serializer.py +165 -0
- browser_use/llm/openrouter/chat.py +211 -0
- browser_use/llm/openrouter/serializer.py +26 -0
- browser_use/llm/schema.py +176 -0
- browser_use/llm/views.py +48 -0
- browser_use/logging_config.py +330 -0
- browser_use/mcp/__init__.py +18 -0
- browser_use/mcp/__main__.py +12 -0
- browser_use/mcp/client.py +544 -0
- browser_use/mcp/controller.py +264 -0
- browser_use/mcp/server.py +1114 -0
- browser_use/observability.py +204 -0
- browser_use/py.typed +0 -0
- browser_use/sandbox/__init__.py +41 -0
- browser_use/sandbox/sandbox.py +637 -0
- browser_use/sandbox/views.py +132 -0
- browser_use/screenshots/__init__.py +1 -0
- browser_use/screenshots/service.py +52 -0
- browser_use/sync/__init__.py +6 -0
- browser_use/sync/auth.py +357 -0
- browser_use/sync/service.py +161 -0
- browser_use/telemetry/__init__.py +51 -0
- browser_use/telemetry/service.py +112 -0
- browser_use/telemetry/views.py +101 -0
- browser_use/tokens/__init__.py +0 -0
- browser_use/tokens/custom_pricing.py +24 -0
- browser_use/tokens/mappings.py +4 -0
- browser_use/tokens/service.py +580 -0
- browser_use/tokens/views.py +108 -0
- browser_use/tools/registry/service.py +572 -0
- browser_use/tools/registry/views.py +174 -0
- browser_use/tools/service.py +1675 -0
- browser_use/tools/utils.py +82 -0
- browser_use/tools/views.py +100 -0
- browser_use/utils.py +670 -0
- optexity_browser_use-0.9.5.dist-info/METADATA +344 -0
- optexity_browser_use-0.9.5.dist-info/RECORD +147 -0
- optexity_browser_use-0.9.5.dist-info/WHEEL +4 -0
- optexity_browser_use-0.9.5.dist-info/entry_points.txt +3 -0
- optexity_browser_use-0.9.5.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
from browser_use.config import CONFIG
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def addLoggingLevel(levelName, levelNum, methodName=None):
|
|
14
|
+
"""
|
|
15
|
+
Comprehensively adds a new logging level to the `logging` module and the
|
|
16
|
+
currently configured logging class.
|
|
17
|
+
|
|
18
|
+
`levelName` becomes an attribute of the `logging` module with the value
|
|
19
|
+
`levelNum`. `methodName` becomes a convenience method for both `logging`
|
|
20
|
+
itself and the class returned by `logging.getLoggerClass()` (usually just
|
|
21
|
+
`logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
|
|
22
|
+
used.
|
|
23
|
+
|
|
24
|
+
To avoid accidental clobberings of existing attributes, this method will
|
|
25
|
+
raise an `AttributeError` if the level name is already an attribute of the
|
|
26
|
+
`logging` module or if the method name is already present
|
|
27
|
+
|
|
28
|
+
Example
|
|
29
|
+
-------
|
|
30
|
+
>>> addLoggingLevel('TRACE', logging.DEBUG - 5)
|
|
31
|
+
>>> logging.getLogger(__name__).setLevel('TRACE')
|
|
32
|
+
>>> logging.getLogger(__name__).trace('that worked')
|
|
33
|
+
>>> logging.trace('so did this')
|
|
34
|
+
>>> logging.TRACE
|
|
35
|
+
5
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
if not methodName:
|
|
39
|
+
methodName = levelName.lower()
|
|
40
|
+
|
|
41
|
+
if hasattr(logging, levelName):
|
|
42
|
+
raise AttributeError(f'{levelName} already defined in logging module')
|
|
43
|
+
if hasattr(logging, methodName):
|
|
44
|
+
raise AttributeError(f'{methodName} already defined in logging module')
|
|
45
|
+
if hasattr(logging.getLoggerClass(), methodName):
|
|
46
|
+
raise AttributeError(f'{methodName} already defined in logger class')
|
|
47
|
+
|
|
48
|
+
# This method was inspired by the answers to Stack Overflow post
|
|
49
|
+
# http://stackoverflow.com/q/2183233/2988730, especially
|
|
50
|
+
# http://stackoverflow.com/a/13638084/2988730
|
|
51
|
+
def logForLevel(self, message, *args, **kwargs):
|
|
52
|
+
if self.isEnabledFor(levelNum):
|
|
53
|
+
self._log(levelNum, message, args, **kwargs)
|
|
54
|
+
|
|
55
|
+
def logToRoot(message, *args, **kwargs):
|
|
56
|
+
logging.log(levelNum, message, *args, **kwargs)
|
|
57
|
+
|
|
58
|
+
logging.addLevelName(levelNum, levelName)
|
|
59
|
+
setattr(logging, levelName, levelNum)
|
|
60
|
+
setattr(logging.getLoggerClass(), methodName, logForLevel)
|
|
61
|
+
setattr(logging, methodName, logToRoot)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def setup_logging(stream=None, log_level=None, force_setup=False, debug_log_file=None, info_log_file=None):
|
|
65
|
+
"""Setup logging configuration for browser-use.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
stream: Output stream for logs (default: sys.stdout). Can be sys.stderr for MCP mode.
|
|
69
|
+
log_level: Override log level (default: uses CONFIG.BROWSER_USE_LOGGING_LEVEL)
|
|
70
|
+
force_setup: Force reconfiguration even if handlers already exist
|
|
71
|
+
debug_log_file: Path to log file for debug level logs only
|
|
72
|
+
info_log_file: Path to log file for info level logs only
|
|
73
|
+
"""
|
|
74
|
+
# Try to add RESULT level, but ignore if it already exists
|
|
75
|
+
try:
|
|
76
|
+
addLoggingLevel('RESULT', 35) # This allows ERROR, FATAL and CRITICAL
|
|
77
|
+
except AttributeError:
|
|
78
|
+
pass # Level already exists, which is fine
|
|
79
|
+
|
|
80
|
+
log_type = log_level or CONFIG.BROWSER_USE_LOGGING_LEVEL
|
|
81
|
+
|
|
82
|
+
# Check if handlers are already set up
|
|
83
|
+
if logging.getLogger().hasHandlers() and not force_setup:
|
|
84
|
+
return logging.getLogger('browser_use')
|
|
85
|
+
|
|
86
|
+
# Clear existing handlers
|
|
87
|
+
root = logging.getLogger()
|
|
88
|
+
root.handlers = []
|
|
89
|
+
|
|
90
|
+
class BrowserUseFormatter(logging.Formatter):
|
|
91
|
+
def __init__(self, fmt, log_level):
|
|
92
|
+
super().__init__(fmt)
|
|
93
|
+
self.log_level = log_level
|
|
94
|
+
|
|
95
|
+
def format(self, record):
|
|
96
|
+
# Only clean up names in INFO mode, keep everything in DEBUG mode
|
|
97
|
+
if self.log_level > logging.DEBUG and isinstance(record.name, str) and record.name.startswith('browser_use.'):
|
|
98
|
+
# Extract clean component names from logger names
|
|
99
|
+
if 'Agent' in record.name:
|
|
100
|
+
record.name = 'Agent'
|
|
101
|
+
elif 'BrowserSession' in record.name:
|
|
102
|
+
record.name = 'BrowserSession'
|
|
103
|
+
elif 'tools' in record.name:
|
|
104
|
+
record.name = 'tools'
|
|
105
|
+
elif 'dom' in record.name:
|
|
106
|
+
record.name = 'dom'
|
|
107
|
+
elif record.name.startswith('browser_use.'):
|
|
108
|
+
# For other browser_use modules, use the last part
|
|
109
|
+
parts = record.name.split('.')
|
|
110
|
+
if len(parts) >= 2:
|
|
111
|
+
record.name = parts[-1]
|
|
112
|
+
return super().format(record)
|
|
113
|
+
|
|
114
|
+
# Setup single handler for all loggers
|
|
115
|
+
console = logging.StreamHandler(stream or sys.stdout)
|
|
116
|
+
|
|
117
|
+
# Determine the log level to use first
|
|
118
|
+
if log_type == 'result':
|
|
119
|
+
log_level = 35 # RESULT level value
|
|
120
|
+
elif log_type == 'debug':
|
|
121
|
+
log_level = logging.DEBUG
|
|
122
|
+
else:
|
|
123
|
+
log_level = logging.INFO
|
|
124
|
+
|
|
125
|
+
# adittional setLevel here to filter logs
|
|
126
|
+
if log_type == 'result':
|
|
127
|
+
console.setLevel('RESULT')
|
|
128
|
+
console.setFormatter(BrowserUseFormatter('%(message)s', log_level))
|
|
129
|
+
else:
|
|
130
|
+
console.setLevel(log_level) # Keep console at original log level (e.g., INFO)
|
|
131
|
+
console.setFormatter(BrowserUseFormatter('%(levelname)-8s [%(name)s] %(message)s', log_level))
|
|
132
|
+
|
|
133
|
+
# Configure root logger only
|
|
134
|
+
root.addHandler(console)
|
|
135
|
+
|
|
136
|
+
# Add file handlers if specified
|
|
137
|
+
file_handlers = []
|
|
138
|
+
|
|
139
|
+
# Create debug log file handler
|
|
140
|
+
if debug_log_file:
|
|
141
|
+
debug_handler = logging.FileHandler(debug_log_file, encoding='utf-8')
|
|
142
|
+
debug_handler.setLevel(logging.DEBUG)
|
|
143
|
+
debug_handler.setFormatter(BrowserUseFormatter('%(asctime)s - %(levelname)-8s [%(name)s] %(message)s', logging.DEBUG))
|
|
144
|
+
file_handlers.append(debug_handler)
|
|
145
|
+
root.addHandler(debug_handler)
|
|
146
|
+
|
|
147
|
+
# Create info log file handler
|
|
148
|
+
if info_log_file:
|
|
149
|
+
info_handler = logging.FileHandler(info_log_file, encoding='utf-8')
|
|
150
|
+
info_handler.setLevel(logging.INFO)
|
|
151
|
+
info_handler.setFormatter(BrowserUseFormatter('%(asctime)s - %(levelname)-8s [%(name)s] %(message)s', logging.INFO))
|
|
152
|
+
file_handlers.append(info_handler)
|
|
153
|
+
root.addHandler(info_handler)
|
|
154
|
+
|
|
155
|
+
# Configure root logger - use DEBUG if debug file logging is enabled
|
|
156
|
+
effective_log_level = logging.DEBUG if debug_log_file else log_level
|
|
157
|
+
root.setLevel(effective_log_level)
|
|
158
|
+
|
|
159
|
+
# Configure browser_use logger
|
|
160
|
+
browser_use_logger = logging.getLogger('browser_use')
|
|
161
|
+
browser_use_logger.propagate = False # Don't propagate to root logger
|
|
162
|
+
browser_use_logger.addHandler(console)
|
|
163
|
+
for handler in file_handlers:
|
|
164
|
+
browser_use_logger.addHandler(handler)
|
|
165
|
+
browser_use_logger.setLevel(effective_log_level)
|
|
166
|
+
|
|
167
|
+
# Configure bubus logger to allow INFO level logs
|
|
168
|
+
bubus_logger = logging.getLogger('bubus')
|
|
169
|
+
bubus_logger.propagate = False # Don't propagate to root logger
|
|
170
|
+
bubus_logger.addHandler(console)
|
|
171
|
+
for handler in file_handlers:
|
|
172
|
+
bubus_logger.addHandler(handler)
|
|
173
|
+
bubus_logger.setLevel(logging.INFO if log_type == 'result' else effective_log_level)
|
|
174
|
+
|
|
175
|
+
# Configure CDP logging using cdp_use's setup function
|
|
176
|
+
# This enables the formatted CDP output using CDP_LOGGING_LEVEL environment variable
|
|
177
|
+
# Convert CDP_LOGGING_LEVEL string to logging level
|
|
178
|
+
cdp_level_str = CONFIG.CDP_LOGGING_LEVEL.upper()
|
|
179
|
+
cdp_level = getattr(logging, cdp_level_str, logging.WARNING)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
from cdp_use.logging import setup_cdp_logging # type: ignore
|
|
183
|
+
|
|
184
|
+
# Use the CDP-specific logging level
|
|
185
|
+
setup_cdp_logging(
|
|
186
|
+
level=cdp_level,
|
|
187
|
+
stream=stream or sys.stdout,
|
|
188
|
+
format_string='%(levelname)-8s [%(name)s] %(message)s' if log_type != 'result' else '%(message)s',
|
|
189
|
+
)
|
|
190
|
+
except ImportError:
|
|
191
|
+
# If cdp_use doesn't have the new logging module, fall back to manual config
|
|
192
|
+
cdp_loggers = [
|
|
193
|
+
'websockets.client',
|
|
194
|
+
'cdp_use',
|
|
195
|
+
'cdp_use.client',
|
|
196
|
+
'cdp_use.cdp',
|
|
197
|
+
'cdp_use.cdp.registry',
|
|
198
|
+
]
|
|
199
|
+
for logger_name in cdp_loggers:
|
|
200
|
+
cdp_logger = logging.getLogger(logger_name)
|
|
201
|
+
cdp_logger.setLevel(cdp_level)
|
|
202
|
+
cdp_logger.addHandler(console)
|
|
203
|
+
cdp_logger.propagate = False
|
|
204
|
+
|
|
205
|
+
logger = logging.getLogger('browser_use')
|
|
206
|
+
# logger.debug('BrowserUse logging setup complete with level %s', log_type)
|
|
207
|
+
|
|
208
|
+
# Silence third-party loggers (but not CDP ones which we configured above)
|
|
209
|
+
third_party_loggers = [
|
|
210
|
+
'WDM',
|
|
211
|
+
'httpx',
|
|
212
|
+
'selenium',
|
|
213
|
+
'playwright',
|
|
214
|
+
'urllib3',
|
|
215
|
+
'asyncio',
|
|
216
|
+
'langsmith',
|
|
217
|
+
'langsmith.client',
|
|
218
|
+
'openai',
|
|
219
|
+
'httpcore',
|
|
220
|
+
'charset_normalizer',
|
|
221
|
+
'anthropic._base_client',
|
|
222
|
+
'PIL.PngImagePlugin',
|
|
223
|
+
'trafilatura.htmlprocessing',
|
|
224
|
+
'trafilatura',
|
|
225
|
+
'groq',
|
|
226
|
+
'portalocker',
|
|
227
|
+
'google_genai',
|
|
228
|
+
'portalocker.utils',
|
|
229
|
+
'websockets', # General websockets (but not websockets.client which we need)
|
|
230
|
+
]
|
|
231
|
+
for logger_name in third_party_loggers:
|
|
232
|
+
third_party = logging.getLogger(logger_name)
|
|
233
|
+
third_party.setLevel(logging.ERROR)
|
|
234
|
+
third_party.propagate = False
|
|
235
|
+
|
|
236
|
+
return logger
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class FIFOHandler(logging.Handler):
|
|
240
|
+
"""Non-blocking handler that writes to a named pipe."""
|
|
241
|
+
|
|
242
|
+
def __init__(self, fifo_path: str):
|
|
243
|
+
super().__init__()
|
|
244
|
+
self.fifo_path = fifo_path
|
|
245
|
+
Path(fifo_path).parent.mkdir(parents=True, exist_ok=True)
|
|
246
|
+
|
|
247
|
+
# Create FIFO if it doesn't exist
|
|
248
|
+
if not os.path.exists(fifo_path):
|
|
249
|
+
os.mkfifo(fifo_path)
|
|
250
|
+
|
|
251
|
+
# Don't open the FIFO yet - will open on first write
|
|
252
|
+
self.fd = None
|
|
253
|
+
|
|
254
|
+
def emit(self, record):
|
|
255
|
+
try:
|
|
256
|
+
# Open FIFO on first write if not already open
|
|
257
|
+
if self.fd is None:
|
|
258
|
+
try:
|
|
259
|
+
self.fd = os.open(self.fifo_path, os.O_WRONLY | os.O_NONBLOCK)
|
|
260
|
+
except OSError:
|
|
261
|
+
# No reader connected yet, skip this message
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
msg = f'{self.format(record)}\n'.encode()
|
|
265
|
+
os.write(self.fd, msg)
|
|
266
|
+
except (OSError, BrokenPipeError):
|
|
267
|
+
# Reader disconnected, close and reset
|
|
268
|
+
if self.fd is not None:
|
|
269
|
+
try:
|
|
270
|
+
os.close(self.fd)
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
self.fd = None
|
|
274
|
+
|
|
275
|
+
def close(self):
|
|
276
|
+
if hasattr(self, 'fd') and self.fd is not None:
|
|
277
|
+
try:
|
|
278
|
+
os.close(self.fd)
|
|
279
|
+
except Exception:
|
|
280
|
+
pass
|
|
281
|
+
super().close()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def setup_log_pipes(session_id: str, base_dir: str | None = None):
|
|
285
|
+
"""Setup named pipes for log streaming.
|
|
286
|
+
|
|
287
|
+
Usage:
|
|
288
|
+
# In browser-use:
|
|
289
|
+
setup_log_pipes(session_id="abc123")
|
|
290
|
+
|
|
291
|
+
# In consumer process:
|
|
292
|
+
tail -f {temp_dir}/buagent.c123/agent.pipe
|
|
293
|
+
"""
|
|
294
|
+
import tempfile
|
|
295
|
+
|
|
296
|
+
if base_dir is None:
|
|
297
|
+
base_dir = tempfile.gettempdir()
|
|
298
|
+
|
|
299
|
+
suffix = session_id[-4:]
|
|
300
|
+
pipe_dir = Path(base_dir) / f'buagent.{suffix}'
|
|
301
|
+
|
|
302
|
+
# Agent logs
|
|
303
|
+
agent_handler = FIFOHandler(str(pipe_dir / 'agent.pipe'))
|
|
304
|
+
agent_handler.setLevel(logging.DEBUG)
|
|
305
|
+
agent_handler.setFormatter(logging.Formatter('%(levelname)-8s [%(name)s] %(message)s'))
|
|
306
|
+
for name in ['browser_use.agent', 'browser_use.tools']:
|
|
307
|
+
logger = logging.getLogger(name)
|
|
308
|
+
logger.addHandler(agent_handler)
|
|
309
|
+
logger.setLevel(logging.DEBUG)
|
|
310
|
+
logger.propagate = True
|
|
311
|
+
|
|
312
|
+
# CDP logs
|
|
313
|
+
cdp_handler = FIFOHandler(str(pipe_dir / 'cdp.pipe'))
|
|
314
|
+
cdp_handler.setLevel(logging.DEBUG)
|
|
315
|
+
cdp_handler.setFormatter(logging.Formatter('%(levelname)-8s [%(name)s] %(message)s'))
|
|
316
|
+
for name in ['websockets.client', 'cdp_use.client']:
|
|
317
|
+
logger = logging.getLogger(name)
|
|
318
|
+
logger.addHandler(cdp_handler)
|
|
319
|
+
logger.setLevel(logging.DEBUG)
|
|
320
|
+
logger.propagate = True
|
|
321
|
+
|
|
322
|
+
# Event logs
|
|
323
|
+
event_handler = FIFOHandler(str(pipe_dir / 'events.pipe'))
|
|
324
|
+
event_handler.setLevel(logging.INFO)
|
|
325
|
+
event_handler.setFormatter(logging.Formatter('%(levelname)-8s [%(name)s] %(message)s'))
|
|
326
|
+
for name in ['bubus', 'browser_use.browser.session']:
|
|
327
|
+
logger = logging.getLogger(name)
|
|
328
|
+
logger.addHandler(event_handler)
|
|
329
|
+
logger.setLevel(logging.INFO) # Enable INFO for event bus
|
|
330
|
+
logger.propagate = True
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) support for browser-use.
|
|
2
|
+
|
|
3
|
+
This module provides integration with MCP servers and clients for browser automation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from browser_use.mcp.client import MCPClient
|
|
7
|
+
from browser_use.mcp.controller import MCPToolWrapper
|
|
8
|
+
|
|
9
|
+
__all__ = ['MCPClient', 'MCPToolWrapper', 'BrowserUseServer'] # type: ignore
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def __getattr__(name):
|
|
13
|
+
"""Lazy import to avoid importing server module when only client is needed."""
|
|
14
|
+
if name == 'BrowserUseServer':
|
|
15
|
+
from browser_use.mcp.server import BrowserUseServer
|
|
16
|
+
|
|
17
|
+
return BrowserUseServer
|
|
18
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|