@docyrus/animaguru 0.11.0 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/animaguru",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Agent-friendly AnimaGuru CLI built with CrustJS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: animaguru
3
- description: Use the AnimaGuru CLI to create and animate assets, scaffold video workspaces, and submit finished HTML5 animations or MP4s for public or selected-user sharing.
3
+ description: Use the AnimaGuru CLI to create and animate assets, create HeyGen avatars, scaffold video workspaces, and submit finished HTML5 animations or MP4s for public or selected-user sharing.
4
4
  ---
5
5
 
6
6
  # AnimaGuru
@@ -33,6 +33,8 @@ Read the project ID from `projects create` and job IDs from submission responses
33
33
 
34
34
  Generation and reference imports spend credits and require `--yes`. Use it for work the user has authorized, respecting any budget or model constraints. Creating or installing this skill grants no spending authorization. Estimates are ranges; actual provider usage determines charges. Project deletion also requires `--yes` and should follow the user's request to delete that project.
35
35
 
36
+ One AnimaGuru credit represents $0.01 of model cost. CLI/API balances, estimates, credit ranges and queued-job receipts already use these credits, including fractional amounts such as 0.7. This includes offline `--dry-run` estimates. Compare them directly with the user's credit budget; do not rescale returned amounts.
37
+
36
38
  `generate`, `jobs submit`, and `files import` accept optional `--image-model` and `--video-model` flags. These select models for that operation and its explicitly queued animations. Use `animaguru models PROJECT_ID --image-model IMAGE_ID --video-model VIDEO_ID` only when the user wants to change defaults for future jobs. Omitted selections use project defaults, then service defaults. Discover available identifiers from `catalog` rather than hardcoding model names.
37
39
 
38
40
  ## Animate, redraw, or import
@@ -83,6 +85,25 @@ For ElevenLabs speech, choose `eleven_v4` or `eleven_v4_turbo`, pass an ElevenLa
83
85
 
84
86
  Use `--dry-run` for local request validation and estimated credits without sign-in or paid work. The estimate does not prove the server has the provider, the credits, or the reference image. Use `--yes` only within the user's existing spending authorization. For file-based requests, write the API asset request as JSON and use `assets submit PROJECT_ID --file asset.json --yes`; `--model` overrides the file's selection. Jobs use the existing `jobs wait PROJECT_ID JOB_ID` workflow. After completion, read the asset id from `assets list` and download with `assets download PROJECT_ID ASSET_ID --output welcome.wav`, or export the complete project ZIP.
85
87
 
88
+ ## HeyGen avatars
89
+
90
+ 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 this project created, which only it can see or change. `avatars looks PROJECT_ID --group GROUP_ID` lists a character's looks, and `avatars templates` lists Look Pack and look template IDs.
91
+
92
+ ```sh
93
+ animaguru avatars create PROJECT_ID --type prompt --name "Ada" \
94
+ --prompt "Woman in her early 30s, warm smile, navy blazer, bright studio" --dry-run
95
+ animaguru avatars create PROJECT_ID --type prompt --name "Ada, keynote" \
96
+ --look LOOK_ID --prompt "On a conference stage under a spotlight" --dry-run
97
+ animaguru avatars create PROJECT_ID --type photo --name "Sam" \
98
+ --file IMAGE_SHA_URL_OR_PATH --subject-consent --dry-run
99
+ animaguru avatars add-looks PROJECT_ID --look LOOK_ID --template TEMPLATE_ID --dry-run
100
+ animaguru avatars wait PROJECT_ID LOOK_ID
101
+ ```
102
+
103
+ `--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`.
104
+
105
+ 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 project's own avatars; deletion is permanent, so run it only when the user asks to delete that avatar.
106
+
86
107
  ## Wait and export
87
108
 
88
109
  ```sh
@@ -9,5 +9,5 @@ let port = 5195;
9
9
  if (rest.length) { if (rest.length !== 2 || rest[0] !== "--port" || !/^\d+$/.test(rest[1])) throw new Error("Usage: npm run dev -- [--animation NAME] [--port NUMBER]"); port = Number(rest[1]); }
10
10
  const workspace = await workspaceInfo(root);
11
11
  const context = animation ? await projectContext(root, animation) : null;
12
- const server = await createServer({ root, configFile: false, base: "./", publicDir: workspace ? false : undefined, server: { host: "127.0.0.1", port, strictPort: true }, plugins: [...(workspace ? [workspacePlugin(root)] : []), htmlExportPlugin(root)] });
12
+ const server = await createServer({ root, configFile: false, base: "./", publicDir: workspace ? false : undefined, server: { host: process.env.ANIMAGURU_HEADLESS === "1" ? "0.0.0.0" : "127.0.0.1", port, strictPort: true }, plugins: [...(workspace ? [workspacePlugin(root)] : []), htmlExportPlugin(root)] });
13
13
  await server.listen(); console.log(`AnimaGuru workspace: http://127.0.0.1:${port}${context?.route ?? "/"}`);
@@ -10,6 +10,7 @@ import ffprobe from "@ffprobe-installer/ffprobe";
10
10
  import { projectArguments, projectContext, publicFile } from "./project-context.mjs";
11
11
  import { workspacePlugin } from "./workspace-plugin.mjs";
12
12
  import { subtitleCues, toSrt, toVtt } from "./subtitles.mjs";
13
+ import { captureHeadless } from "./headless-browser.mjs";
13
14
 
14
15
  const root = fileURLToPath(new URL("../", import.meta.url));
15
16
  const selection = projectArguments(process.argv.slice(2));
@@ -243,3 +244,7 @@ process.once("SIGINT", () => { void cleanup(new Error("Cancelled.")); });
243
244
  process.once("SIGTERM", () => { void cleanup(new Error("Cancelled.")); });
244
245
  try { await server.listen(); } catch (error) { await cleanup(error); throw error; }
245
246
  console.log(`Open this local export page and keep it open until complete:\nhttp://127.0.0.1:${port}${context.route}?capture=1&export=${token}\nOutput: ${destination}\nThe preview can stay open. This capture runs locally without generation credits.`);
247
+ if (process.env.ANIMAGURU_HEADLESS === "1") {
248
+ try { await captureHeadless(`http://127.0.0.1:${port}${context.route}?capture=1&export=${token}`, { endpoint: "/__export/finish" }); }
249
+ catch (error) { await cleanup(error); }
250
+ }
@@ -0,0 +1,17 @@
1
+ /** Uses the same capture page and HTTP protocol as a locally opened browser. */
2
+ export async function captureHeadless(url, { endpoint, timeout = 30 * 60 * 1000 } = {}) {
3
+ const { chromium } = await import("playwright-core");
4
+ const browser = await chromium.launch({
5
+ executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH ?? "/usr/bin/chromium",
6
+ headless: true, args: ["--no-sandbox", "--disable-dev-shm-usage", "--autoplay-policy=no-user-gesture-required"],
7
+ });
8
+ try {
9
+ const page = await browser.newPage({ deviceScaleFactor: 1 });
10
+ const finished = page.waitForResponse((response) => new URL(response.url()).pathname === endpoint, { timeout });
11
+ page.on("pageerror", (error) => console.error(`Capture page: ${error.message}`));
12
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60_000 });
13
+ const response = await finished;
14
+ if (!response.ok()) throw new Error(`Capture failed (${response.status()}): ${await response.text()}`);
15
+ await response.finished();
16
+ } finally { await browser.close(); }
17
+ }
@@ -7,6 +7,7 @@ import { projectArguments, projectContext } from "./project-context.mjs";
7
7
  import { workspacePlugin } from "./workspace-plugin.mjs";
8
8
  import { analyzeReview } from "./review-analysis.mjs";
9
9
  import { inspectAudio } from "./review-audio.mjs";
10
+ import { captureHeadless } from "./headless-browser.mjs";
10
11
 
11
12
  const root=fileURLToPath(new URL("../",import.meta.url));
12
13
  const selection=projectArguments(process.argv.slice(2));
@@ -39,3 +40,7 @@ const server=await createServer({configFile:false,root,base:"./",publicDir:conte
39
40
  });
40
41
  }}]});
41
42
  await server.listen();console.log(`Open this review page and keep it open until completion:\nhttp://127.0.0.1:${port}${context.route}?review=${token}`);
43
+ if(process.env.ANIMAGURU_HEADLESS==="1") {
44
+ try { await captureHeadless(`http://127.0.0.1:${port}${context.route}?review=${token}`,{endpoint:"/__review/report"}); }
45
+ catch(error) { console.error(error.message);process.exitCode=1;await server.close(); }
46
+ }