1046 lines
27 KiB
Markdown
1046 lines
27 KiB
Markdown
# Верстак — браузерное дополнение Firefox / Chrome / Yandex Browser
|
||
|
||
## 1. Назначение
|
||
|
||
Браузерное дополнение нужно потому, что большая часть работы может происходить не в локальных файлах, а в браузере:
|
||
|
||
- админки сайтов;
|
||
- CMS;
|
||
- панели клиентов;
|
||
- GitHub/Gitea/GitLab;
|
||
- webmail;
|
||
- личные кабинеты;
|
||
- документация;
|
||
- витрины сайтов;
|
||
- CRM/ERP клиентов;
|
||
- облачные панели.
|
||
|
||
Если пользователь три часа обновлял витрину сайта через web-админку, локальный Верстак может почти ничего не увидеть.
|
||
Расширение даёт Верстаку браузерные следы:
|
||
|
||
```text
|
||
Похоже, пользователь работал с admin.romashka.ru с 14:05 до 17:12.
|
||
Домен связан с делом “Клиенты / ООО Ромашка / Сайт”.
|
||
Можно предложить запись в журнал: “обновление витрины сайта, примерно 3 часа”.
|
||
```
|
||
|
||
## 2. Главный принцип
|
||
|
||
Расширение — не шпион и не самостоятельный таймтрекер.
|
||
|
||
Оно должно:
|
||
|
||
- работать локально;
|
||
- передавать события только в локальный Верстак;
|
||
- не отправлять данные в облако;
|
||
- не читать DOM страниц;
|
||
- не читать формы;
|
||
- не читать cookies;
|
||
- не перехватывать сетевые запросы;
|
||
- не делать скриншоты;
|
||
- не сохранять полную историю браузера;
|
||
- уважать allowlist/blocklist;
|
||
- иметь pause;
|
||
- показывать пользователю, что отслеживается;
|
||
- не трекать private windows по умолчанию.
|
||
|
||
## 3. Что собирать
|
||
|
||
MVP-событие активности активной вкладки:
|
||
|
||
```json
|
||
{
|
||
"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
|
||
}
|
||
```
|
||
|
||
Отправлять батчами:
|
||
|
||
```json
|
||
{
|
||
"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.
|
||
|
||
Пример:
|
||
|
||
```text
|
||
https://client.example.com/admin/orders?token=abc&session=123#x
|
||
```
|
||
|
||
Безопасный вариант:
|
||
|
||
```text
|
||
https://client.example.com/admin/orders
|
||
```
|
||
|
||
Приватный вариант:
|
||
|
||
```text
|
||
client.example.com
|
||
```
|
||
|
||
## 6. Архитектура расширения
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
http://127.0.0.1:47731/api/browser/events
|
||
```
|
||
|
||
Расширение шлёт туда события через `fetch`.
|
||
|
||
Auth:
|
||
|
||
```http
|
||
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 вызывает:
|
||
|
||
```http
|
||
POST /api/browser/pair
|
||
```
|
||
|
||
Body:
|
||
|
||
```json
|
||
{
|
||
"pairing_token": "123456",
|
||
"extension_id": "...",
|
||
"browser": "firefox|chrome|yandex",
|
||
"device_name": "Firefox on Work PC"
|
||
}
|
||
```
|
||
|
||
Response:
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"local_token": "...",
|
||
"device_id": "..."
|
||
}
|
||
```
|
||
|
||
6. Extension сохраняет `local_token` в local storage.
|
||
7. Token больше не показывается пользователю.
|
||
|
||
## 9. Local Browser API в Верстаке
|
||
|
||
```http
|
||
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
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"app": "Verstak",
|
||
"version": "0.1.0"
|
||
}
|
||
```
|
||
|
||
### Events
|
||
|
||
```http
|
||
POST /api/browser/events
|
||
Authorization: Bearer <local_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Bindings
|
||
|
||
Ответ:
|
||
|
||
```json
|
||
{
|
||
"domains": {
|
||
"admin.romashka.ru": {
|
||
"node_id": "uuid",
|
||
"title": "Клиенты / ООО Ромашка / Сайт"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 10. Permissions
|
||
|
||
Стараться держать permissions минимальными.
|
||
|
||
Возможные:
|
||
|
||
```json
|
||
{
|
||
"permissions": [
|
||
"tabs",
|
||
"storage",
|
||
"idle"
|
||
]
|
||
}
|
||
```
|
||
|
||
Host permissions:
|
||
|
||
- не начинать с агрессивного `<all_urls>`, если можно;
|
||
- по умолчанию режим `allowlist_only`;
|
||
- пользователь явно добавляет домены.
|
||
|
||
Режимы:
|
||
|
||
```text
|
||
allowlist_only
|
||
all_except_blocked
|
||
disabled
|
||
```
|
||
|
||
Default: `allowlist_only`.
|
||
|
||
## 11. Popup UI
|
||
|
||
Popup:
|
||
|
||
```text
|
||
Верстак
|
||
|
||
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 в Верстаке
|
||
|
||
Таблица:
|
||
|
||
```sql
|
||
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
|
||
);
|
||
```
|
||
|
||
Примеры:
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
Похоже, работа по “ООО Ромашка / Сайт”:
|
||
admin.romashka.ru — 2ч 48м
|
||
romashka.ru/catalog — 12м
|
||
|
||
Предлагаемое время: 3ч
|
||
|
||
Основания:
|
||
- 14:05–15:30 admin.romashka.ru/catalog
|
||
- 15:40–16:55 admin.romashka.ru/products
|
||
- 17:00–17: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
|
||
|
||
```markdown
|
||
Создай отдельный модуль `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
|
||
|
||
```markdown
|
||
Добавь 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
|
||
|
||
```markdown
|
||
Реализуй 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
|
||
|
||
```markdown
|
||
Реализуй настройки расширения.
|
||
|
||
Файл:
|
||
`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
|
||
|
||
```markdown
|
||
Реализуй сбор интервалов активности активной вкладки.
|
||
|
||
Файлы:
|
||
- `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
|
||
|
||
```markdown
|
||
Добавь отправку событий в локальный Верстак.
|
||
|
||
Файл:
|
||
`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
|
||
|
||
```markdown
|
||
Добавь 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
|
||
|
||
```markdown
|
||
Сделай 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
|
||
|
||
```markdown
|
||
Сделай 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
|
||
|
||
```markdown
|
||
Добавь 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
|
||
|
||
```markdown
|
||
В основном приложении Верстак добавь 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
|
||
|
||
```markdown
|
||
Добавь привязку доменов к делам в Верстаке.
|
||
|
||
Нужно:
|
||
- таблица 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
|
||
|
||
```markdown
|
||
Добавь построение 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
|
||
|
||
```markdown
|
||
Подготовь 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
|
||
|
||
```markdown
|
||
Подготовь 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
|
||
|
||
```markdown
|
||
Проведи 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. Верстак показывает:
|
||
|
||
```text
|
||
Похоже, работа по “ООО Ромашка / Сайт”:
|
||
admin.romashka.ru — 2ч 48м
|
||
romashka.ru/catalog — 12м
|
||
|
||
[Записать 3ч] [Изменить] [Игнорировать]
|
||
```
|