makefx 1.0.6 → 1.1.0

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 +932 -192
  3. package/package.json +1 -1
package/makefx.mjs CHANGED
@@ -844,7 +844,7 @@ function validateProjectConfig(config, configPath) {
844
844
  * makefx spaces inspect-deleted <space_id> Inspect a soft-deleted space
845
845
  * makefx spaces restore <space_id> Restore a soft-deleted space
846
846
  */
847
- var defaultDeps$9 = {
847
+ var defaultDeps$11 = {
848
848
  loadConfig: loadStoredConfig,
849
849
  resolveBaseUrl,
850
850
  saveProjectConfig,
@@ -859,7 +859,7 @@ async function handleSpaces(parsed) {
859
859
  process$1.exitCode = 1;
860
860
  }
861
861
  }
862
- async function executeSpaces(parsed, deps = defaultDeps$9) {
862
+ async function executeSpaces(parsed, deps = defaultDeps$11) {
863
863
  const env = resolveCommandEnvironment(parsed);
864
864
  const showDetails = parsed.options.details === "true";
865
865
  const spaceId = parsed.options.id;
@@ -5386,7 +5386,7 @@ function isMirrorEntry(value) {
5386
5386
  * makefx upload <file> --space <id> --name <name> Upload as new asset
5387
5387
  * makefx upload <file> --space <id> --name <name> --type <type>
5388
5388
  */
5389
- var defaultDeps$8 = {
5389
+ var defaultDeps$10 = {
5390
5390
  loadConfig: loadStoredConfig,
5391
5391
  loadProjectConfig,
5392
5392
  resolveBaseUrl,
@@ -5415,11 +5415,11 @@ async function handleUpload(parsed) {
5415
5415
  await executeUpload(parsed);
5416
5416
  } catch (error) {
5417
5417
  console.error(error instanceof Error ? `Error: ${error.message}` : "Error: Upload failed");
5418
- if (error instanceof UploadUsageError) printUsage$8();
5418
+ if (error instanceof UploadUsageError) printUsage$10();
5419
5419
  process$1.exitCode = 1;
5420
5420
  }
5421
5421
  }
5422
- async function executeUpload(parsed, deps = defaultDeps$8) {
5422
+ async function executeUpload(parsed, deps = defaultDeps$10) {
5423
5423
  const filePath = parsed.positionals[0];
5424
5424
  if (!filePath) throw new UploadUsageError("File path is required");
5425
5425
  rejectRetiredUploadOrganizationOptions(parsed);
@@ -5599,7 +5599,7 @@ function parseCsvOption(value, optionName) {
5599
5599
  if (value === "true") throw new UploadUsageError(`${optionName} requires a value`);
5600
5600
  return value.split(",").map((entry) => entry.trim()).filter(Boolean);
5601
5601
  }
5602
- function printUsage$8() {
5602
+ function printUsage$10() {
5603
5603
  console.log(`
5604
5604
  Usage:
5605
5605
  makefx upload <file> --asset <id> [--space <id>] Upload media to existing asset
@@ -5916,6 +5916,15 @@ var ServerConfirmationTimeoutError = class extends Error {
5916
5916
  this.name = "ServerConfirmationTimeoutError";
5917
5917
  }
5918
5918
  };
5919
+ function collectionItemMatchesInput(item, input) {
5920
+ if (item.subject_type !== input.subjectType) return false;
5921
+ if (input.subjectType === "asset" && item.asset_id !== input.assetId) return false;
5922
+ if (input.subjectType === "variant" && item.variant_id !== input.variantId) return false;
5923
+ if (input.role !== void 0 && item.role !== input.role) return false;
5924
+ if (input.pinnedVariantId !== void 0 && item.pinned_variant_id !== input.pinnedVariantId) return false;
5925
+ if (input.sortIndex !== void 0 && item.sort_index !== input.sortIndex) return false;
5926
+ return true;
5927
+ }
5919
5928
  var WebSocketClient = class WebSocketClient {
5920
5929
  ws = null;
5921
5930
  baseUrl;
@@ -6219,9 +6228,9 @@ var WebSocketClient = class WebSocketClient {
6219
6228
  case "error": {
6220
6229
  const errorMsg = message;
6221
6230
  const error = /* @__PURE__ */ new Error(`${errorMsg.code}: ${errorMsg.message}`);
6222
- const mutationConsumed = this.failMutationWaiters(error);
6223
- const estimateConsumed = this.failEstimateHandlers(error);
6224
- if (!(mutationConsumed || estimateConsumed)) {
6231
+ const requestConsumed = errorMsg.requestId ? this.failRequestHandler(errorMsg.requestId, error) : false;
6232
+ const mutationConsumed = requestConsumed ? false : this.failMutationWaiters(error);
6233
+ if (!requestConsumed && !mutationConsumed) {
6225
6234
  console.error(`Server error: ${formatServerErrorMessage(errorMsg.code, errorMsg.message)}`);
6226
6235
  this.onError?.(new Error(errorMsg.message));
6227
6236
  }
@@ -6286,14 +6295,36 @@ var WebSocketClient = class WebSocketClient {
6286
6295
  }
6287
6296
  return true;
6288
6297
  }
6289
- failEstimateHandlers(error) {
6290
- if (this.estimateHandlers.size === 0) return false;
6291
- for (const [requestId, handler] of this.estimateHandlers) {
6292
- clearTimeout(handler.timeout);
6298
+ failRequestHandler(requestId, error) {
6299
+ const estimateHandler = this.estimateHandlers.get(requestId);
6300
+ if (estimateHandler) {
6293
6301
  this.estimateHandlers.delete(requestId);
6302
+ clearTimeout(estimateHandler.timeout);
6303
+ estimateHandler.reject(error);
6304
+ return true;
6305
+ }
6306
+ const generateHandler = this.generateHandlers.get(requestId);
6307
+ if (generateHandler) {
6308
+ this.generateHandlers.delete(requestId);
6309
+ clearTimeout(generateHandler.timeout);
6310
+ generateHandler.reject(error);
6311
+ return true;
6312
+ }
6313
+ const batchHandler = this.batchHandlers.get(requestId);
6314
+ if (batchHandler) {
6315
+ this.batchHandlers.delete(requestId);
6316
+ clearTimeout(batchHandler.timeout);
6317
+ for (const variantId of batchHandler.pending) this.batchVariantToRequestId.delete(variantId);
6318
+ batchHandler.reject(error);
6319
+ return true;
6320
+ }
6321
+ for (const [variantId, handler] of this.variantCompletionHandlers) if (handler.requestId === requestId) {
6322
+ this.variantCompletionHandlers.delete(variantId);
6323
+ clearTimeout(handler.timeout);
6294
6324
  handler.reject(error);
6325
+ return true;
6295
6326
  }
6296
- return true;
6327
+ return false;
6297
6328
  }
6298
6329
  /**
6299
6330
  * Send a mutation message and resolve when a matching broadcast arrives, or
@@ -6378,6 +6409,42 @@ var WebSocketClient = class WebSocketClient {
6378
6409
  rating
6379
6410
  }, (msg) => msg.type === "variant:updated" && msg.variant.id === variantId && msg.variant.quality_rating === rating)).variant;
6380
6411
  }
6412
+ async createCollection(input) {
6413
+ const id = input.id ?? crypto.randomUUID();
6414
+ return (await this.awaitServerMessage({
6415
+ type: "collection:create",
6416
+ ...input,
6417
+ id
6418
+ }, (msg) => msg.type === "collection:created" && msg.collection.id === id)).collection;
6419
+ }
6420
+ async deleteCollection(collectionId) {
6421
+ await this.awaitServerMessage({
6422
+ type: "collection:delete",
6423
+ collectionId
6424
+ }, (msg) => msg.type === "collection:deleted" && msg.collectionId === collectionId);
6425
+ }
6426
+ async createCollectionItem(input) {
6427
+ const id = input.id ?? crypto.randomUUID();
6428
+ return (await this.awaitServerMessage({
6429
+ type: "collection_item:create",
6430
+ ...input,
6431
+ id
6432
+ }, (msg) => msg.type === "collection_item:created" && msg.item.collection_id === input.collectionId && msg.item.id === id && collectionItemMatchesInput(msg.item, input))).item;
6433
+ }
6434
+ async deleteCollectionItem(collectionId, itemId) {
6435
+ await this.awaitServerMessage({
6436
+ type: "collection_item:delete",
6437
+ collectionId,
6438
+ itemId
6439
+ }, (msg) => msg.type === "collection_item:deleted" && msg.collectionId === collectionId && msg.itemId === itemId);
6440
+ }
6441
+ async reorderCollectionItems(collectionId, itemIds) {
6442
+ return (await this.awaitServerMessage({
6443
+ type: "collection_items:reorder",
6444
+ collectionId,
6445
+ itemIds
6446
+ }, (msg) => msg.type === "collection_items:reordered" && msg.collectionId === collectionId && itemIds.every((itemId, index) => msg.items[index]?.id === itemId))).items;
6447
+ }
6381
6448
  /**
6382
6449
  * Send a message through the WebSocket
6383
6450
  */
@@ -6441,6 +6508,7 @@ var WebSocketClient = class WebSocketClient {
6441
6508
  prompt: params.prompt,
6442
6509
  count: params.count,
6443
6510
  model: params.model,
6511
+ aspectRatio: params.aspectRatio,
6444
6512
  imageSize: params.imageSize,
6445
6513
  musicProvider: params.musicProvider,
6446
6514
  generateAudio: params.generateAudio,
@@ -6764,6 +6832,7 @@ var VIDEO_GENERATION_RESOLUTIONS = [
6764
6832
  "1080p",
6765
6833
  "4k"
6766
6834
  ];
6835
+ var KLING_VIDEO_GENERATION_RESOLUTIONS = ["720p", "1080p"];
6767
6836
  var VIDEO_GENERATION_DURATION_SECONDS = [
6768
6837
  4,
6769
6838
  6,
@@ -6774,7 +6843,32 @@ var VIDEO_GENERATION_TIERS = [
6774
6843
  "fast",
6775
6844
  "lite"
6776
6845
  ];
6777
- var VIDEO_MODEL_SELECTIONS = ["veo-3.1", "omni-flash"];
6846
+ var VIDEO_MODEL_SELECTIONS = [
6847
+ "veo-3.1",
6848
+ "omni-flash",
6849
+ "kling",
6850
+ "fal-seedance"
6851
+ ];
6852
+ var VIDEO_MODEL_SUPPORTED_OPERATIONS = {
6853
+ "veo-3.1": [
6854
+ "generate",
6855
+ "refine",
6856
+ "derive"
6857
+ ],
6858
+ "omni-flash": [
6859
+ "generate",
6860
+ "refine",
6861
+ "derive"
6862
+ ],
6863
+ kling: [
6864
+ "generate",
6865
+ "refine",
6866
+ "derive"
6867
+ ],
6868
+ "fal-seedance": ["generate"]
6869
+ };
6870
+ var KLING_VIDEO_MODEL = "kling-v3";
6871
+ var FAL_VIDEO_MODEL = "fal-ai/bytedance/seedance/v1/pro/text-to-video";
6778
6872
  var GEMINI_OMNI_FLASH_VIDEO_MODEL = "gemini-omni-flash-preview";
6779
6873
  var VIDEO_GENERATION_AUDIO_TOGGLE_MODELS = [];
6780
6874
  var VIDEO_GENERATION_RESOLUTIONS_BY_TIER = {
@@ -6809,7 +6903,10 @@ function getVideoGenerationModelForTier(tier = DEFAULT_VIDEO_GENERATION_TIER) {
6809
6903
  return VIDEO_GENERATION_TIER_MODELS[tier];
6810
6904
  }
6811
6905
  function getVideoGenerationModelForSelection(selection = DEFAULT_VIDEO_MODEL_SELECTION, tier = DEFAULT_VIDEO_GENERATION_TIER) {
6812
- return selection === "omni-flash" ? GEMINI_OMNI_FLASH_VIDEO_MODEL : getVideoGenerationModelForTier(tier);
6906
+ if (selection === "omni-flash") return GEMINI_OMNI_FLASH_VIDEO_MODEL;
6907
+ if (selection === "kling") return KLING_VIDEO_MODEL;
6908
+ if (selection === "fal-seedance") return FAL_VIDEO_MODEL;
6909
+ return getVideoGenerationModelForTier(tier);
6813
6910
  }
6814
6911
  function getVideoGenerationResolutionsForTier(tier = DEFAULT_VIDEO_GENERATION_TIER) {
6815
6912
  return VIDEO_GENERATION_RESOLUTIONS_BY_TIER[tier];
@@ -6817,28 +6914,55 @@ function getVideoGenerationResolutionsForTier(tier = DEFAULT_VIDEO_GENERATION_TI
6817
6914
  function isVideoGenerationResolutionSupportedForTier(resolution, tier = DEFAULT_VIDEO_GENERATION_TIER) {
6818
6915
  return VIDEO_GENERATION_RESOLUTIONS_BY_TIER[tier].includes(resolution);
6819
6916
  }
6917
+ function getVideoGenerationResolutionsForModel(model) {
6918
+ return isKlingVideoGenerationModel(model) || isFalVideoGenerationModel(model) ? KLING_VIDEO_GENERATION_RESOLUTIONS : getVideoGenerationResolutionsForTier(getVideoGenerationTierForModel(model));
6919
+ }
6920
+ function isVideoGenerationResolutionSupportedForModel(model, resolution) {
6921
+ return getVideoGenerationResolutionsForModel(model).includes(resolution);
6922
+ }
6820
6923
  function getVideoGenerationTierForModel(model) {
6821
6924
  if (typeof model !== "string") return void 0;
6822
6925
  return VIDEO_GENERATION_TIERS.find((tier) => VIDEO_GENERATION_TIER_MODELS[tier] === model);
6823
6926
  }
6927
+ function getVideoModelSelectionForModel(model) {
6928
+ if (model === "gemini-omni-flash-preview") return "omni-flash";
6929
+ if (model === "kling-v3") return "kling";
6930
+ if (model === "fal-ai/bytedance/seedance/v1/pro/text-to-video") return "fal-seedance";
6931
+ return getVideoGenerationTierForModel(model) ? "veo-3.1" : void 0;
6932
+ }
6824
6933
  function isVeoVideoGenerationModel(model) {
6825
6934
  return typeof model === "string" && getVideoGenerationTierForModel(model) !== void 0;
6826
6935
  }
6827
6936
  function isGeminiOmniVideoGenerationModel(model) {
6828
6937
  return model === GEMINI_OMNI_FLASH_VIDEO_MODEL;
6829
6938
  }
6939
+ function isKlingVideoGenerationModel(model) {
6940
+ return model === KLING_VIDEO_MODEL;
6941
+ }
6942
+ function isFalVideoGenerationModel(model) {
6943
+ return model === FAL_VIDEO_MODEL;
6944
+ }
6830
6945
  function isVideoGenerationModel(model) {
6831
- return isVeoVideoGenerationModel(model) || isGeminiOmniVideoGenerationModel(model);
6946
+ return isVeoVideoGenerationModel(model) || isGeminiOmniVideoGenerationModel(model) || isKlingVideoGenerationModel(model) || isFalVideoGenerationModel(model);
6947
+ }
6948
+ function isVideoOperationSupportedByModel(model, operation) {
6949
+ const selection = getVideoModelSelectionForModel(model);
6950
+ return selection !== void 0 && VIDEO_MODEL_SUPPORTED_OPERATIONS[selection].includes(operation);
6832
6951
  }
6833
6952
  function doesVideoGenerationModelSupportAudioToggle(model) {
6834
- return isVeoVideoGenerationModel(model) && VIDEO_GENERATION_AUDIO_TOGGLE_MODELS.includes(model);
6953
+ return isKlingVideoGenerationModel(model) || isVeoVideoGenerationModel(model) && VIDEO_GENERATION_AUDIO_TOGGLE_MODELS.includes(model);
6835
6954
  }
6836
6955
  //#endregion
6837
6956
  //#region src/shared/imageGenerationOptions.ts
6838
- var IMAGE_MODEL_SELECTIONS = ["pro", "flash"];
6957
+ var IMAGE_MODEL_SELECTIONS = [
6958
+ "pro",
6959
+ "flash",
6960
+ "flux"
6961
+ ];
6839
6962
  var IMAGE_MODEL_IDS = {
6840
6963
  pro: "gemini-3-pro-image",
6841
- flash: "gemini-3.1-flash-image"
6964
+ flash: "gemini-3.1-flash-image",
6965
+ flux: "fal-ai/flux/dev"
6842
6966
  };
6843
6967
  IMAGE_MODEL_IDS["pro"];
6844
6968
  var IMAGE_ASPECT_RATIOS = [
@@ -6862,6 +6986,13 @@ var IMAGE_MODEL_CAPABILITIES = {
6862
6986
  pro: {
6863
6987
  selection: "pro",
6864
6988
  modelId: IMAGE_MODEL_IDS.pro,
6989
+ label: "Pro",
6990
+ provider: "gemini",
6991
+ supportedOperations: [
6992
+ "generate",
6993
+ "derive",
6994
+ "refine"
6995
+ ],
6865
6996
  maxReferenceImages: 14,
6866
6997
  supportedImageSizes: [
6867
6998
  "1K",
@@ -6873,6 +7004,13 @@ var IMAGE_MODEL_CAPABILITIES = {
6873
7004
  flash: {
6874
7005
  selection: "flash",
6875
7006
  modelId: IMAGE_MODEL_IDS.flash,
7007
+ label: "Flash",
7008
+ provider: "gemini",
7009
+ supportedOperations: [
7010
+ "generate",
7011
+ "derive",
7012
+ "refine"
7013
+ ],
6876
7014
  maxReferenceImages: 14,
6877
7015
  supportedImageSizes: [
6878
7016
  "1K",
@@ -6880,6 +7018,16 @@ var IMAGE_MODEL_CAPABILITIES = {
6880
7018
  "4K"
6881
7019
  ],
6882
7020
  supportedAspectRatios: IMAGE_ASPECT_RATIOS
7021
+ },
7022
+ flux: {
7023
+ selection: "flux",
7024
+ modelId: IMAGE_MODEL_IDS.flux,
7025
+ label: "FLUX.1 Dev",
7026
+ provider: "fal",
7027
+ supportedOperations: ["generate"],
7028
+ maxReferenceImages: 0,
7029
+ supportedImageSizes: ["1K"],
7030
+ supportedAspectRatios: IMAGE_ASPECT_RATIOS
6883
7031
  }
6884
7032
  };
6885
7033
  function isImageModelSelection(value) {
@@ -6897,7 +7045,9 @@ function normalizeImageSize(value) {
6897
7045
  }
6898
7046
  function getImageModelSelection(value) {
6899
7047
  if (isImageModelSelection(value)) return value;
6900
- return value === IMAGE_MODEL_IDS.flash ? "flash" : "pro";
7048
+ if (value === IMAGE_MODEL_IDS.flash) return "flash";
7049
+ if (value === IMAGE_MODEL_IDS.flux) return "flux";
7050
+ return "pro";
6901
7051
  }
6902
7052
  function getImageModelCapabilities(model) {
6903
7053
  return IMAGE_MODEL_CAPABILITIES[getImageModelSelection(model ?? "pro")];
@@ -6911,6 +7061,9 @@ function isImageAspectRatioSupportedByModel(model, aspectRatio) {
6911
7061
  function getImageModelMaxReferenceImages(model) {
6912
7062
  return getImageModelCapabilities(model).maxReferenceImages;
6913
7063
  }
7064
+ function isImageOperationSupportedByModel(model, operation) {
7065
+ return getImageModelCapabilities(model).supportedOperations.includes(operation);
7066
+ }
6914
7067
  //#endregion
6915
7068
  //#region src/shared/mediaOperationMatrix.ts
6916
7069
  var MEDIA_OPERATION_MATRIX = [
@@ -6961,7 +7114,7 @@ var MEDIA_OPERATION_MATRIX = [
6961
7114
  assetType: "speech",
6962
7115
  promptNoun: "speech clip",
6963
7116
  inheritsReferenceAssetType: false,
6964
- compatibleSlotMediaKinds: ["audio"],
7117
+ compatibleSlotMediaKinds: [],
6965
7118
  supportsBatch: true,
6966
7119
  cliNamespace: "audio",
6967
7120
  cliCommands: ["generate", "batch"],
@@ -6976,7 +7129,7 @@ var MEDIA_OPERATION_MATRIX = [
6976
7129
  assetType: "dialogue",
6977
7130
  promptNoun: "dialogue clip",
6978
7131
  inheritsReferenceAssetType: false,
6979
- compatibleSlotMediaKinds: ["audio"],
7132
+ compatibleSlotMediaKinds: [],
6980
7133
  supportsBatch: true,
6981
7134
  cliNamespace: "audio",
6982
7135
  cliCommands: ["generate", "batch"],
@@ -6991,7 +7144,7 @@ var MEDIA_OPERATION_MATRIX = [
6991
7144
  assetType: "music",
6992
7145
  promptNoun: "music cue",
6993
7146
  inheritsReferenceAssetType: false,
6994
- compatibleSlotMediaKinds: ["audio"],
7147
+ compatibleSlotMediaKinds: [],
6995
7148
  supportsBatch: true,
6996
7149
  cliNamespace: "audio",
6997
7150
  cliCommands: ["generate", "batch"],
@@ -7006,7 +7159,7 @@ var MEDIA_OPERATION_MATRIX = [
7006
7159
  assetType: "sfx",
7007
7160
  promptNoun: "sound effect",
7008
7161
  inheritsReferenceAssetType: false,
7009
- compatibleSlotMediaKinds: ["audio"],
7162
+ compatibleSlotMediaKinds: [],
7010
7163
  supportsBatch: true,
7011
7164
  cliNamespace: "audio",
7012
7165
  cliCommands: ["generate", "batch"],
@@ -7072,7 +7225,7 @@ function cliGenerationSupportsRefs(namespace) {
7072
7225
  //#endregion
7073
7226
  //#region src/cli/commands/forge.ts
7074
7227
  var CLI_GENERATION_MEDIA_KIND = getCliGenerationMediaKind("top-level");
7075
- var defaultDeps$7 = {
7228
+ var defaultDeps$9 = {
7076
7229
  loadConfig: loadStoredConfig,
7077
7230
  loadProjectConfig,
7078
7231
  resolveBaseUrl,
@@ -7146,10 +7299,10 @@ async function handleBatch(parsed) {
7146
7299
  }
7147
7300
  async function handleForgeCommand(command, parsed) {
7148
7301
  try {
7149
- await executeForgeCommand(command, parsed, defaultDeps$7);
7302
+ await executeForgeCommand(command, parsed, defaultDeps$9);
7150
7303
  } catch (error) {
7151
7304
  console.error("Error:", error instanceof Error ? error.message : error);
7152
- if (shouldPrintUsageForForgeError(error)) printUsage$7(command);
7305
+ if (shouldPrintUsageForForgeError(error)) printUsage$9(command);
7153
7306
  process$1.exitCode = 1;
7154
7307
  }
7155
7308
  }
@@ -7158,11 +7311,11 @@ function shouldPrintUsageForForgeError(error) {
7158
7311
  const message = error.message;
7159
7312
  return message.startsWith("--") || message.startsWith("Output path is required") || message.startsWith("Output directory is required") || message.startsWith("Pass either prompt text") || message.startsWith("Speech generation requires") || message.startsWith("Dialogue generation requires") || message.startsWith("Prompt is required");
7160
7313
  }
7161
- async function executeForgeCommand(command, parsed, deps = defaultDeps$7, options = {}) {
7314
+ async function executeForgeCommand(command, parsed, deps = defaultDeps$9, options = {}) {
7162
7315
  const mediaKind = options.mediaKind || CLI_GENERATION_MEDIA_KIND;
7163
7316
  if (parsed.options.follow) {
7164
7317
  if (command === "batch") throw new Error("--follow is only supported for single-output generation commands");
7165
- const ctx = await buildContext$4(parsed, deps);
7318
+ const ctx = await buildContext$6(parsed, deps);
7166
7319
  const client = await deps.createClient(ctx.env, ctx.spaceId);
7167
7320
  try {
7168
7321
  await client.connect();
@@ -7172,11 +7325,13 @@ async function executeForgeCommand(command, parsed, deps = defaultDeps$7, option
7172
7325
  }
7173
7326
  }
7174
7327
  const saveBatchManifest = options.saveBatchManifest ?? mediaKind === "image";
7175
- validateImageCommandReferenceCount(command, parsed, mediaKind, parseImageGenerationOptions(parsed, mediaKind).model);
7328
+ const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
7329
+ validateImageModelOperation(command, mediaKind, imageOptions.model);
7330
+ validateImageCommandReferenceCount(command, parsed, mediaKind, imageOptions.model);
7176
7331
  validateVideoAudioOptions(parsed, mediaKind);
7177
- parseVideoGenerationOptions(parsed, mediaKind);
7332
+ validateVideoModelOperation(command, mediaKind, parseVideoGenerationOptions(parsed, mediaKind).model);
7178
7333
  validateVideoFrameReferenceOptions(command, parsed, mediaKind);
7179
- const ctx = await buildContext$4(parsed, deps);
7334
+ const ctx = await buildContext$6(parsed, deps);
7180
7335
  const client = await deps.createClient(ctx.env, ctx.spaceId);
7181
7336
  try {
7182
7337
  await client.connect();
@@ -7190,7 +7345,7 @@ async function executeForgeCommand(command, parsed, deps = defaultDeps$7, option
7190
7345
  client.disconnect();
7191
7346
  }
7192
7347
  }
7193
- async function executeAudioCommand(command, parsed, deps = defaultDeps$7, options = {}) {
7348
+ async function executeAudioCommand(command, parsed, deps = defaultDeps$9, options = {}) {
7194
7349
  const audioProfile = getCliGenerationProfile("audio");
7195
7350
  if (!audioProfile.commands.includes(command)) throw new Error(`Audio generation does not support ${command}`);
7196
7351
  const modeEntry = options.mode ? getMediaOperationEntry(options.mode) : void 0;
@@ -7203,7 +7358,7 @@ async function executeAudioCommand(command, parsed, deps = defaultDeps$7, option
7203
7358
  audioMode: options.mode
7204
7359
  });
7205
7360
  }
7206
- async function executeVideoCommand(command, parsed, deps = defaultDeps$7) {
7361
+ async function executeVideoCommand(command, parsed, deps = defaultDeps$9) {
7207
7362
  const videoProfile = getCliGenerationProfile("video");
7208
7363
  if (!videoProfile.commands.includes(command)) throw new Error(`Video generation does not support ${command}`);
7209
7364
  return executeForgeCommand(command, parsed, deps, {
@@ -7242,12 +7397,20 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
7242
7397
  const musicProvider = parseMusicProviderOption(parsed, mediaKind, assetType);
7243
7398
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
7244
7399
  const videoFrameRefs = parseVideoFrameReferenceOptions(parsed, "generate", mediaKind).refs;
7245
- const state = videoFrameRefs.length > 0 ? await requestSpaceState(client) : void 0;
7400
+ const state = videoFrameRefs.length > 0 ? await requestSpaceState$1(client) : void 0;
7246
7401
  const referenceVariantIds = videoFrameRefs.length > 0 ? await resolveReferenceVariantIds(videoFrameRefs, ctx, deps, state?.variants, CLI_GENERATION_MEDIA_KIND) : [];
7247
7402
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
7248
7403
  const audioModelOptions = parseAudioGenerationOptions(parsed, mediaKind);
7249
7404
  const videoAudioOptions = parseVideoAudioOptions(parsed, mediaKind);
7250
7405
  const videoOptions = parseVideoGenerationOptions(parsed, mediaKind);
7406
+ const effectiveVideoModel = videoOptions.model ?? getVideoGenerationModelForSelection();
7407
+ if (mediaKind === "video" && isKlingVideoGenerationModel(effectiveVideoModel) && videoFrameRefs.length > 1) throw new Error("--model kling supports at most one image reference");
7408
+ if (mediaKind === "video" && isFalVideoGenerationModel(effectiveVideoModel) && videoFrameRefs.length > 0) throw new Error(`--model ${getVideoModelSelectionForModel(effectiveVideoModel) ?? effectiveVideoModel} does not support image references`);
7409
+ const collectionPlacement = parseCollectionPlacementOptions(parsed);
7410
+ const followCommandOptions = {
7411
+ ...followOptions,
7412
+ ...collectionPlacement
7413
+ };
7251
7414
  await printPreflightEstimate(client, {
7252
7415
  operation: "generate",
7253
7416
  assetType,
@@ -7255,6 +7418,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
7255
7418
  prompt,
7256
7419
  count: 1,
7257
7420
  model: imageOptions.model ?? audioModelOptions.model ?? videoOptions.model,
7421
+ aspectRatio: imageOptions.aspectRatio ?? videoOptions.aspectRatio,
7258
7422
  imageSize: imageOptions.imageSize,
7259
7423
  ...musicProvider ? { musicProvider } : {},
7260
7424
  ...videoAudioOptions,
@@ -7280,7 +7444,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
7280
7444
  ...videoOptions,
7281
7445
  onStarted: (started) => {
7282
7446
  startedVariantId = started.jobId;
7283
- printFollowHint(started, ctx, outputPath, "generate", mediaKind, followOptions);
7447
+ printFollowHint(started, ctx, outputPath, "generate", mediaKind, followCommandOptions);
7284
7448
  }
7285
7449
  });
7286
7450
  } catch (error) {
@@ -7290,7 +7454,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
7290
7454
  ctx,
7291
7455
  command: "generate",
7292
7456
  mediaKind,
7293
- followOptions
7457
+ followOptions: followCommandOptions
7294
7458
  });
7295
7459
  }
7296
7460
  const manifestPath = await saveGenerationManifest({
@@ -7307,6 +7471,7 @@ async function executeGenerate(parsed, ctx, client, deps, mediaKind, followOptio
7307
7471
  refs: videoFrameRefs,
7308
7472
  referenceVariantIds
7309
7473
  });
7474
+ await placeGeneratedAssetInCollection(result, ctx, deps, collectionPlacement);
7310
7475
  await downloadResult(result, outputPath, ctx, deps);
7311
7476
  printResult(result, outputPath, ctx, manifestPath);
7312
7477
  return result;
@@ -7334,7 +7499,7 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7334
7499
  const prompt = getPrompt(parsed, "refine");
7335
7500
  const outputPath = getOutputPath$1(parsed);
7336
7501
  const sourceVariantId = requireOption$1(parsed, "variant");
7337
- const state = await requestSpaceState(client);
7502
+ const state = await requestSpaceState$1(client);
7338
7503
  const sourceVariant = state.variants.find((variant) => variant.id === sourceVariantId);
7339
7504
  if (!sourceVariant) throw new Error(`Variant not found in space sync state: ${sourceVariantId}`);
7340
7505
  const sourceAsset = state.assets.find((asset) => asset.id === sourceVariant.asset_id);
@@ -7342,6 +7507,8 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7342
7507
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
7343
7508
  const videoAudioOptions = parseVideoAudioOptions(parsed, mediaKind);
7344
7509
  const videoOptions = parseVideoGenerationOptions(parsed, mediaKind);
7510
+ const collectionPlacement = parseCollectionPlacementOptions(parsed);
7511
+ const followCommandOptions = { ...collectionPlacement };
7345
7512
  await printPreflightEstimate(client, {
7346
7513
  operation: "refine",
7347
7514
  assetId: sourceVariant.asset_id,
@@ -7350,6 +7517,7 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7350
7517
  prompt,
7351
7518
  count: 1,
7352
7519
  model: imageOptions.model ?? videoOptions.model,
7520
+ aspectRatio: imageOptions.aspectRatio ?? videoOptions.aspectRatio,
7353
7521
  imageSize: imageOptions.imageSize,
7354
7522
  ...videoAudioOptions,
7355
7523
  ...videoOptions
@@ -7369,7 +7537,7 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7369
7537
  ...videoOptions,
7370
7538
  onStarted: (started) => {
7371
7539
  startedVariantId = started.jobId;
7372
- printFollowHint(started, ctx, outputPath, "refine", mediaKind);
7540
+ printFollowHint(started, ctx, outputPath, "refine", mediaKind, followCommandOptions);
7373
7541
  }
7374
7542
  });
7375
7543
  } catch (error) {
@@ -7378,7 +7546,8 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7378
7546
  outputPath,
7379
7547
  ctx,
7380
7548
  command: "refine",
7381
- mediaKind
7549
+ mediaKind,
7550
+ followOptions: followCommandOptions
7382
7551
  });
7383
7552
  }
7384
7553
  const manifestPath = await saveGenerationManifest({
@@ -7395,6 +7564,7 @@ async function executeRefine(parsed, ctx, client, deps, mediaKind) {
7395
7564
  refs: [sourceVariantId],
7396
7565
  referenceVariantIds: [sourceVariantId]
7397
7566
  });
7567
+ await placeGeneratedAssetInCollection(result, ctx, deps, collectionPlacement);
7398
7568
  await downloadResult(result, outputPath, ctx, deps);
7399
7569
  printResult(result, outputPath, ctx, manifestPath);
7400
7570
  return result;
@@ -7410,10 +7580,18 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions
7410
7580
  if (referenceRefs.length === 0) throw new Error("--refs is required");
7411
7581
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
7412
7582
  if (mediaKind === "image") validateImageReferenceCount(imageOptions.model, referenceRefs.length);
7413
- const referenceVariantIds = await resolveReferenceVariantIds(referenceRefs, ctx, deps, (await requestSpaceState(client)).variants, videoFrameRefs.length > 0 ? CLI_GENERATION_MEDIA_KIND : mediaKind);
7583
+ const referenceVariantIds = await resolveReferenceVariantIds(referenceRefs, ctx, deps, (await requestSpaceState$1(client)).variants, videoFrameRefs.length > 0 ? CLI_GENERATION_MEDIA_KIND : mediaKind);
7414
7584
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
7415
7585
  const videoAudioOptions = parseVideoAudioOptions(parsed, mediaKind);
7416
7586
  const videoOptions = parseVideoGenerationOptions(parsed, mediaKind);
7587
+ const effectiveVideoModel = videoOptions.model ?? getVideoGenerationModelForSelection();
7588
+ if (mediaKind === "video" && isKlingVideoGenerationModel(effectiveVideoModel) && referenceRefs.length > 1) throw new Error("--model kling supports at most one image reference");
7589
+ if (mediaKind === "video" && isFalVideoGenerationModel(effectiveVideoModel) && referenceRefs.length > 0) throw new Error(`--model ${getVideoModelSelectionForModel(effectiveVideoModel) ?? effectiveVideoModel} does not support ${referenceRefs.length} image references`);
7590
+ const collectionPlacement = parseCollectionPlacementOptions(parsed);
7591
+ const followCommandOptions = {
7592
+ ...followOptions,
7593
+ ...collectionPlacement
7594
+ };
7417
7595
  await printPreflightEstimate(client, {
7418
7596
  operation: "derive",
7419
7597
  assetType,
@@ -7421,6 +7599,7 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions
7421
7599
  prompt,
7422
7600
  count: 1,
7423
7601
  model: imageOptions.model ?? videoOptions.model,
7602
+ aspectRatio: imageOptions.aspectRatio ?? videoOptions.aspectRatio,
7424
7603
  imageSize: imageOptions.imageSize,
7425
7604
  ...videoAudioOptions,
7426
7605
  ...videoOptions
@@ -7441,7 +7620,7 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions
7441
7620
  ...videoOptions,
7442
7621
  onStarted: (started) => {
7443
7622
  startedVariantId = started.jobId;
7444
- printFollowHint(started, ctx, outputPath, "derive", mediaKind, followOptions);
7623
+ printFollowHint(started, ctx, outputPath, "derive", mediaKind, followCommandOptions);
7445
7624
  }
7446
7625
  });
7447
7626
  } catch (error) {
@@ -7451,7 +7630,7 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions
7451
7630
  ctx,
7452
7631
  command: "derive",
7453
7632
  mediaKind,
7454
- followOptions
7633
+ followOptions: followCommandOptions
7455
7634
  });
7456
7635
  }
7457
7636
  const manifestPath = await saveGenerationManifest({
@@ -7468,6 +7647,7 @@ async function executeDerive(parsed, ctx, client, deps, mediaKind, followOptions
7468
7647
  refs: referenceRefs,
7469
7648
  referenceVariantIds
7470
7649
  });
7650
+ await placeGeneratedAssetInCollection(result, ctx, deps, collectionPlacement);
7471
7651
  await downloadResult(result, outputPath, ctx, deps);
7472
7652
  printResult(result, outputPath, ctx, manifestPath);
7473
7653
  return result;
@@ -7477,8 +7657,13 @@ async function executeFollow(parsed, ctx, client, deps, command, mediaKind, foll
7477
7657
  if (!variantId) throw new Error("--follow requires a variant ID");
7478
7658
  const outputPath = getOutputPath$1(parsed);
7479
7659
  const timeoutMs = parseFollowTimeoutMs(parsed, mediaKind);
7660
+ const collectionPlacement = parseCollectionPlacementOptions(parsed);
7661
+ const followCommandOptions = {
7662
+ ...followOptions,
7663
+ ...collectionPlacement
7664
+ };
7480
7665
  console.log(`Following variant ${variantId} in space ${ctx.spaceId}...`);
7481
- const state = await requestSpaceState(client);
7666
+ const state = await requestSpaceState$1(client);
7482
7667
  const initialVariant = state.variants.find((variant) => variant.id === variantId);
7483
7668
  if (!initialVariant) throw new Error(`Variant not found in space sync state: ${variantId}`);
7484
7669
  const initialStatus = initialVariant.status;
@@ -7498,7 +7683,7 @@ async function executeFollow(parsed, ctx, client, deps, command, mediaKind, foll
7498
7683
  }
7499
7684
  }
7500
7685
  });
7501
- const refreshedVariant = (await requestSpaceState(client)).variants.find((variant) => variant.id === variantId);
7686
+ const refreshedVariant = (await requestSpaceState$1(client)).variants.find((variant) => variant.id === variantId);
7502
7687
  result = refreshedVariant ? resultFromTerminalVariant(refreshedVariant, requestId) : void 0;
7503
7688
  if (result) client.cancelFollowVariant?.(variantId, requestId);
7504
7689
  else try {
@@ -7510,7 +7695,7 @@ async function executeFollow(parsed, ctx, client, deps, command, mediaKind, foll
7510
7695
  ctx,
7511
7696
  command,
7512
7697
  mediaKind,
7513
- followOptions
7698
+ followOptions: followCommandOptions
7514
7699
  });
7515
7700
  }
7516
7701
  }
@@ -7533,6 +7718,7 @@ async function executeFollow(parsed, ctx, client, deps, command, mediaKind, foll
7533
7718
  referenceVariantIds
7534
7719
  });
7535
7720
  await downloadResult(result, outputPath, ctx, deps);
7721
+ await placeGeneratedAssetInCollection(result, ctx, deps, collectionPlacement);
7536
7722
  printResult(result, outputPath, ctx, manifestPath);
7537
7723
  return result;
7538
7724
  }
@@ -7625,10 +7811,11 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
7625
7811
  const imageOptions = parseImageGenerationOptions(parsed, mediaKind);
7626
7812
  const audioModelOptions = parseAudioGenerationOptions(parsed, mediaKind);
7627
7813
  if (mediaKind === "image") validateImageReferenceCount(imageOptions.model, refs.length);
7628
- const state = await requestSpaceState(client);
7814
+ const state = await requestSpaceState$1(client);
7629
7815
  const referenceVariantIds = refs.length > 0 ? await resolveReferenceVariantIds(refs, ctx, deps, state.variants, mediaKind) : void 0;
7630
7816
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
7631
7817
  const runId = deps.createRunId();
7818
+ const collectionPlacement = parseCollectionPlacementOptions(parsed);
7632
7819
  await printPreflightEstimate(client, {
7633
7820
  operation: "batch",
7634
7821
  assetType,
@@ -7636,6 +7823,7 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
7636
7823
  prompt,
7637
7824
  count,
7638
7825
  model: imageOptions.model ?? audioModelOptions.model,
7826
+ aspectRatio: imageOptions.aspectRatio,
7639
7827
  imageSize: imageOptions.imageSize,
7640
7828
  ...musicProvider ? { musicProvider } : {}
7641
7829
  });
@@ -7667,6 +7855,7 @@ async function executeBatch(parsed, ctx, client, deps, mediaKind, saveBatchManif
7667
7855
  }
7668
7856
  const sortedVariants = [...result.variants].sort((a, b) => a.created_at - b.created_at);
7669
7857
  const media = [];
7858
+ await placeGeneratedBatchAssetsInCollection(result, ctx, deps, collectionPlacement);
7670
7859
  for (let index = 0; index < sortedVariants.length; index += 1) {
7671
7860
  const variant = sortedVariants[index];
7672
7861
  const outputPath = path.join(outputDir, `${slugify(name)}-${String(index + 1).padStart(2, "0")}.${getOutputExtension(variant, mediaKind)}`);
@@ -7752,7 +7941,69 @@ async function saveGenerationManifest(input) {
7752
7941
  failed: []
7753
7942
  }, ctx.projectRoot);
7754
7943
  }
7755
- async function buildContext$4(parsed, deps) {
7944
+ function parseCollectionPlacementOptions(parsed) {
7945
+ const collectionId = normalizeCliOption(readOptionalOption(parsed, "collection", "collection") ?? readOptionalOption(parsed, "collection-id", "collectionId"));
7946
+ if (parsed.options.collection !== void 0 || parsed.options.collectionId !== void 0 || parsed.options["collection-id"] !== void 0) {
7947
+ if (!collectionId) throw new Error("--collection requires a collection ID");
7948
+ }
7949
+ return collectionId ? { collectionId } : {};
7950
+ }
7951
+ async function placeGeneratedAssetInCollection(result, ctx, deps, options) {
7952
+ if (!options.collectionId || !result.success || !result.variant) return;
7953
+ await createCollectionAssetItem({
7954
+ ctx,
7955
+ deps,
7956
+ collectionId: options.collectionId,
7957
+ assetId: result.variant.asset_id,
7958
+ pinnedVariantId: result.variant.id
7959
+ });
7960
+ }
7961
+ async function placeGeneratedBatchAssetsInCollection(result, ctx, deps, options) {
7962
+ if (!options.collectionId || !result.success) return;
7963
+ const firstVariantByAsset = /* @__PURE__ */ new Map();
7964
+ for (const variant of [...result.variants].sort((a, b) => a.created_at - b.created_at)) if (!firstVariantByAsset.has(variant.asset_id)) firstVariantByAsset.set(variant.asset_id, variant);
7965
+ for (const variant of firstVariantByAsset.values()) await createCollectionAssetItem({
7966
+ ctx,
7967
+ deps,
7968
+ collectionId: options.collectionId,
7969
+ assetId: variant.asset_id,
7970
+ pinnedVariantId: variant.id
7971
+ });
7972
+ }
7973
+ async function createCollectionAssetItem(input) {
7974
+ const fetchImpl = input.deps.fetch ?? fetch;
7975
+ const itemsUrl = `${input.ctx.baseUrl}/api/spaces/${encodeURIComponent(input.ctx.spaceId)}/collections/${encodeURIComponent(input.collectionId)}/items`;
7976
+ const listResponse = await fetchImpl(itemsUrl, { headers: {
7977
+ "Authorization": `Bearer ${input.ctx.accessToken}`,
7978
+ "Accept": "application/json"
7979
+ } });
7980
+ if (!listResponse.ok) {
7981
+ const errorText = await listResponse.text();
7982
+ throw new Error(`Failed to inspect collection ${input.collectionId} (${listResponse.status}): ${errorText || listResponse.statusText}`);
7983
+ }
7984
+ const existing = await listResponse.json();
7985
+ if (existing.items?.some((item) => item.asset_id === input.assetId)) return;
7986
+ const sortIndex = Math.max(-1, ...(existing.items ?? []).map((item) => typeof item.sort_index === "number" && Number.isFinite(item.sort_index) ? item.sort_index : -1)) + 1;
7987
+ const response = await fetchImpl(itemsUrl, {
7988
+ method: "POST",
7989
+ headers: {
7990
+ "Authorization": `Bearer ${input.ctx.accessToken}`,
7991
+ "Content-Type": "application/json",
7992
+ "Accept": "application/json"
7993
+ },
7994
+ body: JSON.stringify({
7995
+ subjectType: "asset",
7996
+ assetId: input.assetId,
7997
+ pinnedVariantId: input.pinnedVariantId,
7998
+ sortIndex
7999
+ })
8000
+ });
8001
+ if (!response.ok) {
8002
+ const errorText = await response.text();
8003
+ throw new Error(`Failed to place asset ${input.assetId} in collection ${input.collectionId} (${response.status}): ${errorText || response.statusText}`);
8004
+ }
8005
+ }
8006
+ async function buildContext$6(parsed, deps) {
7756
8007
  const projectConfig = await deps.loadProjectConfig();
7757
8008
  const env = resolveCommandEnvironment(parsed, projectConfig);
7758
8009
  const spaceId = resolveCommandSpace(parsed, projectConfig);
@@ -7869,7 +8120,7 @@ function validateReferenceVariant(ref, variants, mediaKind) {
7869
8120
  if (!variant.image_key && !variant.media_key) throw new Error(`Reference variant has no image or video media: ${ref}`);
7870
8121
  }
7871
8122
  }
7872
- function requestSpaceState(client) {
8123
+ function requestSpaceState$1(client) {
7873
8124
  return new Promise((resolve, reject) => {
7874
8125
  const timer = setTimeout(() => {
7875
8126
  reject(/* @__PURE__ */ new Error("Timed out waiting for space sync state"));
@@ -7922,12 +8173,13 @@ function printFollowHint(started, ctx, outputPath, command, mediaKind, options =
7922
8173
  }
7923
8174
  function throwGenerationWaitErrorIfNeeded(input, params) {
7924
8175
  if (!isGenerationWaitTimeout(input)) throw input;
7925
- if (!params.variantId) throw new Error(["Make Effects did not confirm the generation start before the CLI stopped waiting.", "Check the Space in the web app before retrying, so you do not create a duplicate."].join("\n"));
8176
+ if (!params.variantId) throw new Error(["The CLI timed out before Make Effects returned a variant ID.", "The generation may still have started. Check the Space in the web app before retrying, so you do not create a duplicate."].join("\n"));
7926
8177
  const followCommand = formatFollowCommand(params.variantId, params.outputPath, params.ctx, params.command, params.mediaKind, params.followOptions);
7927
8178
  throw new Error([
7928
- "Still generating in Make Effects.",
8179
+ "The CLI stopped waiting, but the Make Effects generation job is still running.",
8180
+ "Do not retry this generation unless you want a duplicate asset.",
7929
8181
  "",
7930
- "Resume the download when the variant is ready:",
8182
+ "Resume the same job and download the variant when it is ready:",
7931
8183
  ` ${followCommand}`,
7932
8184
  "",
7933
8185
  `Variant: ${params.variantId}`
@@ -7935,7 +8187,7 @@ function throwGenerationWaitErrorIfNeeded(input, params) {
7935
8187
  }
7936
8188
  function throwBatchWaitErrorIfNeeded(input, started, ctx) {
7937
8189
  if (!isBatchWaitTimeout(input)) throw input;
7938
- if (!started) throw new Error(["Make Effects did not confirm the batch start before the CLI stopped waiting.", "Check the Space in the web app before retrying, so you do not create duplicate assets."].join("\n"));
8190
+ if (!started) throw new Error(["The CLI timed out before Make Effects returned batch variant IDs.", "The batch may still have started. Check the Space in the web app before retrying, so you do not create duplicate assets."].join("\n"));
7939
8191
  const downloadExample = [
7940
8192
  "makefx assets download <variant_id> -o <file>",
7941
8193
  ...ctx.env === "local" ? ["--local"] : ["--env", ctx.env],
@@ -7943,7 +8195,8 @@ function throwBatchWaitErrorIfNeeded(input, started, ctx) {
7943
8195
  ctx.spaceId
7944
8196
  ].join(" ");
7945
8197
  throw new Error([
7946
- "Batch generation is still running in Make Effects.",
8198
+ "The CLI stopped waiting, but the Make Effects batch jobs are still running.",
8199
+ "Do not retry this batch unless you want duplicate assets.",
7947
8200
  "",
7948
8201
  `Started variants: ${started.jobIds.join(", ")}`,
7949
8202
  "",
@@ -7970,6 +8223,7 @@ function formatFollowCommand(variantId, outputPath, ctx, command, mediaKind, opt
7970
8223
  command
7971
8224
  ] : ["makefx", command];
7972
8225
  const envArgs = ctx.env === "local" ? ["--local"] : ["--env", ctx.env];
8226
+ const collectionArgs = options.collectionId ? ["--collection", options.collectionId] : [];
7973
8227
  return [
7974
8228
  ...baseCommand,
7975
8229
  "--follow",
@@ -7978,7 +8232,8 @@ function formatFollowCommand(variantId, outputPath, ctx, command, mediaKind, opt
7978
8232
  outputPath,
7979
8233
  ...envArgs,
7980
8234
  "--space",
7981
- ctx.spaceId
8235
+ ctx.spaceId,
8236
+ ...collectionArgs
7982
8237
  ].map(shellQuote).join(" ");
7983
8238
  }
7984
8239
  function shellQuote(value) {
@@ -8033,7 +8288,7 @@ function validateVideoAudioOptions(parsed, mediaKind) {
8033
8288
  const videoTier = tierValue === void 0 ? DEFAULT_VIDEO_GENERATION_TIER : normalizeVideoGenerationTier(tierValue);
8034
8289
  if (!videoTier) throw new Error("--tier must be generate, fast, or lite");
8035
8290
  const model = parseVideoModelOption(modelValue, videoTier);
8036
- if (parsed.options.audio !== void 0 && isGeminiOmniVideoGenerationModel(model)) throw new Error("--audio is only supported with --model veo-3.1");
8291
+ if (parsed.options.audio !== void 0 && (isGeminiOmniVideoGenerationModel(model) || isFalVideoGenerationModel(model))) throw new Error("--audio is only supported with --model veo-3.1 or kling");
8037
8292
  if (parsed.options["no-audio"] !== void 0 || parsed.options.noAudio !== void 0) {
8038
8293
  if (!doesVideoGenerationModelSupportAudioToggle(model)) throw new Error(`${model} does not support --no-audio. Use the default audio-enabled output or omit the flag.`);
8039
8294
  }
@@ -8061,7 +8316,9 @@ function parseVideoGenerationOptions(parsed, mediaKind) {
8061
8316
  if (tierValue !== void 0 && !videoTier) throw new Error("--tier must be generate, fast, or lite");
8062
8317
  const model = parseVideoModelOption(modelValue, videoTier);
8063
8318
  if (isGeminiOmniVideoGenerationModel(model) && (resolutionValue !== void 0 || durationValue !== void 0 || tierValue !== void 0)) throw new Error("--resolution, --duration, and --tier are only supported with --model veo-3.1");
8319
+ if ((isKlingVideoGenerationModel(model) || isFalVideoGenerationModel(model)) && tierValue !== void 0) throw new Error("--tier is only supported with --model veo-3.1");
8064
8320
  if (videoResolution && videoTier && !isVideoGenerationResolutionSupportedForTier(videoResolution, videoTier)) throw new Error("--resolution 4k is not supported with --tier lite");
8321
+ if (videoResolution && (isKlingVideoGenerationModel(model) || isFalVideoGenerationModel(model)) && !isVideoGenerationResolutionSupportedForModel(model, videoResolution)) throw new Error(`--model ${getVideoModelSelectionForModel(model) ?? model} supports only --resolution 720p or 1080p`);
8065
8322
  const aspectRatio = aspectValue === void 0 ? void 0 : normalizeVideoGenerationAspectRatio(aspectValue);
8066
8323
  if (aspectValue !== void 0 && !aspectRatio) throw new Error("--aspect must be 16:9 or 9:16");
8067
8324
  return {
@@ -8088,7 +8345,8 @@ function validateVideoFrameReferenceOptions(command, parsed, mediaKind) {
8088
8345
  if (parsed.options.refs) throw new Error("--first-frame and --last-frame cannot be combined with --refs");
8089
8346
  const tierValue = readOptionalOption(parsed, "tier", "tier") ?? readOptionalOption(parsed, "video-tier", "videoTier");
8090
8347
  const videoTier = tierValue === void 0 ? void 0 : normalizeVideoGenerationTier(tierValue);
8091
- if (isGeminiOmniVideoGenerationModel(parseVideoModelOption(readOptionalOption(parsed, "model", "model"), videoTier))) throw new Error("--first-frame and --last-frame are only supported with --model veo-3.1");
8348
+ const model = parseVideoModelOption(readOptionalOption(parsed, "model", "model"), videoTier);
8349
+ if (isFalVideoGenerationModel(model) || isGeminiOmniVideoGenerationModel(model) || isKlingVideoGenerationModel(model) && Boolean(lastFrame)) throw new Error("--first-frame and --last-frame are only supported with --model veo-3.1");
8092
8350
  }
8093
8351
  function readVideoFrameOption(parsed, kebabName, camelName) {
8094
8352
  const value = parsed.options[kebabName] ?? parsed.options[camelName];
@@ -8142,17 +8400,18 @@ function parseVideoModelOption(value, tier) {
8142
8400
  if (selection) return getVideoGenerationModelForSelection(selection, effectiveTier);
8143
8401
  if (isVideoGenerationModel(normalized)) {
8144
8402
  const modelTier = normalizeVideoGenerationTier(tier) ?? void 0;
8145
- if (modelTier && getVideoGenerationModelForTier(modelTier) !== normalized && !isGeminiOmniVideoGenerationModel(normalized)) throw new Error("--model and --tier select different Veo models");
8403
+ if (modelTier && getVideoGenerationModelForTier(modelTier) !== normalized && !isGeminiOmniVideoGenerationModel(normalized) && !isKlingVideoGenerationModel(normalized) && !isFalVideoGenerationModel(normalized)) throw new Error("--model and --tier select different Veo models");
8146
8404
  return normalized;
8147
8405
  }
8148
- throw new Error("--model must be veo-3.1, omni-flash, or an exact video model ID");
8406
+ throw new Error("--model must be veo-3.1, omni-flash, kling, or an exact video model ID");
8149
8407
  }
8150
8408
  function parseImageModelOption(value) {
8151
8409
  if (!value) return void 0;
8152
8410
  if (isImageModelSelection(value)) return value;
8153
8411
  if (value === IMAGE_MODEL_IDS.pro) return "pro";
8154
8412
  if (value === IMAGE_MODEL_IDS.flash) return "flash";
8155
- throw new Error("--model must be pro or flash");
8413
+ if (value === IMAGE_MODEL_IDS.flux) return "flux";
8414
+ throw new Error("--model must be pro, flash, or flux");
8156
8415
  }
8157
8416
  function parseImageSizeOption(value) {
8158
8417
  if (!value) return void 0;
@@ -8175,9 +8434,23 @@ function validateImageReferenceCount(model, referenceCount) {
8175
8434
  }
8176
8435
  function validateImageCommandReferenceCount(command, parsed, mediaKind, model) {
8177
8436
  if (mediaKind !== "image") return;
8178
- if (command !== "derive" && command !== "batch") return;
8179
- if (!parsed.options.refs) return;
8180
- validateImageReferenceCount(model, parseRefs(parsed.options.refs).length);
8437
+ if (command === "refine") {
8438
+ validateImageReferenceCount(model, 1);
8439
+ return;
8440
+ }
8441
+ if ((command === "derive" || command === "batch") && parsed.options.refs) validateImageReferenceCount(model, parseRefs(parsed.options.refs).length);
8442
+ }
8443
+ function validateImageModelOperation(command, mediaKind, model) {
8444
+ if (mediaKind !== "image") return;
8445
+ const operation = command === "batch" ? "generate" : command;
8446
+ if (isImageOperationSupportedByModel(model, operation)) return;
8447
+ throw new Error(`--model ${model ?? "pro"} does not support ${operation}`);
8448
+ }
8449
+ function validateVideoModelOperation(command, mediaKind, model) {
8450
+ if (mediaKind !== "video" || command === "batch") return;
8451
+ const effectiveModel = model ?? getVideoGenerationModelForSelection();
8452
+ if (isVideoOperationSupportedByModel(effectiveModel, command)) return;
8453
+ throw new Error(`--model ${getVideoModelSelectionForModel(effectiveModel) ?? effectiveModel} does not support video ${command}`);
8181
8454
  }
8182
8455
  function readOptionalOption(parsed, kebabName, camelName) {
8183
8456
  const value = parsed.options[kebabName] ?? parsed.options[camelName];
@@ -8221,34 +8494,38 @@ function cliImageSizeValues() {
8221
8494
  function cliImageAspectValues() {
8222
8495
  return cliOptionValues(getImageModelCapabilities().supportedAspectRatios);
8223
8496
  }
8224
- function printUsage$7(command) {
8497
+ function cliImageModelValues(command) {
8498
+ const operation = command === "batch" ? "generate" : command;
8499
+ return cliOptionValues(IMAGE_MODEL_SELECTIONS.filter((model) => isImageOperationSupportedByModel(model, operation)));
8500
+ }
8501
+ function printUsage$9(command) {
8225
8502
  if (command === "generate") {
8226
8503
  console.log(`
8227
8504
  Usage:
8228
- makefx generate "prompt" --name <name> --type <type> -o <file> [--model pro|flash] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--space <id>]
8229
- makefx generate --follow <variant_id> -o <file> [--space <id>]
8505
+ makefx generate "prompt" --name <name> --type <type> -o <file> [--model ${cliImageModelValues(command)}] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--collection <id>] [--space <id>]
8506
+ makefx generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
8230
8507
  `);
8231
8508
  return;
8232
8509
  }
8233
8510
  if (command === "refine") {
8234
8511
  console.log(`
8235
8512
  Usage:
8236
- makefx refine --variant <variant_id> "prompt" -o <file> [--model pro|flash] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--space <id>]
8237
- makefx refine --follow <variant_id> -o <file> [--space <id>]
8513
+ makefx refine --variant <variant_id> "prompt" -o <file> [--model ${cliImageModelValues(command)}] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--collection <id>] [--space <id>]
8514
+ makefx refine --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
8238
8515
  `);
8239
8516
  return;
8240
8517
  }
8241
8518
  if (command === "batch") {
8242
8519
  console.log(`
8243
8520
  Usage:
8244
- makefx batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--model pro|flash] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}]
8521
+ makefx batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--model ${cliImageModelValues(command)}] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--collection <id>]
8245
8522
  `);
8246
8523
  return;
8247
8524
  }
8248
8525
  console.log(`
8249
8526
  Usage:
8250
- makefx derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model pro|flash] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--space <id>]
8251
- makefx derive --follow <variant_id> -o <file> [--space <id>]
8527
+ makefx derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${cliImageModelValues(command)}] [--size ${cliImageSizeValues()}] [--aspect ${cliImageAspectValues()}] [--collection <id>] [--space <id>]
8528
+ makefx derive --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
8252
8529
  `);
8253
8530
  }
8254
8531
  function printBatchResult(result, outputDir, manifestPath, ctx, mediaKind) {
@@ -8262,7 +8539,7 @@ function printBatchResult(result, outputDir, manifestPath, ctx, mediaKind) {
8262
8539
  }
8263
8540
  //#endregion
8264
8541
  //#region src/cli/commands/audio.ts
8265
- var defaultDeps$6 = {
8542
+ var defaultDeps$8 = {
8266
8543
  loadConfig: loadStoredConfig,
8267
8544
  loadProjectConfig,
8268
8545
  resolveBaseUrl,
@@ -8275,11 +8552,11 @@ async function handleAudio(parsed) {
8275
8552
  await executeAudio(parsed);
8276
8553
  } catch (error) {
8277
8554
  console.error("Error:", error instanceof Error ? error.message : error);
8278
- printUsage$6();
8555
+ printUsage$8();
8279
8556
  process$1.exitCode = 1;
8280
8557
  }
8281
8558
  }
8282
- async function executeAudio(parsed, deps = defaultDeps$6) {
8559
+ async function executeAudio(parsed, deps = defaultDeps$8) {
8283
8560
  const invocation = parseAudioInvocation(parsed.positionals);
8284
8561
  if (invocation.command === "voices") return executeAudioVoices({
8285
8562
  ...parsed,
@@ -8352,7 +8629,7 @@ function printVoices(response, print) {
8352
8629
  function formatLabels(labels) {
8353
8630
  return Object.entries(labels).map(([key, value]) => `${key}:${value}`).join(", ");
8354
8631
  }
8355
- function printUsage$6() {
8632
+ function printUsage$8() {
8356
8633
  const modes = AUDIO_FORGE_MEDIA_MODES.join("|");
8357
8634
  console.log(`
8358
8635
  Usage:
@@ -8381,7 +8658,7 @@ async function handleVideo(parsed) {
8381
8658
  });
8382
8659
  } catch (error) {
8383
8660
  console.error("Error:", error instanceof Error ? error.message : error);
8384
- printUsage$5();
8661
+ printUsage$7();
8385
8662
  process$1.exitCode = 1;
8386
8663
  }
8387
8664
  }
@@ -8390,28 +8667,28 @@ function parseVideoCommand(value) {
8390
8667
  if (value && commands.includes(value)) return value;
8391
8668
  throw new Error("Video command is required: generate, refine, or derive");
8392
8669
  }
8393
- function printUsage$5() {
8670
+ function printUsage$7() {
8394
8671
  const aspectValues = VIDEO_GENERATION_ASPECT_RATIOS.join("|");
8395
8672
  const resolutionValues = Array.from(new Set(VIDEO_GENERATION_TIERS.flatMap((tier) => getVideoGenerationResolutionsForTier(tier)))).join("|");
8396
8673
  const durationValues = VIDEO_GENERATION_DURATION_SECONDS.join("|");
8397
8674
  const tierValues = VIDEO_GENERATION_TIERS.join("|");
8398
- const modelValues = VIDEO_MODEL_SELECTIONS.join("|");
8675
+ const modelValues = (operation) => VIDEO_MODEL_SELECTIONS.filter((model) => VIDEO_MODEL_SUPPORTED_OPERATIONS[model].includes(operation)).join("|");
8399
8676
  const resolutionByTier = VIDEO_GENERATION_TIERS.map((tier) => `${tier}: ${getVideoGenerationResolutionsForTier(tier).join(", ")}`).join("; ");
8400
8677
  console.log(`
8401
8678
  Usage:
8402
- makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${modelValues}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8679
+ makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${modelValues("generate")}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8403
8680
  makefx video generate --follow <variant_id> -o <file> [--space <id>]
8404
- makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${modelValues}] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8681
+ makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${modelValues("refine")}] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8405
8682
  makefx video refine --follow <variant_id> -o <file> [--space <id>]
8406
- makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${modelValues}] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8683
+ makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${modelValues("derive")}] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8407
8684
  makefx video derive --first-frame <variant_or_file> [--last-frame <variant_or_file>] --name <name> --type <type> "prompt" -o <file> [--model veo-3.1] [--aspect ${aspectValues}] [--resolution ${resolutionValues}] [--duration ${durationValues}] [--tier ${tierValues}] [--audio] [--space <id>]
8408
8685
  makefx video derive --follow <variant_id> -o <file> [--space <id>]
8409
8686
 
8410
8687
  Video:
8411
- --model <value> Video model: ${modelValues}
8688
+ --model <value> Generate: ${modelValues("generate")}; refine/derive: ${modelValues("refine")}
8412
8689
  --aspect <ratio> Video aspect ratio: ${aspectValues}
8413
- --resolution <value> Veo-only output resolution (${resolutionByTier})
8414
- --duration <seconds> Veo-only output duration: ${durationValues}
8690
+ --resolution <value> Veo: ${resolutionValues}; Kling/fal Seedance: 720p|1080p (${resolutionByTier})
8691
+ --duration <seconds> Veo/Kling/fal Seedance output duration: ${durationValues}
8415
8692
  --tier <tier> Veo-only model tier: ${tierValues}
8416
8693
  --first-frame <ref> Veo start frame image variant or local image path
8417
8694
  --last-frame <ref> Veo final frame image variant or local image path
@@ -8429,17 +8706,17 @@ Audio:
8429
8706
  }
8430
8707
  //#endregion
8431
8708
  //#region src/cli/commands/init.ts
8432
- var defaultDeps$5 = { saveProjectConfig };
8709
+ var defaultDeps$7 = { saveProjectConfig };
8433
8710
  async function handleInit(parsed) {
8434
8711
  try {
8435
8712
  await executeInit(parsed);
8436
8713
  } catch (error) {
8437
8714
  console.error("Error:", error instanceof Error ? error.message : error);
8438
- printUsage$4();
8715
+ printUsage$6();
8439
8716
  process$1.exitCode = 1;
8440
8717
  }
8441
8718
  }
8442
- async function executeInit(parsed, deps = defaultDeps$5) {
8719
+ async function executeInit(parsed, deps = defaultDeps$7) {
8443
8720
  const environment = resolveCommandEnvironment(parsed);
8444
8721
  const spaceId = resolveCommandSpace(parsed);
8445
8722
  if (!spaceId) throw new Error("--space is required");
@@ -8465,7 +8742,7 @@ async function executeInit(parsed, deps = defaultDeps$5) {
8465
8742
  spaceId
8466
8743
  };
8467
8744
  }
8468
- function printUsage$4() {
8745
+ function printUsage$6() {
8469
8746
  console.log(`
8470
8747
  Usage:
8471
8748
  makefx init --space <id> [--env production|stage|local]
@@ -8474,7 +8751,7 @@ Usage:
8474
8751
  }
8475
8752
  //#endregion
8476
8753
  //#region src/cli/commands/runs.ts
8477
- var defaultDeps$4 = {
8754
+ var defaultDeps$6 = {
8478
8755
  loadProjectConfig,
8479
8756
  listRunManifests,
8480
8757
  readRunManifest,
@@ -8487,11 +8764,11 @@ async function handleRuns(parsed) {
8487
8764
  await executeRuns(parsed);
8488
8765
  } catch (error) {
8489
8766
  console.error("Error:", error instanceof Error ? error.message : error);
8490
- printUsage$3();
8767
+ printUsage$5();
8491
8768
  process$1.exitCode = 1;
8492
8769
  }
8493
8770
  }
8494
- async function executeRuns(parsed, deps = defaultDeps$4) {
8771
+ async function executeRuns(parsed, deps = defaultDeps$6) {
8495
8772
  const projectConfig = await deps.loadProjectConfig();
8496
8773
  if (!projectConfig?.projectRoot) throw new Error("Make Effects project config not found. Run: makefx init --space <id>");
8497
8774
  requireDebugAcknowledgement(parsed);
@@ -8609,7 +8886,7 @@ function formatCreated(value) {
8609
8886
  if (Number.isNaN(date.getTime())) return value;
8610
8887
  return value.replace("T", " ").replace(/\.\d{3}Z$/, "Z").slice(0, 20);
8611
8888
  }
8612
- function printUsage$3() {
8889
+ function printUsage$5() {
8613
8890
  console.log(`
8614
8891
  Usage:
8615
8892
  makefx runs --debug
@@ -8624,7 +8901,7 @@ Local run manifests are debug-only artifacts, not a source of truth.
8624
8901
  }
8625
8902
  //#endregion
8626
8903
  //#region src/cli/commands/assets.ts
8627
- var defaultDeps$3 = {
8904
+ var defaultDeps$5 = {
8628
8905
  loadConfig: loadStoredConfig,
8629
8906
  loadProjectConfig,
8630
8907
  resolveBaseUrl,
@@ -8648,15 +8925,15 @@ async function handleAssets(parsed) {
8648
8925
  await executeAssets(parsed);
8649
8926
  } catch (error) {
8650
8927
  console.error("Error:", error instanceof Error ? error.message : error);
8651
- printUsage$2();
8928
+ printUsage$4();
8652
8929
  process$1.exitCode = 1;
8653
8930
  }
8654
8931
  }
8655
- async function executeAssets(parsed, deps = defaultDeps$3) {
8932
+ async function executeAssets(parsed, deps = defaultDeps$5) {
8656
8933
  const subcommand = parsed.positionals[0] || "list";
8657
8934
  if (subcommand === "search") return searchAssets(parsed, deps);
8658
8935
  if (subcommand === "clone") return cloneAsset(parsed, deps);
8659
- const ctx = await buildContext$3(parsed, deps);
8936
+ const ctx = await buildContext$5(parsed, deps);
8660
8937
  if (subcommand === "list") {
8661
8938
  const assets = await listAssets(ctx, deps);
8662
8939
  if (parsed.options.json === "true") deps.print(JSON.stringify(assets.map(toAssetJson), null, 2));
@@ -8682,13 +8959,21 @@ async function executeAssets(parsed, deps = defaultDeps$3) {
8682
8959
  if (!ref) throw new Error("Variant ID or legacy image key is required: makefx assets download <variant-id-or-legacy-image-key> -o <file>");
8683
8960
  const outputPath = getOutputPath(parsed);
8684
8961
  const resolved = resolveDownloadRef(ref, ctx);
8685
- const metadata = getDownloadedVariantMetadata(resolved, await deps.downloadFile({
8686
- baseUrl: ctx.baseUrl,
8687
- accessToken: ctx.accessToken,
8688
- requestPath: resolved.requestPath,
8689
- outputPath,
8690
- force: ctx.force
8691
- }));
8962
+ let download;
8963
+ try {
8964
+ download = await deps.downloadFile({
8965
+ baseUrl: ctx.baseUrl,
8966
+ accessToken: ctx.accessToken,
8967
+ requestPath: resolved.requestPath,
8968
+ outputPath,
8969
+ force: ctx.force
8970
+ });
8971
+ } catch (error) {
8972
+ const failedVariantMessage = await getFailedDownloadVariantMessage(error, resolved, ctx, deps);
8973
+ if (failedVariantMessage) throw new Error(failedVariantMessage);
8974
+ throw error;
8975
+ }
8976
+ const metadata = getDownloadedVariantMetadata(resolved, download);
8692
8977
  await recordDownloadedMirror({
8693
8978
  ctx,
8694
8979
  deps,
@@ -8726,7 +9011,7 @@ async function executeAssets(parsed, deps = defaultDeps$3) {
8726
9011
  }
8727
9012
  if (subcommand === "set-active") {
8728
9013
  const assetId = parsed.positionals[1];
8729
- const variantId = parsed.positionals[2] || normalizeOption(parsed.options.variant);
9014
+ const variantId = parsed.positionals[2] || normalizeOption$1(parsed.options.variant);
8730
9015
  if (!assetId || !variantId) throw new Error("Usage: makefx assets set-active <asset-id> <variant-id>");
8731
9016
  const asset = await setActiveVariant(ctx, deps, assetId, variantId);
8732
9017
  deps.print(`Set active variant of asset ${assetId} to ${asset.active_variant_id}`);
@@ -8737,10 +9022,10 @@ async function executeAssets(parsed, deps = defaultDeps$3) {
8737
9022
  }
8738
9023
  throw new Error(`Unknown assets command: ${subcommand}`);
8739
9024
  }
8740
- function normalizeOption(value) {
9025
+ function normalizeOption$1(value) {
8741
9026
  return !value || value === "true" ? void 0 : value;
8742
9027
  }
8743
- async function buildContext$3(parsed, deps) {
9028
+ async function buildContext$5(parsed, deps) {
8744
9029
  const projectConfig = await deps.loadProjectConfig();
8745
9030
  const env = resolveCommandEnvironment(parsed, projectConfig);
8746
9031
  const spaceId = resolveCommandSpace(parsed, projectConfig);
@@ -8772,7 +9057,7 @@ async function buildAuthContext(parsed, deps) {
8772
9057
  }
8773
9058
  function requireOption(parsed, names, usage) {
8774
9059
  for (const name of names) {
8775
- const value = normalizeOption(parsed.options[name]);
9060
+ const value = normalizeOption$1(parsed.options[name]);
8776
9061
  if (value) return value;
8777
9062
  }
8778
9063
  throw new Error(usage);
@@ -8782,9 +9067,9 @@ async function searchAssets(parsed, deps) {
8782
9067
  const query = parsed.positionals.slice(1).join(" ").trim();
8783
9068
  const params = new URLSearchParams();
8784
9069
  if (query) params.set("q", query);
8785
- const mediaKind = normalizeOption(parsed.options.media) ?? normalizeOption(parsed.options["media-kind"]);
8786
- const assetType = normalizeOption(parsed.options.type);
8787
- const limit = normalizeOption(parsed.options.limit);
9070
+ const mediaKind = normalizeOption$1(parsed.options.media) ?? normalizeOption$1(parsed.options["media-kind"]);
9071
+ const assetType = normalizeOption$1(parsed.options.type);
9072
+ const limit = normalizeOption$1(parsed.options.limit);
8788
9073
  if (mediaKind) params.set("media_kind", mediaKind);
8789
9074
  if (assetType) params.set("type", assetType);
8790
9075
  if (limit) params.set("limit", limit);
@@ -8800,7 +9085,7 @@ async function searchAssets(parsed, deps) {
8800
9085
  }
8801
9086
  async function cloneAsset(parsed, deps) {
8802
9087
  const ctx = await buildAuthContext(parsed, deps);
8803
- const sourceVariantId = parsed.positionals[1] ?? normalizeOption(parsed.options.variant);
9088
+ const sourceVariantId = parsed.positionals[1] ?? normalizeOption$1(parsed.options.variant);
8804
9089
  if (!sourceVariantId) throw new Error("Usage: makefx assets clone <variant-id> --from-space <source-space-id> --to-space <target-space-id>");
8805
9090
  const sourceSpaceId = requireOption(parsed, ["from-space", "source-space"], "Usage: makefx assets clone <variant-id> --from-space <source-space-id> --to-space <target-space-id>");
8806
9091
  const targetSpaceId = requireOption(parsed, ["to-space", "target-space"], "Usage: makefx assets clone <variant-id> --from-space <source-space-id> --to-space <target-space-id>");
@@ -8808,8 +9093,8 @@ async function cloneAsset(parsed, deps) {
8808
9093
  sourceSpaceId,
8809
9094
  sourceVariantId
8810
9095
  };
8811
- const name = normalizeOption(parsed.options.name);
8812
- const type = normalizeOption(parsed.options.type);
9096
+ const name = normalizeOption$1(parsed.options.name);
9097
+ const type = normalizeOption$1(parsed.options.type);
8813
9098
  if (name) body.name = name;
8814
9099
  if (type) body.type = type;
8815
9100
  const response = await deps.fetch(`${ctx.baseUrl}/api/spaces/${encodeURIComponent(targetSpaceId)}/assets/clone`, {
@@ -8857,10 +9142,10 @@ async function recordDownloadedMirror(input) {
8857
9142
  }
8858
9143
  }
8859
9144
  async function listAssets(ctx, deps) {
8860
- return (await fetchJson(ctx, deps, `/api/spaces/${ctx.spaceId}/assets`)).assets || [];
9145
+ return (await fetchJson$1(ctx, deps, `/api/spaces/${ctx.spaceId}/assets`)).assets || [];
8861
9146
  }
8862
9147
  async function getAssetDetails(ctx, deps, assetId) {
8863
- return fetchJson(ctx, deps, `/api/spaces/${ctx.spaceId}/assets/${assetId}`);
9148
+ return fetchJson$1(ctx, deps, `/api/spaces/${ctx.spaceId}/assets/${assetId}`);
8864
9149
  }
8865
9150
  async function setActiveVariant(ctx, deps, assetId, variantId) {
8866
9151
  try {
@@ -8872,7 +9157,63 @@ async function setActiveVariant(ctx, deps, assetId, variantId) {
8872
9157
  throw error;
8873
9158
  }
8874
9159
  }
8875
- async function fetchJson(ctx, deps, apiPath) {
9160
+ async function getFailedDownloadVariantMessage(error, resolved, ctx, deps) {
9161
+ if (resolved.variant || !resolved.requestPath.includes("/variants/")) return null;
9162
+ if (!isVariantMediaUnavailableError(error)) return null;
9163
+ const variantId = resolved.mediaKey;
9164
+ let state;
9165
+ try {
9166
+ state = await requestSpaceState(ctx, deps);
9167
+ } catch {
9168
+ return null;
9169
+ }
9170
+ const variant = state.variants.find((candidate) => {
9171
+ return isVariantRecord(candidate) && candidate.id === variantId;
9172
+ });
9173
+ if (!variant || variant.status !== "failed") return null;
9174
+ const reason = formatGenerationFailureMessage(variant.error_message, variant.provider_metadata);
9175
+ return `Variant ${variant.id} failed and has no downloadable media${reason ? `: ${reason}` : "."}`;
9176
+ }
9177
+ function isVariantMediaUnavailableError(error) {
9178
+ return error instanceof Error && error.message.includes("Variant media not available");
9179
+ }
9180
+ function isVariantRecord(value) {
9181
+ return Boolean(value && typeof value === "object" && typeof value.id === "string");
9182
+ }
9183
+ async function requestSpaceState(ctx, deps) {
9184
+ const client = await deps.createMutationClient(ctx.env, ctx.spaceId);
9185
+ await client.connect();
9186
+ try {
9187
+ return await new Promise((resolve, reject) => {
9188
+ const syncClient = client;
9189
+ if (typeof syncClient.setOnSyncState !== "function" || typeof syncClient.requestSync !== "function") {
9190
+ reject(/* @__PURE__ */ new Error("Space sync is not available"));
9191
+ return;
9192
+ }
9193
+ let settled = false;
9194
+ const finish = (callback) => {
9195
+ if (settled) return;
9196
+ settled = true;
9197
+ clearTimeout(timer);
9198
+ callback();
9199
+ };
9200
+ const timer = setTimeout(() => {
9201
+ finish(() => reject(/* @__PURE__ */ new Error("Timed out waiting for space sync state")));
9202
+ }, 3e4);
9203
+ syncClient.setOnSyncState((state) => {
9204
+ finish(() => resolve(state));
9205
+ });
9206
+ try {
9207
+ syncClient.requestSync();
9208
+ } catch (error) {
9209
+ finish(() => reject(error));
9210
+ }
9211
+ });
9212
+ } finally {
9213
+ client.disconnect();
9214
+ }
9215
+ }
9216
+ async function fetchJson$1(ctx, deps, apiPath) {
8876
9217
  const response = await deps.fetch(`${ctx.baseUrl}${apiPath}`, { headers: {
8877
9218
  "Authorization": `Bearer ${ctx.accessToken}`,
8878
9219
  "Accept": "application/json"
@@ -9054,7 +9395,7 @@ function formatTimestamp(value) {
9054
9395
  if (Number.isNaN(date.getTime())) return String(value);
9055
9396
  return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z").slice(0, 20);
9056
9397
  }
9057
- function printUsage$2() {
9398
+ function printUsage$4() {
9058
9399
  console.log(`
9059
9400
  Usage:
9060
9401
  makefx assets
@@ -9071,7 +9412,7 @@ Usage:
9071
9412
  }
9072
9413
  //#endregion
9073
9414
  //#region src/cli/commands/variants.ts
9074
- var defaultDeps$2 = {
9415
+ var defaultDeps$4 = {
9075
9416
  loadConfig: loadStoredConfig,
9076
9417
  loadProjectConfig,
9077
9418
  resolveBaseUrl,
@@ -9083,12 +9424,12 @@ async function handleVariants(parsed) {
9083
9424
  await executeVariants(parsed);
9084
9425
  } catch (error) {
9085
9426
  console.error("Error:", error instanceof Error ? error.message : error);
9086
- printUsage$1();
9427
+ printUsage$3();
9087
9428
  process$1.exitCode = 1;
9088
9429
  }
9089
9430
  }
9090
- async function executeVariants(parsed, deps = defaultDeps$2) {
9091
- const ctx = await buildContext$2(parsed, deps);
9431
+ async function executeVariants(parsed, deps = defaultDeps$4) {
9432
+ const ctx = await buildContext$4(parsed, deps);
9092
9433
  const subcommand = parsed.positionals[0];
9093
9434
  if (subcommand === "delete") {
9094
9435
  const variantId = requireVariantId(parsed, "delete");
@@ -9136,7 +9477,7 @@ function requireVariantId(parsed, verb) {
9136
9477
  if (!variantId) throw new Error(`Variant ID is required: makefx variants ${verb} <variant-id>`);
9137
9478
  return variantId;
9138
9479
  }
9139
- async function buildContext$2(parsed, deps) {
9480
+ async function buildContext$4(parsed, deps) {
9140
9481
  const projectConfig = await deps.loadProjectConfig();
9141
9482
  const env = resolveCommandEnvironment(parsed, projectConfig);
9142
9483
  const spaceId = resolveCommandSpace(parsed, projectConfig);
@@ -9159,7 +9500,7 @@ async function withVariantClient(ctx, deps, run) {
9159
9500
  client.disconnect();
9160
9501
  }
9161
9502
  }
9162
- function printUsage$1() {
9503
+ function printUsage$3() {
9163
9504
  console.log(`
9164
9505
  Usage:
9165
9506
  makefx variants delete <variant-id>
@@ -9175,6 +9516,329 @@ Options:
9175
9516
  `);
9176
9517
  }
9177
9518
  //#endregion
9519
+ //#region src/cli/commands/recipes.ts
9520
+ var defaultDeps$3 = {
9521
+ loadConfig: loadStoredConfig,
9522
+ loadProjectConfig,
9523
+ resolveBaseUrl,
9524
+ fetch,
9525
+ print: console.log
9526
+ };
9527
+ async function handleRecipes(parsed) {
9528
+ try {
9529
+ await executeRecipes(parsed);
9530
+ } catch (error) {
9531
+ console.error("Error:", error instanceof Error ? error.message : error);
9532
+ printUsage$2();
9533
+ process$1.exitCode = 1;
9534
+ }
9535
+ }
9536
+ async function executeRecipes(parsed, deps = defaultDeps$3) {
9537
+ const subcommand = parsed.positionals[0];
9538
+ if (subcommand !== "run") throw new Error(`Unknown recipes command: ${subcommand ?? "(none)"}`);
9539
+ const sourceVariantId = parsed.positionals[1];
9540
+ if (!sourceVariantId) throw new Error("Variant ID is required: makefx recipes run <variant-id>");
9541
+ const rerun = await runRecipe(await buildContext$3(parsed, deps), deps, sourceVariantId);
9542
+ if (parsed.options.json === "true") deps.print(JSON.stringify(rerun, null, 2));
9543
+ else {
9544
+ deps.print(`Re-ran recipe from variant ${rerun.sourceVariantId}`);
9545
+ deps.print(` Variant: ${rerun.variant.id} (status: ${rerun.variant.status})`);
9546
+ deps.print(` Request: ${rerun.requestId}`);
9547
+ deps.print(`Watch progress with: makefx assets show ${rerun.variant.asset_id}`);
9548
+ }
9549
+ return {
9550
+ type: "run",
9551
+ rerun
9552
+ };
9553
+ }
9554
+ async function buildContext$3(parsed, deps) {
9555
+ const projectConfig = await deps.loadProjectConfig();
9556
+ const env = resolveCommandEnvironment(parsed, projectConfig);
9557
+ const spaceId = resolveCommandSpace(parsed, projectConfig);
9558
+ if (!spaceId) throw new Error("--space is required, or run: makefx init --space <id>");
9559
+ const config = await deps.loadConfig(env);
9560
+ if (!config) throw new Error(`Not logged in to ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
9561
+ if (config.token.expiresAt < Date.now()) throw new Error(`Token expired for ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
9562
+ if (env === "local") process$1.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
9563
+ return {
9564
+ spaceId,
9565
+ baseUrl: deps.resolveBaseUrl(env),
9566
+ accessToken: config.token.accessToken
9567
+ };
9568
+ }
9569
+ async function runRecipe(ctx, deps, sourceVariantId) {
9570
+ const response = await deps.fetch(`${ctx.baseUrl}/api/spaces/${encodeURIComponent(ctx.spaceId)}/variants/${encodeURIComponent(sourceVariantId)}/rerun`, {
9571
+ method: "POST",
9572
+ headers: {
9573
+ Authorization: `Bearer ${ctx.accessToken}`,
9574
+ Accept: "application/json"
9575
+ }
9576
+ });
9577
+ if (!response.ok) {
9578
+ const text = await response.text();
9579
+ throw new Error(`Recipe rerun failed (${response.status}): ${text || response.statusText}`);
9580
+ }
9581
+ return response.json();
9582
+ }
9583
+ function printUsage$2() {
9584
+ console.log(`
9585
+ Usage:
9586
+ makefx recipes run <variant-id>
9587
+ makefx recipes run <variant-id> --json
9588
+
9589
+ Options:
9590
+ --space <id> Target space ID; defaults from the initialized project
9591
+ --json Print machine-readable output
9592
+ --env <env> Environment (production|stage|local)
9593
+ --local Shortcut for --env local
9594
+ `);
9595
+ }
9596
+ //#endregion
9597
+ //#region src/cli/commands/collections.ts
9598
+ var COLLECTION_KINDS = new Set([
9599
+ "cast",
9600
+ "backgrounds",
9601
+ "scenes",
9602
+ "thumbnails",
9603
+ "maps",
9604
+ "deliverables",
9605
+ "custom"
9606
+ ]);
9607
+ var defaultDeps$2 = {
9608
+ loadConfig: loadStoredConfig,
9609
+ loadProjectConfig,
9610
+ resolveBaseUrl,
9611
+ fetch,
9612
+ createMutationClient: (env, spaceId) => WebSocketClient.create(env, spaceId),
9613
+ print: console.log
9614
+ };
9615
+ async function handleCollections(parsed) {
9616
+ try {
9617
+ await executeCollections(parsed);
9618
+ } catch (error) {
9619
+ console.error("Error:", error instanceof Error ? error.message : error);
9620
+ printUsage$1();
9621
+ process$1.exitCode = 1;
9622
+ }
9623
+ }
9624
+ async function executeCollections(parsed, deps = defaultDeps$2) {
9625
+ const subcommand = parsed.positionals[0] || "list";
9626
+ const ctx = await buildContext$2(parsed, deps);
9627
+ if (subcommand === "list") {
9628
+ const collections = await listCollections(ctx, deps);
9629
+ if (parsed.options.json === "true") deps.print(JSON.stringify(collections, null, 2));
9630
+ else printCollections(collections, deps.print);
9631
+ return {
9632
+ type: "list",
9633
+ collections
9634
+ };
9635
+ }
9636
+ if (subcommand === "items") {
9637
+ const collectionId = parsed.positionals[1];
9638
+ if (!collectionId) throw new Error("Collection ID is required: makefx collections items <collection-id>");
9639
+ const items = await listCollectionItems(ctx, deps, collectionId);
9640
+ if (parsed.options.json === "true") deps.print(JSON.stringify(items, null, 2));
9641
+ else printCollectionItems(items, deps.print);
9642
+ return {
9643
+ type: "items",
9644
+ items
9645
+ };
9646
+ }
9647
+ if (subcommand === "create") {
9648
+ const name = parsed.positionals.slice(1).join(" ").trim() || normalizeOption(parsed.options.name);
9649
+ if (!name) throw new Error("Usage: makefx collections create \"<name>\"");
9650
+ const collection = await withCollectionClient(ctx, deps, (client) => client.createCollection({
9651
+ id: normalizeOption(parsed.options.id),
9652
+ name,
9653
+ kind: parseKind(normalizeOption(parsed.options.kind)),
9654
+ color: parseNullableOption(parsed.options.color),
9655
+ description: parseNullableOption(parsed.options.description),
9656
+ sortIndex: parseOptionalInteger(parsed.options["sort-index"], "sort-index")
9657
+ }));
9658
+ printJsonOrLine(parsed, deps, collection, `Created collection ${collection.id} (${collection.name})`);
9659
+ return {
9660
+ type: "create",
9661
+ collection
9662
+ };
9663
+ }
9664
+ if (subcommand === "delete") {
9665
+ const collectionId = parsed.positionals[1];
9666
+ if (!collectionId) throw new Error("Collection ID is required: makefx collections delete <collection-id>");
9667
+ await withCollectionClient(ctx, deps, (client) => client.deleteCollection(collectionId));
9668
+ deps.print(`Deleted collection ${collectionId}`);
9669
+ return {
9670
+ type: "delete",
9671
+ collectionId
9672
+ };
9673
+ }
9674
+ if (subcommand === "add-asset") {
9675
+ const collectionId = parsed.positionals[1];
9676
+ const assetId = parsed.positionals[2] || normalizeOption(parsed.options.asset);
9677
+ if (!collectionId || !assetId) throw new Error("Usage: makefx collections add-asset <collection-id> <asset-id>");
9678
+ const item = await withCollectionClient(ctx, deps, (client) => client.createCollectionItem({
9679
+ collectionId,
9680
+ id: normalizeOption(parsed.options.id),
9681
+ subjectType: "asset",
9682
+ assetId,
9683
+ role: normalizeOption(parsed.options.role),
9684
+ pinnedVariantId: parseNullableOption(parsed.options.variant ?? parsed.options["pinned-variant"]),
9685
+ sortIndex: parseOptionalInteger(parsed.options["sort-index"], "sort-index")
9686
+ }));
9687
+ printJsonOrLine(parsed, deps, item, `Added asset ${assetId} to collection ${collectionId} as item ${item.id}`);
9688
+ return {
9689
+ type: "add",
9690
+ item
9691
+ };
9692
+ }
9693
+ if (subcommand === "add-variant") {
9694
+ const collectionId = parsed.positionals[1];
9695
+ const variantId = parsed.positionals[2] || normalizeOption(parsed.options.variant);
9696
+ if (!collectionId || !variantId) throw new Error("Usage: makefx collections add-variant <collection-id> <variant-id>");
9697
+ const item = await withCollectionClient(ctx, deps, (client) => client.createCollectionItem({
9698
+ collectionId,
9699
+ id: normalizeOption(parsed.options.id),
9700
+ subjectType: "variant",
9701
+ variantId,
9702
+ role: normalizeOption(parsed.options.role),
9703
+ sortIndex: parseOptionalInteger(parsed.options["sort-index"], "sort-index")
9704
+ }));
9705
+ printJsonOrLine(parsed, deps, item, `Added variant ${variantId} to collection ${collectionId} as item ${item.id}`);
9706
+ return {
9707
+ type: "add",
9708
+ item
9709
+ };
9710
+ }
9711
+ if (subcommand === "remove") {
9712
+ const collectionId = parsed.positionals[1];
9713
+ const itemId = parsed.positionals[2] || normalizeOption(parsed.options.item);
9714
+ if (!collectionId || !itemId) throw new Error("Usage: makefx collections remove <collection-id> <item-id>");
9715
+ await withCollectionClient(ctx, deps, (client) => client.deleteCollectionItem(collectionId, itemId));
9716
+ deps.print(`Removed item ${itemId} from collection ${collectionId}`);
9717
+ return {
9718
+ type: "remove",
9719
+ collectionId,
9720
+ itemId
9721
+ };
9722
+ }
9723
+ if (subcommand === "reorder") {
9724
+ const collectionId = parsed.positionals[1];
9725
+ const itemIds = splitIds(parsed.positionals[2] || normalizeOption(parsed.options.items));
9726
+ if (!collectionId || itemIds.length === 0) throw new Error("Usage: makefx collections reorder <collection-id> <item-id,item-id,...>");
9727
+ const items = await withCollectionClient(ctx, deps, (client) => client.reorderCollectionItems(collectionId, itemIds));
9728
+ if (parsed.options.json === "true") deps.print(JSON.stringify(items, null, 2));
9729
+ else deps.print(`Reordered ${items.length} item(s) in collection ${collectionId}`);
9730
+ return {
9731
+ type: "reorder",
9732
+ items
9733
+ };
9734
+ }
9735
+ throw new Error(`Unknown collections command: ${subcommand}`);
9736
+ }
9737
+ async function buildContext$2(parsed, deps) {
9738
+ const projectConfig = await deps.loadProjectConfig();
9739
+ const env = resolveCommandEnvironment(parsed, projectConfig);
9740
+ const spaceId = resolveCommandSpace(parsed, projectConfig);
9741
+ if (!spaceId) throw new Error("--space is required, or run: makefx init --space <id>");
9742
+ const config = await deps.loadConfig(env);
9743
+ if (!config) throw new Error(`Not logged in to ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
9744
+ if (config.token.expiresAt < Date.now()) throw new Error(`Token expired for ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
9745
+ if (env === "local") process$1.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
9746
+ return {
9747
+ env,
9748
+ spaceId,
9749
+ baseUrl: deps.resolveBaseUrl(env),
9750
+ accessToken: config.token.accessToken
9751
+ };
9752
+ }
9753
+ async function withCollectionClient(ctx, deps, run) {
9754
+ const client = await deps.createMutationClient(ctx.env, ctx.spaceId);
9755
+ await client.connect();
9756
+ try {
9757
+ return await run(client);
9758
+ } finally {
9759
+ client.disconnect();
9760
+ }
9761
+ }
9762
+ async function listCollections(ctx, deps) {
9763
+ return (await fetchJson(ctx, deps, `/api/spaces/${encodeURIComponent(ctx.spaceId)}/collections`)).collections || [];
9764
+ }
9765
+ async function listCollectionItems(ctx, deps, collectionId) {
9766
+ return (await fetchJson(ctx, deps, `/api/spaces/${encodeURIComponent(ctx.spaceId)}/collections/${encodeURIComponent(collectionId)}/items`)).items || [];
9767
+ }
9768
+ async function fetchJson(ctx, deps, apiPath) {
9769
+ const response = await deps.fetch(`${ctx.baseUrl}${apiPath}`, { headers: {
9770
+ "Authorization": `Bearer ${ctx.accessToken}`,
9771
+ "Accept": "application/json"
9772
+ } });
9773
+ if (!response.ok) {
9774
+ const errorText = await response.text();
9775
+ throw new Error(`Request failed (${response.status}): ${errorText || response.statusText}`);
9776
+ }
9777
+ return response.json();
9778
+ }
9779
+ function normalizeOption(value) {
9780
+ return !value || value === "true" ? void 0 : value;
9781
+ }
9782
+ function parseNullableOption(value) {
9783
+ if (value === void 0) return void 0;
9784
+ if (value === "true") return void 0;
9785
+ if (value === "" || value === "null" || value === "none") return null;
9786
+ return value;
9787
+ }
9788
+ function parseKind(value) {
9789
+ if (!value) return void 0;
9790
+ if (!COLLECTION_KINDS.has(value)) throw new Error(`Invalid collection kind "${value}". Expected one of: ${Array.from(COLLECTION_KINDS).join(", ")}`);
9791
+ return value;
9792
+ }
9793
+ function parseOptionalInteger(value, name) {
9794
+ const normalized = normalizeOption(value);
9795
+ if (!normalized) return void 0;
9796
+ const parsed = Number.parseInt(normalized, 10);
9797
+ if (!Number.isInteger(parsed) || String(parsed) !== normalized) throw new Error(`--${name} must be an integer`);
9798
+ return parsed;
9799
+ }
9800
+ function splitIds(value) {
9801
+ return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
9802
+ }
9803
+ function printJsonOrLine(parsed, deps, value, line) {
9804
+ if (parsed.options.json === "true") deps.print(JSON.stringify(value, null, 2));
9805
+ else deps.print(line);
9806
+ }
9807
+ function printCollections(collections, print) {
9808
+ if (collections.length === 0) {
9809
+ print("No collections found.");
9810
+ return;
9811
+ }
9812
+ print(`Found ${collections.length} collection(s):\n`);
9813
+ print("Sort".padEnd(8) + "Kind".padEnd(15) + "Collection".padEnd(38) + "Name");
9814
+ print("-".repeat(85));
9815
+ for (const collection of collections) print(String(collection.sort_index).padEnd(8) + truncate(collection.kind, 13).padEnd(15) + truncate(collection.id, 36).padEnd(38) + truncate(collection.name, 40));
9816
+ }
9817
+ function printCollectionItems(items, print) {
9818
+ if (items.length === 0) {
9819
+ print("No collection items found.");
9820
+ return;
9821
+ }
9822
+ print(`Found ${items.length} collection item(s):\n`);
9823
+ print("Sort".padEnd(8) + "Subject".padEnd(10) + "Item".padEnd(34) + "Asset".padEnd(28) + "Variant".padEnd(28) + "Role");
9824
+ print("-".repeat(128));
9825
+ for (const item of items) print(String(item.sort_index).padEnd(8) + item.subject_type.padEnd(10) + truncate(item.id, 32).padEnd(34) + truncate(item.asset_id || "-", 26).padEnd(28) + truncate(item.variant_id || item.pinned_variant_id || "-", 26).padEnd(28) + truncate(item.role, 24));
9826
+ }
9827
+ function printUsage$1() {
9828
+ console.log(`
9829
+ Usage:
9830
+ makefx collections
9831
+ makefx collections --json
9832
+ makefx collections create "<name>" [--kind custom]
9833
+ makefx collections delete <collection-id>
9834
+ makefx collections items <collection-id>
9835
+ makefx collections add-asset <collection-id> <asset-id> [--variant <variant-id>] [--role <role>]
9836
+ makefx collections add-variant <collection-id> <variant-id> [--role <role>]
9837
+ makefx collections remove <collection-id> <item-id>
9838
+ makefx collections reorder <collection-id> <item-id,item-id,...>
9839
+ `);
9840
+ }
9841
+ //#endregion
9178
9842
  //#region src/cli/commands/usage.ts
9179
9843
  var defaultDeps$1 = {
9180
9844
  loadConfig: loadStoredConfig,
@@ -9475,7 +10139,7 @@ Options:
9475
10139
  }
9476
10140
  //#endregion
9477
10141
  //#region src/cli/index.ts
9478
- var CLI_VERSION = "1.0.6+d87c3032db88";
10142
+ var CLI_VERSION = "1.1.0+e479be18b9f5";
9479
10143
  async function main() {
9480
10144
  const argv = process$1.argv.slice(2);
9481
10145
  if (argv[0] === "--") argv.shift();
@@ -9518,15 +10182,21 @@ function imageSizeValues() {
9518
10182
  function imageAspectValues() {
9519
10183
  return optionValues(IMAGE_MODEL_CAPABILITIES["pro"].supportedAspectRatios);
9520
10184
  }
9521
- function imageCapabilityHelp() {
10185
+ function imageModelValues(operation) {
10186
+ return optionValues(IMAGE_MODEL_SELECTIONS.filter((model) => operation === void 0 || isImageOperationSupportedByModel(model, operation)));
10187
+ }
10188
+ function imageCapabilityHelp(operation) {
9522
10189
  const sizeLines = IMAGE_MODEL_SELECTIONS.map((model) => `${model}: ${IMAGE_MODEL_CAPABILITIES[model].supportedImageSizes.join(", ")}`).join("; ");
9523
10190
  const referenceLines = IMAGE_MODEL_SELECTIONS.map((model) => `${model}: ${IMAGE_MODEL_CAPABILITIES[model].maxReferenceImages}`).join("; ");
10191
+ const operationLines = IMAGE_MODEL_SELECTIONS.map((model) => `${model}: ${IMAGE_MODEL_CAPABILITIES[model].supportedOperations.join(", ")}`).join("; ");
9524
10192
  return `Image:
9525
- --model <value> Image model: ${optionValues(IMAGE_MODEL_SELECTIONS)}
10193
+ --model <value> Image model: ${imageModelValues(operation)}
9526
10194
  --size <value> Output size (${sizeLines})
9527
10195
  --aspect <ratio> Aspect ratio: ${imageAspectValues()}
9528
10196
  --refs <refs> Reference limit (${referenceLines})
9529
- --follow <id> Resume/download a variant after a long generation or disconnect`;
10197
+ Model operations: ${operationLines}
10198
+ --follow <id> Resume/download a variant after a long generation or disconnect
10199
+ --collection <id> Place the completed asset in a Space collection`;
9530
10200
  }
9531
10201
  function videoResolutionValues() {
9532
10202
  return optionValues(uniqueValues(VIDEO_GENERATION_TIERS.flatMap((tier) => getVideoGenerationResolutionsForTier(tier))));
@@ -9540,20 +10210,23 @@ function videoDurationValues() {
9540
10210
  function videoTierValues() {
9541
10211
  return optionValues(VIDEO_GENERATION_TIERS);
9542
10212
  }
9543
- function videoModelValues() {
9544
- return optionValues(VIDEO_MODEL_SELECTIONS);
10213
+ function videoModelValues(operation) {
10214
+ return optionValues(VIDEO_MODEL_SELECTIONS.filter((model) => operation === void 0 || VIDEO_MODEL_SUPPORTED_OPERATIONS[model].includes(operation)));
9545
10215
  }
9546
- function videoCapabilityHelp() {
10216
+ function videoCapabilityHelp(operation) {
9547
10217
  const resolutionLines = VIDEO_GENERATION_TIERS.map((tier) => `${tier}: ${getVideoGenerationResolutionsForTier(tier).join(", ")}`).join("; ");
10218
+ const operationLines = VIDEO_MODEL_SELECTIONS.map((model) => `${model}: ${VIDEO_MODEL_SUPPORTED_OPERATIONS[model].join(", ")}`).join("; ");
9548
10219
  return `Video:
9549
- --model <value> Video model: ${videoModelValues()}
10220
+ --model <value> Video model: ${videoModelValues(operation)}
9550
10221
  --aspect <ratio> Video aspect ratio: ${videoAspectValues()}
9551
- --resolution <value> Veo-only output resolution (${resolutionLines})
9552
- --duration <seconds> Veo-only output duration: ${videoDurationValues()}
10222
+ --resolution <value> Veo: ${videoResolutionValues()}; Kling/fal Seedance: 720p|1080p (${resolutionLines})
10223
+ --duration <seconds> Veo/Kling/fal Seedance output duration: ${videoDurationValues()}
9553
10224
  --tier <tier> Veo-only model tier: ${videoTierValues()}
9554
10225
  --first-frame <ref> Veo start frame image variant or local image path
9555
10226
  --last-frame <ref> Veo final frame image variant or local image path
10227
+ Model operations: ${operationLines}
9556
10228
  --follow <id> Resume/download a variant after a long generation or disconnect
10229
+ --collection <id> Place the completed asset in a Space collection
9557
10230
 
9558
10231
  Veo 3.1 frames:
9559
10232
  --first-frame resolves to the first referenceVariantId and becomes the Veo top-level image input.
@@ -9620,6 +10293,10 @@ function printCommandHelp(command, positionals) {
9620
10293
  case "assets":
9621
10294
  printAssetsHelp();
9622
10295
  return;
10296
+ case "collections":
10297
+ case "collection":
10298
+ printCollectionsHelp();
10299
+ return;
9623
10300
  case "usage":
9624
10301
  printUsageHelp();
9625
10302
  return;
@@ -9630,6 +10307,10 @@ function printCommandHelp(command, positionals) {
9630
10307
  case "variant":
9631
10308
  printVariantsHelp();
9632
10309
  return;
10310
+ case "recipes":
10311
+ case "recipe":
10312
+ printRecipesHelp();
10313
+ return;
9633
10314
  default:
9634
10315
  console.error(`Unknown command: ${command}`);
9635
10316
  printHelp();
@@ -9656,6 +10337,7 @@ Generate:
9656
10337
  video generate "prompt" --name <name> --type animation -o <file>
9657
10338
 
9658
10339
  Continue:
10340
+ recipes run <variant-id> Re-run a stored recipe as a new sibling variant
9659
10341
  generate --follow <variant_id> -o <file>
9660
10342
  video generate --follow <variant_id> -o <file>
9661
10343
  audio <mode> generate --follow <variant_id> -o <file>
@@ -9666,6 +10348,8 @@ Inspect:
9666
10348
  assets show <asset-id> Show variants and lineage
9667
10349
  assets download <variant-id> -o <file> Download variant media
9668
10350
  assets clone <variant-id> --from-space <id> --to-space <id>
10351
+ collections add-asset <collection-id> <asset-id>
10352
+ collections add-variant <collection-id> <variant-id>
9669
10353
  variants rate <variant-id> approved|rejected
9670
10354
  variants star <variant-id>
9671
10355
 
@@ -9674,6 +10358,7 @@ Details:
9674
10358
  help audio Speech, dialogue, music, and SFX
9675
10359
  help video Video generate/refine/derive and keyframes
9676
10360
  help assets Asset inspection and downloads
10361
+ help collections Space collection organization
9677
10362
  help spaces Space setup and selection
9678
10363
  help upload Upload local media into a Space
9679
10364
 
@@ -9711,7 +10396,8 @@ Examples:
9711
10396
  makefx derive --refs character.png,background.png --name "Hero Scene" --type scene "Place the character in the background" -o hero-scene.png
9712
10397
  makefx refine --variant variant_123 "Keep composition, make the lighting warmer" -o warm.png
9713
10398
 
9714
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10399
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10400
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9715
10401
  `);
9716
10402
  }
9717
10403
  function printInitHelp() {
@@ -9805,33 +10491,35 @@ function printForgeHelp(command) {
9805
10491
  if (command === "generate") {
9806
10492
  console.log(`
9807
10493
  Usage:
9808
- makefx generate "prompt" --name <name> --type <type> -o <file> [--model ${optionValues(IMAGE_MODEL_SELECTIONS)}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--space <id>]
9809
- makefx generate --follow <variant_id> -o <file> [--space <id>]
10494
+ makefx generate "prompt" --name <name> --type <type> -o <file> [--model ${imageModelValues("generate")}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--collection <id>] [--space <id>]
10495
+ makefx generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9810
10496
 
9811
- ${imageCapabilityHelp()}
10497
+ ${imageCapabilityHelp("generate")}
9812
10498
 
9813
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10499
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10500
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9814
10501
  `);
9815
10502
  return;
9816
10503
  }
9817
10504
  if (command === "refine") {
9818
10505
  console.log(`
9819
10506
  Usage:
9820
- makefx refine --variant <variant_id> "prompt" -o <file> [--model ${optionValues(IMAGE_MODEL_SELECTIONS)}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--space <id>]
9821
- makefx refine --follow <variant_id> -o <file> [--space <id>]
10507
+ makefx refine --variant <variant_id> "prompt" -o <file> [--model ${imageModelValues("refine")}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--collection <id>] [--space <id>]
10508
+ makefx refine --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9822
10509
 
9823
- ${imageCapabilityHelp()}
10510
+ ${imageCapabilityHelp("refine")}
9824
10511
 
9825
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10512
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10513
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9826
10514
  `);
9827
10515
  return;
9828
10516
  }
9829
10517
  if (command === "batch") {
9830
10518
  console.log(`
9831
10519
  Usage:
9832
- makefx batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--model ${optionValues(IMAGE_MODEL_SELECTIONS)}] [--size ${imageSizeValues()}] [--aspect <ratio>]
10520
+ makefx batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--model ${imageModelValues("generate")}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--collection <id>]
9833
10521
 
9834
- ${imageCapabilityHelp()}
10522
+ ${imageCapabilityHelp("generate")}
9835
10523
 
9836
10524
  Batch creates several variants. If waiting stops, inspect the Space or download completed variants with:
9837
10525
  makefx assets download <variant_id> -o <file>
@@ -9840,12 +10528,13 @@ Batch creates several variants. If waiting stops, inspect the Space or download
9840
10528
  }
9841
10529
  console.log(`
9842
10530
  Usage:
9843
- makefx derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${optionValues(IMAGE_MODEL_SELECTIONS)}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--space <id>]
9844
- makefx derive --follow <variant_id> -o <file> [--space <id>]
10531
+ makefx derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${imageModelValues("derive")}] [--size ${imageSizeValues()}] [--aspect <ratio>] [--collection <id>] [--space <id>]
10532
+ makefx derive --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9845
10533
 
9846
- ${imageCapabilityHelp()}
10534
+ ${imageCapabilityHelp("derive")}
9847
10535
 
9848
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10536
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10537
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9849
10538
  `);
9850
10539
  }
9851
10540
  function printAudioHelp(positionals) {
@@ -9864,23 +10553,24 @@ Lists ElevenLabs voices available to the connected account when ElevenLabs is th
9864
10553
  if (second === "generate") {
9865
10554
  console.log(`
9866
10555
  Usage:
9867
- makefx audio ${first} generate "prompt" --name <name> -o <file> [--space <id>]
9868
- makefx audio ${first} generate --input <file> --name <name> -o <file> [--space <id>]
9869
- makefx audio ${first} generate --follow <variant_id> -o <file> [--space <id>]
9870
- ${first === "music" ? " makefx audio music generate \"prompt\" --provider lyria --name <name> -o <file> [--space <id>]\n" : ""}
10556
+ makefx audio ${first} generate "prompt" --name <name> -o <file> [--collection <id>] [--space <id>]
10557
+ makefx audio ${first} generate --input <file> --name <name> -o <file> [--collection <id>] [--space <id>]
10558
+ makefx audio ${first} generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
10559
+ ${first === "music" ? " makefx audio music generate \"prompt\" --provider lyria --name <name> -o <file> [--collection <id>] [--space <id>]\n" : ""}
9871
10560
  ${first === "music" ? "Provider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
9872
10561
  ${first === "speech" || first === "dialogue" ? "Model selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
9873
10562
  ${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" : ""}
9874
10563
  Long runs:
9875
10564
  --follow <variant_id> Resume/download a variant after a long generation or disconnect
10565
+ --collection <id> Place the completed asset in a Space collection
9876
10566
  `);
9877
10567
  return;
9878
10568
  }
9879
10569
  if (second === "batch") {
9880
10570
  console.log(`
9881
10571
  Usage:
9882
- makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
9883
- ${first === "music" ? " makefx audio music batch \"prompt\" --provider lyria --name <name> --count <2-8> --output-dir <dir> [--space <id>]\n" : ""}
10572
+ makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
10573
+ ${first === "music" ? " makefx audio music batch \"prompt\" --provider lyria --name <name> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]\n" : ""}
9884
10574
  ${first === "music" ? "Provider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
9885
10575
  ${first === "speech" || first === "dialogue" ? "Model selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
9886
10576
  ${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" : ""}
@@ -9889,8 +10579,8 @@ ${first === "speech" || first === "dialogue" ? "Voice selection:\n --voice <voi
9889
10579
  }
9890
10580
  console.log(`
9891
10581
  Usage:
9892
- makefx audio ${first} generate "prompt" --name <name> -o <file> [--space <id>]
9893
- makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
10582
+ makefx audio ${first} generate "prompt" --name <name> -o <file> [--collection <id>] [--space <id>]
10583
+ makefx audio ${first} batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
9894
10584
  ${first === "music" ? "\nProvider selection:\n --provider <elevenlabs|lyria> Music provider (default: server default)\n" : ""}
9895
10585
  ${first === "speech" || first === "dialogue" ? "\nModel selection:\n --model <model_id> ElevenLabs model (default: eleven_v3)\n" : ""}
9896
10586
  ${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" : ""}
@@ -9900,31 +10590,31 @@ ${first === "speech" || first === "dialogue" ? "\nVoice selection:\n --voice <v
9900
10590
  if (first === "generate") {
9901
10591
  console.log(`
9902
10592
  Usage:
9903
- makefx audio generate "prompt" --name <name> --type <type> -o <file> [--space <id>]
10593
+ makefx audio generate "prompt" --name <name> --type <type> -o <file> [--collection <id>] [--space <id>]
9904
10594
 
9905
10595
  Preferred mode form:
9906
- makefx audio <${modes}> generate "prompt" --name <name> -o <file> [--space <id>]
10596
+ makefx audio <${modes}> generate "prompt" --name <name> -o <file> [--collection <id>] [--space <id>]
9907
10597
  `);
9908
10598
  return;
9909
10599
  }
9910
10600
  if (first === "batch") {
9911
10601
  console.log(`
9912
10602
  Usage:
9913
- makefx audio batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--space <id>]
10603
+ makefx audio batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
9914
10604
 
9915
10605
  Preferred mode form:
9916
- makefx audio <${modes}> batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
10606
+ makefx audio <${modes}> batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
9917
10607
  `);
9918
10608
  return;
9919
10609
  }
9920
10610
  console.log(`
9921
10611
  Usage:
9922
- makefx audio <${modes}> generate "prompt" --name <name> -o <file> [--space <id>]
9923
- makefx audio <${modes}> generate --input <file> --name <name> -o <file> [--space <id>]
9924
- makefx audio <${modes}> generate --follow <variant_id> -o <file> [--space <id>]
9925
- makefx audio <${modes}> batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--space <id>]
10612
+ makefx audio <${modes}> generate "prompt" --name <name> -o <file> [--collection <id>] [--space <id>]
10613
+ makefx audio <${modes}> generate --input <file> --name <name> -o <file> [--collection <id>] [--space <id>]
10614
+ makefx audio <${modes}> generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
10615
+ makefx audio <${modes}> batch "prompt" --name <name> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
9926
10616
  makefx audio voices [--json]
9927
- makefx audio music generate "prompt" --provider <elevenlabs|lyria> --name <name> -o <file> [--space <id>]
10617
+ makefx audio music generate "prompt" --provider <elevenlabs|lyria> --name <name> -o <file> [--collection <id>] [--space <id>]
9928
10618
 
9929
10619
  Modes:
9930
10620
  speech Spoken narration or voiceover
@@ -9940,11 +10630,12 @@ Model selection:
9940
10630
  --model <model_id> ElevenLabs speech/dialogue model (default: eleven_v3)
9941
10631
 
9942
10632
  Low-level compatibility:
9943
- makefx audio generate "prompt" --name <name> --type <type> -o <file> [--space <id>]
9944
- makefx audio batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--space <id>]
10633
+ makefx audio generate "prompt" --name <name> --type <type> -o <file> [--collection <id>] [--space <id>]
10634
+ makefx audio batch "prompt" --name <name> --type <type> --count <2-8> --output-dir <dir> [--collection <id>] [--space <id>]
9945
10635
 
9946
10636
  Long runs:
9947
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10637
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10638
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9948
10639
  `);
9949
10640
  }
9950
10641
  function printVideoHelp(positionals) {
@@ -9952,67 +10643,73 @@ function printVideoHelp(positionals) {
9952
10643
  if (subcommand === "generate") {
9953
10644
  console.log(`
9954
10645
  Usage:
9955
- makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${videoModelValues()}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
9956
- makefx video generate --follow <variant_id> -o <file> [--space <id>]
10646
+ makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${videoModelValues("generate")}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10647
+ makefx video generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9957
10648
 
9958
- ${videoCapabilityHelp()}
10649
+ ${videoCapabilityHelp("generate")}
9959
10650
 
9960
10651
  Audio:
9961
- Video defaults to generated audio. Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
10652
+ Veo and Kling default to generated audio. fal Seedance is silent and rejects --audio.
10653
+ Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
9962
10654
 
9963
10655
  Long runs:
9964
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10656
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10657
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9965
10658
  `);
9966
10659
  return;
9967
10660
  }
9968
10661
  if (subcommand === "refine") {
9969
10662
  console.log(`
9970
10663
  Usage:
9971
- makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
9972
- makefx video refine --follow <variant_id> -o <file> [--space <id>]
10664
+ makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${videoModelValues("refine")}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10665
+ makefx video refine --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9973
10666
 
9974
- ${videoCapabilityHelp()}
10667
+ ${videoCapabilityHelp("refine")}
9975
10668
 
9976
10669
  Audio:
9977
- Video defaults to generated audio. Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
10670
+ Veo and Kling default to generated audio. Current Veo models do not support --no-audio.
9978
10671
 
9979
10672
  Long runs:
9980
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10673
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10674
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9981
10675
  `);
9982
10676
  return;
9983
10677
  }
9984
10678
  if (subcommand === "derive") {
9985
10679
  console.log(`
9986
10680
  Usage:
9987
- makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
9988
- makefx video derive --first-frame <variant_or_file> [--last-frame <variant_or_file>] --name <name> --type <type> "prompt" -o <file> [--model veo-3.1] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
9989
- makefx video derive --follow <variant_id> -o <file> [--space <id>]
10681
+ makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${videoModelValues("derive")}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10682
+ makefx video derive --first-frame <variant_or_file> [--last-frame <variant_or_file>] --name <name> --type <type> "prompt" -o <file> [--model veo-3.1] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10683
+ makefx video derive --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
9990
10684
 
9991
- ${videoCapabilityHelp()}
10685
+ ${videoCapabilityHelp("derive")}
9992
10686
 
9993
10687
  Audio:
9994
- Video defaults to generated audio. Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
10688
+ Veo and Kling default to generated audio. Current Veo models do not support --no-audio.
9995
10689
 
9996
10690
  Long runs:
9997
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10691
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10692
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
9998
10693
  `);
9999
10694
  return;
10000
10695
  }
10001
10696
  console.log(`
10002
10697
  Usage:
10003
- makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${videoModelValues()}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
10004
- makefx video generate --follow <variant_id> -o <file> [--space <id>]
10005
- makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
10006
- makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
10007
- makefx video derive --first-frame <variant_or_file> [--last-frame <variant_or_file>] --name <name> --type <type> "prompt" -o <file> [--model veo-3.1] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--space <id>]
10698
+ makefx video generate "prompt" --name <name> --type <type> -o <file> [--model ${videoModelValues()}] [--first-frame <variant_or_file>] [--last-frame <variant_or_file>] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10699
+ makefx video generate --follow <variant_id> -o <file> [--collection <id>] [--space <id>]
10700
+ makefx video refine --variant <variant_id> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10701
+ makefx video derive --refs <variant_or_file,variant_or_file> --name <name> --type <type> "prompt" -o <file> [--model ${videoModelValues()}] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10702
+ makefx video derive --first-frame <variant_or_file> [--last-frame <variant_or_file>] --name <name> --type <type> "prompt" -o <file> [--model veo-3.1] [--aspect ${videoAspectValues()}] [--resolution ${videoResolutionValues()}] [--duration ${videoDurationValues()}] [--tier ${videoTierValues()}] [--audio] [--collection <id>] [--space <id>]
10008
10703
 
10009
10704
  ${videoCapabilityHelp()}
10010
10705
 
10011
10706
  Audio:
10012
- Video defaults to generated audio. Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
10707
+ Veo and Kling default to generated audio. fal Seedance is silent and generate-only.
10708
+ Current Veo models do not support --no-audio; the CLI rejects it before creating a job.
10013
10709
 
10014
10710
  Long runs:
10015
- If the CLI stops waiting after a job has started, rerun the printed --follow command to download the variant.
10711
+ If the CLI stops waiting after a job has started, the job keeps running in Make Effects.
10712
+ Rerun the printed --follow command to resume the same variant instead of starting a duplicate.
10016
10713
  `);
10017
10714
  }
10018
10715
  function printRunsHelp() {
@@ -10047,6 +10744,21 @@ Options:
10047
10744
  --local Shortcut for --env local
10048
10745
  `);
10049
10746
  }
10747
+ function printRecipesHelp() {
10748
+ console.log(`
10749
+ Usage:
10750
+ makefx recipes run <variant-id>
10751
+ makefx recipes run <variant-id> --json
10752
+
10753
+ Re-runs the stored generation recipe as a new sibling variant of the same asset.
10754
+
10755
+ Options:
10756
+ --space <id> Target space ID; defaults from the initialized project
10757
+ --json Print machine-readable output
10758
+ --env <env> Environment (production|stage|local)
10759
+ --local Shortcut for --env local
10760
+ `);
10761
+ }
10050
10762
  function printAssetsHelp() {
10051
10763
  console.log(`
10052
10764
  Usage:
@@ -10062,6 +10774,26 @@ Usage:
10062
10774
  makefx assets set-active <asset-id> <variant-id>
10063
10775
  `);
10064
10776
  }
10777
+ function printCollectionsHelp() {
10778
+ console.log(`
10779
+ Usage:
10780
+ makefx collections
10781
+ makefx collections --json
10782
+ makefx collections create "<name>" [--kind cast|backgrounds|scenes|thumbnails|maps|deliverables|custom]
10783
+ makefx collections delete <collection-id>
10784
+ makefx collections items <collection-id>
10785
+ makefx collections add-asset <collection-id> <asset-id> [--variant <variant-id>] [--role <role>]
10786
+ makefx collections add-variant <collection-id> <variant-id> [--role <role>]
10787
+ makefx collections remove <collection-id> <item-id>
10788
+ makefx collections reorder <collection-id> <item-id,item-id,...>
10789
+
10790
+ Options:
10791
+ --space <id> Target space ID; defaults from the initialized project
10792
+ --env <env> Environment (production|stage|local)
10793
+ --local Shortcut for --env local
10794
+ --json Print machine-readable output where supported
10795
+ `);
10796
+ }
10065
10797
  function printUsageHelp() {
10066
10798
  console.log(`
10067
10799
  Usage:
@@ -10144,6 +10876,10 @@ async function dispatchCommand(command, parsed) {
10144
10876
  case "assets":
10145
10877
  await handleAssets(parsed);
10146
10878
  break;
10879
+ case "collections":
10880
+ case "collection":
10881
+ await handleCollections(parsed);
10882
+ break;
10147
10883
  case "usage":
10148
10884
  await handleUsage(parsed);
10149
10885
  break;
@@ -10154,6 +10890,10 @@ async function dispatchCommand(command, parsed) {
10154
10890
  case "variant":
10155
10891
  await handleVariants(parsed);
10156
10892
  break;
10893
+ case "recipes":
10894
+ case "recipe":
10895
+ await handleRecipes(parsed);
10896
+ break;
10157
10897
  default:
10158
10898
  console.error(`Unknown command: ${command}`);
10159
10899
  printHelp();