@docyrus/animaguru 0.36.0 → 0.39.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.36.0",
19764
+ version: "0.39.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" } },
@@ -45267,6 +45269,7 @@ import { chmod as chmod2, mkdir as mkdir4, readFile as readFile2, rename as rena
45267
45269
  import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:path";
45268
45270
  import { homedir as homedir3 } from "node:os";
45269
45271
  import { createHash, randomUUID } from "node:crypto";
45272
+ var USER_AGENT = `animaguru-cli/${package_default.version}`;
45270
45273
  function serverUrl(value) {
45271
45274
  const url3 = new URL(value);
45272
45275
  if (url3.username || url3.password || url3.search || url3.hash || url3.pathname !== "/") throw new Error("Use an origin URL without credentials, path, query or fragment.");
@@ -45332,18 +45335,19 @@ var Client = class {
45332
45335
  origin;
45333
45336
  async request(path, options = {}) {
45334
45337
  const token3 = options.authenticated !== false ? await sessionToken(this.origin) : void 0;
45335
- const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/[^/]+\/consent)$/.test(path);
45338
+ const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/videos|avatars\/[^/]+\/consent)$/.test(path);
45336
45339
  const idempotency = generation ? { "Idempotency-Key": generationRequestId(path, options.body) } : {};
45337
45340
  const response = await fetch(`${this.origin}${path}`, {
45338
45341
  method: options.method ?? "GET",
45339
- headers: { Origin: this.origin, ...token3 ? { Authorization: `Bearer ${token3}` } : {}, ...options.body instanceof Blob ? { "Content-Type": options.body.type || "application/octet-stream" } : options.body !== void 0 && !(options.body instanceof FormData) ? { "Content-Type": "application/json" } : {}, ...idempotency, ...options.headers },
45342
+ headers: { Origin: this.origin, "User-Agent": USER_AGENT, ...token3 ? { Authorization: `Bearer ${token3}` } : {}, ...options.body instanceof Blob ? { "Content-Type": options.body.type || "application/octet-stream" } : options.body !== void 0 && !(options.body instanceof FormData) ? { "Content-Type": "application/json" } : {}, ...idempotency, ...options.headers },
45340
45343
  body: options.body instanceof FormData || options.body instanceof Blob ? options.body : options.body === void 0 ? void 0 : JSON.stringify(options.body),
45341
45344
  redirect: "error",
45342
45345
  signal: this.signal ? AbortSignal.any([this.signal, AbortSignal.timeout(options.timeout ?? 3e4)]) : AbortSignal.timeout(options.timeout ?? 3e4)
45343
45346
  });
45344
45347
  if (response.ok && options.raw) return response;
45345
45348
  const data = await response.json();
45346
- if (!response.ok) throw new ServiceError(response.status, typeof data.error === "string" ? data.error : "request_failed", data.error_description ?? (typeof data.error === "object" ? data.error.message : void 0) ?? data.message ?? `Request failed (${response.status}).`);
45349
+ const code = typeof data.error === "string" ? data.error : typeof data.error?.code === "string" ? data.error.code : "request_failed";
45350
+ if (!response.ok) throw new ServiceError(response.status, code, data.error_description ?? (typeof data.error === "object" ? data.error.message : void 0) ?? data.message ?? `Request failed (${response.status}).`);
45347
45351
  return data;
45348
45352
  }
45349
45353
  };
@@ -46973,6 +46977,130 @@ async function createAnimation(options) {
46973
46977
  };
46974
46978
  }
46975
46979
 
46980
+ // shared/avatars.ts
46981
+ var AVATAR_OWNERSHIPS = ["public", "private"];
46982
+ var AVATAR_LOOK_TYPES = ["studio_avatar", "digital_twin", "photo_avatar"];
46983
+ var AVATAR_CREATE_TYPES = ["prompt", "photo", "digital_twin"];
46984
+ var AVATAR_ASPECT_RATIOS = ["auto", "16:9", "9:16", "1:1", "4:5", "5:4"];
46985
+ var LOOK_PACK_ASPECT_RATIOS = ["16:9", "9:16"];
46986
+ var AVATAR_LOOK_METHODS = ["look_pack", "template"];
46987
+ var AVATAR_CREDIT_UNITS = { digital_twin: 2130, photo: 1320, prompt: 1320 };
46988
+ var AVATAR_LOOK_CREDIT_UNITS = 1320;
46989
+ var LOOKS_PER_METHOD = { look_pack: 5, template: 2 };
46990
+ var AVATAR_LOOK_TEMPLATES = [
46991
+ { id: "5a8e6be843ee4321a014946bf62aa322", method: "look_pack", name: "Modern Corporate", variant: "female" },
46992
+ { id: "5e659e76755d47fd9160a2d7dedce124", method: "look_pack", name: "Modern Corporate", variant: "male" },
46993
+ { id: "e1650a9029d04ea1a350421c163295fd", method: "look_pack", name: "Stylish Business-Casual", variant: "female" },
46994
+ { id: "a5f8e0c0cb654c1a83c695629b173af5", method: "look_pack", name: "Stylish Business-Casual", variant: "male" },
46995
+ { id: "e7b000e2cb0d44f1a2d2f291a38b395a", method: "look_pack", name: "Scandinavian Minimal", variant: "female" },
46996
+ { id: "9600f78f27bb49d792e1c56ce624b929", method: "look_pack", name: "Scandinavian Minimal", variant: "male" },
46997
+ { id: "d7ad40ec97d4459a92b33398b57caf4e", method: "look_pack", name: "Cool Tech / Slate", variant: "female" },
46998
+ { id: "fbd03fc3603a4e3caff594f5743a1733", method: "look_pack", name: "Cool Tech / Slate", variant: "male" },
46999
+ { id: "14f4070bd5a04eb6b1f82ae087cd0f30", method: "look_pack", name: "Healthcare", variant: "female" },
47000
+ { id: "ce706bfa8556401e8c4401765102a455", method: "look_pack", name: "Healthcare", variant: "male" },
47001
+ { id: "b0cae4fbae384ab9ab9a8e73ba504be8", method: "look_pack", name: "Keynote Power Presenter", variant: "female" },
47002
+ { id: "71e84769162d4f4186563ad9e0fc45b8", method: "look_pack", name: "Keynote Power Presenter", variant: "male" },
47003
+ { id: "935ea0b7cdde4e6eb5a198a3d666f677", method: "look_pack", name: "Fitness", variant: "female" },
47004
+ { id: "daeb8b15c92a4acf9ffb437ba5534458", method: "look_pack", name: "Fitness", variant: "male" },
47005
+ { id: "6f183d726933457c9035fc0aa4e9c0c3", method: "look_pack", name: "Automotive Dealership", variant: "female" },
47006
+ { id: "8595c78b466c4e51802731229c59b6e7", method: "look_pack", name: "Automotive Dealership", variant: "male" },
47007
+ { id: "b30c83d99cec48e09c8f5b07ed8aa38c", method: "template", name: "Business Casual", variant: "female" },
47008
+ { id: "2dc326030e2e4471babcc9eda56131b6", method: "template", name: "Business Casual", variant: "male" },
47009
+ { id: "00c49daa4eb94386a0c12508043bab17", method: "template", name: "Podcast", variant: "female" },
47010
+ { id: "0e52ceafe4d5456fb83c1e39899ff76a", method: "template", name: "Podcast", variant: "male" }
47011
+ ];
47012
+ var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
47013
+ var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
47014
+ var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
47015
+ var name = external_exports.string().trim().min(1).max(80);
47016
+ var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
47017
+ var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
47018
+ var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
47019
+ var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
47020
+ var avatarFile = external_exports.discriminatedUnion("type", [
47021
+ external_exports.object({ type: external_exports.literal("file"), sha }),
47022
+ external_exports.object({ type: external_exports.literal("url"), url: https }),
47023
+ external_exports.object({
47024
+ type: external_exports.literal("base64"),
47025
+ mediaType: external_exports.enum([...AVATAR_PHOTO_TYPES, ...AVATAR_FOOTAGE_TYPES]),
47026
+ 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.")
47027
+ })
47028
+ ]);
47029
+ var subjectConsent = external_exports.literal(true, { error: "Confirm that the person shown agreed to become an avatar (subjectConsent: true)." });
47030
+ var avatarCreateRequest = external_exports.discriminatedUnion("type", [
47031
+ external_exports.object({
47032
+ type: external_exports.literal("prompt"),
47033
+ name,
47034
+ prompt: external_exports.string().trim().min(3).max(1e3),
47035
+ aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
47036
+ lookId: heygenId.optional(),
47037
+ groupId: heygenId.optional(),
47038
+ referenceImages: external_exports.array(avatarFile).max(3).default([])
47039
+ }),
47040
+ external_exports.object({ type: external_exports.literal("photo"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent }),
47041
+ external_exports.object({ type: external_exports.literal("digital_twin"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent })
47042
+ ]).superRefine((p, ctx) => {
47043
+ const files = p.type === "prompt" ? p.referenceImages : [p.file];
47044
+ const footage = p.type === "digital_twin";
47045
+ for (const [index2, file2] of files.entries()) {
47046
+ if (file2.type !== "base64") continue;
47047
+ const allowed = footage ? AVATAR_FOOTAGE_TYPES : AVATAR_PHOTO_TYPES;
47048
+ 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"] });
47049
+ }
47050
+ });
47051
+ var avatarLooksRequest = external_exports.discriminatedUnion("method", [
47052
+ external_exports.object({ method: external_exports.literal("look_pack"), lookId: heygenId, templateId: heygenId, aspectRatio: external_exports.enum(LOOK_PACK_ASPECT_RATIOS).default("16:9") }),
47053
+ external_exports.object({ method: external_exports.literal("template"), lookId: heygenId, templateId: heygenId })
47054
+ ]).superRefine((p, ctx) => {
47055
+ const known = avatarTemplate(p.templateId);
47056
+ if (known && known.method !== p.method) ctx.addIssue({ code: "custom", message: `This template is applied with method ${known.method}.`, path: ["method"] });
47057
+ });
47058
+ var heygenVoiceId = external_exports.string().trim().regex(/^[A-Za-z0-9_.-]{1,128}$/, "Use a HeyGen voice ID.");
47059
+ var avatarGroupUpdate = external_exports.object({ defaultVoiceId: heygenVoiceId });
47060
+ var avatarLookUpdate = external_exports.object({ name: external_exports.string().trim().min(1).max(255) });
47061
+ var avatarConsentRequest = external_exports.object({ rerouteUrl: https.optional() });
47062
+ var limit = external_exports.coerce.number().int().min(1).max(50).default(20);
47063
+ var token = external_exports.string().max(2048).optional();
47064
+ var avatarListQuery = external_exports.object({ ownership: external_exports.enum(AVATAR_OWNERSHIPS).default("public"), limit, token });
47065
+ var avatarLookListQuery = external_exports.object({ groupId: heygenId.optional(), avatarType: external_exports.enum(AVATAR_LOOK_TYPES).optional(), limit, token });
47066
+ 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 });
47067
+ var avatarCreateEstimate = (p) => AVATAR_CREDIT_UNITS[p.type];
47068
+ var avatarLooksEstimate = (p) => LOOKS_PER_METHOD[p.method] * AVATAR_LOOK_CREDIT_UNITS;
47069
+ var AVATAR_VIDEO_MODEL_IDS = ["heygen/avatar-iv", "heygen/avatar-v", "heygen/avatar-iii"];
47070
+ var DEFAULT_AVATAR_VIDEO_MODEL = "heygen/avatar-iv";
47071
+ var AVATAR_VIDEO_RESOLUTIONS = ["720p", "1080p"];
47072
+ var AVATAR_EXPRESSIVENESS = ["low", "medium", "high"];
47073
+ var AVATAR_SCRIPT_MAX = 1500;
47074
+ var AVATAR_VIDEO_MAX_SECONDS = 120;
47075
+ var avatarVideoRequest = external_exports.object({
47076
+ type: external_exports.literal("video"),
47077
+ name,
47078
+ model: external_exports.enum(AVATAR_VIDEO_MODEL_IDS).default(DEFAULT_AVATAR_VIDEO_MODEL),
47079
+ /** A look of this workspace's avatars or of HeyGen's catalog. */
47080
+ lookId: heygenId,
47081
+ /** What the avatar says. */
47082
+ script: external_exports.string().trim().min(1).max(AVATAR_SCRIPT_MAX).optional(),
47083
+ /** Saved speech or music in this project that the avatar lip-syncs instead of a script. */
47084
+ soundtrack: sha.optional(),
47085
+ /** The voice that speaks the script; without it, the look's own voice. */
47086
+ voiceId: heygenVoiceId.optional(),
47087
+ aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
47088
+ resolution: external_exports.enum(AVATAR_VIDEO_RESOLUTIONS).default("1080p"),
47089
+ /** How the body and hands move: photo avatars on Avatar IV, any look on Avatar V. */
47090
+ motionPrompt: external_exports.string().trim().min(3).max(1e3).optional(),
47091
+ /** How much a photo avatar moves on Avatar IV (low unless set). */
47092
+ expressiveness: external_exports.enum(AVATAR_EXPRESSIVENESS).optional(),
47093
+ /** The clip's length, which the server sets: the audio's, or the script's estimate until the clip is made. */
47094
+ seconds: external_exports.number().positive().max(600).optional()
47095
+ }).superRefine((p, ctx) => {
47096
+ const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47097
+ if (p.script && p.soundtrack) issue2("soundtrack", "Give the avatar a script or a saved audio track, not both.");
47098
+ if (!p.script && !p.soundtrack) issue2("script", "Write what the avatar says, or choose saved speech or music for it to lip-sync.");
47099
+ if (p.soundtrack && p.voiceId) issue2("voiceId", "A saved audio track speaks in its own voice; leave out voiceId.");
47100
+ if (p.model === "heygen/avatar-iii" && p.motionPrompt) issue2("motionPrompt", "Avatar III doesn't take a motion prompt; use Avatar IV or Avatar V.");
47101
+ if (p.model !== "heygen/avatar-iv" && p.expressiveness) issue2("expressiveness", "Only Avatar IV takes expressiveness.");
47102
+ });
47103
+
46976
47104
  // shared/model-assets.ts
46977
47105
  var MODEL_GENERATORS = [
46978
47106
  {
@@ -47042,7 +47170,7 @@ var rigCostUsd = (clips2) => 0.2 + 0.12 * clips2;
47042
47170
  var modelCostUsd = (p) => generationCostUsd(p) + (p.rig ? rigCostUsd(p.animations.length) : 0);
47043
47171
 
47044
47172
  // shared/assets.ts
47045
- var ASSET_TYPES = ["image", "vector", "video", "music", "speech", "sfx", "model"];
47173
+ var ASSET_TYPES = ["image", "vector", "gif", "video", "music", "speech", "sfx", "model"];
47046
47174
  var SPEECH_MODELS = [
47047
47175
  { id: "gemini-3.8-flash-tts", label: "Gemini 3.8 Flash TTS", provider: "google" },
47048
47176
  { id: "gemini-3.8-flash-lite-tts", label: "Gemini 3.8 Flash-Lite TTS", provider: "google" },
@@ -47325,6 +47453,7 @@ var videoDefaults = (p) => {
47325
47453
  return { ...p, seconds: p.seconds ?? defaultSeconds(model, p.workflow), resolution: p.resolution ?? videoResolutions(model, p.workflow)[0], mode: p.mode ?? model.modes[0] };
47326
47454
  };
47327
47455
  var IMAGE_ASPECT_RATIOS = ["1:1", "3:2", "2:3", "16:9", "9:16"];
47456
+ var IMAGE_SIZES = { "1:1": [1024, 1024], "3:2": [1536, 1024], "2:3": [1024, 1536], "16:9": [1536, 864], "9:16": [864, 1536] };
47328
47457
  var MAX_IMAGE_REFERENCES = 4;
47329
47458
  var MAX_MASK_REGIONS = 8;
47330
47459
  var imageReferences = external_exports.array(blob).max(MAX_IMAGE_REFERENCES).refine((shas2) => new Set(shas2).size === shas2.length, "Choose each reference image once.").default([]);
@@ -47342,10 +47471,13 @@ var imageAsset = external_exports.object({
47342
47471
  var imageChecks = (p, ctx) => {
47343
47472
  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"] });
47344
47473
  };
47345
- var VECTOR_MODELS = [{ id: "fal/ideogram-vector", label: "Ideogram Vector (fal)" }];
47474
+ 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)" }];
47475
+ var VECTOR_MODEL_IDS = ["fal/ideogram-vector", "fal/gpt-image-2.6-vector"];
47476
+ var GPT_VECTOR_MODEL = "fal/gpt-image-2.6-vector";
47346
47477
  var VECTOR_WORKFLOWS = ["text-to-vector", "image-to-vector"];
47347
47478
  var VECTOR_QUALITIES = ["low", "medium", "high"];
47348
47479
  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"];
47480
+ var GPT_VECTOR_ASPECT_RATIOS = VECTOR_ASPECT_RATIOS.filter((ratio) => ratio !== "1:4" && ratio !== "4:1");
47349
47481
  var VECTOR_SHAPES = ["detailed", "simple"];
47350
47482
  var MAX_VECTOR_REFERENCES = 5;
47351
47483
  var VECTOR_REFERENCE_MAX_BYTES = 10 * 1024 * 1024;
@@ -47355,7 +47487,7 @@ var vectorAsset = external_exports.object({
47355
47487
  /** Image-to-vector takes no prompt. */
47356
47488
  prompt: external_exports.string().trim().max(4e3).default(""),
47357
47489
  type: external_exports.literal("vector"),
47358
- model: external_exports.literal("fal/ideogram-vector").default("fal/ideogram-vector"),
47490
+ model: external_exports.enum(VECTOR_MODEL_IDS).default("fal/ideogram-vector"),
47359
47491
  workflow: external_exports.enum(VECTOR_WORKFLOWS).default("text-to-vector"),
47360
47492
  /** Text-to-vector; defaults to medium. */
47361
47493
  quality: external_exports.enum(VECTOR_QUALITIES).optional(),
@@ -47368,11 +47500,24 @@ var vectorAsset = external_exports.object({
47368
47500
  /** Image-to-vector; defaults to detailed. */
47369
47501
  shapes: external_exports.enum(VECTOR_SHAPES).optional(),
47370
47502
  /** Text-to-vector. */
47371
- seed: external_exports.number().int().min(0).max(2 ** 32 - 1).optional()
47503
+ seed: external_exports.number().int().min(0).max(2 ** 32 - 1).optional(),
47504
+ /** GPT Image 2.6: draw the art alone, without a background (default false). */
47505
+ transparent: external_exports.boolean().optional()
47372
47506
  });
47373
47507
  function vectorChecks(p, ctx) {
47374
47508
  const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47375
47509
  if (new Set(p.references).size !== p.references.length) issue2("references", "Choose each reference image once.");
47510
+ if (p.model === GPT_VECTOR_MODEL) {
47511
+ 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).");
47512
+ if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
47513
+ 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).");
47514
+ if (p.source) issue2("source", "Only image-to-vector traces a saved image.");
47515
+ if (p.shapes) issue2("shapes", "Only image-to-vector takes shapes.");
47516
+ if (p.seed !== void 0) issue2("seed", "GPT Image 2.6 Vector doesn't take a seed.");
47517
+ 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.");
47518
+ return;
47519
+ }
47520
+ if (p.transparent !== void 0) issue2("transparent", "Only GPT Image 2.6 Vector (fal/gpt-image-2.6-vector) leaves out the background.");
47376
47521
  if (p.workflow === "text-to-vector") {
47377
47522
  if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
47378
47523
  if (p.source) issue2("source", "Only image-to-vector traces a saved image; use references to guide text-to-vector.");
@@ -47384,7 +47529,39 @@ function vectorChecks(p, ctx) {
47384
47529
  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.`);
47385
47530
  if (p.references.length) issue2("references", "image-to-vector traces its source alone and takes no references.");
47386
47531
  }
47387
- var vectorDefaults = (p) => p.workflow === "text-to-vector" ? { ...p, quality: p.quality ?? "medium", aspectRatio: p.aspectRatio ?? "auto" } : { ...p, shapes: p.shapes ?? "detailed" };
47532
+ 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" };
47533
+ var GIF_MODELS = [
47534
+ { id: "fal/gpt-image-2.6-gif", label: "GPT Image 2.6 Flare GIF (fal)" },
47535
+ { id: "fal/gpt-image-2.6-sunburst-gif", label: "GPT Image 2.6 Sunburst GIF (fal)" }
47536
+ ];
47537
+ var GIF_MODEL_IDS = ["fal/gpt-image-2.6-gif", "fal/gpt-image-2.6-sunburst-gif"];
47538
+ var DEFAULT_GIF_MODEL = "fal/gpt-image-2.6-gif";
47539
+ var GIF_QUALITIES = ["low", "medium", "high"];
47540
+ var GIF_FRAMES = { min: 2, max: 16, default: 9 };
47541
+ var GIF_FRAME_MS = { min: 20, max: 2e3, default: 200 };
47542
+ var MAX_GIF_REFERENCES = 4;
47543
+ var GIF_PIXEL_BUDGET = 3840 * 2160;
47544
+ function gifFrameSize(aspectRatio, frames) {
47545
+ const [w, h] = IMAGE_SIZES[aspectRatio];
47546
+ const scale = Math.min(1, Math.sqrt(GIF_PIXEL_BUDGET / Math.max(1, frames) / (w * h)));
47547
+ const width = Math.max(16, Math.floor(w * scale / 16) * 16);
47548
+ return [width, Math.max(16, Math.floor(width * h / w / 16) * 16)];
47549
+ }
47550
+ var gifAsset = external_exports.object({
47551
+ ...base,
47552
+ type: external_exports.literal("gif"),
47553
+ model: external_exports.enum(GIF_MODEL_IDS).default(DEFAULT_GIF_MODEL),
47554
+ aspectRatio: external_exports.enum(IMAGE_ASPECT_RATIOS).default("1:1"),
47555
+ frames: external_exports.number().int().min(GIF_FRAMES.min).max(GIF_FRAMES.max).default(GIF_FRAMES.default),
47556
+ /** How long each frame shows, in milliseconds; rounded to 10. */
47557
+ frameMs: external_exports.number().min(GIF_FRAME_MS.min).max(GIF_FRAME_MS.max).default(GIF_FRAME_MS.default),
47558
+ quality: external_exports.enum(GIF_QUALITIES).default("medium"),
47559
+ /** Frames without a background, for a sprite over a scene. */
47560
+ transparent: external_exports.boolean().default(false),
47561
+ /** Saved images or sprite art to animate or follow, named Image 1, Image 2… in the prompt. */
47562
+ references: external_exports.array(blob).max(MAX_GIF_REFERENCES).refine((shas2) => new Set(shas2).size === shas2.length, "Choose each reference image once.").default([])
47563
+ });
47564
+ var gifDefaults = (p) => ({ ...p, frameMs: Math.round(p.frameMs / 10) * 10 });
47388
47565
  var musicAsset = external_exports.object({
47389
47566
  name: base.name,
47390
47567
  /** A soundtrack may leave it out, and so may a song sung from lyrics, where it sets the style. */
@@ -47503,17 +47680,23 @@ var assetRequest = external_exports.discriminatedUnion("type", [
47503
47680
  }).transform((p) => ({ ...p, voice: p.voice || defaultSpeechVoice(p.model) })),
47504
47681
  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) }),
47505
47682
  vectorAsset.superRefine(vectorChecks).transform(vectorDefaults),
47506
- modelAsset.superRefine(modelChecks).transform(modelDefaults)
47683
+ modelAsset.superRefine(modelChecks).transform(modelDefaults),
47684
+ gifAsset.transform(gifDefaults)
47507
47685
  ]);
47508
47686
  var assetGenerationRequest = external_exports.discriminatedUnion("type", [imageAsset.omit({ model: true }).superRefine(imageChecks), ...assetRequest.options.slice(1)]);
47509
47687
  var LIBRARY_AUDIO_MODEL = "library-audio";
47510
47688
  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) });
47511
47689
  var UPLOADED_MODEL = "uploaded";
47512
47690
  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) });
47513
- var savedAssetOptions = external_exports.union([assetRequest, libraryAudio, uploadedMedia]);
47691
+ var savedAssetOptions = external_exports.union([assetRequest, avatarVideoRequest, libraryAudio, uploadedMedia]);
47514
47692
  var speechCharacters = (text2) => Array.from(text2).length;
47515
47693
  var imageEstimate = (p) => Math.ceil((130 + 40 * p.references.length) * spriteImageModel(p.model).relCost);
47516
- var vectorEstimate = (p) => p.workflow === "image-to-vector" ? 100 : p.quality === "low" ? 130 : p.quality === "high" ? p.references.length ? 320 : 200 : 160;
47694
+ 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;
47695
+ var GIF_UNITS_PER_MEGAPIXEL = { low: 13, medium: 39, high: 130 };
47696
+ function gifEstimate(p) {
47697
+ const [w, h] = gifFrameSize(p.aspectRatio, p.frames);
47698
+ return Math.ceil(p.frames * (w * h) / (1024 * 1024) * GIF_UNITS_PER_MEGAPIXEL[p.quality] + 12 * p.references.length);
47699
+ }
47517
47700
  function videoExtras(p, facts = {}) {
47518
47701
  const w = p.workflow;
47519
47702
  if (spriteVideoModel(p.model).provider === "heygen") {
@@ -47533,6 +47716,7 @@ var modelEstimate = (p) => Math.max(1, Math.ceil(modelCostUsd(p) / 1e-3));
47533
47716
  function assetEstimate(p, now = Date.now()) {
47534
47717
  if (p.type === "image") return imageEstimate(p);
47535
47718
  if (p.type === "vector") return vectorEstimate(p);
47719
+ if (p.type === "gif") return gifEstimate(p);
47536
47720
  if (p.type === "model") return modelEstimate(p);
47537
47721
  if (p.type === "video") return spriteVideoEstimate(spriteVideoModel(p.model), p, videoExtras(p));
47538
47722
  if (p.type === "music") return Math.ceil(musicCostUsd(p) / 1e-3);
@@ -48218,7 +48402,7 @@ ${entries.join(",\n")}
48218
48402
  }}
48219
48403
  `;
48220
48404
  }
48221
- async function token(context, access4) {
48405
+ async function token2(context, access4) {
48222
48406
  const result = await context.client.request(`/api/v1/cloud/${context.link.projectId}/token`, { method: "POST", body: { access: access4 } });
48223
48407
  const remote = new URL(result.remote);
48224
48408
  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.");
@@ -48385,7 +48569,7 @@ async function cloudStatus(options) {
48385
48569
  await saveState(context);
48386
48570
  let cloud = null;
48387
48571
  if (!options.offline) {
48388
- const latest = await fetchLatest(context, await token(context, "read"));
48572
+ const latest = await fetchLatest(context, await token2(context, "read"));
48389
48573
  cloud = { latest, newer: Boolean(latest && latest !== context.state.base) };
48390
48574
  }
48391
48575
  return {
@@ -48403,7 +48587,7 @@ async function cloudStatus(options) {
48403
48587
  async function pushCloud(options) {
48404
48588
  const context = await openContext(options.dir, options);
48405
48589
  return withLock(context.root, async () => {
48406
- const remote = await token(context, "write");
48590
+ const remote = await token2(context, "write");
48407
48591
  const latest = await fetchLatest(context, remote);
48408
48592
  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.`);
48409
48593
  const { files: scanned, skipped, tooLarge } = await scanFiles(context.root);
@@ -48461,7 +48645,7 @@ function defaultMessage(diff) {
48461
48645
  async function pullCloud(options) {
48462
48646
  const context = await openContext(options.dir, options);
48463
48647
  return withLock(context.root, async () => {
48464
- const latest = await fetchLatest(context, await token(context, "read"));
48648
+ const latest = await fetchLatest(context, await token2(context, "read"));
48465
48649
  if (!latest || latest === context.state.base) return { updated: false, version: context.state.base, message: "Already up to date." };
48466
48650
  const { files: scanned, tooLarge } = await scanFiles(context.root);
48467
48651
  const local = await describeFiles(context, scanned);
@@ -48512,7 +48696,7 @@ async function restoreCloud(options) {
48512
48696
  const context = await openContext(options.dir, options);
48513
48697
  return withLock(context.root, async () => {
48514
48698
  if (!/^[a-f0-9]{4,40}$/.test(options.version)) throw new Error("Pass a version ID from animaguru cloud versions.");
48515
- await fetchLatest(context, await token(context, "read"));
48699
+ await fetchLatest(context, await token2(context, "read"));
48516
48700
  let version4;
48517
48701
  try {
48518
48702
  version4 = await import_isomorphic_git.default.expandOid({ fs, gitdir: context.gitdir, oid: options.version });
@@ -48807,13 +48991,16 @@ var extras = {
48807
48991
  speech: ["voice", "delivery"],
48808
48992
  sfx: ["seconds", "loop", "prompt-influence"],
48809
48993
  model: ["reference", "texture", "pbr", "faces", "rig", "animations", "height", "splats", "photo", "seed"],
48810
- vector: ["workflow", "references", "source", "aspect-ratio", "quality", "shapes", "seed"]
48994
+ vector: ["workflow", "references", "source", "aspect-ratio", "quality", "shapes", "seed", "transparent"],
48995
+ gif: ["aspect-ratio", "frames", "frame-ms", "quality", "transparent", "references"]
48811
48996
  };
48997
+ var typeName = (type) => type === "gif" ? "GIF" : type;
48812
48998
  var list2 = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
48813
48999
  function accepted(type) {
48814
49000
  const options = extras[type];
48815
49001
  if (!options) return "";
48816
- return `${type[0].toUpperCase()}${type.slice(1)} assets accept ${list2(["--name", "--prompt", "--model", ...options.map((option) => `--${option}`)])}.`;
49002
+ const name2 = typeName(type);
49003
+ return `${name2[0].toUpperCase()}${name2.slice(1)} assets accept ${list2(["--name", "--prompt", "--model", ...options.map((option) => `--${option}`)])}.`;
48817
49004
  }
48818
49005
  function parseAsset(input2) {
48819
49006
  const value = input2 && typeof input2 === "object" && "type" in input2 && input2.type === "image" ? { ...input2, model: void 0 } : input2;
@@ -48843,10 +49030,10 @@ function assetFromFlags(flags) {
48843
49030
  for (const name2 of Object.values(extras).flat()) {
48844
49031
  const value = flags[name2];
48845
49032
  const given2 = value !== void 0 && (value !== false || (name2 === "texture" || name2 === "pbr") && flags.type !== "model");
48846
- if (given2 && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${flags.type} assets. ${accepted(flags.type)}`.trim());
49033
+ if (given2 && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${typeName(flags.type)} assets. ${accepted(flags.type)}`.trim());
48847
49034
  }
48848
49035
  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);
48849
- if (!promptOptional && 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" : flags.type === "music" ? " unless Mureka 9.5 Song sings --lyrics or Mureka 9.5 Soundtrack scores --source" : ""}.`);
49036
+ 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" : ""}.`);
48850
49037
  const base2 = { type: flags.type, name: flags.name, prompt: flags.prompt, model: flags.model };
48851
49038
  if (flags.type === "image") return parseAsset({ ...base2, aspectRatio: flags["aspect-ratio"], transparent: flags.transparent, references: shas(flags.references), mask: regions(flags.mask) });
48852
49039
  if (flags.type === "video") {
@@ -48886,7 +49073,8 @@ function assetFromFlags(flags) {
48886
49073
  if (flags.type === "sfx") return parseAsset({ ...base2, seconds: flags.seconds, loop: flags.loop, promptInfluence: flags["prompt-influence"] });
48887
49074
  if (flags.type === "music") return parseAsset({ ...base2, lyrics: flags.lyrics, styles: shas(flags.styles), vocals: flags.vocals, seconds: flags.seconds, source: flags.source });
48888
49075
  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 });
48889
- 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 });
49076
+ 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 });
49077
+ 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) });
48890
49078
  return parseAsset({ ...base2, ...flags.type === "speech" ? { voice: flags.voice, delivery: flags.delivery } : {} });
48891
49079
  }
48892
49080
  async function assetFromFile(file2, model) {
@@ -48982,6 +49170,7 @@ var MIME_EXTENSIONS = {
48982
49170
  "image/png": "png",
48983
49171
  "image/jpeg": "jpg",
48984
49172
  "image/webp": "webp",
49173
+ "image/gif": "gif",
48985
49174
  "image/svg+xml": "svg",
48986
49175
  "video/mp4": "mp4",
48987
49176
  "video/webm": "webm",
@@ -49101,96 +49290,6 @@ async function submitAssetBatch(client, project, requests, options, signal) {
49101
49290
  import { readFile as readFile11, stat as stat6 } from "node:fs/promises";
49102
49291
  import { extname } from "node:path";
49103
49292
  import { setTimeout as delay4 } from "node:timers/promises";
49104
-
49105
- // shared/avatars.ts
49106
- var AVATAR_OWNERSHIPS = ["public", "private"];
49107
- var AVATAR_LOOK_TYPES = ["studio_avatar", "digital_twin", "photo_avatar"];
49108
- var AVATAR_CREATE_TYPES = ["prompt", "photo", "digital_twin"];
49109
- var AVATAR_ASPECT_RATIOS = ["auto", "16:9", "9:16", "1:1", "4:5", "5:4"];
49110
- var LOOK_PACK_ASPECT_RATIOS = ["16:9", "9:16"];
49111
- var AVATAR_LOOK_METHODS = ["look_pack", "template"];
49112
- var AVATAR_CREDIT_UNITS = { digital_twin: 2130, photo: 1320, prompt: 1320 };
49113
- var AVATAR_LOOK_CREDIT_UNITS = 1320;
49114
- var LOOKS_PER_METHOD = { look_pack: 5, template: 2 };
49115
- var AVATAR_LOOK_TEMPLATES = [
49116
- { id: "5a8e6be843ee4321a014946bf62aa322", method: "look_pack", name: "Modern Corporate", variant: "female" },
49117
- { id: "5e659e76755d47fd9160a2d7dedce124", method: "look_pack", name: "Modern Corporate", variant: "male" },
49118
- { id: "e1650a9029d04ea1a350421c163295fd", method: "look_pack", name: "Stylish Business-Casual", variant: "female" },
49119
- { id: "a5f8e0c0cb654c1a83c695629b173af5", method: "look_pack", name: "Stylish Business-Casual", variant: "male" },
49120
- { id: "e7b000e2cb0d44f1a2d2f291a38b395a", method: "look_pack", name: "Scandinavian Minimal", variant: "female" },
49121
- { id: "9600f78f27bb49d792e1c56ce624b929", method: "look_pack", name: "Scandinavian Minimal", variant: "male" },
49122
- { id: "d7ad40ec97d4459a92b33398b57caf4e", method: "look_pack", name: "Cool Tech / Slate", variant: "female" },
49123
- { id: "fbd03fc3603a4e3caff594f5743a1733", method: "look_pack", name: "Cool Tech / Slate", variant: "male" },
49124
- { id: "14f4070bd5a04eb6b1f82ae087cd0f30", method: "look_pack", name: "Healthcare", variant: "female" },
49125
- { id: "ce706bfa8556401e8c4401765102a455", method: "look_pack", name: "Healthcare", variant: "male" },
49126
- { id: "b0cae4fbae384ab9ab9a8e73ba504be8", method: "look_pack", name: "Keynote Power Presenter", variant: "female" },
49127
- { id: "71e84769162d4f4186563ad9e0fc45b8", method: "look_pack", name: "Keynote Power Presenter", variant: "male" },
49128
- { id: "935ea0b7cdde4e6eb5a198a3d666f677", method: "look_pack", name: "Fitness", variant: "female" },
49129
- { id: "daeb8b15c92a4acf9ffb437ba5534458", method: "look_pack", name: "Fitness", variant: "male" },
49130
- { id: "6f183d726933457c9035fc0aa4e9c0c3", method: "look_pack", name: "Automotive Dealership", variant: "female" },
49131
- { id: "8595c78b466c4e51802731229c59b6e7", method: "look_pack", name: "Automotive Dealership", variant: "male" },
49132
- { id: "b30c83d99cec48e09c8f5b07ed8aa38c", method: "template", name: "Business Casual", variant: "female" },
49133
- { id: "2dc326030e2e4471babcc9eda56131b6", method: "template", name: "Business Casual", variant: "male" },
49134
- { id: "00c49daa4eb94386a0c12508043bab17", method: "template", name: "Podcast", variant: "female" },
49135
- { id: "0e52ceafe4d5456fb83c1e39899ff76a", method: "template", name: "Podcast", variant: "male" }
49136
- ];
49137
- var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
49138
- var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
49139
- var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
49140
- var name = external_exports.string().trim().min(1).max(80);
49141
- var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
49142
- var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
49143
- var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
49144
- var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
49145
- var avatarFile = external_exports.discriminatedUnion("type", [
49146
- external_exports.object({ type: external_exports.literal("file"), sha }),
49147
- external_exports.object({ type: external_exports.literal("url"), url: https }),
49148
- external_exports.object({
49149
- type: external_exports.literal("base64"),
49150
- mediaType: external_exports.enum([...AVATAR_PHOTO_TYPES, ...AVATAR_FOOTAGE_TYPES]),
49151
- 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.")
49152
- })
49153
- ]);
49154
- var subjectConsent = external_exports.literal(true, { error: "Confirm that the person shown agreed to become an avatar (subjectConsent: true)." });
49155
- var avatarCreateRequest = external_exports.discriminatedUnion("type", [
49156
- external_exports.object({
49157
- type: external_exports.literal("prompt"),
49158
- name,
49159
- prompt: external_exports.string().trim().min(3).max(1e3),
49160
- aspectRatio: external_exports.enum(AVATAR_ASPECT_RATIOS).default("auto"),
49161
- lookId: heygenId.optional(),
49162
- groupId: heygenId.optional(),
49163
- referenceImages: external_exports.array(avatarFile).max(3).default([])
49164
- }),
49165
- external_exports.object({ type: external_exports.literal("photo"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent }),
49166
- external_exports.object({ type: external_exports.literal("digital_twin"), name, file: avatarFile, groupId: heygenId.optional(), subjectConsent })
49167
- ]).superRefine((p, ctx) => {
49168
- const files = p.type === "prompt" ? p.referenceImages : [p.file];
49169
- const footage = p.type === "digital_twin";
49170
- for (const [index2, file2] of files.entries()) {
49171
- if (file2.type !== "base64") continue;
49172
- const allowed = footage ? AVATAR_FOOTAGE_TYPES : AVATAR_PHOTO_TYPES;
49173
- 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"] });
49174
- }
49175
- });
49176
- var avatarLooksRequest = external_exports.discriminatedUnion("method", [
49177
- external_exports.object({ method: external_exports.literal("look_pack"), lookId: heygenId, templateId: heygenId, aspectRatio: external_exports.enum(LOOK_PACK_ASPECT_RATIOS).default("16:9") }),
49178
- external_exports.object({ method: external_exports.literal("template"), lookId: heygenId, templateId: heygenId })
49179
- ]).superRefine((p, ctx) => {
49180
- const known = avatarTemplate(p.templateId);
49181
- if (known && known.method !== p.method) ctx.addIssue({ code: "custom", message: `This template is applied with method ${known.method}.`, path: ["method"] });
49182
- });
49183
- var avatarGroupUpdate = external_exports.object({ defaultVoiceId: external_exports.string().trim().min(1).max(255) });
49184
- var avatarLookUpdate = external_exports.object({ name: external_exports.string().trim().min(1).max(255) });
49185
- var avatarConsentRequest = external_exports.object({ rerouteUrl: https.optional() });
49186
- var limit = external_exports.coerce.number().int().min(1).max(50).default(20);
49187
- var token2 = external_exports.string().max(2048).optional();
49188
- var avatarListQuery = external_exports.object({ ownership: external_exports.enum(AVATAR_OWNERSHIPS).default("public"), limit, token: token2 });
49189
- var avatarLookListQuery = external_exports.object({ groupId: heygenId.optional(), avatarType: external_exports.enum(AVATAR_LOOK_TYPES).optional(), limit, token: token2 });
49190
- var avatarCreateEstimate = (p) => AVATAR_CREDIT_UNITS[p.type];
49191
- var avatarLooksEstimate = (p) => LOOKS_PER_METHOD[p.method] * AVATAR_LOOK_CREDIT_UNITS;
49192
-
49193
- // cli/src/avatars.ts
49194
49293
  var MEDIA_TYPES = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".mp4": "video/mp4", ".mov": "video/quicktime", ".webm": "video/webm" };
49195
49294
  var avatarsPath = (project) => `/api/v1/sprites/${encodeURIComponent(project)}/avatars`;
49196
49295
  function parse3(schema, input2) {
@@ -49225,6 +49324,24 @@ function looksFromFlags(flags) {
49225
49324
  if (flags["aspect-ratio"] && method !== "look_pack") throw new Error("--aspect-ratio applies only to Look Packs.");
49226
49325
  return parse3(avatarLooksRequest, { method, lookId: flags.look, templateId: flags.template, aspectRatio: flags["aspect-ratio"] });
49227
49326
  }
49327
+ async function avatarVideoFromFlags(flags) {
49328
+ if (flags.script && flags["script-file"]) throw new Error("Pass --script or --script-file, not both.");
49329
+ const script = flags["script-file"] ? (await readFile11(flags["script-file"], "utf8")).trim() : flags.script;
49330
+ 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.");
49331
+ return parse3(avatarVideoRequest, {
49332
+ type: "video",
49333
+ name: flags.name,
49334
+ lookId: flags.look,
49335
+ model: flags.model,
49336
+ script,
49337
+ soundtrack: flags.audio,
49338
+ voiceId: flags.voice,
49339
+ aspectRatio: flags["aspect-ratio"],
49340
+ resolution: flags.resolution,
49341
+ motionPrompt: flags["motion-prompt"],
49342
+ expressiveness: flags.expressiveness
49343
+ });
49344
+ }
49228
49345
  function authorize(options) {
49229
49346
  if (!options.yes) throw new Error("Pass --yes to authorize spending credits, or --dry-run to validate and estimate without creating anything.");
49230
49347
  }
@@ -49238,6 +49355,11 @@ async function submitLooks(client, project, body, options) {
49238
49355
  authorize(options);
49239
49356
  return client.request(`${avatarsPath(project)}/looks`, { method: "POST", body, timeout: 18e4 });
49240
49357
  }
49358
+ async function submitAvatarVideo(client, project, body, options) {
49359
+ if (options["dry-run"]) return client.request(`${avatarsPath(project)}/videos/estimate`, { method: "POST", body });
49360
+ authorize(options);
49361
+ return client.request(`${avatarsPath(project)}/videos`, { method: "POST", body });
49362
+ }
49241
49363
  async function waitForLook(client, project, lookId, timeout, signal, interval = 1e4) {
49242
49364
  if (!Number.isFinite(timeout) || timeout <= 0) throw new Error("Timeout must be positive.");
49243
49365
  const deadline = Date.now() + timeout * 1e3;
@@ -50943,6 +51065,7 @@ async function curl(options, io) {
50943
51065
  if (!headers.has("content-type")) headers.set("Content-Type", parsesAsJson(body) ? "application/json" : "application/x-www-form-urlencoded");
50944
51066
  }
50945
51067
  headers.set("Authorization", `Bearer ${await sessionToken(url3.origin)}`);
51068
+ if (!headers.has("user-agent")) headers.set("User-Agent", USER_AGENT);
50946
51069
  if (!headers.has("origin")) headers.set("Origin", url3.origin);
50947
51070
  const method = (options.request ?? (options.uploadFile !== void 0 ? "PUT" : body !== void 0 ? "POST" : "GET")).toUpperCase();
50948
51071
  const timeout = AbortSignal.timeout(options.maxTime * 1e3);
@@ -50981,7 +51104,7 @@ async function curl(options, io) {
50981
51104
  }
50982
51105
 
50983
51106
  // cli/src/commands.ts
50984
- 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];
51107
+ 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];
50985
51108
  var assetAuthorizationFlags = [
50986
51109
  { name: "yes", type: "boolean", description: "Authorize spending credits" },
50987
51110
  { name: "dry-run", type: "boolean", description: "Validate and estimate locally without submitting a paid job" }
@@ -50994,14 +51117,23 @@ var catalogSections = {
50994
51117
  sprites: ["styles", "kinds", "actions", "videoModels"],
50995
51118
  image: ["assetTypes"],
50996
51119
  vector: ["assetTypes", "vectorModels", "vectorOptions"],
51120
+ gif: ["assetTypes", "gifModels", "gifOptions"],
50997
51121
  video: ["assetTypes", "videoModels", "videoProfiles"],
50998
51122
  music: ["assetTypes", "musicModels", "musicOptions"],
50999
51123
  speech: ["assetTypes", "speechModels", "voices", "elevenLabsVoices", "heygenVoices"],
51000
51124
  sfx: ["assetTypes", "sfxModels"],
51001
51125
  model: ["assetTypes", "modelGenerators", "modelAnimations"],
51002
- avatars: ["avatarTypes", "avatarLookTypes"]
51126
+ avatars: ["avatarTypes", "avatarLookTypes", "avatarVideoModels"]
51127
+ };
51128
+ var gifOptions = {
51129
+ frames: GIF_FRAMES,
51130
+ frameMs: GIF_FRAME_MS,
51131
+ qualities: GIF_QUALITIES,
51132
+ aspectRatios: IMAGE_ASPECT_RATIOS,
51133
+ maxReferences: MAX_GIF_REFERENCES,
51134
+ 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)]))]))
51003
51135
  };
51004
- 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, shapes: VECTOR_SHAPES, maxReferences: MAX_VECTOR_REFERENCES }, elevenLabsVoices: ELEVENLABS_VOICES, heygenVoices: HEYGEN_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS, avatarTypes: AVATAR_CREATE_TYPES, avatarLookTypes: AVATAR_LOOK_TYPES });
51136
+ 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 });
51005
51137
  var modelFlags = [
51006
51138
  { name: "video-model", type: "string", choices: SPRITE_VIDEO_MODEL_IDS, description: "Video model override for this operation" }
51007
51139
  ];
@@ -51221,8 +51353,8 @@ Confirm code: ${device.user_code}`);
51221
51353
  else if (e.code !== "authorization_pending") throw e;
51222
51354
  }
51223
51355
  }
51224
- throw new Error("Device code expired. Run auth login again.");
51225
- }))).add(defineCommand("status", { description: "Show the signed-in user" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request("/api/v1/me"))))).add(defineCommand("logout", { description: "Revoke the CLI session and remove its saved credentials" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
51356
+ throw new Error(`Device code expired. Run auth login again. If the browser said the CLI needs a plan, choose one at ${client.origin}/app/billing first: every account starts with a 7-day Individual Starter trial, and members of a team on the Team plan are covered by it.`);
51357
+ }))).add(defineCommand("status", { description: "Show the signed-in user, its plan or Starter trial, and whether the CLI can act for it (agents)" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request("/api/v1/me"))))).add(defineCommand("logout", { description: "Revoke the CLI session and remove its saved credentials" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
51226
51358
  const client = new Client(flags.url, signal);
51227
51359
  try {
51228
51360
  await client.request("/api/auth/sign-out", { method: "POST", body: {} });
@@ -51248,7 +51380,7 @@ Confirm code: ${device.user_code}`);
51248
51380
  })))).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 }) => {
51249
51381
  const client = new Client(flags.url, signal);
51250
51382
  out(stdout, await client.request(`/api/v1/credits${await workspaceQuery(client)}`));
51251
- }))).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 }) => {
51383
+ }))).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 }) => {
51252
51384
  const full = catalog();
51253
51385
  const section = flags.type;
51254
51386
  out(stdout, section ? Object.fromEntries(catalogSections[section].map((key) => [key, full[key]])) : full);
@@ -51319,21 +51451,21 @@ Confirm code: ${device.user_code}`);
51319
51451
  ).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 }) => {
51320
51452
  if (!flags.yes) throw new Error("Pass --yes to delete this project.");
51321
51453
  out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
51322
- })))).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(
51454
+ })))).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(
51323
51455
  { name: "type", type: "string", required: true, choices: ASSET_TYPES },
51324
51456
  { name: "name", type: "string", required: true },
51325
51457
  { 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" },
51326
- { name: "model", type: "string", choices: assetModelIds, description: "Model for the chosen asset type" },
51327
- { 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" },
51328
- { name: "transparent", type: "boolean", description: "Image: transparent background" },
51329
- { 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)" },
51458
+ { 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)" },
51459
+ { 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" },
51460
+ { name: "transparent", type: "boolean", description: "Image or GIF: transparent background. Vector with --model fal/gpt-image-2.6-vector: leave out the background" },
51461
+ { 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)" },
51330
51462
  { 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" },
51331
51463
  { 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" },
51332
- { 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" },
51464
+ { 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" },
51333
51465
  { name: "end-reference", type: "string", description: "Video: ending image blob SHA; models with endFrameOnly accept it without --reference" },
51334
51466
  { name: "middle-reference", type: "string", description: "Reference-to-video: image blob SHA reached at --middle-seconds; needs --reference and --end-reference, 480p or 768p" },
51335
51467
  { name: "middle-seconds", type: "number", description: "Reference-to-video: when the middle image is reached, in seconds" },
51336
- { 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. Music with --model fal/mureka-9.5-soundtrack: the video (up to 100 MB), image or sprite art to score; a video's soundtrack runs as long as the video" },
51468
+ { 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" },
51337
51469
  { 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" },
51338
51470
  { 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" },
51339
51471
  { 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" },
@@ -51370,7 +51502,9 @@ Confirm code: ${device.user_code}`);
51370
51502
  { name: "splats", type: "number", description: "Model with --model idol: how many Gaussian splats, 32,768\u20131,048,576 (default 262,144)" },
51371
51503
  { name: "photo", type: "boolean", description: "Model with --model idol: --reference is a camera photo, cropped more loosely than a render or generated image" },
51372
51504
  { name: "prompt-influence", type: "number", description: "SFX: prompt adherence, 0\u20131 (default 0.3)" },
51373
- { 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)" },
51505
+ { 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" },
51506
+ { 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)` },
51507
+ { 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` },
51374
51508
  { name: "shapes", type: "string", choices: VECTOR_SHAPES, description: "Vector image-to-vector (10 credits): detailed (default) keeps fine detail, simple uses fewer, simpler shapes" },
51375
51509
  ...assetAuthorizationFlags,
51376
51510
  ...assetWaitFlags,
@@ -51391,7 +51525,7 @@ Confirm code: ${device.user_code}`);
51391
51525
  const result = await submitAssetBatch(new Client(flags.url, signal), args.id, await assetsFromBatchFile(flags.file, flags.model), flags, signal);
51392
51526
  out(stdout, result);
51393
51527
  if (result.error) throw new Error(result.error);
51394
- }))).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 }) => {
51528
+ }))).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 }) => {
51395
51529
  if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
51396
51530
  out(stdout, await new Client(flags.url, signal).request(flags.look ? `${avatarsPath(args.id)}/looks/${encodeURIComponent(flags.look)}` : `${avatarsPath(args.id)}/${encodeURIComponent(flags.group)}`));
51397
51531
  }))).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(
@@ -51399,19 +51533,32 @@ Confirm code: ${device.user_code}`);
51399
51533
  { name: "name", type: "string", required: true },
51400
51534
  { name: "prompt", type: "string", description: "Prompt: the character, wardrobe, setting and light, up to 1000 characters" },
51401
51535
  { name: "aspect-ratio", type: "string", choices: AVATAR_ASPECT_RATIOS, description: "Prompt: image shape (default auto)" },
51402
- { name: "look", type: "string", description: "Prompt: one of this project's looks to put in a new outfit or setting" },
51536
+ { name: "look", type: "string", description: "Prompt: one of this workspace's looks to put in a new outfit or setting" },
51403
51537
  { name: "reference-images", type: "string", description: "Prompt: up to 3 comma-separated wardrobe or style references (project image SHA, HTTPS URL or local file)" },
51404
51538
  { 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)" },
51405
- { name: "group", type: "string", description: "Add the result to one of this project's avatar groups" },
51539
+ { name: "group", type: "string", description: "Add the result to one of this workspace's avatar groups" },
51406
51540
  { name: "subject-consent", type: "boolean", description: "Photo or digital_twin: confirm the person shown agreed to become an avatar" },
51407
51541
  ...assetAuthorizationFlags
51408
- ).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(
51409
- { name: "look", type: "string", required: true, description: "Reference look ID from this project's avatars" },
51542
+ ).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(
51543
+ { name: "look", type: "string", required: true, description: "Reference look ID from this workspace's avatars" },
51410
51544
  { name: "template", type: "string", required: true, description: "Template ID from avatars templates" },
51411
51545
  { name: "method", type: "string", choices: AVATAR_LOOK_METHODS, description: "Needed only for templates outside the gallery" },
51412
51546
  { name: "aspect-ratio", type: "string", choices: LOOK_PACK_ASPECT_RATIOS, description: "Look Pack: 16:9 (default) or 9:16, cropped to portrait" },
51413
51547
  ...assetAuthorizationFlags
51414
- ).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 }) => {
51548
+ ).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(
51549
+ { name: "look", type: "string", required: true, description: "A completed look ID: one of this workspace's avatars or a public one" },
51550
+ { name: "name", type: "string", required: true, description: "Name of the saved video" },
51551
+ { name: "script", type: "string", description: `What the avatar says, up to ${AVATAR_SCRIPT_MAX} characters` },
51552
+ { name: "script-file", type: "string", description: "Read the script from a UTF-8 text file" },
51553
+ { 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` },
51554
+ { name: "voice", type: "string", description: "Voice for the script (avatars voices); the look's own voice by default" },
51555
+ { name: "model", type: "string", choices: AVATAR_VIDEO_MODEL_IDS, description: "Rendering engine the look supports (default heygen/avatar-iv)" },
51556
+ { 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" },
51557
+ { name: "resolution", type: "string", choices: AVATAR_VIDEO_RESOLUTIONS, description: "720p or 1080p (default)" },
51558
+ { name: "motion-prompt", type: "string", description: "Body and hand movement: photo avatars on Avatar IV, any look on Avatar V" },
51559
+ { name: "expressiveness", type: "string", choices: AVATAR_EXPRESSIVENESS, description: "Photo avatars on Avatar IV: how much they move" },
51560
+ ...assetAuthorizationFlags
51561
+ ).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 }) => {
51415
51562
  if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
51416
51563
  if (!flags.yes) throw new Error("Pass --yes to delete the avatar for good.");
51417
51564
  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" }));