@docyrus/animaguru 0.35.1 → 0.38.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.
package/dist/animaguru.js CHANGED
@@ -19761,7 +19761,7 @@ var require_ignore2 = __commonJS({
19761
19761
  // cli/package.json
19762
19762
  var package_default = {
19763
19763
  name: "@docyrus/animaguru",
19764
- version: "0.35.1",
19764
+ version: "0.38.0",
19765
19765
  description: "Agent-friendly AnimaGuru CLI built with CrustJS",
19766
19766
  type: "module",
19767
19767
  bin: {
@@ -45002,9 +45002,11 @@ var ALL_TOP_ACTIONS = [...new Set(Object.values(SPRITE_TOP_ACTIONS).flat())];
45002
45002
  var CUSTOM_FRAMES = { min: 2, max: 12 };
45003
45003
 
45004
45004
  // shared/sprites/models.ts
45005
- var DEFAULT_SPRITE_IMAGE_MODEL = "fal/gpt-image-2.5-medium";
45005
+ var DEFAULT_SPRITE_IMAGE_MODEL = "fal/gpt-image-2.6-medium";
45006
45006
  var SPRITE_IMAGE_MODELS = [
45007
- { id: DEFAULT_SPRITE_IMAGE_MODEL, label: "GPT Image 2.5 \xB7 medium (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 0.3, quality: "medium", endpoints: { generate: "openai/gpt-image-2.5/flare/text-to-image", edit: "openai/gpt-image-2.5/flare/edit" } },
45007
+ { id: DEFAULT_SPRITE_IMAGE_MODEL, label: "GPT Image 2.6 Flare \xB7 medium (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 0.3, quality: "medium", endpoints: { generate: "openai/gpt-image-2.6/flare/text-to-image", edit: "openai/gpt-image-2.6/flare/edit" } },
45008
+ { id: "fal/gpt-image-2.6-sunburst-high", label: "GPT Image 2.6 Sunburst \xB7 high (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 1, quality: "high", endpoints: { generate: "openai/gpt-image-2.6/sunburst/text-to-image", edit: "openai/gpt-image-2.6/sunburst/edit" } },
45009
+ { id: "fal/gpt-image-2.5-medium", label: "GPT Image 2.5 \xB7 medium (fal)", provider: "fal", edit: true, nativeAlpha: true, maxImages: 16, relCost: 0.3, quality: "medium", endpoints: { generate: "openai/gpt-image-2.5/flare/text-to-image", edit: "openai/gpt-image-2.5/flare/edit" } },
45008
45010
  { id: "fal/gpt-image-2-low", label: "GPT Image 2 \xB7 low (fal)", provider: "fal", edit: true, spriteBackupCutout: true, maxImages: 16, relCost: 0.2, quality: "low", endpoints: { generate: "openai/gpt-image-2", edit: "openai/gpt-image-2/edit" } },
45009
45011
  { id: "fal/muse-image", label: "Muse Image (fal)", provider: "fal", edit: true, maxImages: 10, relCost: 0.2, endpoints: { generate: "meta/muse-image/text-to-image", edit: "meta/muse-image/edit" } },
45010
45012
  { id: "fal/krea-2-turbo", label: "Krea 2 Turbo (fal)", provider: "fal", edit: false, maxImages: 0, relCost: 0.16, endpoints: { generate: "fal-ai/krea-2/turbo" } },
@@ -45013,7 +45015,11 @@ var SPRITE_IMAGE_MODELS = [
45013
45015
  { id: "bytedance/seedream-5-pro", label: "Seedream 5 Pro", provider: "workers-ai", edit: true, maxImages: 10, relCost: 1.5 },
45014
45016
  { id: "xai/grok-imagine-image-2.0", label: "Grok Imagine Image 2.0", provider: "workers-ai", edit: true, maxImages: 5, relCost: 1 },
45015
45017
  { id: "alibaba/qwen-image-3.0-pro", label: "Qwen Image 3.0 Pro", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.5 },
45016
- { id: "krea/krea-2-medium-turbo", label: "Krea 2 Medium Turbo", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.25 }
45018
+ { id: "krea/krea-2-medium-turbo", label: "Krea 2 Medium Turbo", provider: "workers-ai", edit: false, maxImages: 0, relCost: 0.25 },
45019
+ // Seedream on AnimaGuru's own BytePlus account: $0.045 an image (Pro, up to 2.61 MP), $0.018 (Flash), $0.035 (Lite).
45020
+ { id: "byteplus/seedream-5.0-pro", label: "Seedream 5.0 Pro (BytePlus)", provider: "byteplus", providerModel: "dola-seedream-5-0-pro-260628", edit: true, spriteBackupCutout: true, maxImages: 10, relCost: 0.35 },
45021
+ { id: "byteplus/seedream-5.0-flash", label: "Seedream 5.0 Flash (BytePlus)", provider: "byteplus", providerModel: "dola-seedream-5-0-flash-260915", edit: true, spriteBackupCutout: true, maxImages: 10, relCost: 0.15 },
45022
+ { id: "byteplus/seedream-5.0-lite", label: "Seedream 5.0 Lite (BytePlus)", provider: "byteplus", providerModel: "seedream-5-0-260128", edit: true, spriteBackupCutout: true, maxImages: 14, relCost: 0.3 }
45017
45023
  ];
45018
45024
  var SPRITE_IMAGE_MODEL_IDS = SPRITE_IMAGE_MODELS.map((m) => m.id);
45019
45025
  function spriteImageModel(id2) {
@@ -45048,6 +45054,8 @@ var HEYGEN_LONG = { max: 30, creditsPerSecond: { "480p": 4.5, "768p": 7.5, "1080
45048
45054
  var H3_MAX = "minimax/h3-max";
45049
45055
  var FLUX_3_MAX = "blackforestlabs/flux-3-max";
45050
45056
  var h3Endpoints = (route) => ({ "image-to-video": `${route}/image-to-video`, "text-to-video": `${route}/text-to-video`, "extend-video": `${route}/extend-video` });
45057
+ var SEEDANCE_WORKFLOWS = ["image-to-video", "text-to-video", "reference-to-video"];
45058
+ var SEEDANCE_REFERENCES = { referencesOnly: true, mediaReferences: ["images", "audio"] };
45051
45059
  var SPRITE_VIDEO_MODELS = [
45052
45060
  {
45053
45061
  id: "pruna/p-video-2-pro",
@@ -45145,6 +45153,59 @@ var SPRITE_VIDEO_MODELS = [
45145
45153
  promptExpansion: ["balanced", "quality"],
45146
45154
  estimatedCreditsPerSecond: { "720p": { quality: 17 }, "1080p": { quality: 29 } },
45147
45155
  seconds: { min: 5, max: 20 }
45156
+ },
45157
+ // BytePlus bills a clip's tokens (width × height × frames / 1024, at 24 fps plus a frame); credits take each resolution's largest shape.
45158
+ {
45159
+ id: "byteplus/seedance-2.5",
45160
+ label: "Seedance 2.5 (BytePlus)",
45161
+ provider: "byteplus",
45162
+ providerModel: "dreamina-seedance-2-5-260628",
45163
+ workflows: SEEDANCE_WORKFLOWS,
45164
+ resolutions: ["480p", "720p"],
45165
+ workflowOptions: { "reference-to-video": { ...SEEDANCE_REFERENCES, mediaSeconds: 30 } },
45166
+ modes: ["quality"],
45167
+ lastFrame: true,
45168
+ estimatedCreditsPerSecond: { "480p": { quality: 10.86 }, "720p": { quality: 23.51 } },
45169
+ seconds: { min: 4, max: 30 }
45170
+ },
45171
+ {
45172
+ id: "byteplus/seedance-2.0",
45173
+ label: "Seedance 2.0 (BytePlus)",
45174
+ provider: "byteplus",
45175
+ providerModel: "dreamina-seedance-2-0-260128",
45176
+ workflows: SEEDANCE_WORKFLOWS,
45177
+ resolutions: ["480p", "720p", "1080p"],
45178
+ workflowOptions: { "reference-to-video": { ...SEEDANCE_REFERENCES, audioNeedsImages: true } },
45179
+ modes: ["quality"],
45180
+ lastFrame: true,
45181
+ estimatedCreditsPerSecond: { "480p": { quality: 7.11 }, "720p": { quality: 15.38 }, "1080p": { quality: 38.06 } },
45182
+ seconds: { min: 4, max: 15 }
45183
+ },
45184
+ {
45185
+ id: "byteplus/seedance-2.0-fast",
45186
+ label: "Seedance 2.0 Fast (BytePlus)",
45187
+ provider: "byteplus",
45188
+ providerModel: "dreamina-seedance-2-0-fast-260128",
45189
+ workflows: SEEDANCE_WORKFLOWS,
45190
+ resolutions: ["480p", "720p"],
45191
+ workflowOptions: { "reference-to-video": { ...SEEDANCE_REFERENCES, audioNeedsImages: true } },
45192
+ modes: ["speed"],
45193
+ lastFrame: true,
45194
+ estimatedCreditsPerSecond: { "480p": { speed: 5.69 }, "720p": { speed: 12.3 } },
45195
+ seconds: { min: 4, max: 15 }
45196
+ },
45197
+ {
45198
+ id: "byteplus/seedance-2.0-mini",
45199
+ label: "Seedance 2.0 Mini (BytePlus)",
45200
+ provider: "byteplus",
45201
+ providerModel: "dreamina-seedance-2-0-mini-260615",
45202
+ workflows: SEEDANCE_WORKFLOWS,
45203
+ resolutions: ["480p", "720p"],
45204
+ workflowOptions: { "reference-to-video": { ...SEEDANCE_REFERENCES, audioNeedsImages: true } },
45205
+ modes: ["cost"],
45206
+ lastFrame: true,
45207
+ estimatedCreditsPerSecond: { "480p": { cost: 3.56 }, "720p": { cost: 7.69 } },
45208
+ seconds: { min: 4, max: 15 }
45148
45209
  }
45149
45210
  ];
45150
45211
  var SPRITE_VIDEO_MODEL_IDS = SPRITE_VIDEO_MODELS.map((m) => m.id);
@@ -45273,7 +45334,7 @@ var Client = class {
45273
45334
  origin;
45274
45335
  async request(path, options = {}) {
45275
45336
  const token3 = options.authenticated !== false ? await sessionToken(this.origin) : void 0;
45276
- const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/[^/]+\/consent)$/.test(path);
45337
+ const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/videos|avatars\/[^/]+\/consent)$/.test(path);
45277
45338
  const idempotency = generation ? { "Idempotency-Key": generationRequestId(path, options.body) } : {};
45278
45339
  const response = await fetch(`${this.origin}${path}`, {
45279
45340
  method: options.method ?? "GET",
@@ -46914,6 +46975,130 @@ async function createAnimation(options) {
46914
46975
  };
46915
46976
  }
46916
46977
 
46978
+ // shared/avatars.ts
46979
+ var AVATAR_OWNERSHIPS = ["public", "private"];
46980
+ var AVATAR_LOOK_TYPES = ["studio_avatar", "digital_twin", "photo_avatar"];
46981
+ var AVATAR_CREATE_TYPES = ["prompt", "photo", "digital_twin"];
46982
+ var AVATAR_ASPECT_RATIOS = ["auto", "16:9", "9:16", "1:1", "4:5", "5:4"];
46983
+ var LOOK_PACK_ASPECT_RATIOS = ["16:9", "9:16"];
46984
+ var AVATAR_LOOK_METHODS = ["look_pack", "template"];
46985
+ var AVATAR_CREDIT_UNITS = { digital_twin: 2130, photo: 1320, prompt: 1320 };
46986
+ var AVATAR_LOOK_CREDIT_UNITS = 1320;
46987
+ var LOOKS_PER_METHOD = { look_pack: 5, template: 2 };
46988
+ var AVATAR_LOOK_TEMPLATES = [
46989
+ { id: "5a8e6be843ee4321a014946bf62aa322", method: "look_pack", name: "Modern Corporate", variant: "female" },
46990
+ { id: "5e659e76755d47fd9160a2d7dedce124", method: "look_pack", name: "Modern Corporate", variant: "male" },
46991
+ { id: "e1650a9029d04ea1a350421c163295fd", method: "look_pack", name: "Stylish Business-Casual", variant: "female" },
46992
+ { id: "a5f8e0c0cb654c1a83c695629b173af5", method: "look_pack", name: "Stylish Business-Casual", variant: "male" },
46993
+ { id: "e7b000e2cb0d44f1a2d2f291a38b395a", method: "look_pack", name: "Scandinavian Minimal", variant: "female" },
46994
+ { id: "9600f78f27bb49d792e1c56ce624b929", method: "look_pack", name: "Scandinavian Minimal", variant: "male" },
46995
+ { id: "d7ad40ec97d4459a92b33398b57caf4e", method: "look_pack", name: "Cool Tech / Slate", variant: "female" },
46996
+ { id: "fbd03fc3603a4e3caff594f5743a1733", method: "look_pack", name: "Cool Tech / Slate", variant: "male" },
46997
+ { id: "14f4070bd5a04eb6b1f82ae087cd0f30", method: "look_pack", name: "Healthcare", variant: "female" },
46998
+ { id: "ce706bfa8556401e8c4401765102a455", method: "look_pack", name: "Healthcare", variant: "male" },
46999
+ { id: "b0cae4fbae384ab9ab9a8e73ba504be8", method: "look_pack", name: "Keynote Power Presenter", variant: "female" },
47000
+ { id: "71e84769162d4f4186563ad9e0fc45b8", method: "look_pack", name: "Keynote Power Presenter", variant: "male" },
47001
+ { id: "935ea0b7cdde4e6eb5a198a3d666f677", method: "look_pack", name: "Fitness", variant: "female" },
47002
+ { id: "daeb8b15c92a4acf9ffb437ba5534458", method: "look_pack", name: "Fitness", variant: "male" },
47003
+ { id: "6f183d726933457c9035fc0aa4e9c0c3", method: "look_pack", name: "Automotive Dealership", variant: "female" },
47004
+ { id: "8595c78b466c4e51802731229c59b6e7", method: "look_pack", name: "Automotive Dealership", variant: "male" },
47005
+ { id: "b30c83d99cec48e09c8f5b07ed8aa38c", method: "template", name: "Business Casual", variant: "female" },
47006
+ { id: "2dc326030e2e4471babcc9eda56131b6", method: "template", name: "Business Casual", variant: "male" },
47007
+ { id: "00c49daa4eb94386a0c12508043bab17", method: "template", name: "Podcast", variant: "female" },
47008
+ { id: "0e52ceafe4d5456fb83c1e39899ff76a", method: "template", name: "Podcast", variant: "male" }
47009
+ ];
47010
+ var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
47011
+ var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
47012
+ var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
47013
+ var name = external_exports.string().trim().min(1).max(80);
47014
+ var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
47015
+ var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
47016
+ var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
47017
+ var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
47018
+ var avatarFile = external_exports.discriminatedUnion("type", [
47019
+ external_exports.object({ type: external_exports.literal("file"), sha }),
47020
+ external_exports.object({ type: external_exports.literal("url"), url: https }),
47021
+ external_exports.object({
47022
+ type: external_exports.literal("base64"),
47023
+ mediaType: external_exports.enum([...AVATAR_PHOTO_TYPES, ...AVATAR_FOOTAGE_TYPES]),
47024
+ data: external_exports.string().max(Math.ceil(AVATAR_INLINE_MAX_BYTES / 3) * 4, "Inline files must be at most 8 MB; use a URL or a project file.").regex(/^[A-Za-z0-9+/]+={0,2}$/, "Send the file as standard base64.")
47025
+ })
47026
+ ]);
47027
+ var subjectConsent = external_exports.literal(true, { error: "Confirm that the person shown agreed to become an avatar (subjectConsent: true)." });
47028
+ var avatarCreateRequest = external_exports.discriminatedUnion("type", [
47029
+ external_exports.object({
47030
+ type: external_exports.literal("prompt"),
47031
+ name,
47032
+ prompt: external_exports.string().trim().min(3).max(1e3),
47033
+ aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
47034
+ lookId: heygenId.optional(),
47035
+ groupId: heygenId.optional(),
47036
+ referenceImages: external_exports.array(avatarFile).max(3).default([])
47037
+ }),
47038
+ external_exports.object({ type: external_exports.literal("photo"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent }),
47039
+ external_exports.object({ type: external_exports.literal("digital_twin"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent })
47040
+ ]).superRefine((p, ctx) => {
47041
+ const files = p.type === "prompt" ? p.referenceImages : [p.file];
47042
+ const footage = p.type === "digital_twin";
47043
+ for (const [index2, file2] of files.entries()) {
47044
+ if (file2.type !== "base64") continue;
47045
+ const allowed = footage ? AVATAR_FOOTAGE_TYPES : AVATAR_PHOTO_TYPES;
47046
+ if (!allowed.includes(file2.mediaType)) ctx.addIssue({ code: "custom", message: footage ? "Send footage as MP4, MOV or WebM." : "Send a PNG, JPEG or WebP image.", path: p.type === "prompt" ? ["referenceImages", index2, "mediaType"] : ["file", "mediaType"] });
47047
+ }
47048
+ });
47049
+ var avatarLooksRequest = external_exports.discriminatedUnion("method", [
47050
+ external_exports.object({ method: external_exports.literal("look_pack"), lookId: heygenId, templateId: heygenId, aspectRatio: external_exports.enum(LOOK_PACK_ASPECT_RATIOS).default("16:9") }),
47051
+ external_exports.object({ method: external_exports.literal("template"), lookId: heygenId, templateId: heygenId })
47052
+ ]).superRefine((p, ctx) => {
47053
+ const known = avatarTemplate(p.templateId);
47054
+ if (known && known.method !== p.method) ctx.addIssue({ code: "custom", message: `This template is applied with method ${known.method}.`, path: ["method"] });
47055
+ });
47056
+ var heygenVoiceId = external_exports.string().trim().regex(/^[A-Za-z0-9_.-]{1,128}$/, "Use a HeyGen voice ID.");
47057
+ var avatarGroupUpdate = external_exports.object({ defaultVoiceId: heygenVoiceId });
47058
+ var avatarLookUpdate = external_exports.object({ name: external_exports.string().trim().min(1).max(255) });
47059
+ var avatarConsentRequest = external_exports.object({ rerouteUrl: https.optional() });
47060
+ var limit = external_exports.coerce.number().int().min(1).max(50).default(20);
47061
+ var token = external_exports.string().max(2048).optional();
47062
+ var avatarListQuery = external_exports.object({ ownership: external_exports.enum(AVATAR_OWNERSHIPS).default("public"), limit, token });
47063
+ var avatarLookListQuery = external_exports.object({ groupId: heygenId.optional(), avatarType: external_exports.enum(AVATAR_LOOK_TYPES).optional(), limit, token });
47064
+ var avatarVoiceListQuery = external_exports.object({ ownership: external_exports.enum(AVATAR_OWNERSHIPS).default("public"), language: external_exports.string().trim().min(1).max(40).optional(), gender: external_exports.enum(["male", "female"]).optional(), limit, token });
47065
+ var avatarCreateEstimate = (p) => AVATAR_CREDIT_UNITS[p.type];
47066
+ var avatarLooksEstimate = (p) => LOOKS_PER_METHOD[p.method] * AVATAR_LOOK_CREDIT_UNITS;
47067
+ var AVATAR_VIDEO_MODEL_IDS = ["heygen/avatar-iv", "heygen/avatar-v", "heygen/avatar-iii"];
47068
+ var DEFAULT_AVATAR_VIDEO_MODEL = "heygen/avatar-iv";
47069
+ var AVATAR_VIDEO_RESOLUTIONS = ["720p", "1080p"];
47070
+ var AVATAR_EXPRESSIVENESS = ["low", "medium", "high"];
47071
+ var AVATAR_SCRIPT_MAX = 1500;
47072
+ var AVATAR_VIDEO_MAX_SECONDS = 120;
47073
+ var avatarVideoRequest = external_exports.object({
47074
+ type: external_exports.literal("video"),
47075
+ name,
47076
+ model: external_exports.enum(AVATAR_VIDEO_MODEL_IDS).default(DEFAULT_AVATAR_VIDEO_MODEL),
47077
+ /** A look of this workspace's avatars or of HeyGen's catalog. */
47078
+ lookId: heygenId,
47079
+ /** What the avatar says. */
47080
+ script: external_exports.string().trim().min(1).max(AVATAR_SCRIPT_MAX).optional(),
47081
+ /** Saved speech or music in this project that the avatar lip-syncs instead of a script. */
47082
+ soundtrack: sha.optional(),
47083
+ /** The voice that speaks the script; without it, the look's own voice. */
47084
+ voiceId: heygenVoiceId.optional(),
47085
+ aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
47086
+ resolution: external_exports.enum(AVATAR_VIDEO_RESOLUTIONS).default("1080p"),
47087
+ /** How the body and hands move: photo avatars on Avatar IV, any look on Avatar V. */
47088
+ motionPrompt: external_exports.string().trim().min(3).max(1e3).optional(),
47089
+ /** How much a photo avatar moves on Avatar IV (low unless set). */
47090
+ expressiveness: external_exports.enum(AVATAR_EXPRESSIVENESS).optional(),
47091
+ /** The clip's length, which the server sets: the audio's, or the script's estimate until the clip is made. */
47092
+ seconds: external_exports.number().positive().max(600).optional()
47093
+ }).superRefine((p, ctx) => {
47094
+ const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47095
+ if (p.script && p.soundtrack) issue2("soundtrack", "Give the avatar a script or a saved audio track, not both.");
47096
+ if (!p.script && !p.soundtrack) issue2("script", "Write what the avatar says, or choose saved speech or music for it to lip-sync.");
47097
+ if (p.soundtrack && p.voiceId) issue2("voiceId", "A saved audio track speaks in its own voice; leave out voiceId.");
47098
+ if (p.model === "heygen/avatar-iii" && p.motionPrompt) issue2("motionPrompt", "Avatar III doesn't take a motion prompt; use Avatar IV or Avatar V.");
47099
+ if (p.model !== "heygen/avatar-iv" && p.expressiveness) issue2("expressiveness", "Only Avatar IV takes expressiveness.");
47100
+ });
47101
+
46917
47102
  // shared/model-assets.ts
46918
47103
  var MODEL_GENERATORS = [
46919
47104
  {
@@ -46983,20 +47168,50 @@ var rigCostUsd = (clips2) => 0.2 + 0.12 * clips2;
46983
47168
  var modelCostUsd = (p) => generationCostUsd(p) + (p.rig ? rigCostUsd(p.animations.length) : 0);
46984
47169
 
46985
47170
  // shared/assets.ts
46986
- var ASSET_TYPES = ["image", "vector", "video", "music", "speech", "sfx", "model"];
47171
+ var ASSET_TYPES = ["image", "vector", "gif", "video", "music", "speech", "sfx", "model"];
46987
47172
  var SPEECH_MODELS = [
46988
- { id: "gemini-3.8-flash-tts", label: "Gemini 3.8 Flash TTS" },
46989
- { id: "gemini-3.8-flash-lite-tts", label: "Gemini 3.8 Flash-Lite TTS" },
46990
- { id: "eleven_v4", label: "ElevenLabs v4" },
46991
- { id: "eleven_v4_turbo", label: "ElevenLabs v4 Turbo" }
47173
+ { id: "gemini-3.8-flash-tts", label: "Gemini 3.8 Flash TTS", provider: "google" },
47174
+ { id: "gemini-3.8-flash-lite-tts", label: "Gemini 3.8 Flash-Lite TTS", provider: "google" },
47175
+ { id: "eleven_v4", label: "ElevenLabs v4", provider: "elevenlabs" },
47176
+ { id: "eleven_v4_turbo", label: "ElevenLabs v4 Turbo", provider: "elevenlabs" },
47177
+ { id: "heygen-voice-1", label: "HeyGen Voice 1", provider: "heygen" },
47178
+ { id: "heygen-voice-1-turbo", label: "HeyGen Voice 1 Turbo", provider: "heygen" }
46992
47179
  ];
46993
47180
  var ELEVENLABS_SPEECH_MODEL_IDS = ["eleven_v4", "eleven_v4_turbo"];
47181
+ var HEYGEN_SPEECH_MODEL_IDS = ["heygen-voice-1", "heygen-voice-1-turbo"];
46994
47182
  var SFX_MODELS = [{ id: "eleven_text_to_sound_v2", label: "ElevenLabs Sound Effects v2" }];
46995
47183
  var DEFAULT_ELEVENLABS_VOICE = "JBFqnCBsd6RMkjVDRZzb";
46996
47184
  var ELEVENLABS_VOICES = [{ id: DEFAULT_ELEVENLABS_VOICE, label: "George" }];
46997
47185
  var isElevenLabsSpeech = (model) => ELEVENLABS_SPEECH_MODEL_IDS.includes(model);
46998
- var MUSIC_MODELS = [{ id: "lyria-3.5", label: "Lyria 3.5" }];
47186
+ var isHeyGenSpeech = (model) => HEYGEN_SPEECH_MODEL_IDS.includes(model);
47187
+ var DEFAULT_HEYGEN_VOICE = "83548de556df48ba8c09c42a57c51d85";
47188
+ var HEYGEN_VOICES = [
47189
+ { id: DEFAULT_HEYGEN_VOICE, label: "Nadine \xB7 Calm & Gentle" },
47190
+ { id: "75300849b571414c8d62693289cb6509", label: "Sabrina \xB7 Upbeat & Lively" },
47191
+ { id: "aa9746e325ff46d482a54fe5bc87890f", label: "Meredith \xB7 Serious & Composed" },
47192
+ { id: "fe333747c7d04a7188d20a8ea2a222d9", label: "Claire \xB7 Rich & Expressive" },
47193
+ { id: "7e48c8d406b04a8089c5eb3cd057cb2e", label: "Jonah \xB7 Clear & Professional" },
47194
+ { id: "826be797681d4b1a92042b54b3c75c64", label: "Gareth \xB7 Conversational & Easygoing" },
47195
+ { id: "9b71011176ac49ee8d0936c36566d665", label: "Hugh \xB7 Firm & Measured" },
47196
+ { id: "7bb4df56d7714d33bb88ed1dde8128f6", label: "Rory \xB7 Upbeat & Lively" }
47197
+ ];
46999
47198
  var SPEECH_VOICES = ["Kore", "Puck", "Charon", "Fenrir", "Aoede", "Leda", "Orus", "Zephyr"];
47199
+ var defaultSpeechVoice = (model) => isElevenLabsSpeech(model) ? DEFAULT_ELEVENLABS_VOICE : isHeyGenSpeech(model) ? DEFAULT_HEYGEN_VOICE : "Kore";
47200
+ var speechVoiceValid = (model, voice) => isElevenLabsSpeech(model) ? /^[a-zA-Z0-9]{20}$/.test(voice) : isHeyGenSpeech(model) ? /^[A-Za-z0-9_]{8,64}$/.test(voice) : SPEECH_VOICES.includes(voice);
47201
+ var MUSIC_MODELS = [
47202
+ { id: "lyria-3.5", label: "Lyria 3.5", provider: "google" },
47203
+ { id: "fal/mureka-9.5-song", label: "Mureka 9.5 Song", provider: "fal" },
47204
+ { id: "fal/mureka-9.5-instrumental", label: "Mureka 9.5 Instrumental", provider: "fal" },
47205
+ { id: "fal/mureka-9.5-soundtrack", label: "Mureka 9.5 Soundtrack", provider: "fal" }
47206
+ ];
47207
+ var MUSIC_MODEL_IDS = ["lyria-3.5", "fal/mureka-9.5-song", "fal/mureka-9.5-instrumental", "fal/mureka-9.5-soundtrack"];
47208
+ var MUREKA_STYLES = ["pop", "rock", "jazz", "r&b", "edm", "ambient", "folk", "latin", "k-pop", "j-pop", "house", "gospel", "lo-fi"];
47209
+ var MUREKA_VOCALS = ["female", "male"];
47210
+ var MAX_SONG_LYRICS = 5e3;
47211
+ var SONG_SECONDS = { min: 120, max: 300 };
47212
+ var SONG_LENGTH = `Choose a length of ${SONG_SECONDS.min}\u2013${SONG_SECONDS.max} whole seconds.`;
47213
+ var SOUNDTRACK_IMAGE_MAX_BYTES = 50 * 1024 * 1024;
47214
+ var SOUNDTRACK_VIDEO_MAX_BYTES = 100 * 1024 * 1024;
47000
47215
  var base = { name: external_exports.string().trim().min(1).max(80), prompt: external_exports.string().trim().min(3).max(4e3) };
47001
47216
  var blob = external_exports.string().regex(/^[a-f0-9]{64}$/);
47002
47217
  var cameraKeyframe = external_exports.object({
@@ -47197,6 +47412,12 @@ function videoChecks(p, ctx) {
47197
47412
  if (options?.referencesOnly) {
47198
47413
  for (const field of ["reference", "endReference", "middleReference"]) if (p[field]) issue2(field, "This video model's reference-to-video takes references only, without a first, last or middle frame.");
47199
47414
  }
47415
+ const kinds = options?.mediaReferences;
47416
+ const files = [["images", "references", references.length], ["videos", "referenceVideos", p.referenceVideos?.length ?? 0], ["audio", "audioReferences", p.audioReferences.length]];
47417
+ if (kinds) {
47418
+ for (const [kind, field, count] of files) if (count && !kinds.includes(kind)) issue2(field, `This video model's reference-to-video takes reference ${kinds.join(" and ")}, not ${kind}.`);
47419
+ }
47420
+ if (options?.audioNeedsImages && p.audioReferences.length && !references.length && !p.referenceVideos?.length) issue2("audioReferences", "This video model needs a reference image beside reference audio.");
47200
47421
  if (references.length + (p.referenceVideos?.length ?? 0) + p.audioReferences.length > MAX_REFERENCE_FILES) issue2("references", `reference-to-video takes at most ${MAX_REFERENCE_FILES} reference files in all.`);
47201
47422
  const frames = !options?.referencesOnly && (p.reference || p.endReference);
47202
47423
  if (!references.length && !p.referenceVideos?.length && !p.audioReferences.length && !frames) issue2("references", `reference-to-video needs reference images, videos or audio${options?.referencesOnly ? "" : ", or a first or last frame"}.`);
@@ -47230,6 +47451,7 @@ var videoDefaults = (p) => {
47230
47451
  return { ...p, seconds: p.seconds ?? defaultSeconds(model, p.workflow), resolution: p.resolution ?? videoResolutions(model, p.workflow)[0], mode: p.mode ?? model.modes[0] };
47231
47452
  };
47232
47453
  var IMAGE_ASPECT_RATIOS = ["1:1", "3:2", "2:3", "16:9", "9:16"];
47454
+ var IMAGE_SIZES = { "1:1": [1024, 1024], "3:2": [1536, 1024], "2:3": [1024, 1536], "16:9": [1536, 864], "9:16": [864, 1536] };
47233
47455
  var MAX_IMAGE_REFERENCES = 4;
47234
47456
  var MAX_MASK_REGIONS = 8;
47235
47457
  var imageReferences = external_exports.array(blob).max(MAX_IMAGE_REFERENCES).refine((shas2) => new Set(shas2).size === shas2.length, "Choose each reference image once.").default([]);
@@ -47247,10 +47469,13 @@ var imageAsset = external_exports.object({
47247
47469
  var imageChecks = (p, ctx) => {
47248
47470
  if (p.mask && !p.references.length) ctx.addIssue({ code: "custom", message: "A mask edits Image 1; add the image to edit as the first reference.", path: ["mask"] });
47249
47471
  };
47250
- var VECTOR_MODELS = [{ id: "fal/ideogram-vector", label: "Ideogram Vector (fal)" }];
47472
+ var VECTOR_MODELS = [{ id: "fal/ideogram-vector", label: "Ideogram Vector (fal)" }, { id: "fal/gpt-image-2.6-vector", label: "GPT Image 2.6 Vector (fal)" }];
47473
+ var VECTOR_MODEL_IDS = ["fal/ideogram-vector", "fal/gpt-image-2.6-vector"];
47474
+ var GPT_VECTOR_MODEL = "fal/gpt-image-2.6-vector";
47251
47475
  var VECTOR_WORKFLOWS = ["text-to-vector", "image-to-vector"];
47252
47476
  var VECTOR_QUALITIES = ["low", "medium", "high"];
47253
47477
  var VECTOR_ASPECT_RATIOS = ["auto", "1:1", "1:4", "1:3", "1:2", "9:16", "10:16", "2:3", "3:4", "4:5", "5:4", "4:3", "3:2", "16:10", "16:9", "2:1", "3:1", "4:1"];
47478
+ var GPT_VECTOR_ASPECT_RATIOS = VECTOR_ASPECT_RATIOS.filter((ratio) => ratio !== "1:4" && ratio !== "4:1");
47254
47479
  var VECTOR_SHAPES = ["detailed", "simple"];
47255
47480
  var MAX_VECTOR_REFERENCES = 5;
47256
47481
  var VECTOR_REFERENCE_MAX_BYTES = 10 * 1024 * 1024;
@@ -47260,7 +47485,7 @@ var vectorAsset = external_exports.object({
47260
47485
  /** Image-to-vector takes no prompt. */
47261
47486
  prompt: external_exports.string().trim().max(4e3).default(""),
47262
47487
  type: external_exports.literal("vector"),
47263
- model: external_exports.literal("fal/ideogram-vector").default("fal/ideogram-vector"),
47488
+ model: external_exports.enum(VECTOR_MODEL_IDS).default("fal/ideogram-vector"),
47264
47489
  workflow: external_exports.enum(VECTOR_WORKFLOWS).default("text-to-vector"),
47265
47490
  /** Text-to-vector; defaults to medium. */
47266
47491
  quality: external_exports.enum(VECTOR_QUALITIES).optional(),
@@ -47273,11 +47498,24 @@ var vectorAsset = external_exports.object({
47273
47498
  /** Image-to-vector; defaults to detailed. */
47274
47499
  shapes: external_exports.enum(VECTOR_SHAPES).optional(),
47275
47500
  /** Text-to-vector. */
47276
- seed: external_exports.number().int().min(0).max(2 ** 32 - 1).optional()
47501
+ seed: external_exports.number().int().min(0).max(2 ** 32 - 1).optional(),
47502
+ /** GPT Image 2.6: draw the art alone, without a background (default false). */
47503
+ transparent: external_exports.boolean().optional()
47277
47504
  });
47278
47505
  function vectorChecks(p, ctx) {
47279
47506
  const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47280
47507
  if (new Set(p.references).size !== p.references.length) issue2("references", "Choose each reference image once.");
47508
+ if (p.model === GPT_VECTOR_MODEL) {
47509
+ if (p.workflow !== "text-to-vector") return issue2("workflow", "GPT Image 2.6 Vector draws from text; trace a saved image with Ideogram Vector (fal/ideogram-vector).");
47510
+ if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
47511
+ if (p.references.length) issue2("references", "GPT Image 2.6 Vector draws from the prompt alone; follow reference images with Ideogram Vector (fal/ideogram-vector).");
47512
+ if (p.source) issue2("source", "Only image-to-vector traces a saved image.");
47513
+ if (p.shapes) issue2("shapes", "Only image-to-vector takes shapes.");
47514
+ if (p.seed !== void 0) issue2("seed", "GPT Image 2.6 Vector doesn't take a seed.");
47515
+ if (p.aspectRatio && !GPT_VECTOR_ASPECT_RATIOS.includes(p.aspectRatio)) issue2("aspectRatio", "GPT Image 2.6 Vector draws between 1:3 and 3:1; choose another aspect ratio.");
47516
+ return;
47517
+ }
47518
+ if (p.transparent !== void 0) issue2("transparent", "Only GPT Image 2.6 Vector (fal/gpt-image-2.6-vector) leaves out the background.");
47281
47519
  if (p.workflow === "text-to-vector") {
47282
47520
  if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
47283
47521
  if (p.source) issue2("source", "Only image-to-vector traces a saved image; use references to guide text-to-vector.");
@@ -47289,7 +47527,93 @@ function vectorChecks(p, ctx) {
47289
47527
  for (const field of ["quality", "aspectRatio", "seed"]) if (p[field] !== void 0) issue2(field, `image-to-vector doesn't take ${field === "aspectRatio" ? "an aspect ratio" : field === "seed" ? "a seed" : "a quality"}; the SVG follows the image.`);
47290
47528
  if (p.references.length) issue2("references", "image-to-vector traces its source alone and takes no references.");
47291
47529
  }
47292
- var vectorDefaults = (p) => p.workflow === "text-to-vector" ? { ...p, quality: p.quality ?? "medium", aspectRatio: p.aspectRatio ?? "auto" } : { ...p, shapes: p.shapes ?? "detailed" };
47530
+ var vectorDefaults = (p) => p.workflow === "text-to-vector" ? { ...p, quality: p.quality ?? "medium", aspectRatio: p.aspectRatio ?? "auto", ...p.model === GPT_VECTOR_MODEL ? { transparent: p.transparent ?? false } : {} } : { ...p, shapes: p.shapes ?? "detailed" };
47531
+ var GIF_MODELS = [
47532
+ { id: "fal/gpt-image-2.6-gif", label: "GPT Image 2.6 Flare GIF (fal)" },
47533
+ { id: "fal/gpt-image-2.6-sunburst-gif", label: "GPT Image 2.6 Sunburst GIF (fal)" }
47534
+ ];
47535
+ var GIF_MODEL_IDS = ["fal/gpt-image-2.6-gif", "fal/gpt-image-2.6-sunburst-gif"];
47536
+ var DEFAULT_GIF_MODEL = "fal/gpt-image-2.6-gif";
47537
+ var GIF_QUALITIES = ["low", "medium", "high"];
47538
+ var GIF_FRAMES = { min: 2, max: 16, default: 9 };
47539
+ var GIF_FRAME_MS = { min: 20, max: 2e3, default: 200 };
47540
+ var MAX_GIF_REFERENCES = 4;
47541
+ var GIF_PIXEL_BUDGET = 3840 * 2160;
47542
+ function gifFrameSize(aspectRatio, frames) {
47543
+ const [w, h] = IMAGE_SIZES[aspectRatio];
47544
+ const scale = Math.min(1, Math.sqrt(GIF_PIXEL_BUDGET / Math.max(1, frames) / (w * h)));
47545
+ const width = Math.max(16, Math.floor(w * scale / 16) * 16);
47546
+ return [width, Math.max(16, Math.floor(width * h / w / 16) * 16)];
47547
+ }
47548
+ var gifAsset = external_exports.object({
47549
+ ...base,
47550
+ type: external_exports.literal("gif"),
47551
+ model: external_exports.enum(GIF_MODEL_IDS).default(DEFAULT_GIF_MODEL),
47552
+ aspectRatio: external_exports.enum(IMAGE_ASPECT_RATIOS).default("1:1"),
47553
+ frames: external_exports.number().int().min(GIF_FRAMES.min).max(GIF_FRAMES.max).default(GIF_FRAMES.default),
47554
+ /** How long each frame shows, in milliseconds; rounded to 10. */
47555
+ frameMs: external_exports.number().min(GIF_FRAME_MS.min).max(GIF_FRAME_MS.max).default(GIF_FRAME_MS.default),
47556
+ quality: external_exports.enum(GIF_QUALITIES).default("medium"),
47557
+ /** Frames without a background, for a sprite over a scene. */
47558
+ transparent: external_exports.boolean().default(false),
47559
+ /** Saved images or sprite art to animate or follow, named Image 1, Image 2… in the prompt. */
47560
+ references: external_exports.array(blob).max(MAX_GIF_REFERENCES).refine((shas2) => new Set(shas2).size === shas2.length, "Choose each reference image once.").default([])
47561
+ });
47562
+ var gifDefaults = (p) => ({ ...p, frameMs: Math.round(p.frameMs / 10) * 10 });
47563
+ var musicAsset = external_exports.object({
47564
+ name: base.name,
47565
+ /** A soundtrack may leave it out, and so may a song sung from lyrics, where it sets the style. */
47566
+ prompt: external_exports.string().trim().max(4e3).default(""),
47567
+ type: external_exports.literal("music"),
47568
+ model: external_exports.enum(MUSIC_MODEL_IDS).default("lyria-3.5"),
47569
+ /** Mureka Song: the words to sing. */
47570
+ lyrics: external_exports.string().trim().min(1).max(MAX_SONG_LYRICS).optional(),
47571
+ /** Mureka Song written from the prompt: its genres. */
47572
+ styles: external_exports.array(external_exports.enum(MUREKA_STYLES)).min(1).max(MUREKA_STYLES.length).optional(),
47573
+ /** Mureka Song sung from lyrics: who sings. */
47574
+ vocals: external_exports.enum(MUREKA_VOCALS).optional(),
47575
+ /** Mureka Song: about how long, in whole seconds; the model chooses when it's left out. */
47576
+ seconds: external_exports.number().int(SONG_LENGTH).min(SONG_SECONDS.min, SONG_LENGTH).max(SONG_SECONDS.max, SONG_LENGTH).optional(),
47577
+ /** Mureka Soundtrack: the saved image or video to score. */
47578
+ source: blob.optional()
47579
+ });
47580
+ var MUSIC_ONLY = {
47581
+ lyrics: ["fal/mureka-9.5-song", "Only Mureka 9.5 Song sings lyrics."],
47582
+ styles: ["fal/mureka-9.5-song", "Only Mureka 9.5 Song takes styles; describe the genre in the prompt."],
47583
+ vocals: ["fal/mureka-9.5-song", "Only Mureka 9.5 Song takes vocals."],
47584
+ seconds: ["fal/mureka-9.5-song", "Only Mureka 9.5 Song takes a length; the other music models choose their own."],
47585
+ source: ["fal/mureka-9.5-soundtrack", "Only Mureka 9.5 Soundtrack scores a saved image or video."]
47586
+ };
47587
+ function musicChecks(p, ctx) {
47588
+ const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47589
+ for (const [field, [model, message]] of Object.entries(MUSIC_ONLY)) if (p[field] !== void 0 && p.model !== model) issue2(field, message);
47590
+ const most = (max2) => {
47591
+ if (p.prompt.length > max2) issue2("prompt", `Describe the music in at most ${max2.toLocaleString("en-US")} characters.`);
47592
+ };
47593
+ if (p.model === "fal/mureka-9.5-song") {
47594
+ if (p.lyrics) {
47595
+ most(1024);
47596
+ if (p.styles) issue2("styles", "A song sung from lyrics takes its style from the prompt; leave out styles.");
47597
+ } else {
47598
+ if (p.prompt.length < 3) issue2("prompt", "Describe the song in at least 3 characters, or give lyrics to sing.");
47599
+ most(2e3);
47600
+ if (p.vocals) issue2("vocals", "Vocals choose who sings lyrics; describe the singer in the prompt instead.");
47601
+ }
47602
+ if (p.styles && new Set(p.styles).size !== p.styles.length) issue2("styles", "Choose each style once.");
47603
+ return;
47604
+ }
47605
+ if (p.model === "fal/mureka-9.5-soundtrack") {
47606
+ if (!p.source) issue2("source", "Choose a saved image or video to score.");
47607
+ return most(1024);
47608
+ }
47609
+ if (p.prompt.length < 3) issue2("prompt", "Describe the music in at least 3 characters.");
47610
+ if (p.model === "fal/mureka-9.5-instrumental") most(1024);
47611
+ }
47612
+ function musicCostUsd(p) {
47613
+ if (p.model === "lyria-3.5") return 0.08;
47614
+ if (p.model === "fal/mureka-9.5-song") return p.lyrics ? 0.15 : 0.5;
47615
+ return p.model === "fal/mureka-9.5-instrumental" ? 0.15 : 0.11;
47616
+ }
47293
47617
  var animationClip = external_exports.union([external_exports.enum(MODEL_ANIMATION_IDS), external_exports.number().int().min(0).max(MAX_MESHY_ACTION)]);
47294
47618
  var modelAsset = external_exports.object({
47295
47619
  name: base.name,
@@ -47346,24 +47670,31 @@ var modelDefaults = (p) => ({ ...p, animations: p.rig && !p.animations.length ?
47346
47670
  var assetRequest = external_exports.discriminatedUnion("type", [
47347
47671
  imageAsset.superRefine(imageChecks),
47348
47672
  videoAsset.superRefine(videoChecks).transform(videoDefaults),
47349
- external_exports.object({ ...base, type: external_exports.literal("music"), model: external_exports.literal("lyria-3.5").default("lyria-3.5") }),
47350
- external_exports.object({ ...base, type: external_exports.literal("speech"), model: external_exports.enum(["gemini-3.8-flash-tts", "gemini-3.8-flash-lite-tts", ...ELEVENLABS_SPEECH_MODEL_IDS]).default("gemini-3.8-flash-lite-tts"), voice: external_exports.string().trim().max(80).default(""), delivery: external_exports.string().trim().max(600).default("") }).superRefine((p, ctx) => {
47351
- if (p.voice && (isElevenLabsSpeech(p.model) ? !/^[a-zA-Z0-9]{20}$/.test(p.voice) : !SPEECH_VOICES.includes(p.voice))) ctx.addIssue({ code: "custom", message: "Choose a voice supported by the speech provider.", path: ["voice"] });
47673
+ musicAsset.superRefine(musicChecks),
47674
+ external_exports.object({ ...base, type: external_exports.literal("speech"), model: external_exports.enum(["gemini-3.8-flash-tts", "gemini-3.8-flash-lite-tts", ...ELEVENLABS_SPEECH_MODEL_IDS, ...HEYGEN_SPEECH_MODEL_IDS]).default("gemini-3.8-flash-lite-tts"), voice: external_exports.string().trim().max(80).default(""), delivery: external_exports.string().trim().max(600).default("") }).superRefine((p, ctx) => {
47675
+ if (p.voice && !speechVoiceValid(p.model, p.voice)) ctx.addIssue({ code: "custom", message: "Choose a voice supported by the speech provider.", path: ["voice"] });
47352
47676
  if (isElevenLabsSpeech(p.model) && p.delivery) ctx.addIssue({ code: "custom", message: "ElevenLabs uses audio tags in the transcript; separate delivery instructions are only supported by Gemini.", path: ["delivery"] });
47353
- }).transform((p) => ({ ...p, voice: p.voice || (isElevenLabsSpeech(p.model) ? DEFAULT_ELEVENLABS_VOICE : "Kore") })),
47677
+ if (isHeyGenSpeech(p.model) && p.delivery) ctx.addIssue({ code: "custom", message: "HeyGen Voice speaks the transcript as written; separate delivery instructions are only supported by Gemini.", path: ["delivery"] });
47678
+ }).transform((p) => ({ ...p, voice: p.voice || defaultSpeechVoice(p.model) })),
47354
47679
  external_exports.object({ ...base, type: external_exports.literal("sfx"), model: external_exports.literal("eleven_text_to_sound_v2").default("eleven_text_to_sound_v2"), seconds: external_exports.number().min(0.5).max(30).default(5), loop: external_exports.boolean().default(false), promptInfluence: external_exports.number().min(0).max(1).default(0.3) }),
47355
47680
  vectorAsset.superRefine(vectorChecks).transform(vectorDefaults),
47356
- modelAsset.superRefine(modelChecks).transform(modelDefaults)
47681
+ modelAsset.superRefine(modelChecks).transform(modelDefaults),
47682
+ gifAsset.transform(gifDefaults)
47357
47683
  ]);
47358
47684
  var assetGenerationRequest = external_exports.discriminatedUnion("type", [imageAsset.omit({ model: true }).superRefine(imageChecks), ...assetRequest.options.slice(1)]);
47359
47685
  var LIBRARY_AUDIO_MODEL = "library-audio";
47360
47686
  var libraryAudio = external_exports.object({ name: base.name, prompt: external_exports.string().trim().max(4e3).default(""), type: external_exports.enum(["music", "sfx"]), model: external_exports.literal(LIBRARY_AUDIO_MODEL), loop: external_exports.boolean().default(false) });
47361
47687
  var UPLOADED_MODEL = "uploaded";
47362
47688
  var uploadedMedia = external_exports.object({ name: base.name, prompt: external_exports.string().trim().max(4e3).default(""), type: external_exports.enum(["image", "video"]), model: external_exports.literal(UPLOADED_MODEL) });
47363
- var savedAssetOptions = external_exports.union([assetRequest, libraryAudio, uploadedMedia]);
47689
+ var savedAssetOptions = external_exports.union([assetRequest, avatarVideoRequest, libraryAudio, uploadedMedia]);
47364
47690
  var speechCharacters = (text2) => Array.from(text2).length;
47365
47691
  var imageEstimate = (p) => Math.ceil((130 + 40 * p.references.length) * spriteImageModel(p.model).relCost);
47366
- var vectorEstimate = (p) => p.workflow === "image-to-vector" ? 100 : p.quality === "low" ? 130 : p.quality === "high" ? p.references.length ? 320 : 200 : 160;
47692
+ var vectorEstimate = (p) => p.model === GPT_VECTOR_MODEL ? p.quality === "low" ? 80 : p.quality === "high" ? 220 : 120 : p.workflow === "image-to-vector" ? 100 : p.quality === "low" ? 130 : p.quality === "high" ? p.references.length ? 320 : 200 : 160;
47693
+ var GIF_UNITS_PER_MEGAPIXEL = { low: 13, medium: 39, high: 130 };
47694
+ function gifEstimate(p) {
47695
+ const [w, h] = gifFrameSize(p.aspectRatio, p.frames);
47696
+ return Math.ceil(p.frames * (w * h) / (1024 * 1024) * GIF_UNITS_PER_MEGAPIXEL[p.quality] + 12 * p.references.length);
47697
+ }
47367
47698
  function videoExtras(p, facts = {}) {
47368
47699
  const w = p.workflow;
47369
47700
  if (spriteVideoModel(p.model).provider === "heygen") {
@@ -47383,11 +47714,13 @@ var modelEstimate = (p) => Math.max(1, Math.ceil(modelCostUsd(p) / 1e-3));
47383
47714
  function assetEstimate(p, now = Date.now()) {
47384
47715
  if (p.type === "image") return imageEstimate(p);
47385
47716
  if (p.type === "vector") return vectorEstimate(p);
47717
+ if (p.type === "gif") return gifEstimate(p);
47386
47718
  if (p.type === "model") return modelEstimate(p);
47387
47719
  if (p.type === "video") return spriteVideoEstimate(spriteVideoModel(p.model), p, videoExtras(p));
47388
- if (p.type === "music") return 80;
47720
+ if (p.type === "music") return Math.ceil(musicCostUsd(p) / 1e-3);
47389
47721
  const units = (credits) => Math.max(1, Math.ceil(credits * UNITS_PER_CREDIT));
47390
47722
  if (p.type === "sfx") return units(Math.max(0, p.seconds) * 0.2);
47723
+ if (isHeyGenSpeech(p.model)) return units(speechCharacters(p.prompt) * (p.model === "heygen-voice-1-turbo" ? 0.4 : 1.5) / 1e3);
47391
47724
  if (isElevenLabsSpeech(p.model)) {
47392
47725
  const promotion2 = now < Date.UTC(2026, 9, 13);
47393
47726
  const perThousandCharacters = p.model === "eleven_v4_turbo" ? promotion2 ? 1.1 : 4 : promotion2 ? 2.2 : 8;
@@ -47411,7 +47744,7 @@ var VIDEO_PART_BYTES = 8 * 1024 * 1024;
47411
47744
  var VIDEO_MAX_BYTES = { html5: 64 * 1024 * 1024, mp4: 512 * 1024 * 1024 };
47412
47745
  var VIDEO_LIBRARY_BYTES = { user: 2 * 1024 ** 3, team: 10 * 1024 ** 3 };
47413
47746
  var VIDEO_VISIBILITIES = ["private", "public"];
47414
- var VIDEO_SHARING_AGREEMENT_VERSION = "2026-10-15-draft-2";
47747
+ var VIDEO_SHARING_AGREEMENT_VERSION = "2026-11-02";
47415
47748
  var videoAgreementAcceptance = external_exports.object({
47416
47749
  version: external_exports.literal(VIDEO_SHARING_AGREEMENT_VERSION),
47417
47750
  language: external_exports.enum(["en", "tr"])
@@ -48067,7 +48400,7 @@ ${entries.join(",\n")}
48067
48400
  }}
48068
48401
  `;
48069
48402
  }
48070
- async function token(context, access4) {
48403
+ async function token2(context, access4) {
48071
48404
  const result = await context.client.request(`/api/v1/cloud/${context.link.projectId}/token`, { method: "POST", body: { access: access4 } });
48072
48405
  const remote = new URL(result.remote);
48073
48406
  if (remote.protocol !== "https:" && !(remote.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(remote.hostname))) throw new Error("The server returned an unsafe cloud remote.");
@@ -48234,7 +48567,7 @@ async function cloudStatus(options) {
48234
48567
  await saveState(context);
48235
48568
  let cloud = null;
48236
48569
  if (!options.offline) {
48237
- const latest = await fetchLatest(context, await token(context, "read"));
48570
+ const latest = await fetchLatest(context, await token2(context, "read"));
48238
48571
  cloud = { latest, newer: Boolean(latest && latest !== context.state.base) };
48239
48572
  }
48240
48573
  return {
@@ -48252,7 +48585,7 @@ async function cloudStatus(options) {
48252
48585
  async function pushCloud(options) {
48253
48586
  const context = await openContext(options.dir, options);
48254
48587
  return withLock(context.root, async () => {
48255
- const remote = await token(context, "write");
48588
+ const remote = await token2(context, "write");
48256
48589
  const latest = await fetchLatest(context, remote);
48257
48590
  if (latest && latest !== context.state.base) throw new Error(`The project's animations have a newer version (${short(latest)}). Run animaguru cloud pull, then push again.`);
48258
48591
  const { files: scanned, skipped, tooLarge } = await scanFiles(context.root);
@@ -48310,7 +48643,7 @@ function defaultMessage(diff) {
48310
48643
  async function pullCloud(options) {
48311
48644
  const context = await openContext(options.dir, options);
48312
48645
  return withLock(context.root, async () => {
48313
- const latest = await fetchLatest(context, await token(context, "read"));
48646
+ const latest = await fetchLatest(context, await token2(context, "read"));
48314
48647
  if (!latest || latest === context.state.base) return { updated: false, version: context.state.base, message: "Already up to date." };
48315
48648
  const { files: scanned, tooLarge } = await scanFiles(context.root);
48316
48649
  const local = await describeFiles(context, scanned);
@@ -48361,7 +48694,7 @@ async function restoreCloud(options) {
48361
48694
  const context = await openContext(options.dir, options);
48362
48695
  return withLock(context.root, async () => {
48363
48696
  if (!/^[a-f0-9]{4,40}$/.test(options.version)) throw new Error("Pass a version ID from animaguru cloud versions.");
48364
- await fetchLatest(context, await token(context, "read"));
48697
+ await fetchLatest(context, await token2(context, "read"));
48365
48698
  let version4;
48366
48699
  try {
48367
48700
  version4 = await import_isomorphic_git.default.expandOid({ fs, gitdir: context.gitdir, oid: options.version });
@@ -48652,18 +48985,20 @@ var extras = {
48652
48985
  "style",
48653
48986
  "damage"
48654
48987
  ],
48655
- music: [],
48988
+ music: ["lyrics", "styles", "vocals", "seconds", "source"],
48656
48989
  speech: ["voice", "delivery"],
48657
48990
  sfx: ["seconds", "loop", "prompt-influence"],
48658
48991
  model: ["reference", "texture", "pbr", "faces", "rig", "animations", "height", "splats", "photo", "seed"],
48659
- vector: ["workflow", "references", "source", "aspect-ratio", "quality", "shapes", "seed"]
48992
+ vector: ["workflow", "references", "source", "aspect-ratio", "quality", "shapes", "seed", "transparent"],
48993
+ gif: ["aspect-ratio", "frames", "frame-ms", "quality", "transparent", "references"]
48660
48994
  };
48995
+ var typeName = (type) => type === "gif" ? "GIF" : type;
48661
48996
  var list2 = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
48662
48997
  function accepted(type) {
48663
- if (type === "music") return "Music accepts only --name, --prompt and --model; the model chooses the length, so fit it during the edit.";
48664
48998
  const options = extras[type];
48665
48999
  if (!options) return "";
48666
- return `${type[0].toUpperCase()}${type.slice(1)} assets accept ${list2(["--name", "--prompt", "--model", ...options.map((option) => `--${option}`)])}.`;
49000
+ const name2 = typeName(type);
49001
+ return `${name2[0].toUpperCase()}${name2.slice(1)} assets accept ${list2(["--name", "--prompt", "--model", ...options.map((option) => `--${option}`)])}.`;
48667
49002
  }
48668
49003
  function parseAsset(input2) {
48669
49004
  const value = input2 && typeof input2 === "object" && "type" in input2 && input2.type === "image" ? { ...input2, model: void 0 } : input2;
@@ -48693,9 +49028,10 @@ function assetFromFlags(flags) {
48693
49028
  for (const name2 of Object.values(extras).flat()) {
48694
49029
  const value = flags[name2];
48695
49030
  const given2 = value !== void 0 && (value !== false || (name2 === "texture" || name2 === "pbr") && flags.type !== "model");
48696
- if (given2 && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${flags.type} assets. ${accepted(flags.type)}`.trim());
49031
+ if (given2 && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${typeName(flags.type)} assets. ${accepted(flags.type)}`.trim());
48697
49032
  }
48698
- if (flags.type !== "video" && flags.type !== "vector" && !(flags.type === "model" && (flags.reference || isSplatGenerator(flags.model ?? ""))) && flags.prompt === void 0) throw new Error(`--prompt is required for ${flags.type} assets${flags.type === "model" ? " unless --reference names an image to build from" : ""}.`);
49033
+ const promptOptional = flags.type === "video" || flags.type === "vector" || flags.type === "model" && (flags.reference || isSplatGenerator(flags.model ?? "")) || flags.type === "music" && (flags.model === "fal/mureka-9.5-soundtrack" || flags.lyrics !== void 0);
49034
+ if (!promptOptional && flags.prompt === void 0) throw new Error(`--prompt is required for ${typeName(flags.type)} assets${flags.type === "model" ? " unless --reference names an image to build from" : flags.type === "music" ? " unless Mureka 9.5 Song sings --lyrics or Mureka 9.5 Soundtrack scores --source" : ""}.`);
48699
49035
  const base2 = { type: flags.type, name: flags.name, prompt: flags.prompt, model: flags.model };
48700
49036
  if (flags.type === "image") return parseAsset({ ...base2, aspectRatio: flags["aspect-ratio"], transparent: flags.transparent, references: shas(flags.references), mask: regions(flags.mask) });
48701
49037
  if (flags.type === "video") {
@@ -48733,8 +49069,10 @@ function assetFromFlags(flags) {
48733
49069
  });
48734
49070
  }
48735
49071
  if (flags.type === "sfx") return parseAsset({ ...base2, seconds: flags.seconds, loop: flags.loop, promptInfluence: flags["prompt-influence"] });
49072
+ if (flags.type === "music") return parseAsset({ ...base2, lyrics: flags.lyrics, styles: shas(flags.styles), vocals: flags.vocals, seconds: flags.seconds, source: flags.source });
48736
49073
  if (flags.type === "model") return parseAsset({ ...base2, reference: flags.reference, texture: flags.texture, pbr: flags.pbr, faces: flags.faces, rig: flags.rig, animations: clips(flags.animations), height: flags.height, splats: flags.splats, photo: flags.photo || void 0, seed: flags.seed });
48737
- if (flags.type === "vector") return parseAsset({ ...base2, workflow: flags.workflow, quality: flags.quality, aspectRatio: flags["aspect-ratio"], references: shas(flags.references), source: flags.source, shapes: flags.shapes, seed: flags.seed });
49074
+ if (flags.type === "vector") return parseAsset({ ...base2, workflow: flags.workflow, quality: flags.quality, aspectRatio: flags["aspect-ratio"], references: shas(flags.references), source: flags.source, shapes: flags.shapes, seed: flags.seed, transparent: flags.transparent || void 0 });
49075
+ if (flags.type === "gif") return parseAsset({ ...base2, aspectRatio: flags["aspect-ratio"], frames: flags.frames, frameMs: flags["frame-ms"], quality: flags.quality, transparent: flags.transparent, references: shas(flags.references) });
48738
49076
  return parseAsset({ ...base2, ...flags.type === "speech" ? { voice: flags.voice, delivery: flags.delivery } : {} });
48739
49077
  }
48740
49078
  async function assetFromFile(file2, model) {
@@ -48830,6 +49168,7 @@ var MIME_EXTENSIONS = {
48830
49168
  "image/png": "png",
48831
49169
  "image/jpeg": "jpg",
48832
49170
  "image/webp": "webp",
49171
+ "image/gif": "gif",
48833
49172
  "image/svg+xml": "svg",
48834
49173
  "video/mp4": "mp4",
48835
49174
  "video/webm": "webm",
@@ -48949,96 +49288,6 @@ async function submitAssetBatch(client, project, requests, options, signal) {
48949
49288
  import { readFile as readFile11, stat as stat6 } from "node:fs/promises";
48950
49289
  import { extname } from "node:path";
48951
49290
  import { setTimeout as delay4 } from "node:timers/promises";
48952
-
48953
- // shared/avatars.ts
48954
- var AVATAR_OWNERSHIPS = ["public", "private"];
48955
- var AVATAR_LOOK_TYPES = ["studio_avatar", "digital_twin", "photo_avatar"];
48956
- var AVATAR_CREATE_TYPES = ["prompt", "photo", "digital_twin"];
48957
- var AVATAR_ASPECT_RATIOS = ["auto", "16:9", "9:16", "1:1", "4:5", "5:4"];
48958
- var LOOK_PACK_ASPECT_RATIOS = ["16:9", "9:16"];
48959
- var AVATAR_LOOK_METHODS = ["look_pack", "template"];
48960
- var AVATAR_CREDIT_UNITS = { digital_twin: 2130, photo: 1320, prompt: 1320 };
48961
- var AVATAR_LOOK_CREDIT_UNITS = 1320;
48962
- var LOOKS_PER_METHOD = { look_pack: 5, template: 2 };
48963
- var AVATAR_LOOK_TEMPLATES = [
48964
- { id: "5a8e6be843ee4321a014946bf62aa322", method: "look_pack", name: "Modern Corporate", variant: "female" },
48965
- { id: "5e659e76755d47fd9160a2d7dedce124", method: "look_pack", name: "Modern Corporate", variant: "male" },
48966
- { id: "e1650a9029d04ea1a350421c163295fd", method: "look_pack", name: "Stylish Business-Casual", variant: "female" },
48967
- { id: "a5f8e0c0cb654c1a83c695629b173af5", method: "look_pack", name: "Stylish Business-Casual", variant: "male" },
48968
- { id: "e7b000e2cb0d44f1a2d2f291a38b395a", method: "look_pack", name: "Scandinavian Minimal", variant: "female" },
48969
- { id: "9600f78f27bb49d792e1c56ce624b929", method: "look_pack", name: "Scandinavian Minimal", variant: "male" },
48970
- { id: "d7ad40ec97d4459a92b33398b57caf4e", method: "look_pack", name: "Cool Tech / Slate", variant: "female" },
48971
- { id: "fbd03fc3603a4e3caff594f5743a1733", method: "look_pack", name: "Cool Tech / Slate", variant: "male" },
48972
- { id: "14f4070bd5a04eb6b1f82ae087cd0f30", method: "look_pack", name: "Healthcare", variant: "female" },
48973
- { id: "ce706bfa8556401e8c4401765102a455", method: "look_pack", name: "Healthcare", variant: "male" },
48974
- { id: "b0cae4fbae384ab9ab9a8e73ba504be8", method: "look_pack", name: "Keynote Power Presenter", variant: "female" },
48975
- { id: "71e84769162d4f4186563ad9e0fc45b8", method: "look_pack", name: "Keynote Power Presenter", variant: "male" },
48976
- { id: "935ea0b7cdde4e6eb5a198a3d666f677", method: "look_pack", name: "Fitness", variant: "female" },
48977
- { id: "daeb8b15c92a4acf9ffb437ba5534458", method: "look_pack", name: "Fitness", variant: "male" },
48978
- { id: "6f183d726933457c9035fc0aa4e9c0c3", method: "look_pack", name: "Automotive Dealership", variant: "female" },
48979
- { id: "8595c78b466c4e51802731229c59b6e7", method: "look_pack", name: "Automotive Dealership", variant: "male" },
48980
- { id: "b30c83d99cec48e09c8f5b07ed8aa38c", method: "template", name: "Business Casual", variant: "female" },
48981
- { id: "2dc326030e2e4471babcc9eda56131b6", method: "template", name: "Business Casual", variant: "male" },
48982
- { id: "00c49daa4eb94386a0c12508043bab17", method: "template", name: "Podcast", variant: "female" },
48983
- { id: "0e52ceafe4d5456fb83c1e39899ff76a", method: "template", name: "Podcast", variant: "male" }
48984
- ];
48985
- var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
48986
- var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
48987
- var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
48988
- var name = external_exports.string().trim().min(1).max(80);
48989
- var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
48990
- var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
48991
- var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
48992
- var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
48993
- var avatarFile = external_exports.discriminatedUnion("type", [
48994
- external_exports.object({ type: external_exports.literal("file"), sha }),
48995
- external_exports.object({ type: external_exports.literal("url"), url: https }),
48996
- external_exports.object({
48997
- type: external_exports.literal("base64"),
48998
- mediaType: external_exports.enum([...AVATAR_PHOTO_TYPES, ...AVATAR_FOOTAGE_TYPES]),
48999
- data: external_exports.string().max(Math.ceil(AVATAR_INLINE_MAX_BYTES / 3) * 4, "Inline files must be at most 8 MB; use a URL or a project file.").regex(/^[A-Za-z0-9+/]+={0,2}$/, "Send the file as standard base64.")
49000
- })
49001
- ]);
49002
- var subjectConsent = external_exports.literal(true, { error: "Confirm that the person shown agreed to become an avatar (subjectConsent: true)." });
49003
- var avatarCreateRequest = external_exports.discriminatedUnion("type", [
49004
- external_exports.object({
49005
- type: external_exports.literal("prompt"),
49006
- name,
49007
- prompt: external_exports.string().trim().min(3).max(1e3),
49008
- aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
49009
- lookId: heygenId.optional(),
49010
- groupId: heygenId.optional(),
49011
- referenceImages: external_exports.array(avatarFile).max(3).default([])
49012
- }),
49013
- external_exports.object({ type: external_exports.literal("photo"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent }),
49014
- external_exports.object({ type: external_exports.literal("digital_twin"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent })
49015
- ]).superRefine((p, ctx) => {
49016
- const files = p.type === "prompt" ? p.referenceImages : [p.file];
49017
- const footage = p.type === "digital_twin";
49018
- for (const [index2, file2] of files.entries()) {
49019
- if (file2.type !== "base64") continue;
49020
- const allowed = footage ? AVATAR_FOOTAGE_TYPES : AVATAR_PHOTO_TYPES;
49021
- if (!allowed.includes(file2.mediaType)) ctx.addIssue({ code: "custom", message: footage ? "Send footage as MP4, MOV or WebM." : "Send a PNG, JPEG or WebP image.", path: p.type === "prompt" ? ["referenceImages", index2, "mediaType"] : ["file", "mediaType"] });
49022
- }
49023
- });
49024
- var avatarLooksRequest = external_exports.discriminatedUnion("method", [
49025
- external_exports.object({ method: external_exports.literal("look_pack"), lookId: heygenId, templateId: heygenId, aspectRatio: external_exports.enum(LOOK_PACK_ASPECT_RATIOS).default("16:9") }),
49026
- external_exports.object({ method: external_exports.literal("template"), lookId: heygenId, templateId: heygenId })
49027
- ]).superRefine((p, ctx) => {
49028
- const known = avatarTemplate(p.templateId);
49029
- if (known && known.method !== p.method) ctx.addIssue({ code: "custom", message: `This template is applied with method ${known.method}.`, path: ["method"] });
49030
- });
49031
- var avatarGroupUpdate = external_exports.object({ defaultVoiceId: external_exports.string().trim().min(1).max(255) });
49032
- var avatarLookUpdate = external_exports.object({ name: external_exports.string().trim().min(1).max(255) });
49033
- var avatarConsentRequest = external_exports.object({ rerouteUrl: https.optional() });
49034
- var limit = external_exports.coerce.number().int().min(1).max(50).default(20);
49035
- var token2 = external_exports.string().max(2048).optional();
49036
- var avatarListQuery = external_exports.object({ ownership: external_exports.enum(AVATAR_OWNERSHIPS).default("public"), limit, token: token2 });
49037
- var avatarLookListQuery = external_exports.object({ groupId: heygenId.optional(), avatarType: external_exports.enum(AVATAR_LOOK_TYPES).optional(), limit, token: token2 });
49038
- var avatarCreateEstimate = (p) => AVATAR_CREDIT_UNITS[p.type];
49039
- var avatarLooksEstimate = (p) => LOOKS_PER_METHOD[p.method] * AVATAR_LOOK_CREDIT_UNITS;
49040
-
49041
- // cli/src/avatars.ts
49042
49291
  var MEDIA_TYPES = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".mp4": "video/mp4", ".mov": "video/quicktime", ".webm": "video/webm" };
49043
49292
  var avatarsPath = (project) => `/api/v1/sprites/${encodeURIComponent(project)}/avatars`;
49044
49293
  function parse3(schema, input2) {
@@ -49073,6 +49322,24 @@ function looksFromFlags(flags) {
49073
49322
  if (flags["aspect-ratio"] && method !== "look_pack") throw new Error("--aspect-ratio applies only to Look Packs.");
49074
49323
  return parse3(avatarLooksRequest, { method, lookId: flags.look, templateId: flags.template, aspectRatio: flags["aspect-ratio"] });
49075
49324
  }
49325
+ async function avatarVideoFromFlags(flags) {
49326
+ if (flags.script && flags["script-file"]) throw new Error("Pass --script or --script-file, not both.");
49327
+ const script = flags["script-file"] ? (await readFile11(flags["script-file"], "utf8")).trim() : flags.script;
49328
+ if (flags.audio && !/^[a-f0-9]{64}$/.test(flags.audio)) throw new Error("--audio takes the file SHA of saved speech or music from assets list.");
49329
+ return parse3(avatarVideoRequest, {
49330
+ type: "video",
49331
+ name: flags.name,
49332
+ lookId: flags.look,
49333
+ model: flags.model,
49334
+ script,
49335
+ soundtrack: flags.audio,
49336
+ voiceId: flags.voice,
49337
+ aspectRatio: flags["aspect-ratio"],
49338
+ resolution: flags.resolution,
49339
+ motionPrompt: flags["motion-prompt"],
49340
+ expressiveness: flags.expressiveness
49341
+ });
49342
+ }
49076
49343
  function authorize(options) {
49077
49344
  if (!options.yes) throw new Error("Pass --yes to authorize spending credits, or --dry-run to validate and estimate without creating anything.");
49078
49345
  }
@@ -49086,6 +49353,11 @@ async function submitLooks(client, project, body, options) {
49086
49353
  authorize(options);
49087
49354
  return client.request(`${avatarsPath(project)}/looks`, { method: "POST", body, timeout: 18e4 });
49088
49355
  }
49356
+ async function submitAvatarVideo(client, project, body, options) {
49357
+ if (options["dry-run"]) return client.request(`${avatarsPath(project)}/videos/estimate`, { method: "POST", body });
49358
+ authorize(options);
49359
+ return client.request(`${avatarsPath(project)}/videos`, { method: "POST", body });
49360
+ }
49089
49361
  async function waitForLook(client, project, lookId, timeout, signal, interval = 1e4) {
49090
49362
  if (!Number.isFinite(timeout) || timeout <= 0) throw new Error("Timeout must be positive.");
49091
49363
  const deadline = Date.now() + timeout * 1e3;
@@ -50829,7 +51101,7 @@ async function curl(options, io) {
50829
51101
  }
50830
51102
 
50831
51103
  // cli/src/commands.ts
50832
- var assetModelIds = [...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id), ...VECTOR_MODELS.map((m) => m.id), ...MODEL_GENERATOR_IDS];
51104
+ var assetModelIds = [...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id), ...VECTOR_MODELS.map((m) => m.id), ...GIF_MODELS.map((m) => m.id), ...MODEL_GENERATOR_IDS];
50833
51105
  var assetAuthorizationFlags = [
50834
51106
  { name: "yes", type: "boolean", description: "Authorize spending credits" },
50835
51107
  { name: "dry-run", type: "boolean", description: "Validate and estimate locally without submitting a paid job" }
@@ -50842,14 +51114,23 @@ var catalogSections = {
50842
51114
  sprites: ["styles", "kinds", "actions", "videoModels"],
50843
51115
  image: ["assetTypes"],
50844
51116
  vector: ["assetTypes", "vectorModels", "vectorOptions"],
51117
+ gif: ["assetTypes", "gifModels", "gifOptions"],
50845
51118
  video: ["assetTypes", "videoModels", "videoProfiles"],
50846
- music: ["assetTypes", "musicModels"],
50847
- speech: ["assetTypes", "speechModels", "voices", "elevenLabsVoices"],
51119
+ music: ["assetTypes", "musicModels", "musicOptions"],
51120
+ speech: ["assetTypes", "speechModels", "voices", "elevenLabsVoices", "heygenVoices"],
50848
51121
  sfx: ["assetTypes", "sfxModels"],
50849
51122
  model: ["assetTypes", "modelGenerators", "modelAnimations"],
50850
- avatars: ["avatarTypes", "avatarLookTypes"]
51123
+ avatars: ["avatarTypes", "avatarLookTypes", "avatarVideoModels"]
51124
+ };
51125
+ var gifOptions = {
51126
+ frames: GIF_FRAMES,
51127
+ frameMs: GIF_FRAME_MS,
51128
+ qualities: GIF_QUALITIES,
51129
+ aspectRatios: IMAGE_ASPECT_RATIOS,
51130
+ maxReferences: MAX_GIF_REFERENCES,
51131
+ frameSizes: Object.fromEntries(IMAGE_ASPECT_RATIOS.map((ratio) => [ratio, Object.fromEntries(Array.from({ length: GIF_FRAMES.max - GIF_FRAMES.min + 1 }, (_, i2) => GIF_FRAMES.min + i2).map((frames) => [frames, gifFrameSize(ratio, frames)]))]))
50851
51132
  };
50852
- var catalog = () => ({ styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), modelGenerators: MODEL_GENERATORS.map((g) => ({ id: g.id, label: g.label, output: g.output, ...g.output === "spz" ? { splats: g.splats } : { faces: g.faces } })), modelAnimations: MODEL_ANIMATIONS, vectorModels: VECTOR_MODELS.map((m) => m.id), vectorOptions: { workflows: VECTOR_WORKFLOWS, qualities: VECTOR_QUALITIES, aspectRatios: VECTOR_ASPECT_RATIOS, shapes: VECTOR_SHAPES, maxReferences: MAX_VECTOR_REFERENCES }, elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS, avatarTypes: AVATAR_CREATE_TYPES, avatarLookTypes: AVATAR_LOOK_TYPES });
51133
+ var catalog = () => ({ styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), musicOptions: { styles: MUREKA_STYLES, vocals: MUREKA_VOCALS, songSeconds: SONG_SECONDS, maxLyrics: MAX_SONG_LYRICS }, speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), modelGenerators: MODEL_GENERATORS.map((g) => ({ id: g.id, label: g.label, output: g.output, ...g.output === "spz" ? { splats: g.splats } : { faces: g.faces } })), modelAnimations: MODEL_ANIMATIONS, vectorModels: VECTOR_MODELS.map((m) => m.id), vectorOptions: { workflows: VECTOR_WORKFLOWS, qualities: VECTOR_QUALITIES, aspectRatios: VECTOR_ASPECT_RATIOS, gptAspectRatios: GPT_VECTOR_ASPECT_RATIOS, shapes: VECTOR_SHAPES, maxReferences: MAX_VECTOR_REFERENCES }, gifModels: GIF_MODELS.map((m) => m.id), gifOptions, elevenLabsVoices: ELEVENLABS_VOICES, heygenVoices: HEYGEN_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS, avatarTypes: AVATAR_CREATE_TYPES, avatarLookTypes: AVATAR_LOOK_TYPES, avatarVideoModels: AVATAR_VIDEO_MODEL_IDS });
50853
51134
  var modelFlags = [
50854
51135
  { name: "video-model", type: "string", choices: SPRITE_VIDEO_MODEL_IDS, description: "Video model override for this operation" }
50855
51136
  ];
@@ -51096,7 +51377,7 @@ Confirm code: ${device.user_code}`);
51096
51377
  })))).add(defineCommand("credits", { description: "Show the selected workspace's remaining credit balance" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
51097
51378
  const client = new Client(flags.url, signal);
51098
51379
  out(stdout, await client.request(`/api/v1/credits${await workspaceQuery(client)}`));
51099
- }))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings, speech voices and avatar types" }, (command) => command.flags({ name: "type", type: "string", choices: Object.keys(catalogSections), description: "Only one part: sprites, image, vector, video, music, speech, sfx, model or avatars" }).action(({ flags, stdout }) => {
51380
+ }))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings, speech voices and avatar types" }, (command) => command.flags({ name: "type", type: "string", choices: Object.keys(catalogSections), description: "Only one part: sprites, image, vector, gif, video, music, speech, sfx, model or avatars" }).action(({ flags, stdout }) => {
51100
51381
  const full = catalog();
51101
51382
  const section = flags.type;
51102
51383
  out(stdout, section ? Object.fromEntries(catalogSections[section].map((key) => [key, full[key]])) : full);
@@ -51167,23 +51448,23 @@ Confirm code: ${device.user_code}`);
51167
51448
  ).action(async ({ args, flags, stdout, signal }) => out(stdout, await moveWork(new Client(flags.url, signal), args.id, flags))))).add(defineCommand("delete", { description: "Delete a project; requires --yes" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
51168
51449
  if (!flags.yes) throw new Error("Pass --yes to delete this project.");
51169
51450
  out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
51170
- })))).command("assets", (assets) => assets.add(defineCommand("list", { description: "List saved assets, their blob hashes, and configured generation models" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/assets`))))).add(defineCommand("generate", { description: "Generate an image, vector (SVG), video, music, speech, sound-effect or 3D model asset; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
51451
+ })))).command("assets", (assets) => assets.add(defineCommand("list", { description: "List saved assets, their blob hashes, and configured generation models" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/assets`))))).add(defineCommand("generate", { description: "Generate an image, vector (SVG), animated GIF, video, music, speech, sound-effect or 3D model asset; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
51171
51452
  { name: "type", type: "string", required: true, choices: ASSET_TYPES },
51172
51453
  { name: "name", type: "string", required: true },
51173
- { name: "prompt", type: "string", description: "Description to generate, or verbatim transcript for speech; video lip-sync and relight and vector image-to-vector take none, and camera-controls, insert-video, recast and 3d-to-video may leave it out" },
51174
- { name: "model", type: "string", choices: assetModelIds, description: "Model for the chosen asset type" },
51175
- { name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS, ...VECTOR_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video, relight and keyframes-to-video: auto (default, the inputs' shape) or a ratio to crop to. Vector from text: auto (default, the model's choice) or 1:4\u20134:1" },
51176
- { name: "transparent", type: "boolean", description: "Image: transparent background" },
51177
- { name: "references", type: "string", description: "Comma-separated blob SHAs of this project's images or sprite art, named Image 1, Image 2\u2026 in the prompt. Image: up to 4 to follow, such as a character sheet. Vector from text: up to 5, the first leading. Video: subjects or looks to keep (reference-to-video and insert-video up to 9, 3d-to-video up to 8), or recast's new people, one photo each (up to 4)" },
51454
+ { name: "prompt", type: "string", description: "Description to generate, or verbatim transcript for speech; video lip-sync and relight and vector image-to-vector take none, and camera-controls, insert-video, recast, 3d-to-video, a Mureka soundtrack and a song with --lyrics (where it sets the style) may leave it out" },
51455
+ { name: "model", type: "string", choices: assetModelIds, description: "Model for the chosen asset type. Vector: fal/ideogram-vector (default) or fal/gpt-image-2.6-vector. GIF: fal/gpt-image-2.6-gif (default, Flare, faster) or fal/gpt-image-2.6-sunburst-gif (finer, slower)" },
51456
+ { name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS, ...VECTOR_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video, relight and keyframes-to-video: auto (default, the inputs' shape) or a ratio to crop to. Vector from text: auto (default, the model's choice) or 1:4\u20134:1 (GPT Image 2.6 1:3\u20133:1). GIF: 1:1 (default), 3:2, 2:3, 16:9 or 9:16" },
51457
+ { name: "transparent", type: "boolean", description: "Image or GIF: transparent background. Vector with --model fal/gpt-image-2.6-vector: leave out the background" },
51458
+ { name: "references", type: "string", description: "Comma-separated blob SHAs of this project's images, GIFs (their first frame) or sprite art, named Image 1, Image 2\u2026 in the prompt. Image: up to 4 to follow, such as a character sheet. Vector from text (Ideogram): up to 5, the first leading. GIF: up to 4 to animate or follow. Video: subjects or looks to keep (reference-to-video and insert-video up to 9, 3d-to-video up to 8), or recast's new people, one photo each (up to 4)" },
51178
51459
  { name: "mask", type: "string", description: "Image: edit only these rectangles of Image 1 (the first --references image), as x,y,width,height fractions of its size from the top left, up to 8 separated by semicolons; the rest is kept" },
51179
- { name: "workflow", type: "string", choices: [...VIDEO_WORKFLOWS, ...VECTOR_WORKFLOWS], description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, FLUX 3 Max keyframes-to-video, HeyGen Video 1 reference-to-video, as the model's videoProfiles allow. Vector: text-to-vector (default) or image-to-vector, which traces --source" },
51180
- { name: "reference", type: "string", description: "Video: starting image blob SHA from this project's assets or sprites; the speaking face for lip-sync, the image the camera moves around for camera-controls. Model: the image or sprite art to build it from (image-to-3D); without one the prompt describes it" },
51460
+ { name: "workflow", type: "string", choices: [...VIDEO_WORKFLOWS, ...VECTOR_WORKFLOWS], description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, FLUX 3 Max keyframes-to-video, HeyGen Video 1 and Seedance reference-to-video, as the model's videoProfiles allow. Vector: text-to-vector (default) or image-to-vector, which traces --source" },
51461
+ { name: "reference", type: "string", description: "Video: starting image blob SHA from this project's assets (a GIF's SHA gives its first frame) or sprites; the speaking face for lip-sync, the image the camera moves around for camera-controls. Model: the image or sprite art to build it from (image-to-3D); without one the prompt describes it" },
51181
51462
  { name: "end-reference", type: "string", description: "Video: ending image blob SHA; models with endFrameOnly accept it without --reference" },
51182
51463
  { name: "middle-reference", type: "string", description: "Reference-to-video: image blob SHA reached at --middle-seconds; needs --reference and --end-reference, 480p or 768p" },
51183
51464
  { name: "middle-seconds", type: "number", description: "Reference-to-video: when the middle image is reached, in seconds" },
51184
- { name: "source", type: "string", description: "Blob SHA of a video asset in this project: the clip to extend, insert a scene into, recast or relight, or the 3D render 3d-to-video turns into footage. Vector image-to-vector: the image or sprite art to trace" },
51465
+ { name: "source", type: "string", description: "Blob SHA of a video asset in this project: the clip to extend, insert a scene into, recast or relight, or the 3D render 3d-to-video turns into footage. Vector image-to-vector: the image, GIF (its first frame) or sprite art to trace. Music with --model fal/mureka-9.5-soundtrack: the video (up to 100 MB), image, GIF still or sprite art to score; a video's soundtrack runs as long as the video" },
51185
51466
  { name: "soundtrack", type: "string", description: "Video from an image or text: blob SHA of a music, speech or SFX asset pinned as the soundtrack (2 s or longer). Lip-sync: the speech to say (5 s\u201315 min), which sets the length" },
51186
- { name: "audio-references", type: "string", description: "Extension or reference-to-video: up to 3 comma-separated audio asset blob SHAs (on fal 2\u201315 s each, 15 s total) guiding the sound; refer to them as Audio 1, Audio 2\u2026 in the prompt" },
51467
+ { name: "audio-references", type: "string", description: "Extension or reference-to-video: up to 3 comma-separated audio asset blob SHAs (on fal and Seedance 2\u201315 s each, 15 s total; Seedance 2.5 30 s) guiding the sound; refer to them as Audio 1, Audio 2\u2026 in the prompt" },
51187
51468
  { name: "reference-videos", type: "string", description: "Reference-to-video or insert-video: up to 3 comma-separated video asset blob SHAs (on fal 2\u201315 s each, 15 s total; HeyGen reads the first 5 s of each) guiding motion, named Video 1, Video 2\u2026 in the prompt" },
51188
51469
  { name: "camera", type: "string", description: 'Camera-controls: 2\u201312 keyframes time,azimuth,elevation,distance (time 0\u20131, angles in degrees, elevation \u221290\u201390) separated by semicolons, e.g. "0,0,0,1;1,90,15,1.2"' },
51189
51470
  { name: "keyframes", type: "string", description: 'Keyframes-to-video (FLUX 3 Max): 1\u201310 seconds,image-SHA pairs separated by semicolons, e.g. "0,SHA;2.5,SHA;5,SHA"; each image is reached at its second, on a frame of its own of the 24 fps clip, within --seconds' },
@@ -51197,14 +51478,17 @@ Confirm code: ${device.user_code}`);
51197
51478
  { name: "generated-references", type: "number", description: "3d-to-video without --references: how many reference images it may draw, 1\u20138 (default 2)" },
51198
51479
  { name: "style", type: "string", choices: VIDEO_STYLES, description: "Style workflow: the preset look, at 768p for 5\u201315 whole seconds" },
51199
51480
  { name: "damage", type: "string", choices: VHS_DAMAGE_LEVELS, description: "VHS style: tape damage (fal's default medium)" },
51200
- { name: "seconds", type: "number", description: "Video or SFX duration in seconds (default 5); H3 takes fractions (6.5), FLUX 3 Max 5\u201320 whole seconds, HeyGen Video 1 5\u201315 (up to 30 from an image or text with --prompt-expansion balanced or quality); extensions add this many seconds, insert-video's scene is 5\u201313; lip-sync, recast, relight and 3d-to-video take their input's length; SFX supports 0.5\u201330" },
51481
+ { name: "seconds", type: "number", description: "Video, SFX or song duration in seconds (default 5; a Mureka song picks its own); H3 takes fractions (6.5), FLUX 3 Max 5\u201320 whole seconds, HeyGen Video 1 5\u201315 (up to 30 from an image or text with --prompt-expansion balanced or quality), Seedance 4\u201315 whole seconds (2.5 up to 30); extensions add this many seconds, insert-video's scene is 5\u201313; lip-sync, recast, relight and 3d-to-video take their input's length; SFX supports 0.5\u201330; Mureka 9.5 Song 120\u2013300 whole seconds" },
51201
51482
  { name: "resolution", type: "string", choices: VIDEO_RESOLUTIONS, description: "Video resolution supported by the selected model and workflow (default its first)" },
51202
51483
  { name: "mode", type: "string", choices: ["cost", "speed", "quality"], description: "Video generation mode supported by the selected model (default its first)" },
51203
51484
  { name: "prompt-expansion", type: "string", choices: PROMPT_EXPANSION_MODES, description: "Video: how much the model may rewrite the prompt (default disabled); extensions treat any other value as on. FLUX 3 Max always reasons about the prompt: quality thoroughly, any other value lightly. HeyGen Video 1 needs balanced or quality for clips over 15 s" },
51204
51485
  { name: "seed", type: "number", description: "Video, vector from text, or model with --model idol: seed for repeatable results, where the model takes one" },
51205
51486
  { name: "extend-output", type: "string", choices: EXTEND_OUTPUTS, description: "Extension: extended (default, source plus new footage) or continuation (a 1.625 s overlap, then the new footage)" },
51206
- { name: "voice", type: "string", description: "Gemini voice name or ElevenLabs voice ID (defaults: Kore / George)" },
51207
- { name: "delivery", type: "string", description: "Gemini speech delivery instructions; ElevenLabs uses audio tags in the transcript" },
51487
+ { name: "voice", type: "string", description: "Gemini voice name, or an ElevenLabs or HeyGen public voice ID (defaults: Kore / George / Nadine; catalog --type speech suggests more)" },
51488
+ { name: "delivery", type: "string", description: "Gemini speech delivery instructions; ElevenLabs uses audio tags in the transcript, and HeyGen Voice reads it as written" },
51489
+ { name: "lyrics", type: "string", description: `Music with --model fal/mureka-9.5-song: the words to sing, up to ${MAX_SONG_LYRICS.toLocaleString("en-US")} characters, sections tagged [Verse], [Chorus]\u2026; --prompt then sets the style. A sung song costs 15 credits, one written from the prompt 50` },
51490
+ { name: "styles", type: "string", description: `Music with --model fal/mureka-9.5-song and no --lyrics: comma-separated genres from ${MUREKA_STYLES.join(", ")}` },
51491
+ { name: "vocals", type: "string", choices: MUREKA_VOCALS, description: "Music with --model fal/mureka-9.5-song and --lyrics: who sings" },
51208
51492
  { name: "loop", type: "boolean", description: "SFX: create a seamless loop" },
51209
51493
  { name: "texture", type: "boolean", description: "Model: textured (default on); --no-texture makes a plain white model" },
51210
51494
  { name: "pbr", type: "boolean", description: "Model: metallic, roughness and normal maps with the texture (default on); --no-pbr leaves them out" },
@@ -51215,7 +51499,9 @@ Confirm code: ${device.user_code}`);
51215
51499
  { name: "splats", type: "number", description: "Model with --model idol: how many Gaussian splats, 32,768\u20131,048,576 (default 262,144)" },
51216
51500
  { name: "photo", type: "boolean", description: "Model with --model idol: --reference is a camera photo, cropped more loosely than a render or generated image" },
51217
51501
  { name: "prompt-influence", type: "number", description: "SFX: prompt adherence, 0\u20131 (default 0.3)" },
51218
- { name: "quality", type: "string", choices: VECTOR_QUALITIES, description: "Vector from text: low (fastest, 13 credits), medium (default, 16) or high (20, or 32 with --references)" },
51502
+ { name: "quality", type: "string", choices: [.../* @__PURE__ */ new Set([...VECTOR_QUALITIES, ...GIF_QUALITIES])], description: "Vector from text: low (fastest), medium (default) or high; Ideogram 13, 16 or 20 credits (32 at high with --references), GPT Image 2.6 holds 8, 12 or 22 and is charged fal's metered tokens. GIF: low, medium (default) or high detail in every frame" },
51503
+ { name: "frames", type: "number", description: `GIF: how many frames, ${GIF_FRAMES.min}\u2013${GIF_FRAMES.max} (default ${GIF_FRAMES.default}); all frames share one picture's pixels, so more frames make each smaller (catalog --type gif lists the sizes)` },
51504
+ { name: "frame-ms", type: "number", description: `GIF: how long each frame shows, ${GIF_FRAME_MS.min}\u2013${GIF_FRAME_MS.max} ms (default ${GIF_FRAME_MS.default}), rounded to 10 ms` },
51219
51505
  { name: "shapes", type: "string", choices: VECTOR_SHAPES, description: "Vector image-to-vector (10 credits): detailed (default) keeps fine detail, simple uses fewer, simpler shapes" },
51220
51506
  ...assetAuthorizationFlags,
51221
51507
  ...assetWaitFlags,
@@ -51236,7 +51522,7 @@ Confirm code: ${device.user_code}`);
51236
51522
  const result = await submitAssetBatch(new Client(flags.url, signal), args.id, await assetsFromBatchFile(flags.file, flags.model), flags, signal);
51237
51523
  out(stdout, result);
51238
51524
  if (result.error) throw new Error(result.error);
51239
- }))).add(defineCommand("download", { description: "Download a saved asset by id; refuses to overwrite" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "asset", type: "string", required: true, description: "Asset id from assets list" }).flags({ name: "output", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await downloadAsset(new Client(flags.url, signal), args.id, args.asset, flags.output)))))).command("avatars", (avatars) => avatars.add(defineCommand("list", { description: "List HeyGen avatar characters: the public catalog, or --ownership private for this project's own" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "ownership", type: "string", choices: AVATAR_OWNERSHIPS, default: "public" }, { name: "limit", type: "number", default: 20, description: "Avatars per page, 1\u201350" }, { name: "token", type: "string", description: "nextToken from the previous page" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}?${new URLSearchParams({ ownership: flags.ownership, limit: String(flags.limit), ...flags.token ? { token: flags.token } : {} })}`))))).add(defineCommand("looks", { description: "List an avatar's looks with --group, or HeyGen's public looks; a look ID is what HeyGen renders with" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string", description: "Avatar group ID: one of this project's or a public one" }, { name: "avatar-type", type: "string", choices: AVATAR_LOOK_TYPES }, { name: "limit", type: "number", default: 20, description: "Looks per page, 1\u201350" }, { name: "token", type: "string", description: "nextToken from the previous page" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/looks?${new URLSearchParams({ limit: String(flags.limit), ...flags.group ? { groupId: flags.group } : {}, ...flags["avatar-type"] ? { avatarType: flags["avatar-type"] } : {}, ...flags.token ? { token: flags.token } : {} })}`))))).add(defineCommand("get", { description: "Show one avatar group (--group) or look (--look), with status and consent" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string" }, { name: "look", type: "string" }).action(async ({ args, flags, stdout, signal }) => {
51525
+ }))).add(defineCommand("download", { description: "Download a saved asset by id; refuses to overwrite" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "asset", type: "string", required: true, description: "Asset id from assets list" }).flags({ name: "output", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await downloadAsset(new Client(flags.url, signal), args.id, args.asset, flags.output)))))).command("avatars", (avatars) => avatars.add(defineCommand("list", { description: "List HeyGen avatar characters: the public catalog, or --ownership private for this workspace's own, which all its projects share" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "ownership", type: "string", choices: AVATAR_OWNERSHIPS, default: "public" }, { name: "limit", type: "number", default: 20, description: "Avatars per page, 1\u201350" }, { name: "token", type: "string", description: "nextToken from the previous page" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}?${new URLSearchParams({ ownership: flags.ownership, limit: String(flags.limit), ...flags.token ? { token: flags.token } : {} })}`))))).add(defineCommand("looks", { description: "List an avatar's looks with --group, or HeyGen's public looks; a look ID is what HeyGen renders with" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string", description: "Avatar group ID: one of this workspace's or a public one" }, { name: "avatar-type", type: "string", choices: AVATAR_LOOK_TYPES }, { name: "limit", type: "number", default: 20, description: "Looks per page, 1\u201350" }, { name: "token", type: "string", description: "nextToken from the previous page" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/looks?${new URLSearchParams({ limit: String(flags.limit), ...flags.group ? { groupId: flags.group } : {}, ...flags["avatar-type"] ? { avatarType: flags["avatar-type"] } : {}, ...flags.token ? { token: flags.token } : {} })}`))))).add(defineCommand("get", { description: "Show one avatar group (--group) or look (--look), with status and consent" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string" }, { name: "look", type: "string" }).action(async ({ args, flags, stdout, signal }) => {
51240
51526
  if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
51241
51527
  out(stdout, await new Client(flags.url, signal).request(flags.look ? `${avatarsPath(args.id)}/looks/${encodeURIComponent(flags.look)}` : `${avatarsPath(args.id)}/${encodeURIComponent(flags.group)}`));
51242
51528
  }))).add(defineCommand("templates", { description: "List HeyGen Look Packs and look templates with their female and male IDs" }, (command) => command.action(({ stdout }) => out(stdout, { templates: AVATAR_LOOK_TEMPLATES })))).add(defineCommand("create", { description: "Create a HeyGen avatar from a prompt, a photo or footage (digital_twin); --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
@@ -51244,19 +51530,32 @@ Confirm code: ${device.user_code}`);
51244
51530
  { name: "name", type: "string", required: true },
51245
51531
  { name: "prompt", type: "string", description: "Prompt: the character, wardrobe, setting and light, up to 1000 characters" },
51246
51532
  { name: "aspect-ratio", type: "string", choices: AVATAR_ASPECT_RATIOS, description: "Prompt: image shape (default auto)" },
51247
- { name: "look", type: "string", description: "Prompt: one of this project's looks to put in a new outfit or setting" },
51533
+ { name: "look", type: "string", description: "Prompt: one of this workspace's looks to put in a new outfit or setting" },
51248
51534
  { name: "reference-images", type: "string", description: "Prompt: up to 3 comma-separated wardrobe or style references (project image SHA, HTTPS URL or local file)" },
51249
51535
  { name: "file", type: "string", description: "Photo or digital_twin: project image/video SHA, HTTPS URL (up to 32 MB) or local file (up to 8 MB)" },
51250
- { name: "group", type: "string", description: "Add the result to one of this project's avatar groups" },
51536
+ { name: "group", type: "string", description: "Add the result to one of this workspace's avatar groups" },
51251
51537
  { name: "subject-consent", type: "boolean", description: "Photo or digital_twin: confirm the person shown agreed to become an avatar" },
51252
51538
  ...assetAuthorizationFlags
51253
- ).action(async ({ args, flags, stdout, signal }) => out(stdout, await submitAvatar(new Client(flags.url, signal), args.id, await avatarFromFlags(flags), flags))))).add(defineCommand("add-looks", { description: "Apply a Look Pack (five looks) or look template (two) to one of this project's looks; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
51254
- { name: "look", type: "string", required: true, description: "Reference look ID from this project's avatars" },
51539
+ ).action(async ({ args, flags, stdout, signal }) => out(stdout, await submitAvatar(new Client(flags.url, signal), args.id, await avatarFromFlags(flags), flags))))).add(defineCommand("add-looks", { description: "Apply a Look Pack (five looks) or look template (two) to one of this workspace's looks; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
51540
+ { name: "look", type: "string", required: true, description: "Reference look ID from this workspace's avatars" },
51255
51541
  { name: "template", type: "string", required: true, description: "Template ID from avatars templates" },
51256
51542
  { name: "method", type: "string", choices: AVATAR_LOOK_METHODS, description: "Needed only for templates outside the gallery" },
51257
51543
  { name: "aspect-ratio", type: "string", choices: LOOK_PACK_ASPECT_RATIOS, description: "Look Pack: 16:9 (default) or 9:16, cropped to portrait" },
51258
51544
  ...assetAuthorizationFlags
51259
- ).action(async ({ args, flags, stdout, signal }) => out(stdout, await submitLooks(new Client(flags.url, signal), args.id, looksFromFlags(flags), flags))))).add(defineCommand("wait", { description: "Wait until HeyGen finishes a new look; fail if it fails or times out" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "look", type: "string", required: true, description: "Look ID from avatars create or add-looks" }).flags({ name: "timeout", type: "number", default: 900, description: "Maximum seconds to wait" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await waitForLook(new Client(flags.url, signal), args.id, args.look, flags.timeout, signal))))).add(defineCommand("rename", { description: "Rename one of this project's photo avatar or digital twin looks" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "look", type: "string", required: true }).flags({ name: "name", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/looks/${encodeURIComponent(args.look)}`, { method: "PATCH", body: { name: flags.name } }))))).add(defineCommand("voice", { description: "Set the default voice of one of this project's avatars" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "group", type: "string", required: true }).flags({ name: "voice", type: "string", required: true, description: "HeyGen voice ID" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/${encodeURIComponent(args.group)}`, { method: "PATCH", body: { defaultVoiceId: flags.voice } }))))).add(defineCommand("consent", { description: "Get a 24-hour link where the person in a digital twin records consent" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "group", type: "string", required: true }).flags({ name: "reroute-url", type: "string", description: "HTTPS page to send the person to afterwards" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/${encodeURIComponent(args.group)}/consent`, { method: "POST", body: flags["reroute-url"] ? { rerouteUrl: flags["reroute-url"] } : {} }))))).add(defineCommand("delete", { description: "Permanently delete one of this project's avatars (--group, with every look) or one look (--look); requires --yes" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string" }, { name: "look", type: "string" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
51545
+ ).action(async ({ args, flags, stdout, signal }) => out(stdout, await submitLooks(new Client(flags.url, signal), args.id, looksFromFlags(flags), flags))))).add(defineCommand("wait", { description: "Wait until HeyGen finishes a new look; fail if it fails or times out" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "look", type: "string", required: true, description: "Look ID from avatars create or add-looks" }).flags({ name: "timeout", type: "number", default: 900, description: "Maximum seconds to wait" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await waitForLook(new Client(flags.url, signal), args.id, args.look, flags.timeout, signal))))).add(defineCommand("rename", { description: "Rename one of this workspace's photo avatar or digital twin looks" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "look", type: "string", required: true }).flags({ name: "name", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/looks/${encodeURIComponent(args.look)}`, { method: "PATCH", body: { name: flags.name } }))))).add(defineCommand("voice", { description: "Set the default voice of one of this workspace's avatars: a public voice or one of its own cloned voices" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "group", type: "string", required: true }).flags({ name: "voice", type: "string", required: true, description: "Voice ID from avatars voices" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/${encodeURIComponent(args.group)}`, { method: "PATCH", body: { defaultVoiceId: flags.voice } }))))).add(defineCommand("voices", { description: "List voices an avatar can speak with: HeyGen's public library, or --ownership private for those cloned from this workspace's digital twins" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "ownership", type: "string", choices: AVATAR_OWNERSHIPS, default: "public" }, { name: "language", type: "string", description: "Public voices in one language, such as English or Turkish" }, { name: "gender", type: "string", choices: ["male", "female"] }, { name: "limit", type: "number", default: 20, description: "Voices per page, 1\u201350" }, { name: "token", type: "string", description: "nextToken from the previous page" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/voices?${new URLSearchParams({ ownership: flags.ownership, limit: String(flags.limit), ...flags.language ? { language: flags.language } : {}, ...flags.gender ? { gender: flags.gender } : {}, ...flags.token ? { token: flags.token } : {} })}`))))).add(defineCommand("video", { description: "Make a video of an avatar look speaking a script or lip-syncing saved audio, saved to the project; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
51546
+ { name: "look", type: "string", required: true, description: "A completed look ID: one of this workspace's avatars or a public one" },
51547
+ { name: "name", type: "string", required: true, description: "Name of the saved video" },
51548
+ { name: "script", type: "string", description: `What the avatar says, up to ${AVATAR_SCRIPT_MAX} characters` },
51549
+ { name: "script-file", type: "string", description: "Read the script from a UTF-8 text file" },
51550
+ { name: "audio", type: "string", description: `Instead of a script: the file SHA of saved speech or music to lip-sync, up to ${AVATAR_VIDEO_MAX_SECONDS} seconds` },
51551
+ { name: "voice", type: "string", description: "Voice for the script (avatars voices); the look's own voice by default" },
51552
+ { name: "model", type: "string", choices: AVATAR_VIDEO_MODEL_IDS, description: "Rendering engine the look supports (default heygen/avatar-iv)" },
51553
+ { name: "aspect-ratio", type: "string", choices: AVATAR_ASPECT_RATIOS, description: "auto (the look's shape, default), 16:9, 9:16, 1:1, 4:5 or 5:4" },
51554
+ { name: "resolution", type: "string", choices: AVATAR_VIDEO_RESOLUTIONS, description: "720p or 1080p (default)" },
51555
+ { name: "motion-prompt", type: "string", description: "Body and hand movement: photo avatars on Avatar IV, any look on Avatar V" },
51556
+ { name: "expressiveness", type: "string", choices: AVATAR_EXPRESSIVENESS, description: "Photo avatars on Avatar IV: how much they move" },
51557
+ ...assetAuthorizationFlags
51558
+ ).action(async ({ args, flags, stdout, signal }) => out(stdout, await submitAvatarVideo(new Client(flags.url, signal), args.id, await avatarVideoFromFlags(flags), flags))))).add(defineCommand("consent", { description: "Get a 24-hour link where the person in a digital twin records consent" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id, { name: "group", type: "string", required: true }).flags({ name: "reroute-url", type: "string", description: "HTTPS page to send the person to afterwards" }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${avatarsPath(args.id)}/${encodeURIComponent(args.group)}/consent`, { method: "POST", body: flags["reroute-url"] ? { rerouteUrl: flags["reroute-url"] } : {} }))))).add(defineCommand("delete", { description: "Permanently delete one of this workspace's avatars (--group, with every look and its cloned voice) or one look (--look); requires --yes" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags({ name: "group", type: "string" }, { name: "look", type: "string" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
51260
51559
  if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
51261
51560
  if (!flags.yes) throw new Error("Pass --yes to delete the avatar for good.");
51262
51561
  out(stdout, await new Client(flags.url, signal).request(flags.look ? `${avatarsPath(args.id)}/looks/${encodeURIComponent(flags.look)}` : `${avatarsPath(args.id)}/${encodeURIComponent(flags.group)}`, { method: "DELETE" }));