Процесс синхронизации встроенных ресурсов
Более 600 файлов в resources/design-studio-builtin/ (Skill / Design System / Craft) не хранятся в git. Они извлекаются из upstream-репозитория nexu-io/open-design по зафиксированному коммиту и размещаются в пользовательском каталоге материализатором при первом запуске.
Причина такого подхода: upstream-контент обширен и обновляется часто. Коммит всего этого создавал бы значительный шум в истории git Elftia и частые конфликты слияния. Фиксация коммита обеспечивает воспроизводимые сборки, превращая «обновление встроенных ресурсов» в однократное обновление lock-файла.
:::warning Исключение при курировании: prompt-templates/ никогда не синхронизируется (2026-06-04)
prompt-templates/ — это вручную отобранное подмножество, поддерживаемое на месте и не участвующее в синхронизации: полная upstream-коллекция содержит материалы с риском, например реальные лица людей (напр., шаблоны для генерации изображений Илона Маска), и эти JSON-файлы будут упакованы как анонимно видимые «шаблоны сообщества» во всех релизах каналов. Поэтому:
subdirsв lock-файле не включаетprompt-templates; в скрипте синхронизации есть защитаNEVER_SYNC_SUBDIRS, которая жёстко завершится с ошибкой, если этот каталог будет добавлен обратно (повторная синхронизация сначала удалит отобранный набор черезclearSubdirs, затем внедрит неотфильтрованный полный upstream-набор).- Чтобы добавить новые шаблоны из upstream: копируйте JSON по одному вручную и проверяйте (реальные лица / бренды / NSFW), обновляйте журнал курирования — см.
resources/design-studio-builtin/prompt-templates/README.md. - Этот каталог входит в git в виде исключения (
.gitignoreинвертирован дляresources/design-studio-builtin/*с!…/prompt-templates/); свежий клон немедленно воспроизводит отобранный набор без доступа к сети.
:::
Задействованные файлы
| Путь | Назначение | В git? |
|---|---|---|
resources/design-studio-builtin.lock.json | Файл фиксации — { repo, commit, subdirs } | ✅ Единственный файл в git |
resources/design-studio-builtin/ | Синхронизированный контент на диске (600+ файлов) | ❌ gitignored |
resources/design-studio-builtin/prompt-templates/ | Исключение при курировании — вручную отобранное подмножество, никогда не синхронизируется (см. предупреждение выше), включает журнал курирования README.md | ✅ Исключение в git (gitignore инвертирован) |
resources/design-studio-builtin/.synced.json | Маркер — содержит коммит последней успешной синхронизации, при совпадении — пропуск | ❌ (внутри игнорируемого каталога) |
resources/design-studio-builtin/NOTICE | Автосгенерированные данные атрибуции третьих сторон | ❌ |
scripts/sync-design-studio.mjs | Скрипт синхронизации (загрузка по lock) | ✅ |
scripts/update-design-studio.mjs | Скрипт автообновления — загрузка HEAD upstream + обновление lock + синхронизация + обновление FLAG_FILENAME одной командой | ✅ |
packages/desktop/app/main/services/content/workspace/design/builtin/index.ts | Материализатор при первом запуске + FLAG_FILENAME | ✅ |
Моменты запуска
| Триггер | Команда / Хук | Режим |
|---|---|---|
npm install | Хук postinstall | --soft (сетевой сбой не блокирует установку) |
npm run setup / setup:native | Ручная инициализация | --soft |
npm run build:electron / build:official / build:steam | Между electron-vite build и electron-builder | Строгий режим (сбой блокирует сборку) |
npm run sync:design | Ручное выполнение | Строгий режим |
npm run sync:design -- --force | Ручное выполнение (принудительная повторная загрузка) | Строгий режим + пропуск маркера |
npm run sync:design:update | Обновление до HEAD upstream одним кликом (рекомендуется) | См. «Обновление до последнего upstream-коммита» ниже |
Когда маркер совпадает (все три поля {repo, commit, subdirs} в точности соответствуют lock), синхронизация пропускается и выводится [sync:design] already synced @ <repo>@<sha7> — skipping (use --force). Это нормальное состояние, не путайте с ошибкой.
Что делает скрипт синхронизации
Основной поток scripts/sync-design-studio.mjs:
- Читает
resources/design-studio-builtin.lock.json, проверяет схему (должны присутствоватьrepo/commit/subdirs[]) - Сравнивает с локальным маркером
.synced.json— если все три поля совпадают и нет--force, пропускает и завершает работу - Загружает tarball:
- Публичный репозиторий →
https://codeload.github.com/<repo>/tar.gz/<commit> - После установки переменных окружения
GITHUB_TOKEN/GH_TOKEN→https://api.github.com/repos/<repo>/tarball/<commit>(для приватных репозиториев)
- Публичный репозиторий →
- Распаковывает с помощью
decompress,strip: 1удаляет внешнюю директорию с именем пакета,filterоставляет толькоsubdirsиз lock - Перед распаковкой
fs.rm(subdir, { recursive: true, force: true })очищает каждый целевой подкаталог во избежание устаревших файлов после удалений в upstream - Записывает маркер
.synced.json - Записывает
NOTICE— автогенерирует данные атрибуции третьих сторон (Apache-2.0)
Кросс-платформенная особенность decompress 4.x (не ломать)
strip применяется до filter, но на Windows пути, получаемые filter, уже нормализованы в обратные слэши. Фильтр в скрипте использует file.path.split(/[\\/]/)[0] для разделения по обоим разделителям — использование только / пропускает все 600+ файлов на Linux, но 0 на Windows, причём режим сбоя тихий (выводит только пустой каталог), что делает отладку болезненной. При изменении этого раздела проверяйте на обеих платформах.
Обновление до последнего upstream-коммита
Рекомендуемый путь: автоматизация одной командой
npm run sync:design:update
scripts/update-design-studio.mjs объединяет три шага процесса «обновления» в одно действие:
GET https://api.github.com/repos/<repo>/commits/HEAD— парсит SHA HEAD ветки по умолчанию upstream- Если совпадает с полем
commitв lock → выводитalready up to date — nothing to doи завершает работу с кодом 0 - Иначе заменяет по регулярному выражению поле
commitв lock-файле (сохраняя исходный формат), вызываетsync-design-studio.mjsдля загрузки и распаковки; при сбое синхронизации автоматически откатывает lock, не оставляя грязного указателя на несинхронизированный коммит - Использует короткий SHA нового коммита upstream (первые 7 символов) для обновления константы
FLAG_FILENAMEвservices/content/workspace/design/builtin/index.tsна.elftia-builtin-materialized-<short-sha>.json— старое имя флага у существующих пользователей в<userData>больше не совпадает, следующий запуск инициирует повторную материализацию нового контента - Выводит подсказки
git diff/git commit
При коммите изменяются только эти два файла: lock.json и builtin/index.ts.
Соглашение об именовании флагов: FLAG_FILENAME содержит короткий SHA коммита upstream (напр., .elftia-builtin-materialized-83ddf76.json). Раньше использовались целочисленные номера версий v1/v2/..., теперь мигрировано на формат короткого SHA — смена коммита автоматически меняет имя, не нужно поддерживать ручные номера версий.
Пробный запуск:
npm run sync:design:update -- --dry-run
Только выводит разницу SHA, не трогает файлы. Можно использовать в CI для обнаружения «доступна ли новая upstream-версия».
Авторизация: Когда GITHUB_TOKEN не установлен, используется публичный API GitHub с ограничением 60 запросов/час без аутентификации. В CI установите GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} для 5000 запросов/час.
Альтернативный путь: три шага вручную
Используйте только если автоскрипт сломан или нужно зафиксироваться на ветке не по умолчанию / историческом коммите:
-
Измените lock: Введите полный 40-символьный SHA в поле
commitфайлаresources/design-studio-builtin.lock.json(не короткий хеш — скрипт выполняет точное строковое сравнение) -
Запустите синхронизацию:
npm run sync:designКогда
commitне совпадает с маркером, автоматически выполняется повторная загрузка,--forceне нужен.--forceнужен только когда маркер и lock совпадают, но локальный каталог повреждён и нужна принудительная повторная загрузка. -
Обновите
FLAG_FILENAME: Отредактируйтеpackages/desktop/app/main/services/content/workspace/design/builtin/index.ts, измените константу на короткий SHA нового коммита:const FLAG_FILENAME = '.elftia-builtin-materialized-<new short-sha>.json';Этот шаг критически важен — старый флаг существующих пользователей Elftia в
<userData>/elftia/.elftia-builtin-materialized-<old>.jsonбольше не совпадает, следующий запуск инициирует повторную материализацию. Без обновления только свежие установки получат новый контент, существующие пользователи продолжат видеть старый.
Проверка
# Удалить локальный маркер для принудительной повторной проверки
rm resources/design-studio-builtin/.synced.json
npm run sync:design
# Просмотр нового количества файлов / даты в NOTICE
cat resources/design-studio-builtin/.synced.json
cat resources/design-studio-builtin/NOTICE | head -10
Типовые операции
| Сценарий | Команда |
|---|---|
| Обновление до последнего upstream-коммита (включая обновление флага) | npm run sync:design:update |
| Проверить наличие новой upstream-версии (без изменения файлов) | npm run sync:design:update -- --dry-run |
| Узнать статус синхронизации / к какому коммиту синхронизировано | cat resources/design-studio-builtin/.synced.json |
| Принудительная повторная загрузка (каталог повреждён / подозрение на неполную распаковку) | npm run sync:design -- --force |
| Получить последний SHA ветки main upstream | gh api repos/nexu-io/open-design/commits/main --jq .sha |
| Временно пропустить синхронизацию (отладка CI) | Удалить lock-файл → скрипт выведет «lock file missing, skipping» и завершит работу |
| Заменить upstream приватным форком | Изменить поле repo в lock, экспортировать GITHUB_TOKEN и запустить синхронизацию |
Устранение неполадок
| Симптом | Причина / Решение |
|---|---|
[sync:design] already synced @ ... — skipping (use --force) | Нормально, маркер совпал. Добавьте -- --force для повторной загрузки |
HTTP 404 ... (private repo? set GITHUB_TOKEN) | Репозиторий в lock приватный или коммит не существует; экспортируйте GITHUB_TOKEN или проверьте SHA |
no files matched subdirs [...] | Каталог в subdirs не существует в этом коммите (upstream переименовал/рефакторил); проверьте структуру каталогов в этом коммите |
| На Windows синхронизация проходит успешно, но каталог продукта пуст | Проблема с разделителем путей в фильтре decompress. Не меняйте split(/[\\/]/) в скрипте на split('/') |
| Пользователь обновился, но не видит новые встроенные Skill | Забыли обновить FLAG_FILENAME, старый флаг пользователя всё ещё там → пропуск материализации. sync:design:update обновляет автоматически; при ручном изменении lock не забудьте шаг 3 |
sync:design:update сообщает HTTP 403 ... rate-limited? | Неаутентифицированный API GitHub ограничен 60 запросами/час. Экспортируйте GITHUB_TOKEN и повторите |
sync:design:update синхронизируется успешно, но флаг не изменился | Проверьте, не был ли переименован FLAG_FILENAME в services/content/workspace/design/builtin/index.ts — скрипт использует регулярное выражение const FLAG_FILENAME = '\.elftia-builtin-materialized-...\.json'; для поиска, после переименования завершится с ошибкой |
| CI загружает tarball слишком медленно / превышает пропускную способность | Используйте actions/cache для кеширования resources/design-studio-builtin/ с хешем lock-файла в качестве ключа; при совпадении пропускайте загрузку |
Связанные файлы / Модули
scripts/sync-design-studio.mjs— загрузка по lock + распаковкаscripts/update-design-studio.mjs— обновление до HEAD upstream одним кликомresources/design-studio-builtin.lock.jsonpackages/desktop/app/main/services/content/workspace/design/builtin/index.ts—FLAG_FILENAME,materializeBuiltinResources,isBuiltinMaterialized- Обзор архитектуры Design Studio