makefx 1.6.8 → 1.6.10

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 +403 -95
  3. package/package.json +11 -2
package/README.md CHANGED
@@ -61,4 +61,4 @@ IDs, or runs. Retired commands and aliases are not redirected.
61
61
 
62
62
  Run `makefx --help` or `makefx <noun> --help` for current options.
63
63
 
64
- Version: 1.6.8
64
+ Version: 1.6.10
package/makefx.mjs CHANGED
@@ -198,6 +198,24 @@ function loginCommandForEnvironment(environment) {
198
198
  if (environment === "local") return "makefx login --local";
199
199
  return `makefx login --env ${environment}`;
200
200
  }
201
+ //#endregion
202
+ //#region src/cli/lib/http-response.ts
203
+ var MAX_ERROR_BODY_LENGTH = 500;
204
+ function boundedBody(body) {
205
+ const trimmed = body.trim();
206
+ if (trimmed.length <= MAX_ERROR_BODY_LENGTH) return trimmed;
207
+ return `${trimmed.slice(0, MAX_ERROR_BODY_LENGTH)}…`;
208
+ }
209
+ async function readJsonResponse(response, description) {
210
+ const contentType = response.headers.get("content-type") ?? "unknown";
211
+ const body = await response.text();
212
+ try {
213
+ return JSON.parse(body);
214
+ } catch {
215
+ const detail = boundedBody(body);
216
+ throw new Error(`${description} returned non-JSON (HTTP ${response.status}; content-type: ${contentType})${detail ? `: ${detail}` : ""}`);
217
+ }
218
+ }
201
219
  var EXT_TO_MEDIA_TYPE = {
202
220
  ".aac": {
203
221
  mediaKind: "audio",
@@ -301,7 +319,7 @@ async function uploadLocalMediaAsReference(input) {
301
319
  headers: { "Authorization": `Bearer ${input.accessToken}` },
302
320
  body: formData
303
321
  });
304
- const data = await response.json();
322
+ const data = await readJsonResponse(response, "Reference upload request");
305
323
  if (!response.ok || !data.success || !data.variant) throw new Error(`Failed to upload reference "${input.filePath}": ${data.error || response.statusText}`);
306
324
  return {
307
325
  asset: data.asset,
@@ -4442,7 +4460,7 @@ require_websocket_server();
4442
4460
  var wrapper_default = import_websocket.default;
4443
4461
  //#endregion
4444
4462
  //#region src/cli/version.ts
4445
- var CLI_VERSION = "1.6.8+06570e7d00c2";
4463
+ var CLI_VERSION = "1.6.10+f750440f39c4";
4446
4464
  var CLI_VERSION_HEADER = "X-MakeFX-CLI-Version";
4447
4465
  function cliVersionHeaders() {
4448
4466
  return {
@@ -4720,7 +4738,6 @@ var WebSocketClient = class WebSocketClient {
4720
4738
  this.onSyncState?.({
4721
4739
  assets: syncMsg.assets,
4722
4740
  variants: syncMsg.variants,
4723
- lineage: syncMsg.lineage,
4724
4741
  collections: syncMsg.collections,
4725
4742
  collectionItems: syncMsg.collectionItems,
4726
4743
  canvasSettings: syncMsg.canvasSettings,
@@ -5789,7 +5806,7 @@ function printAssetSearchResults(results, print) {
5789
5806
  }
5790
5807
  }
5791
5808
  function printAssetDetails(details, ctx, print) {
5792
- const { asset, variants, lineage } = details;
5809
+ const { asset, variants, recipe_references: recipeReferences } = details;
5793
5810
  print(`\nAsset ${createAssetRef(asset.name, asset.id)}\n`);
5794
5811
  print(` Name: ${asset.name}`);
5795
5812
  print(` Type: ${asset.type || "unknown"}`);
@@ -5804,17 +5821,24 @@ function printAssetDetails(details, ctx, print) {
5804
5821
  print(` Status: ${variant.status}`);
5805
5822
  print(` Media: ${variant.media_kind || "-"}`);
5806
5823
  if (variant.media_mime_type) print(` MIME: ${variant.media_mime_type}`);
5807
- const provenance = formatMetadataSummary(variant.generation_provenance, [
5808
- "operation",
5809
- "assetType",
5810
- "mediaKind",
5811
- "model",
5812
- "prompt"
5813
- ]);
5814
- if (provenance) print(` Provenance: ${provenance}`);
5824
+ const detail = recipeReferences.variants.find((candidate) => candidate.variant_id === variant.id);
5825
+ if (detail) {
5826
+ print(` Recipe: ${detail.recipe_state.status}; ${detail.recipe_state.replayability.replace("_", " ")}`);
5827
+ const generator = detail.recipe?.generator;
5828
+ const execution = detail.recipe?.execution;
5829
+ const parameters = generator?.parameters;
5830
+ if (generator?.id) print(` Generator: ${String(generator.id)}`);
5831
+ if (execution?.provider) print(` Provider: ${String(execution.provider)}`);
5832
+ if (execution?.model) print(` Model: ${String(execution.model)}`);
5833
+ if (parameters?.prompt !== void 0) print(` Prompt: ${String(parameters.prompt)}`);
5834
+ if (detail.made_from.length > 0) for (const reference of detail.made_from) print(` Reference[${reference.sequence_index}] ${reference.slot}: ${formatRecipeReferenceTarget(reference)}`);
5835
+ print(` Views: made_from=${detail.made_from.length} used_by=${detail.used_by_total}${detail.used_by_truncated ? "+" : ""}`);
5836
+ }
5815
5837
  }
5816
5838
  }
5817
- if (lineage.length > 0) print(`\nLineage links: ${lineage.length}`);
5839
+ }
5840
+ function formatRecipeReferenceTarget(target) {
5841
+ return target.available && target.asset_id && target.asset_name ? createVariantRef(target.asset_name, target.asset_id, target.variant_id) : `unresolved:${target.variant_id}`;
5818
5842
  }
5819
5843
  function toAssetJson(asset) {
5820
5844
  return {
@@ -5840,29 +5864,41 @@ function toAssetDetailsJson(details) {
5840
5864
  createdAt: variant.created_at || null,
5841
5865
  updatedAt: variant.updated_at || null
5842
5866
  })),
5843
- lineageCount: details.lineage.length
5867
+ recipeReferences: details.recipe_references.variants.map((detail) => ({
5868
+ variantRef: createVariantRef(details.asset.name, details.asset.id, detail.variant_id),
5869
+ recipe: publicCliRecipe(detail.recipe),
5870
+ recipeState: {
5871
+ status: detail.recipe_state.status,
5872
+ replayability: detail.recipe_state.replayability,
5873
+ issues: detail.recipe_state.issues,
5874
+ replayabilityIssues: detail.recipe_state.replayability_issues
5875
+ },
5876
+ references: detail.made_from.map((reference) => ({
5877
+ variantRef: reference.available && reference.asset_id && reference.asset_name ? createVariantRef(reference.asset_name, reference.asset_id, reference.variant_id) : null,
5878
+ ...!reference.available ? { historicalVariantId: reference.variant_id } : {},
5879
+ slot: reference.slot,
5880
+ sequenceIndex: reference.sequence_index,
5881
+ modalityIndex: reference.modality_index,
5882
+ mediaKind: reference.media_kind,
5883
+ mimeType: reference.mime_type,
5884
+ sizeBytes: reference.size_bytes,
5885
+ width: reference.width,
5886
+ height: reference.height,
5887
+ durationMs: reference.duration_ms
5888
+ })),
5889
+ madeFrom: detail.made_from.map(formatRecipeReferenceTarget),
5890
+ usedBy: detail.used_by.map(formatRecipeReferenceTarget),
5891
+ usedByTotal: detail.used_by_total,
5892
+ usedByTruncated: detail.used_by_truncated
5893
+ })),
5894
+ totalVariantCount: details.recipe_references.total_variant_count,
5895
+ truncated: details.recipe_references.truncated
5844
5896
  };
5845
5897
  }
5846
- function formatMetadataSummary(value, preferredKeys) {
5847
- if (!value) return null;
5848
- const parsed = parseJsonObject(value);
5849
- if (!parsed) return truncate(value, 120);
5850
- const parts = [];
5851
- for (const key of preferredKeys) {
5852
- const field = parsed[key];
5853
- if (field === void 0 || field === null || typeof field === "object") continue;
5854
- parts.push(`${key}=${String(field)}`);
5855
- }
5856
- return parts.length > 0 ? truncate(parts.join(" "), 160) : truncate(JSON.stringify(parsed), 160);
5857
- }
5858
- function parseJsonObject(value) {
5859
- try {
5860
- const parsed = JSON.parse(value);
5861
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
5862
- } catch {
5863
- return null;
5864
- }
5865
- return null;
5898
+ function publicCliRecipe(recipe) {
5899
+ if (!recipe) return null;
5900
+ const { references: _references, regeneration: _regeneration, copiedFromVariantId: _copiedFromVariantId, ...publicRecipe } = recipe;
5901
+ return publicRecipe;
5866
5902
  }
5867
5903
  function formatTimestamp(value) {
5868
5904
  if (!value) return "-";
@@ -9327,6 +9363,17 @@ async function executeSpaceLens(parsed, deps = defaultDeps$8) {
9327
9363
  return result;
9328
9364
  }
9329
9365
  IMAGE_MODEL_SELECTIONS.map((selection) => `image/${selection}`);
9366
+ function referenceSlot(slot, mediaKind, minCount, maxCount, providerPosition, requiredMetadata = []) {
9367
+ return {
9368
+ slot,
9369
+ mediaKinds: [mediaKind],
9370
+ minCount,
9371
+ maxCount,
9372
+ providerPosition,
9373
+ ordering: "preserve_occurrence_order",
9374
+ ...requiredMetadata.length ? { requiredMetadata } : {}
9375
+ };
9376
+ }
9330
9377
  function input(name, type, required, description, options = {}) {
9331
9378
  return {
9332
9379
  name,
@@ -9371,7 +9418,12 @@ function imageGenerator(capability) {
9371
9418
  operation: "generate",
9372
9419
  tool: "generate_image",
9373
9420
  description: "Create a new image asset from text.",
9374
- inputs: generateInputs
9421
+ inputs: generateInputs,
9422
+ referenceSlots: [],
9423
+ referenceCardinality: {
9424
+ minCount: 0,
9425
+ maxCount: 0
9426
+ }
9375
9427
  }];
9376
9428
  if (capability.supportedOperations.includes("derive")) operations.push({
9377
9429
  operation: "derive",
@@ -9380,7 +9432,12 @@ function imageGenerator(capability) {
9380
9432
  inputs: [...generateInputs, {
9381
9433
  ...referenceInput,
9382
9434
  required: true
9383
- }]
9435
+ }],
9436
+ referenceSlots: [referenceSlot("reference_variant_refs", "image", 1, capability.maxReferenceImages, 0)],
9437
+ referenceCardinality: {
9438
+ minCount: 1,
9439
+ maxCount: capability.maxReferenceImages
9440
+ }
9384
9441
  });
9385
9442
  if (capability.supportedOperations.includes("refine")) operations.push({
9386
9443
  operation: "refine",
@@ -9409,7 +9466,12 @@ function imageGenerator(capability) {
9409
9466
  defaultValue: "medium"
9410
9467
  })] : [],
9411
9468
  ...capability.supportsSeed ? [input("seed", "integer", false, "Optional reproducible generation seed.")] : []
9412
- ]
9469
+ ],
9470
+ referenceSlots: [referenceSlot("source_variant_ref", "image", 1, 1, 0), referenceSlot("reference_variant_refs", "image", 0, Math.max(0, capability.maxReferenceImages - 1), 1)],
9471
+ referenceCardinality: {
9472
+ minCount: 1,
9473
+ maxCount: capability.maxReferenceImages
9474
+ }
9413
9475
  });
9414
9476
  return {
9415
9477
  id: `image/${capability.selection}`,
@@ -9426,6 +9488,72 @@ function imageGenerator(capability) {
9426
9488
  notes: capability.maxReferenceImages > 0 ? ["Use compact variant references; binary image input is not accepted."] : ["Text-to-image only; reference inputs are not supported."]
9427
9489
  };
9428
9490
  }
9491
+ function customImageGenerator(modelId) {
9492
+ const common = [
9493
+ SPACE_INPUT,
9494
+ fixedInput("generator_id", "image/custom", "Selects the configured custom image generator."),
9495
+ NAME_INPUT,
9496
+ input("asset_type", "string", true, "Asset classification stored in the Space."),
9497
+ PROMPT_INPUT,
9498
+ input("aspect_ratio", "string", false, "Output aspect ratio."),
9499
+ input("image_size", "string", false, "Output image size.")
9500
+ ];
9501
+ const referenceInput = input("reference_variant_refs", "string_array", false, "Ordered image references.", {
9502
+ minItems: 1,
9503
+ maxItems: 14
9504
+ });
9505
+ return {
9506
+ id: "image/custom",
9507
+ label: "Custom image",
9508
+ mediaKind: "image",
9509
+ modelIds: [modelId],
9510
+ defaultModelId: modelId,
9511
+ operations: [
9512
+ {
9513
+ operation: "generate",
9514
+ tool: "generate_image",
9515
+ description: "Generate with the configured custom model.",
9516
+ inputs: common,
9517
+ referenceSlots: [],
9518
+ referenceCardinality: {
9519
+ minCount: 0,
9520
+ maxCount: 0
9521
+ }
9522
+ },
9523
+ {
9524
+ operation: "derive",
9525
+ tool: "generate_image",
9526
+ description: "Generate with ordered image references.",
9527
+ inputs: [...common, {
9528
+ ...referenceInput,
9529
+ required: true
9530
+ }],
9531
+ referenceSlots: [referenceSlot("reference_variant_refs", "image", 1, 14, 0)],
9532
+ referenceCardinality: {
9533
+ minCount: 1,
9534
+ maxCount: 14
9535
+ }
9536
+ },
9537
+ {
9538
+ operation: "refine",
9539
+ tool: "edit_image",
9540
+ description: "Edit a source image.",
9541
+ inputs: [...common, input("source_variant_ref", "string", true, "Source image.")],
9542
+ referenceSlots: [referenceSlot("source_variant_ref", "image", 1, 1, 0)],
9543
+ referenceCardinality: {
9544
+ minCount: 1,
9545
+ maxCount: 1
9546
+ }
9547
+ }
9548
+ ],
9549
+ referenceRules: {
9550
+ mediaKind: "image",
9551
+ completedOnly: true,
9552
+ maxCount: 14
9553
+ },
9554
+ notes: ["The configured custom endpoint owns model-specific parameter support."]
9555
+ };
9556
+ }
9429
9557
  function videoGenerator(selection) {
9430
9558
  const defaultModel = getVideoGenerationModelForSelection(selection);
9431
9559
  const isVeo = selection === "veo-3.1";
@@ -9449,7 +9577,7 @@ function videoGenerator(selection) {
9449
9577
  allowedValues: VIDEO_GENERATION_TIERS,
9450
9578
  defaultValue: DEFAULT_VIDEO_GENERATION_TIER
9451
9579
  }));
9452
- if (isKling) parameterInputs.push(input("generate_audio", "boolean", false, "Generate synchronized audio.", {
9580
+ if (isKling || isVeo) parameterInputs.push(input("generate_audio", "boolean", false, "Generate synchronized audio.", {
9453
9581
  allowedValues: [true, false],
9454
9582
  defaultValue: true
9455
9583
  }));
@@ -9465,7 +9593,12 @@ function videoGenerator(selection) {
9465
9593
  operation: "generate",
9466
9594
  tool: "generate_video",
9467
9595
  description: "Create a new video asset from text.",
9468
- inputs: commonInputs
9596
+ inputs: commonInputs,
9597
+ referenceSlots: [],
9598
+ referenceCardinality: {
9599
+ minCount: 0,
9600
+ maxCount: 0
9601
+ }
9469
9602
  }];
9470
9603
  if (VIDEO_MODEL_SUPPORTED_OPERATIONS[selection].includes("refine")) operations.push({
9471
9604
  operation: "refine",
@@ -9478,7 +9611,12 @@ function videoGenerator(selection) {
9478
9611
  input("source_variant_ref", "string", true, "Completed video variant to refine."),
9479
9612
  PROMPT_INPUT,
9480
9613
  ...parameterInputs
9481
- ]
9614
+ ],
9615
+ referenceSlots: [referenceSlot("source_variant_ref", "video", 1, 1, 0)],
9616
+ referenceCardinality: {
9617
+ minCount: 1,
9618
+ maxCount: 1
9619
+ }
9482
9620
  });
9483
9621
  if (VIDEO_MODEL_SUPPORTED_OPERATIONS[selection].includes("derive")) operations.push({
9484
9622
  operation: "derive",
@@ -9487,9 +9625,14 @@ function videoGenerator(selection) {
9487
9625
  inputs: [...commonInputs, input("reference_variant_refs", "string_array", true, "Completed image variant references from find_assets or get_asset.", {
9488
9626
  minItems: 1,
9489
9627
  maxItems: maxReferences
9490
- })]
9628
+ })],
9629
+ referenceSlots: [referenceSlot("reference_variant_refs", "image", 1, maxReferences, 0)],
9630
+ referenceCardinality: {
9631
+ minCount: 1,
9632
+ maxCount: maxReferences
9633
+ }
9491
9634
  });
9492
- const notes = isVeo ? [`Resolution support by tier: ${VIDEO_GENERATION_TIERS.map((tier) => `${tier}=${VIDEO_GENERATION_RESOLUTIONS_BY_TIER[tier].join("/")}`).join(", ")}.`, "Synchronized audio is always enabled."] : isOmni ? ["Resolution, duration, tier, and generated-audio options are not accepted."] : selection === "fal-seedance" ? ["Text-to-video only; generated audio is not supported."] : ["At most one completed image reference is accepted."];
9635
+ const notes = isVeo ? [`Resolution support by tier: ${VIDEO_GENERATION_TIERS.map((tier) => `${tier}=${VIDEO_GENERATION_RESOLUTIONS_BY_TIER[tier].join("/")}`).join(", ")}.`] : isOmni ? ["Resolution, duration, tier, and generated-audio options are not accepted."] : selection === "fal-seedance" ? ["Text-to-video only; generated audio is not supported."] : ["At most one completed image reference is accepted."];
9493
9636
  return {
9494
9637
  id: `video/${selection}`,
9495
9638
  label: VIDEO_MODEL_LABELS[selection],
@@ -9561,7 +9704,22 @@ function seedanceVideoGenerator(capability) {
9561
9704
  operation,
9562
9705
  tool: "generate_video",
9563
9706
  description: capability.mode === "text" ? "Create a video from text." : capability.mode === "frame" ? "Animate a required authoritative start frame and optional authoritative end frame; extended references cannot be combined with them." : "Direct a video with ordered image, video, and audio references; these guide generation but cannot serve as authoritative start/end frames.",
9564
- inputs: [...common, ...modeInputs]
9707
+ inputs: [...common, ...modeInputs],
9708
+ referenceSlots: capability.mode === "frame" ? [referenceSlot("start_frame_variant_ref", "image", 1, 1, 0), referenceSlot("end_frame_variant_ref", "image", 0, 1, 1)] : capability.mode === "reference" ? [
9709
+ referenceSlot("image_reference_variant_refs", "image", 0, referenceMax("image"), 0, ["mimeType", "sizeBytes"]),
9710
+ referenceSlot("video_reference_variant_refs", "video", 0, referenceMax("video"), 1, ["mimeType", "sizeBytes"]),
9711
+ referenceSlot("audio_reference_variant_refs", "audio", 0, referenceMax("audio"), 2, ["mimeType", "sizeBytes"])
9712
+ ] : [],
9713
+ referenceCardinality: capability.mode === "text" ? {
9714
+ minCount: 0,
9715
+ maxCount: 0
9716
+ } : capability.mode === "frame" ? {
9717
+ minCount: 1,
9718
+ maxCount: 2
9719
+ } : {
9720
+ minCount: operation === "derive" ? 1 : 0,
9721
+ maxCount: capability.maxReferenceFiles
9722
+ }
9565
9723
  }));
9566
9724
  if (capability.operations.includes("refine")) {
9567
9725
  const budget = getSeedance2ReferenceBudget(capability, "refine");
@@ -9587,7 +9745,17 @@ function seedanceVideoGenerator(capability) {
9587
9745
  PROMPT_INPUT,
9588
9746
  ...parameters,
9589
9747
  ...editReferenceInputs
9590
- ]
9748
+ ],
9749
+ referenceSlots: [
9750
+ referenceSlot("source_variant_ref", "video", 1, 1, 0, ["mimeType", "sizeBytes"]),
9751
+ referenceSlot("image_reference_variant_refs", "image", 0, budget.maxAdditionalByKind.image, 1, ["mimeType", "sizeBytes"]),
9752
+ referenceSlot("video_reference_variant_refs", "video", 0, budget.maxAdditionalByKind.video, 2, ["mimeType", "sizeBytes"]),
9753
+ referenceSlot("audio_reference_variant_refs", "audio", 0, budget.maxAdditionalByKind.audio, 3, ["mimeType", "sizeBytes"])
9754
+ ],
9755
+ referenceCardinality: {
9756
+ minCount: 1,
9757
+ maxCount: capability.maxReferenceFiles
9758
+ }
9591
9759
  });
9592
9760
  }
9593
9761
  return {
@@ -9610,8 +9778,7 @@ function seedanceVideoGenerator(capability) {
9610
9778
  promptLabel: reference.promptLabel,
9611
9779
  acceptedMimeTypes: reference.acceptedMimeTypes,
9612
9780
  maxBytesPerFile: reference.maxBytesPerFile,
9613
- ...reference.combinedMaxBytes !== void 0 ? { combinedMaxBytes: reference.combinedMaxBytes } : {},
9614
- ...reference.combinedDurationSeconds ? { combinedDurationSeconds: reference.combinedDurationSeconds } : {}
9781
+ ...reference.combinedMaxBytes !== void 0 ? { combinedMaxBytes: reference.combinedMaxBytes } : {}
9615
9782
  }))
9616
9783
  },
9617
9784
  notes: [
@@ -9682,7 +9849,30 @@ function wan3VideoGenerator(capability) {
9682
9849
  operation: capability.mode === "text" ? "generate" : "derive",
9683
9850
  tool: "generate_video",
9684
9851
  description: capability.mode === "text" ? "Create a native-audio video from text." : capability.mode === "frame" ? "Animate a start frame and optional end frame." : "Direct one video from up to 20 ordered image, video, and audio references.",
9685
- inputs: [...common, ...modeInputs]
9852
+ inputs: [...common, ...modeInputs],
9853
+ referenceSlots: capability.mode === "frame" ? [referenceSlot("start_frame_variant_ref", "image", 1, 1, 0), referenceSlot("end_frame_variant_ref", "image", 0, 1, 1)] : capability.mode === "reference" ? [
9854
+ referenceSlot("image_reference_variant_refs", "image", 0, max("image"), 0, ["mimeType", "sizeBytes"]),
9855
+ referenceSlot("video_reference_variant_refs", "video", 0, max("video"), 1, [
9856
+ "mimeType",
9857
+ "sizeBytes",
9858
+ "durationMs"
9859
+ ]),
9860
+ referenceSlot("audio_reference_variant_refs", "audio", 0, max("audio"), 2, [
9861
+ "mimeType",
9862
+ "sizeBytes",
9863
+ "durationMs"
9864
+ ])
9865
+ ] : [],
9866
+ referenceCardinality: capability.mode === "text" ? {
9867
+ minCount: 0,
9868
+ maxCount: 0
9869
+ } : capability.mode === "frame" ? {
9870
+ minCount: 1,
9871
+ maxCount: 2
9872
+ } : {
9873
+ minCount: 1,
9874
+ maxCount: capability.maxReferenceFiles
9875
+ }
9686
9876
  }];
9687
9877
  if (capability.mode === "reference") operations.push({
9688
9878
  operation: "refine",
@@ -9709,7 +9899,29 @@ function wan3VideoGenerator(capability) {
9709
9899
  maxItems: 4,
9710
9900
  description: "Up to 4 additional videos; the source video is Video 1."
9711
9901
  } : item)
9712
- ]
9902
+ ],
9903
+ referenceSlots: [
9904
+ referenceSlot("source_variant_ref", "video", 1, 1, 0, [
9905
+ "mimeType",
9906
+ "sizeBytes",
9907
+ "durationMs"
9908
+ ]),
9909
+ referenceSlot("image_reference_variant_refs", "image", 0, 10, 1, ["mimeType", "sizeBytes"]),
9910
+ referenceSlot("video_reference_variant_refs", "video", 0, 4, 2, [
9911
+ "mimeType",
9912
+ "sizeBytes",
9913
+ "durationMs"
9914
+ ]),
9915
+ referenceSlot("audio_reference_variant_refs", "audio", 0, 5, 3, [
9916
+ "mimeType",
9917
+ "sizeBytes",
9918
+ "durationMs"
9919
+ ])
9920
+ ],
9921
+ referenceCardinality: {
9922
+ minCount: 1,
9923
+ maxCount: capability.maxReferenceFiles
9924
+ }
9713
9925
  });
9714
9926
  return {
9715
9927
  id: capability.generatorId,
@@ -9786,7 +9998,12 @@ function avatarVideoGenerator(model) {
9786
9998
  minItems: 1,
9787
9999
  maxItems: 1
9788
10000
  })
9789
- ]
10001
+ ],
10002
+ referenceSlots: [referenceSlot("image_reference_variant_refs", "image", 1, 1, 0, ["mimeType", "sizeBytes"]), referenceSlot("audio_reference_variant_refs", "audio", 1, 1, 1, ["mimeType", "sizeBytes"])],
10003
+ referenceCardinality: {
10004
+ minCount: 2,
10005
+ maxCount: 2
10006
+ }
9790
10007
  }],
9791
10008
  referenceRules: {
9792
10009
  mediaKind: null,
@@ -9837,13 +10054,18 @@ function audioGenerator(input_) {
9837
10054
  id: input_.id,
9838
10055
  label: input_.label,
9839
10056
  mediaKind: "audio",
9840
- modelIds: [input_.modelId],
10057
+ modelIds: input_.assetType === "speech" || input_.assetType === "dialogue" ? [...new Set([input_.modelId, DEFAULT_ELEVENLABS_SPEECH_MODEL_ID])] : [input_.modelId],
9841
10058
  defaultModelId: input_.modelId,
9842
10059
  operations: [{
9843
10060
  operation: "generate",
9844
10061
  tool: "generate_audio",
9845
10062
  description: `Create a new ${input_.assetType} audio asset.`,
9846
- inputs
10063
+ inputs,
10064
+ referenceSlots: [],
10065
+ referenceCardinality: {
10066
+ minCount: 0,
10067
+ maxCount: 0
10068
+ }
9847
10069
  }],
9848
10070
  referenceRules: {
9849
10071
  mediaKind: null,
@@ -9860,6 +10082,7 @@ function getGeneratorCatalog(overrides = {}) {
9860
10082
  const lyria = overrides.lyria ?? "lyria-3-clip-preview";
9861
10083
  return [
9862
10084
  ...Object.values(IMAGE_MODEL_CAPABILITIES).map(imageGenerator),
10085
+ ...overrides.customImage ? [customImageGenerator(overrides.customImage)] : [],
9863
10086
  ...VIDEO_MODEL_SELECTIONS.filter((selection) => !getWan3CapabilityBySelection(selection)).map(videoGenerator),
9864
10087
  ...SEEDANCE_2_SELECTIONS.map((selection) => seedanceVideoGenerator(SEEDANCE_2_CAPABILITIES[selection])),
9865
10088
  ...WAN_3_CAPABILITIES.map(wan3VideoGenerator),
@@ -9906,6 +10129,15 @@ function getGeneratorDefinition(id, overrides = {}) {
9906
10129
  }
9907
10130
  //#endregion
9908
10131
  //#region src/shared/generationReferences.ts
10132
+ var GENERATION_REFERENCE_SLOTS = [
10133
+ "source_variant_ref",
10134
+ "reference_variant_refs",
10135
+ "start_frame_variant_ref",
10136
+ "end_frame_variant_ref",
10137
+ "image_reference_variant_refs",
10138
+ "video_reference_variant_refs",
10139
+ "audio_reference_variant_refs"
10140
+ ];
9909
10141
  function isGenerationReference(value) {
9910
10142
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9911
10143
  const reference = value;
@@ -11306,6 +11538,7 @@ function rejectUnknownModelOptions(command, options) {
11306
11538
  var defaultDeps$4 = {
11307
11539
  loadConfig: loadStoredConfig,
11308
11540
  resolveBaseUrl,
11541
+ loadProjectConfig,
11309
11542
  saveProjectConfig,
11310
11543
  fetch,
11311
11544
  print: console.log
@@ -11319,10 +11552,12 @@ async function handleSpaces(parsed) {
11319
11552
  }
11320
11553
  }
11321
11554
  async function executeSpaces(parsed, deps = defaultDeps$4) {
11322
- const env = resolveCommandEnvironment(parsed);
11323
11555
  const subcommand = parsed.positionals[0];
11324
11556
  rejectUnknownSpaceOptions(subcommand, parsed.options);
11325
11557
  const jsonOutput = parsed.options.json === "true";
11558
+ const projectConfig = subcommand === "current" ? await deps.loadProjectConfig() : null;
11559
+ if (subcommand === "current" && !projectConfig) throw new Error("No current MakeFX Space. Run: makefx init --space <space-id>");
11560
+ const env = resolveCommandEnvironment(parsed, projectConfig);
11326
11561
  const config = await deps.loadConfig(env);
11327
11562
  if (!config) throw new Error(`Not logged in to ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
11328
11563
  if (config.token.expiresAt < Date.now()) throw new Error(`Token expired for ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
@@ -11388,7 +11623,35 @@ async function executeSpaces(parsed, deps = defaultDeps$4) {
11388
11623
  assets: details.assets
11389
11624
  };
11390
11625
  }
11391
- if (subcommand !== "list") throw new Error("Spaces command is required: list, show, create, or delete");
11626
+ if (subcommand === "current") {
11627
+ const details = await getSpaceDetails(ctx, deps, projectConfig.spaceId);
11628
+ if (jsonOutput) deps.print(JSON.stringify({
11629
+ project: {
11630
+ root: projectConfig.projectRoot ?? null,
11631
+ configPath: projectConfig.configPath ?? null,
11632
+ environment: env
11633
+ },
11634
+ space: publicSpace(details.space),
11635
+ assets: details.assets.map((asset) => ({
11636
+ assetRef: createAssetRef(asset.name, asset.id),
11637
+ name: asset.name,
11638
+ type: asset.type,
11639
+ mediaKind: asset.media_kind ?? null,
11640
+ activeVariantRef: asset.active_variant_id ? createVariantRef(asset.name, asset.id, asset.active_variant_id) : null
11641
+ }))
11642
+ }, null, 2));
11643
+ else {
11644
+ deps.print(`Current project: ${projectConfig.projectRoot ?? projectConfig.configPath ?? "unknown"}`);
11645
+ printSpaceDetails(details, deps.print);
11646
+ }
11647
+ return {
11648
+ type: "current",
11649
+ space: details.space,
11650
+ assets: details.assets,
11651
+ projectRoot: projectConfig.projectRoot ?? null
11652
+ };
11653
+ }
11654
+ if (subcommand !== "list") throw new Error("Spaces command is required: current, list, show, create, or delete");
11392
11655
  const spaces = await fetchSpaces(ctx, deps);
11393
11656
  if (jsonOutput) deps.print(JSON.stringify(spaces.map(publicSpace), null, 2));
11394
11657
  else printSpaces(spaces, deps.print);
@@ -11404,6 +11667,7 @@ function rejectUnknownSpaceOptions(subcommand, options) {
11404
11667
  "json"
11405
11668
  ];
11406
11669
  const commandOptions = subcommand ? {
11670
+ current: [],
11407
11671
  list: [],
11408
11672
  show: [],
11409
11673
  create: ["name", "init"],
@@ -11740,33 +12004,31 @@ var defaultDeps$2 = {
11740
12004
  print: console.log
11741
12005
  };
11742
12006
  var UploadUsageError = class extends Error {};
11743
- var LINEAGE_RELATION_TYPES = new Set([
11744
- "derived",
11745
- "refined",
11746
- "forked"
11747
- ]);
11748
- function parseLineageOption(raw, defaultRelationType) {
12007
+ function parseRecipeReferencesOption(raw) {
11749
12008
  if (!raw) return [];
11750
12009
  const entries = raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
11751
- if (entries.length === 0) throw new UploadUsageError("--lineage requires at least one compact variant ref");
12010
+ if (entries.length === 0) throw new UploadUsageError("--reference requires at least one slot=variant-ref entry");
11752
12011
  return entries.map((entry) => {
11753
- const atIndex = entry.lastIndexOf("@");
11754
- const separator = entry.lastIndexOf(":");
11755
- let ref = entry;
11756
- let relationType = defaultRelationType;
11757
- if (atIndex !== -1 && separator > atIndex) {
11758
- const suffix = entry.slice(separator + 1);
11759
- if (!LINEAGE_RELATION_TYPES.has(suffix)) throw new UploadUsageError(`--lineage relation in "${entry}" must be derived, refined, or forked`);
11760
- ref = entry.slice(0, separator);
11761
- relationType = suffix;
11762
- }
11763
- if (!ref.startsWith("asset:")) throw new UploadUsageError(`--lineage requires compact variant refs, got "${ref}"`);
12012
+ const separator = entry.indexOf("=");
12013
+ const slot = entry.slice(0, separator);
12014
+ const ref = entry.slice(separator + 1);
12015
+ if (separator <= 0 || !GENERATION_REFERENCE_SLOTS.includes(slot)) throw new UploadUsageError(`--reference requires a canonical slot before "=", got "${entry}"`);
12016
+ const parsed = parseVariantRef(ref);
12017
+ if (!parsed || parsed.variant === "active") throw new UploadUsageError(`--reference requires an exact compact variant ref, got "${ref}"`);
11764
12018
  return {
11765
- ref,
11766
- relationType
12019
+ slot,
12020
+ ref
11767
12021
  };
11768
12022
  });
11769
12023
  }
12024
+ function parseParametersOption(raw) {
12025
+ if (!raw) return {};
12026
+ try {
12027
+ const parsed = JSON.parse(raw);
12028
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
12029
+ } catch {}
12030
+ throw new UploadUsageError("--parameters must be a JSON object");
12031
+ }
11770
12032
  async function handleUpload(parsed) {
11771
12033
  try {
11772
12034
  await executeUpload(parsed);
@@ -11791,7 +12053,8 @@ async function executeUpload(parsed, deps = defaultDeps$2) {
11791
12053
  const jsonOutput = parsed.options.json === "true";
11792
12054
  if (!spaceId) throw new UploadUsageError("--space is required, or run: makefx init --space <id>");
11793
12055
  if (!assetId && !assetName) throw new UploadUsageError("Either --asset or --name is required");
11794
- const lineageEntries = parseLineageOption(parsed.options.lineage, assetId ? "refined" : "derived");
12056
+ const recipeReferences = parseRecipeReferencesOption(parsed.options.reference);
12057
+ const parameters = parseParametersOption(parsed.options.parameters);
11795
12058
  const mediaType = resolveMediaType(path.extname(filePath).toLowerCase(), requestedMediaKind);
11796
12059
  const config = await deps.loadConfig(env);
11797
12060
  if (!config) throw new Error(`Not logged in to ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
@@ -11799,15 +12062,10 @@ async function executeUpload(parsed, deps = defaultDeps$2) {
11799
12062
  const baseUrl = deps.resolveBaseUrl(env);
11800
12063
  const accessToken = config.token.accessToken;
11801
12064
  const createStateClient = () => (deps.createStateClient ?? defaultDeps$2.createStateClient)(env, spaceId);
11802
- let lineage = [];
11803
- if (assetId || lineageEntries.length > 0) {
12065
+ if (assetId) {
11804
12066
  if (assetId && !assetId.startsWith("asset:")) throw new Error("A compact asset ref is required");
11805
12067
  const state = await readReferenceSpaceState(createStateClient);
11806
12068
  if (assetId) assetId = resolveAssetRef(assetId, state.assets);
11807
- lineage = lineageEntries.map((entry) => ({
11808
- parentVariantId: resolveVariantRef(entry.ref, state.assets, state.variants),
11809
- relationType: entry.relationType
11810
- }));
11811
12069
  }
11812
12070
  if (env === "local") process$1.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
11813
12071
  try {
@@ -11826,20 +12084,34 @@ async function executeUpload(parsed, deps = defaultDeps$2) {
11826
12084
  formData.append("assetName", assetName);
11827
12085
  formData.append("assetType", assetType);
11828
12086
  }
11829
- if (lineage.length > 0) formData.append("lineage", JSON.stringify(lineage));
12087
+ if (hasExternalRecipeOptions(parsed.options)) {
12088
+ formData.append("operation", "import");
12089
+ if (parsed.options.generator) formData.append("generatorId", parsed.options.generator);
12090
+ if (parsed.options["generator-operation"]) formData.append("generatorOperation", parsed.options["generator-operation"]);
12091
+ if (parsed.options.service) formData.append("service", parsed.options.service);
12092
+ if (parsed.options.provider) formData.append("provider", parsed.options.provider);
12093
+ if (parsed.options.model) formData.append("model", parsed.options.model);
12094
+ if (parsed.options.prompt !== void 0) formData.append("prompt", parsed.options.prompt);
12095
+ if (parsed.options["external-run-id"]) formData.append("externalRunId", parsed.options["external-run-id"]);
12096
+ formData.append("parameters", JSON.stringify(parameters));
12097
+ formData.append("references", JSON.stringify(recipeReferences.map((reference) => ({
12098
+ slot: reference.slot,
12099
+ variantRef: reference.ref
12100
+ }))));
12101
+ }
11830
12102
  if (!jsonOutput) {
11831
12103
  deps.print(`\nUploading "${fileName}" to space ${spaceId}...`);
11832
12104
  deps.print(` Media kind: ${mediaType.mediaKind}`);
11833
12105
  if (assetId) deps.print(` Target asset: ${requestedAssetRef}`);
11834
12106
  else deps.print(` Creating asset: "${assetName}" (${assetType})`);
11835
- for (const entry of lineageEntries) deps.print(` Lineage: ${entry.relationType} from ${entry.ref}`);
12107
+ for (const entry of recipeReferences) deps.print(` Recipe Reference: ${entry.slot} = ${entry.ref}`);
11836
12108
  }
11837
12109
  const response = await deps.fetch(`${baseUrl}/api/spaces/${spaceId}/upload`, {
11838
12110
  method: "POST",
11839
12111
  headers: { "Authorization": `Bearer ${accessToken}` },
11840
12112
  body: formData
11841
12113
  });
11842
- const data = await response.json();
12114
+ const data = await readJsonResponse(response, "Upload request");
11843
12115
  if (!response.ok) throw new Error(`Upload failed: ${"error" in data ? data.error : response.statusText}`);
11844
12116
  const upload = data;
11845
12117
  const result = {
@@ -11866,7 +12138,8 @@ async function executeUpload(parsed, deps = defaultDeps$2) {
11866
12138
  assetRef: result.assetRef,
11867
12139
  variantRef: result.variantRef,
11868
12140
  status: upload.variant.status,
11869
- mediaKind: upload.variant.media_kind || mediaType.mediaKind
12141
+ mediaKind: upload.variant.media_kind || mediaType.mediaKind,
12142
+ ...upload.provenance ? { provenance: upload.provenance } : {}
11870
12143
  }, null, 2));
11871
12144
  return result;
11872
12145
  }
@@ -11883,6 +12156,10 @@ async function executeUpload(parsed, deps = defaultDeps$2) {
11883
12156
  deps.print(` Status: ${upload.variant.status}`);
11884
12157
  deps.print(` Media: ${upload.variant.media_kind || mediaType.mediaKind}`);
11885
12158
  if (upload.variant.media_mime_type) deps.print(` MIME: ${upload.variant.media_mime_type}`);
12159
+ if (upload.provenance) {
12160
+ deps.print(` Recipe: ${upload.provenance.state}`);
12161
+ if (upload.provenance.missingReasons.length > 0) deps.print(` Guidance: ${upload.provenance.missingReasons.join(", ")}`);
12162
+ }
11886
12163
  deps.print("\nTo inspect:");
11887
12164
  deps.print(` makefx variants show ${result.variantRef} --wait --space ${spaceId}`);
11888
12165
  return result;
@@ -11918,12 +12195,33 @@ function rejectUnknownUploadOptions(options) {
11918
12195
  "name",
11919
12196
  "type",
11920
12197
  "media-kind",
11921
- "lineage",
11922
- "json"
12198
+ "json",
12199
+ "generator",
12200
+ "generator-operation",
12201
+ "service",
12202
+ "provider",
12203
+ "model",
12204
+ "prompt",
12205
+ "parameters",
12206
+ "reference",
12207
+ "external-run-id"
11923
12208
  ]);
11924
12209
  const unknown = Object.keys(options).find((name) => !allowed.has(name));
11925
12210
  if (unknown) throw new UploadUsageError(`Unknown option: --${unknown}`);
11926
12211
  }
12212
+ function hasExternalRecipeOptions(options) {
12213
+ return [
12214
+ "generator",
12215
+ "generator-operation",
12216
+ "service",
12217
+ "provider",
12218
+ "model",
12219
+ "prompt",
12220
+ "parameters",
12221
+ "reference",
12222
+ "external-run-id"
12223
+ ].some((name) => options[name] !== void 0);
12224
+ }
11927
12225
  function printUsage$2() {
11928
12226
  console.log(`
11929
12227
  Usage:
@@ -11936,9 +12234,14 @@ Options:
11936
12234
  --name <name> New asset name (creates asset + variant)
11937
12235
  --type <type> Asset type for new assets (default: character)
11938
12236
  --media-kind <k> Optional explicit kind: image, audio, or video
11939
- --lineage <refs> Source variant refs this upload was made from, comma-separated,
11940
- each optionally suffixed :derived, :refined, or :forked
11941
- (default: refined with --asset, derived with --name)
12237
+ --generator <id> Canonical generator/service schema for an external result
12238
+ --service <id> External execution service
12239
+ --provider <id> Exact external provider
12240
+ --model <id> Exact external model
12241
+ --prompt <text> Exact submitted prompt
12242
+ --parameters <j> Additional exact generator parameters as a JSON object
12243
+ --reference <r> Ordered slot=exact-variant-ref entries, comma-separated
12244
+ --external-run-id <id> Optional bounded external execution identifier
11942
12245
  --json Print machine-readable output
11943
12246
  --env <env> Environment (production|stage|local)
11944
12247
  --local Shortcut for --env local
@@ -11946,7 +12249,9 @@ Options:
11946
12249
  Examples:
11947
12250
  makefx assets upload hero.png --name "Hero Character"
11948
12251
  makefx assets upload paintover.png --asset asset:hero~27f8f176
11949
- makefx assets upload paintover.png --asset asset:hero~27f8f176 --lineage asset:hero~27f8f176@active
12252
+ makefx assets upload avatar.mp4 --name "Avatar" --generator avatar --service fal \
12253
+ --provider fal --model avatar-v1 --prompt "Welcome" \
12254
+ --reference image_reference_variant_refs=asset:portrait~27f8@a1,audio_reference_variant_refs=asset:voice~91ab@b2
11950
12255
  `);
11951
12256
  }
11952
12257
  //#endregion
@@ -12372,7 +12677,7 @@ Assets:
12372
12677
  graph run | status | cancel
12373
12678
 
12374
12679
  Workspace:
12375
- spaces list | show | create | delete | lens
12680
+ spaces current | list | show | create | delete | lens
12376
12681
  init | login | logout
12377
12682
  usage | spend | billing
12378
12683
 
@@ -12424,12 +12729,14 @@ Generate commands also accept [--collection <id>] [--space <id>].`,
12424
12729
  makefx assets show <asset-ref> [--json]
12425
12730
  makefx assets update <asset-ref> [--name <name>] [--type <type>] [--tags <tag,...>]
12426
12731
  makefx assets delete <asset-ref> [--yes]
12427
- makefx assets upload <file> (--name <name> | --asset <asset-ref>) [--type <type>] [--lineage <variant-ref[:relation],...>]
12732
+ makefx assets upload <file> (--name <name> | --asset <asset-ref>) [--type <type>]
12733
+ [--generator <id> --service <id> --provider <id> --model <id>]
12734
+ [--prompt <text>] [--parameters <json>] [--reference <slot=variant-ref,...>]
12428
12735
  makefx assets download <variant-ref> -o <file>
12429
12736
 
12430
- Upload --lineage records which variants the file was made from: comma-separated
12431
- compact variant refs, each optionally suffixed :derived, :refined, or :forked
12432
- (default: refined when uploading to --asset, derived when creating with --name).`,
12737
+ External import options attach the exact canonical Recipe without executing a
12738
+ provider or billing generation. --reference accepts ordered canonical slots and
12739
+ exact compact Variant refs; raw uploads omit all external Recipe options.`,
12433
12740
  variants: `Usage:
12434
12741
  makefx variants show <variant-ref> [--wait <seconds>] [--json]
12435
12742
  makefx variants update <variant-ref> [--starred true|false] [--rating approved|rejected|none]
@@ -12461,6 +12768,7 @@ compact variant refs, each optionally suffixed :derived, :refined, or :forked
12461
12768
  makefx bindings set <draft-ref> <slot> --source variant|asset-active|draft-output --ref <ref> [--sort <index>]
12462
12769
  makefx bindings clear <draft-ref> <slot> [--sort <index>]`,
12463
12770
  spaces: `Usage:
12771
+ makefx spaces current [--json]
12464
12772
  makefx spaces list [--json]
12465
12773
  makefx spaces show <space-id> [--json]
12466
12774
  makefx spaces create "<name>" [--init] [--json]
package/package.json CHANGED
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "makefx",
3
- "version": "1.6.8",
4
- "description": "Command-line interface for AI-assisted game asset production with Make Effects.",
3
+ "version": "1.6.10",
4
+ "description": "MakeFX CLI for reproducible image, video, and audio Assets in Make Effects.",
5
+ "homepage": "https://makefx.app/docs/cli",
6
+ "keywords": [
7
+ "makefx",
8
+ "make-effects",
9
+ "mcp",
10
+ "media-generation",
11
+ "reproducibility",
12
+ "cli"
13
+ ],
5
14
  "license": "MIT",
6
15
  "type": "module",
7
16
  "repository": {