makefx 0.2.5 → 0.2.7

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/makefx.mjs +311 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -176,4 +176,4 @@ Run `makefx --help` or `makefx help <command>` for command-specific options.
176
176
  - Media generation guide: https://makefx.app/docs/media-playbooks
177
177
  - Source: https://github.com/krasnoperov/inventory
178
178
 
179
- Version: 0.2.5
179
+ Version: 0.2.7
package/makefx.mjs CHANGED
@@ -4,9 +4,10 @@ import process$1 from "node:process";
4
4
  import { createHash, randomBytes } from "node:crypto";
5
5
  import http from "node:http";
6
6
  import { spawn } from "node:child_process";
7
- import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
7
+ import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
8
8
  import os from "node:os";
9
9
  import path from "node:path";
10
+ import { createReadStream } from "node:fs";
10
11
  import https from "node:https";
11
12
  //#region \0rolldown/runtime.js
12
13
  var __create = Object.create;
@@ -5160,6 +5161,167 @@ function resolveMediaType(ext, requestedMediaKind) {
5160
5161
  return mediaType;
5161
5162
  }
5162
5163
  //#endregion
5164
+ //#region src/shared/websocket-types.ts
5165
+ var DEFAULT_MEDIA_KIND = "image";
5166
+ //#endregion
5167
+ //#region src/cli/lib/mirror-store.ts
5168
+ var MIRROR_REGISTRY_VERSION = 1;
5169
+ var MIRROR_REGISTRY_FILE = "mirrors.json";
5170
+ function getMirrorRegistryPath(projectRoot = process.cwd()) {
5171
+ return path.join(projectRoot, ".inventory", MIRROR_REGISTRY_FILE);
5172
+ }
5173
+ async function readMirrorRegistry(projectRoot = process.cwd()) {
5174
+ const registryPath = getMirrorRegistryPath(projectRoot);
5175
+ let raw;
5176
+ try {
5177
+ raw = await readFile(registryPath, "utf8");
5178
+ } catch (error) {
5179
+ if (error.code === "ENOENT") return emptyRegistry();
5180
+ throw error;
5181
+ }
5182
+ let parsed;
5183
+ try {
5184
+ parsed = JSON.parse(raw);
5185
+ } catch {
5186
+ throw new Error(`Mirror registry is not valid JSON: ${registryPath}. Move it aside or delete it so the CLI can recreate it.`);
5187
+ }
5188
+ if (!isMirrorRegistry(parsed)) throw new Error(`Mirror registry has an unsupported format: ${registryPath}`);
5189
+ return parsed;
5190
+ }
5191
+ async function writeMirrorRegistry(registry, projectRoot = process.cwd()) {
5192
+ const registryPath = getMirrorRegistryPath(projectRoot);
5193
+ await mkdir(path.dirname(registryPath), { recursive: true });
5194
+ const tempPath = `${registryPath}.${process.pid}.${Date.now()}.tmp`;
5195
+ await writeFile(tempPath, `${JSON.stringify(registry, null, 2)}\n`, "utf8");
5196
+ await rename(tempPath, registryPath);
5197
+ return registryPath;
5198
+ }
5199
+ async function fingerprintFile(filePath) {
5200
+ const fileStat = await stat(filePath);
5201
+ if (!fileStat.isFile()) throw new Error(`Mirror source is not a file: ${filePath}`);
5202
+ const hash = createHash("sha256");
5203
+ await new Promise((resolve, reject) => {
5204
+ const stream = createReadStream(filePath);
5205
+ stream.on("data", (chunk) => hash.update(chunk));
5206
+ stream.on("error", reject);
5207
+ stream.on("end", resolve);
5208
+ });
5209
+ return {
5210
+ sha256: hash.digest("hex"),
5211
+ sizeBytes: fileStat.size
5212
+ };
5213
+ }
5214
+ async function resolveMirrorForFile(input) {
5215
+ const projectRoot = input.projectRoot ?? process.cwd();
5216
+ const registry = await readMirrorRegistry(projectRoot);
5217
+ const fingerprint = await fingerprintFile(input.filePath);
5218
+ const pathAlias = toMirrorPathAlias(input.filePath, projectRoot);
5219
+ const mediaKind = input.mediaKind ?? "image";
5220
+ return {
5221
+ fingerprint,
5222
+ digestEntry: findMirrorByDigest(registry, {
5223
+ ...input,
5224
+ mediaKind,
5225
+ ...fingerprint
5226
+ }),
5227
+ pathEntry: findMirrorByPath(registry, {
5228
+ ...input,
5229
+ mediaKind,
5230
+ pathAlias
5231
+ }),
5232
+ pathAlias
5233
+ };
5234
+ }
5235
+ async function recordMirrorForFile(input) {
5236
+ const projectRoot = input.projectRoot ?? process.cwd();
5237
+ const registry = await readMirrorRegistry(projectRoot);
5238
+ const fingerprint = await fingerprintFile(input.filePath);
5239
+ const pathAlias = toMirrorPathAlias(input.filePath, projectRoot);
5240
+ const mediaKind = input.mediaKind ?? "image";
5241
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5242
+ movePathAliasToCurrentMirror(registry, {
5243
+ ...input,
5244
+ mediaKind,
5245
+ pathAlias
5246
+ });
5247
+ const existing = findMirrorByDigest(registry, {
5248
+ ...input,
5249
+ mediaKind,
5250
+ ...fingerprint
5251
+ });
5252
+ const entry = {
5253
+ version: MIRROR_REGISTRY_VERSION,
5254
+ baseUrl: input.baseUrl,
5255
+ ...input.environment ? { environment: input.environment } : {},
5256
+ spaceId: input.spaceId,
5257
+ sha256: fingerprint.sha256,
5258
+ sizeBytes: fingerprint.sizeBytes,
5259
+ paths: mergePathAliases(existing?.paths ?? [], pathAlias),
5260
+ assetId: input.assetId,
5261
+ variantId: input.variantId,
5262
+ mediaKind,
5263
+ mediaKey: input.mediaKey ?? null,
5264
+ updatedAt: now
5265
+ };
5266
+ if (existing) {
5267
+ const index = registry.entries.indexOf(existing);
5268
+ registry.entries[index] = entry;
5269
+ } else registry.entries.push(entry);
5270
+ await writeMirrorRegistry(registry, projectRoot);
5271
+ return entry;
5272
+ }
5273
+ function describeMirrorMismatch(filePath, entry) {
5274
+ return [
5275
+ `Local reference changed since it was mirrored: ${filePath}`,
5276
+ `Previously mirrored to variant ${entry.variantId} in space ${entry.spaceId}.`,
5277
+ "Use the existing variant ID explicitly, restore the original file, or upload/register a new reference intentionally."
5278
+ ].join(" ");
5279
+ }
5280
+ function findMirrorByDigest(registry, input) {
5281
+ return registry.entries.find((entry) => entry.baseUrl === input.baseUrl && entry.spaceId === input.spaceId && entry.sha256 === input.sha256 && entry.sizeBytes === input.sizeBytes && entry.mediaKind === input.mediaKind && environmentMatches(entry.environment, input.environment));
5282
+ }
5283
+ function findMirrorByPath(registry, input) {
5284
+ return registry.entries.find((entry) => entry.baseUrl === input.baseUrl && entry.spaceId === input.spaceId && entry.paths.includes(input.pathAlias) && entry.mediaKind === input.mediaKind && environmentMatches(entry.environment, input.environment));
5285
+ }
5286
+ function environmentMatches(entryEnvironment, inputEnvironment) {
5287
+ return (entryEnvironment ?? "") === (inputEnvironment ?? "");
5288
+ }
5289
+ function movePathAliasToCurrentMirror(registry, input) {
5290
+ for (const entry of registry.entries) {
5291
+ if (entry.baseUrl !== input.baseUrl || entry.spaceId !== input.spaceId || entry.mediaKind !== input.mediaKind || !environmentMatches(entry.environment, input.environment)) continue;
5292
+ entry.paths = entry.paths.filter((pathAlias) => pathAlias !== input.pathAlias);
5293
+ }
5294
+ }
5295
+ function toMirrorPathAlias(filePath, projectRoot) {
5296
+ const absolutePath = path.resolve(filePath);
5297
+ const relativePath = path.relative(projectRoot, absolutePath);
5298
+ if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) return normalizePath(relativePath);
5299
+ return normalizePath(absolutePath);
5300
+ }
5301
+ function normalizePath(value) {
5302
+ return value.split(path.sep).join("/");
5303
+ }
5304
+ function mergePathAliases(paths, pathAlias) {
5305
+ return Array.from(new Set([...paths, pathAlias])).sort();
5306
+ }
5307
+ function emptyRegistry() {
5308
+ return {
5309
+ version: MIRROR_REGISTRY_VERSION,
5310
+ entries: []
5311
+ };
5312
+ }
5313
+ function isMirrorRegistry(value) {
5314
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
5315
+ const registry = value;
5316
+ if (registry.version !== MIRROR_REGISTRY_VERSION || !Array.isArray(registry.entries)) return false;
5317
+ return registry.entries.every(isMirrorEntry);
5318
+ }
5319
+ function isMirrorEntry(value) {
5320
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
5321
+ const entry = value;
5322
+ return entry.version === MIRROR_REGISTRY_VERSION && typeof entry.baseUrl === "string" && (entry.environment === void 0 || typeof entry.environment === "string") && typeof entry.spaceId === "string" && typeof entry.sha256 === "string" && typeof entry.sizeBytes === "number" && Array.isArray(entry.paths) && entry.paths.every((item) => typeof item === "string") && typeof entry.assetId === "string" && typeof entry.variantId === "string" && typeof entry.mediaKind === "string" && (entry.mediaKey === void 0 || entry.mediaKey === null || typeof entry.mediaKey === "string") && typeof entry.updatedAt === "string";
5323
+ }
5324
+ //#endregion
5163
5325
  //#region src/cli/commands/upload.ts
5164
5326
  /**
5165
5327
  * Upload Command - Upload one media file to create variants
@@ -5176,6 +5338,7 @@ var defaultDeps$12 = {
5176
5338
  fetch,
5177
5339
  readFile,
5178
5340
  stat,
5341
+ recordMirrorForFile,
5179
5342
  print: console.log
5180
5343
  };
5181
5344
  var UploadUsageError = class extends Error {};
@@ -5318,6 +5481,18 @@ async function executeUpload(parsed, deps = defaultDeps$12) {
5318
5481
  lineage: upload.lineage,
5319
5482
  organization
5320
5483
  };
5484
+ await recordUploadMirror({
5485
+ deps,
5486
+ projectRoot: projectConfig?.projectRoot,
5487
+ env,
5488
+ baseUrl,
5489
+ spaceId,
5490
+ filePath,
5491
+ assetId: upload.asset?.id ?? upload.variant.asset_id,
5492
+ variantId: upload.variant.id,
5493
+ mediaKind: upload.variant.media_kind || mediaType.mediaKind,
5494
+ mediaKey: upload.variant.media_key || upload.variant.image_key
5495
+ });
5321
5496
  if (jsonOutput) {
5322
5497
  deps.print(JSON.stringify(result, null, 2));
5323
5498
  return result;
@@ -5359,6 +5534,24 @@ async function executeUpload(parsed, deps = defaultDeps$12) {
5359
5534
  throw error;
5360
5535
  }
5361
5536
  }
5537
+ async function recordUploadMirror(input) {
5538
+ if (!input.deps.recordMirrorForFile) return;
5539
+ try {
5540
+ await input.deps.recordMirrorForFile({
5541
+ projectRoot: input.projectRoot,
5542
+ baseUrl: input.baseUrl,
5543
+ environment: input.env,
5544
+ spaceId: input.spaceId,
5545
+ filePath: input.filePath,
5546
+ assetId: input.assetId,
5547
+ variantId: input.variantId,
5548
+ mediaKind: input.mediaKind,
5549
+ mediaKey: input.mediaKey
5550
+ });
5551
+ } catch (error) {
5552
+ console.warn(`Warning: upload succeeded but mirror registry was not updated: ${error instanceof Error ? error.message : String(error)}`);
5553
+ }
5554
+ }
5362
5555
  function parseJsonObjectOption(value, optionName) {
5363
5556
  if (value === void 0) return void 0;
5364
5557
  let parsed;
@@ -5631,9 +5824,6 @@ Examples:
5631
5824
  `);
5632
5825
  }
5633
5826
  //#endregion
5634
- //#region src/shared/websocket-types.ts
5635
- var DEFAULT_MEDIA_KIND = "image";
5636
- //#endregion
5637
5827
  //#region src/cli/lib/image-transfer.ts
5638
5828
  var MAX_FILE_SIZE_MB = 10;
5639
5829
  var MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024;
@@ -22898,6 +23088,8 @@ var defaultDeps$10 = {
22898
23088
  resolveBaseUrl,
22899
23089
  createClient: WebSocketClient.create,
22900
23090
  uploadLocalReference: uploadLocalImageAsReference,
23091
+ resolveMirrorForFile,
23092
+ recordMirrorForFile,
22901
23093
  downloadImage,
22902
23094
  downloadFile,
22903
23095
  saveRunManifest,
@@ -23030,6 +23222,7 @@ async function prepareAudioParsedArgs(parsed, mode) {
23030
23222
  const positionals = [...parsed.positionals];
23031
23223
  const effectiveMode = mode ?? getAudioModeFromType(options.type);
23032
23224
  validateAudioProviderOption(options.provider, effectiveMode);
23225
+ validateAudioModelOption(options.model, effectiveMode);
23033
23226
  if (mode) options.type = getMediaOperationEntry(mode).assetType;
23034
23227
  validateAudioVoiceOptions(options);
23035
23228
  if (options.input) {
@@ -23061,6 +23254,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
23061
23254
  mediaKind
23062
23255
  });
23063
23256
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
23257
+ const audioModelOptions = parseAudioGenerationOptions(parsed, mediaKind);
23064
23258
  const videoAudioOptions = parseVideoAudioOptions(parsed, mediaKind);
23065
23259
  const videoOptions = parseVideoGenerationOptions(parsed, mediaKind);
23066
23260
  const styleSelection = await resolveStyleSelection(parsed, ctx, deps);
@@ -23070,7 +23264,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
23070
23264
  mediaKind,
23071
23265
  prompt,
23072
23266
  count: 1,
23073
- model: imageOptions.model,
23267
+ model: imageOptions.model ?? audioModelOptions.model,
23074
23268
  imageSize: imageOptions.imageSize,
23075
23269
  ...musicProvider ? { musicProvider } : {},
23076
23270
  ...videoAudioOptions,
@@ -23084,6 +23278,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
23084
23278
  assetType,
23085
23279
  prompt,
23086
23280
  ...imageOptions,
23281
+ ...audioModelOptions,
23087
23282
  aspectRatio: imageOptions.aspectRatio ?? videoOptions.aspectRatio,
23088
23283
  disableStyle: parsed.options["no-style"] === "true",
23089
23284
  ...styleSelection.stylePresetId ? { stylePresetId: styleSelection.stylePresetId } : {},
@@ -23129,6 +23324,10 @@ function validateAudioProviderOption(value, mode) {
23129
23324
  if (mode !== "music") throw new Error("--provider is only supported for audio music");
23130
23325
  if (value !== "elevenlabs" && value !== "lyria") throw new Error("--provider must be elevenlabs or lyria");
23131
23326
  }
23327
+ function validateAudioModelOption(value, mode) {
23328
+ if (!value) return;
23329
+ if (mode !== "speech" && mode !== "dialogue") throw new Error("--model is only supported for speech and dialogue audio");
23330
+ }
23132
23331
  function parseMusicProviderOption(parsed, mediaKind, assetType) {
23133
23332
  const value = parsed.options.provider;
23134
23333
  if (!value) return void 0;
@@ -23439,6 +23638,7 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
23439
23638
  const mode = parseBatchMode(parsed.options.mode || "explore");
23440
23639
  const refs = parsed.options.refs ? parseRefs(parsed.options.refs) : [];
23441
23640
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
23641
+ const audioModelOptions = parseAudioGenerationOptions(parsed, mediaKind);
23442
23642
  if (mediaKind === "image") validateImageReferenceCount(imageOptions.model, refs.length);
23443
23643
  const state = await requestSpaceState(client);
23444
23644
  const referenceVariantIds = refs.length > 0 ? await resolveReferenceVariantIds(refs, ctx, deps, state.variants, mediaKind) : void 0;
@@ -23451,7 +23651,7 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
23451
23651
  mediaKind,
23452
23652
  prompt,
23453
23653
  count,
23454
- model: imageOptions.model,
23654
+ model: imageOptions.model ?? audioModelOptions.model,
23455
23655
  imageSize: imageOptions.imageSize,
23456
23656
  ...musicProvider ? { musicProvider } : {}
23457
23657
  });
@@ -23467,6 +23667,7 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
23467
23667
  mode,
23468
23668
  referenceVariantIds,
23469
23669
  ...imageOptions,
23670
+ ...audioModelOptions,
23470
23671
  aspectRatio: imageOptions.aspectRatio,
23471
23672
  disableStyle: parsed.options["no-style"] === "true",
23472
23673
  ...styleSelection.stylePresetId ? { stylePresetId: styleSelection.stylePresetId } : {},
@@ -23615,12 +23816,41 @@ async function resolveReferenceVariantIds(refs, ctx, deps, variants = [], mediaK
23615
23816
  const variantIds = [];
23616
23817
  for (const ref of refs) {
23617
23818
  if (await deps.fileExists(ref)) {
23819
+ const localReferenceMediaKind = mediaKind === "video" ? CLI_GENERATION_MEDIA_KIND : mediaKind;
23820
+ const mirror = deps.resolveMirrorForFile ? await deps.resolveMirrorForFile({
23821
+ projectRoot: ctx.projectRoot,
23822
+ baseUrl: ctx.baseUrl,
23823
+ environment: ctx.env,
23824
+ spaceId: ctx.spaceId,
23825
+ filePath: ref,
23826
+ mediaKind: localReferenceMediaKind
23827
+ }) : void 0;
23828
+ if (mirror?.pathEntry && (mirror.pathEntry.sha256 !== mirror.fingerprint.sha256 || mirror.pathEntry.sizeBytes !== mirror.fingerprint.sizeBytes)) throw new Error(describeMirrorMismatch(ref, mirror.pathEntry));
23829
+ if (mirror?.digestEntry) {
23830
+ validateReferenceVariant(mirror.digestEntry.variantId, variants, mediaKind);
23831
+ await recordReusedReferenceMirror({
23832
+ ref,
23833
+ ctx,
23834
+ deps,
23835
+ entry: mirror.digestEntry
23836
+ });
23837
+ variantIds.push(mirror.digestEntry.variantId);
23838
+ continue;
23839
+ }
23840
+ if (mirror?.pathEntry) throw new Error(describeMirrorMismatch(ref, mirror.pathEntry));
23618
23841
  const uploaded = await deps.uploadLocalReference({
23619
23842
  baseUrl: ctx.baseUrl,
23620
23843
  accessToken: ctx.accessToken,
23621
23844
  spaceId: ctx.spaceId,
23622
23845
  filePath: ref
23623
23846
  });
23847
+ if (deps.recordMirrorForFile) await recordUploadedReferenceMirror({
23848
+ ref,
23849
+ ctx,
23850
+ deps,
23851
+ uploaded,
23852
+ mediaKind: localReferenceMediaKind
23853
+ });
23624
23854
  variantIds.push(uploaded.variant.id);
23625
23855
  continue;
23626
23856
  }
@@ -23630,6 +23860,40 @@ async function resolveReferenceVariantIds(refs, ctx, deps, variants = [], mediaK
23630
23860
  }
23631
23861
  return variantIds;
23632
23862
  }
23863
+ async function recordReusedReferenceMirror(input) {
23864
+ try {
23865
+ await input.deps.recordMirrorForFile?.({
23866
+ projectRoot: input.ctx.projectRoot,
23867
+ baseUrl: input.ctx.baseUrl,
23868
+ environment: input.ctx.env,
23869
+ spaceId: input.ctx.spaceId,
23870
+ filePath: input.ref,
23871
+ assetId: input.entry.assetId,
23872
+ variantId: input.entry.variantId,
23873
+ mediaKind: input.entry.mediaKind,
23874
+ mediaKey: input.entry.mediaKey
23875
+ });
23876
+ } catch (error) {
23877
+ console.warn(`Warning: reused mirrored reference but mirror registry alias was not updated: ${error instanceof Error ? error.message : String(error)}`);
23878
+ }
23879
+ }
23880
+ async function recordUploadedReferenceMirror(input) {
23881
+ try {
23882
+ await input.deps.recordMirrorForFile?.({
23883
+ projectRoot: input.ctx.projectRoot,
23884
+ baseUrl: input.ctx.baseUrl,
23885
+ environment: input.ctx.env,
23886
+ spaceId: input.ctx.spaceId,
23887
+ filePath: input.ref,
23888
+ assetId: input.uploaded.asset?.id ?? input.uploaded.variant.asset_id,
23889
+ variantId: input.uploaded.variant.id,
23890
+ mediaKind: input.uploaded.variant.media_kind ?? input.mediaKind,
23891
+ mediaKey: input.uploaded.variant.media_key ?? input.uploaded.variant.image_key
23892
+ });
23893
+ } catch (error) {
23894
+ console.warn(`Warning: reference upload succeeded but mirror registry was not updated: ${error instanceof Error ? error.message : String(error)}`);
23895
+ }
23896
+ }
23633
23897
  function validateReferenceVariant(ref, variants, mediaKind) {
23634
23898
  const variant = variants.find((candidate) => candidate.id === ref);
23635
23899
  if (!variant) throw new Error(`Reference variant not found in space sync state: ${ref}`);
@@ -23857,6 +24121,11 @@ function normalizeCliOption(value) {
23857
24121
  if (value === void 0 || value === "true") return void 0;
23858
24122
  return value.trim() || void 0;
23859
24123
  }
24124
+ function parseAudioGenerationOptions(parsed, mediaKind) {
24125
+ if (mediaKind !== "audio") return {};
24126
+ const model = normalizeCliOption(readOptionalOption(parsed, "model", "model"));
24127
+ return model ? { model } : {};
24128
+ }
23860
24129
  function parseImageGenerationOptions(parsed, mediaKind) {
23861
24130
  if (mediaKind !== "image") return {};
23862
24131
  const model = parseImageModelOption(readOptionalOption(parsed, "model", "model"));
@@ -24400,6 +24669,7 @@ var defaultDeps$6 = {
24400
24669
  resolveBaseUrl,
24401
24670
  fetch,
24402
24671
  downloadFile,
24672
+ recordMirrorForFile,
24403
24673
  createMutationClient: (env, spaceId) => WebSocketClient.create(env, spaceId),
24404
24674
  print: console.log
24405
24675
  };
@@ -24456,6 +24726,12 @@ async function executeAssets(parsed, deps = defaultDeps$6) {
24456
24726
  outputPath,
24457
24727
  force: ctx.force
24458
24728
  });
24729
+ await recordDownloadedMirror({
24730
+ ctx,
24731
+ deps,
24732
+ outputPath,
24733
+ variant: resolved.variant
24734
+ });
24459
24735
  deps.print(`Downloaded ${resolved.mediaKey} to ${outputPath}`);
24460
24736
  return {
24461
24737
  type: "download",
@@ -24515,9 +24791,29 @@ async function buildContext$6(parsed, deps) {
24515
24791
  spaceId,
24516
24792
  baseUrl: deps.resolveBaseUrl(env),
24517
24793
  accessToken: config.token.accessToken,
24518
- force: parsed.options.force === "true"
24794
+ force: parsed.options.force === "true",
24795
+ projectRoot: projectConfig?.projectRoot
24519
24796
  };
24520
24797
  }
24798
+ async function recordDownloadedMirror(input) {
24799
+ const variant = input.variant;
24800
+ if (!variant || !input.deps.recordMirrorForFile) return;
24801
+ try {
24802
+ await input.deps.recordMirrorForFile({
24803
+ projectRoot: input.ctx.projectRoot,
24804
+ baseUrl: input.ctx.baseUrl,
24805
+ environment: input.ctx.env,
24806
+ spaceId: input.ctx.spaceId,
24807
+ filePath: input.outputPath,
24808
+ assetId: variant.asset_id,
24809
+ variantId: variant.id,
24810
+ mediaKind: variant.media_kind || "image",
24811
+ mediaKey: variant.media_key || variant.image_key
24812
+ });
24813
+ } catch (error) {
24814
+ console.warn(`Warning: download succeeded but mirror registry was not updated: ${error instanceof Error ? error.message : String(error)}`);
24815
+ }
24816
+ }
24521
24817
  async function listAssets$1(ctx, deps) {
24522
24818
  return (await fetchJson(ctx, deps, `/api/spaces/${ctx.spaceId}/assets`)).assets || [];
24523
24819
  }
@@ -26270,7 +26566,7 @@ Options:
26270
26566
  }
26271
26567
  //#endregion
26272
26568
  //#region src/cli/index.ts
26273
- var CLI_VERSION = "0.2.5+fc579b35cabc";
26569
+ var CLI_VERSION = "0.2.7+1943b2e5248a";
26274
26570
  async function main() {
26275
26571
  const argv = process$1.argv.slice(2);
26276
26572
  if (argv[0] === "--") argv.shift();
@@ -26755,6 +27051,7 @@ Usage:
26755
27051
  makefx audio ${first} generate --follow <variant_id> -o <file> [--space <id>]
26756
27052
  ${first === "music" ? " makefx audio music generate \"prompt\" --provider lyria --name <name> -o <file> [--space <id>]\n" : ""}
26757
27053
  ${first === "music" ? "Provider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
27054
+ ${first === "speech" || first === "dialogue" ? "Model selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
26758
27055
  ${first === "speech" || first === "dialogue" ? "Voice selection:\n --voice <voice_id> Speech voice, or dialogue fallback voice\n --dialogue-voices <id,id,...> Dialogue voices ordered by first speaker appearance\n" : ""}
26759
27056
  `);
26760
27057
  return;
@@ -26765,6 +27062,8 @@ Usage:
26765
27062
  makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
26766
27063
  ${first === "music" ? " makefx audio music batch \"prompt\" --provider lyria --name <name> --count <2-8> --output-dir <dir> [--space <id>]\n" : ""}
26767
27064
  ${first === "music" ? "Provider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
27065
+ ${first === "speech" || first === "dialogue" ? "Model selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
27066
+ ${first === "speech" || first === "dialogue" ? "Voice selection:\n --voice <voice_id> Speech voice, or dialogue fallback voice\n --dialogue-voices <id,id,...> Dialogue voices ordered by first speaker appearance\n" : ""}
26768
27067
  `);
26769
27068
  return;
26770
27069
  }
@@ -26773,6 +27072,7 @@ Usage:
26773
27072
  makefx audio ${first} generate "prompt" --name <name> -o <file> [--space <id>]
26774
27073
  makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
26775
27074
  ${first === "music" ? "\nProvider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
27075
+ ${first === "speech" || first === "dialogue" ? "\nModel selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
26776
27076
  ${first === "speech" || first === "dialogue" ? "\nVoice selection:\n --voice <voice_id> Speech voice, or dialogue fallback voice\n --dialogue-voices <id,id,...> Dialogue voices ordered by first speaker appearance\n" : ""}
26777
27077
  `);
26778
27078
  return;
@@ -26816,6 +27116,9 @@ Voice selection:
26816
27116
  --voice <voice_id> Speech voice, or dialogue fallback voice
26817
27117
  --dialogue-voices <id,id,...> Dialogue voices ordered by first speaker appearance
26818
27118
 
27119
+ Model selection:
27120
+ --model <model_id> ElevenLabs speech/dialogue model (default: eleven_v3)
27121
+
26819
27122
  Low-level compatibility:
26820
27123
  makefx audio generate "prompt" --name <name> --type <type> -o <file> [--space <id>]
26821
27124
  makefx audio batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--space <id>]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makefx",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Command-line interface for AI-assisted game asset production with Make Effects.",
5
5
  "license": "MIT",
6
6
  "type": "module",