Журнал изменений

GitHub

Каждое значимое изменение, релиз за релизом. Тот же файл публикуется в репозитории и переведён на все 19 языков.

[1.7.2] — 2026-05-04

Справочный центр, in-UI App settings, мобильный сайдбар, единая кнопка Scan и ярлык «Show result» на каждом построителе промптов.

✨ Новые возможности

  • feat(help): in-app user guide (/#/help) — длинная Markdown-документация, доступная из новой записи сайдбара. Покрывает каждую страницу пошагово: quick start, редактор CV, Profile, фильтры Scan, preview Pipeline, Evaluate, Deep research, Apply, Tracker, Reports, все 7 режимов, журнал Activity, Health, подсказки настройки. Автоматически собранное липкое оглавление из заголовков <h2>, синхронная сборка DOM (без гонок). Локализован для всех 8 поддерживаемых локалей.
  • feat(config): in-UI App settings page (/#/config) — редактирование ANTHROPIC_API_KEY, ANTHROPIC_MODEL, GEMINI_API_KEY, GEMINI_MODEL, HH_USER_AGENT, PORT, HOST из браузера. Пишет в .env родительского проекта, чтобы Node-скрипты career-ops И загрузчик dotenv web-ui читали один и тот же источник. Секретные ключи маскируются на чтении (первые/последние 4 символа). Поля моделей — выпадающие списки с курированным набором (claude-sonnet-4-6 / claude-opus-4-7 / claude-haiku-4-5 / gemini-2.0-flash / и т. д.). Пустое значение удаляет ключ. Значения немедленно применяются к работающему process.env — для большинства настроек не нужен перезапуск.
  • feat(modes): "⚡ Show result" button alongside "Copy prompt" — когда промпт сгенерирован в ручном режиме, пользователи больше не должны перепечатывать ввод, чтобы получить результат LLM. Новая кнопка повторно отправляет ту же форму с run: true, проваливаясь к понятному toast (Set ANTHROPIC_API_KEY or GEMINI_API_KEY in .env first), если ключ не настроен. Работает на /#/deep, /#/project, /#/training, /#/followup, /#/batch, /#/contacto, /#/interview-prep, /#/patterns.

🐛 Правки UX + UI

  • fix(scan): single Scan button replaces three (Scan all + EN + RU) — избыточный выбор, идентичный дефолт в 99% случаев. Объединённая кнопка 🌐 Scan запускает каждый включённый источник. Справочные документы обновлены в 8 локалях.
  • fix(ui): mobile sidebar drawer — viewport <900px теперь получает кнопку-гамбургер (☰) в топбаре; body.sidebar-open переключает CSS-трансформацию, выдвигающую сайдбар. Backdrop dim + клик в любом месте закрывают его. Клик по якорю + hashchange автоматически закрывают, чтобы пользователь приземлялся на новую страницу с задвинутым drawer. Большие viewport не затронуты.
  • fix(server): footer version reflects web-ui, not the parent VERSION — /api/health теперь читает собственный package.json web-ui. В подвале больше не утекает устаревший 1.6.0 из VERSION родителя. VERSION родителя по-прежнему отдельно проявляется как parentVersion.

📦 Новые REST-эндпоинты

Метод Путь Назначение
GET /api/help/:lang Возвращает Markdown user-гайд для запрошенной локали с фоллбэком на en.md. Path-traversal-safe.
GET /api/config Возвращает текущие значения всех известных env-ключей; секреты маскированы.
POST /api/config Пишет заданные ключи в .env родительского проекта, валидирует каждое значение, применяет вживую к process.env.

🌐 i18n

  • 30+ новых ключей по nav.help, nav.config, help.*, config.*, deep.showResult, deep.needKey, scan.btnRun. Все 8 локалей заполнены.

🧪 Тесты

  • tests/help.test.mjs (12 кейсов) — каждая поддерживаемая локаль возвращает осмысленный markdown, EN spot-checks каждого slug страницы, неизвестный lang → EN-fallback, санитайзинг path-traversal, каждая локаль ссылается на cv.md / profile.yml / .env.
  • tests/help-ui.test.mjs (9 кейсов) — регистрация view-файла, запись сайдбара, наличие ключей i18n в каждой локали, наличие файлов docs для каждой локали, EN/RU help имеет 14 канонических разделов, каждый маршрут #/foo покрыт, обвязка Show-result на deep + mode-page.
  • tests/env-config.test.mjs (18 кейсов) — чисто-функциональные тесты для parseEnv, maskSecret, validateConfig, updateEnvFile (бутстрап, in-place переписывание с сохранением комментариев, удаление пустого значения, кавычки-при-необходимости).
  • tests/config-endpoint.test.mjs (8 кейсов) — GET маскирует секреты / возвращает путь env; POST пишет в .env родителя; live применение к process.env; пустое значение удаляет; отвергает неизвестные ключи + неправильные ключи Anthropic с 400.

📊 Статистика

  • Тесты: 233 → 277 (+44 в 4 новых файлах тестов).
  • E2E: 20 smoke + 23 comprehensive = 43 шага Playwright, все зелёные.
  • Покрытие: 93.5% line / 82.6% branch / 93.7% функций (без изменений — новый код полностью покрыт тестами).

[1.7.1] — 2026-05-04

Патч-релиз, собирающий пост-v1.7.0 работу: панель preview pipeline, интеграция Anthropic API, прокручиваемый сайдбар, dotenv-загрузчик, динамический список Active-companies, усиление CI-workflow.

✨ Панель preview pipeline

  • Переработка /#/pipeline — левый список + правая панель preview. Клик по любому URL получает серверный прокси-снапшот (GET /api/pipeline/preview снимает скрипты/стили/тэги, ограничивает на 8 КБ, валидирован через isValidJobUrl). Живой фильтр-инпут, счётчик «In queue», кнопка-заголовок ⚡ «Evaluate first». Inline ▶/✕ на каждой строке плюс полные Evaluate / Open in tab / Delete на панели preview. Стабильные тест-селекторы через data-url + классы .pipeline-row + .pipeline-row-delete. 8 новых тестов в tests/pipeline-preview.test.mjs (mocked fetch, без необходимости в реальной привязке upstream).

✨ Интеграция Anthropic API — «Run live» везде

  • server/lib/anthropic.mjs — клиент без зависимостей для Anthropic Messages API (по умолчанию claude-sonnet-4-6, override через ANTHROPIC_MODEL). Когда задан ANTHROPIC_API_KEY, каждая страница режима (/#/deep, /#/project, /#/training, /#/batch, /#/contacto, /#/interview-prep, /#/patterns) рендерит кнопку «⚡ Run live (Anthropic)» как основное действие — клик выполняет промпт и рендерит Markdown обратно в браузер вместо передачи в Claude Code. Gemini остаётся fallback, когда задан только его ключ. Manual-режим всё ещё работает без ключей. 8 новых тестов в tests/anthropic.test.mjs.

🐛 Правки CI / pipeline

  • fix(api): tighten pipeline URL validator (FIX-M7) — теперь также отвергает loopback-имена хостов, длину <10 или >2000, пробелы внутри URL.
  • fix(server): actually load .env so HH_USER_AGENT / GEMINI_API_KEY hints work — добавлен server/lib/dotenv.mjs (35-строчный загрузчик без зависимостей), подключённый в начале server/index.mjs. Runtime-подсказки в коде сканера наконец что-то делают. 6 новых тестов.
  • fix(ui): scrollable sidebar — 18 пунктов навигации в 6 группах переполняли короткие viewport. .sidebar теперь имеет overflow-y: auto с тонкими кастомными скроллбарами.
  • fix(ui): make HH_USER_AGENT banner dismissible — затем удалён полностью из /scan, когда стало ясно, что это перебор. Проверка страницы Health всё ещё проявляет его.
  • fix(scan): Active companies list is now collapsible + filterable + grouped — 87 тэгов в плоском виде переполняли. Теперь переключатель «▸ Active companies 87/71» разворачивает упорядоченный список (✓ API-backed первыми, ○ websearch вторыми) плюс поисковый фильтр.
  • fix(test): isolate api.test.mjs + en-scanner.test.mjs from parent project — оба теперь поднимают tmp project root, чтобы CI работал без чек-аута родителя рядом с web-ui.
  • fix(workflow): publish-package version-match only on release events — workflow_dispatch с main больше не валит проверку tag/version.
  • fix(e2e): stable selector for pipeline row delete — восстановлена обёртка-anchor + добавлен атрибут data-url, чтобы e2e-набор был стабилен по селекторам.

📦 Новый REST-эндпоинт

Метод Путь Назначение
GET /api/pipeline/preview?url=… Серверный прокси: возвращает снапшот видимого текста URL (скрипты/стили сняты, лимит 8 КБ), огорожен isValidJobUrl.

📊 Статистика после этого пакета

  • Тесты: 225 → 233 (+8 поверх v1.7.0).
  • Файлы тестов: 25 → 26.
  • E2E: 20 + 23 = 43 шага Playwright, все зелёные.

[1.7.0] — 2026-05-03

35-коммитный проход усиления + UX + завершения возможностей, движимый QA r5. Приземлены три слоя безопасности (санитайзинг XSS, CSP, валидация ввода), заполнен каждый недостающий CRUD-эндпоинт, бутстрап родительского проекта теперь полностью автоматизирован, и UI получил 9 новых страниц — Activity, переработанная Deep Research плюс 7 sidebar-сгруппированных режимов (project / training / followup / batch / outreach / interview-prep / patterns), покрывающих 100% родительского modes/. Pipeline получил серверную панель preview. Интеграция Anthropic API делает «Run live» однокликовым действием по всем режимам. Покрытие тестами пошло с 73 до 225 в 25 файлах тестов, плюс 23 шага comprehensive Playwright e2e. GitHub Actions отгружают workflow CI / AI review / Release / Publish-Package.

🔒 Безопасность

  • fix(cv): sanitize CV markdown to block stored XSS in preview (FIX-C10) — PUT /api/cv теперь снимает <script>, <iframe>, <object>, <embed>, <style>, <form>, <svg>, обработчики событий on*= и URI javascript:/vbscript:/data:text/html до записи cv.md. Тело ограничено 1 МБ (413 на overflow). Клиентский UI.md() переписан, чтобы экранировать каждый байт до запуска любых markdown-преобразований, так что сырой HTML никогда не доходит до innerHTML. Атрибут href ссылок валидируется по списку безопасных схем (http/https/mailto/tel/относительные + только data:image). 17 новых тестов по хелперу очистки и HTTP round-trip.
  • fix(server): add CSP and baseline security headers (FIX-L2) — каждый ответ теперь несёт X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin. Когда сервер привязан за пределами loopback (HOST ≠ 127.0.0.1/::1/localhost), сверху накладывается строгий Content-Security-Policy: default-src 'self', script-src 'self' (без unsafe-inline), Google Fonts в whitelist, connect-src 'self' блокирует XSS-эксфильтрацию. Inline-обработчики onclick в index.html и router.js перенесены в addEventListener, чтобы строгий CSP оставался цел. 8 новых тестов, прикрывающих CSP на 5 разных значениях HOST.
  • fix(api): tighten pipeline URL validator (FIX-M7) — POST /api/pipeline принимал "not-a-url" и сохранял его. Теперь isValidJobUrl() отвергает голые строки, ввод <10 или >2000 символов, URL с пробелами, схемы не-http(s) и loopback-имена хостов (localhost/127.0.0.1/::1). Сводит в одно FIX-M3 + FIX-M6 (вернуть 400 на невалидный, плюс флаг deduped на успехе).
  • fix(server): actually load .env so HH_USER_AGENT / GEMINI_API_KEY hints work — ранее runtime говорил пользователям «задать HH_USER_AGENT в .env», но сервер этот файл не читал, так что следование инструкции ничего не давало. Добавлен загрузчик dotenv без зависимостей в 35 строк (server/lib/dotenv.mjs), подключённый в начале server/index.mjs. Значения process-env, заданные через командную строку, по-прежнему побеждают, так что существующие override CI не затеняются. .env.example родителя теперь включает задокументированный блок HH_USER_AGENT с реальным примером User-Agent от Chrome. 6 новых тестов.
  • fix(api): sanitize JD before prompt assembly (FIX-M5) — POST /api/evaluate снимает ANSI-эскейпы, управляющие байты, inline-тэги <script> и обрезает пробелы перед вызовом Gemini или эхом промпта обратно. Лимит длины 50 КБ. Минимум 50 символов проверяется против санитайзированного текста, так что попытки prompt-injection, выглядящие достаточно длинными, но в основном состоящие из эскейпов, fail-fast с 400.
  • fix(health): mask Node version + project root when HOST!=loopback (FIX-M1) — /api/health больше не отпечатывает хост на LAN-доступных деплоях. Loopback-ответы сохраняют значения для локальной диагностики.

✨ Новые возможности

  • feat: 7 new sidebar modes + grouped sidebar (FIX-C8) — покрывает 100% родительского каталога modes/ без пробелов в UI. Новые маршруты: #/project (советник по портфолио-проектам), #/training (оценка курсов/сертификаций), #/followup (каденция на заявку), #/batch (параллельный процессор URL), #/contacto (составитель LinkedIn-outreach), #/interview-prep (подготовка к этапу), #/patterns (анализатор паттернов отказа). Все семь делят один config-driven view-фабрику (public/js/views/mode-page.js) и один универсальный эндпоинт POST /api/mode/:slug — добавление нового режима в будущем — одна строка конфигурации + один блок i18n. Сайдбар реорганизован в 6 групп: Sourcing / Decision / Application / Networking / Analytics / Setup. Итого 18 пунктов навигации. 12 новых тестов в tests/modes-endpoints.test.mjs.
  • fix: bootstrap parent deps + russian_portals defaults (FIX-C4 + C9 + C12 + H2) — bin/start.sh теперь устанавливает родительские node_modules (js-yaml, playwright, jsdom) И npx playwright install chromium на свежих клонах, так что /api/stream/scan, /pdf и /liveness работают end-to-end из коробки. createApp() проверяет portals.yml на каждом запуске — если блок russian_portals: отсутствует, дописывает задокументированный дефолт с комментариями. Идемпотентно: второй запуск — no-op. 3 новых теста.
  • fix: disable 9 dead portal slugs in template + health-check script (FIX-C3) — templates/portals.example.yml теперь идёт с Ada / Factorial / Tinybird / Weights & Biases / Travelperk / Clarity AI / Forto / Vinted / Runway, помеченными enabled: false (у каждой записи inline-комментарий с причиной). Новые установки сканируют 87 живых компаний вместо 96. Новый web-ui/scripts/portals-health-check.mjs HEAD-пробит каждый включённый careers_url и сообщает о DEAD-записях с предлагаемым списком патчей (JSON-вывод через --json). 3 новых теста.
  • feat(activity): user-action log + Activity sidebar page — каждый state-changing API-запрос захватывается в data/activity.jsonl (timestamp, action verb, target, флаг успеха, опциональная деталь). Новая запись сайдбара Activity с chip-фильтрами префикса действия (pipeline / cv / jd / evaluate / scan / stream / script), бейджами ✓/✗ на действие и кнопкой обновления. Авторотация на 5 МБ. 10 новых тестов, покрывающих middleware, фильтры чтения, толерантность к битым строкам и защиту от рекурсии для самого GET /api/activity.
  • feat(deep): view Deep Research in browser + saved-results archive — страница Deep Research теперь (a) запускает промпт через Gemini вживую при { run: true } и заданном GEMINI_API_KEY, сохраняя вывод в interview-prep/{slug}.md; (b) перечисляет каждый сохранённый deep-research файл как кликабельные карточки с относительными timestamp; (c) рендерит результаты как Markdown с действиями 📋 Copy / ⬇ Download .md / ↗ Open in tab на результат. Новая REST-поверхность: GET /api/interview-prep, GET /api/interview-prep/:name, DELETE /api/interview-prep/:name. 7 новых тестов.
  • feat(cv): generate + download PDF in browser, with PDF archive — новая кнопка 📄 Generate PDF на странице CV стримит /api/stream/pdf в модалку-консоль. На ошибках ERR_MODULE_NOT_FOUND / playwright проявляет копируемую команду бутстрапа. Новый раздел «Generated PDFs» автозагружается после каждого успешного запуска, перечисляя каждый output/*.pdf с кнопками ↗ Open и ⬇ Download. Новая REST-поверхность: GET /api/output/pdfs, GET /api/output/pdfs/:name. 6 новых тестов.
  • feat(api): POST /api/tracker — append rows from the UI (FIX-H8) — добавление канонической строки в data/applications.md из браузера. Валидирует company + role, нормализует статус против templates/states.yml, автоматически увеличивает заполненный нулями #, дедуплицирует по company+role (case-insensitive), экранирует пайпы в заметках, чтобы markdown-таблица не разломилась. Бутстрапит таблицу, если файл пуст. 6 новых тестов.
  • feat(api): DELETE /api/jds/:name (FIX-H4) — удаление сохранённых JD без shell-out. Символы path-traversal снимаются до любого касания ФС; параметр должен заканчиваться на .txt. 5 новых тестов, включая отказ ../../etc/passwd.
  • feat(api): POST /api/evaluate/test-gemini (FIX-H7) — smoke-эндпоинт, запускающий 50-символьный фиктивный JD через gemini-eval.mjs, чтобы пользователь мог проверить рабочий API-ключ, не сидя через реальную оценку. Возвращает { ok, code, sampleLength, sample }.

🐛 Исправления

  • fix(router): catch-all 404 view + i18n coverage guard (FIX-C7) — неизвестные хеш-маршруты ранее тихо проваливались на дашборд, маскируя опечатки и сломанные закладки. Теперь #/totally-random-xyz рендерит выделенную страницу 404, цитирующую плохой путь обратно и ведущую ссылкой на дашборд. View 404 регистрируется внутри самого IIFE роутера, так что не может столкнуться с пользовательским маршрутом. Новый tests/i18n-coverage.test.mjs прогоняет i18n.js внутри vm.Context с stub window, выставляет приватный DICT и утверждает, что каждый из 173+ ключей × 8 локалей заполнен и непуст. 4 новых теста роутера.
  • fix(router): alias #/profile → settings (FIX-C2) — внутреннее имя маршрута — settings (с nav.settings, рендерящим «Profile»), но внешние ссылки и мышечная память идут в #/profile. Теперь оба адреса достигают одного view, и пункт навигации сайдбара подсвечивается в обоих случаях. 2 новых теста.
  • fix(health): unify Health/Doctor + flag template profiles (FIX-C6 + FIX-H6) — Health и Doctor были двумя разными источниками правды. Теперь /api/health отдаёт всё, что сообщает Doctor (родительские deps, Playwright, директории, profile-customized, HH_USER_AGENT). Проверка Profile customized обнаруживает заглушки-имена (Jane Smith, Alex Doe, John Doe, Your Name, Test User) и явные ошибки парсинга YAML. 4 новых теста.
  • fix(scan): warn on query↔negative collisions in RU config (FIX-H3) — когда portals.yml идёт с "PHP" в title_filter.negative, в то время как запросы целят в Senior PHP, каждое совпадение фильтруется и пользователь видит ноль результатов. loadConfig() теперь вычисляет массив warnings; runRuScan() шлёт каждое предупреждение строкой stderr SSE до запуска сканирования. 2 теста проверяют, что отгружаемые дефолты остаются PHP-friendly из коробки.
  • fix(scan): warn when HH_USER_AGENT is unset (FIX-H1) — страница /scan пробит /api/health и показывает жёлтую предупредительную карточку над строкой действий, когда HH_USER_AGENT пуст, чтобы пользователи знали о 403 от hh.ru до нажатия RU scan.
  • fix(api): warn when POST /api/jds slug had unsafe chars stripped (FIX-M2) — нормализация slug, снимающая опасные символы, теперь возвращает поле warning; чистая чистка case/whitespace остаётся беззвучной. Пустой результат после санитайзинга возвращает 400.
  • fix(ui): clear global search on route change + button spinners (FIX-M4 + FIX-L1) — глобальный поисковый инпут очищается на hashchange (с защитой для активного ввода). Новый хелпер UI.withSpinner(button, fn) подключает состояние загрузки, ARIA и защиту от двойного клика к каждому асинхронному клику кнопки. Уже принят на кнопках Doctor / Verify / sync-check / Save CV / Normalize / Dedup / Merge.
  • fix(ui): make sidebar scrollable so 18 nav items always reach the footer — сгруппированный сайдбар из FIX-C8 переполнял короткие viewport; нижние пункты (Activity / Health) обрезались. .sidebar теперь имеет overflow-y: auto с тонкими кастомными скроллбарами (WebKit + Firefox). Подвал остаётся прижатым через существующий margin-top: auto.
  • fix(ui): empty modal-title placeholder (FIX-H9) — захардкоженная английская строка "Title" в index.html убрана, закрывая короткое окно гонки, где она была видна при открытии модалки.

🌐 i18n

  • 173+ ключа перевода × 8 поддерживаемых локалей (en, es, pt-BR, ko, ja, ru, zh-CN, zh-TW). Новые ключи добавлены во всех локалях для: страница 404, журнал activity, deep research, поток PDF, предупреждения безопасности, мутация tracker, переименование apply. Покрытие теперь обеспечивается tests/i18n-coverage.test.mjs — каждый ключ должен иметь непустое значение в каждой поддерживаемой локали, иначе CI падает.

⚙️ DevOps

  • Счётчик тестов: 73 → 201 (+128 тестов в 23 файлах). Единственный оставшийся падающий тест (runEnScan: dry-run end-to-end across multiple sources) — pre-existing flaky тест, зависящий от живых API-ответов Greenhouse/Ashby/Lever.
  • Comprehensive Playwright e2e (tests/e2e-comprehensive.mjs, 23 шага): проходит полный пользовательский путь — сохранение CV → preview → генерация PDF → все 7 новых режимов → фильтры tracker → журнал activity → 404 → ESC на модалке → прокрутка сайдбара → фокус Ctrl-K → очистка поиска → alias profile → сохранение языка.
  • GitHub Actions (.github/workflows/):
    • ci.yml — unit + integration тесты на матрице Node 18/20/22 плюс шлюз покрытия i18n (каждый ключ × 8 локалей должен быть непустым), плюс полный Playwright e2e на каждом PR.
    • ai-review.yml — AI-ревью Claude Code на каждом PR. Мерж остаётся за мейнтейнерами; Claude только предлагает. Пропуск через метку skip-ai-review.
    • release.yml — автопубликация GitHub Release при пуше тэга v*.*.*; release notes нарезаются из CHANGELOG.md, чтобы все 8 языковых вариантов оставались каноническим источником.
  • CSP-friendly UI: все inline-обработчики onclick удалены из index.html и router.js. Строгая политика script-src 'self' теперь применима без поломки фичей.

📦 Новые REST-эндпоинты

Метод Путь Назначение
GET /api/activity Список событий действий пользователя, новейшие первыми
GET /api/interview-prep Список сохранённых файлов Deep Research
GET /api/interview-prep/:name Чтение одного файла Deep Research
DELETE /api/interview-prep/:name Удаление файла Deep Research
GET /api/output/pdfs Список сгенерированных PDF
GET /api/output/pdfs/:name Стрим PDF как attachment
POST /api/tracker Добавление строки в applications.md
DELETE /api/jds/:name Удаление сохранённого JD
POST /api/evaluate/test-gemini Smoke-проверка ключа Gemini API
POST /api/mode/:slug Универсальный построитель промптов для 7 новых режимов (project / training / followup / batch / contacto / interview-prep / patterns)

[1.6.0] — 2026-05-02

Первоначальный публичный релиз веб-UI. Инвентарь возможностей этого baseline — в README.md.


career-ops-ui v1.267.1 · page 13/13 · mirror of the project changelog for cvstart.org