From 607a475e28e6c803f80b32081b35cdeb0bfd2d83 Mon Sep 17 00:00:00 2001 From: vitya Date: Sat, 12 Sep 2026 12:30:05 +0300 Subject: [PATCH] =?UTF-8?q?feat(ops-browser):=20=D1=81=D0=BA=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=81=D0=B2=D0=BE=D0=B5=D0=B3=D0=BE=20=D1=81=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=D0=BE=D0=B3=D0=BE=20=D0=B1=D1=80=D0=B0=D1=83=D0=B7?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=20(=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB?= =?UTF-8?q?=D1=8C+CDP+=D1=85=D0=B5=D0=BD=D0=B4=D0=BE=D1=84=D1=84)=20=D0=B2?= =?UTF-8?q?=20=D0=BA=D0=B0=D1=82=D0=B0=D0=BB=D0=BE=D0=B3;=20=D0=BC=D0=B0?= =?UTF-8?q?=D0=BF=D0=BF=D0=B8=D0=BD=D0=B3=20=D0=B2=20Hermes-=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D0=BE;=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B2=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + README.ru.md | 1 + dist/ops-browser.skill | Bin 0 -> 3181 bytes hermes/mapping.yaml | 4 ++ skills/ops-browser/SKILL.md | 90 ++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 dist/ops-browser.skill create mode 100644 skills/ops-browser/SKILL.md diff --git a/README.md b/README.md index e8d530f..372a4e9 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ an explicit `adapted-from` marker in its frontmatter. | `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — two-axis + Fowler baseline; output: caveman-review format | | `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills core + ideya 8 self-skill-authoring | | `web-search` | `author: ours` — search_web tool (pi-extension) + policy: when to search, «без поиска» session-off | +| `ops-browser` | `author: ours` — свой **скрытый** браузер агента: отдельный профиль + CDP (`eval`/`fetch` из страницы/скриншоты), `handoff` человеку для пароля/капчи; свой замок `ops.lock` | | `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент», рецепты тяжёлых страниц. Закрывает провал базового прогона 2026-09-11 («куки из Chrome + curl + ввод пароля» мимо канала); анонимные прогоны — `browser-cdp` | | `review-subagent` | `author: ours` — review_subagent tool (pi-extension): clean-context review by your own model, optional `model` override | | `report-mappa-issue` | `author: ours` — TEMPORARY stopgap: mappa deviation reporting (mail to `mappa` + `.workshop`) while the service is unstable; retire when stabilized | diff --git a/README.ru.md b/README.ru.md index 8b10b84..04f730f 100644 --- a/README.ru.md +++ b/README.ru.md @@ -91,6 +91,7 @@ bash scripts/build.sh caveman # один | `code-search` | `author: ours` — rg-first код-поиск (замер: 15 мин → 0 сек; роутинг: rg / git grep / interns repo_read / grep_audit) | | `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — двухосевость + Fowler-база; формат вывода: caveman-review | | `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills ядро + идея 8 self-skill-authoring | +| `ops-browser` | `author: ours` — свой скрытый браузер агента (профиль + CDP + `handoff` человеку, замок `ops.lock`) | | `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент»; анонимные прогоны — `browser-cdp` | | остальные `skills/*` | `author: ours` | diff --git a/dist/ops-browser.skill b/dist/ops-browser.skill new file mode 100644 index 0000000000000000000000000000000000000000..14a57eee9ad10ccfe49ac9d241aef65daba75202 GIT binary patch literal 3181 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 + Use when a task needs a real browser — личный кабинет, заказы, чеки, yt-digest, + скриншот живого сайта, «дёрни внутренний API». Свой СКРЫТЫЙ браузер агента: + отдельный профиль, CDP, `fetch()` ИЗ страницы, хендофф человеку для пароля/капчи. +--- + +# ops-browser — свой скрытый браузер агента + +**Есть задача, которой нужен браузер? Работай в своём браузере, скрыто, и не подсовывай куки.** +Профиль владеет сессией сам (куки продлеваются браузером, а не руками) — это и есть лечение +боли «куки протухли». + +## Когда ЭТОТ инструмент, а когда другой + +| Ситуация | Чем работать | +|---|---| +| Публичная страница без логина | `web_search` / `web_extract` / curl — браузер не нужен | +| Личный кабинет, заказы, чеки, внутренний API сайта, антибот | **`ops-browser.sh`** (этот скил) | +| Нужны ЕГО логины и ЕГО Chrome (Avito, кабинеты поставщиков) | канал оператора: Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools` — всё под арендой `driver.lock` | +| Посмотреть глазами, кликнуть по живому сайту, показать ему | панель предпросмотра (`desktop_preview` + `drive_preview`) — без JS, только текст/клики | + +`ops-browser` не заменяет канал оператора: там его сессии, здесь — **мой** профиль. +И то и другое живёт под правилом «один водитель» (у ops свой замок `ops.lock`). + +## Команды + +```bash +OB="$HOME/.config/browser-harness/bin/ops-browser.sh" +bash "$OB" start # headless + аренда (окон нет) +bash "$OB" open https://site/path # перейти +bash "$OB" read 2000 # видимый текст страницы +bash "$OB" eval 'fetch("/api/x",{credentials:"include"}).then(r=>r.json()).then(j=>JSON.stringify(j))' +bash "$OB" screenshot [path] # PNG, печатает путь +bash "$OB" cookies https://site # имена кук (без значений) +bash "$OB" status # аренда + CDP + текущая страница +bash "$OB" stop # ШТАТНО закрыть и отпустить +bash "$OB" handoff # человеку: пароль/2FA/капча (см. ниже) +``` + +`eval` выполняет JS **в контексте страницы** — значит `fetch()` идёт с её куками и заголовками: +так дёргают внутренние эндпоинты приложения (то, что недоступно извне и чего не умеет панель). + +## Хендофф человеку (пароль, 2FA, капча, согласие) + +Останавливаюсь и отдаю экран **сам**, без «скажи готово»: + +1. `bash "$OB" handoff ` — закрывает headless (профиль нельзя открыть дважды) и поднимает + **видимое** окно ТОГО ЖЕ профиля на нужной странице; +2. человек вводит, что нужно (пароль/2FA/капчу вводит только он); +3. признак успеха ловлю сам: URL ушёл со страницы логина/auth **или** выросли куки домена; +4. штатно закрываю окно и возвращаюсь в headless — логин остался в профиле. + +Скриншот делаю **до** шага (что от человека хотят) и **после** (что получилось); **во время ввода +пароля не снимаю**. Если признак успеха не увидел — окно остаётся открытым, а я говорю об этом прямо. + +## Границы (не двигаются) + +- Пароли, 2FA, капчу — **вводит человек**. Я к ним не подхожу. +- Деньги, оформление заказов, формы с перс.данными — только с явного согласия оператора. +- Куки не выгружаю и не расшифровываю; содержимое залогиненных страниц не уходит в логи/вику. +- Антибот-челленджи не обхожу; если сайт невзлюбил `headless` — это повод для `handoff`, а не для трюков. + +## Грабли (стоили времени, проверены живьём) + +- **Закрывать только штатно** (`stop` → `Browser.close`). Жёсткий kill может не сбросить профиль → + потеря логина. Сессионные куки (без срока) перезапуск не переживают по дизайну — настоящие логины живут. +- **MSYS-пути**: нативным программам нужен вид `C:/...` (`cygpath -m`), а `powershell -File` — + только `C:\...` (`cygpath -w`). Иначе «модуль не найден» / «файл .ps1 не найден». +- **Экранирование PowerShell внутри bash-строки тихо ломается** (`$_` подменяется) — PS-запросы + держим в отдельных `.ps1`. +- **Профиль нельзя открыть дважды** — перед видимым окном headless обязан остановиться (это делает `handoff`). +- **Профиль**: `~/.config/browser-harness/profiles/ops`; порт CDP: `OPS_CDP_PORT` (9346). + +## Аренда + +```bash +BL="$HOME/.config/browser-harness/bin/browser-lease.sh" +bash "$BL" --lock ops status # состояние ops-замка (у Chrome оператора — driver.lock) +``` + +Чужой держатель `ops.lock` → `start` честно отказывает (rc=1). Мёртвый держатель отпускается по PID/TTL. +Идёшь в браузер **сам** (в обход `ops-browser.sh`)? Возьми `ops.lock` так же, как любой другой харнесс. + +Контракт: mappa `concepts/ops-browser-contract` (проект `pi-extensions`), +требования `requirements:42`, план `plan:35`. Исходники: репо `pi-extensions/scripts/browser/` +(установка `just install-browser`).