@docyrus/animaguru 0.8.0 → 0.8.1
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
CHANGED
|
@@ -80,7 +80,7 @@ CLI 0.7.1 can select a cloud workspace for project listing, project creation, an
|
|
|
80
80
|
animaguru workspaces list
|
|
81
81
|
animaguru workspaces use team_0123456789abcdef0123456789abcdef
|
|
82
82
|
animaguru projects list
|
|
83
|
-
animaguru projects create --name "Shared cast"
|
|
83
|
+
animaguru projects create --name "Shared cast"
|
|
84
84
|
animaguru credits
|
|
85
85
|
animaguru workspaces use personal
|
|
86
86
|
```
|
|
@@ -89,10 +89,12 @@ Use a team ID returned by `workspaces list`. The choice is saved per server in t
|
|
|
89
89
|
|
|
90
90
|
## Agent workflow
|
|
91
91
|
|
|
92
|
+
Remote projects are neutral containers. `projects create` has no `--style` flag. Choose a style when generating a sprite; local film setup keeps its separate style interview.
|
|
93
|
+
|
|
92
94
|
```sh
|
|
93
|
-
animaguru projects create --name "Forest cast"
|
|
95
|
+
animaguru projects create --name "Forest cast"
|
|
94
96
|
# Read the returned project.id and use it below.
|
|
95
|
-
animaguru generate PROJECT_ID --name "Knight" --description "A blue knight with a red scarf" --actions idle,walk --yes
|
|
97
|
+
animaguru generate PROJECT_ID --name "Knight" --description "A blue knight with a red scarf" --style pixel --actions idle,walk --yes
|
|
96
98
|
animaguru jobs wait PROJECT_ID JOB_ID --timeout 600
|
|
97
99
|
animaguru jobs list PROJECT_ID
|
|
98
100
|
animaguru projects get PROJECT_ID
|
|
@@ -123,7 +125,7 @@ animaguru models PROJECT_ID --image-model gpt-image-2.5 --video-model pruna/p-vi
|
|
|
123
125
|
# Either selection can be changed independently:
|
|
124
126
|
animaguru models PROJECT_ID --video-model heygen/heygen-video-1
|
|
125
127
|
# Or choose them when creating a project:
|
|
126
|
-
animaguru projects create --name "Forest cast"
|
|
128
|
+
animaguru projects create --name "Forest cast" \
|
|
127
129
|
--image-model gpt-image-2.5 --video-model heygen/heygen-video-1
|
|
128
130
|
```
|
|
129
131
|
|
|
@@ -136,7 +138,7 @@ These flags are available in CLI 0.2.0 and later. Rerun the macOS installer to u
|
|
|
136
138
|
CLI 0.4.0 adds standalone assets alongside the existing sprite commands. Use any project, or create an empty one first. `assets list` returns saved asset ids, image blob hashes for video references, and the models configured on that server.
|
|
137
139
|
|
|
138
140
|
```sh
|
|
139
|
-
animaguru projects create --name "Adventure assets"
|
|
141
|
+
animaguru projects create --name "Adventure assets"
|
|
140
142
|
animaguru assets list PROJECT_ID
|
|
141
143
|
|
|
142
144
|
animaguru assets generate PROJECT_ID --type image --name "Forest" \
|
package/dist/animaguru.js
CHANGED
|
@@ -542,7 +542,7 @@ var init_dist = __esm({
|
|
|
542
542
|
// cli/package.json
|
|
543
543
|
var package_default = {
|
|
544
544
|
name: "@docyrus/animaguru",
|
|
545
|
-
version: "0.8.
|
|
545
|
+
version: "0.8.1",
|
|
546
546
|
description: "Agent-friendly AnimaGuru CLI built with CrustJS",
|
|
547
547
|
type: "module",
|
|
548
548
|
bin: {
|
|
@@ -27563,10 +27563,10 @@ Confirm code: ${device.user_code}`);
|
|
|
27563
27563
|
}))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings and speech voices" }, (command) => command.action(({ stdout }) => out(stdout, { styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, imageModels: SPRITE_IMAGE_MODEL_IDS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS })))).command("projects", (projects) => projects.add(defineCommand("list", { description: "List projects in the selected workspace" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
|
|
27564
27564
|
const client = new Client(flags.url, signal);
|
|
27565
27565
|
out(stdout, await client.request(`/api/v1/projects${await workspaceQuery(client)}`));
|
|
27566
|
-
}))).add(defineCommand("create", { description: "Create an empty sprite project; generate artwork with jobs submit" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).flags(...modelFlags).flags({ name: "name", type: "string", required: true }
|
|
27566
|
+
}))).add(defineCommand("create", { description: "Create an empty sprite project; generate artwork with jobs submit" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).flags(...modelFlags).flags({ name: "name", type: "string", required: true }).action(async ({ flags, stdout, signal }) => {
|
|
27567
27567
|
const client = new Client(flags.url, signal);
|
|
27568
27568
|
const selected = await selectedWorkspace(client.origin);
|
|
27569
|
-
out(stdout, await client.request("/api/v1/sprites", { method: "POST", body: { owner: selected, name: flags.name,
|
|
27569
|
+
out(stdout, await client.request("/api/v1/sprites", { method: "POST", body: { owner: selected, name: flags.name, ...selectedModels(flags) } }));
|
|
27570
27570
|
}))).add(defineCommand("get", { description: "Read project, subjects, animations, estimates and job state" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(projectPath(args.id)))))).add(defineCommand("rename", { description: "Rename a project" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "name", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "PATCH", body: { name: flags.name } }))))).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 }) => {
|
|
27571
27571
|
if (!flags.yes) throw new Error("Pass --yes to delete this project.");
|
|
27572
27572
|
out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
|
|
@@ -27616,9 +27616,9 @@ Confirm code: ${device.user_code}`);
|
|
|
27616
27616
|
const body = selectedModels(flags);
|
|
27617
27617
|
if (!Object.keys(body).length) throw new Error("Pass --image-model or --video-model.");
|
|
27618
27618
|
out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/models`, { method: "PUT", body }));
|
|
27619
|
-
}))).add(defineCommand("generate", { description: "Draw a subject; --yes authorizes generation costs" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).flags({ name: "name", type: "string", required: true }, { name: "description", type: "string", required: true }, { name: "kind", type: "string", choices: SPRITE_KINDS, default: "character" }, { name: "actions", type: "string", description: "Comma-separated built-in actions to animate after drawing" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
27619
|
+
}))).add(defineCommand("generate", { description: "Draw a subject; --yes authorizes generation costs" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).flags({ name: "name", type: "string", required: true }, { name: "description", type: "string", required: true }, { name: "style", type: "string", choices: SPRITE_STYLES, description: "Artwork style for this sprite" }, { name: "kind", type: "string", choices: SPRITE_KINDS, default: "character" }, { name: "actions", type: "string", description: "Comma-separated built-in actions to animate after drawing" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
27620
27620
|
if (!flags.yes) throw new Error("Pass --yes to authorize spending credits.");
|
|
27621
|
-
const body = spriteJobBody.parse({ kind: "character", name: flags.name, description: flags.description, subjectKind: flags.kind, actions: actions(flags.actions), ...selectedModels(flags) });
|
|
27621
|
+
const body = spriteJobBody.parse({ kind: "character", name: flags.name, description: flags.description, ...flags.style ? { style: flags.style } : {}, subjectKind: flags.kind, actions: actions(flags.actions), ...selectedModels(flags) });
|
|
27622
27622
|
out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/jobs`, { method: "POST", body }));
|
|
27623
27623
|
}))).add(defineCommand("schema", { description: "Print the Crust command schema for agent discovery" }, (command) => command.action(async ({ stdout }) => out(stdout, await createCli().snapshot())))).command("files", (files) => files.add(defineCommand("import", { description: "Import a PNG, JPEG or WebP reference; --yes authorizes redraw costs when configured" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(...modelFlags).flags({ name: "file", type: "string", required: true }, { name: "name", type: "string", required: true }, { name: "description", type: "string" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
27624
27624
|
if (!flags.yes) throw new Error("Pass --yes to authorize reference redraw costs.");
|
package/package.json
CHANGED
|
@@ -24,9 +24,9 @@ Share the [animation-style gallery](https://animaguru.com/animation-styles), or
|
|
|
24
24
|
Reuse the user's chosen project. Otherwise inspect `animaguru projects list` before creating one. Read `animaguru projects get PROJECT_ID` for subjects, character identifiers, generation estimates, and current job state. Use identifiers returned by the service rather than guessing slugs.
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
-
animaguru projects create --name "Forest cast"
|
|
27
|
+
animaguru projects create --name "Forest cast"
|
|
28
28
|
animaguru generate PROJECT_ID --name "Knight" \
|
|
29
|
-
--description "A blue knight with a red scarf" --actions idle,walk --yes
|
|
29
|
+
--description "A blue knight with a red scarf" --style pixel --actions idle,walk --yes
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Read the project ID from `projects create` and job IDs from submission responses. Supported actions depend on the subject kind. Use `--kind` for a different subject kind, based on the catalog.
|
|
@@ -63,7 +63,7 @@ Import reference artwork with `animaguru files import PROJECT_ID --file knight.p
|
|
|
63
63
|
|
|
64
64
|
## Standalone images, video, music and speech
|
|
65
65
|
|
|
66
|
-
CLI 0.4.0 adds `assets generate`, `assets submit`, `assets list`, and `assets download`. Use `assets list PROJECT_ID` to discover saved assets and configured provider models.
|
|
66
|
+
CLI 0.4.0 adds `assets generate`, `assets submit`, `assets list`, and `assets download`. Use `assets list PROJECT_ID` to discover saved assets and configured provider models. Create neutral asset projects with `projects create --name NAME`. Projects have no art style; choose style when generating an individual sprite.
|
|
67
67
|
|
|
68
68
|
```sh
|
|
69
69
|
animaguru assets generate PROJECT_ID --type image --name "Forest" \
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile, cp, mkdir } from "node:fs/promises";
|
|
1
|
+
import { readFile, cp, mkdir, stat } from "node:fs/promises";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { listAnimations, projectContext, publicFile, readBranding } from "./project-context.mjs";
|
|
@@ -50,9 +50,19 @@ void mountPlayer({config,Film,chapters,branding:${JSON.stringify(context.brandin
|
|
|
50
50
|
}
|
|
51
51
|
const file = await publicFile(context, match[2]);
|
|
52
52
|
const type = ({svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",webp:"image/webp",mp3:"audio/mpeg",wav:"audio/wav",m4a:"audio/mp4",woff2:"font/woff2",woff:"font/woff",ttf:"font/ttf",mp4:"video/mp4",webm:"video/webm"})[file.split(".").at(-1).toLowerCase()] ?? "application/octet-stream";
|
|
53
|
-
|
|
53
|
+
// Media elements seek with byte ranges; without them Chrome cannot load or seek video.
|
|
54
|
+
const { size } = await stat(file);
|
|
55
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(request.headers.range ?? "");
|
|
56
|
+
let start = 0, end = size - 1;
|
|
57
|
+
if (range && (range[1] || range[2])) {
|
|
58
|
+
start = range[1] ? Number(range[1]) : Math.max(0, size - Number(range[2]));
|
|
59
|
+
if (range[1] && range[2]) end = Math.min(Number(range[2]), size - 1);
|
|
60
|
+
if (start > end) { response.writeHead(416, { "Content-Range": `bytes */${size}` }).end(); return; }
|
|
61
|
+
}
|
|
62
|
+
const partial = Boolean(range && (range[1] || range[2]));
|
|
63
|
+
response.writeHead(partial ? 206 : 200, { "Content-Type": type, "Cache-Control": "no-store", "Accept-Ranges": "bytes", "Content-Length": String(end - start + 1), ...(partial ? { "Content-Range": `bytes ${start}-${end}/${size}` } : {}) });
|
|
54
64
|
if (request.method === "HEAD") { response.end(); return; }
|
|
55
|
-
const stream = createReadStream(file); response.once("close", () => stream.destroy()); stream.pipe(response);
|
|
65
|
+
const stream = createReadStream(file, { start, end }); response.once("close", () => stream.destroy()); stream.pipe(response);
|
|
56
66
|
})().catch((error) => { if (!response.headersSent) response.writeHead(404).end(error.message); });
|
|
57
67
|
});
|
|
58
68
|
},
|