組み込みリソース同期プロセス
resources/design-studio-builtin/ 配下の 600 件以上のファイル (Skill / Design System / Craft) は git には含まれていません。これらはコミットピンに基づいて upstream の nexu-io/open-design リポジトリから取得され、初回起動時にマテリアライザーによってユーザーディレクトリへ実体化されます。
この方式を採用している理由: upstream のコンテンツは大規模で、頻繁に更新されます。すべてをコミットすると、elftia の git 履歴に大きなノイズが入り、マージコンフリクトも頻発します。コミットをピン留めすることで再現可能なビルドを保証しつつ、「組み込みアセットのアップグレード」をロックファイルの一度きりの更新にできます。
:::warning キュレーション例外: prompt-templates/ は同期しない (2026-06-04)
prompt-templates/ は、その場で管理される手動キュレーション済みサブセットであり、同期には参加しません。upstream の完全なコレクションには、実在人物の顔(例: Elon Musk の画像を生成するテンプレート)のようなコンテンツリスクのあるエントリが含まれており、これらの JSON はすべてのチャンネルリリースで匿名閲覧可能な「コミュニティテンプレート」としてパッケージ化されてしまうためです。したがって:
- ロックの
subdirsにはprompt-templatesが含まれていません。同期スクリプトにはNEVER_SYNC_SUBDIRSセーフガードがあり、これを戻すとハードフェイルします(再同期はまずclearSubdirsでキュレーション済みセットを削除し、その後フィルターされていない upstream の完全セットを注入します)。 - upstream から新しいテンプレートを導入する場合: JSON を 1 つずつ手動でコピーしてレビューし(実在人物の顔 / ブランド IP / NSFW)、キュレーションログを更新します —
resources/design-studio-builtin/prompt-templates/README.mdを参照してください。 - このディレクトリは例外的に git に入ります(
.gitignoreはresources/design-studio-builtin/*に対して!…/prompt-templates/で反転されています)。新規 clone でもネットワークアクセスなしでキュレーション済みセットをすぐに再現できます。
:::
関連ファイル
| パス | 目的 | 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 | 同期スクリプト(ロックに基づいて取得) | ✅ |
scripts/update-design-studio.mjs | 自動アップグレードスクリプト — upstream HEAD の取得 + ロック更新 + 同期 + FLAG_FILENAME 更新を 1 コマンドで実行 | ✅ |
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 | upstream HEAD へのワンクリックアップグレード(推奨) | 下記「upstream 最新コミットへのアップグレード」を参照 |
マーカーがヒットした場合({repo, commit, subdirs} の 3 フィールドすべてがロックと完全一致)、そのままスキップし、[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マーカーと比較 — 3 フィールドすべてが一致し、--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のみを残す- 展開前に
fs.rm(subdir, { recursive: true, force: true })で各ターゲットサブディレクトリをクリーンにし、upstream で削除された後の古いファイルが残らないようにする - マーカー
.synced.jsonを書き込む NOTICEを書き込む — サードパーティ帰属表示を自動生成(Apache-2.0)
decompress 4.x のクロスプラットフォーム注意点(壊さないこと)
strip は filter より前に適用されますが、Windows では filter が受け取るパスはすでにバックスラッシュに正規化されています。スクリプトの filter は file.path.split(/[\\/]/)[0] を使い、両方の区切り文字に対応しています — / だけで分割すると Linux では 600 件以上のファイルがすべて通りますが、Windows では 0 件になり、失敗モードは無音(空のディレクトリだけが出力される)なのでデバッグが厄介です。このセクションを変更する場合は、両方のプラットフォームで検証してください。
upstream 最新コミットへのアップグレード
推奨パス: ワンクリック自動化
npm run sync:design:update
scripts/update-design-studio.mjs は、「アップグレード」プロセスの 3 ステップを 1 つのアクションにまとめます:
GET https://api.github.com/repos/<repo>/commits/HEAD— upstream デフォルトブランチ HEAD の SHA を解析- ロック内の
commitと同じ場合 →already up to date — nothing to doをログ出力して exit 0 - 異なる場合は、ロックファイルの
commitフィールドを正規表現で置換(元のフォーマットを保持)し、sync-design-studio.mjsを spawn して取得・展開します。同期が失敗した場合は自動的にロックをロールバックし、未同期コミットへの汚れたポインターを残しません - upstream の新しいコミットの short-sha(先頭 7 文字)を使い、
services/content/workspace/design/builtin/index.tsのFLAG_FILENAME定数を.elftia-builtin-materialized-<short-sha>.jsonに正規表現で書き換えます — 既存ユーザーの<userData>内にある古いフラグファイル名とは一致しなくなり、次回起動時に新しいコンテンツが再マテリアライズされます git diff/git commitのヒントを表示
コミット時に変更されるファイルは lock.json と builtin/index.ts の 2 つだけです。
フラグ命名規則: FLAG_FILENAME には upstream コミットの short-sha(例: .elftia-builtin-materialized-83ddf76.json)を埋め込みます。以前は整数のバージョン番号 v1/v2/... でしたが、short-sha 形式に移行しました — コミットを切り替えると名前も自然に変わるため、手動のバージョン番号を管理する必要はありません。
Dry-run:
npm run sync:design:update -- --dry-run
SHA の差分だけを表示し、ファイルには一切触れません。CI で「新しい upstream バージョンが利用可能かどうか」を検出するために使用できます。
Auth: GITHUB_TOKEN が設定されていない場合、GitHub パブリック API は未認証で 60 req/hr に制限されます。CI では GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} を設定して 5000 req/hr にしてください。
代替パス: 手動 3 ステップ
自動スクリプトが壊れている場合、またはデフォルトブランチ以外 / 過去のコミットにピン留めしたい場合にのみ使用してください:
-
ロックを変更:
resources/design-studio-builtin.lock.jsonのcommitフィールドに 40 文字の完全な SHA を書き込む(短縮ハッシュではありません — スクリプトは厳密な文字列比較を行います) -
同期を実行:
npm run sync:designcommitがマーカーと一致しない場合は自動的に再取得されるため、--forceは不要です。--forceは、マーカーとロックが一致しているがローカルディレクトリが破損しており、強制的に再取得したい場合にのみ使います。 -
FLAG_FILENAMEを更新:packages/desktop/app/main/services/content/workspace/design/builtin/index.tsを編集し、定数を新しいコミットの short-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 |
| 1 回だけ強制再取得(ディレクトリ破損 / 不完全な展開の疑い) | npm run sync:design -- --force |
| upstream main の最新 SHA を問い合わせ | gh api repos/nexu-io/open-design/commits/main --jq .sha |
| 一時的に同期をスキップ(CI デバッグ) | ロックファイルを削除 → スクリプトが "lock file missing, skipping" とログ出力して終了 |
| upstream をプライベートフォークに置き換え | ロックの repo フィールドを変更し、GITHUB_TOKEN を export してから同期を実行 |
トラブルシューティング
| 症状 | 原因 / 解決策 |
|---|---|
[sync:design] already synced @ ... — skipping (use --force) | 正常、マーカーがヒットしています。再取得するには -- --force を追加 |
HTTP 404 ... (private repo? set GITHUB_TOKEN) | ロック内のリポジトリがプライベート、またはコミットが存在しません。GITHUB_TOKEN を export するか SHA を確認 |
no files matched subdirs [...] | subdirs のディレクトリがそのコミットに存在しません(upstream でリネーム / リファクタリング済み)。そのコミットのディレクトリ構成を確認 |
| Windows では同期が成功するが製品ディレクトリが空 | decompress filter のパス区切り文字問題。スクリプトの split(/[\\/]/) を split('/') に変更しないでください |
| ユーザーがアップグレードしたが新しい組み込み Skill が見えない | FLAG_FILENAME の更新忘れです。古いユーザーの古いフラグが残っている → マテリアライズをスキップ。sync:design:update は自動更新しますが、手動でロックを変更する場合はステップ 3 を忘れないでください |
sync:design:update が HTTP 403 ... rate-limited? を報告 | GitHub 未認証 API は 60 req/hr に制限されています。GITHUB_TOKEN を export して再実行 |
sync:design:update は同期に成功したがフラグが変わらない | services/content/workspace/design/builtin/index.ts の FLAG_FILENAME がリネームされていないか確認してください — スクリプトは正規表現 const FLAG_FILENAME = '\.elftia-builtin-materialized-...\.json'; で位置を特定するため、リネーム後は失敗します |
| CI の tarball 取得が遅すぎる / 帯域幅を超える | actions/cache を使い、ロックファイルハッシュをキーに resources/design-studio-builtin/ をキャッシュして、ヒット時はダウンロードをスキップ |
関連ファイル / モジュール
scripts/sync-design-studio.mjs— ロックに基づいて取得 + 展開scripts/update-design-studio.mjs— upstream HEAD へのワンクリックアップグレードresources/design-studio-builtin.lock.jsonpackages/desktop/app/main/services/content/workspace/design/builtin/index.ts—FLAG_FILENAME,materializeBuiltinResources,isBuiltinMaterialized- Design Studio アーキテクチャ概要