본문으로 건너뛰기

내장 리소스 동기화 프로세스

resources/design-studio-builtin/ 아래의 600개 이상 파일(Skill / Design System / Craft)은 git에 포함되지 않습니다. 이 파일들은 commit pin을 기준으로 upstream nexu-io/open-design repository에서 가져오며, 첫 시작 시 materializer가 사용자 디렉터리에 구체화합니다.

이 방식을 사용하는 이유: upstream 콘텐츠는 규모가 크고 자주 업데이트됩니다. 모든 파일을 commit하면 elftia의 git 기록에 상당한 노이즈가 생기고 merge conflict가 자주 발생합니다. commit을 pin하면 재현 가능한 build를 보장하면서 "내장 asset 업그레이드"를 한 번의 lock file bump로 처리할 수 있습니다.

:::warning 큐레이션 예외: prompt-templates/는 절대 sync하지 않음(2026-06-04)

prompt-templates/는 제자리에서 유지 관리되는 수동 큐레이션 subset이며 sync에 참여하지 않습니다. upstream 전체 컬렉션에는 실제 인물 얼굴(예: Elon Musk 이미지를 생성하는 template) 같은 콘텐츠 위험 항목이 포함되어 있고, 이 JSON들은 모든 channel release에 익명으로 보이는 "community templates"로 packaging됩니다. 따라서:

  • lock의 subdirs에는 prompt-templates포함되지 않습니다. sync script에는 다시 추가되면 hard fail하는 NEVER_SYNC_SUBDIRS safeguard가 있습니다(resync는 먼저 clearSubdirs로 큐레이션된 set을 삭제한 뒤 필터링되지 않은 upstream 전체 set을 주입합니다).
  • upstream에서 새 template을 도입하려면: JSON을 하나씩 수동으로 복사하고 검토합니다(실제 인물 얼굴 / brand IP / NSFW). curation log를 업데이트하세요 — resources/design-studio-builtin/prompt-templates/README.md를 참고하세요.
  • 이 디렉터리는 예외적으로 git에 포함됩니다(.gitignoreresources/design-studio-builtin/*에 대해 !…/prompt-templates/로 반전됨). fresh clone에서도 network access 없이 큐레이션된 set을 즉시 재현할 수 있습니다.

:::

관련 파일

PathPurposeIn git?
resources/design-studio-builtin.lock.jsonPin file — { repo, commit, subdirs }✅ git에 포함되는 유일한 파일
resources/design-studio-builtin/disk에 동기화된 콘텐츠(600개 이상 파일)❌ gitignored
resources/design-studio-builtin/prompt-templates/큐레이션 예외 — 수동 큐레이션 subset, 절대 sync하지 않음(위 warning 참고), README.md curation log 포함✅ git의 예외 항목(gitignore 반전)
resources/design-studio-builtin/.synced.jsonMarker — 마지막으로 성공한 sync commit을 기록하고, hit 시 skip❌(ignored directory 내부)
resources/design-studio-builtin/NOTICE자동 생성되는 third-party attribution
scripts/sync-design-studio.mjsSync script(lock 기준 pull)
scripts/update-design-studio.mjs자동 upgrade script — upstream HEAD pull + lock bump + sync + FLAG_FILENAME bump를 한 command로 수행
packages/desktop/app/main/services/content/workspace/design/builtin/index.ts첫 시작 materializer + FLAG_FILENAME

Trigger 시점

TriggerCommand / HookMode
npm installpostinstall hook--soft(network failure가 install을 막지 않음)
npm run setup / setup:native수동 initialization--soft
npm run build:electron / build:official / build:steamelectron-vite buildelectron-builder 사이Strict mode(failure 시 build 실패)
npm run sync:design수동 실행Strict mode
npm run sync:design -- --force수동 실행(force refetch)Strict mode + marker skip
npm run sync:design:updateupstream HEAD로 one-click upgrade(권장)아래 "Upgrade to upstream latest commit" 참고

marker가 hit되면({repo, commit, subdirs}의 세 필드가 lock과 정확히 일치) 바로 skip하고 [sync:design] already synced @ <repo>@<sha7> — skipping (use --force)를 출력합니다. 이는 정상 상태이므로 error로 오해하지 마세요.

Sync Script가 하는 일

scripts/sync-design-studio.mjs의 main flow:

  1. resources/design-studio-builtin.lock.json을 읽고 schema를 validate합니다(repo / commit / subdirs[] 필수)
  2. local .synced.json marker를 비교합니다 — 세 필드가 모두 일치하고 --force가 없으면 skip하고 exit합니다
  3. tarball을 pull합니다:
    • Public repo → https://codeload.github.com/<repo>/tar.gz/<commit>
    • GITHUB_TOKEN / GH_TOKEN env vars 설정 후 → https://api.github.com/repos/<repo>/tarball/<commit>(private repos는 이 방식을 사용)
  4. decompress로 extract하고, strip: 1로 외부 package name directory를 제거하며, filter로 lock에 나열된 subdirs만 유지합니다
  5. extract 전에 fs.rm(subdir, { recursive: true, force: true })로 각 target subdir을 정리해 upstream에서 삭제된 뒤 stale file이 남지 않도록 합니다
  6. marker .synced.json을 씁니다
  7. NOTICE를 씁니다 — third-party attribution(Apache-2.0)을 자동 생성합니다

decompress 4.x cross-platform gotcha(깨뜨리지 말 것)

stripfilter보다 먼저 적용되지만, Windows에서는 filter가 받는 path가 이미 backslash로 normalize되어 있습니다. script의 filter는 file.path.split(/[\\/]/)[0]를 사용해 두 separator를 모두 자릅니다. /만 자르면 Linux에서는 600개 이상 파일이 모두 통과하지만 Windows에서는 0개가 통과하며, failure mode가 silent입니다(빈 directory만 출력). 디버깅이 어렵습니다. 이 섹션을 수정할 때는 양쪽 platform에서 모두 validate하세요.

Upgrade to Upstream Latest Commit

권장 경로: One-Click Automation

npm run sync:design:update

scripts/update-design-studio.mjs는 "upgrade" process의 세 단계를 하나의 action으로 묶습니다:

  1. GET https://api.github.com/repos/<repo>/commits/HEAD — upstream default branch HEAD의 SHA를 parse합니다
  2. lock의 commit과 같으면 → already up to date — nothing to do를 log하고 0으로 exit합니다
  3. 그렇지 않으면 lock file의 commit field를 regex replace하고(원래 format 유지), sync-design-studio.mjs를 spawn해 pull 및 extract합니다. sync가 실패하면 lock을 자동 rollback하므로, 동기화되지 않은 commit을 가리키는 dirty pointer를 남기지 않습니다
  4. upstream 새 commit의 short-sha(앞 7자)를 사용해 services/content/workspace/design/builtin/index.tsFLAG_FILENAME constant를 .elftia-builtin-materialized-<short-sha>.json으로 regex-rewrite합니다 — 기존 user의 <userData>에 있는 old flag filename이 더 이상 match되지 않아, 다음 startup에서 새 content를 다시 materialize합니다
  5. git diff / git commit hint를 출력합니다

commit 시 변경되는 파일은 lock.jsonbuiltin/index.ts 두 개뿐입니다.

Flag naming convention: FLAG_FILENAME에는 upstream commit의 short-sha가 포함됩니다(예: .elftia-builtin-materialized-83ddf76.json). 이전에는 integer version number v1/v2/...를 사용했지만 short-sha format으로 migration되었습니다 — commit을 바꾸면 이름도 자연스럽게 바뀌므로 manual version number를 유지할 필요가 없습니다.

Dry-run:

npm run sync:design:update -- --dry-run

SHA 차이만 출력하고 어떤 file도 건드리지 않습니다. CI에서 "새 upstream version이 있는지" 감지하는 데 사용할 수 있습니다.

Auth: GITHUB_TOKEN이 설정되어 있지 않으면 GitHub public API를 사용하며 unauthenticated 제한은 60 req/hr입니다. CI에서는 GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}를 설정해 5000 req/hr로 올리세요.

대체 경로: 수동 세 단계

auto script가 깨졌거나 non-default branch / historical commit으로 pin하려는 경우에만 사용하세요:

  1. Change lock: resources/design-studio-builtin.lock.jsoncommit field에 전체 40자 SHA를 씁니다(short hash가 아님 — script는 exact string comparison을 수행합니다)

  2. Run sync:

    npm run sync:design

    commit이 marker와 match하지 않으면 자동으로 refetch하므로 --force가 필요 없습니다. --force는 marker와 lock이 match하지만 local directory가 손상되어 force refetch하려는 경우에만 사용합니다.

  3. Bump FLAG_FILENAME: packages/desktop/app/main/services/content/workspace/design/builtin/index.ts를 edit하고 constant를 새 commit의 short-sha로 바꿉니다:

    const FLAG_FILENAME = '.elftia-builtin-materialized-<new short-sha>.json';

    이 단계는 critical합니다 — 기존 elftia user의 <userData>/elftia/.elftia-builtin-materialized-<old>.json flag가 더 이상 match되지 않아야 다음 startup에서 re-materialize가 trigger됩니다. bump하지 않으면 fresh install만 새 content를 받고, 기존 user는 계속 old content를 보게 됩니다.

Verification

# Delete local marker to force rerun verification
rm resources/design-studio-builtin/.synced.json
npm run sync:design

# See new file count / date in NOTICE
cat resources/design-studio-builtin/.synced.json
cat resources/design-studio-builtin/NOTICE | head -10

일반 작업

ScenarioCommand
upstream latest commit으로 upgrade(flag bump 포함)npm run sync:design:update
새 upstream version이 있는지 확인(file 수정 없음)npm run sync:design:update -- --dry-run
local이 sync되었는지 / 어떤 commit에 sync되었는지 확인cat resources/design-studio-builtin/.synced.json
한 번 force refetch(directory 손상 / incomplete extract 의심)npm run sync:design -- --force
upstream main latest SHA 조회gh api repos/nexu-io/open-design/commits/main --jq .sha
sync를 임시로 skip(CI debugging)lock file 삭제 → script가 "lock file missing, skipping"을 log하고 exit
upstream을 private fork로 교체lock의 repo field 변경, GITHUB_TOKEN export 후 sync 실행

Troubleshooting

SymptomCause / Solution
[sync:design] already synced @ ... — skipping (use --force)정상, marker hit. refetch하려면 -- --force 추가
HTTP 404 ... (private repo? set GITHUB_TOKEN)lock의 repo가 private이거나 commit이 존재하지 않습니다. GITHUB_TOKEN을 export하거나 SHA를 verify하세요
no files matched subdirs [...]subdirs의 directory가 해당 commit에 존재하지 않습니다(upstream rename/refactor). 해당 commit의 directory layout을 확인하세요
Windows에서 sync는 성공하지만 product directory가 비어 있음decompress filter path separator 문제. script의 split(/[\\/]/)split('/')로 바꾸지 마세요
User가 upgrade했지만 새 built-in Skill이 보이지 않음FLAG_FILENAME bump를 잊었고 old user의 old flag가 남아 있음 → materialize skip. sync:design:update는 자동 bump하며, lock을 수동 변경했다면 step 3을 잊지 마세요
sync:design:updateHTTP 403 ... rate-limited?를 report함GitHub unauthenticated API는 60 req/hr로 제한됩니다. GITHUB_TOKEN을 export하고 다시 실행하세요
sync:design:update sync는 성공했지만 flag가 바뀌지 않음services/content/workspace/design/builtin/index.tsFLAG_FILENAME이 rename되었는지 확인하세요 — script는 regex const FLAG_FILENAME = '\.elftia-builtin-materialized-...\.json';로 locate하므로 rename 후에는 실패합니다
CI에서 tarball pull이 너무 느림 / bandwidth 초과actions/cache를 사용해 resources/design-studio-builtin/를 lock file hash key로 cache하고, hit 시 download를 skip하세요

관련 파일 / 모듈