akari-video 0.1.73 → 0.1.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/package.json +1 -1
- package/src/cli.mjs +9 -1
- package/src/kits.mjs +24 -25
- package/src/store-command.mjs +12 -6
- package/vendor/docs/contract-2026-07-13-asset-library.md +8 -4
- package/vendor/docs/contract-2026-08-02-creator-root-v1.md +6 -2
- package/vendor/docs/contract-2026-09-02-asset-reference-model.md +55 -5
- package/vendor/packages/akari-launcher/README.md +6 -2
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/asset-resolver/README.md +30 -5
- package/vendor/packages/asset-resolver/bin/akari-assets.mjs +13 -4
- package/vendor/packages/asset-resolver/src/bundle.mjs +3 -5
- package/vendor/packages/asset-resolver/src/installed.mjs +39 -29
- package/vendor/packages/asset-resolver/src/library.mjs +24 -35
- package/vendor/packages/asset-resolver/src/resolve.mjs +11 -9
- package/vendor/packages/asset-resolver/src/state.mjs +3 -1
- package/vendor/packages/asset-resolver/test/installed-assets.test.mjs +1 -1
- package/vendor/packages/asset-resolver/test/library-migration.test.mjs +49 -0
- package/vendor/packages/audio-library-setup/README.md +6 -2
- package/vendor/packages/audio-library-setup/bin/beat-grid.mjs +6 -5
- package/vendor/packages/audio-library-setup/bin/declare-helper.mjs +2 -2
- package/vendor/packages/audio-library-setup/bin/fetch-akari-sounds.mjs +5 -4
- package/vendor/packages/audio-library-setup/bin/gallery-helper.mjs +2 -2
- package/vendor/packages/audio-library-setup/bin/register-drop-folder.mjs +3 -2
- package/vendor/packages/audio-library-setup/bin/review-sfx-mapping.mjs +6 -5
- package/vendor/packages/audio-library-setup/bin/suggest-bgm.mjs +7 -6
- package/vendor/packages/audio-library-setup/bin/suggest-sfx.mjs +6 -6
- package/vendor/packages/audio-library-setup/declare-server.mjs +7 -20
- package/vendor/packages/audio-library-setup/gallery-server.mjs +7 -14
- package/vendor/packages/audio-library-setup/shared/library-roots.mjs +33 -0
- package/vendor/packages/audio-library-setup/test/library-location.test.mjs +68 -0
- package/vendor/packages/creator-root/src/index.mjs +295 -1
- package/vendor/packages/creator-root/test/library-migration.test.mjs +229 -0
- package/vendor/packages/edit-lint/src/edit-lint.mjs +4 -4
- package/vendor/packages/edit-lint/src/library-reference.mjs +33 -16
- package/vendor/packages/edit-store/lib/edit-v2-item-write.d.ts +1 -0
- package/vendor/packages/edit-store/lib/edit-v2-item-write.js +15 -0
- package/vendor/packages/edit-store/lib/generation-meta.d.ts +40 -2
- package/vendor/packages/edit-store/lib/generation-meta.js +21 -1
- package/vendor/packages/schemas/fixtures/generation-meta/generating-placeholder.json +61 -0
- package/vendor/packages/schemas/fixtures/generation-meta/planned-next.json +85 -0
- package/vendor/packages/schemas/fixtures/generation-meta/still-next.json +106 -0
- package/vendor/packages/schemas/generation-meta.schema.json +150 -0
- package/vendor/packages/schemas/test/generation-meta.test.mjs +12 -1
- package/vendor/skills/akari/SKILL.md +5 -0
- package/vendor/skills/declare-audio/SKILL.md +8 -3
- package/vendor/skills/declare-audio/after-save.md +7 -2
- package/vendor/skills/declare-audio/launch.md +8 -4
- package/vendor/skills/edit-plan/beat-sync.md +7 -3
- package/vendor/skills/edit-plan/expression-selection.md +5 -2
- package/vendor/skills/edit-plan/report-guide.md +6 -2
- package/vendor/skills/harvest-asset/SKILL.md +7 -2
- package/vendor/skills/setup-audio-library/SKILL.md +7 -2
- package/vendor/skills/setup-audio-library/assisted-fetch.md +6 -2
- package/vendor/skills/setup-audio-library/drop-folder.md +6 -2
- package/vendor/skills/setup-audio-library/first-party.md +6 -2
- package/vendor/skills/setup-audio-library/gallery.md +7 -4
- package/vendor/skills/setup-audio-library/store-unlock.md +5 -1
- package/vendor/skills/setup-library/fetch-and-validate.md +5 -1
- package/vendor/templates/project-default/AGENTS.md +6 -2
- package/vendor/templates/project-default/CLAUDE.md +6 -2
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# akari-launcher(`akari` コマンド)
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
「UI に依存したくない。opencode 単体でも、どんなディレクトリでも始められるように」を
|
|
4
8
|
実現する薄いラッパー CLI。npm パッケージ名は `akari-video`、提供するコマンド名は `akari`
|
|
5
9
|
(npm の `akari` は別プロダクトが取得済みのため。オーナー裁定 2026-07-21 §8-2)。
|
|
@@ -24,7 +28,7 @@ akari
|
|
|
24
28
|
├─ 4. 公式音源ライブラリ(AKARI Sounds)の初回セットアップ(src/sounds-setup.mjs):
|
|
25
29
|
│ 未導入かつ TTY のとき生涯 1 回だけ [Y/n](既定 Yes)を聞き、Yes なら
|
|
26
30
|
│ packages/audio-library-setup/bin/fetch-akari-sounds.mjs で一括ダウンロード。
|
|
27
|
-
│ n は marker
|
|
31
|
+
│ n は marker(<ライブラリの置き場>/audio/.akari-sounds-declined.json)を書いて以後
|
|
28
32
|
│ 聞かない。再入口は `akari sounds`。失敗しても起動は止めない
|
|
29
33
|
│
|
|
30
34
|
└─ 5. 最後に opencode を exec する
|
|
@@ -48,7 +52,7 @@ DL・sha256 検証・適用まで実行。それ以外(npm グローバル / g
|
|
|
48
52
|
`akari store <connect|status|install|download|disconnect>`(AKARI Store 連携。マイページで発行した
|
|
49
53
|
接続トークンを `~/.akari/store-credentials.json`(0600)に保存し、購入済み一覧の確認と
|
|
50
54
|
配布物の取得ができる。`install <productId> [--from <zip>]` は購入パックを展開し、`PACK.json` の
|
|
51
|
-
収載素材を
|
|
55
|
+
収載素材を `<ライブラリの置き場>/installed.json` へ登録する。`--from` は開発・オフライン導入時に手元の
|
|
52
56
|
zip を使う。`src/store-command.mjs`)/
|
|
53
57
|
`akari assets <list|fetch|sync|...>`(素材カタログの一覧・取得・同期。
|
|
54
58
|
`packages/asset-resolver` の CLI への薄い委譲で、カタログ合成・entitlements 判定・
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.74",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/cli.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { detectProjectState } from './project-state.mjs';
|
|
|
7
7
|
import { findClaudeExecutable, findExecutable, findOpencodeExecutable } from './path-lookup.mjs';
|
|
8
8
|
import { loadTaskLabels } from './task-labels.mjs';
|
|
9
9
|
import { describeForceReinstall, describeInstalledVersions, describeIntake, claudeMissingGuidance, opencodeMissingGuidance, describeUpdateCacheFallback, describeUpdateCommand, describeVersionStatus, formatUpdateNotice } from './messages.mjs';
|
|
10
|
-
import { resolveEffectiveProjectRoot } from './first-run.mjs';
|
|
10
|
+
import { defaultLoadCreatorRootModule, resolveEffectiveProjectRoot } from './first-run.mjs';
|
|
11
11
|
import { maybeShowAssetIntroNotice } from './sounds-setup.mjs';
|
|
12
12
|
import {
|
|
13
13
|
checkForUpdateSync,
|
|
@@ -82,6 +82,14 @@ export async function run(args, options = {}) {
|
|
|
82
82
|
projectRoot = await resolveEffectiveProjectRoot({ projectRoot, env, platform, now, log, assets, autoConfirm, options });
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
try {
|
|
86
|
+
const creatorRoot = await defaultLoadCreatorRootModule(assets);
|
|
87
|
+
const migration = await creatorRoot?.migrateAssetLibrary({ env, platform, automatic: true, notify: log });
|
|
88
|
+
for (const failure of migration?.failures ?? []) log(`素材の移行を再試行します: ${failure.message}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
log(`素材の移行を再試行します: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
let state = detectProjectState(projectRoot);
|
|
86
94
|
|
|
87
95
|
if (!state.scaffolded) {
|
package/src/kits.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
1
2
|
import { spawnSync } from 'node:child_process';
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import {
|
|
@@ -7,6 +8,12 @@ import {
|
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
|
|
9
10
|
import { resolveLauncherAssets } from './repo-assets.mjs';
|
|
11
|
+
const creatorRootModulePath = resolveLauncherAssets().creatorRootModulePath;
|
|
12
|
+
const creatorRoot = creatorRootModulePath ? await import(pathToFileURL(creatorRootModulePath).href) : null;
|
|
13
|
+
function resolveAssetLibraryRoots(env) {
|
|
14
|
+
if (!creatorRoot) throw new Error('creator-root が見つからないため素材の置き場を解決できません。AKARI Video を再インストールしてください。');
|
|
15
|
+
return creatorRoot.resolveAssetLibraryRoots(env);
|
|
16
|
+
}
|
|
10
17
|
|
|
11
18
|
const KITS_SCHEMA = 'akari-installed-kits/v0';
|
|
12
19
|
const INSTALLED_ASSETS_SCHEMA = 'akari-installed-assets/v0';
|
|
@@ -152,16 +159,8 @@ export function linkKitAssets(kitDir, manifest, home, options = {}) {
|
|
|
152
159
|
} else {
|
|
153
160
|
warnings.push(`素材 ${asset.category}/${asset.id} の検査ツールが見つからないため検査をスキップしました。`);
|
|
154
161
|
}
|
|
155
|
-
const destination = path.join(
|
|
156
|
-
const
|
|
157
|
-
const kitSubdir = path.relative(productRoot, kitDir);
|
|
158
|
-
const result = replaceSymlink(source, destination, {
|
|
159
|
-
...options,
|
|
160
|
-
relativeTarget: path.join(
|
|
161
|
-
'..', '..', 'assets', 'store', manifest.id, kitSubdir,
|
|
162
|
-
'assets', asset.category, asset.id
|
|
163
|
-
)
|
|
164
|
-
});
|
|
162
|
+
const destination = path.join(resolveAssetLibraryRoots({ ...(options.env ?? process.env), AKARI_HOME: home }).write, asset.category, asset.id);
|
|
163
|
+
const result = replaceSymlink(source, destination, options);
|
|
165
164
|
if (result.status === 'occupied') {
|
|
166
165
|
warnings.push(`既存の実ディレクトリを保持しました: ${destination}`);
|
|
167
166
|
} else if (result.status === 'permission-denied') {
|
|
@@ -174,8 +173,8 @@ export function linkKitAssets(kitDir, manifest, home, options = {}) {
|
|
|
174
173
|
return { linked, items, warnings };
|
|
175
174
|
}
|
|
176
175
|
|
|
177
|
-
function readInstalledAssetsIndex(home) {
|
|
178
|
-
const indexPath = path.join(
|
|
176
|
+
function readInstalledAssetsIndex(home, env = process.env) {
|
|
177
|
+
const indexPath = path.join(resolveAssetLibraryRoots({ ...env, AKARI_HOME: home }).write, 'installed.json');
|
|
179
178
|
if (!existsSync(indexPath)) return { schema: INSTALLED_ASSETS_SCHEMA, packs: {} };
|
|
180
179
|
const index = JSON.parse(readFileSync(indexPath, 'utf8'));
|
|
181
180
|
if (index?.schema !== INSTALLED_ASSETS_SCHEMA
|
|
@@ -185,38 +184,38 @@ function readInstalledAssetsIndex(home) {
|
|
|
185
184
|
return index;
|
|
186
185
|
}
|
|
187
186
|
|
|
188
|
-
function writeInstalledAssetsIndex(home, index) {
|
|
189
|
-
const indexPath = path.join(
|
|
187
|
+
function writeInstalledAssetsIndex(home, index, env = process.env) {
|
|
188
|
+
const indexPath = path.join(resolveAssetLibraryRoots({ ...env, AKARI_HOME: home }).write, 'installed.json');
|
|
190
189
|
mkdirSync(path.dirname(indexPath), { recursive: true });
|
|
191
190
|
const temporary = `${indexPath}.tmp-${process.pid}`;
|
|
192
191
|
writeFileSync(temporary, `${JSON.stringify(index, null, 2)}\n`, { mode: 0o600 });
|
|
193
192
|
renameSync(temporary, indexPath);
|
|
194
193
|
}
|
|
195
194
|
|
|
196
|
-
export function registerKitAssets(home, manifest, kitDir, items) {
|
|
195
|
+
export function registerKitAssets(home, manifest, kitDir, items, env = process.env) {
|
|
197
196
|
const root = path.resolve(kitDir);
|
|
198
|
-
const storeRoot = path.join(
|
|
197
|
+
const storeRoot = path.join(resolveAssetLibraryRoots({ ...env, AKARI_HOME: home }).write, 'store', manifest.id);
|
|
199
198
|
if (root !== storeRoot && !root.startsWith(`${storeRoot}${path.sep}`)) {
|
|
200
199
|
throw new Error(`キット素材の root が展開先の外を指しています: ${root}`);
|
|
201
200
|
}
|
|
202
|
-
const index = readInstalledAssetsIndex(home);
|
|
201
|
+
const index = readInstalledAssetsIndex(home, env);
|
|
203
202
|
index.packs[manifest.id] = {
|
|
204
203
|
version: manifest.version,
|
|
205
204
|
installedAt: new Date().toISOString(),
|
|
206
205
|
root,
|
|
207
206
|
items
|
|
208
207
|
};
|
|
209
|
-
writeInstalledAssetsIndex(home, index);
|
|
208
|
+
writeInstalledAssetsIndex(home, index, env);
|
|
210
209
|
return items;
|
|
211
210
|
}
|
|
212
211
|
|
|
213
|
-
function unregisterKitAssets(home, productId) {
|
|
214
|
-
const indexPath = path.join(
|
|
212
|
+
function unregisterKitAssets(home, productId, env = process.env) {
|
|
213
|
+
const indexPath = path.join(resolveAssetLibraryRoots({ ...env, AKARI_HOME: home }).write, 'installed.json');
|
|
215
214
|
if (!existsSync(indexPath)) return;
|
|
216
|
-
const index = readInstalledAssetsIndex(home);
|
|
215
|
+
const index = readInstalledAssetsIndex(home, env);
|
|
217
216
|
if (!Object.hasOwn(index.packs, productId)) return;
|
|
218
217
|
delete index.packs[productId];
|
|
219
|
-
writeInstalledAssetsIndex(home, index);
|
|
218
|
+
writeInstalledAssetsIndex(home, index, env);
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
export function linkKitSkills(kitDir, manifest, home, options = {}) {
|
|
@@ -276,20 +275,20 @@ export function writeKitsLedger(home, entry) {
|
|
|
276
275
|
return ledger;
|
|
277
276
|
}
|
|
278
277
|
|
|
279
|
-
export function removeKit(home, productId) {
|
|
278
|
+
export function removeKit(home, productId, env = process.env) {
|
|
280
279
|
const ledger = readKitsLedger(home);
|
|
281
280
|
const entry = ledger.kits.find((kit) => kit.id === productId);
|
|
282
281
|
if (!entry) return false;
|
|
283
282
|
for (const name of entry.skills ?? []) removeOwnedSymlink(path.join(home, 'kits', 'plugin', 'skills', name), entry.kitDir);
|
|
284
283
|
for (const asset of entry.assets ?? []) {
|
|
285
|
-
removeOwnedSymlink(path.join(
|
|
284
|
+
for (const root of resolveAssetLibraryRoots({ ...env, AKARI_HOME: home }).read) removeOwnedSymlink(path.join(root, asset.category, asset.id), entry.kitDir);
|
|
286
285
|
}
|
|
287
286
|
ledger.kits = ledger.kits.filter((kit) => kit.id !== productId);
|
|
288
287
|
const ledgerPath = path.join(home, 'kits', 'installed.json');
|
|
289
288
|
const temporary = `${ledgerPath}.tmp-${process.pid}`;
|
|
290
289
|
writeFileSync(temporary, `${JSON.stringify(ledger, null, 2)}\n`, { mode: 0o600 });
|
|
291
290
|
renameSync(temporary, ledgerPath);
|
|
292
|
-
unregisterKitAssets(home, productId);
|
|
291
|
+
unregisterKitAssets(home, productId, env);
|
|
293
292
|
return true;
|
|
294
293
|
}
|
|
295
294
|
|
package/src/store-command.mjs
CHANGED
|
@@ -20,6 +20,12 @@ import {
|
|
|
20
20
|
} from './store-device-connect.mjs';
|
|
21
21
|
import { readOwnVersion } from './update-check.mjs';
|
|
22
22
|
import { resolveLauncherAssets } from './repo-assets.mjs';
|
|
23
|
+
const creatorRootModulePath = resolveLauncherAssets().creatorRootModulePath;
|
|
24
|
+
const creatorRoot = creatorRootModulePath ? await import(pathToFileURL(creatorRootModulePath).href) : null;
|
|
25
|
+
function resolveAssetLibraryRoots(env) {
|
|
26
|
+
if (!creatorRoot) throw new Error('creator-root が見つからないため素材の置き場を解決できません。AKARI Video を再インストールしてください。');
|
|
27
|
+
return creatorRoot.resolveAssetLibraryRoots(env);
|
|
28
|
+
}
|
|
23
29
|
import {
|
|
24
30
|
checkRequires,
|
|
25
31
|
enableHint,
|
|
@@ -154,8 +160,7 @@ function flattenPackContents(pack, packRoot) {
|
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
function registerInstalledPack(env, productId, packPath) {
|
|
157
|
-
const
|
|
158
|
-
const indexPath = path.join(home, 'assets', 'installed.json');
|
|
163
|
+
const indexPath = path.join(resolveAssetLibraryRoots(env).write, 'installed.json');
|
|
159
164
|
const packRoot = path.dirname(packPath);
|
|
160
165
|
const pack = readJsonFile(packPath);
|
|
161
166
|
if ((typeof pack?.version !== 'string' && typeof pack?.version !== 'number')) {
|
|
@@ -319,7 +324,7 @@ export async function runStoreCommand(args, options = {}) {
|
|
|
319
324
|
log('使い方: akari store uninstall <productId>');
|
|
320
325
|
return { exitCode: 1 };
|
|
321
326
|
}
|
|
322
|
-
if (!removeKit(resolveAkariHome(env), productId)) {
|
|
327
|
+
if (!removeKit(resolveAkariHome(env), productId, env)) {
|
|
323
328
|
log(`導入済みの拡張キットが見つかりません: ${productId}`);
|
|
324
329
|
return { exitCode: 1 };
|
|
325
330
|
}
|
|
@@ -430,7 +435,7 @@ export async function runStoreCommand(args, options = {}) {
|
|
|
430
435
|
log('パック内に declarations.json が見つかりませんでした。zip の中身を確認してください。');
|
|
431
436
|
return { exitCode: 1 };
|
|
432
437
|
}
|
|
433
|
-
const destDir = path.join(
|
|
438
|
+
const destDir = path.join(resolveAssetLibraryRoots(env).write, 'audio');
|
|
434
439
|
mkdirSync(destDir, { recursive: true });
|
|
435
440
|
const dest = path.join(destDir, 'declarations.json');
|
|
436
441
|
if (existsSync(dest)) {
|
|
@@ -445,7 +450,7 @@ export async function runStoreCommand(args, options = {}) {
|
|
|
445
450
|
}
|
|
446
451
|
|
|
447
452
|
// 既知の導入手順が無い商品は素材置き場に展開して README を案内
|
|
448
|
-
const destDir = path.join(
|
|
453
|
+
const destDir = path.join(resolveAssetLibraryRoots(env).write, 'store', productId);
|
|
449
454
|
rmSync(destDir, { recursive: true, force: true });
|
|
450
455
|
cpSync(extractDir, destDir, { recursive: true });
|
|
451
456
|
const readme = findFile(destDir, 'README.md');
|
|
@@ -531,6 +536,7 @@ export async function runStoreCommand(args, options = {}) {
|
|
|
531
536
|
|
|
532
537
|
const hadInstalledKit = readKitsLedger(home).kits.length > 0;
|
|
533
538
|
const assetLinks = linkKitAssets(kitDir, manifest, home, {
|
|
539
|
+
env,
|
|
534
540
|
assets: options.assets,
|
|
535
541
|
spawnSyncImpl: options.spawnSync,
|
|
536
542
|
platform: options.platform
|
|
@@ -541,7 +547,7 @@ export async function runStoreCommand(args, options = {}) {
|
|
|
541
547
|
for (const blocker of skillLinks.blockers) log(`導入できません: ${blocker}`);
|
|
542
548
|
return { exitCode: 1 };
|
|
543
549
|
}
|
|
544
|
-
registerKitAssets(home, manifest, kitDir, assetLinks.items);
|
|
550
|
+
registerKitAssets(home, manifest, kitDir, assetLinks.items, env);
|
|
545
551
|
writeKitsLedger(home, {
|
|
546
552
|
id: manifest.id,
|
|
547
553
|
version: manifest.version,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# 素材ライブラリ契約 v0
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
- 日付: 2026-07-13
|
|
4
8
|
- 状態: 設計確定
|
|
5
9
|
- 前提: 本体(エンジン)は合成だけ。素材とその知識は全部外側に置く
|
|
@@ -215,7 +219,7 @@ catalog に載せる素材は、取得元のライセンスが CC0 相当(帰
|
|
|
215
219
|
|---|---|---|
|
|
216
220
|
| `local` | `<プロジェクト>/assets/` | そのプロジェクトのみ |
|
|
217
221
|
| `shared` | プロジェクトから上位へ辿った各ディレクトリの `.akari/assets/`(2026-07-25 第三裁定で確定) | そのディレクトリ配下の全プロジェクト(事業・組織単位。複数層可) |
|
|
218
|
-
| `user` |
|
|
222
|
+
| `user` | `<ライブラリの置き場>/`(2026-07-25 第三裁定で確定) | そのマシンの全プロジェクト |
|
|
219
223
|
| `builtin` | 本リポの `assets/` | 製品出荷デフォルト |
|
|
220
224
|
| `catalog` | 本リポの `catalog/`(remote) | 取得して任意の層へ入庫 |
|
|
221
225
|
|
|
@@ -241,7 +245,7 @@ catalog に載せる素材は、取得元のライセンスが CC0 相当(帰
|
|
|
241
245
|
~~**確定裁定(2026-07-25 再裁定): プロジェクト外の置き場所は `~/.akari-video/` をベースに
|
|
242
246
|
統一する。** 初期案保留(旧「`.akari-video/` は初期案」項)はこれで解消。~~
|
|
243
247
|
|
|
244
|
-
|
|
248
|
+
**履歴(2026-07-25 第三裁定): 当時の user 素材は `~/.akari/` 直下だった。2026-09-21 から素材は作業場 `library/` に移行し、マシン状態のみ `~/.akari/` に残す。**
|
|
245
249
|
`~/.akari/` 直下には他ソフトウェアの既存物が併存する場合があるため、共存規約
|
|
246
250
|
(本節末尾を参照)を必ず守る。
|
|
247
251
|
|
|
@@ -250,7 +254,7 @@ catalog に載せる素材は、取得元のライセンスが CC0 相当(帰
|
|
|
250
254
|
|
|
251
255
|
確定事項(2026-07-25 第三裁定で更新。カッコ内は第二裁定時点の値):
|
|
252
256
|
|
|
253
|
-
- `user` 層:
|
|
257
|
+
- `user` 層: `<ライブラリの置き場>/`(第二裁定時点: `~/.akari-video/assets/`)
|
|
254
258
|
- `shared` 層: 上位ディレクトリの `.akari/assets/`(第二裁定時点: `.akari-video/assets/`)
|
|
255
259
|
- レシピ: `~/.akari/recipes/`(recipe v0 の現行表記のまま・変更不要。出所は
|
|
256
260
|
`docs/contract-2026-07-25-recipe-v0.md` §2 に追記)
|
|
@@ -380,7 +384,7 @@ catalog に載せる素材は、取得元のライセンスが CC0 相当(帰
|
|
|
380
384
|
- **カテゴリを増やさない**のが本改訂の要点である。新しい主題(ホワイトボード、ノート風、付箋…)は
|
|
381
385
|
既存カテゴリ + tags で表す。カテゴリ追加を提案するときは「既存 6 つのどの形にも当てはまらない
|
|
382
386
|
配布・消費のされ方か」を先に示す
|
|
383
|
-
- `audio` を `media`
|
|
387
|
+
- `audio` を `media` に畳む案は見送った。`<ライブラリの置き場>/audio/`(user 層)に実データがあり、
|
|
384
388
|
リポ外のユーザー資産の移設を伴うため。`audio` と `broll` は消費経路(音声トラック / 映像トラック)が
|
|
385
389
|
異なるので、形の軸としても分けたままで筋が通る
|
|
386
390
|
- 移行時の実績: `assets/` 3 件・`catalog/` 3 件のディレクトリ移動と `category` 値の書き換え、
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# creator-root-v1(作業場)契約
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
- 日付: 2026-08-02
|
|
4
8
|
- 状態: **v1 ドラフト・要オーナーレビュー**(設計方針はオーナー承認済み 2026-08-02。
|
|
5
9
|
判断根拠・出典調査は非公開の内部記録で管理する)
|
|
@@ -41,8 +45,8 @@ AKARI Video がマシン上に持ってよい場所は次の 3 つ**だけ**で
|
|
|
41
45
|
- マシン状態の実パスは**既存実装の規約を追認**する(`~/.akari/` = 更新キャッシュ・接続マーカー・
|
|
42
46
|
可搬 Node runtime 等 / 資格情報は `~/.config/akari-video/credentials.env`)。新たな隠し場所を増やさない
|
|
43
47
|
- 原則: マシン状態には**ユーザーの内容物(作品・素材・記憶)を置かない**。
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
旧音源ライブラリの例外は 2026-09-21 の移行で回収した。素材は作業場 `library/` へ移し、
|
|
49
|
+
マシン設定の `library-location.json` に置き場を固定する(作業場なし・同期フォルダは移さない)。
|
|
46
50
|
|
|
47
51
|
- **secrets(API キー・トークン類)はマシン設定にのみ置く**。作業場は可視・バックアップ・
|
|
48
52
|
同期対象なので、secrets を混ぜてはならない
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# contract — 素材の参照モデル v0(共有ライブラリ参照の台帳と解決規則)
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
- 状態: 実装済み(機械層のみ。シェル UI の採用は後続)
|
|
4
8
|
- 決定日: 2026-09-02
|
|
5
9
|
- 実装: `packages/asset-resolver`(記帳・実体化)/ `packages/render-cut`・`packages/edit-lint`(解決)
|
|
@@ -7,7 +11,7 @@
|
|
|
7
11
|
## 1. 目的
|
|
8
12
|
|
|
9
13
|
カタログ素材をプロジェクトごとに実体コピーすると、同じ素材が何度もダウンロード・複製されて
|
|
10
|
-
|
|
14
|
+
プロジェクトが肥大する。実体は**マシン単位の共有ライブラリ**(`<ライブラリの置き場>/<category>/<id>/`)に
|
|
11
15
|
1 部だけ置き、プロジェクトには**参照だけを記録**できるようにする。
|
|
12
16
|
|
|
13
17
|
## 2. 設計の要点
|
|
@@ -25,11 +29,11 @@
|
|
|
25
29
|
edit.json の version とは無関係。
|
|
26
30
|
- **解決規則**: 宣言されたプロジェクト相対パスが `assets/<category>/<id>/<rest>` の形で、
|
|
27
31
|
(1) プロジェクト実体が存在せず、(2) 台帳に `{category, id}` があるとき、
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
`resolveAssetLibraryRoots().read` の順(新しい置き場 → 従来の置き場)にフォールバックする。
|
|
33
|
+
解決先は各ルートで字句・realpath containment を満たす正規ファイルで
|
|
30
34
|
あること(`..` 等の脱出は fail-closed で拒否)。
|
|
31
35
|
- render-cut は解決した入力を render inputs 記録に `scope: "library"` として残す
|
|
32
|
-
(既存 `scope: "akari"` と同列の additive
|
|
36
|
+
(既存 `scope: "akari"` と同列の additive 記録)。採用した実ルートを `library_root` に保存して後段でも検査する。edit-lint は解決できる参照を欠落と報告せず、
|
|
33
37
|
台帳にあるが実体が無い参照は「共有ライブラリ参照(未取得)」として欠落報告する。
|
|
34
38
|
- render-cut / edit-lint は依存ゼロ CLI のため、解決ロジックは各パッケージ内に**同一実装を重複**して
|
|
35
39
|
持つ(`src/library-reference.mjs`)。挙動同一性は両テストの同一ケース表で担保する。
|
|
@@ -47,7 +51,53 @@ akari-assets bundle --project <dir> [--dry-run]
|
|
|
47
51
|
bundle は台帳の各参照をキャッシュから `assets/<category>/<id>/` へ実体化して台帳から除去する。
|
|
48
52
|
未取得の参照は resolve(取得)を試み、取得できないものは台帳に残して部分成功(exit 非 0)で報告する。冪等。
|
|
49
53
|
|
|
50
|
-
## 4.
|
|
54
|
+
## 4. 書き出しエンジンの配信
|
|
55
|
+
|
|
56
|
+
OSR / GPU の共通配信サーバーは、render-cut が解決した入力のうち `scope: "library"` の
|
|
57
|
+
全件を許可表として受け取る。映像だけでなく、音・静止画・オーバーレイ内の素材も対象。
|
|
58
|
+
キーは宣言パス `assets/<category>/<id>/<rest>`(区切りは `/`)、値は
|
|
59
|
+
`{ absolute: <実体の絶対パス>, library_root: <採用ルート> }`。
|
|
60
|
+
配信側は置き場の探索や台帳からの再解決を行わない。
|
|
61
|
+
|
|
62
|
+
ffmpeg の計画には、同じ render inputs の解決結果で素材パス欄だけを絶対パスへ置き換えた
|
|
63
|
+
edit の写しを渡す。BGM・SFX・ナレーション・分離音声・レイヤー音声の入力と尺の probe が対象。
|
|
64
|
+
映像ソースの cut 音声は既存の解決済み capabilities を使い、contact sheet は書き出した動画を読む。
|
|
65
|
+
元の edit.json、配信用の宣言パス、receipt の入力パスは書き換えない。
|
|
66
|
+
|
|
67
|
+
別プロセスへの受け渡しには
|
|
68
|
+
`<projectRoot>/.akari/render-tmp/media-references-<render-cut の PID>.json` を使う。
|
|
69
|
+
表の形式は `{ "token": "<合言葉>", "references": { "<宣言パス>": { "absolute": "<実体の絶対パス>", "library_root": "<採用ルート>" } } }`。
|
|
70
|
+
render-cut は実行ごとに `crypto.randomBytes(32)` の合言葉を hex 文字列として生成し、
|
|
71
|
+
同じ値を環境変数 `AKARI_RENDER_MEDIA_REFERENCES_TOKEN` に設定してから書き出しを起動する。
|
|
72
|
+
この変数は Electron 子プロセスへ継承される。終了時は `finally` で元の値へ戻し、元が未定義なら削除する。
|
|
73
|
+
合言葉はログ・エラー文・render.json に出さない。
|
|
74
|
+
render-cut は子プロセスの起動前に排他的に作成し、OSR / GPU(GPU から OSR への再試行を含む)の
|
|
75
|
+
終了時に `finally` で成功・失敗とも削除する。Electron は親 PID からパスを決め、サーバー生成時に
|
|
76
|
+
一度だけ読む。別 CLI の並行実行は表を共有しない。プロセス内の使用中パス集合で
|
|
77
|
+
同じプロジェクトの重複実行(ネスト呼び出しを含む)を拒否する。集合に無い自 PID の既存表は、
|
|
78
|
+
クラッシュや PID 再利用による残存表として削除してから排他的に作り直す。
|
|
79
|
+
使用中の記録は作成・書き出し・後片付けの失敗時にも解除する。他 PID の表は回収しない。
|
|
80
|
+
ファイル経由の表は、環境変数が無い・空、表の token が文字列でない、UTF-8 バイト長が異なる、
|
|
81
|
+
または `crypto.timingSafeEqual` で一致しない場合は一切使わない。token の無い旧形式や壊れた JSON も空の表として扱う。
|
|
82
|
+
これにより単体 CLI など render-cut を通らない起動では、プロジェクトに植え込まれた表から外部ファイルを配信しない。
|
|
83
|
+
配信 API の明示引数 `mediaReferences` がある場合は従来どおりそちらを優先し、
|
|
84
|
+
表が無い場合や認証できない場合はプロジェクト内だけを配信する。
|
|
85
|
+
|
|
86
|
+
`/media/<宣言パス>` は次の順で検査する。
|
|
87
|
+
|
|
88
|
+
1. デコード後に `..` セグメント(`/` と `\` の両区切り)を含む要求は 403。
|
|
89
|
+
2. プロジェクト内の実体があれば優先する。字句・realpath の両方でプロジェクト内に収まることを
|
|
90
|
+
検査し、symlink による脱出は 403、正規ファイルでなければ 404。
|
|
91
|
+
3. プロジェクトに実体が無い場合のみ、許可表の完全一致キーを調べる。表に無いファイルは
|
|
92
|
+
ライブラリ配下に存在しても 404。
|
|
93
|
+
4. 表の実体が採用ルート内にあることを字句で確認し、さらに実体と採用ルートを毎回 realpath で
|
|
94
|
+
解決して包含を再検査する。脱出は 403、欠落・非正規ファイルは 404。
|
|
95
|
+
5. 許可した実体は既存のファイル配信を使い、通常取得は 200、Range 取得は 206 とする。
|
|
96
|
+
|
|
97
|
+
台帳にあっても置き場に無い必須素材は、従来の render inputs / 計画段階のエラーで停止する。
|
|
98
|
+
配信ページの 404 を待ってから失敗させない。
|
|
99
|
+
|
|
100
|
+
## 5. スコープ外(後続)
|
|
51
101
|
|
|
52
102
|
- シェル UI の採用(取り込みフローの reference 既定化・プロジェクト面の「参照」バッジ・
|
|
53
103
|
プレビュー経路のフォールバック)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# akari-launcher(`akari` コマンド)
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
「UI に依存したくない。opencode 単体でも、どんなディレクトリでも始められるように」を
|
|
4
8
|
実現する薄いラッパー CLI。npm パッケージ名は `akari-video`、提供するコマンド名は `akari`
|
|
5
9
|
(npm の `akari` は別プロダクトが取得済みのため。オーナー裁定 2026-07-21 §8-2)。
|
|
@@ -24,7 +28,7 @@ akari
|
|
|
24
28
|
├─ 4. 公式音源ライブラリ(AKARI Sounds)の初回セットアップ(src/sounds-setup.mjs):
|
|
25
29
|
│ 未導入かつ TTY のとき生涯 1 回だけ [Y/n](既定 Yes)を聞き、Yes なら
|
|
26
30
|
│ packages/audio-library-setup/bin/fetch-akari-sounds.mjs で一括ダウンロード。
|
|
27
|
-
│ n は marker
|
|
31
|
+
│ n は marker(<ライブラリの置き場>/audio/.akari-sounds-declined.json)を書いて以後
|
|
28
32
|
│ 聞かない。再入口は `akari sounds`。失敗しても起動は止めない
|
|
29
33
|
│
|
|
30
34
|
└─ 5. 最後に opencode を exec する
|
|
@@ -48,7 +52,7 @@ DL・sha256 検証・適用まで実行。それ以外(npm グローバル / g
|
|
|
48
52
|
`akari store <connect|status|install|download|disconnect>`(AKARI Store 連携。マイページで発行した
|
|
49
53
|
接続トークンを `~/.akari/store-credentials.json`(0600)に保存し、購入済み一覧の確認と
|
|
50
54
|
配布物の取得ができる。`install <productId> [--from <zip>]` は購入パックを展開し、`PACK.json` の
|
|
51
|
-
収載素材を
|
|
55
|
+
収載素材を `<ライブラリの置き場>/installed.json` へ登録する。`--from` は開発・オフライン導入時に手元の
|
|
52
56
|
zip を使う。`src/store-command.mjs`)/
|
|
53
57
|
`akari assets <list|fetch|sync|...>`(素材カタログの一覧・取得・同期。
|
|
54
58
|
`packages/asset-resolver` の CLI への薄い委譲で、カタログ合成・entitlements 判定・
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.74",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。 [akari-video npm vendor: bin/akari.mjs is reference-only. These CLI entrypoints are not included in the akari-video npm package. Use `akari doctor --json` and run the path reported in `render_cut.path`. Full installations provide it in a monorepo checkout, ~/.akari/app, /Applications/AKARI Video.app/Contents/Resources/packages, or %LOCALAPPDATA%\\Programs\\@akari-videoshell\\resources\\packages.]",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# @akari-video/asset-resolver
|
|
2
2
|
|
|
3
|
+
ライブラリの置き場は既定で作業場の `library/`。作業場が無いときは従来の `~/.akari/assets/` を使う。
|
|
4
|
+
`akari-assets list`(または `akari assets list`)の先頭行で実際の置き場を確認する。
|
|
5
|
+
以下の `<ライブラリの置き場>` はその表示先を指し、音源はその下の `audio/` に入る。
|
|
6
|
+
|
|
3
7
|
無料素材の参照配布 + オンデマンド取得 resolver v0。
|
|
4
8
|
|
|
5
9
|
「このアカウントで使える素材(無料 + 購入済み)」をカタログ + entitlements + ローカル取得状態から
|
|
6
|
-
1 リストに合成し、**使った素材だけ**を
|
|
10
|
+
1 リストに合成し、**使った素材だけ**を `<ライブラリの置き場>/<category>/<id>/` へ取得・検証して登録する。
|
|
7
11
|
「全部ダウンロード」を既定にしない、という設計契約(内部リポ
|
|
8
12
|
`planning/notes-2026-08-04-asset-reference-distribution.md`)の本体側実装。
|
|
9
13
|
|
|
@@ -17,6 +21,7 @@
|
|
|
17
21
|
```sh
|
|
18
22
|
akari-assets list [--category <c>] [--json] # 合成カタログ一覧(取得状態バッジ込み)
|
|
19
23
|
akari-assets fetch <id> [--project <dir>] [--force] # 素材を解決してローカルへ登録
|
|
24
|
+
akari-assets migrate [--dry-run] # 旧置き場を再移行(dry-run は変更しない)
|
|
20
25
|
akari-assets sync # カタログを取得してローカルにキャッシュ
|
|
21
26
|
akari-assets browse [--port <n>] # ローカル HTTP サーバでカタログを閲覧・投入
|
|
22
27
|
```
|
|
@@ -25,14 +30,14 @@ akari-assets browse [--port <n>] # ローカル HTTP サ
|
|
|
25
30
|
`[installed]` `akari store install` で導入済み。
|
|
26
31
|
|
|
27
32
|
`akari store install <productId> [--from <zip>]` が `PACK.json` を持つ購入パックを展開すると、
|
|
28
|
-
収載素材は
|
|
33
|
+
収載素材は `<ライブラリの置き場>/installed.json` に登録される。resolver はこの索引をリモートカタログへ
|
|
29
34
|
マージし、同じ id があれば導入済みのローカル実体を優先する。`fetch` はネットワークや entitlement
|
|
30
35
|
照会を使わずパックからコピーし、`PACK.json` 記載の sha256 と照合してから通常の素材ライブラリへ
|
|
31
36
|
原子的に登録する。
|
|
32
37
|
|
|
33
38
|
`fetch` はキャッシュヒットなら即座にそのパスを返す。未取得なら、カタログの `files[]` を全部
|
|
34
39
|
一時ディレクトリへ実体化 → sha256 検証 → (`meta.json` を含む素材は)`validate-asset.mjs` で
|
|
35
|
-
契約検証 → 全部通ってから
|
|
40
|
+
契約検証 → 全部通ってから `<ライブラリの置き場>/<category>/<id>/` へ原子的に登録する。
|
|
36
41
|
途中で失敗したら一時ディレクトリを破棄し、登録先には一切書き込まない(fail-closed。部分状態を
|
|
37
42
|
残さない)。有料で未購入(`price > 0` かつ entitlements に無い)は `fetch` を拒否する
|
|
38
43
|
(一度取得済みのキャッシュはそのまま使える — ゲートは「新規取得」だけにかかる)。
|
|
@@ -54,7 +59,7 @@ BSD `cp -Rc`(`clonefile(2)` 直呼び)を試し、失敗時のみ上記 `fs.
|
|
|
54
59
|
直下)で全ファイルの sha256 を検証
|
|
55
60
|
3. `README.md` / `LICENSE.md` / `checksums.txt` を除く素材ペイロードを一時ディレクトリへコピー
|
|
56
61
|
4. (`meta.json` を含む素材は)`validate-asset.mjs` で契約検証
|
|
57
|
-
5. 全部通ってから
|
|
62
|
+
5. 全部通ってから `<ライブラリの置き場>/<category>/<id>/` へ原子的に登録
|
|
58
63
|
|
|
59
64
|
無料経路と同じ fail-closed の規律(1 件でも失敗したら一時ディレクトリを破棄し、登録先には
|
|
60
65
|
一切書き込まない)を踏襲する。ダウンロード失敗(オフライン・トークン失効)・zip 構成不正・
|
|
@@ -101,7 +106,8 @@ checksums 不一致は、いずれも `AssetResolverError`(`code: 'download_fa
|
|
|
101
106
|
|
|
102
107
|
| 変数 | 既定値 | 用途 |
|
|
103
108
|
| --- | --- | --- |
|
|
104
|
-
| `AKARI_HOME` | `~/.akari` |
|
|
109
|
+
| `AKARI_HOME` | `~/.akari` | マシン状態(library-location.json・カタログキャッシュ・store-credentials.json)。作業場なしでは旧 assets/ もここに置く |
|
|
110
|
+
| `AKARI_LIBRARY_ROOT` | 未設定 | ライブラリの書き込み先の明示上書き(読みは旧置き場にもフォールバック) |
|
|
105
111
|
| `AKARI_ASSETS_CATALOG` | `https://akari-oss.app/assets/catalog.json` | カタログの取得元。**URL** ならリモート fetch、それ以外はローカルファイルパスとして読む(未デプロイの開発時は store リポのローカル出力を指す) |
|
|
106
112
|
| `AKARI_ASSETS_BASE` | カタログの `base` フィールド | 素材実体の配信ベースの上書き(ローカル開発でディレクトリを直接指すときに使う) |
|
|
107
113
|
| `AKARI_STORE_API` | `https://akari-oss.app` | entitlements API のホスト上書き。未設定時は `~/.akari/store-credentials.json` の `url`(`akari store connect` が書き込む値)から組み立てる |
|
|
@@ -145,3 +151,22 @@ node --test
|
|
|
145
151
|
- `packages/akari-launcher/src/store-command.mjs` の `akari store connect` が書く
|
|
146
152
|
`~/.akari/store-credentials.json`(`{ url, token, email }`)をそのまま読む
|
|
147
153
|
(依存追加を避けるため import はせず、同じファイル規約だけを踏襲)
|
|
154
|
+
|
|
155
|
+
## ライブラリの解決と移行
|
|
156
|
+
|
|
157
|
+
`resolveAssetLibraryRoots(env, { platform })` の正本は `packages/creator-root/src/index.mjs`。
|
|
158
|
+
書き込みは `AKARI_LIBRARY_ROOT` → `<AKARI_HOME>/library-location.json` → 従来の `assets/` の順で 1 か所に決まる。
|
|
159
|
+
location が有効なのは migrating / done のときだけ。pending / declined では旧置き場への読み書きを維持し、同期フォルダへは書き込まない。
|
|
160
|
+
明示の AKARI_LIBRARY_ROOT は最優先。読み取りは有効な置き場と従来の置き場を重複なく見る(新しい方を優先)。cwd の上方探索はしない。
|
|
161
|
+
`list --json` は互換性のため従来どおり素材配列のみを返す。置き場表示は通常の `list` の先頭行。
|
|
162
|
+
|
|
163
|
+
シェルと launcher の起動時に共通の移行実装を呼ぶ。`library-location.json` は version 0、
|
|
164
|
+
root(絶対パス)、state(pending / migrating / done / declined)、decidedAt、migratedAt、notifiedAt を持ち、
|
|
165
|
+
一時ファイル + rename で原子的に更新する。不在・壊れは従来の置き場へフォールバックする。
|
|
166
|
+
移行先を初めて決めるときだけ `AKARI_CREATOR_ROOT` とマシンポインタの `lastRoot` を使う。
|
|
167
|
+
|
|
168
|
+
同一ディスクは rename、EXDEV は複製・サイズと sha256 照合・旧側削除。同名素材は上書きせず旧側に残し、
|
|
169
|
+
結果の skipped に記録する。カテゴリの中の新規素材は再移行できる。付随ファイルとキットの参照も追随する。
|
|
170
|
+
途中失敗は migrating のまま再開可能。done 後に旧 CLI が追加した素材は `akari-assets migrate` で寄せる。
|
|
171
|
+
作業場なしは何もしない。OneDrive / Dropbox / iCloud Drive / Google Drive 配下は pending に留め、
|
|
172
|
+
結果に同期先と総容量を返す。ドロップフォルダと reviews はマシン状態として元の場所に残す。
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// akari-assets sync
|
|
8
8
|
// akari-assets browse [--port <n>]
|
|
9
9
|
|
|
10
|
+
import { migrateAssetLibrary } from '../../creator-root/src/index.mjs';
|
|
10
11
|
import { startBrowseServer } from '../src/browse-server.mjs';
|
|
11
12
|
import { bundleProjectReferences } from '../src/bundle.mjs';
|
|
12
13
|
import { cacheCatalog, loadCatalog } from '../src/catalog.mjs';
|
|
@@ -29,7 +30,7 @@ function badgeOf(item) {
|
|
|
29
30
|
async function cmdList(args, env) {
|
|
30
31
|
const category = flagValue(args, '--category');
|
|
31
32
|
const asJson = args.includes('--json');
|
|
32
|
-
const {
|
|
33
|
+
const { libraryRoots, items } = await composeState({ env });
|
|
33
34
|
const filtered = category ? items.filter((item) => item.category === category) : items;
|
|
34
35
|
|
|
35
36
|
if (asJson) {
|
|
@@ -37,7 +38,7 @@ async function cmdList(args, env) {
|
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
console.log(`使える素材 ${filtered.length} 件(ライブラリ: ${
|
|
41
|
+
console.log(`使える素材 ${filtered.length} 件(ライブラリ: ${libraryRoots.write})`);
|
|
41
42
|
for (const item of filtered) {
|
|
42
43
|
console.log(` ${badgeOf(item)} ${item.id}\t[${item.category}]\t${item.title}`);
|
|
43
44
|
}
|
|
@@ -119,17 +120,19 @@ async function cmdBrowse(args, env) {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
function printUsage() {
|
|
122
|
-
console.log(`使い方: akari-assets <list|fetch|bundle|sync|browse> [options]
|
|
123
|
+
console.log(`使い方: akari-assets <list|fetch|bundle|migrate|sync|browse> [options]
|
|
123
124
|
|
|
124
125
|
list [--category <c>] [--json] 合成カタログ一覧(取得状態バッジ込み)
|
|
125
126
|
fetch <id> [--project <dir>] [--reference] [--force]
|
|
126
127
|
素材を解決して登録(--reference はコピーせず参照を記帳)
|
|
127
128
|
bundle --project <dir> [--dry-run] 参照素材をプロジェクトへ実体化(素材をまとめる)
|
|
129
|
+
migrate [--dry-run] ライブラリを作業場へ移行
|
|
128
130
|
sync カタログを取得してローカルにキャッシュ(オフライン用)
|
|
129
131
|
browse [--port <n>] ローカル HTTP サーバでカタログを閲覧・投入(既定 8910)
|
|
130
132
|
|
|
131
133
|
環境変数:
|
|
132
|
-
AKARI_HOME
|
|
134
|
+
AKARI_HOME マシン設定の置き場(既定: ~/.akari)
|
|
135
|
+
AKARI_LIBRARY_ROOT ライブラリの置き場の上書き
|
|
133
136
|
AKARI_ASSETS_CATALOG カタログの取得元。URL またはローカルパス(既定: akari-oss.app/assets/catalog.json)
|
|
134
137
|
AKARI_ASSETS_BASE 素材実体の配信ベースの上書き(既定はカタログの "base" フィールド)
|
|
135
138
|
AKARI_STORE_API entitlements API のホスト上書き(既定: akari-oss.app)`);
|
|
@@ -139,6 +142,12 @@ async function main() {
|
|
|
139
142
|
const [sub, ...rest] = process.argv.slice(2);
|
|
140
143
|
const env = process.env;
|
|
141
144
|
|
|
145
|
+
if (sub === 'migrate') {
|
|
146
|
+
const result = await migrateAssetLibrary({ env, dryRun: rest.includes('--dry-run') });
|
|
147
|
+
console.log(JSON.stringify(result, null, 2));
|
|
148
|
+
if (result.failures.length) process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
142
151
|
if (sub === 'list') return cmdList(rest, env);
|
|
143
152
|
if (sub === 'fetch') return cmdFetch(rest, env);
|
|
144
153
|
if (sub === 'bundle') return cmdBundle(rest, env);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isAssetCached, localAssetDir } from './library.mjs';
|
|
1
|
+
import { isAssetCached, cachedAssetDir } from './library.mjs';
|
|
3
2
|
import { readProjectReferences, removeProjectReference } from './project-references.mjs';
|
|
4
3
|
import { copyIntoProject, resolve as resolveAsset } from './resolve.mjs';
|
|
5
4
|
|
|
@@ -12,10 +11,9 @@ export async function bundleProjectReferences({
|
|
|
12
11
|
const result = { planned, materialized: [], failures: [] };
|
|
13
12
|
if (dryRun) return result;
|
|
14
13
|
|
|
15
|
-
const home = resolveAkariHome(env);
|
|
16
14
|
for (const reference of planned) {
|
|
17
15
|
try {
|
|
18
|
-
if (!isAssetCached(
|
|
16
|
+
if (!isAssetCached(env, reference.category, reference.id)) {
|
|
19
17
|
const resolved = await resolveAsset(reference.id, { env });
|
|
20
18
|
if (resolved.category !== reference.category) {
|
|
21
19
|
throw new Error(
|
|
@@ -23,7 +21,7 @@ export async function bundleProjectReferences({
|
|
|
23
21
|
);
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
|
-
const sourceDir =
|
|
24
|
+
const sourceDir = cachedAssetDir(env, reference.category, reference.id);
|
|
27
25
|
const projectDir = await copyIntoProject(
|
|
28
26
|
sourceDir,
|
|
29
27
|
project,
|