Журнал изменений
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.jsonweb-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*=и URIjavascript:/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.mjsHEAD-пробит каждый включённый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с stubwindow, выставляет приватный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