@docyrus/animaguru 0.36.0 → 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/README.md +58 -9
- package/dist/animaguru.js +274 -130
- package/package.json +1 -1
- package/skills/animaguru/SKILL.md +18 -3
- package/skills/animaguru/references/avatars.md +15 -2
- package/skills/animaguru-video/SKILL.md +19 -1
- package/templates/video/src/kit/gif.ts +40 -0
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ The npm package requires Node.js 24+. Its default registry is https://registry.n
|
|
|
29
29
|
|
|
30
30
|
From this repository, `npm ci && npm run cli:build` builds the CLI and `npm run cli -- --help` runs it. `npm run cli:release` prepares the npm tarball and public Mac downloads. `npm run cli:publish` publishes that reviewed release under the Docyrus organization; publishing requires a logged-in npm account with organization access and any required 2FA. Each release needs a new version in `cli/package.json`; the CLI version is read from that file. `npm run deploy` builds release downloads before uploading the site.
|
|
31
31
|
|
|
32
|
-
The command `animaguru schema` returns the Crust command schema, and `animaguru catalog` lists supported art styles, actions, asset types, models, video profiles and speech voices; `catalog --type music` (or `sprites`, `image`, `vector`, `video`, `speech`, `sfx`, `model`, `avatars`) returns only that part. Shell completions: `animaguru completion zsh` (also bash and fish).
|
|
32
|
+
The command `animaguru schema` returns the Crust command schema, and `animaguru catalog` lists supported art styles, actions, asset types, models, video profiles and speech voices; `catalog --type music` (or `sprites`, `image`, `vector`, `gif`, `video`, `speech`, `sfx`, `model`, `avatars`) returns only that part. Shell completions: `animaguru completion zsh` (also bash and fish).
|
|
33
33
|
|
|
34
34
|
## Required updates
|
|
35
35
|
|
|
@@ -65,6 +65,8 @@ CLI 0.35.0 adds photoreal Gaussian splats: `assets generate --type model --model
|
|
|
65
65
|
|
|
66
66
|
CLI 0.36.0 adds Mureka 9.5 music on fal, HeyGen Voice speech and Seedance video on BytePlus (`--model byteplus/seedance-2.5`, `-2.0`, `-2.0-fast` or `-2.0-mini`). `--type music --model fal/mureka-9.5-song` sings `--lyrics` with `--vocals female|male` or writes a song from the prompt in `--styles`, for `--seconds` 120–300 or a length of its own; `fal/mureka-9.5-instrumental` writes from the prompt; `fal/mureka-9.5-soundtrack --source SHA` scores a saved video, image or sprite art. `--type speech --model heygen-voice-1` or `heygen-voice-1-turbo` speaks a HeyGen public voice ID given as `--voice` (Nadine by default). `catalog` lists `musicOptions` and `heygenVoices`. See [image, video, music and speech generation](#image-video-music-and-speech-generation).
|
|
67
67
|
|
|
68
|
+
CLI 0.38.0 adds animated GIFs and GPT Image 2.6 vectors, both drawn by GPT Image 2.6 on fal. `--type gif` draws a short looping animation, such as a sprite's walk cycle, an icon that moves or a sticker, with `--frames` (2–16), `--frame-ms`, `--quality`, `--aspect-ratio`, `--transparent` and up to four `--references` to animate; it downloads as `.gif`, and its SHA works as a video frame or reference, through its first frame. `--type vector --model fal/gpt-image-2.6-vector` draws an SVG from the prompt alone and `--transparent` leaves out its background. `catalog --type gif` lists the models, limits and each frame's size. See [GIFs](#gifs) and [Vectors](#vectors).
|
|
69
|
+
|
|
68
70
|
CLI 0.16.2 also installs `animaguru-art-motion` for Codex and Claude Code: one skill with 36 English art-treatment references and nine explainer motion grammars, plus AnimaGuru generation, timing, camera and review guidance. Request it explicitly for a custom film treatment; its references are separate from the gallery presets. Source coverage and the upstream MIT notice are included in the skill.
|
|
69
71
|
|
|
70
72
|
CLI 0.5.2 includes a complete local PixiJS video workspace, now with 60 visual styles:
|
|
@@ -367,7 +369,7 @@ animaguru assets generate PROJECT_ID --type video --model byteplus/seedance-2.5
|
|
|
367
369
|
|
|
368
370
|
### Vectors
|
|
369
371
|
|
|
370
|
-
CLI 0.27.0 adds vectors: editable SVG files drawn by Ideogram on fal.ai (`fal/ideogram-vector`), for logos, icons, mascots and flat illustrations that stay sharp at any size and can be animated shape by shape.
|
|
372
|
+
CLI 0.27.0 adds vectors: editable SVG files drawn by Ideogram on fal.ai (`fal/ideogram-vector`), for logos, icons, mascots and flat illustrations that stay sharp at any size and can be animated shape by shape. CLI 0.38.0 adds a second model, GPT Image 2.6 on fal (`fal/gpt-image-2.6-vector`).
|
|
371
373
|
|
|
372
374
|
```sh
|
|
373
375
|
# Draw from a prompt; --references guide the look, the first one leading.
|
|
@@ -378,9 +380,44 @@ animaguru assets generate PROJECT_ID --type vector --name "Fox mascot" \
|
|
|
378
380
|
# Trace a saved image or sprite into shapes; it takes no prompt.
|
|
379
381
|
animaguru assets generate PROJECT_ID --type vector --name "Logo" \
|
|
380
382
|
--workflow image-to-vector --source IMAGE_SHA --shapes simple --dry-run
|
|
383
|
+
|
|
384
|
+
# GPT Image 2.6 draws from the prompt alone; --transparent leaves out the background.
|
|
385
|
+
animaguru assets generate PROJECT_ID --type vector --model fal/gpt-image-2.6-vector \
|
|
386
|
+
--name "Coffee badge" --prompt "A round coffee shop badge, two flat colors, bold outlines" \
|
|
387
|
+
--quality medium --aspect-ratio 1:1 --transparent --dry-run
|
|
381
388
|
```
|
|
382
389
|
|
|
383
|
-
`--workflow` is `text-to-vector` (default) or `image-to-vector`. From text, `--quality` is `low` (13 credits), `medium` (default, 16) or `high` (20, or 32 with references); `--aspect-ratio` is `auto` (default, the model's choice) or a ratio from 1:4 to 4:1 (`catalog --type vector` lists them); `--references` takes up to five of this project's images or sprite art of up to 10 MB each, named Image 1, Image 2… in the prompt; `--seed` repeats a result. `image-to-vector` traces `--source`, one of this project's images or sprite art of up to 50 MB, for 10 credits: `--shapes detailed` (default) keeps fine detail and `simple` uses fewer, simpler shapes. The result is saved as `image/svg+xml` and downloads as `NAME.svg`.
|
|
390
|
+
With Ideogram, `--workflow` is `text-to-vector` (default) or `image-to-vector`. From text, `--quality` is `low` (13 credits), `medium` (default, 16) or `high` (20, or 32 with references); `--aspect-ratio` is `auto` (default, the model's choice) or a ratio from 1:4 to 4:1 (`catalog --type vector` lists them); `--references` takes up to five of this project's images or sprite art of up to 10 MB each, named Image 1, Image 2… in the prompt; `--seed` repeats a result. `image-to-vector` traces `--source`, one of this project's images or sprite art of up to 50 MB, for 10 credits: `--shapes detailed` (default) keeps fine detail and `simple` uses fewer, simpler shapes. The result is saved as `image/svg+xml` and downloads as `NAME.svg`.
|
|
391
|
+
|
|
392
|
+
GPT Image 2.6 Vector (`--model fal/gpt-image-2.6-vector`) draws a picture from `--prompt` at `--quality low|medium|high` and converts it to paths. It only draws from text: it takes no `--references`, `--seed`, `--workflow image-to-vector`, `--source` or `--shapes`, and `--aspect-ratio` is `auto` (default) or a ratio from 1:3 to 3:1 (`gptAspectRatios` in `catalog --type vector`). `--transparent` leaves out the background; Ideogram always draws one and refuses the flag. fal publishes no price per SVG, so the dry run holds 8, 12 or 22 credits by quality, and the job is charged the tokens fal meters for the picture and its paths. Choose Ideogram to follow reference images or to trace one.
|
|
393
|
+
|
|
394
|
+
Vectors can't be used as image or video references; trace or draw one from an image instead. GIFs can, through their first frame (see [GIFs](#gifs)).
|
|
395
|
+
|
|
396
|
+
### GIFs
|
|
397
|
+
|
|
398
|
+
CLI 0.38.0 adds animated GIFs drawn by GPT Image 2.6 on fal.ai: short loops such as a sprite's walk or idle cycle, an icon that moves, or a sticker. `fal/gpt-image-2.6-gif` (Flare, the default) is faster; `fal/gpt-image-2.6-sunburst-gif` (Sunburst) draws finer detail more slowly, at the same token prices.
|
|
399
|
+
|
|
400
|
+
```sh
|
|
401
|
+
# Nine square frames at 120 ms each: a loop of just over a second.
|
|
402
|
+
animaguru assets generate PROJECT_ID --type gif --name "Fox walk" \
|
|
403
|
+
--prompt "A cartoon fox walk cycle, side view, flat colors, the same fox in every frame" \
|
|
404
|
+
--frames 9 --frame-ms 120 --transparent --dry-run
|
|
405
|
+
|
|
406
|
+
# Animate a saved character: Image 1 leads, as in an image edit.
|
|
407
|
+
animaguru assets generate PROJECT_ID --type gif --model fal/gpt-image-2.6-sunburst-gif \
|
|
408
|
+
--name "Fox wave" --prompt "The fox from Image 1 waves its paw and smiles, then holds still" \
|
|
409
|
+
--references CHARACTER_SHA --frames 6 --quality high --download loops/ --yes
|
|
410
|
+
|
|
411
|
+
# Start a video on the GIF's first frame.
|
|
412
|
+
animaguru assets generate PROJECT_ID --type video --name "Fox walks off" \
|
|
413
|
+
--prompt "The fox walks out of the frame to the right" --reference GIF_SHA --dry-run
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`--prompt` is required (3–4,000 characters). `--frames` is 2–16 (default 9), and `--frame-ms` is how long each frame shows, 20–2,000 ms (default 200), rounded to 10 ms. `--aspect-ratio` is 1:1 (default), 3:2, 2:3, 16:9 or 9:16, and `--quality` low, medium (default) or high. `--transparent` draws the frames without a background, for a sprite over a scene. `--references` takes up to four of this project's images, GIF stills or sprite art to animate or follow, named Image 1, Image 2… in the prompt.
|
|
417
|
+
|
|
418
|
+
All frames share the pixels of one picture, 3,840 × 2,160, so the more frames, the smaller each one: nine square frames are 960 × 960 and sixteen are 720 × 720. `catalog --type gif` lists `gifOptions.frameSizes` for every shape and frame count. The dry run holds credits at GPT Image 2.5's still prices for those frames, plus a little for each reference (about 31 credits for nine square frames at medium); fal meters the tokens actually used and that is what the job is charged.
|
|
419
|
+
|
|
420
|
+
A saved GIF is `image/gif` and downloads as `NAME.gif`. `assets list` gives its `result.gif` (`width`, `height`, `frames`, `durationMs`) and `preview`, the SHA of a PNG still of its first frame. Either SHA works wherever a saved image does: a video's `--reference`, `--end-reference`, `--middle-reference`, `--references`, `--keyframes` or `--lighting`, an image's or a vector's `--references`, the image a vector traces (`--source`), a 3D model's `--reference`, and the picture a Mureka soundtrack scores. Each uses the first frame. In a film workspace, play the GIF itself with the kit's `gifClip()`; the `animaguru-video` skill shows how.
|
|
384
421
|
|
|
385
422
|
Images have a full background by default; `--transparent` requests a cutout. CLI 0.17.0 adds `--references`, `--mask` and the 16:9 and 9:16 shapes. `--aspect-ratio` is 1:1 (default, 1024×1024), 3:2 (1536×1024), 2:3 (1024×1536), 16:9 (1536×864) or 9:16 (864×1536). `--references` takes up to four comma-separated blob SHAs of this project's images (including attached uploads) or sprite art, and the new image follows them: generate a character sheet once, then reference it in every scene to keep the character consistent. Refer to them in the prompt as Image 1, Image 2 and so on, and say what to take from each. Each reference adds a little to the estimate. `--mask` edits only part of Image 1, the first reference: give up to eight rectangles as `x,y,width,height` fractions of its size from the top left, separated by semicolons (`0,0,1,0.4` is the top 40%). The rest of the image is kept, and the result keeps Image 1's shape, which must be one of the five above. Gemini speech's `--prompt` is the verbatim transcript, and `--delivery` controls how it sounds without being spoken. Gemini speech supports both `gemini-3.8-flash-tts` and `gemini-3.8-flash-lite-tts`. Music uses Lyria. Google audio requires the server's `AI_PROVIDER_KEY_GOOGLE` secret; no provider keys are stored in the CLI.
|
|
386
423
|
|
|
@@ -404,7 +441,7 @@ animaguru assets submit PROJECT_ID --file speech.json --model gemini-3.8-flash-t
|
|
|
404
441
|
|
|
405
442
|
`--model` overrides the file's model. Unrelated type-specific flags are rejected rather than ignored, and the error lists the flags that type accepts.
|
|
406
443
|
|
|
407
|
-
`assets generate` and `assets submit` accept `--wait` to block until the job finishes (failing if generation fails) and `--download PATH` to wait and then save the result; `--timeout` sets the wait in seconds (default 600). A `PATH` that is an existing folder or ends in `/` receives `NAME.EXT`, named after the asset and its type (`.wav`, `.mp3`, `.png`, `.svg`…). The JSON output adds `job`, `asset` (`id`, `name`, `type`, `mimeType`) and `download` (`path`, `size`) to the submission receipt.
|
|
444
|
+
`assets generate` and `assets submit` accept `--wait` to block until the job finishes (failing if generation fails) and `--download PATH` to wait and then save the result; `--timeout` sets the wait in seconds (default 600). A `PATH` that is an existing folder or ends in `/` receives `NAME.EXT`, named after the asset and its type (`.wav`, `.mp3`, `.png`, `.svg`, `.gif`…). The JSON output adds `job`, `asset` (`id`, `name`, `type`, `mimeType`) and `download` (`path`, `size`) to the submission receipt.
|
|
408
445
|
|
|
409
446
|
```sh
|
|
410
447
|
animaguru assets generate PROJECT_ID --type speech --name vo-01-hook \
|
|
@@ -427,7 +464,7 @@ Every item is validated before anything is sent, names must be distinct, and a b
|
|
|
427
464
|
|
|
428
465
|
## HeyGen avatars
|
|
429
466
|
|
|
430
|
-
CLI 0.13.0 adds `avatars` for HeyGen's avatar API. A group is a character; its looks are outfits, poses or styles, and a look ID is what HeyGen renders with. Any project can browse HeyGen's public catalog. An avatar you create belongs to
|
|
467
|
+
CLI 0.13.0 adds `avatars` for HeyGen's avatar API. A group is a character; its looks are outfits, poses or styles, and a look ID is what HeyGen renders with. Any project can browse HeyGen's public catalog. An avatar you create belongs to its project's workspace: every project there lists, uses and changes it, and no other workspace can see, change or render it. The voice HeyGen clones for a digital twin belongs to the workspace the same way.
|
|
431
468
|
|
|
432
469
|
```sh
|
|
433
470
|
animaguru avatars list PROJECT_ID --limit 10
|
|
@@ -438,7 +475,7 @@ animaguru avatars list PROJECT_ID --ownership private
|
|
|
438
475
|
animaguru avatars create PROJECT_ID --type prompt --name "Ada" \
|
|
439
476
|
--prompt "Woman in her early 30s, warm smile, navy blazer, bright studio, soft key light" \
|
|
440
477
|
--aspect-ratio 16:9 --dry-run
|
|
441
|
-
# A new outfit or setting for one of the
|
|
478
|
+
# A new outfit or setting for one of the workspace's avatars.
|
|
442
479
|
animaguru avatars create PROJECT_ID --type prompt --name "Ada, keynote" \
|
|
443
480
|
--look LOOK_ID --prompt "On a conference stage under a spotlight" --yes
|
|
444
481
|
# A photo avatar from a saved image, an HTTPS URL or a local file.
|
|
@@ -449,7 +486,7 @@ animaguru avatars wait PROJECT_ID LOOK_ID
|
|
|
449
486
|
|
|
450
487
|
`create` and `add-looks` return the new look IDs. Looks start `processing`; `avatars wait` polls until HeyGen finishes (`completed`) or fails. `--file` and `--reference-images` take a project image's SHA (from `assets list`, or a character's `art`), an HTTPS URL HeyGen downloads (up to 32 MB), or a local file sent inline (up to 8 MB). Photos and footage show a real person: pass `--subject-consent` only when that person agreed to become an avatar.
|
|
451
488
|
|
|
452
|
-
A digital twin trains on footage of a real person (about two minutes of natural talking: a project video SHA, a URL or a local file) and clones their voice. It renders only after that person records consent on camera:
|
|
489
|
+
A digital twin trains on footage of a real person (about two minutes of natural talking: a project video SHA, a URL or a local file) and clones their voice, which only this workspace can use. It renders only after that person records consent on camera:
|
|
453
490
|
|
|
454
491
|
```sh
|
|
455
492
|
animaguru avatars create PROJECT_ID --type digital_twin --name "Sam" \
|
|
@@ -458,7 +495,7 @@ animaguru avatars consent PROJECT_ID GROUP_ID # a 24-hour link for that per
|
|
|
458
495
|
animaguru avatars get PROJECT_ID --group GROUP_ID # consentStatus: pending, accepted or rejected
|
|
459
496
|
```
|
|
460
497
|
|
|
461
|
-
Look Packs dress one of the
|
|
498
|
+
Look Packs dress one of the workspace's looks in a themed set of five looks; a single template makes two. `avatars templates` lists their IDs, each in a female and a male variant:
|
|
462
499
|
|
|
463
500
|
```sh
|
|
464
501
|
animaguru avatars templates
|
|
@@ -466,7 +503,19 @@ animaguru avatars add-looks PROJECT_ID --look LOOK_ID \
|
|
|
466
503
|
--template 5a8e6be843ee4321a014946bf62aa322 --aspect-ratio 9:16 --yes
|
|
467
504
|
```
|
|
468
505
|
|
|
469
|
-
Credits are charged once HeyGen accepts a request: 132 for a photo or prompt avatar, 213 for a digital twin, and 132 for each generated look (a Look Pack makes five). `--dry-run` validates and estimates offline; the server checks files, avatar ownership and credits before calling HeyGen. Paid requests carry a retry key, so a retried request is not charged twice. `avatars rename PROJECT_ID LOOK_ID --name NAME` and `avatars voice PROJECT_ID GROUP_ID --voice VOICE_ID` change the
|
|
506
|
+
Credits are charged once HeyGen accepts a request: 132 for a photo or prompt avatar, 213 for a digital twin, and 132 for each generated look (a Look Pack makes five). `--dry-run` validates and estimates offline; the server checks files, avatar ownership and credits before calling HeyGen. Paid requests carry a retry key, so a retried request is not charged twice. `avatars rename PROJECT_ID LOOK_ID --name NAME` and `avatars voice PROJECT_ID GROUP_ID --voice VOICE_ID` change the workspace's own avatars; a default voice must be public or one of the workspace's cloned voices. `avatars delete PROJECT_ID --group GROUP_ID --yes` (or `--look LOOK_ID`) deletes for good, the voice cloned for a digital twin included; deleting an avatar's last look deletes the avatar. The server needs `AI_PROVIDER_KEY_HEYGEN`.
|
|
507
|
+
|
|
508
|
+
CLI 0.37.0 adds avatar videos. `avatars video` films one completed look speaking a script (up to 1,500 characters) or lip-syncing saved speech or music (`--audio SHA`, up to 120 seconds), and saves the clip to the project as a video asset. A script speaks in the look's own voice unless `--voice` picks a public voice or one of the workspace's cloned voices; `avatars voices` lists both. `--model` is the engine and must be one the look's `supportedEngines` list.
|
|
509
|
+
|
|
510
|
+
```sh
|
|
511
|
+
animaguru avatars voices PROJECT_ID --language English --gender female
|
|
512
|
+
animaguru avatars video PROJECT_ID --look LOOK_ID --name "Welcome" \
|
|
513
|
+
--script "Hi, I'm Ada. Here is what we shipped this week." --aspect-ratio 9:16 --dry-run
|
|
514
|
+
animaguru avatars video PROJECT_ID --look LOOK_ID --name "Voice-over cut" --audio SPEECH_SHA --model heygen/avatar-v --yes
|
|
515
|
+
animaguru jobs wait PROJECT_ID JOB_ID
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The dry run asks the server, which checks the look, voice and audio and measures the audio. A video is charged for the seconds HeyGen makes: about 6.7 credits a second on Avatar IV and V (5 for photo avatars on Avatar IV) and 1.7 for digital twins and studio avatars on Avatar III, so a 30-second clip on Avatar IV costs about 200 credits.
|
|
470
519
|
|
|
471
520
|
## Typed job files
|
|
472
521
|
|
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.
|
|
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.
|
|
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.
|
|
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" } },
|
|
@@ -45332,7 +45334,7 @@ var Client = class {
|
|
|
45332
45334
|
origin;
|
|
45333
45335
|
async request(path, options = {}) {
|
|
45334
45336
|
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);
|
|
45337
|
+
const generation = options.method === "POST" && /^\/api\/v1\/sprites\/[^/]+\/(jobs|assets|avatars|avatars\/looks|avatars\/videos|avatars\/[^/]+\/consent)$/.test(path);
|
|
45336
45338
|
const idempotency = generation ? { "Idempotency-Key": generationRequestId(path, options.body) } : {};
|
|
45337
45339
|
const response = await fetch(`${this.origin}${path}`, {
|
|
45338
45340
|
method: options.method ?? "GET",
|
|
@@ -46973,6 +46975,130 @@ async function createAnimation(options) {
|
|
|
46973
46975
|
};
|
|
46974
46976
|
}
|
|
46975
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
|
+
|
|
46976
47102
|
// shared/model-assets.ts
|
|
46977
47103
|
var MODEL_GENERATORS = [
|
|
46978
47104
|
{
|
|
@@ -47042,7 +47168,7 @@ var rigCostUsd = (clips2) => 0.2 + 0.12 * clips2;
|
|
|
47042
47168
|
var modelCostUsd = (p) => generationCostUsd(p) + (p.rig ? rigCostUsd(p.animations.length) : 0);
|
|
47043
47169
|
|
|
47044
47170
|
// shared/assets.ts
|
|
47045
|
-
var ASSET_TYPES = ["image", "vector", "video", "music", "speech", "sfx", "model"];
|
|
47171
|
+
var ASSET_TYPES = ["image", "vector", "gif", "video", "music", "speech", "sfx", "model"];
|
|
47046
47172
|
var SPEECH_MODELS = [
|
|
47047
47173
|
{ id: "gemini-3.8-flash-tts", label: "Gemini 3.8 Flash TTS", provider: "google" },
|
|
47048
47174
|
{ id: "gemini-3.8-flash-lite-tts", label: "Gemini 3.8 Flash-Lite TTS", provider: "google" },
|
|
@@ -47325,6 +47451,7 @@ var videoDefaults = (p) => {
|
|
|
47325
47451
|
return { ...p, seconds: p.seconds ?? defaultSeconds(model, p.workflow), resolution: p.resolution ?? videoResolutions(model, p.workflow)[0], mode: p.mode ?? model.modes[0] };
|
|
47326
47452
|
};
|
|
47327
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] };
|
|
47328
47455
|
var MAX_IMAGE_REFERENCES = 4;
|
|
47329
47456
|
var MAX_MASK_REGIONS = 8;
|
|
47330
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([]);
|
|
@@ -47342,10 +47469,13 @@ var imageAsset = external_exports.object({
|
|
|
47342
47469
|
var imageChecks = (p, ctx) => {
|
|
47343
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"] });
|
|
47344
47471
|
};
|
|
47345
|
-
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";
|
|
47346
47475
|
var VECTOR_WORKFLOWS = ["text-to-vector", "image-to-vector"];
|
|
47347
47476
|
var VECTOR_QUALITIES = ["low", "medium", "high"];
|
|
47348
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");
|
|
47349
47479
|
var VECTOR_SHAPES = ["detailed", "simple"];
|
|
47350
47480
|
var MAX_VECTOR_REFERENCES = 5;
|
|
47351
47481
|
var VECTOR_REFERENCE_MAX_BYTES = 10 * 1024 * 1024;
|
|
@@ -47355,7 +47485,7 @@ var vectorAsset = external_exports.object({
|
|
|
47355
47485
|
/** Image-to-vector takes no prompt. */
|
|
47356
47486
|
prompt: external_exports.string().trim().max(4e3).default(""),
|
|
47357
47487
|
type: external_exports.literal("vector"),
|
|
47358
|
-
model: external_exports.
|
|
47488
|
+
model: external_exports.enum(VECTOR_MODEL_IDS).default("fal/ideogram-vector"),
|
|
47359
47489
|
workflow: external_exports.enum(VECTOR_WORKFLOWS).default("text-to-vector"),
|
|
47360
47490
|
/** Text-to-vector; defaults to medium. */
|
|
47361
47491
|
quality: external_exports.enum(VECTOR_QUALITIES).optional(),
|
|
@@ -47368,11 +47498,24 @@ var vectorAsset = external_exports.object({
|
|
|
47368
47498
|
/** Image-to-vector; defaults to detailed. */
|
|
47369
47499
|
shapes: external_exports.enum(VECTOR_SHAPES).optional(),
|
|
47370
47500
|
/** Text-to-vector. */
|
|
47371
|
-
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()
|
|
47372
47504
|
});
|
|
47373
47505
|
function vectorChecks(p, ctx) {
|
|
47374
47506
|
const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
|
|
47375
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.");
|
|
47376
47519
|
if (p.workflow === "text-to-vector") {
|
|
47377
47520
|
if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
|
|
47378
47521
|
if (p.source) issue2("source", "Only image-to-vector traces a saved image; use references to guide text-to-vector.");
|
|
@@ -47384,7 +47527,39 @@ function vectorChecks(p, ctx) {
|
|
|
47384
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.`);
|
|
47385
47528
|
if (p.references.length) issue2("references", "image-to-vector traces its source alone and takes no references.");
|
|
47386
47529
|
}
|
|
47387
|
-
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 });
|
|
47388
47563
|
var musicAsset = external_exports.object({
|
|
47389
47564
|
name: base.name,
|
|
47390
47565
|
/** A soundtrack may leave it out, and so may a song sung from lyrics, where it sets the style. */
|
|
@@ -47503,17 +47678,23 @@ var assetRequest = external_exports.discriminatedUnion("type", [
|
|
|
47503
47678
|
}).transform((p) => ({ ...p, voice: p.voice || defaultSpeechVoice(p.model) })),
|
|
47504
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) }),
|
|
47505
47680
|
vectorAsset.superRefine(vectorChecks).transform(vectorDefaults),
|
|
47506
|
-
modelAsset.superRefine(modelChecks).transform(modelDefaults)
|
|
47681
|
+
modelAsset.superRefine(modelChecks).transform(modelDefaults),
|
|
47682
|
+
gifAsset.transform(gifDefaults)
|
|
47507
47683
|
]);
|
|
47508
47684
|
var assetGenerationRequest = external_exports.discriminatedUnion("type", [imageAsset.omit({ model: true }).superRefine(imageChecks), ...assetRequest.options.slice(1)]);
|
|
47509
47685
|
var LIBRARY_AUDIO_MODEL = "library-audio";
|
|
47510
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) });
|
|
47511
47687
|
var UPLOADED_MODEL = "uploaded";
|
|
47512
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) });
|
|
47513
|
-
var savedAssetOptions = external_exports.union([assetRequest, libraryAudio, uploadedMedia]);
|
|
47689
|
+
var savedAssetOptions = external_exports.union([assetRequest, avatarVideoRequest, libraryAudio, uploadedMedia]);
|
|
47514
47690
|
var speechCharacters = (text2) => Array.from(text2).length;
|
|
47515
47691
|
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;
|
|
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
|
+
}
|
|
47517
47698
|
function videoExtras(p, facts = {}) {
|
|
47518
47699
|
const w = p.workflow;
|
|
47519
47700
|
if (spriteVideoModel(p.model).provider === "heygen") {
|
|
@@ -47533,6 +47714,7 @@ var modelEstimate = (p) => Math.max(1, Math.ceil(modelCostUsd(p) / 1e-3));
|
|
|
47533
47714
|
function assetEstimate(p, now = Date.now()) {
|
|
47534
47715
|
if (p.type === "image") return imageEstimate(p);
|
|
47535
47716
|
if (p.type === "vector") return vectorEstimate(p);
|
|
47717
|
+
if (p.type === "gif") return gifEstimate(p);
|
|
47536
47718
|
if (p.type === "model") return modelEstimate(p);
|
|
47537
47719
|
if (p.type === "video") return spriteVideoEstimate(spriteVideoModel(p.model), p, videoExtras(p));
|
|
47538
47720
|
if (p.type === "music") return Math.ceil(musicCostUsd(p) / 1e-3);
|
|
@@ -48218,7 +48400,7 @@ ${entries.join(",\n")}
|
|
|
48218
48400
|
}}
|
|
48219
48401
|
`;
|
|
48220
48402
|
}
|
|
48221
|
-
async function
|
|
48403
|
+
async function token2(context, access4) {
|
|
48222
48404
|
const result = await context.client.request(`/api/v1/cloud/${context.link.projectId}/token`, { method: "POST", body: { access: access4 } });
|
|
48223
48405
|
const remote = new URL(result.remote);
|
|
48224
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.");
|
|
@@ -48385,7 +48567,7 @@ async function cloudStatus(options) {
|
|
|
48385
48567
|
await saveState(context);
|
|
48386
48568
|
let cloud = null;
|
|
48387
48569
|
if (!options.offline) {
|
|
48388
|
-
const latest = await fetchLatest(context, await
|
|
48570
|
+
const latest = await fetchLatest(context, await token2(context, "read"));
|
|
48389
48571
|
cloud = { latest, newer: Boolean(latest && latest !== context.state.base) };
|
|
48390
48572
|
}
|
|
48391
48573
|
return {
|
|
@@ -48403,7 +48585,7 @@ async function cloudStatus(options) {
|
|
|
48403
48585
|
async function pushCloud(options) {
|
|
48404
48586
|
const context = await openContext(options.dir, options);
|
|
48405
48587
|
return withLock(context.root, async () => {
|
|
48406
|
-
const remote = await
|
|
48588
|
+
const remote = await token2(context, "write");
|
|
48407
48589
|
const latest = await fetchLatest(context, remote);
|
|
48408
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.`);
|
|
48409
48591
|
const { files: scanned, skipped, tooLarge } = await scanFiles(context.root);
|
|
@@ -48461,7 +48643,7 @@ function defaultMessage(diff) {
|
|
|
48461
48643
|
async function pullCloud(options) {
|
|
48462
48644
|
const context = await openContext(options.dir, options);
|
|
48463
48645
|
return withLock(context.root, async () => {
|
|
48464
|
-
const latest = await fetchLatest(context, await
|
|
48646
|
+
const latest = await fetchLatest(context, await token2(context, "read"));
|
|
48465
48647
|
if (!latest || latest === context.state.base) return { updated: false, version: context.state.base, message: "Already up to date." };
|
|
48466
48648
|
const { files: scanned, tooLarge } = await scanFiles(context.root);
|
|
48467
48649
|
const local = await describeFiles(context, scanned);
|
|
@@ -48512,7 +48694,7 @@ async function restoreCloud(options) {
|
|
|
48512
48694
|
const context = await openContext(options.dir, options);
|
|
48513
48695
|
return withLock(context.root, async () => {
|
|
48514
48696
|
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
|
|
48697
|
+
await fetchLatest(context, await token2(context, "read"));
|
|
48516
48698
|
let version4;
|
|
48517
48699
|
try {
|
|
48518
48700
|
version4 = await import_isomorphic_git.default.expandOid({ fs, gitdir: context.gitdir, oid: options.version });
|
|
@@ -48807,13 +48989,16 @@ var extras = {
|
|
|
48807
48989
|
speech: ["voice", "delivery"],
|
|
48808
48990
|
sfx: ["seconds", "loop", "prompt-influence"],
|
|
48809
48991
|
model: ["reference", "texture", "pbr", "faces", "rig", "animations", "height", "splats", "photo", "seed"],
|
|
48810
|
-
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"]
|
|
48811
48994
|
};
|
|
48995
|
+
var typeName = (type) => type === "gif" ? "GIF" : type;
|
|
48812
48996
|
var list2 = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
|
|
48813
48997
|
function accepted(type) {
|
|
48814
48998
|
const options = extras[type];
|
|
48815
48999
|
if (!options) return "";
|
|
48816
|
-
|
|
49000
|
+
const name2 = typeName(type);
|
|
49001
|
+
return `${name2[0].toUpperCase()}${name2.slice(1)} assets accept ${list2(["--name", "--prompt", "--model", ...options.map((option) => `--${option}`)])}.`;
|
|
48817
49002
|
}
|
|
48818
49003
|
function parseAsset(input2) {
|
|
48819
49004
|
const value = input2 && typeof input2 === "object" && "type" in input2 && input2.type === "image" ? { ...input2, model: void 0 } : input2;
|
|
@@ -48843,10 +49028,10 @@ function assetFromFlags(flags) {
|
|
|
48843
49028
|
for (const name2 of Object.values(extras).flat()) {
|
|
48844
49029
|
const value = flags[name2];
|
|
48845
49030
|
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());
|
|
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());
|
|
48847
49032
|
}
|
|
48848
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);
|
|
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" : ""}.`);
|
|
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" : ""}.`);
|
|
48850
49035
|
const base2 = { type: flags.type, name: flags.name, prompt: flags.prompt, model: flags.model };
|
|
48851
49036
|
if (flags.type === "image") return parseAsset({ ...base2, aspectRatio: flags["aspect-ratio"], transparent: flags.transparent, references: shas(flags.references), mask: regions(flags.mask) });
|
|
48852
49037
|
if (flags.type === "video") {
|
|
@@ -48886,7 +49071,8 @@ function assetFromFlags(flags) {
|
|
|
48886
49071
|
if (flags.type === "sfx") return parseAsset({ ...base2, seconds: flags.seconds, loop: flags.loop, promptInfluence: flags["prompt-influence"] });
|
|
48887
49072
|
if (flags.type === "music") return parseAsset({ ...base2, lyrics: flags.lyrics, styles: shas(flags.styles), vocals: flags.vocals, seconds: flags.seconds, source: flags.source });
|
|
48888
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 });
|
|
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 });
|
|
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) });
|
|
48890
49076
|
return parseAsset({ ...base2, ...flags.type === "speech" ? { voice: flags.voice, delivery: flags.delivery } : {} });
|
|
48891
49077
|
}
|
|
48892
49078
|
async function assetFromFile(file2, model) {
|
|
@@ -48982,6 +49168,7 @@ var MIME_EXTENSIONS = {
|
|
|
48982
49168
|
"image/png": "png",
|
|
48983
49169
|
"image/jpeg": "jpg",
|
|
48984
49170
|
"image/webp": "webp",
|
|
49171
|
+
"image/gif": "gif",
|
|
48985
49172
|
"image/svg+xml": "svg",
|
|
48986
49173
|
"video/mp4": "mp4",
|
|
48987
49174
|
"video/webm": "webm",
|
|
@@ -49101,96 +49288,6 @@ async function submitAssetBatch(client, project, requests, options, signal) {
|
|
|
49101
49288
|
import { readFile as readFile11, stat as stat6 } from "node:fs/promises";
|
|
49102
49289
|
import { extname } from "node:path";
|
|
49103
49290
|
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
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" };
|
|
49195
49292
|
var avatarsPath = (project) => `/api/v1/sprites/${encodeURIComponent(project)}/avatars`;
|
|
49196
49293
|
function parse3(schema, input2) {
|
|
@@ -49225,6 +49322,24 @@ function looksFromFlags(flags) {
|
|
|
49225
49322
|
if (flags["aspect-ratio"] && method !== "look_pack") throw new Error("--aspect-ratio applies only to Look Packs.");
|
|
49226
49323
|
return parse3(avatarLooksRequest, { method, lookId: flags.look, templateId: flags.template, aspectRatio: flags["aspect-ratio"] });
|
|
49227
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
|
+
}
|
|
49228
49343
|
function authorize(options) {
|
|
49229
49344
|
if (!options.yes) throw new Error("Pass --yes to authorize spending credits, or --dry-run to validate and estimate without creating anything.");
|
|
49230
49345
|
}
|
|
@@ -49238,6 +49353,11 @@ async function submitLooks(client, project, body, options) {
|
|
|
49238
49353
|
authorize(options);
|
|
49239
49354
|
return client.request(`${avatarsPath(project)}/looks`, { method: "POST", body, timeout: 18e4 });
|
|
49240
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
|
+
}
|
|
49241
49361
|
async function waitForLook(client, project, lookId, timeout, signal, interval = 1e4) {
|
|
49242
49362
|
if (!Number.isFinite(timeout) || timeout <= 0) throw new Error("Timeout must be positive.");
|
|
49243
49363
|
const deadline = Date.now() + timeout * 1e3;
|
|
@@ -50981,7 +51101,7 @@ async function curl(options, io) {
|
|
|
50981
51101
|
}
|
|
50982
51102
|
|
|
50983
51103
|
// 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];
|
|
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];
|
|
50985
51105
|
var assetAuthorizationFlags = [
|
|
50986
51106
|
{ name: "yes", type: "boolean", description: "Authorize spending credits" },
|
|
50987
51107
|
{ name: "dry-run", type: "boolean", description: "Validate and estimate locally without submitting a paid job" }
|
|
@@ -50994,14 +51114,23 @@ var catalogSections = {
|
|
|
50994
51114
|
sprites: ["styles", "kinds", "actions", "videoModels"],
|
|
50995
51115
|
image: ["assetTypes"],
|
|
50996
51116
|
vector: ["assetTypes", "vectorModels", "vectorOptions"],
|
|
51117
|
+
gif: ["assetTypes", "gifModels", "gifOptions"],
|
|
50997
51118
|
video: ["assetTypes", "videoModels", "videoProfiles"],
|
|
50998
51119
|
music: ["assetTypes", "musicModels", "musicOptions"],
|
|
50999
51120
|
speech: ["assetTypes", "speechModels", "voices", "elevenLabsVoices", "heygenVoices"],
|
|
51000
51121
|
sfx: ["assetTypes", "sfxModels"],
|
|
51001
51122
|
model: ["assetTypes", "modelGenerators", "modelAnimations"],
|
|
51002
|
-
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)]))]))
|
|
51003
51132
|
};
|
|
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 });
|
|
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 });
|
|
51005
51134
|
var modelFlags = [
|
|
51006
51135
|
{ name: "video-model", type: "string", choices: SPRITE_VIDEO_MODEL_IDS, description: "Video model override for this operation" }
|
|
51007
51136
|
];
|
|
@@ -51248,7 +51377,7 @@ Confirm code: ${device.user_code}`);
|
|
|
51248
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 }) => {
|
|
51249
51378
|
const client = new Client(flags.url, signal);
|
|
51250
51379
|
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 }) => {
|
|
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 }) => {
|
|
51252
51381
|
const full = catalog();
|
|
51253
51382
|
const section = flags.type;
|
|
51254
51383
|
out(stdout, section ? Object.fromEntries(catalogSections[section].map((key) => [key, full[key]])) : full);
|
|
@@ -51319,21 +51448,21 @@ Confirm code: ${device.user_code}`);
|
|
|
51319
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 }) => {
|
|
51320
51449
|
if (!flags.yes) throw new Error("Pass --yes to delete this project.");
|
|
51321
51450
|
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(
|
|
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(
|
|
51323
51452
|
{ name: "type", type: "string", required: true, choices: ASSET_TYPES },
|
|
51324
51453
|
{ name: "name", type: "string", required: true },
|
|
51325
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" },
|
|
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)" },
|
|
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)" },
|
|
51330
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" },
|
|
51331
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" },
|
|
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" },
|
|
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" },
|
|
51333
51462
|
{ name: "end-reference", type: "string", description: "Video: ending image blob SHA; models with endFrameOnly accept it without --reference" },
|
|
51334
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" },
|
|
51335
51464
|
{ 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" },
|
|
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" },
|
|
51337
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" },
|
|
51338
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" },
|
|
51339
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" },
|
|
@@ -51370,7 +51499,9 @@ Confirm code: ${device.user_code}`);
|
|
|
51370
51499
|
{ name: "splats", type: "number", description: "Model with --model idol: how many Gaussian splats, 32,768\u20131,048,576 (default 262,144)" },
|
|
51371
51500
|
{ name: "photo", type: "boolean", description: "Model with --model idol: --reference is a camera photo, cropped more loosely than a render or generated image" },
|
|
51372
51501
|
{ 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
|
|
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` },
|
|
51374
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" },
|
|
51375
51506
|
...assetAuthorizationFlags,
|
|
51376
51507
|
...assetWaitFlags,
|
|
@@ -51391,7 +51522,7 @@ Confirm code: ${device.user_code}`);
|
|
|
51391
51522
|
const result = await submitAssetBatch(new Client(flags.url, signal), args.id, await assetsFromBatchFile(flags.file, flags.model), flags, signal);
|
|
51392
51523
|
out(stdout, result);
|
|
51393
51524
|
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
|
|
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 }) => {
|
|
51395
51526
|
if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
|
|
51396
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)}`));
|
|
51397
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(
|
|
@@ -51399,19 +51530,32 @@ Confirm code: ${device.user_code}`);
|
|
|
51399
51530
|
{ name: "name", type: "string", required: true },
|
|
51400
51531
|
{ name: "prompt", type: "string", description: "Prompt: the character, wardrobe, setting and light, up to 1000 characters" },
|
|
51401
51532
|
{ 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
|
|
51533
|
+
{ name: "look", type: "string", description: "Prompt: one of this workspace's looks to put in a new outfit or setting" },
|
|
51403
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)" },
|
|
51404
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)" },
|
|
51405
|
-
{ name: "group", type: "string", description: "Add the result to one of this
|
|
51536
|
+
{ name: "group", type: "string", description: "Add the result to one of this workspace's avatar groups" },
|
|
51406
51537
|
{ name: "subject-consent", type: "boolean", description: "Photo or digital_twin: confirm the person shown agreed to become an avatar" },
|
|
51407
51538
|
...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
|
|
51409
|
-
{ name: "look", type: "string", required: true, description: "Reference look ID from this
|
|
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" },
|
|
51410
51541
|
{ name: "template", type: "string", required: true, description: "Template ID from avatars templates" },
|
|
51411
51542
|
{ name: "method", type: "string", choices: AVATAR_LOOK_METHODS, description: "Needed only for templates outside the gallery" },
|
|
51412
51543
|
{ name: "aspect-ratio", type: "string", choices: LOOK_PACK_ASPECT_RATIOS, description: "Look Pack: 16:9 (default) or 9:16, cropped to portrait" },
|
|
51413
51544
|
...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
|
|
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 }) => {
|
|
51415
51559
|
if (Boolean(flags.group) === Boolean(flags.look)) throw new Error("Pass --group or --look.");
|
|
51416
51560
|
if (!flags.yes) throw new Error("Pass --yes to delete the avatar for good.");
|
|
51417
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" }));
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ Before creative work in a multi-animation workspace, select or create the intend
|
|
|
17
17
|
|
|
18
18
|
Share the [animation-style gallery](https://animaguru.com/animation-styles), or open that URL in the internal browser when available, while asking the user to choose a style. Read the installed `animaguru.templates.json` for available styles and their skill names; `animaguru templates` lists the current CLI catalog. Offer the local catalog's choices so users select a skill actually installed in their workspace. Templates define appearance and motion; derive audience, message and story from the brief. Local scaffolding is separate from `projects create`, which creates a remote asset project. Never run `create` over an existing workspace.
|
|
19
19
|
|
|
20
|
-
- Run `animaguru schema` for the installed command structure and flags, and `animaguru catalog` for styles, subject kinds, actions, asset types, models, video settings, and speech voices; `catalog --type music|speech|sfx|model|image|vector|video|sprites|avatars` prints one section. Use `--help` on a leaf command for details.
|
|
20
|
+
- Run `animaguru schema` for the installed command structure and flags, and `animaguru catalog` for styles, subject kinds, actions, asset types, models, video settings, and speech voices; `catalog --type music|speech|sfx|model|image|vector|gif|video|sprites|avatars` prints one section. Use `--help` on a leaf command for details.
|
|
21
21
|
- Check `animaguru auth status`, `animaguru workspaces list`, and `animaguru credits` before generation. If sign-in is needed, run `animaguru auth login --no-browser` and let the user approve the printed browser URL. Select the user's requested Personal or team workspace with `animaguru workspaces use personal|TEAM_ID` before listing or creating projects. Do not read or print stored credentials. Agent runners may inject `ANIMAGURU_TOKEN` and `ANIMAGURU_WORKSPACE` as secrets or configuration.
|
|
22
22
|
- The default service is `https://animaguru.com`. Respect an existing `ANIMAGURU_URL` or pass `--url` to the leaf command when the user chooses another service. HTTP is supported only for loopback development.
|
|
23
23
|
- `animaguru skills install [--dir PATH]` (CLI 0.28.0) installs every AnimaGuru and PixiJS skill, the latest published on animaguru.com, into `.claude/skills/` and `.agents/skills/` with `npx skills`, and records them in `skills-lock.json`; run it when the user wants the skills in a folder or refreshed. `npx skills update --project --yes` refreshes them later. In a workspace made by an older CLI, newer skills may name commands or kit helpers its code lacks, so suggest updating the workspace as well.
|
|
@@ -74,11 +74,26 @@ HeyGen Voice (`heygen-voice-1`, or the faster and cheaper `heygen-voice-1-turbo`
|
|
|
74
74
|
|
|
75
75
|
CLI 0.27.0 adds vectors: `--type vector` draws an editable SVG with Ideogram on fal (`fal/ideogram-vector`), for logos, icons, mascots and flat illustrations that must stay sharp at any size or move shape by shape. `--workflow text-to-vector` (default) draws `--prompt` at `--quality low|medium|high` (13, 16 or 20 credits; 32 at high with references), with `--aspect-ratio auto` (default) or 1:4–4:1, `--seed`, and up to five `--references` (this project's images or sprite art, the first leading, named Image 1… in the prompt). `--workflow image-to-vector --source IMAGE_SHA` traces a saved image or sprite for 10 credits, with `--shapes detailed|simple` and no `--prompt`; to vectorize a brand logo or a generated picture, save it as an image asset first. `--download` saves `NAME.svg`. Vectors can't be used as image or video references. Check `catalog --type vector` before using them with an installed CLI.
|
|
76
76
|
|
|
77
|
+
CLI 0.38.0 adds a second vector model, GPT Image 2.6 (`--model fal/gpt-image-2.6-vector`). It draws from `--prompt` alone at `--quality low|medium|high`, with `--aspect-ratio auto` (default) or 1:3–3:1, and `--transparent` leaves out the background. It takes no `--references`, `--seed` or image-to-vector; use Ideogram to follow or trace an image. Its dry run holds 8, 12 or 22 credits by quality, and the job is charged the tokens fal meters.
|
|
78
|
+
|
|
77
79
|
```sh
|
|
78
80
|
animaguru assets generate PROJECT_ID --type vector --name "Fox mascot" \
|
|
79
81
|
--prompt "A friendly fox mascot waving, flat colors, bold clean outlines" --quality medium --dry-run
|
|
80
82
|
animaguru assets generate PROJECT_ID --type vector --name "Logo" \
|
|
81
83
|
--workflow image-to-vector --source IMAGE_SHA --shapes simple --dry-run
|
|
84
|
+
animaguru assets generate PROJECT_ID --type vector --model fal/gpt-image-2.6-vector --name "Badge" \
|
|
85
|
+
--prompt "A round coffee shop badge, two flat colors" --transparent --dry-run
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
CLI 0.38.0 adds animated GIFs: `--type gif` draws a short loop with GPT Image 2.6 on fal, for a sprite's walk or idle cycle, an icon that moves or a sticker. `--prompt` is required; describe one motion that loops and say the subject stays the same in every frame. `--frames 2–16` (default 9), `--frame-ms 20–2000` per frame (default 200, rounded to 10), `--aspect-ratio 1:1|3:2|2:3|16:9|9:16` (default 1:1), `--quality low|medium|high` (default medium), `--transparent` for frames without a background, and up to four `--references` (this project's images, GIF stills or sprite art, named Image 1… in the prompt) to animate or follow. `--model fal/gpt-image-2.6-gif` (Flare, default) is faster and `fal/gpt-image-2.6-sunburst-gif` finer and slower. All frames share one 3840×2160 picture, so more frames are smaller: nine square frames are 960×960, sixteen 720×720 (`catalog --type gif` lists every size). The dry run holds credits at still-image prices for those frames (about 31 for the defaults); fal meters what is charged. `--download` saves `NAME.gif`. A GIF's `file` SHA, or its `preview` (a PNG of the first frame), works wherever a saved image does: a video's `--reference`, `--end-reference` or `--references`, an image's `--references`, a vector to trace, a 3D model's `--reference`. So to put a GIF in a generated video, pass its SHA as the frame or reference. In a film workspace, play the GIF itself with the kit's `gifClip()` (see `animaguru-video`). Check `catalog --type gif` before using GIFs with an installed CLI.
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
animaguru assets generate PROJECT_ID --type gif --name "Fox walk" \
|
|
92
|
+
--prompt "A cartoon fox walk cycle, side view, the same fox in every frame" --frames 8 --frame-ms 120 --transparent --dry-run
|
|
93
|
+
animaguru assets generate PROJECT_ID --type gif --name "Fox wave" \
|
|
94
|
+
--prompt "The fox from Image 1 waves its paw, then holds still" --references CHARACTER_SHA --frames 6 --dry-run
|
|
95
|
+
animaguru assets generate PROJECT_ID --type video --name "Fox walks off" \
|
|
96
|
+
--prompt "The fox walks out of the frame to the right" --reference GIF_SHA --dry-run
|
|
82
97
|
```
|
|
83
98
|
|
|
84
99
|
CLI 0.28.0 adds 3D models: `--type model` builds a GLB from a saved image (`--reference SHA`, best) or from `--prompt` alone, with `--model tripo-h3.1` (default) or `hunyuan-3d-3.1-pro`, `--no-texture`, `--no-pbr` and `--faces`. `--rig` auto-rigs a humanoid character standing in a T- or A-pose, with `--animations idle,walk,wave` (named clips from `catalog --type model`, or Meshy library numbers; default idle,walk) and `--height` in metres. Downloads are `.glb`. CLI 0.35.0 adds `--model idol`: photoreal Gaussian splats of the object in a saved image (`--reference SHA`, required; `--photo` for a camera photo, `--splats`, `--seed`), downloaded as `.spz` and staged with the kit's `stage.splat()`. Read `animaguru-3d-models` before generating models or building 3D shots: it covers costs, rig limits (no fingers or facial blend shapes), when splats or meshes fit, optimisation and the kit's three.js stage.
|
|
@@ -102,7 +117,7 @@ animaguru files get PROJECT_ID references/ID/document.pdf --output references/le
|
|
|
102
117
|
|
|
103
118
|
`jobs wait` tracks only the specified job. Drawing can queue follow-up animations: inspect `jobs list` and wait for the relevant jobs before exporting. A timeout or CLI exit does not cancel server work. CLI 0.32.0 adds `jobs cancel PROJECT_ID JOB_ID --yes`. A project runs its jobs one at a time; this cancels a queued job, or stops a running one at its next check on the provider, and the job fails with the reason. Credits it already spent stay spent, and a result the provider hasn't returned is lost. Use it when the user asks, or when one job is stuck and holds up the jobs behind it; never to hurry a job that is only slow. Inspect existing jobs before retrying a paid submission to avoid duplicate charges. Report failed jobs and their errors instead of repeatedly submitting the same work.
|
|
104
119
|
|
|
105
|
-
`projects move PROJECT_ID --to OTHER_PROJECT_ID [--characters SLUG,SLUG] [--assets ASSET_ID] [--avatars GROUP_ID]` moves work between projects for free: characters with all their animations, saved assets and the
|
|
120
|
+
`projects move PROJECT_ID --to OTHER_PROJECT_ID [--characters SLUG,SLUG] [--assets ASSET_ID] [--avatars GROUP_ID]` moves work between projects for free: characters with all their animations, saved assets and, to a project in another workspace, the workspace's own avatars. Use it to gather work into one project instead of regenerating it; it needs CLI 0.16.0.
|
|
106
121
|
|
|
107
122
|
`files get` downloads one file by its path from `files list`, such as an image, clip or PDF the user attached as a reference. `files export` downloads a ZIP. Both refuse to overwrite an existing file. Choose a fresh output path. Inspect the exported manifest and actual assets before integrating them into the game or app; use their real dimensions, frame ordering, and animation metadata.
|
|
108
123
|
|
|
@@ -149,6 +164,6 @@ Pro and Team accounts: `animaguru library search DESCRIPTION [--type sprite|imag
|
|
|
149
164
|
Read these only when the task needs them:
|
|
150
165
|
|
|
151
166
|
- `references/sprites.md`: animate, redraw or import sprite characters (`jobs submit` JSON).
|
|
152
|
-
- `references/avatars.md`: HeyGen avatars and
|
|
167
|
+
- `references/avatars.md`: HeyGen avatars, looks, voices and avatar videos.
|
|
153
168
|
- `references/sharing.md`: hosting on AnimaGuru (`submit`, `videos`) and uploading to YouTube. Making or exporting a film never authorizes sharing it.
|
|
154
169
|
- `references/cloud.md`: cloud sync, versions and cloning.
|
|
@@ -4,7 +4,7 @@ Read from the `animaguru` skill only when the user wants a HeyGen avatar.
|
|
|
4
4
|
|
|
5
5
|
## HeyGen avatars
|
|
6
6
|
|
|
7
|
-
CLI 0.13.0 adds `avatars`. Check `animaguru avatars --help` before using it with an installed CLI. A group is a character, its looks are outfits, poses or styles, and a look ID is what HeyGen renders with. `avatars list PROJECT_ID` browses HeyGen's public catalog from any project; `--ownership private` lists the avatars
|
|
7
|
+
CLI 0.13.0 adds `avatars`, and 0.37.0 adds `avatars video` and `avatars voices`. Check `animaguru avatars --help` before using it with an installed CLI. A group is a character, its looks are outfits, poses or styles, and a look ID is what HeyGen renders with. `avatars list PROJECT_ID` browses HeyGen's public catalog from any project; `--ownership private` lists the avatars of the project's workspace. Every project in that workspace can use them, and no other workspace can see, change or render them. `avatars looks PROJECT_ID --group GROUP_ID` lists a character's looks, and `avatars templates` lists Look Pack and look template IDs.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
animaguru avatars create PROJECT_ID --type prompt --name "Ada" \
|
|
@@ -19,4 +19,17 @@ animaguru avatars wait PROJECT_ID LOOK_ID
|
|
|
19
19
|
|
|
20
20
|
`--file` and `--reference-images` take a project file SHA, an HTTPS URL (up to 32 MB) or a local file (up to 8 MB). Photos and footage show a real person: pass `--subject-consent` only after the user confirms that person agreed to become an avatar, and do not make avatars of people the user has no permission to depict. A `digital_twin` trains on about two minutes of footage and also needs the person's own consent: run `avatars consent PROJECT_ID GROUP_ID`, give them the 24-hour link, and check `consentStatus` with `avatars get PROJECT_ID --group GROUP_ID`.
|
|
21
21
|
|
|
22
|
-
Avatars and looks are charged when HeyGen accepts them and need `--yes`; get the estimate with `--dry-run` first. New looks start `processing`: use `avatars wait` rather than creating them again. `avatars rename`, `avatars voice` (default voice) and `avatars delete --yes` change only the
|
|
22
|
+
Avatars and looks are charged when HeyGen accepts them and need `--yes`; get the estimate with `--dry-run` first. New looks start `processing`: use `avatars wait` rather than creating them again. `avatars rename`, `avatars voice` (default voice) and `avatars delete --yes` change only the workspace's own avatars; deletion is permanent and also deletes the voice cloned for a digital twin, so run it only when the user asks to delete that avatar.
|
|
23
|
+
|
|
24
|
+
## Avatar videos
|
|
25
|
+
|
|
26
|
+
`avatars video` makes a video of one completed look (the workspace's or a public one) and saves it to the project as a video asset. Give `--script` (or `--script-file`, up to 1,500 characters) for the avatar to speak, or `--audio SHA` for saved speech or music to lip-sync, up to 120 seconds. Without `--voice` a script uses the look's own voice; `avatars voices PROJECT_ID --language Turkish` lists public voices, and `--ownership private` the voices cloned from the workspace's digital twins. Voices cloned for another workspace are refused. `--model` picks the engine (`heygen/avatar-iv` by default, `heygen/avatar-v`, `heygen/avatar-iii`) and must be in the look's `supportedEngines`. A digital twin renders only once its subject's consent is accepted.
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
animaguru avatars video PROJECT_ID --look LOOK_ID --name "Welcome" \
|
|
30
|
+
--script "Hi, I'm Ada. Here is what we shipped this week." --dry-run
|
|
31
|
+
animaguru avatars video PROJECT_ID --look LOOK_ID --name "Voice-over cut" --audio SPEECH_SHA --yes
|
|
32
|
+
animaguru jobs wait PROJECT_ID JOB_ID
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The dry run asks the server, which checks the look, voice and audio and measures the audio's length. The video is charged for the seconds HeyGen makes; wait for the returned job with `jobs wait` and find the video with `assets list`.
|
|
@@ -81,6 +81,7 @@ Build on the shared kit instead of re-implementing helpers: `src/kit/` in a stan
|
|
|
81
81
|
- Motion: `win`, `inOut`, `easeOut`/`easeIn`/`easeInOut`/`easeBack`, `bump`, `lerp`, `mixColor`, `hex`, seeded `rng`, `show`, `setText`.
|
|
82
82
|
- Text and fonts: `txt(value, size, { family: "heading" | "body" | "mono", weight, fill, … })`, `useBrandFonts(branding, monoFamily?)`, `loadFonts([{ family, url }])` (variable fonts get their weight range), `gradientFill(colors)`.
|
|
83
83
|
- Drawing: `panel`, `partialLine`, `bezier`, `pointAt`, `check`, `cross`, `person`, `glowTexture`, `vignetteTexture`, `rasterSvg` (exact-width logo textures), `Badge`, `Pill`.
|
|
84
|
+
- GIFs: `gifClip(url, { loop, speed, scaleMode })` from `kit/gif` (kept out of the index), an animated GIF as a sprite on the film clock. See [GIFs](#gifs).
|
|
84
85
|
- Layout: `safeArea(config)` and `stack(nodes, { x, y, gap })`. Set `"safeArea": "reels"` (or `{ top, right, bottom, left }` pixels) in `film.config.json` for vertical social films; review checks text against it.
|
|
85
86
|
|
|
86
87
|
Keep `src/film.ts` to setup and the scene list, and put each scene in `src/scenes/NAME.ts` as `export function hook(film: FilmBase, cue: CueSheet) { … }` that adds to `film.world` and calls `film.on`. Write and fix one scene file at a time with targeted edits rather than rewriting the whole film. Claude Code's Write tool refuses to replace a file that has not been opened with Read in this session (`cat` does not count), so Read the starter `src/film.ts` before replacing it.
|
|
@@ -95,7 +96,24 @@ Pro and Team accounts can take ready-made music, sound effects, voice-overs, cli
|
|
|
95
96
|
|
|
96
97
|
## Vectors
|
|
97
98
|
|
|
98
|
-
Logos, icons and flat mascots can be generated as SVG (`animaguru assets generate PROJECT_ID --type vector …`, see the `animaguru` skill) or traced from a saved image (`--workflow image-to-vector`), then saved into the film with `--download public/assets/`. Draw one with the kit's `rasterSvg(url, width)` at the width it appears on screen, so it stays sharp; for shapes that scale or move separately, load it as geometry with `Assets.load({ src, data: { parseAsGraphicsContext: true } })` and `new Graphics(context)` (see `pixijs-assets`). Ask for `--shapes simple` or `--quality low` when the art will be small or animated part by part.
|
|
99
|
+
Logos, icons and flat mascots can be generated as SVG (`animaguru assets generate PROJECT_ID --type vector …`, see the `animaguru` skill) or traced from a saved image (`--workflow image-to-vector`), then saved into the film with `--download public/assets/`. Ideogram (the default) can follow reference images; `--model fal/gpt-image-2.6-vector` draws from the prompt alone and takes `--transparent`, so the art comes without a background to place over a scene. Draw one with the kit's `rasterSvg(url, width)` at the width it appears on screen, so it stays sharp; for shapes that scale or move separately, load it as geometry with `Assets.load({ src, data: { parseAsGraphicsContext: true } })` and `new Graphics(context)` (see `pixijs-assets`). Ask for `--shapes simple` or `--quality low` when the art will be small or animated part by part.
|
|
100
|
+
|
|
101
|
+
## GIFs
|
|
102
|
+
|
|
103
|
+
A GIF asset (`animaguru assets generate PROJECT_ID --type gif …`, see the `animaguru` skill) is a short looping animation drawn by GPT Image 2.6: a character's walk or idle cycle, a waving mascot, a spinning coin, a blinking icon. Pass a saved image or sprite art as `--references` to animate that exact character, and `--transparent` for frames without a background to place over a scene. Save it into the film with `--download public/assets/`.
|
|
104
|
+
|
|
105
|
+
Never play a GIF with an `<img>` or a GifSprite that runs on its own: it would follow the wall clock, so seeking, review frames and the MP4 export would show other frames than the preview. Load it with the kit's `gifClip` and drive it from the scene's update:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { gifClip } from "../kit/gif"; // workspace: "../../../../runtime/kit/gif" from src/scenes/
|
|
109
|
+
|
|
110
|
+
const fox = await gifClip(publicAsset("assets/fox-wave.gif"), { scaleMode: "nearest" }); // nearest keeps pixel art crisp
|
|
111
|
+
fox.sprite.anchor.set(0.5, 1); fox.sprite.position.set(960, 900);
|
|
112
|
+
film.world.addChild(fox.sprite);
|
|
113
|
+
film.on((t) => { fox.at(t - 4.2); fox.sprite.visible = t >= 4.2; });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`at(seconds)` shows the frame for that moment of the GIF from its own frame timings, looping unless `{ loop: false }` (then it holds the last frame); `speed` plays it faster or slower, and `frames`/`duration` give its length. Move, scale and fade the sprite with the usual motion helpers while it plays. Each frame is a separate texture, so keep a film to a few GIFs; for a long or many-frame cycle use a sprite sheet with `AnimatedSprite` instead. A GIF can also open an AI clip: its file SHA works as `--reference` for `assets generate --type video` (the server uses its first frame).
|
|
99
117
|
|
|
100
118
|
## Voiceover, music and captions
|
|
101
119
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Assets } from "pixi.js";
|
|
2
|
+
import { GifSprite, type GifSource } from "pixi.js/gif";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Animated GIFs on the film's clock. Import from "./kit/gif" (standalone) or "../../../runtime/kit/gif"
|
|
6
|
+
(workspace animation); it is kept out of the kit's index so films without a GIF don't load the
|
|
7
|
+
GIF decoder.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type GifClip = {
|
|
11
|
+
/** A Sprite showing the frame `at` chose; position, scale and anchor it like any sprite. */
|
|
12
|
+
sprite: GifSprite;
|
|
13
|
+
/** How many frames the GIF has, and how long one loop plays in seconds. */
|
|
14
|
+
frames: number;
|
|
15
|
+
duration: number;
|
|
16
|
+
/** Shows the frame for `seconds` into the GIF (pass `t - start` from the scene's update). */
|
|
17
|
+
at: (seconds: number) => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
Loads an animated GIF, such as an AnimaGuru GIF asset (a sprite's walk cycle, a moving icon), as a
|
|
22
|
+
sprite that never plays by itself: each update calls `at`, which picks the frame from the GIF's own
|
|
23
|
+
frame timings. Preview, seeking, review frames and MP4 export then show the same frame at the same
|
|
24
|
+
moment. It loops unless `loop` is false, when it holds its last frame; `speed` scales its timing.
|
|
25
|
+
Pixel-art GIFs stay crisp with `scaleMode: "nearest"`.
|
|
26
|
+
*/
|
|
27
|
+
export async function gifClip(url: string, { loop = true, speed = 1, scaleMode }: { loop?: boolean; speed?: number; scaleMode?: "linear" | "nearest" } = {}): Promise<GifClip> {
|
|
28
|
+
const source = await Assets.load<GifSource>(scaleMode ? { src: url, data: { scaleMode } } : url);
|
|
29
|
+
const sprite = new GifSprite({ source, autoPlay: false, autoUpdate: false, loop });
|
|
30
|
+
const last = source.totalFrames - 1;
|
|
31
|
+
const at = (seconds: number) => {
|
|
32
|
+
const ms = Math.max(0, seconds * speed * 1000);
|
|
33
|
+
if (!loop && ms >= source.duration) { sprite.currentFrame = last; return; }
|
|
34
|
+
const time = ms % source.duration;
|
|
35
|
+
const index = source.frames.findIndex((frame) => time >= frame.start && time < frame.end);
|
|
36
|
+
sprite.currentFrame = index === -1 ? last : index;
|
|
37
|
+
};
|
|
38
|
+
at(0);
|
|
39
|
+
return { sprite, frames: source.totalFrames, duration: source.duration / 1000, at };
|
|
40
|
+
}
|