Compare commits
10 Commits
5c726eb5ec
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 79adaf928d | |||
| b51657bfe4 | |||
| 48fa29e9dc | |||
| 65a2518a5e | |||
| 84e28c5d1c | |||
| 607a475e28 | |||
| 252e22ec80 | |||
| ac0c41feb8 | |||
| 314b15ea25 | |||
| c310ada38d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -90,3 +90,6 @@ coverage/
|
||||
# Missing here made `git status` see `?? .tasks/claims/` → poller skipped every
|
||||
# claim with "working tree dirty". Mirrors .common/.gitignore.
|
||||
.tasks/claims/
|
||||
|
||||
# mappa bootstrap cache (генерируется, не в репо)
|
||||
.mappa/share/
|
||||
|
||||
@@ -6,3 +6,4 @@ tenant: vitya
|
||||
url: https://mappa.vds.kzntsv.site
|
||||
git_provider: gitea
|
||||
git: OpeItcLoc03/skills
|
||||
git_host: git.kzntsv.site
|
||||
|
||||
19
AGENTS.md
19
AGENTS.md
@@ -8,10 +8,27 @@ check across all projects
|
||||
pull remote before work
|
||||
session handoff: read on start, write on end
|
||||
inbox monitor: raise on start
|
||||
session sync: write to mappa
|
||||
follow project discipline
|
||||
follow tdd-criteria
|
||||
delegate to interns when allowed
|
||||
recommend, don't menu
|
||||
we're on Windows
|
||||
mappa sync: run on start
|
||||
|
||||
<!-- mappa:canon-block (auto; do not edit) -->
|
||||
## Mappa canon
|
||||
**Gates (canon/gates — shared):** краткая суть; полный текст — `wiki_get(slug='canon/gates', full=true)`
|
||||
- Г1. Знание и артефакты → mappa, не файлы — durable-знание и артефакты проекта живут в mappa; файловые каналы (`.brainstorm/`, `.tasks/`, `.wiki/`) и «сохранить рядом с проектом» закрыты.
|
||||
- Г2. Контракт каналов — письмо (inbox) — носитель вердиктов/указаний/находок, полным телом; комментарии на тасках — короткий след. Адресация `about`/`to`/`thread` (XOR); lifecycle `ack`/`resolve`/`cancel`.
|
||||
- Г3. Поиск до угадывания — не угадывать slug/роут: сначала `search`/`wiki_search`; `wiki_get` без project = shared; общая память — `search(mode='recall')`.
|
||||
- Г4. Адресация и слаги — рефы полными именами (`[[task:N]]`/`[[wiki:slug]]`); слаги kebab-case, латиница; номера `task:N` выдаёт сервер.
|
||||
- Г5. .mappa-гейт — папка участвует в mappa-операциях только с маркером `.mappa`; нет маркера → сказать человеку, мутации — отказ.
|
||||
- Г6. Секреты — в mappa не пишутся (422); только `secret:<path>`-рефы, значения мимо.
|
||||
- Г7. Degraded-режим — mappa недоступна: читать кэш `.mappa/` (canon/methodology/runbooks), мутации → `.mappa/pending/`; нет кэша → стоп, не импровизировать.
|
||||
- Г8. Перед работой с вики/каноном — первым действием прочитать канон-блок AGENTS.md проекта.
|
||||
- Г9. Живое состояние до заявления — статус заявлять только по свежему чтению mappa, не по памяти/кэшу/ответу create.
|
||||
**Entity → runbook (runbooks/index — shared):** task → [[runbooks/tasks]] · wiki → [[runbooks/wiki]] · inbox → [[runbooks/inbox]] · **thread** → [[runbooks/threads]] · session → [[runbooks/session]] · search → [[runbooks/search]] · issue → [[runbooks/issue]] · **intent** → [[runbooks/intent]] · requirements → [[runbooks/requirements]] · plan → [[runbooks/plan]] · comment → [[runbooks/comment]] · tag → [[runbooks/tag]] · attachment → [[runbooks/attachment]] · release → [[runbooks/release]] · brainstorm → [[runbooks/brainstorm]] · agent → [[runbooks/agent-operator]] · repo → [[runbooks/repo-commit]] · project → [[runbooks/project]] · skill → [[runbooks/skill]] · entity-слой → [[runbooks/entity]] · sched → [[runbooks/sched-telemetry]]
|
||||
**Methodology:** `methodology/kzntsv`
|
||||
**Canon version:** 4
|
||||
<!-- /mappa:canon-block -->
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ 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 |
|
||||
| all other `skills/*` | `author: ours` |
|
||||
|
||||
@@ -91,6 +91,8 @@ 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` |
|
||||
|
||||
Политика адаптации: клон переписывается под наши конвенции (доски `.tasks/`,
|
||||
|
||||
BIN
dist/browser-cdp.skill
vendored
BIN
dist/browser-cdp.skill
vendored
Binary file not shown.
BIN
dist/browser-operator.skill
vendored
Normal file
BIN
dist/browser-operator.skill
vendored
Normal file
Binary file not shown.
BIN
dist/ops-browser.skill
vendored
Normal file
BIN
dist/ops-browser.skill
vendored
Normal file
Binary file not shown.
@@ -55,6 +55,14 @@ skills:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
browser-operator:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
ops-browser:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
using-markitdown:
|
||||
mode: auto
|
||||
category: productivity
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
name: browser-cdp
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
version: 0.1.1
|
||||
description: >
|
||||
Веб-автоматизация через минимальные CDP CLI-тулы в bash — вместо playwright-mcp
|
||||
/ Chrome-DevTools-MCP (подход «what if you don't need MCP»). Запуск Chrome с remote
|
||||
debugging, навигация, eval JS, скриншоты. Trigger: «браузер», «скрейпинг», «открой
|
||||
страницу», «перейди на», «сделай скриншот», «playwright», «веб-автоматизация»,
|
||||
«web scraping», «browser».
|
||||
«web scraping», «browser». Для ЛИЧНЫХ КАБИНЕТОВ оператора (его логины, антибот) —
|
||||
НЕ этот скил, а `browser-operator`.
|
||||
---
|
||||
|
||||
# browser-cdp
|
||||
@@ -20,6 +21,9 @@ description: >
|
||||
снять скриншот, собрать данные (скрейпинг). Использовать **вместо** playwright-mcp или
|
||||
Chrome-DevTools-MCP.
|
||||
|
||||
- ⚠️ **Для личных кабинетов оператора этот путь НЕ годится:** здесь свой Chrome и свой
|
||||
профиль (без его логинов). Нужен браузер оператора — скил `browser-operator`.
|
||||
|
||||
## Процесс
|
||||
|
||||
1. **Прочитай полную справку** (обязательно, первый шаг):
|
||||
|
||||
128
skills/browser-operator/SKILL.md
Normal file
128
skills/browser-operator/SKILL.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
name: browser-operator
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
description: >
|
||||
Работа в браузере ОПЕРАТОРА — его Chrome, его профиль, его живые логины: личные
|
||||
кабинеты поставщиков (ЧипДип, ДКО, Промэлектроника, ТМ), Avito, порталы под
|
||||
антиботом. Trigger: «зайди в личный кабинет», «открой ЛК», «собери заказы»,
|
||||
«посмотри в браузере», «нужна его сессия», «ЧипДип/Ozon/Avito», «browser».
|
||||
НЕ для публичных страниц (там сначала обычный fetch/curl) и НЕ для анонимных
|
||||
прогонов (там скил browser-cdp).
|
||||
---
|
||||
|
||||
# Browser operator — браузер оператора
|
||||
|
||||
**Браузер оператора — разделяемый ресурс с его живыми сессиями.** Работай через
|
||||
готовый канал своего харнесса и **только под арендой «один водитель за раз»**.
|
||||
Не вытаскивай его куки, не логинься сам, не поднимай свой Chrome.
|
||||
|
||||
## Когда браузер действительно нужен
|
||||
|
||||
Сначала спроси себя, нужен ли браузер вообще:
|
||||
|
||||
- **Нет** — публичная страница, API, доки: обычный fetch/curl/поиск. Браузер тут лишний.
|
||||
- **Да** — страница требует взаимодействия (клик/форма/навигация), JS-рендер,
|
||||
**его логин** (личный кабинет, Avito), либо портал режет не-браузерный трафик.
|
||||
|
||||
## Канал по харнессу (не изобретай свой)
|
||||
|
||||
| Харнесс | Чем работать | Аренда |
|
||||
|---|---|---|
|
||||
| **Hermes** | тул `browser_exec` (демон `browser-use`, тот же профиль оператора) | автоматически: shell-хук `pre_tool_call` берёт аренду и блокирует вызов при чужой |
|
||||
| **pi** | тул `browser`: `open` / `read` / `js` / `click` / `screenshot` / `lease` | автоматически внутри тула; `lease` покажет, кто держит |
|
||||
| **Claude Code** | MCP `chrome-devtools` (`list_pages`, `navigate_page`, `take_snapshot`, `evaluate_script`, `click`, `fill`, `take_screenshot`) | автоматически: обёртка берёт аренду перед стартом сервера |
|
||||
|
||||
**Никогда:** `curl` с куками из его профиля, `browser_cookie3`-выгрузка куки,
|
||||
свой `chrome --remote-debugging-port` с пустым профилем (там нет его логинов),
|
||||
`pip install`-стек ради одного кабинета.
|
||||
|
||||
## Аренда: «один водитель в браузере за раз»
|
||||
|
||||
Браузер один. Если его держит другой харнесс — **не ломиться**, сказать «занято,
|
||||
держит X» и вернуться позже (или попросить оператора освободить).
|
||||
|
||||
Идёшь в браузер **в обход** тула (например, скриптом или `browser-use` из терминала) —
|
||||
бери аренду сам:
|
||||
|
||||
```bash
|
||||
BL="$HOME/.config/browser-harness/bin/browser-lease.sh"
|
||||
HOLD="manual:$$" # метка держателя
|
||||
MYPID="$(cat /proc/$$/winpid 2>/dev/null || echo 0)" # НАСТОЯЩИЙ windows-PID
|
||||
bash "$BL" acquire --holder "$HOLD" --ttl 900 --pid "$MYPID" --tool browser # 0 = взял, 3 = занято, 2 = ошибка
|
||||
# … работа …
|
||||
bash "$BL" release --holder "$HOLD"
|
||||
```
|
||||
|
||||
**PID — только настоящий.** MSYS `$$` это НЕ windows-PID: по нему живость аренды врёт
|
||||
(мёртвый держатель выглядит живым, живой — мёртвым). В bash бери `/proc/$$/winpid`,
|
||||
в pi/Node — `process.pid`; не знаешь — передай `0` («неизвестен», живость решит TTL).
|
||||
|
||||
**`driver.lock` руками не трогай** (в том числе пустой или «битый» — это окно чужой
|
||||
записи): отбор мёртвой аренды делает CLI по `ts`/TTL/мёртвому PID. Чужую аренду не снимай.
|
||||
|
||||
Контракт аренды (формат файла, TTL, кого связывать): вики mappa
|
||||
`concepts/browser-lease-contract`.
|
||||
|
||||
## Как работать в страницах (рецепты)
|
||||
|
||||
- **Первым делом — своя вкладка.** `ensure_real_tab()` / `new_tab(url)`; не полагайся
|
||||
на активную вкладку оператора: тяжёлая или аудио-страница подвешивает демон
|
||||
(все вызовы падают в таймаут, хотя `browser-use --doctor` говорит «alive»).
|
||||
- **Тяжёлые страницы — читать изнутри, а не обходом.** Если у сайта есть внутренний
|
||||
JSON-эндпоинт, зови его `fetch(path, {credentials:'include'})` из уже открытой
|
||||
страницы: это быстрее и надёжнее десятков навигаций.
|
||||
- **По одной штуке за раз, с паузами.** Паузы — в Python/процессе, **не** в JS `await`
|
||||
(иначе `Runtime.evaluate timed out`). Ориентир оператора: 1 запрос, пауза 7–13 с,
|
||||
перекур каждые ~40, стоп после 3 ошибок подряд.
|
||||
- **Клики:** сначала дерево доступности (`Accessibility.getFullAXTree`) или
|
||||
`querySelector` → центр элемента (`getBoundingClientRect`) → `click_at_xy` → **проверь
|
||||
результат** отдельным `js(...)`/`page_info()`. Не кликай «на глаз» по скриншоту.
|
||||
- **Прогресс — сразу на диск** (jsonl/atomic), а не в конце прогона: длинные обходы
|
||||
обрываются.
|
||||
- **Долгие обходы** — в фоновый процесс, а не в цикл интерактивных вызовов.
|
||||
|
||||
## Границы: где человек, а где агент
|
||||
|
||||
- **Пароли и второй фактор — никогда.** Не вводим и не просим в аргументах командной
|
||||
строки. Уже залогиненная сессия оператора — вот твой доступ.
|
||||
- **Попап «Разрешить удалённую отладку?»** — это человеческое подтверждение: агент его
|
||||
не жмёт, а просит оператора нажать «Разрешить» и повторяет попытку.
|
||||
- **Деньги/платежи/отправка форм с персональными данными** — только с явным
|
||||
подтверждением оператора.
|
||||
- **Секреты и содержимое залогиненных страниц** не пишем в логи, файлы репозитория и
|
||||
вики: в mappa — только агрегаты и идентификаторы.
|
||||
|
||||
## Проверенные адреса и факты
|
||||
|
||||
- **ЧипДип, кабинет заказов:** `https://www.chipdip.ru/order/list`
|
||||
(⚠️ `/cabinet` и `/orders` отдают 404 — не перебирай наугад, ссылка есть в шапке).
|
||||
- Залогиненность видна в шапке кабинета (имя оператора); если видишь форму логина —
|
||||
**сессия потеряна: стоп и скажи оператору**, не логинься сам.
|
||||
- `about:blank`-вкладка с титулом-лошадкой в `document.title` = страницу ведёт агент.
|
||||
|
||||
## Так делать НЕ надо
|
||||
|
||||
| Соблазн | Почему нет |
|
||||
|---|---|
|
||||
| «Вытащу куки из Chrome и пойду `curl`'ом» | мимо канала и мимо аренды; пароль/2FA оказываются рядом; ломается при смене защиты |
|
||||
| «Подниму свой Chrome с отладкой» | там **нет** его логинов → выкинет на логин/капчу, плюс это второй водитель |
|
||||
| «Введу логин/пароль через `read -s`» | пароли и 2FA не вводим никогда |
|
||||
| «Проверю ещё пяток URL кабинета» | адрес подтверждай по ссылке в интерфейсе, а не перебором |
|
||||
| «Налью 20 запросов параллельно, быстрее» | антибот + оператор останавливает такие прогоны; один водитель, одна очередь |
|
||||
|
||||
Основание: базовый прогон без скила (2026-09-11) ушёл именно в «куки + curl + пароль»
|
||||
мимо канала; скил закрывает этот путь.
|
||||
|
||||
## Красные флаги (стоп и перечитай)
|
||||
|
||||
- Собираешься вытащить куки / поднять свой браузер / ввести пароль или код.
|
||||
- Работаешь с браузером **без** аренды (или ломишься, когда «занято»).
|
||||
- Полливаешь запросами без паузы или ждёшь паузу в JS.
|
||||
- Пишешь содержимое залогиненных страниц/секреты в файл, лог или вики.
|
||||
|
||||
## Вне скоупа
|
||||
|
||||
- Анонимные/антидетект-прогоны, свой профиль, `mode: fresh` — скил `browser-cdp`.
|
||||
- Облачные браузеры (Browser Use Cloud и прочие) — не берём.
|
||||
- Свой stdio-MCP-сервер поверх общего демона — отдельная тема.
|
||||
90
skills/ops-browser/SKILL.md
Normal file
90
skills/ops-browser/SKILL.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: ops-browser
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
description: >
|
||||
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 <url> # человеку: пароль/2FA/капча (см. ниже)
|
||||
```
|
||||
|
||||
`eval` выполняет JS **в контексте страницы** — значит `fetch()` идёт с её куками и заголовками:
|
||||
так дёргают внутренние эндпоинты приложения (то, что недоступно извне и чего не умеет панель).
|
||||
|
||||
## Хендофф человеку (пароль, 2FA, капча, согласие)
|
||||
|
||||
Останавливаюсь и отдаю экран **сам**, без «скажи готово»:
|
||||
|
||||
1. `bash "$OB" handoff <url>` — закрывает 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`).
|
||||
Reference in New Issue
Block a user