Team Tracker
Team Tracker записує активні хвилини (тики раз на хвилину), класифікує їх для кожного workspace як internal або external за Git remote активного редактора, і надсилає агрегати в team API SnakeFlow. Функція вимкнена, доки ви не увійдете й не ввімкнете її.
Облік хвилин не залежить від Wake Lock → (запобігання сну ОС): трекер не вважає «людина активна» за фактом утримання wake lock.
Вимоги
- SnakeFlow: Team Tracker — Login (GitHub OAuth у редакторі).
devManager.team.enabled= true (команда входу може увімкнути це автоматично).- Для системної миші та клавіатури поза редактором: ActivityWatch (
aw-qt) на машині (рекомендовано; див. нижче).
ActivityWatch і статус-бар
Коли ActivityWatch запущений, розширення бачить активність у інших програмах (браузер, зовнішній термінал тощо), щоб не переходити в IDLE лише через відсутність подій у VS Code. Якщо є window watcher (bucket aw-watcher-window_*), раз на хвилину зчитується назва програми та заголовок вікна (коротке кешування) для allowlist програм і підказки в статус-барі.
- Клік по елементу Team Tracker у статус-барі — запуск
aw-qt(якщо встановлено) або зупинка ActivityWatch (завершує локальні процеси ActivityWatch на ОС). - Якщо
aw-qtне знайдено — пропозиція відкрити сторінку завантаження. - SnakeFlow: Team Tracker — Show Activity Log (Command Palette) — детальний лог (
ACTIVE/IDLE/GATED).
Жорстка вимога (requireActivityWatch)
Якщо devManager.team.requireActivityWatch = true (за замовчуванням), хвилини не рахуються, доки REST API ActivityWatch недоступний за activityWatchUrl. У статус-барі — червоний індикатор і підказка, що час не записується, доки не запустити ActivityWatch.
Вимкніть requireActivityWatch лише для ролей, яких не потрібно примушувати (наприклад адміни на власних ПК).
Що вважається «активним»
Кожен 60-секундний тик спершу перевіряє опційний Інтервал сну (примусовий IDLE, якщо локальний час потрапляє в idleSleepWindow).
Інакше collector потребує доступного ActivityWatch (див. Жорстка вимога). Далі оцінюються три сигнали:
| Сигнал | Джерело | Роль |
|---|---|---|
| Ввід AW | REST до bucket afk / input | «ActivityWatch бачив клавіатуру/мишу в межах вікна тиші». |
| Простій ОС | Windows GetLastInputInfo, macOS HIDIdleTime, Linux xprintidle (якщо встановлено) | «Справжній вік останнього вводу» — стійкіше до синтетичного вводу, який обманює лише AW. |
| Фокус IDE | window.state.focused + час останнього фокусу | Підтверджує AW, коли вікно VS Code недавно мало системний фокус. |
«Годинник» активності (lastAwAt) оновлюється лише коли виконується одна з умов trustworthy:
- AW і ОС згодні — обидва звітують недавній ввід (найсильніший сигнал).
- Лише ОС (Windows / macOS) — API простою каже «недавній ввід» (навіть якщо AW розходиться).
- AW + недавній фокус IDE — AW каже «активно» і це вікно VS Code мало OS-фокус у межах вікна тиші (захист від синтетики, коли ви не біля редактора).
- Fallback Linux — якщо helper простою недоступний (немає
xprintidle), лишається поведінка «лише AW», щоб не ламати Linux без X-утиліт.
Якщо жодна умова не виконується довше за ефективне вікно тиші, хвилина — IDLE. Ефективне вікно = мінімум idle_window_minutes з усіх workspace, до яких ви належите (налаштування адміна на сервері). Без membership використовується вбудовані 5 хв до вступу в workspace.
Добу обмежує бекенд (див. API-документацію).
Internal vs external
Для кожного workspace тик internal лише якщо одночасно виконуються умови:
- Збіг репо — remote активного текстового редактора (
owner/repo) відповідає патернам репо workspace (glob, як раніше). - Збіг програми (якщо налаштовано) — allowlist програм порожній (фільтр вимкнено), або хоча б один regex збігається з полем app або title вікна з ActivityWatch (без урахування регістру). Список зберігається на сервері; адміни редагують, усі учасники отримують через
/api/team/me.
Інакше — +1 external для цього workspace:
- Невірний репо → external з реальним
owner/repo, якщо відомо. - Програма поза allowlist (список непорожній і вікно не підходить під жоден regex) → external з міткою
_app:НазваПрограми— бекенд зберігає такі хвилини окремо від зовнішніх репо.
Порожній allowlist програм означає «будь-яка програма підходить» для фільтра за додатком; далі вирішують лише патерни репо.
Примітка: коли ActivityWatch фіксує активність у іншій програмі, класифікація за репо все одно береться з останнього активного редактора в Cursor/VS Code. Якщо в workspace непорожній allowlist, але немає window watcher (bucket), app/title можуть бути недоступні → тик не проходить фільтр програм і йде в external для цього workspace.
Allowlist програм (адмін)
Адміни workspace ведуть список regex на сервері:
- SnakeFlow Cloud → вкладка Team → панель workspace → редагування allowlist програм (іконка камери) — той самий сценарій, що й команда нижче.
- SnakeFlow: Team Tracker — Edit App Allowlist — regex через кому; порожній рядок очищає список (знову «усі програми дозволені»).
Сервер перевіряє кожен патерн як JavaScript RegExp (прапор без урахування регістру) і відхиляє невалідний синтаксис. Є обмеження на кількість патернів і довжину рядка (див. реалізацію API).
Порада: regex перевіряються і по назві процесу / app, і по заголовку вікна; за змістом це близько до категоризації ActivityWatch, але правила задає ваша команда для звітів SnakeFlow.
Налаштування
| Параметр | Тип | За замовчуванням | Опис |
|---|---|---|---|
devManager.team.enabled | boolean | false | Головний перемикач Team Tracker. |
devManager.team.idleWindowMinutes | number | 5 | Заявлене вікно тиші (1–15); зміна перезапускає collector. Ефективне вікно з membership = мінімум серверного idle_window_minutes — див. Що вважається активним. |
devManager.team.useActivityWatch | boolean | true | Запитувати локальний ActivityWatch для системного вводу. |
devManager.team.requireActivityWatch | boolean | true | Якщо true — немає обліку, поки ActivityWatch недоступний. |
devManager.team.activityWatchUrl | string | http://localhost:5600 | Базовий URL aw-server. |
devManager.team.requireTracking | boolean | false | Якщо true — попередження, коли трекінг вимкнено і сесію не запущено. |
devManager.team.idleSleepWindow | string | "" | Інтервал локального часу HH:MM-HH:MM, у якому трекер примусово IDLE — див. Інтервал сну. |
Зміна цих ключів перезапускає collector без перезавантаження вікна.
Інтервал сну
devManager.team.idleSleepWindow (формат HH:MM-HH:MM у локальному часі, за замовчуванням порожньо) — додатковий запобіжник проти хибних спрацьовувань активності вночі. Поки поточний локальний час потрапляє в інтервал, collector примусово IDLE, незалежно від ActivityWatch, OS idle time чи фокусу VS Code.
Коли вмикати:
- Синтетичний input від програм віддаленого керування або «anti-idle» утиліт (AnyDesk, mouse jiggler, окремі драйвери) вводить ActivityWatch в оману: він пише
not-afk, поки ви спите. - Запущена довгограюча задача або витік wake lock тримає ОС активною всю ніч.
Інтервал коректно обгортає північ, якщо кінець менший за початок — 23:00-07:00 покриває типову нічну зміну. Порожній рядок вимикає режим.
Працює разом із правилами з розділу Що вважається активним: навіть без інтервалу сну collector віддає перевагу узгодженим сигналам (AW + ОС, лише ОС на Windows/macOS, або AW + недавній фокус VS Code), тож одиничний синтетичний ввід від віддалених утиліт рідко зарахує хвилину.
Команди
| Команда | Опис |
|---|---|
| SnakeFlow: Team Tracker — Login | Вхід; може увімкнути трекінг. |
| SnakeFlow: Team Tracker — Logout | Зупинка та вихід. |
| SnakeFlow: Team Tracker — Status | Короткий статус у повідомленні. |
| SnakeFlow: Team Tracker — Show Activity Log | Output-канал з причинами кожної хвилини. |
| SnakeFlow: Team Tracker — Start/Stop ActivityWatch | Те саме, що клік у статус-барі. |
| SnakeFlow: Team Tracker — Edit App Allowlist | Адмін workspace: allowlist regex для вікна ActivityWatch (на сервері). |
| SnakeFlow: Team Tracker — Create Workspace | Адмін: новий workspace + invite. |
| SnakeFlow: Team Tracker — Join Workspace | Вхід за кодом запрошення. |
| SnakeFlow: Team Tracker — Manage Workspaces | Список / контекст. |
| SnakeFlow: Team Tracker — Sync Commits (admin) | Синхронізація агрегованих комітів для дашбордів. |
| SnakeFlow: Team Tracker — Sync Closed Issues (admin) | Синхронізація закритих GitHub issues (assignee + UTC-день закриття) для дашбордів. |
Синхронізація комітів і закритих issues (адмін)
Адміни workspace з увімкненим Team Tracker і сесією GitHub можуть синхронізувати агреговані дані GitHub у SnakeFlow Cloud (D1). Бекенд не отримує GitHub-токен; розширення питає GitHub Search і надсилає лічильники POST-запитами.
| Метрика | Запит GitHub (на учасника, UTC-день) | Таблиця | Internal / external |
|---|---|---|---|
| Коміти | author:{login} author-date:{YYYY-MM-DD} | daily_commits | Репо відповідає repo patterns workspace |
| Закриті issues | assignee:{login} is:issue is:closed closed:{YYYY-MM-DD} | daily_closed_issues | Ті самі repo patterns |
- Для закритих issues потрібен assignee — issues без assignee не рахуються.
- Хто закрив не важить — лише assignee та дата закриття.
- Синхронізація щогодини під час трекінгу (admin workspace) або вручну командами вище (за замовчуванням 7 UTC-днів).
- SnakeFlow Cloud → Activity показує колонки internal/external для обох метрик.
Погодинна смуга та часові пояси учасників
У SnakeFlow Cloud → Activity для кожного учасника показується добова смуга на 24 години (0h–23h). На бекенді погодинні бакети зберігаються в UTC (година на момент flush на сервері). Якщо лише так відображати дані, «9 ранку локально» у різних регіонах з’їхали б по різних колонках.
Після кожного успішного POST /api/team/active-minutes розширення надсилає IANA-часовий пояс учасника (з Intl.DateTimeFormat().resolvedOptions().timeZone, наприклад Europe/Kyiv). API зберігає його в рядку workspace_members для цього учасника (останнє значення перемагає).
Дашборд перетворює кожен збережений бакет (UTC-доба, UTC-година) у локальну календарну годину цього учасника перед відмальовуванням смуги. Виходить спільна вісь 0–23 «локальних робочих годин»: якщо один починає о 09:00 у Києві, а інший о 09:00 у Лондоні, обидва потрапляють у ту саму колонку (година 9) — зручно для читання «хто коли був активний».
- Старі учасники, які ще не робили push клієнтом із полем
timezone, у API маютьnull; інтерфейс для перетворення тимчасово використовує UTC, доки наступний flush не оновить поле. - Зміна часового поясу в ОС на машині оновить значення на наступному push; історичні бакети інтерпретуються з останнім збереженим поясом (для огляду активності команди це прийнятно).
- Добові суми (
daily_minutes) лишаються на UTC-календарних добах з сервера; зсув за поясом застосовується лише до погодинної візуалізації.
Окремого налаштування «часовий пояс воркспейсу» для цієї фічі немає — вирівнювання дає поєдиний пояс, який надсилає кожен учасник.
Див. також
- Wake Lock → — окрема підсистема запобігання сну ОС
- Усі налаштування →
- Довідник команд палітри →