Skip to content
dsh.fish
Bundle

dsh-yandex-browser

DSH plugin: runs Yandex Browser with a persistent signed-in profile for the agent, and exposes tools to read and drive the pages inside it over the DevTools protocol.

Source
DmitPerson42
License
MIT
Updated
Updated 21 days ago

Readme

# dsh-yandex-browser

**Русский** | [English](README.en.md)

Плагин для DeepSeek Harness: поднимает **Яндекс Браузер** с постоянным профилем и
отладочным портом, чтобы агент мог работать в твоём настоящем браузере - вместе с
авторизациями, которые ты в него входишь, - и даёт модели прямые инструменты для
страниц.

## Зачем он нужен

Агент в браузере обычно работает в одноразовом профиле: каждый запуск - чистый
браузер без вкладок, расширений и cookies. Отсюда неудобства: нельзя открыть
почту, нельзя зайти в личный кабинет, нельзя пользоваться расширениями.

Плагин решает ровно это: у браузера агента **один постоянный профиль**
(`%USERPROFILE%\.dsh\browsers\yandex`). Ты один раз входишь в аккаунты руками -
дальше сессии живут, и агент ими пользуется.

## Как это устроено

Яндекс Браузер - это Chromium, поэтому он умеет открывать локальный порт
отладки (Chrome DevTools Protocol). Через него внешняя программа видит вкладки,
открывает новые и выполняет JavaScript внутри страницы. Плагин делает три вещи:

```
DSH GUI -> композиция
   |-- yandex_browser: поднимает браузер на порту 9222 и следит за ним
   |-- yandex_page:    читает страницу (текст, элементы, eval, скриншот)
   |-- yandex_act:     действует на странице (переход, клик, ввод, вкладки)
   `-- MCP yandex (необязательно): Playwright с теми же 25 инструментами
```

Плагин не зависит от MCP: `yandex_page` и `yandex_act` говорят с браузером
напрямую по тому же порту. Нужен именно этот слой, когда MCP-клиент не может
подключиться - он делал это и раньше, и падал по таймауту.

## Инструменты

### `yandex_browser` - жизненный цикл

| Действие | Что делает |
|---|---|
| `status` | Браузер запущен или нет, версия, адрес эндпоинта, путь профиля, открытые вкладки |
| `start` | Запустить браузер с профилем агента и портом отладки (если уже запущен - ничего не делает) |
| `open` | Открыть URL в новой вкладке; если браузер не запущен - сначала запустит |
| `login` | Открыть страницу входа (по умолчанию Яндекс Паспорт), чтобы **ты сам** ввёл пароль |
| `tabs` | Список открытых вкладок |
| `close` | Закрыть браузер; сохранённые входы остаются в профиле |

### `yandex_page` - чтение

| Действие | Что делает |
|---|---|
| `text` | Текст страницы плюс список интерактивных элементов с подписями |
| `eval` | Выполнить произвольное JavaScript-выражение и вернуть значение |
| `screenshot` | Сохранить PNG (по умолчанию видна только область экрана, параметр `fullPage` берёт всю страницу) |
| `tabs` | Вкладки с их id; у вкладок, созданных плагином, стоит отметка |

`text` отдаёт не только `innerText`, но и подписи элементов из `aria-label`,
`title` и `placeholder`. Это то, что делает читаемым интерфейс на веб-компонентах:
у Google Диска, например, строки файлов живут в кастомных элементах, и в обычном
тексте страницы их нет.

### `yandex_act` - действия

| Действие | Что делает |
|---|---|
| `navigate` | Перейти по URL в текущей или новой вкладке |
| `click` | Кликнуть элемент, найденный по CSS-селектору или по видимому тексту |
| `type` | Ввести текст в поле (очищает поле, работает с React-формами), опционально отправить Enter |
| `key` | Нажать клавишу (Enter, Tab, Escape, стрелки, PageUp/PageDown) |
| `activate` | Показать вкладку пользователю |
| `close` | Закрыть одну вкладку |
| `close_created` | Закрыть все вкладки, которые открыл этот плагин |
| `wait` | Ждать появления элемента по селектору или тексту |

Типичный сценарий: `status` -> `open` -> `text` (понять, что на странице) ->
`click` / `type` -> `close`.

Пароли агент никогда не вводит. Для входа в аккаунт он зовёт `login`, а ты вводишь
данные в видимом окне браузера.

## Правило про вкладки

Вкладка, открытая агентом, - это мусор, если её оставить. Поэтому плагин помнит
id всех вкладок, которые открыл сам (`agent-tabs.json` в профиле, переживает
перезапуск DSH), и умеет закрывать их одной командой:

```
yandex_act action: "close_created"   # закроет всё, что открыл плагин
```

Твои рабочие вкладки плагин не трогает: закрыть можно только по явному id,
который вернул `yandex_page action: "tabs"` или `yandex_act`.

## Установка

```sh
dsh plugin --profile web add github:DmitPerson42/dsh-yandex-browser
```

Плагин ставится как обычный bundle: пакет объявляет `dsh.bundle.patch`, а лежащий
рядом `cordis.patch.yml` добавляет его в профиль. Перезапусти DSH GUI - и всё.

Тем же способом он ставится кнопкой из маркета плагинов
(`dsh plugin --profile web add dshmarket`), когда плагин появится в каталоге.

### Установка вручную, без CLI

```powershell
git clone https://github.com/DmitPerson42/dsh-yandex-browser
pwsh -File .\dsh-yandex-browser\install.ps1
```

Скрипт копирует плагин в `~\.dsh\plugins\dsh-yandex-browser` (без зависимостей,
только встроенные модули Node) и печатает, что нужно добавить в
`~\.dsh\profiles\web\cordis.patch.yml`:

```yaml
- insert:
    - id: yandex-browser
      name: "file:///C:/Users/Druli/.dsh/plugins/dsh-yandex-browser/lib/index.js"
      config:
        autoStart: true
```

После правки конфигурации перезапусти DSH GUI.

### Важно: исправление MCP-клиента

Тот же `cordis.patch.yml` содержит MCP-сервер `yandex`. В исходной конфигурации
адрес передавался позиционным аргументом:

```yaml
args: ["-y", "@playwright/mcp@latest", "http://127.0.0.1:9222"]   # так не работает
```

Актуальная версия Playwright MCP такого аргумента не принимает и падает с
`too many arguments. Expected 0 arguments but got 1`, из-за чего сервер всегда
висел в статусе `disconnected`. Нужен явный флаг:

```yaml
args:
  - "-y"
  - "@playwright/mcp@latest"
  - "--cdp-endpoint"
  - "http://localhost:9222"
```

Здесь два момента, оба проверены на практике.

**Адрес пишется литералом.** Синтаксис `!!js` в patch-записях не вычисляется:
MCP-сервер получал строку `process.env.YANDEX_CDP ?? 'http://localhost:9222'` и
падал с `Invalid URL`. Меняешь порт - правь эту строку и `port` плагина
одновременно.

**Именно `localhost`, а не `127.0.0.1`.** Браузер поднимает сокет отладки то на
IPv4, то только на IPv6 - на этой машине он один раз слушался на `[::1]`, и запрос
к `127.0.0.1` его не видел. `localhost` в Node перебирает оба стека. Плагин
делает то же самое: пробует оба адреса и в ответе показывает тот, который
реально отвечает.

## Вход в аккаунты - один раз

1. Запусти DSH с плагином: браузер поднимется сам (`autoStart: true`).
2. Попроси агента: «открой страницу входа в Яндекс» - или вызови
   `yandex_browser` с `action: "login"`.
3. В окне браузера войди в аккаунты **вручную**. Поставь галочку «Запомнить меня»
   и, если предлагается, «Выходить на всех устройствах» - не надо, пароль хранится
   в профиле.
4. Готово. Сессии лежат в профиле агента и работают при каждом следующем запуске.

Полезно один раз зайти и в расширения, которые хочешь иметь в браузере агента:
расширения ставятся в профиль и переживают перезапуски.

## Настройки

| Поле | По умолчанию | Смысл |
|---|---|---|
| `autoStart` | `true` | Поднимать браузер при загрузке плагина, чтобы MCP-клиент сразу нашёл живой эндпоинт |
| `cdpEndpoint` | `YANDEX_CDP` или `http://localhost:9222` | Адрес DevTools |
| `profileDir` | `YANDEX_BROWSER_PROFILE` или `~\.dsh\browsers\yandex` | Постоянный профиль |
| `executable` | `YANDEX_BROWSER_PATH` или автопоиск | Путь к `browser.exe` |
| `port` | `YANDEX_BROWSER_PORT` или порт из эндпоинта | Порт отладки |
| `screenshotDir` | `~\.dsh\browser-shots` | Куда складывать скриншоты без явного `file` |
| `tabStateFile` | `<profileDir>\agent-tabs.json` | Память вкладок, созданных плагином |

Порт и профиль связаны с MCP-клиентом: меняешь `YANDEX_CDP` - меняются оба.

## Проверка

```powershell
node .\test\smoke.mjs                 # только status, браузер не запускается
node .\test\smoke.mjs --launch        # start + open + tabs
node .\test\check-login.mjs           # вошёл ли пользователь в аккаунт
node .\test\mcp-check.mjs             # поднимает MCP поверх живого браузера и печатает инструменты
node .\test\page-tools.mjs            # низкоуровневый прогон CDP: вкладка, текст, eval, скриншот
node .\test\tools.mjs                 # все три инструмента на локальной странице-фикстуре
node .\test\close-test-browsers.mjs   # закрыть тестовые экземпляры: порты в аргументах
```

`tools.mjs` - главный тест: он загружает плагин с поддельным реестром инструментов,
открывает `test/fixtures/page.html` в новой вкладке, читает страницу, вводит
текст, нажимает кнопки по подписи и по `aria-label`, снимает скриншот и закрывает
вкладку. Запуск без DSH не нужен - достаточно запущенного браузера агента.

`check-login` не полагается на редиректы: он читает имя аккаунта из куки
`yandex_login`, наличие `Session_id` и открывает `passport.yandex.ru/profile` -
если форма входа не появилась, сессия живая. Значения остальных кук не читаются.

## Если что-то пошло не так

**Страница в фоне не отвечает.** Chromium замораживает фоновые вкладки, а
замороженная вкладка не отвечает ни на один `Runtime.evaluate` - выглядит это
как мёртвая страница. Плагин решает это сам: каждая сессия начинается с
`Page.bringToFront`. Побочный эффект - вкладка, с которой работает агент,
всплывает наверх. Это заметно, но иначе фоновые страницы читать нельзя.

**Порт 9222 уже занят другим браузером** - плагин скажет, кем именно. Закрой его
или перенеси порт через `YANDEX_CDP` (не забудь про `YANDEX_BROWSER_PORT`, чтобы
плагин и MCP смотрели на один адрес).

**Первый запуск дольше минуты** - холодный профиль один раз создаёт свои
компоненты (`AsrSubtitles`, `component_crx_cache` и прочее) и показывает
приветственную страницу. Плагин ждёт до 60 секунд; следующие запуски занимают
пару секунд.

**MCP `server is disconnected`** - клиент не дождался браузера и исчерпал попытки
переподключения. Запусти браузер через `yandex_browser action: "start"` и
перезапусти сессию DSH. На `yandex_page` и `yandex_act` это не влияет - они
подключаются на каждый вызов заново.

**Браузер не стартует, профиль пустой** - начиная с Chromium 136 ключ
`--remote-debugging-port` игнорируется для основного профиля. Плагин всегда
запускает браузер с отдельным `--user-data-dir`; менять это нельзя, иначе порт не
откроется.

**Второй экземпляр не поднимается** - если уже открыт Яндекс Браузер с твоим
обычным профилем, агент всё равно запускает отдельный экземпляр со своим
профилем. Оба могут работать одновременно; порт при этом занимает только
экземпляр агента.

**Песочница** - в режиме `workspace-write` плагин не сможет создать профиль за
пределами рабочей папки. В DSH плагин работает в хост-процессе, поэтому штатной
этой проблемы нет; ограничение касается только запуска тестов из песочницы
(`tools.mjs` в этом случае пишет скриншоты во временный каталог и предупреждает
о неудачной записи списка вкладок - на результат теста это не влияет).

## Как устроен код

| Файл | Смысл |
|---|---|
| `lib/index.js` | Три инструмента, жизненный цикл браузера, учёт вкладок |
| `lib/cdp.js` | Низкоуровневый клиент CDP: HTTP-эндпоинт, WebSocket-сессия вкладки, скрипты чтения страницы |
| `test/*.mjs` | Прогоны без DSH: смоук, логин, MCP, CDP, все инструменты |
| `install.ps1` | Копирование плагина в профиль и подсказка для `cordis.patch.yml` |

Сессии CDP короткоживущие: на одну операцию - один WebSocket, который сразу
закрывается. Так нечему протекать, и такой сеанс переживает вкладку, которую
закрыли или перезагрузили посреди работы.

## Лицензия

MIT.

Install

dsh plugin --profile web add github:DmitPerson42/dsh-yandex-browser

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source