Snapshot of /home/parf/Downloads/manifest.zip as delivered, before any
GSD-driven remediation. Contains a partially-broken first attempt at the
Russian SPEC "Тз расширение фаза1.md" (Phase 1 of operator-session-recorder).
Source layout:
- manifest.json — MV3 declaration with tabCapture/activeTab/downloads/etc.
- src/background/index.ts — service worker (video buffer + archive packaging)
- src/content/index.ts — rrweb + user-event logger
- src/popup/{index.html,index.ts,style.css} — Russian popup UI
- offscreen/{index.html,index.ts} — orphaned offscreen (see audit)
- vite.config.ts — inline plugin emitting a separate live offscreen.js
- generate-icons.js, icons/ — minimal PNG icons
- "Тз расширение фаза1.md" — authoritative Russian SPEC
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
200 lines
9.0 KiB
Markdown
200 lines
9.0 KiB
Markdown
# Техническое задание: Браузерное расширение для записи сессий операторов
|
||
## Фаза 1 — Локальная запись + экспорт архива
|
||
|
||
---
|
||
|
||
## 1. Контекст и цель
|
||
|
||
Операторы работают в браузере и периодически совершают ошибки, причины которых сложно диагностировать.
|
||
Цель Фазы 1 — незаметно для оператора вести непрерывную запись его сессии,
|
||
а по нажатию кнопки — сохранять архив с данными сессии в папку «Загрузки» для передачи в поддержку.
|
||
|
||
---
|
||
|
||
## 2. Стек
|
||
|
||
| Компонент | Технология |
|
||
|---|---|
|
||
| Тип расширения | Chrome Extension, Manifest V3 |
|
||
| Service Worker | Background script (Manifest V3) |
|
||
| Захват экрана | `chrome.tabCapture` API |
|
||
| Захват DOM | `rrweb` (npm: rrweb) |
|
||
| Лог событий | Content Script |
|
||
| Упаковка архива | `JSZip` (npm: jszip) |
|
||
| Сохранение файла | `chrome.downloads` API |
|
||
| Хранение буфера | In-memory (Service Worker + Content Script) |
|
||
|
||
---
|
||
|
||
## 3. Структура расширения
|
||
|
||
```
|
||
extension/
|
||
├── manifest.json
|
||
├── background/
|
||
│ └── service-worker.js # координатор, буфер видео, упаковка архива
|
||
├── content/
|
||
│ ├── recorder.js # rrweb + лог событий
|
||
│ └── inject.js # инжектируется на каждую страницу
|
||
├── popup/
|
||
│ ├── popup.html # кнопка «Сохранить архив»
|
||
│ └── popup.js
|
||
└── libs/
|
||
├── rrweb.min.js
|
||
└── jszip.min.js
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Что записывается
|
||
|
||
### 4.1 Кольцевой видеобуфер (последние 30 секунд)
|
||
|
||
- Захват активной вкладки через `chrome.tabCapture.capture()`
|
||
- Кодек: `video/webm; codecs=vp9`
|
||
- Битрейт: `400 000 bps` (~3 МБ/мин)
|
||
- `MediaRecorder` пишет чанки каждые `2000 мс`
|
||
- Буфер хранится в памяти Service Worker как массив `{ data: Blob, timestamp: number }`
|
||
- Чанки старше 30 секунд удаляются автоматически после каждого нового чанка
|
||
- Первый чанк (WebM-заголовок) **никогда не удаляется** — он нужен для корректного воспроизведения файла
|
||
**Ожидаемый размер в памяти:** ~1.5–2 МБ постоянно
|
||
|
||
### 4.2 DOM-снимки через rrweb (последние 10 минут)
|
||
|
||
- `rrweb.record()` пишет события в массив
|
||
- Хранится в Content Script, передаётся в Service Worker по запросу
|
||
- События старше 10 минут удаляются по таймеру каждые 60 секунд
|
||
- Маскирование чувствительных полей: все `input[type=password]` и поля с `data-sensitive="true"` маскируются через `rrweb` опцию `maskInputSelector`
|
||
**Ожидаемый размер в памяти:** ~1–3 МБ
|
||
|
||
### 4.3 Лог действий пользователя (последние 10 минут)
|
||
|
||
Каждая запись лога:
|
||
```json
|
||
{
|
||
"timestamp": 1716800000000,
|
||
"type": "click | input | navigation | error | network",
|
||
"target": "CSS-селектор элемента",
|
||
"value": "текст/значение (маскируется для паролей)",
|
||
"url": "текущий URL",
|
||
"meta": {}
|
||
}
|
||
```
|
||
|
||
Перехватываемые события:
|
||
- `click` — клик по любому элементу (фиксируется `target`, текст элемента)
|
||
- `input` — изменение значения поля (без паролей)
|
||
- `navigation` — `popstate`, `hashchange`, переходы по страницам
|
||
- `js_error` — `window.onerror`, `window.onunhandledrejection`
|
||
- `network_error` — `fetch` / `XMLHttpRequest` с кодом ответа `>= 400`
|
||
**Ожидаемый размер в памяти:** ~100–300 КБ
|
||
|
||
### 4.4 Скриншот текущего состояния
|
||
|
||
- Делается в момент нажатия кнопки «Сохранить архив»
|
||
- Через `chrome.tabs.captureVisibleTab()`
|
||
- Формат: PNG, сохраняется как `screenshot.png` в архив
|
||
---
|
||
|
||
## 5. Кнопка «Сохранить архив» (Popup)
|
||
|
||
### UI
|
||
- Минималистичный popup: одна кнопка **«Сохранить отчёт об ошибке»**
|
||
- Под кнопкой: серый текст «Последние 30 сек видео + 10 мин лога»
|
||
- Состояния кнопки: `idle` → `Сохраняю...` → `Готово! ✓` → `idle` (через 3 сек)
|
||
### Поведение при нажатии
|
||
1. Сделать скриншот активной вкладки
|
||
2. Запросить у Service Worker: видеобуфер + лог событий
|
||
3. Запросить у Content Script: rrweb-снимки
|
||
4. Собрать архив (см. раздел 6)
|
||
5. Скачать архив через `chrome.downloads.download()`
|
||
6. Показать статус «Готово! ✓»
|
||
---
|
||
|
||
## 6. Структура архива
|
||
|
||
Имя файла: `session_report_YYYY-MM-DD_HH-MM-SS.zip`
|
||
|
||
```
|
||
session_report_2025-05-15_14-32-10.zip
|
||
├── video/
|
||
│ └── last_30sec.webm # склеенные чанки видеобуфера
|
||
├── rrweb/
|
||
│ └── session.json # массив DOM-событий rrweb
|
||
├── logs/
|
||
│ └── events.json # лог действий пользователя
|
||
├── screenshot.png # скриншот в момент сохранения
|
||
└── meta.json # метаданные сессии
|
||
```
|
||
|
||
Содержимое `meta.json`:
|
||
```json
|
||
{
|
||
"timestamp": "2025-05-15T14:32:10Z",
|
||
"url": "https://...",
|
||
"userAgent": "Chrome/...",
|
||
"extensionVersion": "1.0.0",
|
||
"videoBufferSeconds": 30,
|
||
"logDurationMinutes": 10,
|
||
"totalEvents": 143
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Разрешения в manifest.json
|
||
|
||
```json
|
||
{
|
||
"permissions": [
|
||
"tabCapture",
|
||
"activeTab",
|
||
"downloads",
|
||
"scripting",
|
||
"storage"
|
||
],
|
||
"host_permissions": [
|
||
"<all_urls>"
|
||
]
|
||
}
|
||
```
|
||
|
||
> **Примечание:** `tabCapture` требует жеста пользователя при первом запуске.
|
||
> При первой установке расширение запрашивает разрешение явно.
|
||
|
||
---
|
||
|
||
## 8. Важные ограничения и решения
|
||
|
||
| Проблема | Решение |
|
||
|---|---|
|
||
| WebM без заголовка не воспроизводится | Первый чанк (header) хранится всегда, не удаляется из буфера |
|
||
| Service Worker выгружается через 30 сек простоя | Keepalive через `chrome.alarms` каждые 20 сек |
|
||
| `tabCapture` только для активной вкладки | Фиксируется при активации расширения; при смене вкладки — переподключение |
|
||
| Большой объём rrweb-данных | Лимит буфера: 5 000 событий; при превышении удаляются самые старые |
|
||
| Пароли и чувствительные данные | `maskInputSelector` в rrweb + фильтр в логгере событий |
|
||
|
||
---
|
||
|
||
## 9. Что НЕ входит в Фазу 1
|
||
|
||
- Отправка данных на сервер
|
||
- AI-диагностика
|
||
- Автоматическое создание тикетов
|
||
- Аналитический дашборд
|
||
- Запись звука
|
||
Эти функции — Фаза 2.
|
||
|
||
---
|
||
|
||
## 10. Критерии приёмки Фазы 1
|
||
|
||
- [ ] Расширение устанавливается в Chrome без ошибок
|
||
- [ ] Видеобуфер непрерывно работает на любой вкладке
|
||
- [ ] В буфере всегда есть не более 30 секунд видео
|
||
- [ ] rrweb пишет DOM-события без ошибок на типовых страницах (формы, таблицы, модальные окна)
|
||
- [ ] Лог событий фиксирует клики, навигацию и сетевые ошибки
|
||
- [ ] При нажатии кнопки архив скачивается в «Загрузки» за < 5 секунд
|
||
- [ ] Архив открывается, `last_30sec.webm` воспроизводится в браузере
|
||
- [ ] Пароли не попадают в лог и rrweb-снимки
|
||
- [ ] RAM-потребление расширения не превышает 50 МБ в фоне |