
Вступление
ИИ-агентам нужно ходить по настоящему вебу. Но настоящий веб враждебен к автоматизации: Playwright блокируют, headless-версия Chrome палится по отпечаткам, а стелс-плагины просто становятся частью отпечатка. Если вы когда-нибудь писали скраперы или инструменты для агентов, вы знаете, как это бывает - всё работает локально, а потом продакшен разваливается за Cloudflare-челленджем.
Несколько проектов пытались решить эту проблему со стороны браузера. Проект https://camoufox.com патчит Firefox на уровне C++, так что navigator.hardwareConcurrency, WebGL-рендереры, AudioContext, геометрия экрана и WebRTC подменяются ещё до того, как JavaScript их увидит. Браузер https://github.com/jo-inc/camofox-browser оборачивает этот движок в REST API, заточенный под агентов: снимки доступности вместо раздутого HTML, стабильные ссылки на элементы для кликов и изоляция сессий.
Остаётся только одна дыра: инструментарий вокруг этой экосистемы завязан на JavaScript/Python. Если вы живёте в Go - а весь стек Go-агентов, взорвавшийся за последние пару лет, весомый аргумент в его пользу - вам оставалось писать сырые вызовы curl.
Пакет https://github.com/yvv4git/go-juggler исправляет это. Это Go-клиент для протокола автоматизации Juggler (того самого, который патчит и расширяет Camoufox) под лицензией MIT. Он управляет Firefox/Camoufox из Go с единственной зависимостью и чистым, слоистым API.
Что такое Juggler
Juggler - это протокол автоматизации браузера, разработанный для Firefox ещё до того, как в браузере появился CDP (Chrome DevTools Protocol). Репозиторий https://github.com/puppeteer/juggler принадлежит проекту Puppeteer. Это отдельный модуль внутри Firefox, а не часть ядра браузера, что упрощает его патчинг и контроль над тем, что раскрывается странице.
Проводной формат повторяет привычную CDP-раскладку:
{ "id": 1, "method": "Browser.newPage", "params": { ... } } { "id": 1, "result": { ... } } { "method": "Page.crashed", "params": { ... } }
Camoufox патчит Juggler, чтобы дать ему собственную изолированную "копию" страницы для работы. Playwright может свободно читать и редактировать свою версию, тогда как реальная страница остаётся нетронутой и не может обнаружить чтения, слушатели или внедрённых агентов. Ввод проходит через исходные обработчики ввода Firefox, поэтому клики и нажатия клавиш ведут себя точно как человеческие. Это та самая стелс-основа, на которой стоит go-juggler.
Что такое go-juggler
Пакет go-juggler - это Go-клиент для протокола Juggler. Он автоматизирует браузеры с поддержкой Juggler, такие как Firefox и Camoufox, и предоставляет два способа управления ими:
1. Высокоуровневый REST-клиент (juggler.NewClient("http://localhost:9377")), который общается с запущенной HTTP-обёрткой camofox-browser - здесь сегодня живёт вся готовая к использованию магия.
2. Низкоуровневый слой транспорта и протокола (pipe и WebSocket-каналы с CDP-стилем фрейминга сообщений), который напрямую говорит на проводном протоколе Juggler - основа для запуска и подключения к процессу браузера внутри процесса.
Установка:
go get github.com/yvv4git/go-juggler
Быстрый старт
docker run -d -p 9377:9377 --name camoufox yvv4docker/camoufox:latest
Затем минимальная программа, проходящая весь жизненный цикл вкладки:
package main
package main import ( "context" "log" "github.com/yvv4git/go-juggler" ) func main() { ctx := context.Background() c := juggler.NewClient("http://localhost:9377") tab, _ := c.OpenTab(ctx, "demo", "http://example.com") snap, _ := c.Snapshot(ctx, tab.TabID, "demo") c.Click(ctx, tab.TabID, "demo", "e1", "") c.CloseTab(ctx, tab.TabID, "demo") }
Запуск полного примера жизненного цикла против живого экземпляра даёт вывод примерно такого вида:
health: ok=true engine=camoufox sessions=0 tab opened: 60bf9c8c-e6d9-4be0-ba09-738058215af6 navigated to rutube.ru snapshot (128908 chars): - img - banner: - button "Закрыть меню навигации" [e1]: ... - button "Вход" [e9] - navigation: - list: - listitem: - link "Главная" [e10]: tab closed session closed
Маркеры [e1], [e9], [e10] - это ключевая идея: снимок - это дерево доступности, где у каждого интерактивного элемента есть стабильная ссылка. Агенты находят и кликают элементы по ссылкам вместо борьбы с CSS-селекторами.
Возможности
Health- проверить статус браузера (движок, соединение, память)OpenTab- открыть новую вкладку и перейти по URLNavigate- загрузить URL в существующей вкладкеSnapshot- получить ARIA-дерево страницы (ссылки на элементы)Click- кликнуть по элементу по ссылке или CSS-селекторуType- заполнить поле ввода по ссылке или селекторуPress- нажать клавишу клавиатуры (Enter, Tab, Escape и т.д.)Scroll- прокрутить страницу вверх/вниз на N пикселейBack- перейти назад по историиForward- перейти вперёд по историиRefresh- перезагрузить текущую страницуLinks- вывести все ссылки на странице с пагинациейScreenshot- сделать PNG-скриншот (страницы или вьюпорта)Evaluate- выполнить произвольный JavaScript в контексте страницыNetworkRequests- получить все загруженные ресурсы (навигация + подресурсы)PollNetworkRequests- опрашивать ресурсы с течением времени с дедупликациейStats- получить состояние вкладки (URL, посещённые URL, ссылки)ListTabs- вывести все вкладки в сессии (URL, заголовок)CloseTab- закрыть вкладкуCloseSession- уничтожить всю сессию и все её вкладки
Три из них заслуживают более пристального взгляда.
Токен-эффективные снимки
Вызов Snapshot возвращает ARIA-дерево доступности вместо сырого HTML. Это огромный выигрыш для LLM-агентов, где каждый байт стоит токенов: снимок доступности примерно на 90% меньше соответствующего HTML, и он уже структурирован по семантическим ролям (button, link, list item) с прикреплёнными ссылками.
Инъекция JavaScript
Evaluate выполняет произвольный JavaScript в контексте страницы - удобно для разовых изменений, которые API не покрывает. Пример inject устанавливает заголовок документа и читает его обратно - проверяемый побочный эффект:
document.title = "Hi, friends!"; document.title
Примечание: эндпоинт /evaluate требует camofox-browser >= 1.4.0. Клиент обнаруживает более старый сервер (HTTP 404) и прямо сообщает вам об этом, а не молча падает.
Перехват сетевых запросов
NetworkRequests читает Performance API браузера и возвращает всё, что загрузила страница, это основной документ плюс подресурсы (CSS, скрипты, изображения, трекеры) - в хронологическом порядке. PollNetworkRequests расширяет это: он семплирует в течение заданного времени и объединяет результаты с дедупликацией, так что поздно загружающаяся реклама и трекеры аналитики тоже захватываются.
total requests: 6 1. [NAVIGATION ] https://www.wikipedia.org/ 2. [CSS ] https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-e49fbf32.svg 3. [SCRIPT ] https://www.wikipedia.org/portal/wikipedia.org/assets/js/index-34f340e24a.js 4. [SCRIPT ] https://www.wikipedia.org/portal/wikipedia.org/assets/js/gt-ie9-507b16b6be.js 5. [IMG ] https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png 6. [IMG ] https://www.wikipedia.org/static/favicon/wikipedia.ico
Архитектура: три слоя, одно направление
go-juggler/ ├── juggler.go # корневой пакет: публичный API-фасад ├── transport/ # байтовые каналы: pipe (fd 3/fd 4) и WebSocket ├── protocol/ # типы сообщений Juggler, JSON-кодирование/декодирование ├── browser/ # высокоуровневое управление браузером и вкладками └── examples/ # запускаемые примеры программ
transport/перемещает сырые байты.Transport- это двунаправленный канал, передающий JSON, разбитый по строкам. Есть две реализации:PipeTransport, работающий через унаследованную пару OS-pipe (fd 3/fd 4, классическая внутрипроцессная схема для запущенного браузера), иWebSocketTransport, подключающийся к эндпоинту автоматизации браузера. Транспорты безопасны для конкурентного использования и уважают отмену контекста - блокирующее чтение прерывается, как только контекст завершён.protocol/придаёт этим байтам смысл. Он определяет типMessage(id / method / params / result / error), трёхстороннюю классификацию (IsRequest,IsResponse,IsEvent) и JSON-хелперы кодирования/декодирования. Этот пакет остаётся независимым от любого транспорта.browser/- высокоуровневая поверхность.Browserоборачивает транспорт с насосом сообщений, чтобы вызывающие работали с типизированными хендлами вместо сырого JSON.Client- HTTP-клиент для REST API camofox-browser.Launcher- место, где реальный процесс браузера порождается и подключается.
Зависимости текут в одну сторону, сверху вниз: browser/ импортирует transport/, а protocol/ остаётся независимым. Корневой пакет реэкспортирует API браузера, так что большинству вызывающих достаточно одного импорта:
import "github.com/yvv4git/go-juggler"
Примеры программ
examples/basic- полный жизненный цикл вкладки (health, open, navigate, snapshot, close).examples/tab- все 16 операций с вкладками в одной программе: open, snapshot, links, stats, evaluate, click, navigate, back, forward, type, press, scroll, refresh, screenshot, close.examples/inject- инъекция JavaScript черезEvaluate.examples/requests- сопутствующий захват сетевых запросов с опросом и дедупликацией.examples/screen- скриншоты вьюпорта и полной страницы в PNG.examples/html- HTML страницы до и после загрузки динамического контента.
Пример с несколькими вкладками показывает, как масштабируются сессии:
opened 1: http://example.com/ opened 2: http://example.org/ opened 3: http://example.net/ total tabs: 3 1. [Example Domain] http://example.com/ 2. [Example Domain] http://example.org/ 3. [Example Domain] http://example.net/ closed acd74c0c closed 8c0951ac closed 57adc6dd session closed
Статус
go-juggler находится в активной разработке, и стоит чётко сказать, что сделано, а что нет.
Работает сегодня: полный HTTP REST-клиент для camofox-browser (все девятнадцать методов выше), слой транспорта (pipe + WebSocket с фреймингом, учитывающим контекст), типы сообщений протокола и все шесть примеров программ.
В дорожной карте: прямой путь - browser.Launch, порождающий исполняемый файл Camoufox с унаследованными pipe-fd и подключающийся по сырому протоколу Juggler, плюс полный API насоса сообщений Browser/Tab (navigate, reload, события страницы). Всё это помечено как TODO в исходниках и появится по мере завершения низкоуровневой поверхности протокола.
Если ваш сценарий - управление экземпляром Camoufox через REST API camofox-browser сегодня, go-juggler готов. Если вам нужно внутрипроцессное запускание браузера по сырым pipe - оно на подходе.
Итоги
Экосистема автоматизации антидетект-браузеров слишком долго была историей про JavaScript. go-juggler приносит те же стелс-возможности - подмену отпечатков на уровне C++, токен-эффективные ARIA-снимки, стабильные ссылки на элементы, изоляцию сессий - в экосистему Go с одним импортом и почти пустым деревом зависимостей.
Ссылки:
go-juggler on GitHub https://github.com/yvv4git/go-juggler
Juggler Protocol Architecture https://deepwiki.com/daijro/camoufox/6.1-juggler-system)
Camoufox https://camoufox.com
camofox-browser https://github.com/jo-inc/camofox-browser

