Compare commits

...
14 Commits
Author SHA1 Message Date
nafer 0b74efb3e6 добавил browser-data в переменную, чтобы инстансы имели разные аккаунты 2026-04-17 21:32:29 +03:00
nafer 7256e215bb container_name: browser-use удалил, чтобы не было конфликта 2026-04-17 21:26:55 +03:00
nafer 4e75beaeca изменил порты на переменные, чтобы можно было инстансы дублировать 2026-04-17 21:14:03 +03:00
nafer 87b8bbb075 cap_psyho 2026-03-10 20:45:53 +03:00
nafer 6959670729 capsolver_sit_watch 2026-03-10 20:04:22 +03:00
nafer d436d6aaca capsolver_py_wrap 2026-03-10 19:45:43 +03:00
nafer e8143e2627 14 2026-03-10 19:26:38 +03:00
nafer a3c3d65c02 13 2026-03-10 18:49:03 +03:00
nafer 84ccb7bac4 12 2026-03-10 18:35:24 +03:00
nafer b3d31cb2d2 1 2026-03-10 18:33:27 +03:00
nafer 80760b9dd8 cp_naf 2026-03-10 18:11:02 +03:00
nafer 729ada2a6f cap7 2026-03-10 17:02:16 +03:00
nafer 7625b3a9ad cap6 2026-03-10 15:45:22 +03:00
nafer c706116de3 cap5 2026-03-10 15:34:35 +03:00
4 changed files with 165 additions and 59 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
# ===========================================
# Browser Use MCP Server Configuration
# ===========================================
#
# OpenRouter API Key (обязательно)
# Получить на https://openrouter.ai/keys
OPENROUTER_API_KEY=your_openrouter_api_key_here
+3 -1
View File
@@ -1,10 +1,12 @@
FROM mcr.microsoft.com/playwright/python:v1.49.0-noble
# Установка системных зависимостей
# socat — проброс портов для CDP
# socat — проброс портов для CDP_
# Видеокодеки и мультимедийные библиотеки для воспроизведения видео в Chrome
RUN apt-get update && apt-get install -y \
socat \
wget \
unzip \
libgstreamer1.0-0 \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
+155 -52
View File
@@ -40,7 +40,7 @@ CAPSOLVER_API_KEY = os.getenv("CAPSOLVER_API_KEY", "")
AUTO_SOLVE_CAPTCHA = os.getenv("AUTO_SOLVE_CAPTCHA", "false").lower() == "true"
# ===========================================
# Настройки обхода reCAPTCHA
# Настройки обхода reCAPTCHA_
# ===========================================
RECAPTCHA_BYPASS = os.getenv("RECAPTCHA_BYPASS", "false").lower() == "true"
@@ -59,8 +59,28 @@ logger.info(f"USE_XVFB: {USE_XVFB}")
logger.info(f"REMOTE_DEBUGGING_ENABLED: {REMOTE_DEBUGGING_ENABLED}")
logger.info(f"RECAPTCHA_BYPASS: {RECAPTCHA_BYPASS}")
logger.info(f"AUTO_SOLVE_CAPTCHA: {AUTO_SOLVE_CAPTCHA}")
logger.info(f"CAPSOLVER_API_KEY: {'set' if CAPSOLVER_API_KEY else 'not set'}")
logger.info(f"PROXY_URL: {PROXY_URL if PROXY_URL else 'not set'}")
logger.info(f"===========================")
# Настраиваем CapSolver расширение с API ключом
if CAPSOLVER_API_KEY:
capsolver_config_path = "/app/capsolver-extension/assets/config.js"
if os.path.exists(capsolver_config_path):
try:
with open(capsolver_config_path, 'r') as f:
config_content = f.read()
# Заменяем apiKey на реальный
import re
config_content = re.sub(r'apiKey:\s*"[^"]*"', f'apiKey: "{CAPSOLVER_API_KEY}"', config_content)
with open(capsolver_config_path, 'w') as f:
f.write(config_content)
logger.info("CapSolver API key configured in extension")
except Exception as e:
logger.warning(f"Failed to configure CapSolver API key: {e}")
else:
logger.warning(f"CapSolver config not found at {capsolver_config_path}")
# Создаём Tools с кастомными действиями
try:
from browser_use import Tools, BrowserSession
@@ -198,7 +218,7 @@ try:
sitekey = await turnstile_element.get_attribute('data-sitekey')
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
if turnstile_frame and sitekey:
captcha_type = "TurnstileTask"
captcha_type = "AntiTurnstileTaskProxyLess"
if not sitekey or not captcha_type:
return ActionResult(
@@ -299,7 +319,7 @@ try:
include_in_memory=True
)
elif captcha_type == "TurnstileTask":
elif captcha_type == "AntiTurnstileTaskProxyLess":
token = solution.get("token")
if token:
await page.evaluate(f'''(token) => {{
@@ -418,7 +438,7 @@ async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
if sitekey:
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
if turnstile_frame:
captcha_type = "TurnstileTask"
captcha_type = "AntiTurnstileTaskProxyLess"
if not sitekey or not captcha_type:
logger.info("No captcha found on page")
@@ -487,7 +507,7 @@ async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
await textarea.fill(h_captcha_response)
logger.info("hCaptcha solved successfully!")
return True
elif captcha_type == "TurnstileTask":
elif captcha_type == "AntiTurnstileTaskProxyLess":
token = solution.get("token")
if token:
# Вставляем токен
@@ -791,6 +811,7 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
f"--server-args=-screen 0 {width}x{height}x24",
])
# Всегда добавляем --no-sandbox
chrome_cmd.extend([
chrome_exe,
f"--remote-debugging-port={CHROME_INTERNAL_PORT}",
@@ -823,18 +844,23 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
"--enable-unsafe-swiftshader",
"--ignore-gpu-blocklist",
"--enable-webgl",
"--use-gl=angle",
"--use-angle=swiftshader",
"--test-type",
# WebGPU флаги для Cloudflare Turnstile
"--enable-webgpu",
"--enable-webgpu-developer-features",
"--use-angle=swiftshader",
# Отключено: stealth-расширение вызывает ошибки и ломает Cloudflare Turnstile
# "--load-extension=/app/stealth-extension",
# "--disable-extensions-except=/app/stealth-extension",
"--use-gl=desktop",
"--ignore-gpu-blocklist",
"--enable-accelerated-2d-canvas",
"--enable-gpu-rasterization",
"--enable-zero-copy",
"--ignore-webgl-blacklist",
# Добавляем экспериментальные флаги для WebGL
"--enable-experimental-webgl",
"--enable-webgl2-compute-context",
])
# Убираем загрузку расширения CapSolver - используем API вместо него
# Расширение конфликтует с Cloudflare Turnstile
# Решение капчи происходит через функцию solve_captcha_with_capsolver
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
logger.info(f"[{task_id}] CapSolver will solve CAPTCHA via API (not extension)")
# Добавляем специальные флаги для обхода reCAPTCHA если включено
if RECAPTCHA_BYPASS:
chrome_cmd.extend([
@@ -848,6 +874,14 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
"--disable-ipc-flooding-protection",
])
logger.info(f"[{task_id}] Added reCAPTCHA bypass flags")
else:
# Всегда добавляем --no-sandbox для работы в контейнере
# и --disable-blink-features=AutomationControlled для обхода Cloudflare Turnstile
chrome_cmd.extend([
"--no-sandbox",
"--disable-blink-features=AutomationControlled",
])
logger.info(f"[{task_id}] Added minimal stealth flags for Cloudflare Turnstile")
# Добавляем прокси если есть
chrome_cmd.extend(proxy_args)
@@ -913,39 +947,70 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
logger.info(f"[{task_id}] Using new Chrome on internal port {CHROME_INTERNAL_PORT}")
# Инжектируем stealth-скрипты через CDP для дополнительной защиты
# Отключено: stealth-скрипты вызывают ошибки и ломают Cloudflare Turnstile
# try:
# import json
# stealth_script_path = "/app/stealth-extension/stealth.js"
# if os.path.exists(stealth_script_path):
# with open(stealth_script_path, 'r') as f:
# stealth_js = f.read()
#
# # Получаем список целей (вкладок) через CDP
# response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
# targets = json.loads(response.read().decode())
#
# for target in targets:
# if target.get('type') == 'page':
# ws_url = target.get('webSocketDebuggerUrl')
# if ws_url:
# logger.info(f"[{task_id}] Injecting stealth scripts via CDP into {target.get('url', 'unknown')}")
#
# logger.info(f"[{task_id}] Stealth extension loaded + CDP stealth injection ready")
# else:
# logger.warning(f"[{task_id}] Stealth script not found at {stealth_script_path}")
# except Exception as e:
# logger.warning(f"[{task_id}] CDP stealth injection failed (extension should still work): {e}")
try:
import json
stealth_script_path = "/app/stealth-extension/stealth.js"
if os.path.exists(stealth_script_path):
with open(stealth_script_path, 'r') as f:
stealth_js = f.read()
# Получаем список целей (вкладок) через CDP
response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
targets = json.loads(response.read().decode())
for target in targets:
if target.get('type') == 'page':
ws_url = target.get('webSocketDebuggerUrl')
if ws_url:
logger.info(f"[{task_id}] Injecting stealth scripts via CDP into {target.get('url', 'unknown')}")
logger.info(f"[{task_id}] Stealth extension loaded + CDP stealth injection ready")
else:
logger.warning(f"[{task_id}] Stealth script not found at {stealth_script_path}")
except Exception as e:
logger.warning(f"[{task_id}] CDP stealth injection failed (extension should still work): {e}")
# Подключаемся через CDP
browser = Browser(cdp_url=cdp_url)
else:
# Обычный запуск
# Добавляем флаги для работы в контейнере
extra_chromium_args = [
"--no-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--enable-webgl",
"--use-gl=desktop",
"--ignore-gpu-blocklist",
"--enable-accelerated-2d-canvas",
"--enable-gpu-rasterization",
"--enable-zero-copy",
"--ignore-webgl-blacklist",
"--enable-experimental-webgl",
]
# Используем Google Chrome если доступен
chrome_path = os.getenv("CHROME_PATH", "/usr/bin/google-chrome")
if os.path.exists(chrome_path):
logger.info(f"Using Google Chrome at {chrome_path}")
else:
logger.info("Google Chrome not found, using default Chromium")
chrome_path = None
# Убираем загрузку расширения CapSolver - используем API
# Расширение конфликтует с Cloudflare Turnstile
extensions = []
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
logger.info("CapSolver will solve CAPTCHA via API (not extension)")
browser_profile = BrowserProfile(
user_data_dir="/app/browser-data/main_profile",
profile_directory="Default",
headless=False, # Всегда false для поддержки reCAPTCHA
keep_alive=True,
extra_chromium_args=extra_chromium_args,
executable_path=chrome_path,
extensions=extensions,
)
browser = Browser(browser_profile=browser_profile)
@@ -963,29 +1028,67 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
if custom_tools is not None:
agent_kwargs["tools"] = custom_tools
# Добавляем информацию о капче в промпт, если включено авто-решение
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
captcha_hint = "\n\nIMPORTANT: If you encounter a CAPTCHA (reCAPTCHA, hCaptcha, or Cloudflare Turnstile), use the solve_captcha tool to automatically solve it. The tool requires CAPSOLVER_API_KEY to be configured."
agent_kwargs["task"] = task + captcha_hint
agent = Agent(**agent_kwargs)
# Автоматическое решение капчи перед запуском, если включено
if AUTO_SOLVE_CAPTCHA and CAPSOLVER_API_KEY:
logger.info(f"[{task_id}] AUTO_SOLVE_CAPTCHA enabled, checking for captcha...")
try:
# Получаем страницу через browser_use
from browser_use import BrowserSession
browser_session = BrowserSession(browser=browser)
page = await browser_session.get_current_page()
# Получаем страницу через browser
page = None
try:
# Пробуем получить страницу через CDP
import urllib.request
import json
response = urllib.request.urlopen(f"{cdp_url}/json/list", timeout=5)
targets = json.loads(response.read().decode())
for target in targets:
if target.get('type') == 'page' and target.get('url', '') not in ['about:blank', 'chrome://newtab/']:
# Найдена страница с URL
logger.info(f"[{task_id}] Found page: {target.get('url')}")
# Подключаемся к странице через Playwright
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(cdp_url)
context = browser.contexts[0] if browser.contexts else None
if context:
pages = context.pages
if pages:
page = pages[0]
break
except Exception as e_page:
logger.warning(f"[{task_id}] Could not get page: {e_page}")
if page:
logger.info(f"[{task_id}] Page obtained, checking for captcha elements...")
# Проверяем наличие капчи
captcha_found = await page.query_selector('[data-sitekey]')
turnstile_found = await page.query_selector('iframe[src*="turnstile"]')
if captcha_found or turnstile_found:
logger.info(f"[{task_id}] CAPTCHA detected, attempting to solve automatically...")
# Вызываем функцию решения капчи
solved = await solve_captcha_with_capsolver(page, CAPSOLVER_API_KEY)
if solved:
logger.info(f"[{task_id}] CAPTCHA solved successfully!")
try:
captcha_found = await page.query_selector('[data-sitekey]')
turnstile_found = await page.query_selector('iframe[src*="turnstile"]')
logger.info(f"[{task_id}] Captcha found: {captcha_found}, Turnstile found: {turnstile_found}")
if captcha_found or turnstile_found:
logger.info(f"[{task_id}] CAPTCHA detected, attempting to solve automatically...")
# Вызываем функцию решения капчи
solved = await solve_captcha_with_capsolver(page, CAPSOLVER_API_KEY)
if solved:
logger.info(f"[{task_id}] CAPTCHA solved successfully!")
else:
logger.warning(f"[{task_id}] Failed to solve CAPTCHA automatically")
else:
logger.warning(f"[{task_id}] Failed to solve CAPTCHA automatically")
logger.info(f"[{task_id}] No captcha detected on page")
except Exception as e_check:
logger.warning(f"[{task_id}] Error checking captcha: {e_check}")
else:
logger.warning(f"[{task_id}] Could not get page from browser")
except Exception as e:
logger.warning(f"[{task_id}] Auto-captcha solve failed: {e}")
logger.error(f"[{task_id}] Auto-captcha solve failed: {e}")
else:
logger.info(f"[{task_id}] AUTO_SOLVE_CAPTCHA={AUTO_SOLVE_CAPTCHA}, CAPSOLVER_API_KEY={'set' if CAPSOLVER_API_KEY else 'not set'}")
# Выполнение задачи
history: AgentHistoryList = await agent.run(max_steps=max_steps)
+6 -5
View File
@@ -6,23 +6,24 @@ services:
context: .
dockerfile: Dockerfile
image: browser-use-mcp:latest
container_name: browser-use
security_opt:
- seccomp:unconfined
- apparmor:unconfined
ports:
- "8383:8383"
- "9222:9222"
- "${APP_PORT_8383}:${CONTAINER_PORT_8383}"
- "${APP_PORT_9222}:${CONTAINER_PORT_9222}"
environment:
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- LLM_MODEL_NAME=${LLM_MODEL_NAME:-arcee-ai/trinity-large-preview:free}
- REMOTE_DEBUGGING_ENABLED=true
- REMOTE_DEBUGGING_PORT=9222
- REMOTE_DEBUGGING_PORT=${CONTAINER_PORT_9222}
# CapSolver для автоматического решения капчи (Turnstile, reCAPTCHA, hCaptcha)
- CAPSOLVER_API_KEY=${CAPSOLVER_API_KEY:-}
- AUTO_SOLVE_CAPTCHA=${AUTO_SOLVE_CAPTCHA:-false}
# ВАЖНО! Прокси обязательна для Turnstile! Без прокси Cloudflare детектит бота
- PROXY_URL=${PROXY_URL:-}
volumes:
- /home/nafer/dokploy_data/browser-use:/app/browser-data
- "${APP_DATA_PATH}:/app/browser-data"
shm_size: '4gb'
ipc: host
deploy: