@docyrus/animaguru 0.15.0 → 0.16.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 +5 -3
- package/dist/animaguru.js +191 -150
- package/package.json +1 -1
- package/skills/animaguru/SKILL.md +3 -1
- package/skills/animaguru/references/sprites.md +1 -1
- package/skills/animaguru-cutcraft/SKILL.md +48 -0
- package/skills/animaguru-cutcraft/agents/openai.yaml +4 -0
- package/skills/animaguru-cutcraft/references/generation.md +61 -0
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ Scaffolding requires no AnimaGuru sign-in and spends no generation credits; depe
|
|
|
55
55
|
|
|
56
56
|
The official PixiJS skills are vendored with their MIT license and a hashed provenance manifest. Skills and template files ship in both the npm package and macOS installer; `create` does not fetch agent instructions from a remote repository at runtime.
|
|
57
57
|
|
|
58
|
+
CLI 0.16.1 also installs **AnimaGuru Cutcraft** in both agent skill folders. Ask your agent to use `$animaguru-cutcraft` for tactile paper-collage explainers with exact editorial lettering, one narrator and purposeful cutout motion. Its generation adapter checks the server's enabled models, tracks asynchronous jobs and supports controlled animation of generated transparent artwork when precise placement is needed or video providers are unavailable. Cutcraft is an additional skill; the gallery retains its 31 visual styles.
|
|
59
|
+
|
|
58
60
|
## Install the project skill
|
|
59
61
|
|
|
60
62
|
Run this in the folder where you want your coding agent to use AnimaGuru:
|
|
@@ -89,7 +91,7 @@ Use a team ID returned by `workspaces list`. The choice is saved per server in t
|
|
|
89
91
|
|
|
90
92
|
## Agent workflow
|
|
91
93
|
|
|
92
|
-
Remote projects
|
|
94
|
+
Remote projects have no art style. `projects create` takes a name only. Choose the style for each sprite with `generate --style` or `files import --style` (hd-cartoon when left out); local film setup keeps its separate style interview.
|
|
93
95
|
|
|
94
96
|
```sh
|
|
95
97
|
animaguru projects create --name "Forest cast"
|
|
@@ -102,7 +104,7 @@ animaguru files list PROJECT_ID
|
|
|
102
104
|
animaguru files export PROJECT_ID --output forest-cast.zip
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
`jobs wait` waits for the specified job only. A drawing may queue follow-up animation jobs; inspect `jobs list` and wait for those before exporting. Jobs continue on the server if the CLI exits. `files export` refuses to overwrite a file. `projects rename PROJECT_ID --name "New name"` and `projects delete PROJECT_ID --yes` manage the library. `models PROJECT_ID --image-model gpt-image-2.5 --video-model heygen/heygen-video-1` sets the defaults for future jobs. The previous `--model` option remains an alias for `--image-model`.
|
|
107
|
+
`jobs wait` waits for the specified job only. A drawing may queue follow-up animation jobs; inspect `jobs list` and wait for those before exporting. Jobs continue on the server if the CLI exits. `files export` refuses to overwrite a file. `projects rename PROJECT_ID --name "New name"` and `projects delete PROJECT_ID --yes` manage the library. `projects move PROJECT_ID --to OTHER_PROJECT_ID --characters SLUG,SLUG --assets ASSET_ID --avatars GROUP_ID` moves characters (with all their animations), saved assets and the project's own avatars to another project you can write to, without regenerating anything (CLI 0.16.0). `models PROJECT_ID --image-model gpt-image-2.5 --video-model heygen/heygen-video-1` sets the defaults for future jobs. The previous `--model` option remains an alias for `--image-model`.
|
|
106
108
|
|
|
107
109
|
Generation and reference imports require `--yes`, which authorizes credit spending. Read estimates from `projects get` before submitting. Estimates are ranges; actual provider usage is recorded to your own balance. Paid generation needs configured production provider bindings and secrets.
|
|
108
110
|
|
|
@@ -313,7 +315,7 @@ Custom motions require 2–12 frame descriptions, a 20–600 character prompt, a
|
|
|
313
315
|
Reference images:
|
|
314
316
|
|
|
315
317
|
```sh
|
|
316
|
-
animaguru files import PROJECT_ID --file knight.png --name Knight --description "Keep the blue armor" --yes
|
|
318
|
+
animaguru files import PROJECT_ID --file knight.png --name Knight --style pixel --description "Keep the blue armor" --yes
|
|
317
319
|
```
|
|
318
320
|
|
|
319
321
|
PNG, JPEG and WebP are accepted up to 10 MB. A configured service redraws the reference and charges credits; otherwise it imports the original artwork.
|
package/dist/animaguru.js
CHANGED
|
@@ -10605,12 +10605,12 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
|
|
|
10605
10605
|
* @param {string} transport
|
|
10606
10606
|
* @param {string} [suggestion]
|
|
10607
10607
|
*/
|
|
10608
|
-
constructor(
|
|
10608
|
+
constructor(url3, transport, suggestion) {
|
|
10609
10609
|
super(
|
|
10610
|
-
`Git remote "${
|
|
10610
|
+
`Git remote "${url3}" uses an unrecognized transport protocol: "${transport}"`
|
|
10611
10611
|
);
|
|
10612
10612
|
this.code = this.name = _UnknownTransportError.code;
|
|
10613
|
-
this.data = { url:
|
|
10613
|
+
this.data = { url: url3, transport, suggestion };
|
|
10614
10614
|
}
|
|
10615
10615
|
};
|
|
10616
10616
|
UnknownTransportError.code = "UnknownTransportError";
|
|
@@ -10618,10 +10618,10 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
|
|
|
10618
10618
|
/**
|
|
10619
10619
|
* @param {string} url
|
|
10620
10620
|
*/
|
|
10621
|
-
constructor(
|
|
10622
|
-
super(`Cannot parse remote URL: "${
|
|
10621
|
+
constructor(url3) {
|
|
10622
|
+
super(`Cannot parse remote URL: "${url3}"`);
|
|
10623
10623
|
this.code = this.name = _UrlParseError.code;
|
|
10624
|
-
this.data = { url:
|
|
10624
|
+
this.data = { url: url3 };
|
|
10625
10625
|
}
|
|
10626
10626
|
};
|
|
10627
10627
|
UrlParseError.code = "UrlParseError";
|
|
@@ -12487,7 +12487,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
12487
12487
|
throw err2;
|
|
12488
12488
|
}
|
|
12489
12489
|
}
|
|
12490
|
-
async function _addRemote({ fs: fs2, gitdir, remote, url:
|
|
12490
|
+
async function _addRemote({ fs: fs2, gitdir, remote, url: url3, force }) {
|
|
12491
12491
|
if (!isValidRef(remote, true)) {
|
|
12492
12492
|
throw new InvalidRefNameError(remote, cleanGitRef.clean(remote));
|
|
12493
12493
|
}
|
|
@@ -12495,12 +12495,12 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
12495
12495
|
if (!force) {
|
|
12496
12496
|
const remoteNames = await config2.getSubsections("remote");
|
|
12497
12497
|
if (remoteNames.includes(remote)) {
|
|
12498
|
-
if (
|
|
12498
|
+
if (url3 !== await config2.get(`remote.${remote}.url`)) {
|
|
12499
12499
|
throw new AlreadyExistsError("remote", remote);
|
|
12500
12500
|
}
|
|
12501
12501
|
}
|
|
12502
12502
|
}
|
|
12503
|
-
await config2.set(`remote.${remote}.url`,
|
|
12503
|
+
await config2.set(`remote.${remote}.url`, url3);
|
|
12504
12504
|
await config2.set(
|
|
12505
12505
|
`remote.${remote}.fetch`,
|
|
12506
12506
|
`+refs/heads/*:refs/remotes/${remote}/*`
|
|
@@ -12512,21 +12512,21 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
12512
12512
|
dir,
|
|
12513
12513
|
gitdir = join15(dir, ".git"),
|
|
12514
12514
|
remote,
|
|
12515
|
-
url:
|
|
12515
|
+
url: url3,
|
|
12516
12516
|
force = false
|
|
12517
12517
|
}) {
|
|
12518
12518
|
try {
|
|
12519
12519
|
assertParameter("fs", fs2);
|
|
12520
12520
|
assertParameter("gitdir", gitdir);
|
|
12521
12521
|
assertParameter("remote", remote);
|
|
12522
|
-
assertParameter("url",
|
|
12522
|
+
assertParameter("url", url3);
|
|
12523
12523
|
const fsp = new FileSystem(fs2);
|
|
12524
12524
|
const updatedGitdir = await discoverGitdir({ fsp, dotgit: gitdir });
|
|
12525
12525
|
return await _addRemote({
|
|
12526
12526
|
fs: fsp,
|
|
12527
12527
|
gitdir: updatedGitdir,
|
|
12528
12528
|
remote,
|
|
12529
|
-
url:
|
|
12529
|
+
url: url3,
|
|
12530
12530
|
force
|
|
12531
12531
|
});
|
|
12532
12532
|
} catch (err2) {
|
|
@@ -14086,10 +14086,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14086
14086
|
if (!ref.startsWith("refs/")) return;
|
|
14087
14087
|
return fullname ? ref : abbreviateRef(ref);
|
|
14088
14088
|
}
|
|
14089
|
-
function translateSSHtoHTTP(
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
return
|
|
14089
|
+
function translateSSHtoHTTP(url3) {
|
|
14090
|
+
url3 = url3.replace(/^[^/@:]+@([^/@:]+):/, "https://$1/");
|
|
14091
|
+
url3 = url3.replace(/^ssh:\/\//, "https://");
|
|
14092
|
+
return url3;
|
|
14093
14093
|
}
|
|
14094
14094
|
function calculateBasicAuthHeader({ username = "", password = "" }) {
|
|
14095
14095
|
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
@@ -14119,16 +14119,16 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14119
14119
|
return result;
|
|
14120
14120
|
}
|
|
14121
14121
|
var CREDENTIALS = /^(https?:\/\/)([^/?#]+)@/i;
|
|
14122
|
-
function extractAuthFromUrl(
|
|
14123
|
-
const credentials =
|
|
14124
|
-
if (credentials == null) return { url:
|
|
14122
|
+
function extractAuthFromUrl(url3) {
|
|
14123
|
+
const credentials = url3.match(CREDENTIALS);
|
|
14124
|
+
if (credentials == null) return { url: url3, auth: {} };
|
|
14125
14125
|
return {
|
|
14126
|
-
url:
|
|
14127
|
-
auth: parseUserinfo(
|
|
14126
|
+
url: url3.replace(CREDENTIALS, "$1"),
|
|
14127
|
+
auth: parseUserinfo(url3, credentials[2])
|
|
14128
14128
|
};
|
|
14129
14129
|
}
|
|
14130
|
-
function parseUserinfo(
|
|
14131
|
-
const parsed = parseUrl(
|
|
14130
|
+
function parseUserinfo(url3, userpass) {
|
|
14131
|
+
const parsed = parseUrl(url3);
|
|
14132
14132
|
const hasPassword = userpass.indexOf(":") !== -1;
|
|
14133
14133
|
if (parsed !== null) {
|
|
14134
14134
|
return {
|
|
@@ -14142,10 +14142,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14142
14142
|
password: hasPassword ? userpass.slice(separatorIndex + 1) : void 0
|
|
14143
14143
|
};
|
|
14144
14144
|
}
|
|
14145
|
-
function parseUrl(
|
|
14145
|
+
function parseUrl(url3) {
|
|
14146
14146
|
if (typeof URL !== "function") return null;
|
|
14147
14147
|
try {
|
|
14148
|
-
return new URL(
|
|
14148
|
+
return new URL(url3);
|
|
14149
14149
|
} catch (_) {
|
|
14150
14150
|
return null;
|
|
14151
14151
|
}
|
|
@@ -14269,7 +14269,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14269
14269
|
}
|
|
14270
14270
|
return split;
|
|
14271
14271
|
}
|
|
14272
|
-
var corsProxify = (corsProxy,
|
|
14272
|
+
var corsProxify = (corsProxy, url3) => corsProxy.endsWith("?") ? `${corsProxy}${url3}` : `${corsProxy}/${url3.replace(/^https?:\/\//, "")}`;
|
|
14273
14273
|
var updateHeaders = (headers, auth) => {
|
|
14274
14274
|
if (auth.username || auth.password) {
|
|
14275
14275
|
headers.Authorization = calculateBasicAuthHeader(auth);
|
|
@@ -14330,8 +14330,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14330
14330
|
signal,
|
|
14331
14331
|
protocolVersion
|
|
14332
14332
|
}) {
|
|
14333
|
-
let { url:
|
|
14334
|
-
const proxifiedURL = corsProxy ? corsProxify(corsProxy,
|
|
14333
|
+
let { url: url3, auth } = extractAuthFromUrl(_origUrl);
|
|
14334
|
+
const proxifiedURL = corsProxy ? corsProxify(corsProxy, url3) : url3;
|
|
14335
14335
|
if (auth.username || auth.password) {
|
|
14336
14336
|
headers.Authorization = calculateBasicAuthHeader(auth);
|
|
14337
14337
|
}
|
|
@@ -14353,7 +14353,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14353
14353
|
if (res.statusCode === 401 || res.statusCode === 203) {
|
|
14354
14354
|
const getAuth = providedAuthBefore ? onAuthFailure : onAuth;
|
|
14355
14355
|
if (getAuth) {
|
|
14356
|
-
auth = await getAuth(
|
|
14356
|
+
auth = await getAuth(url3, {
|
|
14357
14357
|
...auth,
|
|
14358
14358
|
headers: { ...headers }
|
|
14359
14359
|
});
|
|
@@ -14366,7 +14366,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14366
14366
|
}
|
|
14367
14367
|
}
|
|
14368
14368
|
} else if (res.statusCode === 200 && providedAuthBefore && onAuthSuccess) {
|
|
14369
|
-
await onAuthSuccess(
|
|
14369
|
+
await onAuthSuccess(url3, auth);
|
|
14370
14370
|
}
|
|
14371
14371
|
} while (tryAgain);
|
|
14372
14372
|
if (res.statusCode !== 200) {
|
|
@@ -14409,22 +14409,22 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14409
14409
|
onProgress,
|
|
14410
14410
|
corsProxy,
|
|
14411
14411
|
service,
|
|
14412
|
-
url:
|
|
14412
|
+
url: url3,
|
|
14413
14413
|
auth,
|
|
14414
14414
|
body,
|
|
14415
14415
|
headers,
|
|
14416
14416
|
signal
|
|
14417
14417
|
}) {
|
|
14418
|
-
const urlAuth = extractAuthFromUrl(
|
|
14419
|
-
if (urlAuth)
|
|
14420
|
-
if (corsProxy)
|
|
14418
|
+
const urlAuth = extractAuthFromUrl(url3);
|
|
14419
|
+
if (urlAuth) url3 = urlAuth.url;
|
|
14420
|
+
if (corsProxy) url3 = corsProxify(corsProxy, url3);
|
|
14421
14421
|
headers["content-type"] = `application/x-${service}-request`;
|
|
14422
14422
|
headers.accept = `application/x-${service}-result`;
|
|
14423
14423
|
updateHeaders(headers, auth);
|
|
14424
14424
|
const res = await http.request({
|
|
14425
14425
|
onProgress,
|
|
14426
14426
|
method: "POST",
|
|
14427
|
-
url: `${
|
|
14427
|
+
url: `${url3}/${service}`,
|
|
14428
14428
|
body,
|
|
14429
14429
|
headers,
|
|
14430
14430
|
signal
|
|
@@ -14446,33 +14446,33 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14446
14446
|
* @throws {UrlParseError} - If the URL cannot be parsed.
|
|
14447
14447
|
* @throws {UnknownTransportError} - If the transport is not supported.
|
|
14448
14448
|
*/
|
|
14449
|
-
static getRemoteHelperFor({ url:
|
|
14449
|
+
static getRemoteHelperFor({ url: url3 }) {
|
|
14450
14450
|
const remoteHelpers = /* @__PURE__ */ new Map();
|
|
14451
14451
|
remoteHelpers.set("http", GitRemoteHTTP);
|
|
14452
14452
|
remoteHelpers.set("https", GitRemoteHTTP);
|
|
14453
|
-
const parts = parseRemoteUrl({ url:
|
|
14453
|
+
const parts = parseRemoteUrl({ url: url3 });
|
|
14454
14454
|
if (!parts) {
|
|
14455
|
-
throw new UrlParseError(
|
|
14455
|
+
throw new UrlParseError(url3);
|
|
14456
14456
|
}
|
|
14457
14457
|
if (remoteHelpers.has(parts.transport)) {
|
|
14458
14458
|
return remoteHelpers.get(parts.transport);
|
|
14459
14459
|
}
|
|
14460
14460
|
throw new UnknownTransportError(
|
|
14461
|
-
|
|
14461
|
+
url3,
|
|
14462
14462
|
parts.transport,
|
|
14463
|
-
parts.transport === "ssh" ? translateSSHtoHTTP(
|
|
14463
|
+
parts.transport === "ssh" ? translateSSHtoHTTP(url3) : void 0
|
|
14464
14464
|
);
|
|
14465
14465
|
}
|
|
14466
14466
|
};
|
|
14467
14467
|
var scpLike = /^[^/@:]+@[^/@:]+:/;
|
|
14468
|
-
function parseRemoteUrl({ url:
|
|
14469
|
-
if (scpLike.test(
|
|
14468
|
+
function parseRemoteUrl({ url: url3 }) {
|
|
14469
|
+
if (scpLike.test(url3)) {
|
|
14470
14470
|
return {
|
|
14471
14471
|
transport: "ssh",
|
|
14472
|
-
address:
|
|
14472
|
+
address: url3
|
|
14473
14473
|
};
|
|
14474
14474
|
}
|
|
14475
|
-
const matches =
|
|
14475
|
+
const matches = url3.match(/(\w+)(:\/\/|::)(.*)/);
|
|
14476
14476
|
if (matches === null) return;
|
|
14477
14477
|
if (matches[2] === "://") {
|
|
14478
14478
|
return {
|
|
@@ -14583,9 +14583,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14583
14583
|
}
|
|
14584
14584
|
function addCredentialUsername({ config: config2, onAuth }) {
|
|
14585
14585
|
if (!onAuth) return onAuth;
|
|
14586
|
-
return async (
|
|
14587
|
-
const username = auth.username || await config2.get(`credential.${
|
|
14588
|
-
return onAuth(
|
|
14586
|
+
return async (url3, auth) => {
|
|
14587
|
+
const username = auth.username || await config2.get(`credential.${url3}.username`);
|
|
14588
|
+
return onAuth(url3, username ? { ...auth, username } : auth);
|
|
14589
14589
|
};
|
|
14590
14590
|
}
|
|
14591
14591
|
function emptyPackfile(pack) {
|
|
@@ -14911,15 +14911,15 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14911
14911
|
const ref = _ref || await _currentBranch({ fs: fs2, gitdir, test: true });
|
|
14912
14912
|
const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
|
|
14913
14913
|
const remote = _remote || ref && await config2.get(`branch.${ref}.remote`) || "origin";
|
|
14914
|
-
const
|
|
14915
|
-
if (typeof
|
|
14914
|
+
const url3 = _url2 || await config2.get(`remote.${remote}.url`);
|
|
14915
|
+
if (typeof url3 === "undefined") {
|
|
14916
14916
|
throw new MissingParameterError("remote OR url");
|
|
14917
14917
|
}
|
|
14918
14918
|
const remoteRef = _remoteRef || ref && await config2.get(`branch.${ref}.merge`) || _ref || "HEAD";
|
|
14919
14919
|
if (corsProxy === void 0) {
|
|
14920
14920
|
corsProxy = await config2.get("http.corsProxy");
|
|
14921
14921
|
}
|
|
14922
|
-
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url:
|
|
14922
|
+
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
|
|
14923
14923
|
const remoteHTTP = await GitRemoteHTTP2.discover({
|
|
14924
14924
|
http,
|
|
14925
14925
|
onAuth: addCredentialUsername({ config: config2, onAuth }),
|
|
@@ -14927,7 +14927,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
14927
14927
|
onAuthFailure: addCredentialUsername({ config: config2, onAuth: onAuthFailure }),
|
|
14928
14928
|
corsProxy,
|
|
14929
14929
|
service: "git-upload-pack",
|
|
14930
|
-
url:
|
|
14930
|
+
url: url3,
|
|
14931
14931
|
headers,
|
|
14932
14932
|
protocolVersion: 1
|
|
14933
14933
|
});
|
|
@@ -15013,7 +15013,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15013
15013
|
onProgress,
|
|
15014
15014
|
corsProxy,
|
|
15015
15015
|
service: "git-upload-pack",
|
|
15016
|
-
url:
|
|
15016
|
+
url: url3,
|
|
15017
15017
|
auth,
|
|
15018
15018
|
body: [packbuffer],
|
|
15019
15019
|
headers
|
|
@@ -15101,7 +15101,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15101
15101
|
const noun = fullref.startsWith("refs/tags") ? "tag" : "branch";
|
|
15102
15102
|
response.FETCH_HEAD = {
|
|
15103
15103
|
oid,
|
|
15104
|
-
description: `${noun} '${abbreviateRef(fullref)}' of ${
|
|
15104
|
+
description: `${noun} '${abbreviateRef(fullref)}' of ${url3}`
|
|
15105
15105
|
};
|
|
15106
15106
|
if (onProgress || onMessage) {
|
|
15107
15107
|
const lines = splitLines(response.progress);
|
|
@@ -15190,7 +15190,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15190
15190
|
onPostCheckout,
|
|
15191
15191
|
dir,
|
|
15192
15192
|
gitdir,
|
|
15193
|
-
url:
|
|
15193
|
+
url: url3,
|
|
15194
15194
|
corsProxy,
|
|
15195
15195
|
ref,
|
|
15196
15196
|
remote,
|
|
@@ -15207,7 +15207,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15207
15207
|
}) {
|
|
15208
15208
|
try {
|
|
15209
15209
|
await _init({ fs: fs2, gitdir });
|
|
15210
|
-
await _addRemote({ fs: fs2, gitdir, remote, url:
|
|
15210
|
+
await _addRemote({ fs: fs2, gitdir, remote, url: url3, force: false });
|
|
15211
15211
|
if (corsProxy) {
|
|
15212
15212
|
const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
|
|
15213
15213
|
await config2.set(`http.corsProxy`, corsProxy);
|
|
@@ -15266,7 +15266,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15266
15266
|
onPostCheckout,
|
|
15267
15267
|
dir,
|
|
15268
15268
|
gitdir = join15(dir, ".git"),
|
|
15269
|
-
url:
|
|
15269
|
+
url: url3,
|
|
15270
15270
|
corsProxy = void 0,
|
|
15271
15271
|
ref = void 0,
|
|
15272
15272
|
remote = "origin",
|
|
@@ -15289,7 +15289,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15289
15289
|
if (!noCheckout) {
|
|
15290
15290
|
assertParameter("dir", dir);
|
|
15291
15291
|
}
|
|
15292
|
-
assertParameter("url",
|
|
15292
|
+
assertParameter("url", url3);
|
|
15293
15293
|
const fsp = new FileSystem(fs2);
|
|
15294
15294
|
const updatedGitdir = await discoverGitdir({ fsp, dotgit: gitdir });
|
|
15295
15295
|
return await _clone({
|
|
@@ -15304,7 +15304,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15304
15304
|
onPostCheckout,
|
|
15305
15305
|
dir,
|
|
15306
15306
|
gitdir: updatedGitdir,
|
|
15307
|
-
url:
|
|
15307
|
+
url: url3,
|
|
15308
15308
|
corsProxy,
|
|
15309
15309
|
ref,
|
|
15310
15310
|
remote,
|
|
@@ -15781,7 +15781,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15781
15781
|
dir,
|
|
15782
15782
|
gitdir,
|
|
15783
15783
|
ref,
|
|
15784
|
-
url:
|
|
15784
|
+
url: url3,
|
|
15785
15785
|
remote,
|
|
15786
15786
|
remoteRef,
|
|
15787
15787
|
prune,
|
|
@@ -15815,7 +15815,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15815
15815
|
gitdir,
|
|
15816
15816
|
corsProxy,
|
|
15817
15817
|
ref,
|
|
15818
|
-
url:
|
|
15818
|
+
url: url3,
|
|
15819
15819
|
remote,
|
|
15820
15820
|
remoteRef,
|
|
15821
15821
|
singleBranch,
|
|
@@ -15864,7 +15864,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15864
15864
|
dir,
|
|
15865
15865
|
gitdir = join15(dir, ".git"),
|
|
15866
15866
|
ref,
|
|
15867
|
-
url:
|
|
15867
|
+
url: url3,
|
|
15868
15868
|
remote,
|
|
15869
15869
|
remoteRef,
|
|
15870
15870
|
corsProxy,
|
|
@@ -15896,7 +15896,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15896
15896
|
dir,
|
|
15897
15897
|
gitdir: updatedGitdir,
|
|
15898
15898
|
ref,
|
|
15899
|
-
url:
|
|
15899
|
+
url: url3,
|
|
15900
15900
|
remote,
|
|
15901
15901
|
remoteRef,
|
|
15902
15902
|
fastForwardOnly: true,
|
|
@@ -15924,7 +15924,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15924
15924
|
ref,
|
|
15925
15925
|
remote,
|
|
15926
15926
|
remoteRef,
|
|
15927
|
-
url:
|
|
15927
|
+
url: url3,
|
|
15928
15928
|
corsProxy,
|
|
15929
15929
|
depth = null,
|
|
15930
15930
|
since = null,
|
|
@@ -15956,7 +15956,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
15956
15956
|
ref,
|
|
15957
15957
|
remote,
|
|
15958
15958
|
remoteRef,
|
|
15959
|
-
url:
|
|
15959
|
+
url: url3,
|
|
15960
15960
|
corsProxy,
|
|
15961
15961
|
depth,
|
|
15962
15962
|
since,
|
|
@@ -16078,14 +16078,14 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16078
16078
|
onAuthSuccess,
|
|
16079
16079
|
onAuthFailure,
|
|
16080
16080
|
corsProxy,
|
|
16081
|
-
url:
|
|
16081
|
+
url: url3,
|
|
16082
16082
|
headers = {},
|
|
16083
16083
|
forPush = false
|
|
16084
16084
|
}) {
|
|
16085
16085
|
try {
|
|
16086
16086
|
assertParameter("http", http);
|
|
16087
|
-
assertParameter("url",
|
|
16088
|
-
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url:
|
|
16087
|
+
assertParameter("url", url3);
|
|
16088
|
+
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
|
|
16089
16089
|
const remote = await GitRemoteHTTP2.discover({
|
|
16090
16090
|
http,
|
|
16091
16091
|
onAuth,
|
|
@@ -16093,7 +16093,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16093
16093
|
onAuthFailure,
|
|
16094
16094
|
corsProxy,
|
|
16095
16095
|
service: forPush ? "git-receive-pack" : "git-upload-pack",
|
|
16096
|
-
url:
|
|
16096
|
+
url: url3,
|
|
16097
16097
|
headers,
|
|
16098
16098
|
protocolVersion: 1
|
|
16099
16099
|
});
|
|
@@ -16143,15 +16143,15 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16143
16143
|
onAuthSuccess,
|
|
16144
16144
|
onAuthFailure,
|
|
16145
16145
|
corsProxy,
|
|
16146
|
-
url:
|
|
16146
|
+
url: url3,
|
|
16147
16147
|
headers = {},
|
|
16148
16148
|
forPush = false,
|
|
16149
16149
|
protocolVersion = 2
|
|
16150
16150
|
}) {
|
|
16151
16151
|
try {
|
|
16152
16152
|
assertParameter("http", http);
|
|
16153
|
-
assertParameter("url",
|
|
16154
|
-
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url:
|
|
16153
|
+
assertParameter("url", url3);
|
|
16154
|
+
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
|
|
16155
16155
|
const remote = await GitRemoteHTTP2.discover({
|
|
16156
16156
|
http,
|
|
16157
16157
|
onAuth,
|
|
@@ -16159,7 +16159,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16159
16159
|
onAuthFailure,
|
|
16160
16160
|
corsProxy,
|
|
16161
16161
|
service: forPush ? "git-receive-pack" : "git-upload-pack",
|
|
16162
|
-
url:
|
|
16162
|
+
url: url3,
|
|
16163
16163
|
headers,
|
|
16164
16164
|
protocolVersion
|
|
16165
16165
|
});
|
|
@@ -16561,8 +16561,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16561
16561
|
const remoteNames = await config2.getSubsections("remote");
|
|
16562
16562
|
const remotes = Promise.all(
|
|
16563
16563
|
remoteNames.map(async (remote) => {
|
|
16564
|
-
const
|
|
16565
|
-
return { remote, url:
|
|
16564
|
+
const url3 = await config2.get(`remote.${remote}.url`);
|
|
16565
|
+
return { remote, url: url3 };
|
|
16566
16566
|
})
|
|
16567
16567
|
);
|
|
16568
16568
|
return remotes;
|
|
@@ -16625,7 +16625,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16625
16625
|
onAuthSuccess,
|
|
16626
16626
|
onAuthFailure,
|
|
16627
16627
|
corsProxy,
|
|
16628
|
-
url:
|
|
16628
|
+
url: url3,
|
|
16629
16629
|
headers = {},
|
|
16630
16630
|
forPush = false,
|
|
16631
16631
|
protocolVersion = 2,
|
|
@@ -16635,7 +16635,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16635
16635
|
}) {
|
|
16636
16636
|
try {
|
|
16637
16637
|
assertParameter("http", http);
|
|
16638
|
-
assertParameter("url",
|
|
16638
|
+
assertParameter("url", url3);
|
|
16639
16639
|
const remote = await GitRemoteHTTP.discover({
|
|
16640
16640
|
http,
|
|
16641
16641
|
onAuth,
|
|
@@ -16643,7 +16643,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16643
16643
|
onAuthFailure,
|
|
16644
16644
|
corsProxy,
|
|
16645
16645
|
service: forPush ? "git-receive-pack" : "git-upload-pack",
|
|
16646
|
-
url:
|
|
16646
|
+
url: url3,
|
|
16647
16647
|
headers,
|
|
16648
16648
|
protocolVersion
|
|
16649
16649
|
});
|
|
@@ -16657,7 +16657,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
16657
16657
|
headers,
|
|
16658
16658
|
corsProxy,
|
|
16659
16659
|
service: forPush ? "git-receive-pack" : "git-upload-pack",
|
|
16660
|
-
url:
|
|
16660
|
+
url: url3,
|
|
16661
16661
|
body
|
|
16662
16662
|
});
|
|
16663
16663
|
return parseListRefsResponse(res.body);
|
|
@@ -17114,7 +17114,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17114
17114
|
dir,
|
|
17115
17115
|
gitdir = join15(dir, ".git"),
|
|
17116
17116
|
ref,
|
|
17117
|
-
url:
|
|
17117
|
+
url: url3,
|
|
17118
17118
|
remote,
|
|
17119
17119
|
remoteRef,
|
|
17120
17120
|
prune = false,
|
|
@@ -17159,7 +17159,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17159
17159
|
dir,
|
|
17160
17160
|
gitdir: updatedGitdir,
|
|
17161
17161
|
ref,
|
|
17162
|
-
url:
|
|
17162
|
+
url: url3,
|
|
17163
17163
|
remote,
|
|
17164
17164
|
remoteRef,
|
|
17165
17165
|
fastForward: fastForward2,
|
|
@@ -17342,12 +17342,12 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17342
17342
|
}
|
|
17343
17343
|
const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
|
|
17344
17344
|
remote = remote || await config2.get(`branch.${ref}.pushRemote`) || await config2.get("remote.pushDefault") || await config2.get(`branch.${ref}.remote`) || "origin";
|
|
17345
|
-
const
|
|
17346
|
-
if (typeof
|
|
17345
|
+
const url3 = _url2 || await config2.get(`remote.${remote}.pushurl`) || await config2.get(`remote.${remote}.url`);
|
|
17346
|
+
if (typeof url3 === "undefined") {
|
|
17347
17347
|
throw new MissingParameterError("remote OR url");
|
|
17348
17348
|
}
|
|
17349
17349
|
const remoteRef = _remoteRef || await config2.get(`branch.${ref}.merge`);
|
|
17350
|
-
if (typeof
|
|
17350
|
+
if (typeof url3 === "undefined") {
|
|
17351
17351
|
throw new MissingParameterError("remoteRef");
|
|
17352
17352
|
}
|
|
17353
17353
|
if (corsProxy === void 0) {
|
|
@@ -17355,7 +17355,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17355
17355
|
}
|
|
17356
17356
|
const fullRef = await GitRefManager.expand({ fs: fs2, gitdir, ref });
|
|
17357
17357
|
const oid = _delete ? "0000000000000000000000000000000000000000" : await GitRefManager.resolve({ fs: fs2, gitdir, ref: fullRef });
|
|
17358
|
-
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url:
|
|
17358
|
+
const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
|
|
17359
17359
|
const httpRemote = await GitRemoteHTTP2.discover({
|
|
17360
17360
|
http,
|
|
17361
17361
|
onAuth: addCredentialUsername({ config: config2, onAuth }),
|
|
@@ -17363,7 +17363,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17363
17363
|
onAuthFailure: addCredentialUsername({ config: config2, onAuth: onAuthFailure }),
|
|
17364
17364
|
corsProxy,
|
|
17365
17365
|
service: "git-receive-pack",
|
|
17366
|
-
url:
|
|
17366
|
+
url: url3,
|
|
17367
17367
|
headers,
|
|
17368
17368
|
protocolVersion: 1,
|
|
17369
17369
|
signal
|
|
@@ -17390,7 +17390,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17390
17390
|
if (onPrePush) {
|
|
17391
17391
|
const hookCancel = await onPrePush({
|
|
17392
17392
|
remote,
|
|
17393
|
-
url:
|
|
17393
|
+
url: url3,
|
|
17394
17394
|
localRef: { ref: _delete ? "(delete)" : fullRef, oid },
|
|
17395
17395
|
remoteRef: { ref: fullRemoteRef, oid: oldoid }
|
|
17396
17396
|
});
|
|
@@ -17487,7 +17487,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17487
17487
|
onProgress,
|
|
17488
17488
|
corsProxy,
|
|
17489
17489
|
service: "git-receive-pack",
|
|
17490
|
-
url:
|
|
17490
|
+
url: url3,
|
|
17491
17491
|
auth,
|
|
17492
17492
|
headers,
|
|
17493
17493
|
body: [...packstream1, ...packstream2],
|
|
@@ -17537,7 +17537,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17537
17537
|
ref,
|
|
17538
17538
|
remoteRef,
|
|
17539
17539
|
remote = "origin",
|
|
17540
|
-
url:
|
|
17540
|
+
url: url3,
|
|
17541
17541
|
force = false,
|
|
17542
17542
|
delete: _delete = false,
|
|
17543
17543
|
corsProxy,
|
|
@@ -17565,7 +17565,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
|
|
|
17565
17565
|
ref,
|
|
17566
17566
|
remoteRef,
|
|
17567
17567
|
remote,
|
|
17568
|
-
url:
|
|
17568
|
+
url: url3,
|
|
17569
17569
|
force,
|
|
17570
17570
|
delete: _delete,
|
|
17571
17571
|
corsProxy,
|
|
@@ -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.16.1",
|
|
19765
19765
|
description: "Agent-friendly AnimaGuru CLI built with CrustJS",
|
|
19766
19766
|
type: "module",
|
|
19767
19767
|
bin: {
|
|
@@ -22817,16 +22817,16 @@ function serializeParams(options) {
|
|
|
22817
22817
|
if (id2.includes(":") || id2.includes(";")) throw new TypeError('Invalid hyperlink id: ":" and ";" are reserved by the OSC 8 format.');
|
|
22818
22818
|
return `id=${id2}`;
|
|
22819
22819
|
}
|
|
22820
|
-
function linkCode(
|
|
22821
|
-
assertMatches(
|
|
22820
|
+
function linkCode(url3, options) {
|
|
22821
|
+
assertMatches(url3, "hyperlink URL", /^[\x21-\x7e]*$/, " without spaces");
|
|
22822
22822
|
const params = serializeParams(options);
|
|
22823
22823
|
return {
|
|
22824
|
-
open: `${OSC}8;${params};${
|
|
22824
|
+
open: `${OSC}8;${params};${url3}${ST}`,
|
|
22825
22825
|
close: HYPERLINK_CLOSE
|
|
22826
22826
|
};
|
|
22827
22827
|
}
|
|
22828
|
-
function link$1(text2,
|
|
22829
|
-
return applyStyle(text2, linkCode(
|
|
22828
|
+
function link$1(text2, url3, options) {
|
|
22829
|
+
return applyStyle(text2, linkCode(url3, options));
|
|
22830
22830
|
}
|
|
22831
22831
|
var dynamicColorKinds = ["fg", "bg"];
|
|
22832
22832
|
function stepIsModifier(step) {
|
|
@@ -22966,9 +22966,9 @@ function createStyleInstance(options, runtime) {
|
|
|
22966
22966
|
get colorDepth() {
|
|
22967
22967
|
return resolveCapabilities().colorDepth;
|
|
22968
22968
|
},
|
|
22969
|
-
link(text2,
|
|
22970
|
-
if (resolveCapabilities().modifiersEnabled) return link$1(text2,
|
|
22971
|
-
linkCode(
|
|
22969
|
+
link(text2, url3, hyperlinkOptions) {
|
|
22970
|
+
if (resolveCapabilities().modifiersEnabled) return link$1(text2, url3, hyperlinkOptions);
|
|
22971
|
+
linkCode(url3, hyperlinkOptions);
|
|
22972
22972
|
return text2;
|
|
22973
22973
|
},
|
|
22974
22974
|
...Object.fromEntries(dynamicColorKinds.map((kind) => [kind, (...args) => {
|
|
@@ -24086,8 +24086,8 @@ function isNewerVersion(current, latest) {
|
|
|
24086
24086
|
}
|
|
24087
24087
|
async function fetchLatestVersion(packageName, registryUrl, timeoutMs) {
|
|
24088
24088
|
try {
|
|
24089
|
-
const
|
|
24090
|
-
const response = await fetch(
|
|
24089
|
+
const url3 = `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
|
|
24090
|
+
const response = await fetch(url3, {
|
|
24091
24091
|
signal: AbortSignal.timeout(timeoutMs),
|
|
24092
24092
|
headers: { Accept: "application/vnd.npm.install-v1+json" }
|
|
24093
24093
|
});
|
|
@@ -25306,7 +25306,7 @@ var toUnits = (credits) => Math.round(credits * UNITS_PER_CREDIT);
|
|
|
25306
25306
|
|
|
25307
25307
|
// shared/sprites/types.ts
|
|
25308
25308
|
var SPRITE_KINDS = ["character", "vehicle", "machine", "effect"];
|
|
25309
|
-
var SPRITE_ACTIONS = ["idle", "walk", "run", "shoot", "jump", "hurt", "death", "slide", "punch", "kick"];
|
|
25309
|
+
var SPRITE_ACTIONS = ["idle", "walk", "run", "skid-stop", "shoot", "jump", "hurt", "death", "slide", "punch", "kick"];
|
|
25310
25310
|
var SPRITE_KIND_ACTIONS = {
|
|
25311
25311
|
character: SPRITE_ACTIONS,
|
|
25312
25312
|
vehicle: ["idle", "move", "fire", "hurt", "destroyed"],
|
|
@@ -28228,21 +28228,21 @@ var asciiTabOrNewline = /[\t\n\r]/g;
|
|
|
28228
28228
|
function stripTabAndNewline(value) {
|
|
28229
28229
|
return value.replace(asciiTabOrNewline, "");
|
|
28230
28230
|
}
|
|
28231
|
-
function urlHostnameOk(
|
|
28231
|
+
function urlHostnameOk(url3, hostname3) {
|
|
28232
28232
|
hostname3.lastIndex = 0;
|
|
28233
|
-
return hostname3.test(
|
|
28233
|
+
return hostname3.test(url3.hostname);
|
|
28234
28234
|
}
|
|
28235
|
-
function urlProtocolOk(
|
|
28235
|
+
function urlProtocolOk(url3, protocol) {
|
|
28236
28236
|
protocol.lastIndex = 0;
|
|
28237
|
-
return protocol.test(
|
|
28237
|
+
return protocol.test(url3.protocol.endsWith(":") ? url3.protocol.slice(0, -1) : url3.protocol);
|
|
28238
28238
|
}
|
|
28239
28239
|
var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
28240
28240
|
$ZodStringFormat.init(inst, def);
|
|
28241
28241
|
inst._zod.check = (payload) => {
|
|
28242
28242
|
try {
|
|
28243
28243
|
const trimmed = payload.value.trim();
|
|
28244
|
-
const
|
|
28245
|
-
if (
|
|
28244
|
+
const url3 = validateURL(trimmed, def);
|
|
28245
|
+
if (url3 === URL_BAD_FORMAT) {
|
|
28246
28246
|
payload.issues.push({
|
|
28247
28247
|
code: "invalid_format",
|
|
28248
28248
|
format: "url",
|
|
@@ -28253,7 +28253,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
|
28253
28253
|
});
|
|
28254
28254
|
return;
|
|
28255
28255
|
}
|
|
28256
|
-
if (
|
|
28256
|
+
if (url3 === URL_UNPARSEABLE) {
|
|
28257
28257
|
payload.issues.push({
|
|
28258
28258
|
code: "invalid_format",
|
|
28259
28259
|
format: "url",
|
|
@@ -28263,11 +28263,11 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
|
28263
28263
|
});
|
|
28264
28264
|
return;
|
|
28265
28265
|
}
|
|
28266
|
-
if (
|
|
28266
|
+
if (url3 === true) {
|
|
28267
28267
|
payload.value = stripTabAndNewline(trimmed);
|
|
28268
28268
|
return;
|
|
28269
28269
|
}
|
|
28270
|
-
if (def.hostname && !urlHostnameOk(
|
|
28270
|
+
if (def.hostname && !urlHostnameOk(url3, def.hostname)) {
|
|
28271
28271
|
payload.issues.push({
|
|
28272
28272
|
code: "invalid_format",
|
|
28273
28273
|
format: "url",
|
|
@@ -28278,7 +28278,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
|
28278
28278
|
continue: !def.abort
|
|
28279
28279
|
});
|
|
28280
28280
|
}
|
|
28281
|
-
if (def.protocol && !urlProtocolOk(
|
|
28281
|
+
if (def.protocol && !urlProtocolOk(url3, def.protocol)) {
|
|
28282
28282
|
payload.issues.push({
|
|
28283
28283
|
code: "invalid_format",
|
|
28284
28284
|
format: "url",
|
|
@@ -28289,7 +28289,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
|
|
|
28289
28289
|
continue: !def.abort
|
|
28290
28290
|
});
|
|
28291
28291
|
}
|
|
28292
|
-
payload.value = def.normalize ?
|
|
28292
|
+
payload.value = def.normalize ? url3.href : stripTabAndNewline(trimmed);
|
|
28293
28293
|
return;
|
|
28294
28294
|
} catch (_) {
|
|
28295
28295
|
payload.issues.push({
|
|
@@ -45105,10 +45105,10 @@ import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:pa
|
|
|
45105
45105
|
import { homedir as homedir3 } from "node:os";
|
|
45106
45106
|
import { createHash, randomUUID } from "node:crypto";
|
|
45107
45107
|
function serverUrl(value) {
|
|
45108
|
-
const
|
|
45109
|
-
if (
|
|
45110
|
-
if (
|
|
45111
|
-
return
|
|
45108
|
+
const url3 = new URL(value);
|
|
45109
|
+
if (url3.username || url3.password || url3.search || url3.hash || url3.pathname !== "/") throw new Error("Use an origin URL without credentials, path, query or fragment.");
|
|
45110
|
+
if (url3.protocol !== "https:" && !(url3.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname))) throw new Error("Use HTTPS, except for local development.");
|
|
45111
|
+
return url3.origin;
|
|
45112
45112
|
}
|
|
45113
45113
|
function credentialsPath() {
|
|
45114
45114
|
return resolve3(process.env.ANIMAGURU_CONFIG_DIR ?? join4(homedir3(), ".config", "animaguru"), "credentials.json");
|
|
@@ -46168,7 +46168,7 @@ async function createVideoProject(options, installer = installDependencies) {
|
|
|
46168
46168
|
const manifest = JSON.parse(await readFile4(packagePath, "utf8"));
|
|
46169
46169
|
manifest.name = basename2(directory).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100) || "animaguru-video";
|
|
46170
46170
|
await writeFile6(packagePath, JSON.stringify(manifest, null, 2) + "\n");
|
|
46171
|
-
const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
|
|
46171
|
+
const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
|
|
46172
46172
|
const pixiSkillsRoot = join6(packageRoot, "vendor/pixijs-skills/skills");
|
|
46173
46173
|
const pixiSkills = (await readdir2(pixiSkillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
46174
46174
|
for (const agent of [".agents", ".claude"]) {
|
|
@@ -46402,6 +46402,9 @@ var assetRequest = external_exports.discriminatedUnion("type", [
|
|
|
46402
46402
|
}).transform((p) => ({ ...p, voice: p.voice || (isElevenLabsSpeech(p.model) ? DEFAULT_ELEVENLABS_VOICE : "Kore") })),
|
|
46403
46403
|
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) })
|
|
46404
46404
|
]);
|
|
46405
|
+
var LIBRARY_AUDIO_MODEL = "library-audio";
|
|
46406
|
+
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) });
|
|
46407
|
+
var savedAssetOptions = external_exports.union([assetRequest, libraryAudio]);
|
|
46405
46408
|
var speechCharacters = (text2) => Array.from(text2).length;
|
|
46406
46409
|
function assetEstimate(p, now = Date.now()) {
|
|
46407
46410
|
if (p.type === "image") return Math.ceil(130 * spriteImageModel(p.model).relCost);
|
|
@@ -47088,7 +47091,7 @@ var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
|
|
|
47088
47091
|
var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
|
|
47089
47092
|
var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
|
|
47090
47093
|
var name = external_exports.string().trim().min(1).max(80);
|
|
47091
|
-
var https = external_exports.string().trim().max(2048).url().refine((
|
|
47094
|
+
var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
|
|
47092
47095
|
var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
47093
47096
|
var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
|
|
47094
47097
|
var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
|
|
@@ -47458,10 +47461,10 @@ async function providerError(response) {
|
|
|
47458
47461
|
if (response.status === 403) return new YouTubeError("YouTube denied this request. Check channel permissions and your Google project's YouTube API configuration.", false, false, reason);
|
|
47459
47462
|
return new YouTubeError(`YouTube request failed (${response.status}${reason ? `: ${reason}` : ""}).`, response.status === 429 || response.status >= 500, false, reason);
|
|
47460
47463
|
}
|
|
47461
|
-
async function googleFetch(
|
|
47464
|
+
async function googleFetch(url3, init, signal) {
|
|
47462
47465
|
signal?.throwIfAborted();
|
|
47463
47466
|
try {
|
|
47464
|
-
return await fetch(
|
|
47467
|
+
return await fetch(url3, { ...init, redirect: "manual", signal: AbortSignal.any([AbortSignal.timeout(18e4), ...signal ? [signal] : []]) });
|
|
47465
47468
|
} catch {
|
|
47466
47469
|
signal?.throwIfAborted();
|
|
47467
47470
|
throw new YouTubeError("YouTube connection interrupted.", true);
|
|
@@ -47493,8 +47496,8 @@ async function accessToken(credentials, signal, force = false) {
|
|
|
47493
47496
|
await saveYouTubeCredentials(credentials);
|
|
47494
47497
|
return credentials.accessToken;
|
|
47495
47498
|
}
|
|
47496
|
-
async function youtubeRequest(credentials,
|
|
47497
|
-
const send = (token3) => googleFetch(
|
|
47499
|
+
async function youtubeRequest(credentials, url3, init = {}, signal) {
|
|
47500
|
+
const send = (token3) => googleFetch(url3, { ...init, headers: { ...init.headers, Authorization: `Bearer ${token3}` } }, signal);
|
|
47498
47501
|
let response = await send(await accessToken(credentials, signal));
|
|
47499
47502
|
if (response.status === 401) {
|
|
47500
47503
|
await response.body?.cancel();
|
|
@@ -47567,26 +47570,26 @@ async function youtubeLogin(options) {
|
|
|
47567
47570
|
response.setHeader("Referrer-Policy", "no-referrer");
|
|
47568
47571
|
response.setHeader("Content-Type", "text/plain; charset=UTF-8");
|
|
47569
47572
|
response.setHeader("Content-Security-Policy", "default-src 'none'");
|
|
47570
|
-
let
|
|
47573
|
+
let url3;
|
|
47571
47574
|
try {
|
|
47572
|
-
|
|
47575
|
+
url3 = new URL(request2.url ?? "/", redirectUri);
|
|
47573
47576
|
} catch {
|
|
47574
47577
|
response.writeHead(400).end("Invalid request.");
|
|
47575
47578
|
return;
|
|
47576
47579
|
}
|
|
47577
|
-
if (request2.method !== "GET" || request2.headers.host !== new URL(redirectUri).host ||
|
|
47580
|
+
if (request2.method !== "GET" || request2.headers.host !== new URL(redirectUri).host || url3.pathname !== "/oauth2callback" || (request2.url?.length ?? 0) > 8192) {
|
|
47578
47581
|
response.writeHead(404).end("Not found.");
|
|
47579
47582
|
return;
|
|
47580
47583
|
}
|
|
47581
|
-
const returnedState = Buffer.from(
|
|
47584
|
+
const returnedState = Buffer.from(url3.searchParams.get("state") ?? "");
|
|
47582
47585
|
const expectedState = Buffer.from(state);
|
|
47583
47586
|
if (used || returnedState.length !== expectedState.length || !timingSafeEqual(returnedState, expectedState)) {
|
|
47584
47587
|
response.writeHead(400).end("Invalid authorization state.");
|
|
47585
47588
|
return;
|
|
47586
47589
|
}
|
|
47587
47590
|
used = true;
|
|
47588
|
-
const code =
|
|
47589
|
-
if (
|
|
47591
|
+
const code = url3.searchParams.get("code");
|
|
47592
|
+
if (url3.searchParams.has("error") || !code || code.length > 4096) {
|
|
47590
47593
|
response.writeHead(400).end("Authorization cancelled. Return to your terminal.");
|
|
47591
47594
|
fail(new Error("YouTube authorization cancelled."));
|
|
47592
47595
|
return;
|
|
@@ -47607,11 +47610,11 @@ async function youtubeLogin(options) {
|
|
|
47607
47610
|
let token3;
|
|
47608
47611
|
try {
|
|
47609
47612
|
options.signal?.throwIfAborted();
|
|
47610
|
-
const
|
|
47611
|
-
|
|
47613
|
+
const url3 = new URL("https://accounts.google.com/o/oauth2/v2/auth");
|
|
47614
|
+
url3.search = new URLSearchParams({ client_id: client.clientId, redirect_uri: redirectUri, response_type: "code", scope: requested.join(" "), state, code_challenge: createHash4("sha256").update(verifier).digest("base64url"), code_challenge_method: "S256", access_type: "offline", prompt: "consent select_account" }).toString();
|
|
47612
47615
|
options.log?.("YouTube authorization stays on this machine. Tokens and upload receipts are saved in owner-only local files. Video bytes go directly to Google. Google privacy: https://policies.google.com/privacy \xB7 YouTube terms: https://www.youtube.com/t/terms");
|
|
47613
|
-
options.log?.(`Open ${
|
|
47614
|
-
if (options.browser !== false) await options.openBrowser?.(
|
|
47616
|
+
options.log?.(`Open ${url3.href}`);
|
|
47617
|
+
if (options.browser !== false) await options.openBrowser?.(url3.href);
|
|
47615
47618
|
const body = await tokenRequest(client, { grant_type: "authorization_code", code: await callback, code_verifier: verifier, redirect_uri: redirectUri }, options.signal);
|
|
47616
47619
|
token3 = typeof body.refresh_token === "string" ? body.refresh_token : typeof body.access_token === "string" ? body.access_token : void 0;
|
|
47617
47620
|
const scopes = typeof body.scope === "string" ? body.scope.split(" ") : [];
|
|
@@ -47746,7 +47749,7 @@ function fromStream(stream) {
|
|
|
47746
47749
|
}
|
|
47747
47750
|
async function request({
|
|
47748
47751
|
onProgress,
|
|
47749
|
-
url:
|
|
47752
|
+
url: url3,
|
|
47750
47753
|
method = "GET",
|
|
47751
47754
|
headers = {},
|
|
47752
47755
|
fetchOptions = {},
|
|
@@ -47756,7 +47759,7 @@ async function request({
|
|
|
47756
47759
|
if (body) {
|
|
47757
47760
|
body = await collect(body);
|
|
47758
47761
|
}
|
|
47759
|
-
const res = await fetch(
|
|
47762
|
+
const res = await fetch(url3, {
|
|
47760
47763
|
...fetchOptions,
|
|
47761
47764
|
method,
|
|
47762
47765
|
headers,
|
|
@@ -47947,13 +47950,13 @@ async function withLock(root, run) {
|
|
|
47947
47950
|
await rm2(path, { force: true });
|
|
47948
47951
|
}
|
|
47949
47952
|
}
|
|
47950
|
-
async function openContext(dir, options,
|
|
47953
|
+
async function openContext(dir, options, url3) {
|
|
47951
47954
|
const root = await cloudRootFor(dir);
|
|
47952
47955
|
const link2 = await readJson2(join11(root, LINK_FILE));
|
|
47953
47956
|
if (!link2) throw new Error("This folder is not linked to a cloud project. Run animaguru cloud link, or animaguru cloud clone ID.");
|
|
47954
47957
|
if (link2.schemaVersion !== 1 || !cloudProjectId.safeParse(link2.projectId).success) throw new Error(`Unsupported cloud link in ${LINK_FILE}. Run animaguru cloud unlink and link again.`);
|
|
47955
47958
|
const state = await readJson2(join11(root, STATE_FILE)) ?? { schemaVersion: 1, base: null, files: {} };
|
|
47956
|
-
return { root, gitdir: join11(root, GIT_DIR), link: link2, state, client: new Client(
|
|
47959
|
+
return { root, gitdir: join11(root, GIT_DIR), link: link2, state, client: new Client(url3 ?? link2.origin, options.signal), options };
|
|
47957
47960
|
}
|
|
47958
47961
|
async function cloudLink(dir) {
|
|
47959
47962
|
const link2 = await readJson2(join11(await cloudRootFor(dir), LINK_FILE));
|
|
@@ -48431,14 +48434,14 @@ async function restoreCloud(options) {
|
|
|
48431
48434
|
// cli/src/youtube-upload.ts
|
|
48432
48435
|
var YOUTUBE_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
48433
48436
|
function safeYouTubeSession(value) {
|
|
48434
|
-
let
|
|
48437
|
+
let url3;
|
|
48435
48438
|
try {
|
|
48436
|
-
|
|
48439
|
+
url3 = new URL(value);
|
|
48437
48440
|
} catch {
|
|
48438
48441
|
throw new YouTubeError("Google returned an invalid upload session.");
|
|
48439
48442
|
}
|
|
48440
|
-
if (
|
|
48441
|
-
return
|
|
48443
|
+
if (url3.protocol !== "https:" || !["www.googleapis.com", "youtube.googleapis.com"].includes(url3.hostname) || url3.port || url3.username || url3.password || url3.hash || !/^\/upload\/youtube\/v3\/videos\/?$/.test(url3.pathname) || !url3.searchParams.get("upload_id")) throw new YouTubeError("Google returned an invalid upload session.");
|
|
48444
|
+
return url3.href;
|
|
48442
48445
|
}
|
|
48443
48446
|
async function inspectMp4(file2, signal) {
|
|
48444
48447
|
const handle = await open6(file2, "r");
|
|
@@ -48632,14 +48635,14 @@ async function completed(job, response) {
|
|
|
48632
48635
|
return youtubeJobSummary(job);
|
|
48633
48636
|
}
|
|
48634
48637
|
async function transfer(credentials, job, source, options) {
|
|
48635
|
-
const
|
|
48638
|
+
const url3 = safeYouTubeSession(job.sessionUrl);
|
|
48636
48639
|
let attempts = 0;
|
|
48637
48640
|
let stalled = 0;
|
|
48638
48641
|
try {
|
|
48639
48642
|
while (true) {
|
|
48640
48643
|
await source.check();
|
|
48641
48644
|
try {
|
|
48642
|
-
const probe = await youtubeRequest(credentials,
|
|
48645
|
+
const probe = await youtubeRequest(credentials, url3, { method: "PUT", headers: { "Content-Range": `bytes */${job.bytes}`, "Content-Length": "0" }, body: Buffer.alloc(0) }, options.signal);
|
|
48643
48646
|
if (probe.ok) return await completed(job, probe);
|
|
48644
48647
|
const offset = receivedBytes(probe, job.bytes);
|
|
48645
48648
|
await probe.body?.cancel();
|
|
@@ -48648,7 +48651,7 @@ async function transfer(credentials, job, source, options) {
|
|
|
48648
48651
|
const read2 = await source.handle.read(bytes, 0, bytes.length, offset);
|
|
48649
48652
|
if (read2.bytesRead !== bytes.length) throw new Error("The MP4 changed during upload. Restore it before resuming.");
|
|
48650
48653
|
await source.check();
|
|
48651
|
-
const response = await youtubeRequest(credentials,
|
|
48654
|
+
const response = await youtubeRequest(credentials, url3, { method: "PUT", headers: { "Content-Type": "video/mp4", "Content-Length": String(bytes.length), "Content-Range": `bytes ${offset}-${offset + bytes.length - 1}/${job.bytes}` }, body: new Uint8Array(bytes) }, options.signal);
|
|
48652
48655
|
if (response.ok) return await completed(job, response);
|
|
48653
48656
|
const uploaded = receivedBytes(response, job.bytes);
|
|
48654
48657
|
await response.body?.cancel();
|
|
@@ -48891,18 +48894,32 @@ var metadataFields = {
|
|
|
48891
48894
|
/** Facts the server cannot read from the file, such as tempo or mood; they override what it measured. */
|
|
48892
48895
|
details: detailsInput.default({})
|
|
48893
48896
|
};
|
|
48897
|
+
var LIBRARY_UPLOAD_MAX_BYTES = 95 * 1024 * 1024;
|
|
48898
|
+
var libraryBlobSha = external_exports.string().regex(/^[a-f0-9]{64}$/, "Use the file's lowercase hex SHA-256.");
|
|
48899
|
+
var url2 = external_exports.string().trim().url().max(500);
|
|
48900
|
+
var libraryOrigin = external_exports.object({
|
|
48901
|
+
name: text(80),
|
|
48902
|
+
url: url2,
|
|
48903
|
+
author: external_exports.string().trim().min(1).max(120).optional(),
|
|
48904
|
+
authorUrl: url2.optional(),
|
|
48905
|
+
license: text(80),
|
|
48906
|
+
licenseUrl: url2.optional()
|
|
48907
|
+
});
|
|
48894
48908
|
var libraryPublishBody = external_exports.object({
|
|
48895
48909
|
/** Defaults to the title as a slug. */
|
|
48896
48910
|
id: slug.optional(),
|
|
48897
|
-
|
|
48911
|
+
/** The project an asset or sprite comes from; uploads have none. */
|
|
48912
|
+
projectId: external_exports.string().regex(/^prj_[A-Za-z0-9]{8,40}$/).optional(),
|
|
48898
48913
|
source: external_exports.discriminatedUnion("kind", [
|
|
48899
48914
|
external_exports.object({ kind: external_exports.literal("asset"), assetId: external_exports.string().uuid() }),
|
|
48900
|
-
external_exports.object({ kind: external_exports.literal("sprite"), character: slug })
|
|
48915
|
+
external_exports.object({ kind: external_exports.literal("sprite"), character: slug }),
|
|
48916
|
+
/** Audio uploaded with PUT /api/v1/asset-library/uploads/SHA256, e.g. a CC0 recording, with who made it. */
|
|
48917
|
+
external_exports.object({ kind: external_exports.literal("upload"), type: external_exports.enum(["music", "sfx"]), sha: libraryBlobSha, origin: libraryOrigin })
|
|
48901
48918
|
]),
|
|
48902
48919
|
/** Replace an existing entry with the same id. */
|
|
48903
48920
|
replace: external_exports.boolean().default(false),
|
|
48904
48921
|
...metadataFields
|
|
48905
|
-
});
|
|
48922
|
+
}).refine((body) => body.source.kind === "upload" || body.projectId, { message: "Choose a project.", path: ["projectId"] });
|
|
48906
48923
|
var libraryUpdateBody = external_exports.object({ ...metadataFields, keywords: keywordList, details: detailsInput }).partial().refine((value) => Object.keys(value).length > 0, "Change at least one field.");
|
|
48907
48924
|
|
|
48908
48925
|
// cli/src/library.ts
|
|
@@ -48961,6 +48978,24 @@ async function updateLibraryAsset(client, id2, flags) {
|
|
|
48961
48978
|
return client.request(libraryPath(id2), { method: "PATCH", body: checked(libraryUpdateBody.safeParse(await words(flags))), timeout: 12e4 });
|
|
48962
48979
|
}
|
|
48963
48980
|
|
|
48981
|
+
// shared/projects.ts
|
|
48982
|
+
var MOVE_WORK_MAX = 50;
|
|
48983
|
+
var moveList = (item) => external_exports.array(item).max(MOVE_WORK_MAX).default([]);
|
|
48984
|
+
var moveWorkBody = external_exports.object({
|
|
48985
|
+
to: external_exports.string().trim().regex(/^prj_[A-Za-z0-9]{8,40}$/, "Choose the project to move the work to."),
|
|
48986
|
+
characters: moveList(external_exports.string().trim().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}$/, "Use a character id from the project.")),
|
|
48987
|
+
assets: moveList(external_exports.string().trim().uuid("Use an asset id from the project.")),
|
|
48988
|
+
avatars: moveList(heygenId)
|
|
48989
|
+
}).refine((body) => body.characters.length + body.assets.length + body.avatars.length > 0, { message: "Choose at least one character, asset or avatar to move." }).refine((body) => body.characters.length + body.assets.length + body.avatars.length <= MOVE_WORK_MAX, { message: `Move at most ${MOVE_WORK_MAX} items at a time.` });
|
|
48990
|
+
|
|
48991
|
+
// cli/src/projects.ts
|
|
48992
|
+
var commaList2 = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean);
|
|
48993
|
+
async function moveWork(client, project, flags) {
|
|
48994
|
+
const parsed = moveWorkBody.safeParse({ to: flags.to, characters: commaList2(flags.characters), assets: commaList2(flags.assets), avatars: commaList2(flags.avatars) });
|
|
48995
|
+
if (!parsed.success) throw new Error(parsed.error.issues[0]?.message ?? "Choose what to move with --characters, --assets or --avatars.");
|
|
48996
|
+
return client.request(`/api/v1/sprites/${encodeURIComponent(project)}/move`, { method: "POST", body: parsed.data });
|
|
48997
|
+
}
|
|
48998
|
+
|
|
48964
48999
|
// cli/src/commands.ts
|
|
48965
49000
|
var assetModelIds = [...SPRITE_IMAGE_MODEL_IDS, ...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id)];
|
|
48966
49001
|
var assetAuthorizationFlags = [
|
|
@@ -49007,9 +49042,9 @@ var workspaceQuery = async (client) => {
|
|
|
49007
49042
|
const selected = await selectedWorkspace(client.origin);
|
|
49008
49043
|
return selected === "me" ? "" : `?workspace=${encodeURIComponent(selected)}`;
|
|
49009
49044
|
};
|
|
49010
|
-
async function openBrowser(
|
|
49045
|
+
async function openBrowser(url3) {
|
|
49011
49046
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
49012
|
-
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler",
|
|
49047
|
+
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url3] : [url3];
|
|
49013
49048
|
await new Promise((resolve15) => {
|
|
49014
49049
|
const child = spawn3(command, args, { stdio: "ignore", detached: true });
|
|
49015
49050
|
child.once("error", () => resolve15());
|
|
@@ -49179,7 +49214,12 @@ Confirm code: ${device.user_code}`);
|
|
|
49179
49214
|
const client = new Client(flags.url, signal);
|
|
49180
49215
|
const selected = await selectedWorkspace(client.origin);
|
|
49181
49216
|
out(stdout, await client.request("/api/v1/sprites", { method: "POST", body: { owner: selected, name: flags.name, ...selectedModels(flags) } }));
|
|
49182
|
-
}))).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("
|
|
49217
|
+
}))).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("move", { description: "Move characters (with their animations), saved assets and avatars to another project you can write to" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(
|
|
49218
|
+
{ name: "to", type: "string", required: true, description: "Project ID to move the work to" },
|
|
49219
|
+
{ name: "characters", type: "string", description: "Comma-separated character slugs from projects get" },
|
|
49220
|
+
{ name: "assets", type: "string", description: "Comma-separated asset IDs from assets list" },
|
|
49221
|
+
{ name: "avatars", type: "string", description: "Comma-separated avatar group IDs from avatars list --ownership private" }
|
|
49222
|
+
).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 }) => {
|
|
49183
49223
|
if (!flags.yes) throw new Error("Pass --yes to delete this project.");
|
|
49184
49224
|
out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
|
|
49185
49225
|
})))).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, video, music, speech or sound-effect asset; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
|
|
@@ -49260,7 +49300,7 @@ Confirm code: ${device.user_code}`);
|
|
|
49260
49300
|
if (!flags.yes) throw new Error("Pass --yes to authorize spending credits.");
|
|
49261
49301
|
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) });
|
|
49262
49302
|
out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/jobs`, { method: "POST", body }));
|
|
49263
|
-
}))).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 }) => {
|
|
49303
|
+
}))).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 and draw it in --style; --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: "style", type: "string", choices: SPRITE_STYLES, description: "Artwork style to draw it in (default hd-cartoon)" }, { name: "yes", type: "boolean", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
|
49264
49304
|
if (!flags.yes) throw new Error("Pass --yes to authorize reference redraw costs.");
|
|
49265
49305
|
const extension = flags.file.split(".").at(-1)?.toLowerCase();
|
|
49266
49306
|
const type = extension === "png" ? "image/png" : extension === "jpg" || extension === "jpeg" ? "image/jpeg" : extension === "webp" ? "image/webp" : void 0;
|
|
@@ -49271,6 +49311,7 @@ Confirm code: ${device.user_code}`);
|
|
|
49271
49311
|
form.set("image", new Blob([bytes], { type }), `reference.${extension}`);
|
|
49272
49312
|
form.set("name", flags.name);
|
|
49273
49313
|
if (flags.description) form.set("description", flags.description);
|
|
49314
|
+
if (flags.style) form.set("style", flags.style);
|
|
49274
49315
|
for (const [key, value] of Object.entries(selectedModels(flags))) form.set(key, value);
|
|
49275
49316
|
out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/reference`, { method: "POST", body: form }));
|
|
49276
49317
|
}))).add(defineCommand("list", { description: "List project file paths and content hashes" }, (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(`/api/v1/projects/${encodeURIComponent(args.id)}/files`))))).add(defineCommand("export", { description: "Download all project assets as a ZIP; refuses to overwrite" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags({ name: "output", type: "string", required: true }).action(async ({ args, flags, stdout, signal }) => {
|
package/package.json
CHANGED
|
@@ -39,7 +39,7 @@ CLI/API balances, estimates, credit ranges and queued-job receipts use AnimaGuru
|
|
|
39
39
|
|
|
40
40
|
## Standalone images, video, music and speech
|
|
41
41
|
|
|
42
|
-
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
|
|
42
|
+
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 asset projects with `projects create --name NAME`. Projects have no art style; choose the style for each sprite with `generate --style` or `files import --style`.
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
45
|
animaguru assets generate PROJECT_ID --type image --name "Forest" \
|
|
@@ -75,6 +75,8 @@ animaguru files export PROJECT_ID --output forest-cast.zip
|
|
|
75
75
|
|
|
76
76
|
`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. 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.
|
|
77
77
|
|
|
78
|
+
`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 project's own avatars. Use it to gather work into one project instead of regenerating it; it needs CLI 0.16.0.
|
|
79
|
+
|
|
78
80
|
`files export` downloads a ZIP and refuses 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.
|
|
79
81
|
|
|
80
82
|
## Shared workspace commands
|
|
@@ -26,4 +26,4 @@ Custom motion:
|
|
|
26
26
|
|
|
27
27
|
Custom motions require 2–12 frame descriptions, a 20–600 character prompt, and 4–24 fps. Explicit model flags override selections in the JSON file.
|
|
28
28
|
|
|
29
|
-
Import reference artwork with `animaguru files import PROJECT_ID --file knight.png --name Knight --description "Keep the blue armor" --yes`. PNG, JPEG, and WebP are accepted up to 10 MB. When configured, the service redraws the reference and charges credits; otherwise it imports the original artwork.
|
|
29
|
+
Import reference artwork with `animaguru files import PROJECT_ID --file knight.png --name Knight --style pixel --description "Keep the blue armor" --yes`. PNG, JPEG, and WebP are accepted up to 10 MB. `--style` picks the art style to redraw it in (projects have none; without it the drawing is hd-cartoon, and it needs CLI 0.16.0). When configured, the service redraws the reference and charges credits; otherwise it imports the original artwork.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: animaguru-cutcraft
|
|
3
|
+
description: Create AnimaGuru Cutcraft explainers and product stories with tactile paper collage, precise editorial typography, purposeful motion, and AnimaGuru-generated media. Use for this visual treatment or a Vox-inspired explainer adapted to AnimaGuru.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AnimaGuru Cutcraft
|
|
7
|
+
|
|
8
|
+
Make an original editorial film that explains an idea through things the viewer can see move, connect, separate and resolve. Use AnimaGuru for generation and a controlled composition for titles, labels, timing and branding. The style can serve another product; use that product's facts and identity.
|
|
9
|
+
|
|
10
|
+
## Establish the film
|
|
11
|
+
|
|
12
|
+
Reuse the user's subject, references, language, placement and generation authorization. Resolve only consequential missing choices. For an exploratory sample, state sensible assumptions and proceed: a short English landscape film with a single narrator is a useful starting point, not a requirement for every film.
|
|
13
|
+
|
|
14
|
+
Read first-party product material before writing claims. Separate actual product footage from conceptual illustrations in the production records. Avoid unverified performance claims, invented customer evidence, and promises of perfect AI results. End an advertisement with a relevant invitation; end an educational explainer on its insight unless a CTA was requested.
|
|
15
|
+
|
|
16
|
+
Write a concise narration and a beat sheet pairing each spoken idea with a visible event. Duration follows the measured voice and the user's brief. A problem, a concrete mechanism, an outcome and a short close can work for a product film; choose the structure that fits the subject.
|
|
17
|
+
|
|
18
|
+
## The Cutcraft visual language
|
|
19
|
+
|
|
20
|
+
- **Material:** matte colored cardstock, visible fibers, slightly irregular scissor edges and short directional contact shadows. Layer flat pieces; keep silhouettes readable. Avoid glossy plastic and unrelated photorealistic environments.
|
|
21
|
+
- **AnimaGuru palette:** warm cream `#F7F2E8`, ink `#162B38`, brand blue `#2C6BD0`, orange `#E67D43`, with restrained sage `#93AA8B`. For client films, replace accents with the client's brand. Backgrounds should have quiet areas for type.
|
|
22
|
+
- **Typography:** one bold editorial heading family and a compatible reading face. Use actual local fonts, vector logos and rendered text for names, numbers and claims. Keep generated footage free of essential lettering. Short labels summarize; narration carries the full sentence.
|
|
23
|
+
- **Composition:** one dominant idea per beat, useful negative space, visibly separated foreground and background. A print-like grid, small folio marks and a fine blue connector line give the film its own identity. Avoid copying Vox's logo, layouts or other identifying brand elements.
|
|
24
|
+
- **Motion:** cutouts slide, rotate at plausible paper joints, pin into place, peel, stack or reveal. A connector is drawn when a relationship becomes clear. Motion answers the spoken line; texture and camera movement remain restrained.
|
|
25
|
+
- **Continuity:** lock material, light direction, palette, camera language and recurring character description. An orange fox with a cobalt scarf is an optional AnimaGuru motif, not compulsory casting for other subjects.
|
|
26
|
+
- **Sound:** one narrator across the film, a quiet instrumental bed and a few motivated paper taps or rustles. Keep speech intelligible and let the closing thought breathe.
|
|
27
|
+
|
|
28
|
+
Build a visual explanation instead of using the same decorative collage under every sentence. If illustrating control, visibly move one object while its surroundings stay fixed. If explaining a connected workflow, show the pieces joining in the order the narration names them.
|
|
29
|
+
|
|
30
|
+
## Generate with AnimaGuru
|
|
31
|
+
|
|
32
|
+
Read [the generation adapter](references/generation.md) when producing media. It covers CLI discovery, supported references, credit estimates, continuity and local assembly. Do not substitute MaxFusion calls or assume a Google Omni model exists.
|
|
33
|
+
|
|
34
|
+
Prefer the installed AnimaGuru CLI for local asset work; use connected MCP tools when they are available and appropriate. Creating this skill is not generation authorization. A user request to generate the film authorizes a proportionate initial attempt; honor stated budgets and surface a concrete estimate before submission. Do not purchase credits or change provider defaults as part of film production.
|
|
35
|
+
|
|
36
|
+
Generate artwork and motion separately from exact type. For a short trial, a few strong compositions with changing editorial labels usually communicate more than many loosely related clips. Reuse good existing textures, music or SFX when they fit. A generated clip may be one layer inside a controlled film rather than the entire frame.
|
|
37
|
+
|
|
38
|
+
For precise placement or when video providers are unavailable, generate isolated transparent paper subjects and animate their transforms in the composition. Build the fixed setting from separate paper layers. This is a complete production route: character movement, path reveals, clip placement and typography can remain editable without a video-model call. Describe it as animation of generated artwork, not as AI-generated video footage.
|
|
39
|
+
|
|
40
|
+
Use a single speech asset or consistently configured segments. Measure the recording before finalizing scenes; do not assume the requested pacing was achieved. Replace or mute incidental speech from video generation when using the separate narrator.
|
|
41
|
+
|
|
42
|
+
## Review and deliver
|
|
43
|
+
|
|
44
|
+
Keep the brief, narration, scene timings, request settings, estimates, job IDs, returned asset IDs, costs and source links together with the film's production files. Preserve an editable composition or reproducible render recipe and local copies of the media.
|
|
45
|
+
|
|
46
|
+
Inspect the actual opening, middle, transitions and closing frames. Check exact brand spelling, readable labels, character consistency, contrast, crop and any model artifacts. Check audio duration and clipping; listen when playback tools permit, and describe listening limitations honestly. Repair the composition before buying another generation when timing, crop or typography can solve the problem.
|
|
47
|
+
|
|
48
|
+
Export a playable local film and report what was generated, what was reused, and any remaining defects. Return a preview, the final file, and a link to the skill. Creating a film does not imply publishing it publicly or changing the product website.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AnimaGuru generation adapter
|
|
2
|
+
|
|
3
|
+
## Discover the actual environment
|
|
4
|
+
|
|
5
|
+
For CLI work, inspect `animaguru --help`, `animaguru schema`, `animaguru auth status`, `animaguru credits` and `animaguru catalog`. Do not read credential files. If it is absent, use connected MCP or obtain agreement to install the CLI; installation and authentication are separate actions.
|
|
6
|
+
|
|
7
|
+
List projects before choosing one, then inspect the selected project and `assets list PROJECT_ID`. Reuse the user's project where appropriate. Use returned project IDs, asset IDs and file SHAs. Create a separate project when the film needs its own asset container. Saved filenames are not file SHAs.
|
|
8
|
+
|
|
9
|
+
The CLI catalog describes known capabilities; the live project's `availableModels` and `videoProfiles` describe enabled models. Choose from their intersection. A local `--dry-run` can succeed for a disabled server model, so it does not prove provider availability.
|
|
10
|
+
|
|
11
|
+
With MCP, discover the equivalent project, catalog, assets, estimate, generate and job tools. If account linking is needed, let the user complete it. Never invent tool calls or fall back to database access.
|
|
12
|
+
|
|
13
|
+
## Plan and estimate
|
|
14
|
+
|
|
15
|
+
Save requests as JSON and validate each with `assets generate ... --dry-run` or a batch with `assets batch PROJECT_ID --file REQUESTS.json --dry-run`. Add all estimates, including speech and any music/SFX, before the first submission. These are AnimaGuru credits; compare returned amounts directly with the authorized credit budget. Actual usage can differ from estimates.
|
|
16
|
+
|
|
17
|
+
Use only a model and options returned by the live catalog. An image can establish an art direction or become a video reference. An illustrative prompt shell is:
|
|
18
|
+
|
|
19
|
+
> Original tactile editorial paper collage: matte warm cream cardstock, cobalt blue and fox-orange cutouts, visible paper fibers, slightly imperfect scissor edges, soft short shadows falling down-right. [One concrete composition and action.] Restrained studio lighting and readable silhouettes. Keep a quiet area for separately composed editorial type. No lettering, captions, logos or watermark.
|
|
20
|
+
|
|
21
|
+
Describe the subject, position, scale and material of each important element. Keep recurring character features verbatim between requests. Exact logos and text belong in the composition, not in model-generated pixels.
|
|
22
|
+
|
|
23
|
+
## Submit and track
|
|
24
|
+
|
|
25
|
+
For example, after replacing placeholders with discovered values and validating the settings:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
animaguru assets generate PROJECT_ID --type image --name "cutcraft-keyframe" \
|
|
29
|
+
--model IMAGE_MODEL --aspect-ratio 3:2 --prompt "SAVED ART DIRECTION" --dry-run
|
|
30
|
+
animaguru assets generate PROJECT_ID --type video --name "cutcraft-scene" \
|
|
31
|
+
--model VIDEO_MODEL --workflow image-to-video --reference IMAGE_FILE_SHA \
|
|
32
|
+
--seconds 8 --resolution 768p --mode speed \
|
|
33
|
+
--prompt "SAVED VISUAL ACTION" --dry-run
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The literal values above are examples, not assumed capabilities. Image aspect ratios, video modes, duration limits, soundtrack handling and audio support vary by model. Add `--yes` only for the user's authorized work. Save the queued receipt immediately. Submission results may be job receipts rather than completed assets.
|
|
37
|
+
|
|
38
|
+
Use `jobs list PROJECT_ID` and `jobs wait PROJECT_ID JOB_ID --timeout SECONDS` to track the original job. A timeout does not cancel it. Check the job and saved assets before resubmitting after an uncertain response. Do not repeatedly regenerate failed work without diagnosing the error and checking the remaining budget.
|
|
39
|
+
|
|
40
|
+
When a job explicitly fails because its provider is unavailable, preserve that receipt and inspect models from another provider. A replacement with compatible capabilities and an estimate inside the authorized scope can continue the task. Remove unsupported options when changing providers; a seed or prompt-expansion setting valid for one model may be invalid for another. Relay the returned charge or refund status without assuming all failed jobs are free.
|
|
41
|
+
|
|
42
|
+
Download finished assets with `assets download PROJECT_ID ASSET_ID --output FILE`. Keep stdout receipts separate from media and never overwrite an existing downloaded file inadvertently.
|
|
43
|
+
|
|
44
|
+
## Choose continuity deliberately
|
|
45
|
+
|
|
46
|
+
- For different explanatory ideas, use intentional cuts, a shared palette and a common layout. An unrelated next scene does not need the previous clip's last frame.
|
|
47
|
+
- For one continuous action, use a supported video extension workflow with the saved video file SHA as `--source`. Inspect the catalog for its resolution and duration limits. `extendOutput` distinguishes the combined video from continuation-only output; inspect and trim any returned overlap rather than counting it twice.
|
|
48
|
+
- First/last-frame workflows use `--reference` and `--end-reference` only when the model profile supports them. Both must be saved files belonging to the selected project.
|
|
49
|
+
- A locally extracted end frame is not automatically a project asset. Use an explicitly supported upload/import path before referencing it; do not invent an extract-frame or raw upload MCP tool. A character-import command can redraw and charge credits, so it is not a transparent generic frame upload.
|
|
50
|
+
|
|
51
|
+
Do not carry over the reference workflow's fixed ten-second clip assumption. Choose durations from the model profile and the story, and measure the returned files.
|
|
52
|
+
|
|
53
|
+
## Narration and assembly
|
|
54
|
+
|
|
55
|
+
Generate one narrator with `assets generate ... --type speech --voice VOICE --delivery INSTRUCTIONS --prompt TRANSCRIPT`, after discovering the available speech model and voice. Use delivery instructions supported by that provider. Keep the spoken transcript independent of the visual directions. Download and measure the actual recording with `ffprobe`.
|
|
56
|
+
|
|
57
|
+
For generated motion layers, mute their sound when it conflicts with the separate narration. Reuse or generate a compatible instrumental bed at low level. Keep clip selection, crop, text, logos and all timing deterministic in the composition. Use an existing AnimaGuru HTML/Pixi film workspace when one is supplied; otherwise choose a local composition/export tool suited to the requested deliverable. Keep source files and a reproducible export command.
|
|
58
|
+
|
|
59
|
+
For controlled cutout animation, request `transparent: true` image assets (CLI `--transparent`) and check the downloaded alpha channel. Keep a subject's complete silhouette inside the image. Animate these assets as separate layers against a fixed setting; drive position, rotation, entrances and holds from absolute time. Use actual transparent silhouettes for a moving subject instead of moving an opaque rectangular illustration. If video providers are all unavailable, this route can complete the film using working image and speech providers without repeated failed video submissions.
|
|
60
|
+
|
|
61
|
+
Normalize frame size, frame rate and pixel format before concatenation. Export a broadly playable H.264/AAC MP4, or the user's requested format. Check duration, dimensions, audio presence, frame count and representative rendered frames. Preserve truthful asset attribution: motion made with AnimaGuru remains generated footage even when exact typography and editing are composed locally.
|