verstak/docs/verstak_browser_extension_p...

27 KiB
Raw Permalink Blame History

Верстак — браузерное дополнение Firefox / Chrome / Yandex Browser

1. Назначение

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

  • админки сайтов;
  • CMS;
  • панели клиентов;
  • GitHub/Gitea/GitLab;
  • webmail;
  • личные кабинеты;
  • документация;
  • витрины сайтов;
  • CRM/ERP клиентов;
  • облачные панели.

Если пользователь три часа обновлял витрину сайта через web-админку, локальный Верстак может почти ничего не увидеть.
Расширение даёт Верстаку браузерные следы:

Похоже, пользователь работал с admin.romashka.ru с 14:05 до 17:12.
Домен связан с делом “Клиенты / ООО Ромашка / Сайт”.
Можно предложить запись в журнал: “обновление витрины сайта, примерно 3 часа”.

2. Главный принцип

Расширение — не шпион и не самостоятельный таймтрекер.

Оно должно:

  • работать локально;
  • передавать события только в локальный Верстак;
  • не отправлять данные в облако;
  • не читать DOM страниц;
  • не читать формы;
  • не читать cookies;
  • не перехватывать сетевые запросы;
  • не делать скриншоты;
  • не сохранять полную историю браузера;
  • уважать allowlist/blocklist;
  • иметь pause;
  • показывать пользователю, что отслеживается;
  • не трекать private windows по умолчанию.

3. Что собирать

MVP-событие активности активной вкладки:

{
  "type": "active_tab_interval",
  "browser": "firefox|chrome|yandex",
  "window_id": 1,
  "tab_id": 123,
  "domain": "admin.romashka.ru",
  "origin": "https://admin.romashka.ru",
  "url": "https://admin.romashka.ru/catalog/products",
  "title": "Каталог товаров — Админка",
  "started_at": "2026-05-30T14:05:00+03:00",
  "ended_at": "2026-05-30T14:18:00+03:00",
  "duration_seconds": 780,
  "source": "browser_extension",
  "private_window": false
}

Отправлять батчами:

{
  "browser": "firefox",
  "device_id": "browser-extension-device-id",
  "events": [
    {
      "type": "active_tab_interval",
      "domain": "admin.romashka.ru",
      "origin": "https://admin.romashka.ru",
      "url": "https://admin.romashka.ru/catalog",
      "title": "Каталог товаров",
      "started_at": "2026-05-30T14:05:00+03:00",
      "ended_at": "2026-05-30T14:18:00+03:00",
      "duration_seconds": 780
    }
  ]
}

4. Что не собирать

В MVP запрещено:

  • DOM;
  • содержимое страниц;
  • значения input/textarea;
  • cookies;
  • localStorage/sessionStorage страниц;
  • POST bodies;
  • webRequest details;
  • screenshot;
  • пароли;
  • приватные окна без явного включения;
  • полную историю браузера;
  • query string с токенами.

5. Очистка URL

По умолчанию:

  • query string удалять;
  • fragment удалять;
  • full URL хранить только если пользователь включил;
  • для чувствительных доменов хранить только domain/origin.

Пример:

https://client.example.com/admin/orders?token=abc&session=123#x

Безопасный вариант:

https://client.example.com/admin/orders

Приватный вариант:

client.example.com

6. Архитектура расширения

browser-extension/
  src/
    background.js
    popup.html
    popup.js
    options.html
    options.js
    shared/
      browser-api.js
      activity.js
      storage.js
      transport.js
      url.js
  manifests/
    manifest.chrome.json
    manifest.firefox.json
  package.json
  build.js
  dist/
    chrome/
    firefox/

Yandex Browser использовать Chrome-compatible build.

7. Транспорт в Верстак

Вариант MVP: Local HTTP endpoint

Верстак поднимает локальный endpoint:

http://127.0.0.1:47731/api/browser/events

Расширение шлёт туда события через fetch.

Auth:

Authorization: Bearer <local_browser_token>

Плюсы:

  • проще отлаживать;
  • одинаково для Firefox/Chrome/Yandex;
  • не надо регистрировать native host;
  • быстрее сделать MVP.

Минусы:

  • endpoint надо защищать токеном;
  • bind только на 127.0.0.1;
  • нужен pairing flow.

Вариант позже: Native Messaging

Плюсы:

  • более официальный канал extension ↔ native app;
  • не нужен HTTP endpoint.

Минусы:

  • сложнее установка;
  • нужно регистрировать native messaging host отдельно для Chrome/Firefox;
  • тяжелее для MVP.

Решение: в MVP Local HTTP, Native Messaging оставить на будущее.

8. Pairing flow

  1. Пользователь открывает Верстак.
  2. Нажимает “Подключить браузер”.
  3. Верстак показывает одноразовый pairing token.
  4. Пользователь вводит token в extension options.
  5. Extension вызывает:
POST /api/browser/pair

Body:

{
  "pairing_token": "123456",
  "extension_id": "...",
  "browser": "firefox|chrome|yandex",
  "device_name": "Firefox on Work PC"
}

Response:

{
  "ok": true,
  "local_token": "...",
  "device_id": "..."
}
  1. Extension сохраняет local_token в local storage.
  2. Token больше не показывается пользователю.

9. Local Browser API в Верстаке

GET  /api/browser/health
POST /api/browser/pair
POST /api/browser/events
GET  /api/browser/bindings
POST /api/browser/link-domain-request
POST /api/browser/pause

Health

{
  "ok": true,
  "app": "Verstak",
  "version": "0.1.0"
}

Events

POST /api/browser/events
Authorization: Bearer <local_token>
Content-Type: application/json

Bindings

Ответ:

{
  "domains": {
    "admin.romashka.ru": {
      "node_id": "uuid",
      "title": "Клиенты / ООО Ромашка / Сайт"
    }
  }
}

10. Permissions

Стараться держать permissions минимальными.

Возможные:

{
  "permissions": [
    "tabs",
    "storage",
    "idle"
  ]
}

Host permissions:

  • не начинать с агрессивного <all_urls>, если можно;
  • по умолчанию режим allowlist_only;
  • пользователь явно добавляет домены.

Режимы:

allowlist_only
all_except_blocked
disabled

Default: allowlist_only.

11. Popup UI

Popup:

Верстак

Status: connected / not connected / paused
Current site: admin.romashka.ru

[Track this site]
[Stop tracking this site]
[Link this site to case]
[Pause 1 hour]
[Open settings]

Today:
admin.romashka.ru — 1h 20m
git.mirv.top — 35m

12. Options UI

Настройки:

  • endpoint URL;
  • pairing token;
  • test connection;
  • tracking enabled;
  • tracking mode:
    • allowlist only;
    • all except blocked;
    • disabled;
  • allowlist domains;
  • blocklist domains;
  • send full URL: yes/no;
  • strip query string: yes/no;
  • include private windows: no by default;
  • idle timeout minutes;
  • batch interval seconds;
  • max pending events;
  • debug mode;
  • clear pending events;
  • reset pairing.

13. Activity collector

Логика:

  1. Следить за active tab.
  2. При смене active tab закрывать текущий interval.
  3. При смене URL активной вкладки закрывать текущий interval и открывать новый.
  4. При смене active window закрывать/переключать interval.
  5. Если tab URL не trackable — не создавать interval.
  6. Если domain не в allowlist при режиме allowlist_only — не создавать interval.
  7. Если domain в blocklist — не создавать interval.
  8. Если пользователь idle — закрыть interval.
  9. Раз в N секунд отправить pending events в Верстак.
  10. Если Верстак недоступен — хранить pending events локально.

14. Idle detection

Если пользователь idle дольше заданного времени:

  • закрыть текущий interval;
  • не считать idle time как работу;
  • при возвращении открыть новый interval для текущей вкладки.

15. Domain bindings в Верстаке

Таблица:

CREATE TABLE domain_bindings (
    id TEXT PRIMARY KEY,
    node_id TEXT NOT NULL REFERENCES nodes(id),
    domain TEXT NOT NULL,
    match_type TEXT NOT NULL DEFAULT 'exact', -- exact | suffix
    created_at TEXT NOT NULL
);

Примеры:

admin.romashka.ru → Клиенты / ООО Ромашка / Сайт
*.romashka.ru → Клиенты / ООО Ромашка / Сайт
git.mirv.top/mirivlad/sshkeeper → Личные проекты / sshkeeper

MVP:

  • exact domain;
  • suffix match позже.

16. Worklog suggestions

Верстак группирует browser events:

  • по node_id;
  • по domain;
  • по дню;
  • intervals with gap <= 20 minutes объединяются.

Suggestion:

Похоже, работа по “ООО Ромашка / Сайт”:
admin.romashka.ru — 2ч 48м
romashka.ru/catalog — 12м

Предлагаемое время: 3ч

Основания:
- 14:0515:30 admin.romashka.ru/catalog
- 15:4016:55 admin.romashka.ru/products
- 17:0017:12 romashka.ru/catalog

[Записать 3ч] [Изменить] [Игнорировать]

17. Safety requirements

  1. Не использовать content scripts в MVP, если не нужны.
  2. Не читать DOM.
  3. Не читать cookies.
  4. Не читать forms.
  5. Не использовать webRequest.
  6. Не использовать history.
  7. Query strip по умолчанию.
  8. Private windows ignore по умолчанию.
  9. Pause в popup.
  10. Allowlist mode по умолчанию.
  11. Blocklist есть всегда.
  12. Pending events ограничены.
  13. Endpoint только 127.0.0.1.
  14. Pairing token одноразовый.
  15. Local token не логировать.
  16. No external network requests.

Промпты для ИИ-кодера

Prompt 01 — Browser extension skeleton

Создай отдельный модуль `browser-extension` для проекта Верстак.

Цель:
браузерное расширение для Firefox / Chrome / Yandex Browser, которое собирает локальную активность активной вкладки и отправляет её в локальный Верстак.

Важно:
- не собирать DOM;
- не читать формы;
- не читать cookies;
- не делать screenshots;
- не использовать webRequest/history;
- не отправлять данные в интернет;
- только local endpoint `http://127.0.0.1:47731`;
- query string удалять по умолчанию;
- private windows игнорировать по умолчанию.

Структура:
- `browser-extension/src/background.js`
- `browser-extension/src/shared/browser-api.js`
- `browser-extension/src/shared/url.js`
- `browser-extension/src/shared/storage.js`
- `browser-extension/src/shared/transport.js`
- `browser-extension/src/popup.html`
- `browser-extension/src/popup.js`
- `browser-extension/src/options.html`
- `browser-extension/src/options.js`
- `browser-extension/manifests/manifest.chrome.json`
- `browser-extension/manifests/manifest.firefox.json`
- `browser-extension/package.json`
- `browser-extension/build.js`

Сделай manifests для Chrome MV3 и Firefox WebExtension.
Для Yandex Browser использовать Chrome build.

MVP skeleton должен:
- загружаться как unpacked extension в Chrome/Chromium;
- загружаться временно в Firefox через about:debugging;
- показывать popup;
- иметь options page;
- хранить настройки в browser/chrome storage local;
- иметь build script для `dist/chrome` и `dist/firefox`.

Не реализуй пока отправку активности, только skeleton и настройки.
После выполнения дай инструкции запуска.

Prompt 02 — Cross-browser API layer

Добавь compatibility layer для Chrome/Firefox.

Файл:
`src/shared/browser-api.js`

Нужно:
- использовать `globalThis.browser`, если есть;
- fallback на `globalThis.chrome`;
- promisify callback APIs, где нужно;
- методы:
  - getActiveTab()
  - getCurrentWindow()
  - onTabActivated(handler)
  - onTabUpdated(handler)
  - onWindowFocusChanged(handler)
  - storageGet(keys)
  - storageSet(values)
  - idleQueryState(minutes)
  - onIdleStateChanged(handler)

Правила:
- popup/options/background не должны напрямую использовать chrome/browser;
- весь доступ к browser APIs через browserApi;
- не добавлять тяжёлые зависимости без необходимости.

Acceptance criteria:
- работает в Chrome;
- работает в Firefox;
- storage работает;
- active tab можно получить.

Prompt 03 — URL sanitizer

Реализуй URL sanitizer.

Файл:
`src/shared/url.js`

Функции:
- parseUrl(rawUrl)
- sanitizeUrl(rawUrl, options)
- getDomain(rawUrl)
- getOrigin(rawUrl)
- isTrackableUrl(rawUrl)
- isBlockedUrl(rawUrl, settings)
- isAllowedUrl(rawUrl, settings)

Правила:
- не трекать chrome://, about:, moz-extension:, chrome-extension:, edge:, opera:, file:// по умолчанию;
- query string удалять по умолчанию;
- hash удалять по умолчанию;
- full URL хранить только если setting `sendFullUrl=true`;
- режимы:
  - allowlist_only;
  - all_except_blocked;
  - disabled.

Добавь простые unit tests.
Покажи примеры sanitize для URL с token/session/query.

Prompt 04 — Settings storage

Реализуй настройки расширения.

Файл:
`src/shared/storage.js`

Настройки:
- endpointUrl default `http://127.0.0.1:47731`;
- localToken;
- deviceId;
- pairedAt;
- trackingEnabled;
- trackingMode: allowlist_only | all_except_blocked | disabled;
- allowlistDomains;
- blocklistDomains;
- sendFullUrl default false;
- stripQueryString default true;
- includePrivateWindows default false;
- idleTimeoutMinutes default 5;
- batchIntervalSeconds default 30;
- maxPendingEvents default 1000;
- debugMode default false;
- pendingEvents array.

Нужно:
- getSettings()
- saveSettings(partial)
- addPendingEvent(event)
- getPendingEvents()
- removePendingEvents(ids)
- clearPendingEvents()

Acceptance criteria:
- options page может менять настройки;
- pending events переживают перезапуск браузера;
- maxPendingEvents соблюдается.

Prompt 05 — Active tab interval collector

Реализуй сбор интервалов активности активной вкладки.

Файлы:
- `src/background.js`
- `src/shared/activity.js`

Логика:
- при старте background получить active tab;
- при tabs.onActivated закрыть старый interval и открыть новый;
- при tabs.onUpdated для активной вкладки, если URL изменился, закрыть старый interval и открыть новый;
- при windows.onFocusChanged закрывать/переключать interval;
- если URL не trackable — interval не создавать;
- если домен не проходит allowlist/blocklist — interval не создавать;
- interval хранить:
  - id
  - domain
  - origin
  - sanitized URL
  - title
  - started_at
  - ended_at
  - duration_seconds
  - private_window false/true if available

Пока не отправляй на сервер, складывай pending events в storage.

Popup должен в debug mode показывать последние 10 pending events.

Prompt 06 — Local HTTP transport

Добавь отправку событий в локальный Верстак.

Файл:
`src/shared/transport.js`

Endpoint:
`POST http://127.0.0.1:47731/api/browser/events`

Header:
`Authorization: Bearer <localToken>`

Логика:
- раз в batchIntervalSeconds отправлять pending events;
- если нет localToken — не отправлять;
- если endpoint недоступен — оставить pending;
- если 401 — status auth_failed;
- если success — удалить отправленные events;
- body:
  {
    browser,
    device_id,
    events
  }

Popup должен показывать:
- connected / disconnected / auth failed;
- pending count;
- last successful send.

Acceptance criteria:
- с mock server events отправляются;
- при server down events остаются pending;
- query string не отправляется.

Prompt 07 — Pairing flow

Добавь pairing flow.

Options page:
- endpoint URL;
- pairing token;
- button “Pair with Verstak”;
- button “Test connection”;
- status area.

API:
GET /api/browser/health
POST /api/browser/pair

Pair request:
{
  "pairing_token": "...",
  "extension_id": "...",
  "browser": "chrome|firefox|yandex",
  "device_name": "..."
}

Pair response:
{
  "ok": true,
  "local_token": "...",
  "device_id": "..."
}

После pairing:
- сохранить localToken;
- сохранить deviceId;
- сохранить pairedAt;
- очистить pairingToken из UI;
- popup показывает connected.

Не логировать токены.

Prompt 08 — Popup UI

Сделай popup UI.

Показывать:
- статус подключения;
- текущий сайт;
- текущий режим tracking;
- pending events count;
- today summary по доменам.

Кнопки:
- Track this site;
- Stop tracking this site;
- Link this site to case;
- Pause 1 hour;
- Resume;
- Open settings.

Track this site:
- добавляет текущий domain в allowlist.

Stop tracking this site:
- добавляет domain в blocklist или удаляет из allowlist.

Link this site to case:
- отправляет в Верстак:
  POST /api/browser/link-domain-request
  {
    "domain": "...",
    "url": "...",
    "title": "..."
  }

Если Верстак недоступен, показать понятную ошибку.

Prompt 09 — Options UI

Сделай options page.

Настройки:
- endpoint URL;
- pairing/test connection;
- tracking enabled;
- tracking mode:
  - allowlist_only
  - all_except_blocked
  - disabled
- allowlist domains;
- blocklist domains;
- send full URL yes/no;
- strip query string yes/no;
- include private windows yes/no, default false;
- idle timeout minutes;
- batch interval seconds;
- max pending events;
- debug mode.

Функции:
- validate domain;
- import/export settings JSON;
- clear pending events;
- reset pairing;
- show current extension version.

Сделай простой чистый UI без framework.

Prompt 10 — Idle detection

Добавь idle detection.

Использовать browser/chrome idle API через compatibility layer.

Логика:
- если пользователь idle больше idleTimeoutMinutes, закрыть текущий interval;
- пока idle, новые intervals не создавать;
- при возвращении active открыть interval для текущей вкладки;
- idle time не должен попадать в duration.

Popup debug показывает current idle state.

Acceptance criteria:
- idle event закрывает interval;
- duration не растёт во время idle.

Prompt 11 — Verstak desktop receiver

В основном приложении Верстак добавь local browser activity receiver.

Нужно:
- локальный HTTP server на 127.0.0.1:47731;
- endpoints:
  - GET /api/browser/health
  - POST /api/browser/pair
  - POST /api/browser/events
  - POST /api/browser/link-domain-request
- local pairing tokens;
- local browser tokens;
- таблица browser_devices;
- сохранение browser events в activity_events.

Безопасность:
- bind только на 127.0.0.1;
- Bearer token required после pairing;
- pairing token одноразовый;
- не логировать token;
- ограничить body size;
- reject requests from non-loopback interface.

Acceptance criteria:
- extension может pair;
- extension может отправить events;
- events появляются в activity_events;
- invalid token rejected.

Prompt 12 — Domain bindings in Verstak

Добавь привязку доменов к делам в Верстаке.

Нужно:
- таблица domain_bindings:
  - id
  - node_id
  - domain
  - match_type exact|suffix
  - created_at
- UI в карточке дела:
  - список связанных доменов;
  - добавить домен;
  - удалить домен.
- обработка link-domain-request:
  - показать запрос в Неразобранном;
  - пользователь выбирает дело;
  - создаётся binding.

Matching:
- exact first;
- suffix for *.example.com later;
- unmatched browser events попадают в unresolved activity.

Acceptance criteria:
- admin.romashka.ru привязан к делу;
- события этого домена видны в активности дела;
- неизвестные домены попадают в Неразобранное.

Prompt 13 — Browser activity worklog suggestions

Добавь построение worklog suggestions из browser activity.

Логика:
- брать browser activity events за день;
- группировать по node_id/domain;
- объединять интервалы с gap <= 20 минут;
- суммировать duration;
- создавать suggestion:
  - node_id
  - date
  - started_at
  - ended_at
  - suggested_minutes
  - evidence_json
  - summary template.

Summary template:
“Работа в браузере по связанному сайту: {domains}.”

UI:
- Today показывает suggestions;
- Case Activity tab показывает suggestions;
- кнопки:
  - Записать;
  - Изменить;
  - Игнорировать.

Acceptance criteria:
- 3 часа активности admin.romashka.ru создают suggestion;
- пользователь может записать suggestion в worklog;
- evidence показывает домены, titles, intervals.

Prompt 14 — Firefox packaging

Подготовь Firefox build.

Нужно:
- npm run build:firefox;
- использовать manifests/manifest.firefox.json;
- output dist/firefox;
- инструкция:
  1. открыть about:debugging;
  2. This Firefox;
  3. Load Temporary Add-on;
  4. выбрать manifest.json.

Проверить:
- popup работает;
- options page работает;
- active tab tracking работает;
- local HTTP events отправляются.

Не публиковать в AMO на этом этапе.

Prompt 15 — Chrome/Yandex packaging

Подготовь Chrome/Yandex build.

Нужно:
- npm run build:chrome;
- output dist/chrome;
- инструкция для Chrome:
  1. открыть chrome://extensions;
  2. включить Developer mode;
  3. Load unpacked;
  4. выбрать dist/chrome.
- инструкция для Yandex Browser:
  - использовать Chrome-compatible build;
  - проверить установку как unpacked, если доступно в dev mode;
  - иначе оставить ручную инструкцию для совместимой установки.

Проверить:
- popup работает;
- options page работает;
- active tab tracking работает;
- local HTTP events отправляются.

Не публиковать в Chrome Web Store на этом этапе.

Prompt 16 — Privacy/security audit

Проведи privacy/security audit расширения.

Проверь:
- нет сбора DOM;
- нет content scripts без необходимости;
- нет cookies permission;
- нет webRequest permission;
- нет history permission;
- query string strip по умолчанию;
- private windows ignore по умолчанию;
- pause работает;
- allowlist mode работает;
- blocklist работает;
- local token не логируется;
- pending events ограничены;
- endpoint только 127.0.0.1;
- pairing token одноразовый;
- нет external network requests;
- errors не содержат секретов.

Выдай список найденных проблем и исправь критичные.

MVP scope

Входит

  • Chrome-compatible build;
  • Firefox build;
  • Yandex via Chrome-compatible build;
  • popup;
  • options;
  • pairing;
  • active tab intervals;
  • allowlist/blocklist;
  • query stripping;
  • local HTTP transport;
  • pending queue;
  • idle detection;
  • domain linking request.

Не входит

  • content scripts;
  • DOM reading;
  • form tracking;
  • network request tracking;
  • screenshots;
  • cloud sync from extension;
  • AI summaries;
  • password detection;
  • automatic client detection by page content;
  • browser history import.

Первый полезный сценарий

  1. Пользователь создаёт дело “ООО Ромашка / Сайт”.
  2. Добавляет домен admin.romashka.ru.
  3. Устанавливает extension.
  4. Pair extension with Verstak.
  5. Работает в админке сайта.
  6. Extension отправляет intervals.
  7. Верстак показывает:
Похоже, работа по “ООО Ромашка / Сайт”:
admin.romashka.ru — 2ч 48м
romashka.ru/catalog — 12м

[Записать 3ч] [Изменить] [Игнорировать]