Files
Helpomatica/README.md
T
2026-08-14 11:06:58 +07:00

17 KiB
Raw Blame History

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 HttpOnly helpomatica_session (SameSite=Lax, без Secure — HTTP по IP в LAN), срок 30 дней, не больше 200 сессий. Пароль: PBKDF2-HMAC-SHA256, 200000 итераций.
  • Доступ: все авторизованные видят все проекты и общие чаты. Отдельных 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 в .depsrequirements.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.


Как проходит запрос к агенту

  1. Пользователь пишет сообщение. Фронт сохраняет его POST /api/chats/{id}/messages (роль user, вложения, автор).
  2. Сразу же POST /api/chats/{id}/respond — SSE-поток (text/event-stream).
  3. Сервер занимает слот чата (или ставит в очередь на один). Событие status: queued / starting.
  4. Собирается 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.
  5. Если вопрос похож на разбор логов (log_preanalysis.looks_like_log_job) и в скоупе есть *.log*:
    • ищется analysis_cache;
    • иначе поток-скан логов с SSE progress («Читаю лог… N%»);
    • результат пишется в ./PREANALYSIS.md.
  6. Промпт: инструкции проекта + выдержки файлов + последние 30 сообщений
    • правило «cwd уже workspace, читай PREANALYSIS.md первым».
  7. В отдельном потоке (на Windows — WindowsProactorEventLoopPolicy) вызывается AsyncClient.launch_bridge(workspace=cwd) и AsyncAgent.create / agent.send. События моста читаются в этом потоке; основной event loop забирает их через asyncio.to_thread(queue.get).
  8. В браузер уходят SSE:
    • thinking — дельты thinking-delta;
    • activity — инструменты, shell, статус, шаги «Логи»;
    • delta — токены ответа (text-delta);
    • thinking_done, done / cancelled / error.
  9. Готовый текст сохраняется как сообщение ассистента (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