Helpomatica
Локальная веб-панель в духе Claude Projects: проекты, инструкции, файлы, общие чаты, пользователи с ролями и ответы Cursor Agent. Интерфейс на русском. Это не SPA-фреймворк и не облачный продукт — один процесс FastAPI раздаёт статику и JSON API, данные лежат на диске сервера.
Ответы агента не полностью офлайн: нужен CURSOR_API_KEY с
Cursor Dashboard → Integrations.
Веб-панель, логин, файлы и предразбор логов работают и без ключа.
Как запустить локально
Скопируйте .env.example в .env и вставьте ключ:
CURSOR_API_KEY=crsr_your_key_here
CURSOR_MODEL=auto
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
Затем:
.\run.ps1
Скрипт ставит зависимости в .deps (pip install --target .deps),
выставляет PYTHONPATH и поднимает uvicorn с --reload на
0.0.0.0:8010.
- На этой машине: http://127.0.0.1:8010
- С других ПК в LAN:
http://<IP-этой-машины>:8010 - Вход по умолчанию:
admin/admin
Если PowerShell блокирует локальные сценарии:
powershell -ExecutionPolicy Bypass -File .\run.ps1
Если страница не открывается с другого ПК, разрешите входящий TCP 8010 в брандмауэре Windows:
netsh advfirewall firewall add rule name="Helpomatica 8010" dir=in action=allow protocol=TCP localport=8010
Загрузки и агент выполняются на машине сервера, не в браузере клиента.
Файлы копируются во временный workspace из data/uploads/.
Docker
Нужен файл .env (из .env.example). Данные не кладутся в образ:
том ./data:/app/data хранит db.json и загрузки.
docker compose up -d --build
Откройте http://localhost:8010 и войдите. После пересборки контейнера
проекты, чаты и файлы остаются в ./data.
Healthcheck бьёт в GET / (страница логина, без авторизации).
GET /api/auth/me для проверки не подходит — без cookie он отдаёт 401.
Подробности про Cursor Agent в Linux-контейнере — в разделе Cursor SDK и Docker.
Что это такое
Панель для команды на одной машине / в LAN:
- Проекты — название, описание, инструкции, модель, иконка.
- Инструкции — общий контекст для всех чатов проекта.
- Файлы — материалы проекта (видны во всех чатах) или только этого чата.
- Чаты — общие на проект: любой вошедший пользователь читает и пишет в тех же разговорах.
- Пользователи — роли
adminиuser. Админ видит имена авторов сообщений; обычный пользователь видит «Вы» / «Коллега». - Ответы — локальный Cursor Agent (
AsyncAgent) со стримингом, размышлениями, историей инструментов и кнопкой «Стоп».
Дополнительно в UI: поиск по чатам, экспорт чата в Markdown, чипы контекстных файлов, копирование кода, цитаты строк лога, диаграммы Mermaid, формулы KaTeX, светлая/тёмная тема, индикатор статуса агента.
Архитектура
Браузер (static/index.html + app.js + style.css)
│ cookie helpomatica_session
▼
FastAPI (main.py) ──► data/db.json
│ data/uploads/
│ %TEMP%/helpomatica-agent/hlp-* (в Docker: /tmp/...)
▼
cursor-sdk: AsyncClient.launch_bridge → AsyncAgent.send → SSE
▼
Cursor API (нужен CURSOR_API_KEY)
- Бэкенд: FastAPI, статика смонтирована на
/static, кореньGET /отдаётstatic/index.html. Фронтенд — ванильный JS, без React/Vue. - Хранение: JSON-файл
data/db.json(атомарная запись через.tmp) и файловая системаdata/uploads/. Папкаdata/в Git не входит. - Сессии: список в
db.json, cookie HttpOnlyhelpomatica_session(SameSite=Lax, безSecure— HTTP по IP в LAN), срок 30 дней, не больше 200 сессий. Пароль: PBKDF2-HMAC-SHA256, 200 000 итераций. - Доступ: все авторизованные видят все проекты и общие чаты.
Отдельных ACL на проект нет. Файлы с
scope=chatпопадают в агент только этого чата;scope=project— во все чаты проекта. - Параллельность: глобального single-flight нет. У каждого
chat_idсвоя очередь: один активный запрос и максимум один в ожидании (второй лишний → HTTP 409). Разные чаты запускают отдельные local bridge. - Слушает
0.0.0.0:8010.
SESSION_SECRET в .env — заготовка, main.py его не читает.
Стек
Версии из текущего pip install в .deps (в requirements.txt
пакеты без пинов):
| Слой | Что |
|---|---|
| Язык | Python 3.12 (локально 3.12.10). cursor-sdk требует ≥ 3.10 |
| API | FastAPI 0.141.1, Starlette, Pydantic v2 |
| Сервер | uvicorn[standard] 0.52.1 |
| Агент | cursor-sdk 1.0.27 (AsyncAgent, AsyncClient.launch_bridge, LocalAgentOptions) + httpx |
| Конфиг | python-dotenv 1.2.2 |
| Загрузки | python-multipart 0.0.32 |
| Предразбор логов | log_preanalysis.py (stdlib) |
| Фронтенд | static/app.js, static/style.css — без сборки |
| CDN | Mermaid 11, KaTeX 0.16.22, шрифты Google (Manrope, Playfair Display) |
| Данные | JSON + файлы на диске |
Локальный запуск (run.ps1) кладёт пакеты в .deps и добавляет каталог
в sys.path. Docker ставит те же пакеты в venv через pip install -r requirements.txt.
Форма data/db.json
Корень — объект со списками. При пустом файле / первом старте создаётся
админ из ADMIN_USERNAME / ADMIN_PASSWORD.
{
"users": [
{
"id": "hex",
"username": "admin",
"display_name": "Admin",
"password_hash": "salt$pbkdf2hex",
"role": "admin",
"created_at": "ISO-8601"
}
],
"sessions": [{ "id": "token", "user_id": "…", "created_at": "…" }],
"projects": [{
"id": "…", "name": "…", "description": "…", "instructions": "…",
"icon": "✦", "model": "auto", "created_by": "…",
"created_at": "…", "updated_at": "…"
}],
"chats": [{
"id": "…", "project_id": "…", "title": "…", "sort_order": 0,
"created_by": "…", "updated_by": "…",
"created_at": "…", "updated_at": "…"
}],
"messages": [{
"id": "…", "chat_id": "…", "role": "user|assistant", "content": "…",
"attachments": [{ "id": "…", "name": "…" }],
"user_id": "… или null", "author_name": "Admin|Помощник|…",
"thinking": "…", "thinking_duration_ms": 17519,
"activity": [{ "id": "a1", "type": "thinking|shell|…", "title": "…",
"detail": "…", "status": "running|completed" }],
"created_at": "…"
}],
"files": [{
"id": "…", "project_id": "…", "chat_id": "null или id чата",
"scope": "project|chat", "name": "line_u_codes.log.1",
"label": "", "stored_name": "<uuid>_оригинал",
"content_type": "…", "size": 123, "created_at": "…"
}],
"analysis_cache": [{
"key": "sha256", "file_sig": "sha256", "project_id": "…",
"job_id": "", "question": "нормализованный текст",
"answer": "…", "preanalysis": "markdown", "summary": "…",
"created_at": "…"
}]
}
Байты файлов лежат в data/uploads/<stored_name>, в UI показывается
оригинальное name.
Как проходит запрос к агенту
- Пользователь пишет сообщение. Фронт сохраняет его
POST /api/chats/{id}/messages(рольuser, вложения, автор). - Сразу же
POST /api/chats/{id}/respond— SSE-поток (text/event-stream). - Сервер занимает слот чата (или ставит в очередь на один).
Событие
status: queued/starting. - Собирается workspace:
prepare_agent_workspaceсоздаёт%TEMP%/helpomatica-agent/hlp-<project8>-*(в Linux/Docker —/tmp/helpomatica-agent/hlp-…). Туда hardlink/копия загрузок (и в./, и в./files/), плюс подсказкиREADME.md,PROJECT_FILES.md,AGENTS.md,.cursorignore. - Если вопрос похож на разбор логов (
log_preanalysis.looks_like_log_job) и в скоупе есть*.log*:- ищется
analysis_cache; - иначе поток-скан логов с SSE
progress(«Читаю лог… N%»); - результат пишется в
./PREANALYSIS.md.
- ищется
- Промпт: инструкции проекта + выдержки файлов + последние 30 сообщений
- правило «cwd уже workspace, читай PREANALYSIS.md первым».
- В отдельном потоке (на Windows —
WindowsProactorEventLoopPolicy) вызываетсяAsyncClient.launch_bridge(workspace=cwd)иAsyncAgent.create/agent.send. События моста читаются в этом потоке; основной event loop забирает их черезasyncio.to_thread(queue.get). - В браузер уходят SSE:
thinking— дельтыthinking-delta;activity— инструменты, shell, статус, шаги «Логи»;delta— токены ответа (text-delta);thinking_done,done/cancelled/error.
- Готовый текст сохраняется как сообщение ассистента (
thinking,activity,thinking_duration_ms). При успехе пишется кэш разбора (до 80 записей).
Кнопка «Стоп» — POST /api/chats/{id}/cancel.
Workspace агента и файлы
- Каталог:
tempfile.gettempdir()/helpomatica-agent/hlp-…. - Копируются файлы проекта + файлы этого чата + вложения сообщений.
- Вложения, chat-scope и логи копируются всегда; остальные файлы проекта режутся лимитами 40 МБ / файл и 80 МБ суммарно.
- Если hardlink недоступен —
shutil.copy2. - Агент работает только с этим cwd; исходники панели и диск клиента ему не отдаются.
Цитаты в ответе вида :15231:line_u_codes.log.1 или
::log:line_u_codes.log.1:15231:: фронт превращает в ссылку на
GET /api/files/{id}/excerpt?start=&end= (фрагмент ±25 строк).
Авторизация и API (кратко)
Публично: GET /, /static/*, POST /api/auth/login.
Остальные /api/* требуют cookie.
| Метод | Путь | Назначение |
|---|---|---|
| POST | /api/auth/login |
вход, Set-Cookie |
| POST | /api/auth/logout |
выход |
| GET | /api/auth/me |
текущий пользователь |
| GET/POST | /api/users |
список / создание (только admin) |
| GET | /api/agent/status |
ключ, SDK, busy, очереди чатов |
| CRUD | /api/projects, /api/chats |
проекты, чаты, порядок |
| GET | /api/chats/{id}/export |
Markdown |
| POST | /api/chats/{id}/messages |
сохранить сообщение |
| POST | /api/chats/{id}/respond |
SSE-ответ агента |
| POST | /api/chats/{id}/cancel |
остановить генерацию |
| GET | /api/search?q= |
поиск по названиям и тексту (от 2 символов, до 30) |
| POST | /api/projects/{id}/files |
загрузка (scope + chat_id) |
| POST | /api/chats/{id}/files/bind |
привязать файлы к чату |
| GET | /api/files/{id} |
скачать |
| GET | /api/files/{id}/excerpt |
фрагмент лога по строкам |
Модель проекта выбирается в UI (auto, composer-2.5, composer-2,
gpt-5.2, claude-4.6-sonnet) и уходит в AgentOptions.model.
По умолчанию — CURSOR_MODEL из .env.
Cursor SDK и Docker
На Windows wheel cursor-sdk кладёт в
cursor_sdk/_vendor/bridge/bin/:
cursor-sdk-bridge.cmd→ запускаетnode.exe(вендорный, ~89 МБ)../dist/bin/cursor-sdk-bridge.js— сам мост (Node, не отдельный .exe агента)
resolve_bridge_path() ищет: CURSOR_SDK_BRIDGE_BIN, затем bundled
bin/cursor-sdk-bridge (POSIX) / cursor-sdk-bridge.cmd (Windows),
затем PATH.
Не копируйте .deps с Windows-хоста в Linux-образ — там node.exe.
Dockerfile ставит пакет заново: Linux-wheel cursor-sdk (x64/arm64)
должен принести свой node + launcher cursor-sdk-bridge.
Если в контейнере мост не стартует (нет bundled node, ошибка glibc,
нет cursor-sdk-bridge):
- веб-UI, логин, файлы и предразбор логов всё равно работают;
- ответы Cursor Agent нужно снимать на Windows-хосте через
.\run.ps1.
Образ не содержит .env. Ключ передаётся env_file: .env.
Безопасность
- Сразу смените пароль админа (
admin/adminтолько для первого старта). - Не коммитьте
.env(уже в.gitignore). Ключ Cursor — секрет. - Панель слушает весь LAN: ограничьте доступ брандмауэром.
- Cookie без
Secure: нормально для HTTP в LAN, не для публичного HTTPS без доработки. - Это не multi-tenant isolation: любой
userвидит все проекты и чаты. - Генерации картинок в панели нет; агенту это явно запрещено в промпте.
Файлы репозитория
| Путь | Роль |
|---|---|
main.py |
FastAPI-приложение |
log_preanalysis.py |
потоковый скан логов → PREANALYSIS.md |
static/ |
HTML/CSS/JS |
run.ps1 |
локальный запуск на Windows |
requirements.txt |
зависимости |
.env.example |
шаблон переменных (без секретов) |
Dockerfile, docker-compose.yml, .dockerignore |
контейнер |
test_preanalysis.py |
короткий тест предразбора логов |
data/ |
БД и загрузки на диске сервера — не в Git (только data/.gitkeep) |
Выгрузка в Gitea
В репозиторий кладите только исходники продукта: main.py, log_preanalysis.py, static/, requirements.txt, run.ps1, Docker-файлы, .env.example, .gitignore, README.md.
Не коммитьте и не копируйте в Gitea:
.env— тамCURSOR_API_KEYи пароль админаdata/— чаты, загрузки,db.json(том Docker./dataтоже только локальный).deps/— локальныйpip install --target(ставится заново изrequirements.txt)
После клона:
copy .env.example .env
# вставьте CURSOR_API_KEY и смените ADMIN_PASSWORD
docker compose up -d --build
# или локально на Windows:
.\run.ps1