|
|
||
|---|---|---|
| .forgejo/workflows | ||
| cdp | ||
| cli | ||
| core | ||
| docs | ||
| exec | ||
| fixtures | ||
| profiles | ||
| scenarios | ||
| schemas | ||
| scripts | ||
| stand | ||
| testing | ||
| trace | ||
| .gitignore | ||
| AGENTS.md | ||
| bun.lock | ||
| CLAUDE.md | ||
| cli.ts | ||
| CONTEXT.md | ||
| docker-compose.yml | ||
| package.json | ||
| profiles.example.json | ||
| README.md | ||
| tsconfig.json | ||
| worker.ts | ||
Mimic
Движок автоматизации браузера. Сценарий — обычный JSON из готовых блоков, рантайм их исполняет. Браузеры Mimic не запускает: он подключается к уже работающим по CDP (Chrome DevTools Protocol), поэтому работает с любым Chromium и с антидетект-браузером, где у каждого профиля свой отпечаток.
Ключевой принцип: сценарий это данные, а не код. Ни eval, ни генерации TypeScript —
модель отдаёт JSON, он проходит проверку схемой до запуска.
Документы
| Что | Где |
|---|---|
| Что система делает и для кого | docs/prd.md |
| Как устроена | docs/design.md |
| Почему принято так | docs/decisions/ |
| Словарь терминов | CONTEXT.md |
План работы живёт в Linear (product:mimic, Initiative Mimic), не в этом файле.
Стек
TypeScript на Bun 1.2+, TS strict, Zod для схем блоков, воркеры Bun для параллелизма.
Ноль зависимостей в слое CDP — свой тонкий клиент на нативном WebSocket
(docs/decisions/ADR-1-own-cdp-client.md). Поставка — один самодостаточный бинарь.
Запуск
bun install
# самая короткая проверка живости: печатает document.title первой страницы
bun run cli.ts probe --ws <browser-level endpoint>
# проверка формы сценария по схемам блоков — браузер не нужен вовсе
bun run cli.ts validate scenarios/login-upload.json
# схемы блоков → schemas/*.json (артефакт в git, вход компилятора сценариев)
bun run schemas:dump
# адреса профилей: по строке <id> <wsEndpoint>, ничего не поднимая
bun run cli.ts profiles up --profiles profiles.example.json
# прогон сценария на всех профилях
bun run cli.ts run scenarios/login-upload.json --profiles profiles.json --run-id run-1
# сборка бинаря: воркер обязан быть второй точкой входа
bun build --compile --outfile mimic cli.ts worker.ts
--ws ждёт browser-level endpoint — поле webSocketDebuggerUrl из
GET /json/version. document.title с него не читается: page-таргет клиент находит
сам через Target.getTargets и цепляется Target.attachToTarget {flatten: true}.
Годится любой уже запущенный Chromium, CloakBrowser не нужен:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --remote-debugging-port=9333 --user-data-dir=/tmp/mimic-probe about:blank &
bun run cli.ts probe --ws "$(curl -s http://127.0.0.1:9333/json/version | jq -r .webSocketDebuggerUrl)"
Два таймаута названы и разведены по типам ошибки, чтобы в трассе было видно, что
именно не уложилось: CDP_COMMAND_TIMEOUT_MS (одна команда CDP, умолчание 10000,
читается из окружения) и params.timeoutMs шага (умолчание 15000).
Браузеры и приёмочный стенд поднимаются отдельно и живут своей жизнью:
docker compose up -d
curl 'http://localhost:9222/json/version?fingerprint=11111' | jq -r .webSocketDebuggerUrl
Поднимать стек только из основного чекаута, не из worktree. ./fixtures в compose
превращается в абсолютный путь хоста: контейнер, поднятый из worktree, переживает его
снос, но монтирование указывает в никуда — файлы внутри /files исчезают, а uploadFile
падает ERR_FILE_NOT_FOUND на пустом месте.
--idle-timeout=0 в compose обязателен: иначе бездействующий профиль умирает в окне
между двумя прогонами.
Профили описываются файлом profiles.json (образец — profiles.example.json):
[{id, options, vars}]. options уходит провайдеру браузеров как есть — это параметры
cloakserve, ядро их не знает; vars — переменные сценария, провайдер их не видит.
Погасить профиль командой нельзя: cloakserve такой ручки не отдаёт, и прогон её всё
равно не зовёт — браузеры обязаны переживать смерть рантайма.
Стенд (stand/) в поставку не входит — сценарии гейта ходят на него, а не на живой
сайт. Изнутри контейнера браузера он доступен как http://stand:3000, с хоста —
http://127.0.0.1:3000. Фикстуры загрузки лежат в fixtures/<profileId>/ и видны
браузеру как /files/<profileId>/<filename>.
Проверка
bun test # CDP-клиент, таймауты, схемы, маскирование, round-trip трассы
bun run smoke:seeds # два сида в одном контейнере одновременно + фикстуры
bun run smoke:idle # --idle-timeout=0: сид переживает 5 минут простоя
./scripts/gate.sh # приёмка: 4 исполнения профиля + проверка изоляции
Гейт судит по двум вещам — коду выхода и summary.json. Логи и формат трассы на вердикт
не влияют. Ретраев нет.
Ему нужен поднятый стек (docker compose up -d из основного чекаута) — сам он браузеры
не поднимает и не гасит. Что делает: два вызова CLI по два профиля (gate-1, gate-2,
между ними процесс умирает целиком), сверка browser-id и StartedAt контейнера, затем
негативный прогон — профилю подменяют файл на соседский, и он обязан покраснеть.
Переменные: MIMIC_PROFILES (умолчание profiles.example.json), MIMIC_SCENARIO,
MIMIC_ARTIFACTS, MIMIC_RUN (MIMIC_RUN=./mimic — гнать собранный бинарь).
Коды выхода: 0 прошло · 1 прогон упал · 2 кривой сценарий или конфиг (браузеры не
трогали) · 130 прервали руками.