Скриншоты жгут токены, headless не залогинен: отдаём ИИ-агенту настоящий Chrome через accessibility-дерево
Давайте будем честны: сегодня способы дать ИИ-агенту поработать в браузере выглядят так.
Playwright или Selenium: браузер, который агент запускает сам, со свежим профилем. Ни одной сессии, и на первом же шаге — стена логина, 2FA или капча. Настроить remote-debugging и подключиться к живому Chrome: работает, но требует запуска с флагом и убивает нормальную сессию. MCP-браузерные серверы: JSON-конфиги, совместимость клиента, и часто всё равно скриншоты.
Вендорские решения тоже появились — и все решают задачу внутри своей экосистемы. Расширение Anthropic водит вашим Chrome только из Claude: подписка, логин в claude.ai, сессия гоняется через облако, модель одна — Claude. Расширение OpenAI управляется десктоп-приложением, и в Codex CLI браузер по документации самого OpenAI недоступен. Встроенный браузер VS Code Copilot — отдельная среда со своим профилем, не ваш повседневный Chrome. Десктоп-агенты с computer use работают в своей песочнице со своими логинами. Каждый отвечает на вопрос «как дать моему агенту браузер», и никто — на вопрос «как дать агенту мой браузер».
При этом у каждого из нас уже есть идеально настроенный, залогиненный во всё браузер. Свой. И агент до него не дотягивается — по одной причине: никто не провёл нейтральный мост между расширением и терминалом.
Я этот мост провёл. Проект называется chrome-bridge: крошечное расширение для Chrome плюс CLI на Node без единой npm-зависимости. Агент получает ваш настоящий браузер — открытые вкладки, залогиненные сессии, SSO. Open source, MIT.
Как это устроено
Архитектура укладывается в одну строку: расширение (один читаемый background.js) — WebSocket — локальный Node-сервер — CLI. Никакого облака и никаких аккаунтов: код и данные не покидают машину.
Клиент у CLI — обычный shell, поэтому работает любой агент, который умеет запускать команды: Claude Code, Cursor, Codex, GLM, Kimi, Qwen, DeepSeek, локальные модели. MCP не нужен, SDK не нужен, версия модели не важна.
node cli.mjs snap habr.com # страница как accessibility-дерево
node cli.mjs click habr.com @e14 # клик по элементу из дерева
node cli.mjs fill habr.com @e12 "chrome extension"
node cli.mjs shot habr.com out.png --max 800 # когда всё-таки нужны пиксели
Дерево выглядит так — весь текст страницы со ссылками на элементы:
table "Hacker News new | past | comments | ask | show | jobs | submit" @e1
link "Hacker News" @e5
link "new" @e6
link "submit" @e12
link "login" @e13
Ссылки @eN — это и есть главная экономия. Замер на живой странице: дерево целиком — 2,4 КБ, скриншот той же страницы шириной 1000px — 16 КБ. На порядок меньше контекста на каждый шаг, и агент не «угадывает координаты по картинке», а кликает по ссылке из дерева. Ссылки переживают повторные снимки и истекают только при навигации — после перехода агент делает новый snap.
События, доверие и вердикты
Синтетические события не имеют флага isTrusted, и часть приложений их честно игнорирует. Для этого есть режим --trusted: клик идёт через DevTools Input с isTrusted=true. Плата очевидна: команды через CDP (net, shot, --trusted) монтируют отладчик, и страница это видит. Никакого «стелса» в проекте специально нет — это инструмент для своих аккаунтов на своей машине, и в README это написано прямым текстом.
Вторая обязательная часть инженерии — вердикты. После действия агент получает не тишину, а диагноз: succeeded, needs_human (стена логина — зовём человека), blocked (rate limit) или uncertain (событие ушло, ничего не изменилось — проверь иначе). На Excalidraw это выглядело честно: клик в канвас вернул uncertain, скриншот подтвердил, что ничего не произошло, а --trusted с двойным кликом прошёл и создал текстовый элемент, который потом появился прямо в дереве.
Память команд и реплей
Сервер держит кольцевой журнал: каждая команда попадает в него в момент выполнения со статусом ok/fail. history --batch out экспортирует журнал как реплейбельный скрипт: упавшая строка закомментирована с текстом ошибки, секретные значения (fill/type/paste) вырезаны. Полуупавший батч можно дописать с места падения — с одной честной оговоркой: реплей не идемпотентен, уже сделанный клик сделается ещё раз, хвост скрипта допиливается руками.
Ограничения
Сервер слушает 127.0.0.1 и не отвечает запросам со страниц, но любой локальный процесс может им воспользоваться. Инструмент — для машины, которой вы доверяете.
Canvas-приложения деревом почти не видны; там агент чаще работает скриншотами.
CDP-команды детектируются антибот-системами; «стелс»-стек в проект сознательно не включён.
Расширение просит <all_urls> и debugger — это цена функциональности. Код при этом читается целиком: один background.js и манифест, без сборки.
Итог
chrome-bridge — это попытка решить одну конкретную проблему: агент весь день сидит в терминале рядом с моим залогиненным браузером и не может в него зайти. Теперь может: дерево вместо скриншотов, клики по ссылкам вместо координат, фиолетовая плашка с историей каждого действия и кнопка ⏏, которая одним нажатием возвращает мне контроль.
GitHub: https://github.com/siropkin/chrome-bridge Chrome Web Store: https://chromewebstore.google.com/detail/chrome-bridge/kmhjlnokjigmnimgjjmiahlinjbcebkg
Если инструмент пригодится — расскажите в комментариях, что вы им автоматизируете. Issue и рецепты принимаются.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.