@docyrus/animaguru 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +41 -3
  2. package/dist/animaguru.js +756 -467
  3. package/package.json +1 -1
  4. package/skills/animaguru/SKILL.md +13 -1
  5. package/skills/animaguru-3d-models/SKILL.md +86 -0
  6. package/skills/animaguru-character-stage-3d/SKILL.md +35 -0
  7. package/skills/animaguru-character-stage-3d/references/recipe.md +26 -0
  8. package/skills/animaguru-exploded-teardown-3d/SKILL.md +38 -0
  9. package/skills/animaguru-exploded-teardown-3d/references/recipe.md +20 -0
  10. package/skills/animaguru-product-turntable-3d/SKILL.md +38 -0
  11. package/skills/animaguru-product-turntable-3d/references/recipe.md +27 -0
  12. package/skills/animaguru-quiz/SKILL.md +1 -1
  13. package/skills/animaguru-toy-diorama-3d/SKILL.md +33 -0
  14. package/skills/animaguru-toy-diorama-3d/references/recipe.md +26 -0
  15. package/skills/animaguru-video/SKILL.md +5 -1
  16. package/templates/video/AGENTS.md +1 -1
  17. package/templates/video/README.md +5 -1
  18. package/templates/video/package.json +3 -1
  19. package/templates/video/scripts/install-example.mjs +10 -6
  20. package/templates/video/shared/locales/en/style-studies.json +35 -0
  21. package/templates/video/shared/locales/en/timeline.json +1 -0
  22. package/templates/video/src/kit/index.ts +1 -0
  23. package/templates/video/src/kit/three-stage.ts +262 -0
  24. package/templates/video/src/resource-timeline.ts +1 -0
  25. package/templates/video/src/studies/three/character-stage-3d.ts +172 -0
  26. package/templates/video/src/studies/three/exploded-teardown-3d.ts +214 -0
  27. package/templates/video/src/studies/three/index.ts +16 -0
  28. package/templates/video/src/studies/three/product-turntable-3d.ts +160 -0
  29. package/templates/video/src/studies/three/toy-diorama-3d.ts +245 -0
  30. package/templates/video/src/studies/three-film.ts +42 -0
  31. package/templates/video/src/style.css +2 -1
  32. package/templates/video/src/timeline-model.ts +1 -1
package/dist/animaguru.js CHANGED
@@ -369,19 +369,19 @@ function jsonInstabilityIssues(document2) {
369
369
  return issues;
370
370
  }
371
371
  function createStore(options) {
372
- const { dirPath, name: name2, fields, pruneUnknown, access: access3 } = options;
372
+ const { dirPath, name: name2, fields, pruneUnknown, access: access4 } = options;
373
373
  const validators = /* @__PURE__ */ new Map();
374
374
  for (const [key, def] of Object.entries(fields)) if (def.schema !== void 0) {
375
375
  for (const option of ["default", "validate"]) if (def[option] !== void 0) throw new CrustStoreError("DEFINITION", `field "${key}" mixes "schema" with "${option}" \u2014 the schema exclusively owns validation, transformation, defaults, and optionality`);
376
376
  validators.set(key, makeSchemaValidator(def.schema));
377
377
  } else if (def.validate !== void 0) validators.set(key, def.validate);
378
378
  const filePath = resolveStorePath(dirPath, name2);
379
- const writeOptions = access3 === "private" ? {
379
+ const writeOptions = access4 === "private" ? {
380
380
  fileMode: 384,
381
381
  directoryMode: 448
382
- } : access3 === void 0 || access3 === "default" ? {} : {
383
- fileMode: access3.file,
384
- directoryMode: access3.directory
382
+ } : access4 === void 0 || access4 === "default" ? {} : {
383
+ fileMode: access4.file,
384
+ directoryMode: access4.directory
385
385
  };
386
386
  function normalizeStateTypes(state) {
387
387
  const normalized = { ...state };
@@ -2312,8 +2312,8 @@ var require_lib = __commonJS({
2312
2312
  if (typeof cb !== "function") {
2313
2313
  opts = cb;
2314
2314
  cb = null;
2315
- deferred = new this.Promise(function(resolve18, reject) {
2316
- deferredResolve = resolve18;
2315
+ deferred = new this.Promise(function(resolve19, reject) {
2316
+ deferredResolve = resolve19;
2317
2317
  deferredReject = reject;
2318
2318
  });
2319
2319
  }
@@ -2461,17 +2461,17 @@ var require_lib = __commonJS({
2461
2461
  if (typeof cb === "function") {
2462
2462
  fnx(cb);
2463
2463
  } else {
2464
- return new this.Promise(function(resolve18, reject) {
2464
+ return new this.Promise(function(resolve19, reject) {
2465
2465
  if (fnx.length === 1) {
2466
2466
  fnx(function(err2, ret) {
2467
2467
  if (err2) {
2468
2468
  reject(err2);
2469
2469
  } else {
2470
- resolve18(ret);
2470
+ resolve19(ret);
2471
2471
  }
2472
2472
  });
2473
2473
  } else {
2474
- resolve18(fnx());
2474
+ resolve19(fnx());
2475
2475
  }
2476
2476
  });
2477
2477
  }
@@ -6862,7 +6862,7 @@ var require_pify = __commonJS({
6862
6862
  "use strict";
6863
6863
  var processFn = (fn, options) => function(...args) {
6864
6864
  const P = options.promiseModule;
6865
- return new P((resolve18, reject) => {
6865
+ return new P((resolve19, reject) => {
6866
6866
  if (options.multiArgs) {
6867
6867
  args.push((...result) => {
6868
6868
  if (options.errorFirst) {
@@ -6870,10 +6870,10 @@ var require_pify = __commonJS({
6870
6870
  reject(result);
6871
6871
  } else {
6872
6872
  result.shift();
6873
- resolve18(result);
6873
+ resolve19(result);
6874
6874
  }
6875
6875
  } else {
6876
- resolve18(result);
6876
+ resolve19(result);
6877
6877
  }
6878
6878
  });
6879
6879
  } else if (options.errorFirst) {
@@ -6881,11 +6881,11 @@ var require_pify = __commonJS({
6881
6881
  if (error62) {
6882
6882
  reject(error62);
6883
6883
  } else {
6884
- resolve18(result);
6884
+ resolve19(result);
6885
6885
  }
6886
6886
  });
6887
6887
  } else {
6888
- args.push(resolve18);
6888
+ args.push(resolve19);
6889
6889
  }
6890
6890
  fn.apply(this, args);
6891
6891
  });
@@ -8083,17 +8083,17 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8083
8083
  const length = Math.ceil((62 + bpath.length + 1) / 8) * 8;
8084
8084
  const written = Buffer.alloc(length);
8085
8085
  const writer = new BufferCursor(written);
8086
- const stat11 = normalizeStats(entry);
8087
- writer.writeUInt32BE(stat11.ctimeSeconds);
8088
- writer.writeUInt32BE(stat11.ctimeNanoseconds);
8089
- writer.writeUInt32BE(stat11.mtimeSeconds);
8090
- writer.writeUInt32BE(stat11.mtimeNanoseconds);
8091
- writer.writeUInt32BE(stat11.dev);
8092
- writer.writeUInt32BE(stat11.ino);
8093
- writer.writeUInt32BE(stat11.mode);
8094
- writer.writeUInt32BE(stat11.uid);
8095
- writer.writeUInt32BE(stat11.gid);
8096
- writer.writeUInt32BE(stat11.size);
8086
+ const stat12 = normalizeStats(entry);
8087
+ writer.writeUInt32BE(stat12.ctimeSeconds);
8088
+ writer.writeUInt32BE(stat12.ctimeNanoseconds);
8089
+ writer.writeUInt32BE(stat12.mtimeSeconds);
8090
+ writer.writeUInt32BE(stat12.mtimeNanoseconds);
8091
+ writer.writeUInt32BE(stat12.dev);
8092
+ writer.writeUInt32BE(stat12.ino);
8093
+ writer.writeUInt32BE(stat12.mode);
8094
+ writer.writeUInt32BE(stat12.uid);
8095
+ writer.writeUInt32BE(stat12.gid);
8096
+ writer.writeUInt32BE(stat12.size);
8097
8097
  writer.write(entry.oid, 20, "hex");
8098
8098
  writer.writeUInt16BE(renderCacheEntryFlags(entry));
8099
8099
  writer.write(entry.path, bpath.length, "utf8");
@@ -8145,13 +8145,13 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8145
8145
  };
8146
8146
  }
8147
8147
  async function updateCachedIndexFile(fs2, filepath, cache) {
8148
- const [stat11, rawIndexFile] = await Promise.all([
8148
+ const [stat12, rawIndexFile] = await Promise.all([
8149
8149
  fs2.lstat(filepath),
8150
8150
  fs2.read(filepath)
8151
8151
  ]);
8152
8152
  const index3 = await GitIndex.from(rawIndexFile);
8153
8153
  cache.map.set(filepath, index3);
8154
- cache.stats.set(filepath, stat11);
8154
+ cache.stats.set(filepath, stat12);
8155
8155
  }
8156
8156
  async function isIndexStale(fs2, filepath, cache) {
8157
8157
  const savedStats = cache.stats.get(filepath);
@@ -8208,7 +8208,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8208
8208
  }
8209
8209
  return path;
8210
8210
  }
8211
- function dirname15(path) {
8211
+ function dirname16(path) {
8212
8212
  const last = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
8213
8213
  if (last === -1) return ".";
8214
8214
  if (last === 0) return "/";
@@ -8216,7 +8216,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8216
8216
  }
8217
8217
  function flatFileListToDirectoryStructure(files) {
8218
8218
  const inodes = /* @__PURE__ */ new Map();
8219
- const mkdir16 = function(name2) {
8219
+ const mkdir17 = function(name2) {
8220
8220
  if (!inodes.has(name2)) {
8221
8221
  const dir = {
8222
8222
  type: "tree",
@@ -8226,7 +8226,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8226
8226
  children: []
8227
8227
  };
8228
8228
  inodes.set(name2, dir);
8229
- dir.parent = mkdir16(dirname15(name2));
8229
+ dir.parent = mkdir17(dirname16(name2));
8230
8230
  if (dir.parent && dir.parent !== dir) dir.parent.children.push(dir);
8231
8231
  }
8232
8232
  return inodes.get(name2);
@@ -8239,7 +8239,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8239
8239
  basename: basename8(name2),
8240
8240
  metadata,
8241
8241
  // This recursively generates any missing parent folders.
8242
- parent: mkdir16(dirname15(name2)),
8242
+ parent: mkdir17(dirname16(name2)),
8243
8243
  children: []
8244
8244
  };
8245
8245
  if (file2.parent) file2.parent.children.push(file2);
@@ -8247,7 +8247,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8247
8247
  }
8248
8248
  return inodes.get(name2);
8249
8249
  };
8250
- mkdir16(".");
8250
+ mkdir17(".");
8251
8251
  for (const file2 of files) {
8252
8252
  mkfile(file2.path, file2);
8253
8253
  }
@@ -8650,7 +8650,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
8650
8650
  }
8651
8651
  return isAbsolute2 ? `/${normalized}` : normalized;
8652
8652
  }
8653
- function join19(...args) {
8653
+ function join20(...args) {
8654
8654
  if (args.length === 0) return ".";
8655
8655
  let joined;
8656
8656
  for (let i2 = 0; i2 < args.length; ++i2) {
@@ -9052,7 +9052,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
9052
9052
  for (const [key, value] of actualRefsToWrite) {
9053
9053
  await acquireLock(
9054
9054
  key,
9055
- async () => fs2.write(join19(gitdir, key), `${value.trim()}
9055
+ async () => fs2.write(join20(gitdir, key), `${value.trim()}
9056
9056
  `, "utf8")
9057
9057
  );
9058
9058
  }
@@ -9076,7 +9076,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
9076
9076
  }
9077
9077
  await acquireLock(
9078
9078
  ref,
9079
- async () => fs2.write(join19(gitdir, ref), `${value.trim()}
9079
+ async () => fs2.write(join20(gitdir, ref), `${value.trim()}
9080
9080
  `, "utf8")
9081
9081
  );
9082
9082
  }
@@ -9094,7 +9094,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
9094
9094
  assertWritableRef(ref);
9095
9095
  await acquireLock(
9096
9096
  ref,
9097
- async () => fs2.write(join19(gitdir, ref), `ref: ${value.trim()}
9097
+ async () => fs2.write(join20(gitdir, ref), `ref: ${value.trim()}
9098
9098
  `, "utf8")
9099
9099
  );
9100
9100
  }
@@ -9121,7 +9121,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
9121
9121
  */
9122
9122
  static async deleteRefs({ fs: fs2, gitdir, refs }) {
9123
9123
  refs.forEach(assertWritableRef);
9124
- await Promise.all(refs.map((ref) => fs2.rm(join19(gitdir, ref))));
9124
+ await Promise.all(refs.map((ref) => fs2.rm(join20(gitdir, ref))));
9125
9125
  let text2 = await acquireLock(
9126
9126
  "packed-refs",
9127
9127
  async () => fs2.read(`${gitdir}/packed-refs`, { encoding: "utf8" })
@@ -10208,7 +10208,7 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
10208
10208
  format = "content",
10209
10209
  getExternalRefDelta
10210
10210
  }) {
10211
- let list3 = await fs2.readdir(join19(gitdir, "objects/pack"));
10211
+ let list3 = await fs2.readdir(join20(gitdir, "objects/pack"));
10212
10212
  list3 = list3.filter((x2) => x2.endsWith(".idx"));
10213
10213
  for (const filename of list3) {
10214
10214
  const indexFile = `${gitdir}/objects/pack/${filename}`;
@@ -11065,9 +11065,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11065
11065
  }
11066
11066
  const tree = GitTree.from(object2);
11067
11067
  for (const entry2 of tree) {
11068
- map2.set(join19(filepath, entry2.path), entry2);
11068
+ map2.set(join20(filepath, entry2.path), entry2);
11069
11069
  }
11070
- return tree.entries().map((entry2) => join19(filepath, entry2.path));
11070
+ return tree.entries().map((entry2) => join20(filepath, entry2.path));
11071
11071
  }
11072
11072
  async type(entry) {
11073
11073
  if (entry._type === false) {
@@ -11160,9 +11160,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11160
11160
  if (await entry.type() !== "tree") return null;
11161
11161
  const filepath = entry._fullpath;
11162
11162
  const { fs: fs2, dir } = this;
11163
- const names = await fs2.readdir(join19(dir, filepath));
11163
+ const names = await fs2.readdir(join20(dir, filepath));
11164
11164
  if (names === null) return null;
11165
- return names.map((name2) => join19(filepath, name2));
11165
+ return names.map((name2) => join20(filepath, name2));
11166
11166
  }
11167
11167
  async type(entry) {
11168
11168
  if (entry._type === false) {
@@ -11179,23 +11179,23 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11179
11179
  async stat(entry) {
11180
11180
  if (entry._stat === false) {
11181
11181
  const { fs: fs2, dir } = this;
11182
- let stat11 = await fs2.lstat(`${dir}/${entry._fullpath}`);
11183
- if (!stat11) {
11182
+ let stat12 = await fs2.lstat(`${dir}/${entry._fullpath}`);
11183
+ if (!stat12) {
11184
11184
  throw new Error(
11185
11185
  `ENOENT: no such file or directory, lstat '${entry._fullpath}'`
11186
11186
  );
11187
11187
  }
11188
- let type = stat11.isDirectory() ? "tree" : "blob";
11189
- if (type === "blob" && !stat11.isFile() && !stat11.isSymbolicLink()) {
11188
+ let type = stat12.isDirectory() ? "tree" : "blob";
11189
+ if (type === "blob" && !stat12.isFile() && !stat12.isSymbolicLink()) {
11190
11190
  type = "special";
11191
11191
  }
11192
11192
  entry._type = type;
11193
- stat11 = normalizeStats(stat11);
11194
- entry._mode = stat11.mode;
11195
- if (stat11.size === -1 && entry._actualSize) {
11196
- stat11.size = entry._actualSize;
11193
+ stat12 = normalizeStats(stat12);
11194
+ entry._mode = stat12.mode;
11195
+ if (stat12.size === -1 && entry._actualSize) {
11196
+ stat12.size = entry._actualSize;
11197
11197
  }
11198
- entry._stat = stat11;
11198
+ entry._stat = stat12;
11199
11199
  }
11200
11200
  return entry._stat;
11201
11201
  }
@@ -11383,24 +11383,24 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11383
11383
  };
11384
11384
  return walk2(root);
11385
11385
  }
11386
- async function mkdirp(mkdir16, filepath, retries = 10) {
11386
+ async function mkdirp(mkdir17, filepath, retries = 10) {
11387
11387
  try {
11388
- await mkdir16(filepath);
11388
+ await mkdir17(filepath);
11389
11389
  } catch (err2) {
11390
11390
  if (err2 === null) return;
11391
11391
  if (err2.code === "EEXIST") return;
11392
11392
  if (err2.code !== "ENOENT") throw err2;
11393
- const parent = dirname15(filepath);
11393
+ const parent = dirname16(filepath);
11394
11394
  if (parent === "." || parent === "/" || parent === filepath) throw err2;
11395
- await mkdirp(mkdir16, parent, retries);
11395
+ await mkdirp(mkdir17, parent, retries);
11396
11396
  for (let attempt = 0; ; attempt++) {
11397
11397
  try {
11398
- await mkdir16(filepath);
11398
+ await mkdir17(filepath);
11399
11399
  return;
11400
11400
  } catch (err22) {
11401
11401
  if (err22 === null || err22.code === "EEXIST") return;
11402
11402
  if (err22.code === "ENOENT" && attempt < retries) {
11403
- await new Promise((resolve18) => setTimeout(resolve18, 0));
11403
+ await new Promise((resolve19) => setTimeout(resolve19, 0));
11404
11404
  continue;
11405
11405
  }
11406
11406
  throw err22;
@@ -11415,10 +11415,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11415
11415
  } else if (entries.length) {
11416
11416
  await Promise.all(
11417
11417
  entries.map((entry) => {
11418
- const subpath = join19(filepath, entry);
11419
- return fs2.lstat(subpath).then((stat11) => {
11420
- if (!stat11) return;
11421
- return stat11.isDirectory() ? rmRecursive(fs2, subpath) : fs2.rm(subpath);
11418
+ const subpath = join20(filepath, entry);
11419
+ return fs2.lstat(subpath).then((stat12) => {
11420
+ if (!stat12) return;
11421
+ return stat12.isDirectory() ? rmRecursive(fs2, subpath) : fs2.rm(subpath);
11422
11422
  });
11423
11423
  })
11424
11424
  ).then(() => fs2.rmdir(filepath));
@@ -11554,7 +11554,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11554
11554
  try {
11555
11555
  await this._writeFile(filepath, contents, options);
11556
11556
  } catch (err2) {
11557
- await this.mkdir(dirname15(filepath));
11557
+ await this.mkdir(dirname16(filepath));
11558
11558
  await this._writeFile(filepath, contents, options);
11559
11559
  }
11560
11560
  }
@@ -11700,7 +11700,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11700
11700
  if (isAbsolute(submoduleGitdir)) {
11701
11701
  return submoduleGitdir;
11702
11702
  }
11703
- const gitdir = join19(dirname15(dotgit), submoduleGitdir);
11703
+ const gitdir = join20(dirname16(dotgit), submoduleGitdir);
11704
11704
  return gitdir;
11705
11705
  });
11706
11706
  } else {
@@ -11722,7 +11722,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11722
11722
  async function abortMerge({
11723
11723
  fs: _fs,
11724
11724
  dir,
11725
- gitdir = join19(dir, ".git"),
11725
+ gitdir = join20(dir, ".git"),
11726
11726
  commit: commit2 = "HEAD",
11727
11727
  cache = {}
11728
11728
  }) {
@@ -11803,17 +11803,17 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11803
11803
  * @param {string} args.filepath - The path of the file to check.
11804
11804
  * @returns {Promise<boolean>} - `true` if the file is ignored, `false` otherwise.
11805
11805
  */
11806
- static async isIgnored({ fs: fs2, dir, gitdir = join19(dir, ".git"), filepath }) {
11806
+ static async isIgnored({ fs: fs2, dir, gitdir = join20(dir, ".git"), filepath }) {
11807
11807
  if (basename8(filepath) === ".git") return true;
11808
11808
  if (filepath === ".") return false;
11809
11809
  let excludes = "";
11810
- const excludesFile = join19(gitdir, "info", "exclude");
11810
+ const excludesFile = join20(gitdir, "info", "exclude");
11811
11811
  if (await fs2.exists(excludesFile)) {
11812
11812
  excludes = await fs2.read(excludesFile, "utf8");
11813
11813
  }
11814
11814
  const pairs = [
11815
11815
  {
11816
- gitignore: join19(dir, ".gitignore"),
11816
+ gitignore: join20(dir, ".gitignore"),
11817
11817
  filepath
11818
11818
  }
11819
11819
  ];
@@ -11822,7 +11822,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11822
11822
  const folder = pieces.slice(0, i2).join("/");
11823
11823
  const file2 = pieces.slice(i2).join("/");
11824
11824
  pairs.push({
11825
- gitignore: join19(dir, folder, ".gitignore"),
11825
+ gitignore: join20(dir, folder, ".gitignore"),
11826
11826
  filepath: file2
11827
11827
  });
11828
11828
  }
@@ -11836,7 +11836,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11836
11836
  }
11837
11837
  const ign = ignore2().add(excludes);
11838
11838
  ign.add(file2);
11839
- const parentdir = dirname15(p.filepath);
11839
+ const parentdir = dirname16(p.filepath);
11840
11840
  if (parentdir !== "." && ign.ignores(parentdir)) return true;
11841
11841
  if (ignoredStatus) {
11842
11842
  ignoredStatus = !ign.test(p.filepath).unignored;
@@ -11909,7 +11909,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11909
11909
  async function add({
11910
11910
  fs: _fs,
11911
11911
  dir,
11912
- gitdir = join19(dir, ".git"),
11912
+ gitdir = join20(dir, ".git"),
11913
11913
  filepath,
11914
11914
  cache = {},
11915
11915
  force = false,
@@ -11973,17 +11973,17 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11973
11973
  });
11974
11974
  if (ignored) return;
11975
11975
  }
11976
- const stats = await fs2.lstat(join19(dir, currentFilepath));
11976
+ const stats = await fs2.lstat(join20(dir, currentFilepath));
11977
11977
  if (!stats) throw new NotFoundError(currentFilepath);
11978
11978
  if (stats.isDirectory()) {
11979
- const children = await fs2.readdir(join19(dir, currentFilepath));
11979
+ const children = await fs2.readdir(join20(dir, currentFilepath));
11980
11980
  if (parallel) {
11981
11981
  const promises3 = children.map(
11982
11982
  (child) => addToIndex({
11983
11983
  dir,
11984
11984
  gitdir,
11985
11985
  fs: fs2,
11986
- filepath: [join19(currentFilepath, child)],
11986
+ filepath: [join20(currentFilepath, child)],
11987
11987
  index: index3,
11988
11988
  force,
11989
11989
  parallel,
@@ -11997,7 +11997,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
11997
11997
  dir,
11998
11998
  gitdir,
11999
11999
  fs: fs2,
12000
- filepath: [join19(currentFilepath, child)],
12000
+ filepath: [join20(currentFilepath, child)],
12001
12001
  index: index3,
12002
12002
  force,
12003
12003
  parallel,
@@ -12006,7 +12006,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12006
12006
  }
12007
12007
  }
12008
12008
  } else {
12009
- const object2 = stats.isSymbolicLink() ? await fs2.readlink(join19(dir, currentFilepath)).then(posixifyPathBuffer) : await fs2.read(join19(dir, currentFilepath), { autocrlf });
12009
+ const object2 = stats.isSymbolicLink() ? await fs2.readlink(join20(dir, currentFilepath)).then(posixifyPathBuffer) : await fs2.read(join20(dir, currentFilepath), { autocrlf });
12010
12010
  if (object2 === null) throw new NotFoundError(currentFilepath);
12011
12011
  const oid = await _writeObject({ fs: fs2, gitdir, type: "blob", object: object2 });
12012
12012
  index3.insert({ filepath: currentFilepath, stats, oid });
@@ -12440,7 +12440,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12440
12440
  fs: _fs,
12441
12441
  onSign,
12442
12442
  dir,
12443
- gitdir = join19(dir, ".git"),
12443
+ gitdir = join20(dir, ".git"),
12444
12444
  ref = "refs/notes/commits",
12445
12445
  oid,
12446
12446
  note,
@@ -12510,7 +12510,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12510
12510
  async function addRemote({
12511
12511
  fs: fs2,
12512
12512
  dir,
12513
- gitdir = join19(dir, ".git"),
12513
+ gitdir = join20(dir, ".git"),
12514
12514
  remote,
12515
12515
  url: url3,
12516
12516
  force = false
@@ -12580,7 +12580,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12580
12580
  fs: _fs,
12581
12581
  onSign,
12582
12582
  dir,
12583
- gitdir = join19(dir, ".git"),
12583
+ gitdir = join20(dir, ".git"),
12584
12584
  ref,
12585
12585
  tagger: _tagger,
12586
12586
  message = ref,
@@ -12661,7 +12661,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12661
12661
  async function branch({
12662
12662
  fs: fs2,
12663
12663
  dir,
12664
- gitdir = join19(dir, ".git"),
12664
+ gitdir = join20(dir, ".git"),
12665
12665
  ref,
12666
12666
  object: object2,
12667
12667
  checkout: checkout2 = false,
@@ -13325,7 +13325,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13325
13325
  onProgress,
13326
13326
  onPostCheckout,
13327
13327
  dir,
13328
- gitdir = join19(dir, ".git"),
13328
+ gitdir = join20(dir, ".git"),
13329
13329
  remote = "origin",
13330
13330
  ref: _ref,
13331
13331
  filepaths,
@@ -13406,7 +13406,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13406
13406
  fs: fs2,
13407
13407
  cache,
13408
13408
  dir,
13409
- gitdir = join19(dir, ".git"),
13409
+ gitdir = join20(dir, ".git"),
13410
13410
  index: index3,
13411
13411
  ourOid,
13412
13412
  baseOid,
@@ -13676,7 +13676,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13676
13676
  workdir: WORKDIR
13677
13677
  };
13678
13678
  async function checkAndWriteBlob(fs2, gitdir, dir, filepath, oid = null) {
13679
- const currentFilepath = join19(dir, filepath);
13679
+ const currentFilepath = join20(dir, filepath);
13680
13680
  const stats = await fs2.lstat(currentFilepath);
13681
13681
  if (!stats) throw new NotFoundError(currentFilepath);
13682
13682
  if (stats.isDirectory())
@@ -13845,7 +13845,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13845
13845
  stageUpdated.push({
13846
13846
  filepath,
13847
13847
  oid,
13848
- stats: await fs2.lstat(join19(dir, filepath))
13848
+ stats: await fs2.lstat(join20(dir, filepath))
13849
13849
  });
13850
13850
  return {
13851
13851
  method: "write",
@@ -13858,7 +13858,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13858
13858
  });
13859
13859
  await acquireLock(gitdir, async () => {
13860
13860
  for (const op of ops) {
13861
- const currentFilepath = join19(dir, op.filepath);
13861
+ const currentFilepath = join20(dir, op.filepath);
13862
13862
  switch (op.method) {
13863
13863
  case "rmdir":
13864
13864
  await fs2.rmdir(currentFilepath);
@@ -13992,7 +13992,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
13992
13992
  async function cherryPick({
13993
13993
  fs: _fs,
13994
13994
  dir,
13995
- gitdir = join19(dir, ".git"),
13995
+ gitdir = join20(dir, ".git"),
13996
13996
  oid,
13997
13997
  cache = {},
13998
13998
  committer,
@@ -14497,7 +14497,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14497
14497
  * @returns {Promise<Set<string>>} - A set of shallow object IDs.
14498
14498
  */
14499
14499
  static async read({ fs: fs2, gitdir }) {
14500
- const filepath = join19(gitdir, "shallow");
14500
+ const filepath = join20(gitdir, "shallow");
14501
14501
  const oids = /* @__PURE__ */ new Set();
14502
14502
  await acquireLock(filepath, async function() {
14503
14503
  const text2 = await fs2.read(filepath, { encoding: "utf8" });
@@ -14518,7 +14518,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14518
14518
  * @returns {Promise<void>}
14519
14519
  */
14520
14520
  static async write({ fs: fs2, gitdir, oids }) {
14521
- const filepath = join19(gitdir, "shallow");
14521
+ const filepath = join20(gitdir, "shallow");
14522
14522
  if (oids.size > 0) {
14523
14523
  const text2 = [...oids].join("\n") + "\n";
14524
14524
  await acquireLock(filepath, async function() {
@@ -14544,7 +14544,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14544
14544
  oid,
14545
14545
  getExternalRefDelta
14546
14546
  }) {
14547
- let list3 = await fs2.readdir(join19(gitdir, "objects/pack"));
14547
+ let list3 = await fs2.readdir(join20(gitdir, "objects/pack"));
14548
14548
  list3 = list3.filter((x2) => x2.endsWith(".idx"));
14549
14549
  for (const filename of list3) {
14550
14550
  const indexFile = `${gitdir}/objects/pack/${filename}`;
@@ -14616,9 +14616,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14616
14616
  throw Error("You cannot write to a FIFO that has already been ended!");
14617
14617
  }
14618
14618
  if (this._waiting) {
14619
- const resolve18 = this._waiting;
14619
+ const resolve19 = this._waiting;
14620
14620
  this._waiting = null;
14621
- resolve18({ value: chunk });
14621
+ resolve19({ value: chunk });
14622
14622
  } else {
14623
14623
  this._queue.push(chunk);
14624
14624
  }
@@ -14626,9 +14626,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14626
14626
  end() {
14627
14627
  this._ended = true;
14628
14628
  if (this._waiting) {
14629
- const resolve18 = this._waiting;
14629
+ const resolve19 = this._waiting;
14630
14630
  this._waiting = null;
14631
- resolve18({ done: true });
14631
+ resolve19({ done: true });
14632
14632
  }
14633
14633
  }
14634
14634
  destroy(err2) {
@@ -14647,8 +14647,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14647
14647
  "You cannot call read until the previous call to read has returned!"
14648
14648
  );
14649
14649
  }
14650
- return new Promise((resolve18) => {
14651
- this._waiting = resolve18;
14650
+ return new Promise((resolve19) => {
14651
+ this._waiting = resolve19;
14652
14652
  });
14653
14653
  }
14654
14654
  };
@@ -14803,7 +14803,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14803
14803
  const acks = [];
14804
14804
  let nak = false;
14805
14805
  let done = false;
14806
- return new Promise((resolve18, reject) => {
14806
+ return new Promise((resolve19, reject) => {
14807
14807
  forAwait2(packetlines, (data) => {
14808
14808
  const line = data.toString("utf8").trim();
14809
14809
  if (line.startsWith("shallow")) {
@@ -14830,11 +14830,11 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14830
14830
  nak = true;
14831
14831
  }
14832
14832
  if (done) {
14833
- stream.error ? reject(stream.error) : resolve18({ shallows, unshallows, acks, nak, packfile, progress });
14833
+ stream.error ? reject(stream.error) : resolve19({ shallows, unshallows, acks, nak, packfile, progress });
14834
14834
  }
14835
14835
  }).finally(() => {
14836
14836
  if (!done) {
14837
- stream.error ? reject(stream.error) : resolve18({ shallows, unshallows, acks, nak, packfile, progress });
14837
+ stream.error ? reject(stream.error) : resolve19({ shallows, unshallows, acks, nak, packfile, progress });
14838
14838
  }
14839
14839
  });
14840
14840
  });
@@ -15135,7 +15135,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15135
15135
  }
15136
15136
  if (packfileSha !== "" && !emptyPackfile(packfile)) {
15137
15137
  res.packfile = `objects/pack/pack-${packfileSha}.pack`;
15138
- const fullpath = join19(gitdir, res.packfile);
15138
+ const fullpath = join20(gitdir, res.packfile);
15139
15139
  await fs2.write(fullpath, packfile);
15140
15140
  const getExternalRefDelta = (oid2) => _readObject({ fs: fs2, cache, gitdir, oid: oid2 });
15141
15141
  const idx = await GitPackIndex.fromPack({
@@ -15151,7 +15151,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15151
15151
  fs: fs2,
15152
15152
  bare = false,
15153
15153
  dir,
15154
- gitdir = bare ? dir : join19(dir, ".git"),
15154
+ gitdir = bare ? dir : join20(dir, ".git"),
15155
15155
  defaultBranch = "master"
15156
15156
  }) {
15157
15157
  if (await fs2.exists(gitdir + "/config")) return;
@@ -15265,7 +15265,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15265
15265
  onAuthFailure,
15266
15266
  onPostCheckout,
15267
15267
  dir,
15268
- gitdir = join19(dir, ".git"),
15268
+ gitdir = join20(dir, ".git"),
15269
15269
  url: url3,
15270
15270
  corsProxy = void 0,
15271
15271
  ref = void 0,
@@ -15328,7 +15328,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15328
15328
  fs: _fs,
15329
15329
  onSign,
15330
15330
  dir,
15331
- gitdir = join19(dir, ".git"),
15331
+ gitdir = join20(dir, ".git"),
15332
15332
  message,
15333
15333
  author,
15334
15334
  committer,
@@ -15377,7 +15377,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15377
15377
  async function currentBranch({
15378
15378
  fs: fs2,
15379
15379
  dir,
15380
- gitdir = join19(dir, ".git"),
15380
+ gitdir = join20(dir, ".git"),
15381
15381
  fullname = false,
15382
15382
  test = false
15383
15383
  }) {
@@ -15418,7 +15418,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15418
15418
  async function deleteBranch({
15419
15419
  fs: fs2,
15420
15420
  dir,
15421
- gitdir = join19(dir, ".git"),
15421
+ gitdir = join20(dir, ".git"),
15422
15422
  ref
15423
15423
  }) {
15424
15424
  try {
@@ -15436,7 +15436,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15436
15436
  throw err2;
15437
15437
  }
15438
15438
  }
15439
- async function deleteRef({ fs: fs2, dir, gitdir = join19(dir, ".git"), ref }) {
15439
+ async function deleteRef({ fs: fs2, dir, gitdir = join20(dir, ".git"), ref }) {
15440
15440
  try {
15441
15441
  assertParameter("fs", fs2);
15442
15442
  assertParameter("ref", ref);
@@ -15456,7 +15456,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15456
15456
  async function deleteRemote({
15457
15457
  fs: fs2,
15458
15458
  dir,
15459
- gitdir = join19(dir, ".git"),
15459
+ gitdir = join20(dir, ".git"),
15460
15460
  remote
15461
15461
  }) {
15462
15462
  try {
@@ -15478,7 +15478,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15478
15478
  ref = ref.startsWith("refs/tags/") ? ref : `refs/tags/${ref}`;
15479
15479
  await GitRefManager.deleteRef({ fs: fs2, gitdir, ref });
15480
15480
  }
15481
- async function deleteTag({ fs: fs2, dir, gitdir = join19(dir, ".git"), ref }) {
15481
+ async function deleteTag({ fs: fs2, dir, gitdir = join20(dir, ".git"), ref }) {
15482
15482
  try {
15483
15483
  assertParameter("fs", fs2);
15484
15484
  assertParameter("ref", ref);
@@ -15507,7 +15507,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15507
15507
  getExternalRefDelta
15508
15508
  }) {
15509
15509
  const results = [];
15510
- let list3 = await fs2.readdir(join19(gitdir, "objects/pack"));
15510
+ let list3 = await fs2.readdir(join20(gitdir, "objects/pack"));
15511
15511
  list3 = list3.filter((x2) => x2.endsWith(".idx"));
15512
15512
  for (const filename of list3) {
15513
15513
  const indexFile = `${gitdir}/objects/pack/${filename}`;
@@ -15550,7 +15550,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15550
15550
  async function expandOid({
15551
15551
  fs: fs2,
15552
15552
  dir,
15553
- gitdir = join19(dir, ".git"),
15553
+ gitdir = join20(dir, ".git"),
15554
15554
  oid,
15555
15555
  cache = {}
15556
15556
  }) {
@@ -15571,7 +15571,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15571
15571
  throw err2;
15572
15572
  }
15573
15573
  }
15574
- async function expandRef({ fs: fs2, dir, gitdir = join19(dir, ".git"), ref }) {
15574
+ async function expandRef({ fs: fs2, dir, gitdir = join20(dir, ".git"), ref }) {
15575
15575
  try {
15576
15576
  assertParameter("fs", fs2);
15577
15577
  assertParameter("gitdir", gitdir);
@@ -15862,7 +15862,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15862
15862
  onAuthSuccess,
15863
15863
  onAuthFailure,
15864
15864
  dir,
15865
- gitdir = join19(dir, ".git"),
15865
+ gitdir = join20(dir, ".git"),
15866
15866
  ref,
15867
15867
  url: url3,
15868
15868
  remote,
@@ -15920,7 +15920,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15920
15920
  onAuthSuccess,
15921
15921
  onAuthFailure,
15922
15922
  dir,
15923
- gitdir = join19(dir, ".git"),
15923
+ gitdir = join20(dir, ".git"),
15924
15924
  ref,
15925
15925
  remote,
15926
15926
  remoteRef,
@@ -15976,7 +15976,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15976
15976
  async function findMergeBase({
15977
15977
  fs: fs2,
15978
15978
  dir,
15979
- gitdir = join19(dir, ".git"),
15979
+ gitdir = join20(dir, ".git"),
15980
15980
  oids,
15981
15981
  cache = {}
15982
15982
  }) {
@@ -15998,10 +15998,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15998
15998
  }
15999
15999
  }
16000
16000
  async function _findRoot({ fs: fs2, filepath }) {
16001
- if (await fs2.exists(join19(filepath, ".git"))) {
16001
+ if (await fs2.exists(join20(filepath, ".git"))) {
16002
16002
  return filepath;
16003
16003
  } else {
16004
- const parent = dirname15(filepath);
16004
+ const parent = dirname16(filepath);
16005
16005
  if (parent === filepath) {
16006
16006
  throw new NotFoundError(`git root for ${filepath}`);
16007
16007
  }
@@ -16018,7 +16018,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16018
16018
  throw err2;
16019
16019
  }
16020
16020
  }
16021
- async function getConfig({ fs: fs2, dir, gitdir = join19(dir, ".git"), path }) {
16021
+ async function getConfig({ fs: fs2, dir, gitdir = join20(dir, ".git"), path }) {
16022
16022
  try {
16023
16023
  assertParameter("fs", fs2);
16024
16024
  assertParameter("gitdir", gitdir);
@@ -16042,7 +16042,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16042
16042
  async function getConfigAll({
16043
16043
  fs: fs2,
16044
16044
  dir,
16045
- gitdir = join19(dir, ".git"),
16045
+ gitdir = join20(dir, ".git"),
16046
16046
  path
16047
16047
  }) {
16048
16048
  try {
@@ -16231,7 +16231,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16231
16231
  filepath
16232
16232
  }) {
16233
16233
  try {
16234
- filepath = join19(dir, filepath);
16234
+ filepath = join20(dir, filepath);
16235
16235
  const pack = await fs2.read(filepath);
16236
16236
  const getExternalRefDelta = (oid) => _readObject({ fs: fs2, cache, gitdir, oid });
16237
16237
  const idx = await GitPackIndex.fromPack({
@@ -16252,7 +16252,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16252
16252
  fs: fs2,
16253
16253
  onProgress,
16254
16254
  dir,
16255
- gitdir = join19(dir, ".git"),
16255
+ gitdir = join20(dir, ".git"),
16256
16256
  filepath,
16257
16257
  cache = {}
16258
16258
  }) {
@@ -16280,7 +16280,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16280
16280
  fs: fs2,
16281
16281
  bare = false,
16282
16282
  dir,
16283
- gitdir = bare ? dir : join19(dir, ".git"),
16283
+ gitdir = bare ? dir : join20(dir, ".git"),
16284
16284
  defaultBranch = "master"
16285
16285
  }) {
16286
16286
  try {
@@ -16354,7 +16354,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16354
16354
  async function isDescendent({
16355
16355
  fs: fs2,
16356
16356
  dir,
16357
- gitdir = join19(dir, ".git"),
16357
+ gitdir = join20(dir, ".git"),
16358
16358
  oid,
16359
16359
  ancestor,
16360
16360
  depth = -1,
@@ -16383,7 +16383,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16383
16383
  async function isIgnored({
16384
16384
  fs: fs2,
16385
16385
  dir,
16386
- gitdir = join19(dir, ".git"),
16386
+ gitdir = join20(dir, ".git"),
16387
16387
  filepath
16388
16388
  }) {
16389
16389
  try {
@@ -16407,7 +16407,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16407
16407
  async function listBranches({
16408
16408
  fs: fs2,
16409
16409
  dir,
16410
- gitdir = join19(dir, ".git"),
16410
+ gitdir = join20(dir, ".git"),
16411
16411
  remote
16412
16412
  }) {
16413
16413
  try {
@@ -16464,17 +16464,17 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16464
16464
  gitdir,
16465
16465
  oid: entry.oid,
16466
16466
  filenames,
16467
- prefix: join19(prefix, entry.path)
16467
+ prefix: join20(prefix, entry.path)
16468
16468
  });
16469
16469
  } else {
16470
- filenames.push(join19(prefix, entry.path));
16470
+ filenames.push(join20(prefix, entry.path));
16471
16471
  }
16472
16472
  }
16473
16473
  }
16474
16474
  async function listFiles({
16475
16475
  fs: fs2,
16476
16476
  dir,
16477
- gitdir = join19(dir, ".git"),
16477
+ gitdir = join20(dir, ".git"),
16478
16478
  ref,
16479
16479
  cache = {}
16480
16480
  }) {
@@ -16518,7 +16518,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16518
16518
  async function listNotes({
16519
16519
  fs: fs2,
16520
16520
  dir,
16521
- gitdir = join19(dir, ".git"),
16521
+ gitdir = join20(dir, ".git"),
16522
16522
  ref = "refs/notes/commits",
16523
16523
  cache = {}
16524
16524
  }) {
@@ -16542,7 +16542,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16542
16542
  async function listRefs({
16543
16543
  fs: fs2,
16544
16544
  dir,
16545
- gitdir = join19(dir, ".git"),
16545
+ gitdir = join20(dir, ".git"),
16546
16546
  filepath
16547
16547
  }) {
16548
16548
  try {
@@ -16567,7 +16567,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16567
16567
  );
16568
16568
  return remotes;
16569
16569
  }
16570
- async function listRemotes({ fs: fs2, dir, gitdir = join19(dir, ".git") }) {
16570
+ async function listRemotes({ fs: fs2, dir, gitdir = join20(dir, ".git") }) {
16571
16571
  try {
16572
16572
  assertParameter("fs", fs2);
16573
16573
  assertParameter("gitdir", gitdir);
@@ -16666,7 +16666,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16666
16666
  throw err2;
16667
16667
  }
16668
16668
  }
16669
- async function listTags({ fs: fs2, dir, gitdir = join19(dir, ".git") }) {
16669
+ async function listTags({ fs: fs2, dir, gitdir = join20(dir, ".git") }) {
16670
16670
  try {
16671
16671
  assertParameter("fs", fs2);
16672
16672
  assertParameter("gitdir", gitdir);
@@ -16719,7 +16719,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16719
16719
  const walks = tree.entries().map(function(entry) {
16720
16720
  let result;
16721
16721
  if (entry.oid === fileId) {
16722
- result = join19(parentPath, entry.path);
16722
+ result = join20(parentPath, entry.path);
16723
16723
  filepaths.push(result);
16724
16724
  } else if (entry.type === "tree") {
16725
16725
  result = _readObject({
@@ -16736,7 +16736,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16736
16736
  fileId,
16737
16737
  oid,
16738
16738
  filepaths,
16739
- parentPath: join19(parentPath, entry.path)
16739
+ parentPath: join20(parentPath, entry.path)
16740
16740
  });
16741
16741
  });
16742
16742
  }
@@ -16911,7 +16911,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16911
16911
  async function log({
16912
16912
  fs: fs2,
16913
16913
  dir,
16914
- gitdir = join19(dir, ".git"),
16914
+ gitdir = join20(dir, ".git"),
16915
16915
  filepath,
16916
16916
  ref = "HEAD",
16917
16917
  depth,
@@ -16949,7 +16949,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16949
16949
  fs: _fs,
16950
16950
  onSign,
16951
16951
  dir,
16952
- gitdir = join19(dir, ".git"),
16952
+ gitdir = join20(dir, ".git"),
16953
16953
  ours,
16954
16954
  theirs,
16955
16955
  fastForward: fastForward2 = true,
@@ -17026,7 +17026,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17026
17026
  fs: fs2,
17027
17027
  cache,
17028
17028
  dir,
17029
- gitdir = join19(dir, ".git"),
17029
+ gitdir = join20(dir, ".git"),
17030
17030
  oids
17031
17031
  }) {
17032
17032
  const hash2 = new Hash();
@@ -17069,7 +17069,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17069
17069
  const packfileSha = packfile.slice(-20).toString("hex");
17070
17070
  const filename = `pack-${packfileSha}.pack`;
17071
17071
  if (write) {
17072
- await fs2.write(join19(gitdir, `objects/pack/${filename}`), packfile);
17072
+ await fs2.write(join20(gitdir, `objects/pack/${filename}`), packfile);
17073
17073
  return { filename };
17074
17074
  }
17075
17075
  return {
@@ -17080,7 +17080,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17080
17080
  async function packObjects({
17081
17081
  fs: fs2,
17082
17082
  dir,
17083
- gitdir = join19(dir, ".git"),
17083
+ gitdir = join20(dir, ".git"),
17084
17084
  oids,
17085
17085
  write = false,
17086
17086
  cache = {}
@@ -17112,7 +17112,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17112
17112
  onAuthSuccess,
17113
17113
  onAuthFailure,
17114
17114
  dir,
17115
- gitdir = join19(dir, ".git"),
17115
+ gitdir = join20(dir, ".git"),
17116
17116
  ref,
17117
17117
  url: url3,
17118
17118
  remote,
@@ -17182,7 +17182,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17182
17182
  fs: fs2,
17183
17183
  cache,
17184
17184
  dir,
17185
- gitdir = join19(dir, ".git"),
17185
+ gitdir = join20(dir, ".git"),
17186
17186
  start,
17187
17187
  finish
17188
17188
  }) {
@@ -17230,7 +17230,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17230
17230
  fs: fs2,
17231
17231
  cache,
17232
17232
  dir,
17233
- gitdir = join19(dir, ".git"),
17233
+ gitdir = join20(dir, ".git"),
17234
17234
  oids
17235
17235
  }) {
17236
17236
  const visited = /* @__PURE__ */ new Set();
@@ -17533,7 +17533,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17533
17533
  onAuthFailure,
17534
17534
  onPrePush,
17535
17535
  dir,
17536
- gitdir = join19(dir, ".git"),
17536
+ gitdir = join20(dir, ".git"),
17537
17537
  ref,
17538
17538
  remoteRef,
17539
17539
  remote = "origin",
@@ -17609,7 +17609,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17609
17609
  async function readBlob({
17610
17610
  fs: fs2,
17611
17611
  dir,
17612
- gitdir = join19(dir, ".git"),
17612
+ gitdir = join20(dir, ".git"),
17613
17613
  oid,
17614
17614
  filepath,
17615
17615
  cache = {}
@@ -17635,7 +17635,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17635
17635
  async function readCommit({
17636
17636
  fs: fs2,
17637
17637
  dir,
17638
- gitdir = join19(dir, ".git"),
17638
+ gitdir = join20(dir, ".git"),
17639
17639
  oid,
17640
17640
  cache = {}
17641
17641
  }) {
@@ -17676,7 +17676,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17676
17676
  async function readNote({
17677
17677
  fs: fs2,
17678
17678
  dir,
17679
- gitdir = join19(dir, ".git"),
17679
+ gitdir = join20(dir, ".git"),
17680
17680
  ref = "refs/notes/commits",
17681
17681
  oid,
17682
17682
  cache = {}
@@ -17703,7 +17703,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17703
17703
  async function readObject({
17704
17704
  fs: _fs,
17705
17705
  dir,
17706
- gitdir = join19(dir, ".git"),
17706
+ gitdir = join20(dir, ".git"),
17707
17707
  oid,
17708
17708
  format = "parsed",
17709
17709
  filepath = void 0,
@@ -17792,7 +17792,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17792
17792
  async function readTag({
17793
17793
  fs: fs2,
17794
17794
  dir,
17795
- gitdir = join19(dir, ".git"),
17795
+ gitdir = join20(dir, ".git"),
17796
17796
  oid,
17797
17797
  cache = {}
17798
17798
  }) {
@@ -17816,7 +17816,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17816
17816
  async function readTree({
17817
17817
  fs: fs2,
17818
17818
  dir,
17819
- gitdir = join19(dir, ".git"),
17819
+ gitdir = join20(dir, ".git"),
17820
17820
  oid,
17821
17821
  filepath = void 0,
17822
17822
  cache = {}
@@ -17842,7 +17842,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17842
17842
  async function remove2({
17843
17843
  fs: _fs,
17844
17844
  dir,
17845
- gitdir = join19(dir, ".git"),
17845
+ gitdir = join20(dir, ".git"),
17846
17846
  filepath,
17847
17847
  cache = {}
17848
17848
  }) {
@@ -17915,7 +17915,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17915
17915
  fs: _fs,
17916
17916
  onSign,
17917
17917
  dir,
17918
- gitdir = join19(dir, ".git"),
17918
+ gitdir = join20(dir, ".git"),
17919
17919
  ref = "refs/notes/commits",
17920
17920
  oid,
17921
17921
  author: _author,
@@ -18003,7 +18003,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18003
18003
  async function renameBranch({
18004
18004
  fs: fs2,
18005
18005
  dir,
18006
- gitdir = join19(dir, ".git"),
18006
+ gitdir = join20(dir, ".git"),
18007
18007
  ref,
18008
18008
  oldref,
18009
18009
  checkout: checkout2 = false
@@ -18033,7 +18033,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18033
18033
  async function resetIndex({
18034
18034
  fs: _fs,
18035
18035
  dir,
18036
- gitdir = join19(dir, ".git"),
18036
+ gitdir = join20(dir, ".git"),
18037
18037
  filepath,
18038
18038
  ref,
18039
18039
  cache = {}
@@ -18080,7 +18080,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18080
18080
  gid: 0,
18081
18081
  size: 0
18082
18082
  };
18083
- const object2 = dir && await fs2.read(join19(dir, filepath));
18083
+ const object2 = dir && await fs2.read(join20(dir, filepath));
18084
18084
  if (object2) {
18085
18085
  workdirOid = await hashObject$1({
18086
18086
  gitdir: updatedGitdir,
@@ -18088,7 +18088,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18088
18088
  object: object2
18089
18089
  });
18090
18090
  if (oid === workdirOid) {
18091
- stats = await fs2.lstat(join19(dir, filepath));
18091
+ stats = await fs2.lstat(join20(dir, filepath));
18092
18092
  }
18093
18093
  }
18094
18094
  await GitIndexManager.acquire(
@@ -18108,7 +18108,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18108
18108
  async function resolveRef2({
18109
18109
  fs: fs2,
18110
18110
  dir,
18111
- gitdir = join19(dir, ".git"),
18111
+ gitdir = join20(dir, ".git"),
18112
18112
  ref,
18113
18113
  depth
18114
18114
  }) {
@@ -18133,7 +18133,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18133
18133
  async function setConfig({
18134
18134
  fs: _fs,
18135
18135
  dir,
18136
- gitdir = join19(dir, ".git"),
18136
+ gitdir = join20(dir, ".git"),
18137
18137
  path,
18138
18138
  value,
18139
18139
  append = false
@@ -18201,7 +18201,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18201
18201
  * @param {string} args.dir - The working directory.
18202
18202
  * @param {string}[args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
18203
18203
  */
18204
- constructor({ fs: fs2, dir, gitdir = join19(dir, ".git") }) {
18204
+ constructor({ fs: fs2, dir, gitdir = join20(dir, ".git") }) {
18205
18205
  Object.assign(this, {
18206
18206
  fs: fs2,
18207
18207
  dir,
@@ -18231,7 +18231,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18231
18231
  * @returns {string} - The file path for the stash reference.
18232
18232
  */
18233
18233
  get refStashPath() {
18234
- return join19(this.gitdir, _GitStashManager.refStash);
18234
+ return join20(this.gitdir, _GitStashManager.refStash);
18235
18235
  }
18236
18236
  /**
18237
18237
  * Gets the file path for the stash reflogs.
@@ -18239,7 +18239,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18239
18239
  * @returns {string} - The file path for the stash reflogs.
18240
18240
  */
18241
18241
  get refLogsStashPath() {
18242
- return join19(this.gitdir, _GitStashManager.refLogsStash);
18242
+ return join20(this.gitdir, _GitStashManager.refLogsStash);
18243
18243
  }
18244
18244
  /**
18245
18245
  * Retrieves the author information for the stash.
@@ -18545,7 +18545,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18545
18545
  async function stash({
18546
18546
  fs: fs2,
18547
18547
  dir,
18548
- gitdir = join19(dir, ".git"),
18548
+ gitdir = join20(dir, ".git"),
18549
18549
  op = "push",
18550
18550
  message = "",
18551
18551
  refIdx = 0
@@ -18568,7 +18568,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18568
18568
  const _fs = new FileSystem(fs2);
18569
18569
  const updatedGitdir = await discoverGitdir({ fsp: _fs, dotgit: gitdir });
18570
18570
  const folders = ["refs", "logs", "logs/refs"];
18571
- folders.map((f) => join19(updatedGitdir, f)).forEach(async (folder) => {
18571
+ folders.map((f) => join20(updatedGitdir, f)).forEach(async (folder) => {
18572
18572
  if (!await _fs.exists(folder)) {
18573
18573
  await _fs.mkdir(folder);
18574
18574
  }
@@ -18598,7 +18598,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18598
18598
  async function status({
18599
18599
  fs: _fs,
18600
18600
  dir,
18601
- gitdir = join19(dir, ".git"),
18601
+ gitdir = join20(dir, ".git"),
18602
18602
  filepath,
18603
18603
  cache = {},
18604
18604
  refresh: refresh2 = true
@@ -18637,7 +18637,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18637
18637
  return "ignored";
18638
18638
  }
18639
18639
  }
18640
- const stats = await fs2.lstat(join19(dir, filepath));
18640
+ const stats = await fs2.lstat(join20(dir, filepath));
18641
18641
  const H = treeOid !== null;
18642
18642
  const I = indexEntry !== null;
18643
18643
  const W = stats !== null;
@@ -18647,7 +18647,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18647
18647
  } else {
18648
18648
  const config2 = await GitConfigManager.get({ fs: fs2, gitdir: updatedGitdir });
18649
18649
  const autocrlf = await config2.get("core.autocrlf");
18650
- const object2 = await fs2.read(join19(dir, filepath), { autocrlf });
18650
+ const object2 = await fs2.read(join20(dir, filepath), { autocrlf });
18651
18651
  const workdirOid = await hashObject$1({
18652
18652
  gitdir: updatedGitdir,
18653
18653
  type: "blob",
@@ -18696,9 +18696,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18696
18696
  }
18697
18697
  async function getOidAtPath({ fs: fs2, cache, gitdir: updatedGitdir, tree, path }) {
18698
18698
  if (typeof path === "string") path = path.split("/");
18699
- const dirname16 = path.shift();
18699
+ const dirname17 = path.shift();
18700
18700
  for (const entry of tree) {
18701
- if (entry.path === dirname16) {
18701
+ if (entry.path === dirname17) {
18702
18702
  if (path.length === 0) {
18703
18703
  return entry.oid;
18704
18704
  }
@@ -18738,7 +18738,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18738
18738
  async function statusMatrix({
18739
18739
  fs: _fs,
18740
18740
  dir,
18741
- gitdir = join19(dir, ".git"),
18741
+ gitdir = join20(dir, ".git"),
18742
18742
  ref = "HEAD",
18743
18743
  filepaths = ["."],
18744
18744
  filter,
@@ -18811,7 +18811,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18811
18811
  async function tag({
18812
18812
  fs: _fs,
18813
18813
  dir,
18814
- gitdir = join19(dir, ".git"),
18814
+ gitdir = join20(dir, ".git"),
18815
18815
  ref,
18816
18816
  object: object2,
18817
18817
  force = false
@@ -18843,7 +18843,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18843
18843
  async function updateIndex$1({
18844
18844
  fs: _fs,
18845
18845
  dir,
18846
- gitdir = join19(dir, ".git"),
18846
+ gitdir = join20(dir, ".git"),
18847
18847
  cache = {},
18848
18848
  filepath,
18849
18849
  oid,
@@ -18863,7 +18863,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18863
18863
  { fs: fs2, gitdir: updatedGitdir, cache },
18864
18864
  async function(index3) {
18865
18865
  if (!force) {
18866
- const fileStats2 = await fs2.lstat(join19(dir, filepath));
18866
+ const fileStats2 = await fs2.lstat(join20(dir, filepath));
18867
18867
  if (fileStats2) {
18868
18868
  if (fileStats2.isDirectory()) {
18869
18869
  throw new InvalidFilepathError("directory");
@@ -18881,7 +18881,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18881
18881
  }
18882
18882
  let fileStats;
18883
18883
  if (!oid) {
18884
- fileStats = await fs2.lstat(join19(dir, filepath));
18884
+ fileStats = await fs2.lstat(join20(dir, filepath));
18885
18885
  if (!fileStats) {
18886
18886
  throw new NotFoundError(
18887
18887
  `file at "${filepath}" on disk and "remove" not set`
@@ -18902,7 +18902,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18902
18902
  let stats;
18903
18903
  if (!oid) {
18904
18904
  stats = fileStats;
18905
- const object2 = stats.isSymbolicLink() ? await fs2.readlink(join19(dir, filepath)) : await fs2.read(join19(dir, filepath));
18905
+ const object2 = stats.isSymbolicLink() ? await fs2.readlink(join20(dir, filepath)) : await fs2.read(join20(dir, filepath));
18906
18906
  oid = await _writeObject({
18907
18907
  fs: fs2,
18908
18908
  gitdir: updatedGitdir,
@@ -18946,7 +18946,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18946
18946
  async function walk({
18947
18947
  fs: fs2,
18948
18948
  dir,
18949
- gitdir = join19(dir, ".git"),
18949
+ gitdir = join20(dir, ".git"),
18950
18950
  trees,
18951
18951
  map: map2,
18952
18952
  reduce,
@@ -18974,7 +18974,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18974
18974
  throw err2;
18975
18975
  }
18976
18976
  }
18977
- async function writeBlob({ fs: fs2, dir, gitdir = join19(dir, ".git"), blob: blob2 }) {
18977
+ async function writeBlob({ fs: fs2, dir, gitdir = join20(dir, ".git"), blob: blob2 }) {
18978
18978
  try {
18979
18979
  assertParameter("fs", fs2);
18980
18980
  assertParameter("gitdir", gitdir);
@@ -18996,7 +18996,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
18996
18996
  async function writeCommit({
18997
18997
  fs: fs2,
18998
18998
  dir,
18999
- gitdir = join19(dir, ".git"),
18999
+ gitdir = join20(dir, ".git"),
19000
19000
  commit: commit2
19001
19001
  }) {
19002
19002
  try {
@@ -19018,7 +19018,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
19018
19018
  async function writeObject({
19019
19019
  fs: _fs,
19020
19020
  dir,
19021
- gitdir = join19(dir, ".git"),
19021
+ gitdir = join20(dir, ".git"),
19022
19022
  type,
19023
19023
  object: object2,
19024
19024
  format = "parsed",
@@ -19064,7 +19064,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
19064
19064
  async function writeRef({
19065
19065
  fs: _fs,
19066
19066
  dir,
19067
- gitdir = join19(dir, ".git"),
19067
+ gitdir = join20(dir, ".git"),
19068
19068
  ref,
19069
19069
  value,
19070
19070
  force = false,
@@ -19119,7 +19119,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
19119
19119
  });
19120
19120
  return oid;
19121
19121
  }
19122
- async function writeTag({ fs: fs2, dir, gitdir = join19(dir, ".git"), tag: tag2 }) {
19122
+ async function writeTag({ fs: fs2, dir, gitdir = join20(dir, ".git"), tag: tag2 }) {
19123
19123
  try {
19124
19124
  assertParameter("fs", fs2);
19125
19125
  assertParameter("gitdir", gitdir);
@@ -19136,7 +19136,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
19136
19136
  throw err2;
19137
19137
  }
19138
19138
  }
19139
- async function writeTree2({ fs: fs2, dir, gitdir = join19(dir, ".git"), tree }) {
19139
+ async function writeTree2({ fs: fs2, dir, gitdir = join20(dir, ".git"), tree }) {
19140
19140
  try {
19141
19141
  assertParameter("fs", fs2);
19142
19142
  assertParameter("gitdir", gitdir);
@@ -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.26.0",
19764
+ version: "0.28.0",
19765
19765
  description: "Agent-friendly AnimaGuru CLI built with CrustJS",
19766
19766
  type: "module",
19767
19767
  bin: {
@@ -19978,14 +19978,14 @@ function normalizeFlag(name2, def) {
19978
19978
  name: name2,
19979
19979
  reason: "empty-env"
19980
19980
  });
19981
- for (const [field, delimiter2] of [["delimiter", def.delimiter], ["env.delimiter", def.env?.delimiter]]) {
19982
- if (delimiter2 === void 0) continue;
19981
+ for (const [field, delimiter3] of [["delimiter", def.delimiter], ["env.delimiter", def.env?.delimiter]]) {
19982
+ if (delimiter3 === void 0) continue;
19983
19983
  if (!def.multiple) throw new CrustError("DEFINITION", `Flag "${name2}" ${field} requires multiple: true`, {
19984
19984
  subject: "flag",
19985
19985
  name: name2,
19986
19986
  reason: "delimiter-without-multiple"
19987
19987
  });
19988
- if (delimiter2 === "") throw new CrustError("DEFINITION", `Flag "${name2}" ${field} must be non-empty`, {
19988
+ if (delimiter3 === "") throw new CrustError("DEFINITION", `Flag "${name2}" ${field} must be non-empty`, {
19989
19989
  subject: "flag",
19990
19990
  name: name2,
19991
19991
  reason: "empty-delimiter"
@@ -20457,9 +20457,9 @@ function resolveAliases(tokens, spellings) {
20457
20457
  }
20458
20458
  return canonical;
20459
20459
  }
20460
- function splitOccurrences(values, delimiter2) {
20461
- if (delimiter2 === void 0) return [...values];
20462
- return values.flatMap((value) => value.split(delimiter2)).filter((value) => value !== "");
20460
+ function splitOccurrences(values, delimiter3) {
20461
+ if (delimiter3 === void 0) return [...values];
20462
+ return values.flatMap((value) => value.split(delimiter3)).filter((value) => value !== "");
20463
20463
  }
20464
20464
  function envFlagValue(name2, def, raw) {
20465
20465
  const occurrences = def.multiple ? splitOccurrences([raw], def.env?.delimiter) : [raw];
@@ -24295,8 +24295,8 @@ var version = defineExtension(VERSION).factory((extension, value, options = {})
24295
24295
 
24296
24296
  // cli/src/commands.ts
24297
24297
  import { spawn as spawn3 } from "node:child_process";
24298
- import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
24299
- import { dirname as dirname14, resolve as resolve17 } from "node:path";
24298
+ import { mkdir as mkdir16, readFile as readFile20, writeFile as writeFile16 } from "node:fs/promises";
24299
+ import { dirname as dirname15, resolve as resolve18 } from "node:path";
24300
24300
  import { setTimeout as delay6 } from "node:timers/promises";
24301
24301
 
24302
24302
  // node_modules/fflate/esm/index.mjs
@@ -27487,8 +27487,8 @@ function emoji2() {
27487
27487
  }
27488
27488
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
27489
27489
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
27490
- var mac = (delimiter2) => {
27491
- const escapedDelim = escapeRegex(delimiter2 ?? ":");
27490
+ var mac = (delimiter3) => {
27491
+ const escapedDelim = escapeRegex(delimiter3 ?? ":");
27492
27492
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
27493
27493
  };
27494
27494
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -30516,7 +30516,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
30516
30516
  var NONE = 0;
30517
30517
  var ASSUMED = 1;
30518
30518
  var PROVEN = 2;
30519
- function isRecursive(inst, stack, resolve18) {
30519
+ function isRecursive(inst, stack, resolve19) {
30520
30520
  const cached2 = recursive.get(inst);
30521
30521
  if (cached2 !== void 0)
30522
30522
  return cached2 ? PROVEN : NONE;
@@ -30526,7 +30526,7 @@ function isRecursive(inst, stack, resolve18) {
30526
30526
  let result = NONE;
30527
30527
  const check2 = (child) => {
30528
30528
  if (result !== PROVEN && child?._zod) {
30529
- const answer = isRecursive(child, stack, resolve18);
30529
+ const answer = isRecursive(child, stack, resolve19);
30530
30530
  if (answer > result)
30531
30531
  result = answer;
30532
30532
  }
@@ -30537,7 +30537,7 @@ function isRecursive(inst, stack, resolve18) {
30537
30537
  const desc = Object.getOwnPropertyDescriptor(sh, key);
30538
30538
  if (spread && !desc.enumerable)
30539
30539
  continue;
30540
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve18) : NONE;
30540
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve19) : NONE;
30541
30541
  if (child > answer)
30542
30542
  answer = child;
30543
30543
  }
@@ -30601,7 +30601,7 @@ function isRecursive(inst, stack, resolve18) {
30601
30601
  break;
30602
30602
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
30603
30603
  case "lazy": {
30604
- const inner = def._cachedInner ?? (resolve18 ? inst._zod.innerType : void 0);
30604
+ const inner = def._cachedInner ?? (resolve19 ? inst._zod.innerType : void 0);
30605
30605
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
30606
30606
  break;
30607
30607
  }
@@ -45293,10 +45293,62 @@ async function initializeSkill(directory = process.cwd()) {
45293
45293
  return { path, created };
45294
45294
  }
45295
45295
 
45296
+ // cli/src/skills.ts
45297
+ import { spawn as nodeSpawn } from "node:child_process";
45298
+ import { access, mkdir as mkdir6, readFile as readFile4, readdir as readdir2, stat } from "node:fs/promises";
45299
+ import { delimiter, dirname as dirname5, join as join6, resolve as resolve5 } from "node:path";
45300
+ var SKILLS_CLI = "skills@1.7.4";
45301
+ var SKILL_AGENTS = { "claude-code": ".claude/skills", codex: ".agents/skills" };
45302
+ var skillsSource = (server) => `${new URL(server).origin}/cli/skills`;
45303
+ async function skillsInstallCommand(source, platform = process.platform, execPath = process.execPath) {
45304
+ const args = ["-y", SKILLS_CLI, "add", source, "--skill", "*", ...Object.keys(SKILL_AGENTS).flatMap((agent) => ["--agent", agent]), "--copy", "--yes"];
45305
+ const adjacentNpx = resolve5(dirname5(execPath), "../lib/node_modules/npm/bin/npx-cli.js");
45306
+ try {
45307
+ await access(adjacentNpx);
45308
+ return { command: execPath, args: [adjacentNpx, ...args], shell: false };
45309
+ } catch {
45310
+ return { command: platform === "win32" ? "npx.cmd" : "npx", args, shell: platform === "win32" };
45311
+ }
45312
+ }
45313
+ async function lockedSkills(directory, source) {
45314
+ try {
45315
+ const lock = JSON.parse(await readFile4(join6(directory, "skills-lock.json"), "utf8"));
45316
+ return Object.entries(lock.skills ?? {}).filter(([, skill]) => skill.sourceUrl === source).map(([name2]) => name2).sort();
45317
+ } catch {
45318
+ return [];
45319
+ }
45320
+ }
45321
+ async function installSkills(options) {
45322
+ const directory = resolve5(options.dir ?? ".");
45323
+ await mkdir6(directory, { recursive: true });
45324
+ if (!(await stat(directory)).isDirectory()) throw new Error(`${directory} is not a folder.`);
45325
+ const source = skillsSource(options.url);
45326
+ const { command, args, shell } = await skillsInstallCommand(source);
45327
+ options.log?.(`Installing AnimaGuru skills from ${source} into ${directory}\u2026`);
45328
+ await new Promise((done, reject) => {
45329
+ const child = (options.spawn ?? nodeSpawn)(command, args, {
45330
+ cwd: directory,
45331
+ signal: options.signal,
45332
+ stdio: ["ignore", "pipe", "pipe"],
45333
+ shell,
45334
+ // The skills CLI reports installs to skills.sh unless told not to.
45335
+ env: { ...process.env, DISABLE_TELEMETRY: "1", DO_NOT_TRACK: "1", PATH: `${dirname5(process.execPath)}${delimiter}${process.env.PATH ?? ""}` }
45336
+ });
45337
+ child.stdout?.on("data", (chunk) => options.log?.(chunk.toString().trimEnd()));
45338
+ child.stderr?.on("data", (chunk) => options.log?.(chunk.toString().trimEnd()));
45339
+ child.once("error", (error62) => reject(error62.code === "ENOENT" ? new Error("Installing skills needs npx, which comes with npm and Node.js 24. Install Node.js, or run the AnimaGuru installer again.") : error62));
45340
+ child.once("close", (code) => code === 0 ? done() : reject(new Error(`npx ${SKILLS_CLI} add exited with code ${code}; see its output above.`)));
45341
+ });
45342
+ const installed = await lockedSkills(directory, source);
45343
+ const folders = await Promise.all(Object.values(SKILL_AGENTS).map(async (folder) => (await readdir2(join6(directory, folder)).catch(() => [])).length));
45344
+ if (!installed.length || folders.some((count) => count === 0)) throw new Error("The skills CLI finished without installing AnimaGuru skills; see its output above.");
45345
+ return { directory, source, skills: installed.length, installed, agents: SKILL_AGENTS, lockfile: join6(directory, "skills-lock.json"), update: "npx skills update --project --yes" };
45346
+ }
45347
+
45296
45348
  // cli/src/create.ts
45297
45349
  import { spawn } from "node:child_process";
45298
- import { cp, mkdir as mkdir6, readFile as readFile4, readdir as readdir2, access, writeFile as writeFile6, rename as rename3 } from "node:fs/promises";
45299
- import { basename as basename2, dirname as dirname5, delimiter, join as join6, resolve as resolve5 } from "node:path";
45350
+ import { cp, mkdir as mkdir7, readFile as readFile5, readdir as readdir3, access as access2, writeFile as writeFile6, rename as rename3 } from "node:fs/promises";
45351
+ import { basename as basename2, dirname as dirname6, delimiter as delimiter2, join as join7, resolve as resolve6 } from "node:path";
45300
45352
  import { fileURLToPath } from "node:url";
45301
45353
 
45302
45354
  // shared/locales/en/animation-styles.json
@@ -46275,6 +46327,74 @@ var animation_styles_default = {
46275
46327
  previewCaption: "One champagne line draws a sun on the horizon, three arches and a woman's profile without lifting, closes a ring that fills with gold from the point where it met, then pulls back to the wordmark. Invented brand, drawn entirely in code.",
46276
46328
  family: "graphic",
46277
46329
  example: true
46330
+ },
46331
+ "product-turntable-3d": {
46332
+ name: "3D Product Turntable",
46333
+ description: "A real 3D product turns on a studio set while the camera orbits and pushes in, studio light slides across its materials and callouts stay pinned to its parts.",
46334
+ materials: [
46335
+ "Generated PBR product model",
46336
+ "Studio HDR reflections",
46337
+ "Shallow depth of field"
46338
+ ],
46339
+ motion: [
46340
+ "Turntable orbit and push-in",
46341
+ "Rack focus between details",
46342
+ "Callouts that track parts"
46343
+ ],
46344
+ previewCaption: "Generated headphones turn on a stone plinth as the camera pulls back from a macro of the brushed yoke, focus racking from part to part under pinned callouts, ending on a lockup for an invented brand. Model generated with Tripo H3.1, rendered in three.js.",
46345
+ family: "3d",
46346
+ example: true
46347
+ },
46348
+ "character-stage-3d": {
46349
+ name: "3D Character Stage",
46350
+ description: "A rigged 3D character walks, turns and gestures in front of a painted 2D backdrop, casting real shadows onto the painting while captions carry the story.",
46351
+ materials: [
46352
+ "Rigged character model",
46353
+ "Painted 2D backdrop",
46354
+ "Shadows cast onto the painting"
46355
+ ],
46356
+ motion: [
46357
+ "Walk cycles at true speed",
46358
+ "Crossfaded gestures",
46359
+ "A camera that follows the character"
46360
+ ],
46361
+ previewCaption: "A generated, auto-rigged baker walks across a painted square, turns to camera, waves and explains, his shadow falling over the painting. Character made with Tripo H3.1 and Meshy's auto-rig, rendered in three.js.",
46362
+ family: "3d",
46363
+ example: true
46364
+ },
46365
+ "toy-diorama-3d": {
46366
+ name: "3D Toy Diorama",
46367
+ description: "A miniature 3D world on a tabletop, seen through tilt-shift focus as a free camera glides over tiny props and characters going about their day.",
46368
+ materials: [
46369
+ "Toy-scale models",
46370
+ "Tilt-shift depth of field",
46371
+ "Warm tabletop light"
46372
+ ],
46373
+ motion: [
46374
+ "Free camera glides",
46375
+ "Little cars and figures on loops",
46376
+ "Focus pulled through the scene"
46377
+ ],
46378
+ previewCaption: "A camera glides over a toy harbour town as little cars and boats run their loops, everything sharp only in a thin band of focus. Props generated with Tripo H3.1, rendered in three.js.",
46379
+ family: "3d",
46380
+ example: true
46381
+ },
46382
+ "exploded-teardown-3d": {
46383
+ name: "3D Exploded Teardown",
46384
+ description: "A 3D product orbits and comes apart along its assembly axes, each part followed by a leader label that stays with it through the turn.",
46385
+ materials: [
46386
+ "Part-by-part 3D model",
46387
+ "Leader labels and callouts",
46388
+ "Clean studio grey"
46389
+ ],
46390
+ motion: [
46391
+ "Orbit while the parts separate",
46392
+ "Parts sliding along their axes",
46393
+ "Labels that follow each part"
46394
+ ],
46395
+ previewCaption: "An invented handheld console comes apart along its assembly axes as the camera orbits, every part keeping its label. Modelled and rendered in code.",
46396
+ family: "3d",
46397
+ example: true
46278
46398
  }
46279
46399
  },
46280
46400
  browseStyles: "Browse animation styles",
@@ -46290,7 +46410,8 @@ var animation_styles_default = {
46290
46410
  material: "Materials & depth",
46291
46411
  cinematic: "Cinema & media",
46292
46412
  evidence: "Evidence & data",
46293
- documentary: "Documentary & broadcast"
46413
+ documentary: "Documentary & broadcast",
46414
+ "3d": "3D scenes"
46294
46415
  },
46295
46416
  searchLabel: "Search visual styles",
46296
46417
  searchPlaceholder: "Try paper, product, typography\u2026",
@@ -46423,7 +46544,11 @@ var VIDEO_STYLE_IDS = [
46423
46544
  "synthwave-y2k",
46424
46545
  "sticker-explainer",
46425
46546
  "variety-captions",
46426
- "one-line-art"
46547
+ "one-line-art",
46548
+ "product-turntable-3d",
46549
+ "character-stage-3d",
46550
+ "toy-diorama-3d",
46551
+ "exploded-teardown-3d"
46427
46552
  ];
46428
46553
  var VIDEO_WORKFLOW_IDS = [
46429
46554
  "product-proof",
@@ -46483,10 +46608,10 @@ function listTemplates() {
46483
46608
  return videoTemplateCatalog();
46484
46609
  }
46485
46610
  async function installDependencies(directory, options) {
46486
- const adjacentNpm = resolve5(dirname5(process.execPath), "../lib/node_modules/npm/bin/npm-cli.js");
46611
+ const adjacentNpm = resolve6(dirname6(process.execPath), "../lib/node_modules/npm/bin/npm-cli.js");
46487
46612
  let npmScript;
46488
46613
  try {
46489
- await access(adjacentNpm);
46614
+ await access2(adjacentNpm);
46490
46615
  npmScript = adjacentNpm;
46491
46616
  } catch {
46492
46617
  }
@@ -46497,7 +46622,7 @@ async function installDependencies(directory, options) {
46497
46622
  signal: options.signal,
46498
46623
  stdio: ["ignore", "pipe", "pipe"],
46499
46624
  shell: !npmScript && process.platform === "win32",
46500
- env: { ...process.env, PATH: `${dirname5(process.execPath)}${delimiter}${process.env.PATH ?? ""}` }
46625
+ env: { ...process.env, PATH: `${dirname6(process.execPath)}${delimiter2}${process.env.PATH ?? ""}` }
46501
46626
  });
46502
46627
  child.stdout.on("data", (chunk) => options.log?.(chunk.toString().trimEnd()));
46503
46628
  child.stderr.on("data", (chunk) => options.log?.(chunk.toString().trimEnd()));
@@ -46522,7 +46647,7 @@ var STARTER_STORY = {
46522
46647
  { id: "invitation", label: "The invitation", start: 16, end: 24, ending: "Your next story starts here" }
46523
46648
  ]
46524
46649
  };
46525
- var writeStarterStory = (directory) => writeFile6(join6(directory, "story.json"), JSON.stringify(STARTER_STORY, null, 2) + "\n", { flag: "wx" });
46650
+ var writeStarterStory = (directory) => writeFile6(join7(directory, "story.json"), JSON.stringify(STARTER_STORY, null, 2) + "\n", { flag: "wx" });
46526
46651
  function starterQuiz(story = false) {
46527
46652
  return {
46528
46653
  version: 1,
@@ -46535,42 +46660,42 @@ function starterQuiz(story = false) {
46535
46660
  ] }
46536
46661
  };
46537
46662
  }
46538
- var writeStarterQuiz = (directory, story = false) => writeFile6(join6(directory, "quiz.json"), JSON.stringify(starterQuiz(story), null, 2) + "\n", { flag: "wx" });
46663
+ var writeStarterQuiz = (directory, story = false) => writeFile6(join7(directory, "quiz.json"), JSON.stringify(starterQuiz(story), null, 2) + "\n", { flag: "wx" });
46539
46664
  async function createVideoProject(options, installer = installDependencies) {
46540
46665
  if (!options.directory.trim()) throw new Error("Choose a new project folder, for example: animaguru create my-video");
46541
- const directory = resolve5(options.directory);
46666
+ const directory = resolve6(options.directory);
46542
46667
  options.signal?.throwIfAborted();
46543
46668
  try {
46544
- await mkdir6(directory);
46669
+ await mkdir7(directory);
46545
46670
  } catch (error62) {
46546
46671
  if (error62.code === "EEXIST") throw new Error(`Destination already exists: ${directory}. Choose a new folder; existing files were not changed.`);
46547
46672
  throw error62;
46548
46673
  }
46549
46674
  options.log?.(`Creating a video workspace in ${directory}\u2026`);
46550
- const templateRoot = join6(packageRoot, "templates/video");
46551
- for (const entry of await readdir2(templateRoot)) {
46675
+ const templateRoot = join7(packageRoot, "templates/video");
46676
+ for (const entry of await readdir3(templateRoot)) {
46552
46677
  if (entry === "WORKSPACE-AGENTS.md") continue;
46553
- await cp(join6(templateRoot, entry), join6(directory, entry), { recursive: true, errorOnExist: true, force: false });
46678
+ await cp(join7(templateRoot, entry), join7(directory, entry), { recursive: true, errorOnExist: true, force: false });
46554
46679
  }
46555
- await rename3(join6(directory, "gitignore"), join6(directory, ".gitignore"));
46556
- await mkdir6(join6(directory, "public/assets"), { recursive: true });
46557
- await mkdir6(join6(directory, "production"), { recursive: true });
46558
- const packagePath = join6(directory, "package.json");
46559
- const manifest = JSON.parse(await readFile4(packagePath, "utf8"));
46680
+ await rename3(join7(directory, "gitignore"), join7(directory, ".gitignore"));
46681
+ await mkdir7(join7(directory, "public/assets"), { recursive: true });
46682
+ await mkdir7(join7(directory, "production"), { recursive: true });
46683
+ const packagePath = join7(directory, "package.json");
46684
+ const manifest = JSON.parse(await readFile5(packagePath, "utf8"));
46560
46685
  manifest.name = basename2(directory).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100) || "animaguru-video";
46561
46686
  await writeFile6(packagePath, JSON.stringify(manifest, null, 2) + "\n");
46562
- const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", "animaguru-art-motion", "animaguru-branching-story", "animaguru-quiz", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
46563
- const pixiSkillsRoot = join6(packageRoot, "vendor/pixijs-skills/skills");
46564
- const pixiSkills = (await readdir2(pixiSkillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
46687
+ const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", "animaguru-art-motion", "animaguru-branching-story", "animaguru-quiz", "animaguru-3d-models", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
46688
+ const pixiSkillsRoot = join7(packageRoot, "vendor/pixijs-skills/skills");
46689
+ const pixiSkills = (await readdir3(pixiSkillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
46565
46690
  for (const agent of [".agents", ".claude"]) {
46566
- const skillRoot = join6(directory, agent, "skills");
46567
- await mkdir6(skillRoot, { recursive: true });
46568
- for (const skill of skills) await cp(join6(packageRoot, "skills", skill), join6(skillRoot, skill), { recursive: true });
46569
- for (const skill of pixiSkills) await cp(join6(pixiSkillsRoot, skill), join6(skillRoot, skill), { recursive: true });
46570
- }
46571
- await cp(join6(packageRoot, "vendor/pixijs-skills/LICENSE"), join6(directory, "PIXIJS-SKILLS-LICENSE.txt"));
46572
- await cp(join6(packageRoot, "vendor/pixijs-skills/provenance.json"), join6(directory, "production/pixijs-skills-provenance.json"));
46573
- await writeFile6(join6(directory, "animaguru.project.json"), JSON.stringify({
46691
+ const skillRoot = join7(directory, agent, "skills");
46692
+ await mkdir7(skillRoot, { recursive: true });
46693
+ for (const skill of skills) await cp(join7(packageRoot, "skills", skill), join7(skillRoot, skill), { recursive: true });
46694
+ for (const skill of pixiSkills) await cp(join7(pixiSkillsRoot, skill), join7(skillRoot, skill), { recursive: true });
46695
+ }
46696
+ await cp(join7(packageRoot, "vendor/pixijs-skills/LICENSE"), join7(directory, "PIXIJS-SKILLS-LICENSE.txt"));
46697
+ await cp(join7(packageRoot, "vendor/pixijs-skills/provenance.json"), join7(directory, "production/pixijs-skills-provenance.json"));
46698
+ await writeFile6(join7(directory, "animaguru.project.json"), JSON.stringify({
46574
46699
  schemaVersion: 3,
46575
46700
  cliVersion: package_default.version,
46576
46701
  category: null,
@@ -46582,7 +46707,7 @@ async function createVideoProject(options, installer = installDependencies) {
46582
46707
  interview: "pending",
46583
46708
  animaguruProjectId: null
46584
46709
  }, null, 2) + "\n");
46585
- await writeFile6(join6(directory, "animaguru.templates.json"), JSON.stringify(listTemplates(), null, 2) + "\n");
46710
+ await writeFile6(join7(directory, "animaguru.templates.json"), JSON.stringify(listTemplates(), null, 2) + "\n");
46586
46711
  if (options.story) await writeStarterStory(directory);
46587
46712
  if (options.quiz) await writeStarterQuiz(directory, options.story);
46588
46713
  if (options.install !== false) {
@@ -46609,28 +46734,28 @@ ${options.install === false ? "Run npm install, then " : "Run "}npm run dev insi
46609
46734
  }
46610
46735
  async function createWorkspace(options, installer = installDependencies) {
46611
46736
  const result = await createVideoProject({ ...options, install: false, log: (message) => {
46612
- if (message.startsWith("Creating")) options.log?.(`Creating a shared-brand workspace in ${resolve5(options.directory)}\u2026`);
46737
+ if (message.startsWith("Creating")) options.log?.(`Creating a shared-brand workspace in ${resolve6(options.directory)}\u2026`);
46613
46738
  } }, installer);
46614
46739
  const directory = result.path;
46615
46740
  const { rm: rm3 } = await import("node:fs/promises");
46616
- await rename3(join6(directory, "src"), join6(directory, "runtime"));
46617
- await mkdir6(join6(directory, "templates/animation/src"), { recursive: true });
46618
- await mkdir6(join6(directory, "templates/animation/shared/locales/en"), { recursive: true });
46741
+ await rename3(join7(directory, "src"), join7(directory, "runtime"));
46742
+ await mkdir7(join7(directory, "templates/animation/src"), { recursive: true });
46743
+ await mkdir7(join7(directory, "templates/animation/shared/locales/en"), { recursive: true });
46619
46744
  for (const file2 of ["film.ts", "film-resources.ts"]) {
46620
- const original = await readFile4(join6(directory, "runtime", file2), "utf8");
46745
+ const original = await readFile5(join7(directory, "runtime", file2), "utf8");
46621
46746
  const rewritten = original.replace('"./player-types"', '"../../../runtime/player-types"').replace('"./timeline-model"', '"../../../runtime/timeline-model"').replace('"./kit"', '"../../../runtime/kit"').replace('"../shared/locales/en/timeline.json"', '"../../../shared/locales/en/timeline.json"');
46622
- await writeFile6(join6(directory, "templates/animation/src", file2), rewritten);
46623
- await rm3(join6(directory, "runtime", file2));
46747
+ await writeFile6(join7(directory, "templates/animation/src", file2), rewritten);
46748
+ await rm3(join7(directory, "runtime", file2));
46624
46749
  }
46625
- await rm3(join6(directory, "runtime/main.ts"));
46626
- await rename3(join6(directory, "film.config.json"), join6(directory, "templates/animation/film.config.json"));
46627
- await rename3(join6(directory, "shared/locales/en/film.json"), join6(directory, "templates/animation/shared/locales/en/film.json"));
46750
+ await rm3(join7(directory, "runtime/main.ts"));
46751
+ await rename3(join7(directory, "film.config.json"), join7(directory, "templates/animation/film.config.json"));
46752
+ await rename3(join7(directory, "shared/locales/en/film.json"), join7(directory, "templates/animation/shared/locales/en/film.json"));
46628
46753
  const { createHash: createHash7 } = await import("node:crypto");
46629
- await writeFile6(join6(directory, "templates/animation/starter-film.sha256"), createHash7("sha256").update(await readFile4(join6(directory, "templates/animation/src/film.ts"))).digest("hex") + "\n");
46630
- await rm3(join6(directory, "animaguru.project.json"));
46631
- await mkdir6(join6(directory, "animations"));
46632
- await mkdir6(join6(directory, "branding"));
46633
- await writeFile6(join6(directory, "branding/brand.json"), JSON.stringify({
46754
+ await writeFile6(join7(directory, "templates/animation/starter-film.sha256"), createHash7("sha256").update(await readFile5(join7(directory, "templates/animation/src/film.ts"))).digest("hex") + "\n");
46755
+ await rm3(join7(directory, "animaguru.project.json"));
46756
+ await mkdir7(join7(directory, "animations"));
46757
+ await mkdir7(join7(directory, "branding"));
46758
+ await writeFile6(join7(directory, "branding/brand.json"), JSON.stringify({
46634
46759
  name: "Your brand",
46635
46760
  tagline: "Your next story starts here",
46636
46761
  logo: null,
@@ -46638,18 +46763,18 @@ async function createWorkspace(options, installer = installDependencies) {
46638
46763
  fonts: { heading: { family: "Arial, sans-serif", file: null }, body: { family: "Arial, sans-serif", file: null } },
46639
46764
  assets: []
46640
46765
  }, null, 2) + "\n");
46641
- await writeFile6(join6(directory, "branding/README.md"), "# Shared branding\n\nThe starter brand.json contains placeholders. On the first creative request, the agent asks for your official website or brand guide unless you already supplied one. It researches the identity and records sources and usage notes in brand-guide.md. You can also provide logos, fonts and guides here or proceed without a website. Register reusable web assets in brand.json with workspace-relative paths, such as branding/logo.svg. All animations inherit this brand. Only registered web assets are included in exports; guides and other local documents remain in this workspace.\n");
46642
- await writeFile6(join6(directory, "animaguru.workspace.json"), JSON.stringify({ schemaVersion: 1, cliVersion: package_default.version, name: basename2(directory), animationsDirectory: "animations", branding: "branding/brand.json" }, null, 2) + "\n");
46643
- const pkg = JSON.parse(await readFile4(join6(directory, "package.json"), "utf8"));
46766
+ await writeFile6(join7(directory, "branding/README.md"), "# Shared branding\n\nThe starter brand.json contains placeholders. On the first creative request, the agent asks for your official website or brand guide unless you already supplied one. It researches the identity and records sources and usage notes in brand-guide.md. You can also provide logos, fonts and guides here or proceed without a website. Register reusable web assets in brand.json with workspace-relative paths, such as branding/logo.svg. All animations inherit this brand. Only registered web assets are included in exports; guides and other local documents remain in this workspace.\n");
46767
+ await writeFile6(join7(directory, "animaguru.workspace.json"), JSON.stringify({ schemaVersion: 1, cliVersion: package_default.version, name: basename2(directory), animationsDirectory: "animations", branding: "branding/brand.json" }, null, 2) + "\n");
46768
+ const pkg = JSON.parse(await readFile5(join7(directory, "package.json"), "utf8"));
46644
46769
  pkg.scripts.dev = "node scripts/dev.mjs";
46645
- await writeFile6(join6(directory, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
46646
- const tsconfig = JSON.parse(await readFile4(join6(directory, "tsconfig.json"), "utf8"));
46770
+ await writeFile6(join7(directory, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
46771
+ const tsconfig = JSON.parse(await readFile5(join7(directory, "tsconfig.json"), "utf8"));
46647
46772
  tsconfig.include = ["runtime", "animations/*/src"];
46648
- await writeFile6(join6(directory, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
46649
- await writeFile6(join6(directory, "AGENTS.md"), await readFile4(join6(packageRoot, "templates/video/WORKSPACE-AGENTS.md"), "utf8"));
46650
- await writeFile6(join6(directory, "CLAUDE.md"), "# AnimaGuru workspace\n\nRead AGENTS.md for the multi-animation workflow, shared branding and commands. Claude skills are in .claude/skills. For an incomplete shared kit, read .claude/skills/animaguru-brand-kit/SKILL.md and ask for the official website or brand guide if the user did not supply one. Select the animation folder before editing; reuse shared runtime/ and scripts/.\n");
46651
- await rm3(join6(directory, "WORKSPACE-AGENTS.md"), { force: true });
46652
- await writeFile6(join6(directory, "README.md"), "# Your AnimaGuru workspace\n\nOpen this root folder in Codex or Claude Code and describe a film. If you have an official website or brand guide, include it; otherwise the agent asks for it while setting up the shared branding/ kit. You can also provide local brand assets or proceed without a website. Create a film with animaguru create animation my-video.\n\nRun npm run dev for the workspace hub. Run npm run build -- --animation my-video, npm run export:html -- --animation my-video, or npm run export:mp4 -- --animation my-video. Player code, dependencies, skills and export tools are shared once; animation folders contain only their own production files.\n");
46773
+ await writeFile6(join7(directory, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
46774
+ await writeFile6(join7(directory, "AGENTS.md"), await readFile5(join7(packageRoot, "templates/video/WORKSPACE-AGENTS.md"), "utf8"));
46775
+ await writeFile6(join7(directory, "CLAUDE.md"), "# AnimaGuru workspace\n\nRead AGENTS.md for the multi-animation workflow, shared branding and commands. Claude skills are in .claude/skills. For an incomplete shared kit, read .claude/skills/animaguru-brand-kit/SKILL.md and ask for the official website or brand guide if the user did not supply one. Select the animation folder before editing; reuse shared runtime/ and scripts/.\n");
46776
+ await rm3(join7(directory, "WORKSPACE-AGENTS.md"), { force: true });
46777
+ await writeFile6(join7(directory, "README.md"), "# Your AnimaGuru workspace\n\nOpen this root folder in Codex or Claude Code and describe a film. If you have an official website or brand guide, include it; otherwise the agent asks for it while setting up the shared branding/ kit. You can also provide local brand assets or proceed without a website. Create a film with animaguru create animation my-video.\n\nRun npm run dev for the workspace hub. Run npm run build -- --animation my-video, npm run export:html -- --animation my-video, or npm run export:mp4 -- --animation my-video. Player code, dependencies, skills and export tools are shared once; animation folders contain only their own production files.\n");
46653
46778
  if (options.install !== false) {
46654
46779
  try {
46655
46780
  await installer(directory, options);
@@ -46665,23 +46790,23 @@ async function createWorkspace(options, installer = installDependencies) {
46665
46790
  skills: result.skills,
46666
46791
  agents: result.agents,
46667
46792
  installed: options.install !== false,
46668
- branding: join6(directory, "branding"),
46669
- animations: join6(directory, "animations"),
46793
+ branding: join7(directory, "branding"),
46794
+ animations: join7(directory, "animations"),
46670
46795
  nextSteps: ["Open this workspace in Codex or Claude Code and provide your website or brand guide; the agent asks if it is missing.", "Build the shared brand kit in branding/.", "Run animaguru create animation my-video.", "Run npm run dev for the animation hub."]
46671
46796
  };
46672
46797
  }
46673
46798
  async function findWorkspace(start = process.cwd()) {
46674
46799
  const { realpath } = await import("node:fs/promises");
46675
- let directory = await realpath(resolve5(start));
46800
+ let directory = await realpath(resolve6(start));
46676
46801
  while (true) {
46677
46802
  try {
46678
- const info = JSON.parse(await readFile4(join6(directory, "animaguru.workspace.json"), "utf8"));
46803
+ const info = JSON.parse(await readFile5(join7(directory, "animaguru.workspace.json"), "utf8"));
46679
46804
  if (info.schemaVersion !== 1 || info.animationsDirectory !== "animations" || info.branding !== "branding/brand.json") throw new Error("Unsupported AnimaGuru workspace.");
46680
46805
  return directory;
46681
46806
  } catch (error62) {
46682
46807
  if (error62.code !== "ENOENT") throw error62;
46683
46808
  }
46684
- const parent = dirname5(directory);
46809
+ const parent = dirname6(directory);
46685
46810
  if (parent === directory) throw new Error("Run this inside an AnimaGuru workspace, or pass --workspace PATH. Create one with animaguru create workspace NAME.");
46686
46811
  directory = parent;
46687
46812
  }
@@ -46690,23 +46815,23 @@ async function createAnimation(options) {
46690
46815
  if (!/^[a-z0-9][a-z0-9-]{0,79}$/.test(options.name)) throw new Error("Choose a lowercase, hyphenated animation name, such as product-launch.");
46691
46816
  options.signal?.throwIfAborted();
46692
46817
  const root = await findWorkspace(options.workspace);
46693
- const directory = join6(root, "animations", options.name);
46818
+ const directory = join7(root, "animations", options.name);
46694
46819
  try {
46695
- await mkdir6(directory);
46820
+ await mkdir7(directory);
46696
46821
  } catch (error62) {
46697
46822
  if (error62.code === "EEXIST") throw new Error(`Animation already exists: ${options.name}. Existing files were not changed.`);
46698
46823
  throw error62;
46699
46824
  }
46700
- for (const file2 of await readdir2(join6(root, "templates/animation"))) {
46701
- if (file2 !== "starter-film.sha256") await cp(join6(root, "templates/animation", file2), join6(directory, file2), { recursive: true, errorOnExist: true, force: false });
46825
+ for (const file2 of await readdir3(join7(root, "templates/animation"))) {
46826
+ if (file2 !== "starter-film.sha256") await cp(join7(root, "templates/animation", file2), join7(directory, file2), { recursive: true, errorOnExist: true, force: false });
46702
46827
  }
46703
- const config2 = JSON.parse(await readFile4(join6(directory, "film.config.json"), "utf8"));
46828
+ const config2 = JSON.parse(await readFile5(join7(directory, "film.config.json"), "utf8"));
46704
46829
  config2.slug = options.name;
46705
46830
  config2.title = options.name.replaceAll("-", " ").replace(/^./, (value) => value.toUpperCase());
46706
- await writeFile6(join6(directory, "film.config.json"), JSON.stringify(config2, null, 2) + "\n");
46707
- await mkdir6(join6(directory, "public/assets"), { recursive: true });
46708
- await mkdir6(join6(directory, "production"), { recursive: true });
46709
- await writeFile6(join6(directory, "animaguru.project.json"), JSON.stringify({ schemaVersion: 4, cliVersion: package_default.version, workspace: "../..", category: null, template: null, workflow: null, skill: null, setupStage: "category", galleryUrl: ANIMATION_STYLE_GALLERY_URL, interview: "pending", animaguruProjectId: null, branding: "inherit" }, null, 2) + "\n");
46831
+ await writeFile6(join7(directory, "film.config.json"), JSON.stringify(config2, null, 2) + "\n");
46832
+ await mkdir7(join7(directory, "public/assets"), { recursive: true });
46833
+ await mkdir7(join7(directory, "production"), { recursive: true });
46834
+ await writeFile6(join7(directory, "animaguru.project.json"), JSON.stringify({ schemaVersion: 4, cliVersion: package_default.version, workspace: "../..", category: null, template: null, workflow: null, skill: null, setupStage: "category", galleryUrl: ANIMATION_STYLE_GALLERY_URL, interview: "pending", animaguruProjectId: null, branding: "inherit" }, null, 2) + "\n");
46710
46835
  if (options.story) await writeStarterStory(directory);
46711
46836
  if (options.quiz) await writeStarterQuiz(directory, options.story);
46712
46837
  const kind = options.story && options.quiz ? "Branching story with a quiz" : options.story ? "Branching story" : options.quiz ? "Quiz film" : "Animation";
@@ -46725,8 +46850,65 @@ async function createAnimation(options) {
46725
46850
  };
46726
46851
  }
46727
46852
 
46853
+ // shared/model-assets.ts
46854
+ var MODEL_GENERATORS = [
46855
+ {
46856
+ id: "tripo-h3.1",
46857
+ label: "Tripo H3.1",
46858
+ endpoints: { image: "tripo3d/h3.1/image-to-3d", text: "tripo3d/h3.1/text-to-3d" },
46859
+ faces: { min: 1e3, max: 2e6 },
46860
+ /** The direction the generated model faces; saved models are turned to +Z. */
46861
+ facing: "+x"
46862
+ },
46863
+ {
46864
+ id: "hunyuan-3d-3.1-pro",
46865
+ label: "Hunyuan3D 3.1 Pro",
46866
+ endpoints: { image: "fal-ai/hunyuan-3d/v3.1/pro/image-to-3d", text: "fal-ai/hunyuan-3d/v3.1/pro/text-to-3d" },
46867
+ faces: { min: 4e4, max: 15e5 },
46868
+ facing: "+z"
46869
+ }
46870
+ ];
46871
+ var MODEL_GENERATOR_IDS = MODEL_GENERATORS.map((m) => m.id);
46872
+ var DEFAULT_MODEL_GENERATOR = "tripo-h3.1";
46873
+ var modelGenerator = (id2) => MODEL_GENERATORS.find((m) => m.id === id2) ?? MODEL_GENERATORS[0];
46874
+ var MAX_MODEL_CLIPS = 10;
46875
+ var MAX_RIG_FACES = 3e5;
46876
+ var MAX_MESHY_ACTION = 696;
46877
+ var MAX_MODEL_PROMPT = 1e3;
46878
+ var MODEL_ANIMATIONS = [
46879
+ { id: "idle", action: 0, label: "Idle" },
46880
+ { id: "idle-2", action: 11, label: "Idle 2" },
46881
+ { id: "idle-3", action: 12, label: "Idle 3" },
46882
+ { id: "walk", action: 30, label: "Casual walk" },
46883
+ { id: "walk-woman", action: 1, label: "Walking (woman)" },
46884
+ { id: "stage-walk", action: 55, label: "Stage walk" },
46885
+ { id: "thoughtful-walk", action: 121, label: "Thoughtful walk" },
46886
+ { id: "run", action: 14, label: "Run" },
46887
+ { id: "run-fast", action: 16, label: "Fast run" },
46888
+ { id: "wave", action: 28, label: "Big wave hello" },
46889
+ { id: "wave-one-hand", action: 290, label: "One-hand wave" },
46890
+ { id: "cheer", action: 59, label: "Victory cheer" },
46891
+ { id: "cheer-hands-up", action: 298, label: "Cheer with both hands up" },
46892
+ { id: "fist-pump", action: 403, label: "Victory fist pump" },
46893
+ { id: "talk", action: 313, label: "Talk with open hands" },
46894
+ { id: "talk-passionately", action: 308, label: "Talk passionately" },
46895
+ { id: "phone-call", action: 312, label: "Phone conversation" },
46896
+ { id: "agree", action: 25, label: "Agree gesture" },
46897
+ { id: "jump", action: 466, label: "Jump" },
46898
+ { id: "jump-arms-open", action: 460, label: "Jump with arms open" },
46899
+ { id: "dance", action: 22, label: "Funny dance" },
46900
+ { id: "dance-2", action: 64, label: "All-night dance" },
46901
+ { id: "shuffle", action: 63, label: "Arm-circle shuffle" }
46902
+ ];
46903
+ var MODEL_ANIMATION_IDS = MODEL_ANIMATIONS.map((a) => a.id);
46904
+ var DEFAULT_MODEL_ANIMATIONS = ["idle", "walk"];
46905
+ var animationAction = (clip) => typeof clip === "number" ? clip : MODEL_ANIMATIONS.find((a) => a.id === clip).action;
46906
+ var generationCostUsd = (p) => p.model === "hunyuan-3d-3.1-pro" ? 0.375 + (p.texture && p.pbr ? 0.15 : 0) + (p.faces !== void 0 ? 0.15 : 0) : p.texture ? 0.3 : 0.2;
46907
+ var rigCostUsd = (clips2) => 0.2 + 0.12 * clips2;
46908
+ var modelCostUsd = (p) => generationCostUsd(p) + (p.rig ? rigCostUsd(p.animations.length) : 0);
46909
+
46728
46910
  // shared/assets.ts
46729
- var ASSET_TYPES = ["image", "video", "music", "speech", "sfx"];
46911
+ var ASSET_TYPES = ["image", "vector", "video", "music", "speech", "sfx", "model"];
46730
46912
  var SPEECH_MODELS = [
46731
46913
  { id: "gemini-3.8-flash-tts", label: "Gemini 3.8 Flash TTS" },
46732
46914
  { id: "gemini-3.8-flash-lite-tts", label: "Gemini 3.8 Flash-Lite TTS" },
@@ -46979,6 +47161,86 @@ var imageAsset = external_exports.object({
46979
47161
  var imageChecks = (p, ctx) => {
46980
47162
  if (p.mask && !p.references.length) ctx.addIssue({ code: "custom", message: "A mask edits Image 1; add the image to edit as the first reference.", path: ["mask"] });
46981
47163
  };
47164
+ var VECTOR_MODELS = [{ id: "fal/ideogram-vector", label: "Ideogram Vector (fal)" }];
47165
+ var VECTOR_WORKFLOWS = ["text-to-vector", "image-to-vector"];
47166
+ var VECTOR_QUALITIES = ["low", "medium", "high"];
47167
+ var VECTOR_ASPECT_RATIOS = ["auto", "1:1", "1:4", "1:3", "1:2", "9:16", "10:16", "2:3", "3:4", "4:5", "5:4", "4:3", "3:2", "16:10", "16:9", "2:1", "3:1", "4:1"];
47168
+ var VECTOR_SHAPES = ["detailed", "simple"];
47169
+ var MAX_VECTOR_REFERENCES = 5;
47170
+ var VECTOR_REFERENCE_MAX_BYTES = 10 * 1024 * 1024;
47171
+ var VECTOR_SOURCE_MAX_BYTES = 50 * 1024 * 1024;
47172
+ var vectorAsset = external_exports.object({
47173
+ name: base.name,
47174
+ /** Image-to-vector takes no prompt. */
47175
+ prompt: external_exports.string().trim().max(4e3).default(""),
47176
+ type: external_exports.literal("vector"),
47177
+ model: external_exports.literal("fal/ideogram-vector").default("fal/ideogram-vector"),
47178
+ workflow: external_exports.enum(VECTOR_WORKFLOWS).default("text-to-vector"),
47179
+ /** Text-to-vector; defaults to medium. */
47180
+ quality: external_exports.enum(VECTOR_QUALITIES).optional(),
47181
+ /** Text-to-vector; defaults to auto. */
47182
+ aspectRatio: external_exports.enum(VECTOR_ASPECT_RATIOS).optional(),
47183
+ /** Text-to-vector: saved images or sprite art to follow, the first being the main one. */
47184
+ references: external_exports.array(blob).max(MAX_VECTOR_REFERENCES).default([]),
47185
+ /** Image-to-vector: the saved image or sprite art to trace. */
47186
+ source: blob.optional(),
47187
+ /** Image-to-vector; defaults to detailed. */
47188
+ shapes: external_exports.enum(VECTOR_SHAPES).optional(),
47189
+ /** Text-to-vector. */
47190
+ seed: external_exports.number().int().min(0).max(2 ** 32 - 1).optional()
47191
+ });
47192
+ function vectorChecks(p, ctx) {
47193
+ const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47194
+ if (new Set(p.references).size !== p.references.length) issue2("references", "Choose each reference image once.");
47195
+ if (p.workflow === "text-to-vector") {
47196
+ if (p.prompt.length < 3) issue2("prompt", "Describe the vector in at least 3 characters.");
47197
+ if (p.source) issue2("source", "Only image-to-vector traces a saved image; use references to guide text-to-vector.");
47198
+ if (p.shapes) issue2("shapes", "Only image-to-vector takes shapes.");
47199
+ return;
47200
+ }
47201
+ if (p.prompt) issue2("prompt", "image-to-vector takes no prompt; leave it out.");
47202
+ if (!p.source) issue2("source", "Choose a saved image or sprite to trace.");
47203
+ for (const field of ["quality", "aspectRatio", "seed"]) if (p[field] !== void 0) issue2(field, `image-to-vector doesn't take ${field === "aspectRatio" ? "an aspect ratio" : field === "seed" ? "a seed" : "a quality"}; the SVG follows the image.`);
47204
+ if (p.references.length) issue2("references", "image-to-vector traces its source alone and takes no references.");
47205
+ }
47206
+ var vectorDefaults = (p) => p.workflow === "text-to-vector" ? { ...p, quality: p.quality ?? "medium", aspectRatio: p.aspectRatio ?? "auto" } : { ...p, shapes: p.shapes ?? "detailed" };
47207
+ var animationClip = external_exports.union([external_exports.enum(MODEL_ANIMATION_IDS), external_exports.number().int().min(0).max(MAX_MESHY_ACTION)]);
47208
+ var modelAsset = external_exports.object({
47209
+ name: base.name,
47210
+ /** Describes the model for text-to-3D; with a reference image it is only a note. */
47211
+ prompt: external_exports.string().trim().max(4e3).default(""),
47212
+ type: external_exports.literal("model"),
47213
+ model: external_exports.enum(MODEL_GENERATOR_IDS).default(DEFAULT_MODEL_GENERATOR),
47214
+ /** A saved image or sprite art the model is built from. */
47215
+ reference: blob.optional(),
47216
+ /** Textured (default), or a plain white model. */
47217
+ texture: external_exports.boolean().default(true),
47218
+ /** Metallic, roughness and normal maps with the texture, so scene lights shape the surface. */
47219
+ pbr: external_exports.boolean().default(true),
47220
+ /** Target face count, within the generator's range; Hunyuan3D charges extra for it. */
47221
+ faces: external_exports.number().int().optional(),
47222
+ /** Auto-rig a humanoid character standing in a T- or A-pose. */
47223
+ rig: external_exports.boolean().default(false),
47224
+ /** Clips the rig gets (idle and walk when none are named). */
47225
+ animations: external_exports.array(animationClip).max(MAX_MODEL_CLIPS).default([]),
47226
+ /** The rigged character's height in metres (default 1.7). */
47227
+ height: external_exports.number().min(0.3).max(5).optional()
47228
+ });
47229
+ function modelChecks(p, ctx) {
47230
+ const issue2 = (path, message) => ctx.addIssue({ code: "custom", message, path: [path] });
47231
+ const generator = modelGenerator(p.model);
47232
+ if (!p.reference && p.prompt.length < 3) issue2("prompt", "Describe the model in at least 3 characters, or choose a saved image to build it from.");
47233
+ if (!p.reference && p.prompt.length > MAX_MODEL_PROMPT) issue2("prompt", `Describe the model in at most ${MAX_MODEL_PROMPT.toLocaleString("en-US")} characters.`);
47234
+ if (p.faces !== void 0 && (p.faces < generator.faces.min || p.faces > generator.faces.max)) issue2("faces", `${generator.label} makes ${generator.faces.min.toLocaleString("en-US")}\u2013${generator.faces.max.toLocaleString("en-US")} faces.`);
47235
+ if (!p.rig && p.animations.length) issue2("animations", "Animation clips need rig: true.");
47236
+ if (!p.rig && p.height !== void 0) issue2("height", "height is the rigged character's height; it needs rig: true.");
47237
+ if (!p.rig) return;
47238
+ if (p.faces === void 0 && p.model === "hunyuan-3d-3.1-pro") issue2("faces", `Rigging takes at most ${MAX_RIG_FACES.toLocaleString("en-US")} faces, and Hunyuan3D makes 500,000 unless you set faces.`);
47239
+ else if (p.faces !== void 0 && p.faces > MAX_RIG_FACES) issue2("faces", `Rigging takes at most ${MAX_RIG_FACES.toLocaleString("en-US")} faces.`);
47240
+ const actions2 = p.animations.map(animationAction);
47241
+ if (new Set(actions2).size !== actions2.length) issue2("animations", "Choose each clip once.");
47242
+ }
47243
+ var modelDefaults = (p) => ({ ...p, animations: p.rig && !p.animations.length ? [...DEFAULT_MODEL_ANIMATIONS] : p.animations });
46982
47244
  var assetRequest = external_exports.discriminatedUnion("type", [
46983
47245
  imageAsset.superRefine(imageChecks),
46984
47246
  videoAsset.superRefine(videoChecks).transform(videoDefaults),
@@ -46987,7 +47249,9 @@ var assetRequest = external_exports.discriminatedUnion("type", [
46987
47249
  if (p.voice && (isElevenLabsSpeech(p.model) ? !/^[a-zA-Z0-9]{20}$/.test(p.voice) : !SPEECH_VOICES.includes(p.voice))) ctx.addIssue({ code: "custom", message: "Choose a voice supported by the speech provider.", path: ["voice"] });
46988
47250
  if (isElevenLabsSpeech(p.model) && p.delivery) ctx.addIssue({ code: "custom", message: "ElevenLabs uses audio tags in the transcript; separate delivery instructions are only supported by Gemini.", path: ["delivery"] });
46989
47251
  }).transform((p) => ({ ...p, voice: p.voice || (isElevenLabsSpeech(p.model) ? DEFAULT_ELEVENLABS_VOICE : "Kore") })),
46990
- 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) })
47252
+ 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) }),
47253
+ vectorAsset.superRefine(vectorChecks).transform(vectorDefaults),
47254
+ modelAsset.superRefine(modelChecks).transform(modelDefaults)
46991
47255
  ]);
46992
47256
  var assetGenerationRequest = external_exports.discriminatedUnion("type", [imageAsset.omit({ model: true }).superRefine(imageChecks), ...assetRequest.options.slice(1)]);
46993
47257
  var LIBRARY_AUDIO_MODEL = "library-audio";
@@ -46997,17 +47261,21 @@ var uploadedMedia = external_exports.object({ name: base.name, prompt: external_
46997
47261
  var savedAssetOptions = external_exports.union([assetRequest, libraryAudio, uploadedMedia]);
46998
47262
  var speechCharacters = (text2) => Array.from(text2).length;
46999
47263
  var imageEstimate = (p) => Math.ceil((130 + 40 * p.references.length) * spriteImageModel(p.model).relCost);
47264
+ var vectorEstimate = (p) => p.workflow === "image-to-vector" ? 100 : p.quality === "low" ? 130 : p.quality === "high" ? p.references.length ? 320 : 200 : 160;
47000
47265
  function videoExtras(p, facts = {}) {
47001
47266
  const w = p.workflow;
47002
47267
  if (w !== "reference-to-video" && w !== "insert-video" && w !== "3d-to-video") return {};
47003
47268
  const images = (p.references ?? []).reduce((sum, sha2) => sum + imageReferenceTokens(facts.size?.(sha2)), 0);
47004
47269
  if (w === "3d-to-video") return { referenceTokens: images + clipReferenceTokens(p.seconds, p.resolution), generatedReferences: p.references?.length ? 0 : p.generatedReferences ?? DEFAULT_GENERATED_REFERENCES };
47005
- const clips = Math.min(15, (p.referenceVideos ?? []).reduce((sum, sha2) => sum + Math.min(p.seconds, facts.seconds?.(sha2) ?? p.seconds), 0));
47270
+ const clips2 = Math.min(15, (p.referenceVideos ?? []).reduce((sum, sha2) => sum + Math.min(p.seconds, facts.seconds?.(sha2) ?? p.seconds), 0));
47006
47271
  const audio = Math.min(15, p.audioReferences.reduce((sum, sha2) => sum + (facts.seconds?.(sha2) ?? 15), 0));
47007
- return { referenceTokens: images + clipReferenceTokens(clips, p.resolution) + (w === "reference-to-video" ? audioReferenceTokens(audio) : 0) };
47272
+ return { referenceTokens: images + clipReferenceTokens(clips2, p.resolution) + (w === "reference-to-video" ? audioReferenceTokens(audio) : 0) };
47008
47273
  }
47274
+ var modelEstimate = (p) => Math.max(1, Math.ceil(modelCostUsd(p) / 1e-3));
47009
47275
  function assetEstimate(p, now = Date.now()) {
47010
47276
  if (p.type === "image") return imageEstimate(p);
47277
+ if (p.type === "vector") return vectorEstimate(p);
47278
+ if (p.type === "model") return modelEstimate(p);
47011
47279
  if (p.type === "video") return spriteVideoEstimate(spriteVideoModel(p.model), p, videoExtras(p));
47012
47280
  if (p.type === "music") return 80;
47013
47281
  const units = (credits) => Math.max(1, Math.ceil(credits * UNITS_PER_CREDIT));
@@ -47024,8 +47292,8 @@ function assetEstimate(p, now = Date.now()) {
47024
47292
  }
47025
47293
 
47026
47294
  // cli/src/videos.ts
47027
- import { open as open4, readFile as readFile8, stat as stat3 } from "node:fs/promises";
47028
- import { basename as basename4, join as join9, resolve as resolve9 } from "node:path";
47295
+ import { open as open4, readFile as readFile9, stat as stat4 } from "node:fs/promises";
47296
+ import { basename as basename4, join as join10, resolve as resolve10 } from "node:path";
47029
47297
  import { setTimeout as delay2 } from "node:timers/promises";
47030
47298
  import { spawn as spawn2 } from "node:child_process";
47031
47299
  import { createHash as createHash4 } from "node:crypto";
@@ -47121,12 +47389,12 @@ function parseSubtitles(input2, duration3) {
47121
47389
  }
47122
47390
 
47123
47391
  // cli/src/export-files.ts
47124
- import { open as open2, readFile as readFile5, stat } from "node:fs/promises";
47125
- import { basename as basename3, resolve as resolve6 } from "node:path";
47392
+ import { open as open2, readFile as readFile6, stat as stat2 } from "node:fs/promises";
47393
+ import { basename as basename3, resolve as resolve7 } from "node:path";
47126
47394
  import { createHash as createHash2 } from "node:crypto";
47127
47395
  async function exportReceipt(file2) {
47128
47396
  try {
47129
- const receipt = JSON.parse(await readFile5(file2.replace(/\.mp4$/i, ".json"), "utf8"));
47397
+ const receipt = JSON.parse(await readFile6(file2.replace(/\.mp4$/i, ".json"), "utf8"));
47130
47398
  if (receipt.file !== basename3(file2)) return {};
47131
47399
  return { title: typeof receipt.title === "string" ? receipt.title : void 0, language: typeof receipt.settings?.language === "string" ? receipt.settings.language : void 0 };
47132
47400
  } catch {
@@ -47136,22 +47404,22 @@ async function exportReceipt(file2) {
47136
47404
  async function readSubtitleFile(file2, maxBytes) {
47137
47405
  let info;
47138
47406
  try {
47139
- info = await stat(file2);
47407
+ info = await stat2(file2);
47140
47408
  } catch {
47141
47409
  throw new Error(`Cannot read the subtitle file ${file2}.`);
47142
47410
  }
47143
47411
  if (!info.isFile() || !/\.(?:srt|vtt)$/i.test(file2) || info.size < 1 || info.size > maxBytes) throw new Error(`Use a .srt or .vtt subtitle file up to ${maxBytes / 1024 / 1024} MiB, such as the one npm run export:mp4 writes beside the MP4.`);
47144
- const content = await readFile5(file2);
47412
+ const content = await readFile6(file2);
47145
47413
  const text2 = content.toString("utf8");
47146
47414
  if (text2.includes("\uFFFD") || !/\d\d:\d\d[,.]\d{3} --> /.test(text2)) throw new Error("Use a UTF-8 .srt or .vtt subtitle file with timed captions.");
47147
47415
  return { content, sha256: createHash2("sha256").update(content).digest("hex"), language: /^\uFEFF?WEBVTT[^\n]*\n(?:[^\n]+\n)*?Language:[ \t]*([^\s]+)/.exec(text2)?.[1] };
47148
47416
  }
47149
47417
  async function subtitleFile(root, mp4, choice, extensions) {
47150
47418
  if (choice === "none") return null;
47151
- if (choice !== "auto") return { file: resolve6(root, choice), explicit: true };
47419
+ if (choice !== "auto") return { file: resolve7(root, choice), explicit: true };
47152
47420
  for (const extension of extensions) {
47153
47421
  const file2 = mp4.replace(/\.mp4$/i, extension);
47154
- if (await stat(file2).then((info) => info.isFile(), () => false)) return { file: file2, explicit: false };
47422
+ if (await stat2(file2).then((info) => info.isFile(), () => false)) return { file: file2, explicit: false };
47155
47423
  }
47156
47424
  return null;
47157
47425
  }
@@ -47226,10 +47494,10 @@ async function mp4Info(file2) {
47226
47494
  }
47227
47495
 
47228
47496
  // cli/src/local-project.ts
47229
- import { readFile as readFile6, readdir as readdir3 } from "node:fs/promises";
47230
- import { join as join7, relative, resolve as resolve7, sep } from "node:path";
47497
+ import { readFile as readFile7, readdir as readdir4 } from "node:fs/promises";
47498
+ import { join as join8, relative, resolve as resolve8, sep } from "node:path";
47231
47499
  async function localProject(directory, selected) {
47232
- const root = resolve7(directory);
47500
+ const root = resolve8(directory);
47233
47501
  let workspace3;
47234
47502
  try {
47235
47503
  workspace3 = await findWorkspace(root);
@@ -47241,10 +47509,10 @@ async function localProject(directory, selected) {
47241
47509
  let animation = selected ?? (location[0] === "animations" ? location[1] : void 0);
47242
47510
  if (!animation) {
47243
47511
  const names = [];
47244
- for (const entry of await readdir3(join7(workspace3, "animations"), { withFileTypes: true })) {
47512
+ for (const entry of await readdir4(join8(workspace3, "animations"), { withFileTypes: true })) {
47245
47513
  if (!entry.isDirectory()) continue;
47246
47514
  try {
47247
- await readFile6(join7(workspace3, "animations", entry.name, "animaguru.project.json"));
47515
+ await readFile7(join8(workspace3, "animations", entry.name, "animaguru.project.json"));
47248
47516
  names.push(entry.name);
47249
47517
  } catch (error62) {
47250
47518
  if (error62.code !== "ENOENT") throw error62;
@@ -47254,17 +47522,17 @@ async function localProject(directory, selected) {
47254
47522
  animation = names[0];
47255
47523
  }
47256
47524
  if (!/^[a-z0-9][a-z0-9-]{0,79}$/.test(animation)) throw new Error("Use a lowercase, hyphenated animation name.");
47257
- const projectRoot = join7(workspace3, "animations", animation);
47258
- await readFile6(join7(projectRoot, "animaguru.project.json"));
47525
+ const projectRoot = join8(workspace3, "animations", animation);
47526
+ await readFile7(join8(projectRoot, "animaguru.project.json"));
47259
47527
  return { toolsRoot: workspace3, projectRoot, animation };
47260
47528
  }
47261
47529
 
47262
47530
  // cli/src/cloud-sync.ts
47263
47531
  var import_isomorphic_git = __toESM(require_isomorphic_git(), 1);
47264
47532
  import * as fs from "node:fs";
47265
- import { chmod as chmod3, lstat, mkdir as mkdir7, open as open3, readFile as readFile7, readdir as readdir4, rename as rename4, rm as rm2, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
47533
+ import { chmod as chmod3, lstat, mkdir as mkdir8, open as open3, readFile as readFile8, readdir as readdir5, rename as rename4, rm as rm2, stat as stat3, writeFile as writeFile7 } from "node:fs/promises";
47266
47534
  import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
47267
- import { dirname as dirname6, join as join8, resolve as resolve8 } from "node:path";
47535
+ import { dirname as dirname7, join as join9, resolve as resolve9 } from "node:path";
47268
47536
  import { Readable } from "node:stream";
47269
47537
  import { pipeline } from "node:stream/promises";
47270
47538
  import { setTimeout as delay } from "node:timers/promises";
@@ -47483,37 +47751,37 @@ var list = (paths, limit2 = 200) => paths.length > limit2 ? [...paths.slice(0, l
47483
47751
  var short = (oid) => oid.slice(0, 12);
47484
47752
  var executable = (mode) => (mode & 73) !== 0;
47485
47753
  async function cloudRootFor(start) {
47486
- let directory = resolve8(start);
47754
+ let directory = resolve9(start);
47487
47755
  while (true) {
47488
- if (await exists(join8(directory, LINK_FILE)) || await exists(join8(directory, "animaguru.workspace.json"))) return directory;
47756
+ if (await exists(join9(directory, LINK_FILE)) || await exists(join9(directory, "animaguru.workspace.json"))) return directory;
47489
47757
  try {
47490
- const project = JSON.parse(await readFile7(join8(directory, "animaguru.project.json"), "utf8"));
47758
+ const project = JSON.parse(await readFile8(join9(directory, "animaguru.project.json"), "utf8"));
47491
47759
  if (!project.workspace) return directory;
47492
47760
  } catch (error62) {
47493
47761
  if (error62.code !== "ENOENT" && !(error62 instanceof SyntaxError)) throw error62;
47494
47762
  }
47495
- const parent = dirname6(directory);
47763
+ const parent = dirname7(directory);
47496
47764
  if (parent === directory) throw new Error("Run this inside an AnimaGuru video workspace or project, or pass --dir. Create one with animaguru create workspace NAME.");
47497
47765
  directory = parent;
47498
47766
  }
47499
47767
  }
47500
47768
  async function readJson2(path) {
47501
47769
  try {
47502
- return JSON.parse(await readFile7(path, "utf8"));
47770
+ return JSON.parse(await readFile8(path, "utf8"));
47503
47771
  } catch (error62) {
47504
47772
  if (error62.code === "ENOENT") return null;
47505
47773
  throw error62;
47506
47774
  }
47507
47775
  }
47508
47776
  async function writeJson2(path, value) {
47509
- await mkdir7(dirname6(path), { recursive: true });
47777
+ await mkdir8(dirname7(path), { recursive: true });
47510
47778
  const temp = `${path}.${randomUUID2()}.tmp`;
47511
47779
  await writeFile7(temp, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
47512
47780
  await rename4(temp, path);
47513
47781
  }
47514
47782
  async function withLock(root, run) {
47515
- const path = join8(root, SYNC_DIR, "lock");
47516
- await mkdir7(dirname6(path), { recursive: true });
47783
+ const path = join9(root, SYNC_DIR, "lock");
47784
+ await mkdir8(dirname7(path), { recursive: true });
47517
47785
  for (let attempt = 0; ; attempt++) {
47518
47786
  try {
47519
47787
  await writeFile7(path, JSON.stringify({ pid: process.pid, at: Date.now() }), { flag: "wx" });
@@ -47541,27 +47809,27 @@ async function withLock(root, run) {
47541
47809
  }
47542
47810
  async function openContext(dir, options, url3) {
47543
47811
  const root = await cloudRootFor(dir);
47544
- const link2 = await readJson2(join8(root, LINK_FILE));
47812
+ const link2 = await readJson2(join9(root, LINK_FILE));
47545
47813
  if (!link2) throw new Error("This folder is not linked to a project. Run animaguru cloud link, or animaguru cloud clone PROJECT_ID.");
47546
47814
  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.`);
47547
- const state = await readJson2(join8(root, STATE_FILE)) ?? { schemaVersion: 1, base: null, files: {} };
47548
- return { root, gitdir: join8(root, GIT_DIR), link: link2, state, client: new Client(url3 ?? link2.origin, options.signal), options };
47815
+ const state = await readJson2(join9(root, STATE_FILE)) ?? { schemaVersion: 1, base: null, files: {} };
47816
+ return { root, gitdir: join9(root, GIT_DIR), link: link2, state, client: new Client(url3 ?? link2.origin, options.signal), options };
47549
47817
  }
47550
47818
  async function cloudLink(dir) {
47551
- const link2 = await readJson2(join8(await cloudRootFor(dir), LINK_FILE));
47819
+ const link2 = await readJson2(join9(await cloudRootFor(dir), LINK_FILE));
47552
47820
  if (!link2) throw new Error("This folder is not linked to a project. Pass a project ID.");
47553
47821
  return link2;
47554
47822
  }
47555
- var saveState = (context) => writeJson2(join8(context.root, STATE_FILE), context.state);
47823
+ var saveState = (context) => writeJson2(join9(context.root, STATE_FILE), context.state);
47556
47824
  async function scanFiles(root) {
47557
47825
  const files = /* @__PURE__ */ new Map();
47558
47826
  const skipped = [];
47559
47827
  const tooLarge = [];
47560
47828
  async function walk(directory, prefix, rules) {
47561
- const entries = (await readdir4(directory, { withFileTypes: true })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
47829
+ const entries = (await readdir5(directory, { withFileTypes: true })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
47562
47830
  const scoped = [...rules];
47563
47831
  for (const name2 of [".gitignore", ".animaguruignore"]) {
47564
- if (entries.some((entry) => entry.name === name2 && entry.isFile())) scoped.push({ base: prefix, rules: (0, import_ignore.default)().add(await readFile7(join8(directory, name2), "utf8")) });
47832
+ if (entries.some((entry) => entry.name === name2 && entry.isFile())) scoped.push({ base: prefix, rules: (0, import_ignore.default)().add(await readFile8(join9(directory, name2), "utf8")) });
47565
47833
  }
47566
47834
  for (const entry of entries) {
47567
47835
  const path = prefix ? `${prefix}/${entry.name}` : entry.name;
@@ -47572,7 +47840,7 @@ async function scanFiles(root) {
47572
47840
  continue;
47573
47841
  }
47574
47842
  if (entry.isDirectory()) {
47575
- await walk(join8(directory, entry.name), path, scoped);
47843
+ await walk(join9(directory, entry.name), path, scoped);
47576
47844
  continue;
47577
47845
  }
47578
47846
  if (!entry.isFile()) continue;
@@ -47583,7 +47851,7 @@ async function scanFiles(root) {
47583
47851
  skipped.push(`${path} (unsupported name)`);
47584
47852
  continue;
47585
47853
  }
47586
- const info = await stat2(join8(directory, entry.name));
47854
+ const info = await stat3(join9(directory, entry.name));
47587
47855
  if (info.size > CLOUD_OBJECT_MAX_BYTES) {
47588
47856
  skipped.push(`${path} (larger than ${CLOUD_OBJECT_MAX_BYTES / 1024 ** 3} GB)`);
47589
47857
  continue;
@@ -47592,7 +47860,7 @@ async function scanFiles(root) {
47592
47860
  tooLarge.push(safe);
47593
47861
  continue;
47594
47862
  }
47595
- files.set(safe, { path: safe, absolute: join8(directory, entry.name), size: info.size, mtimeMs: info.mtimeMs, mode: executable(info.mode) ? "100755" : "100644" });
47863
+ files.set(safe, { path: safe, absolute: join9(directory, entry.name), size: info.size, mtimeMs: info.mtimeMs, mode: executable(info.mode) ? "100755" : "100644" });
47596
47864
  if (files.size > CLOUD_MAX_FILES) throw new Error(`Cloud sync supports up to ${CLOUD_MAX_FILES} files. Add generated folders to .animaguruignore.`);
47597
47865
  }
47598
47866
  }
@@ -47624,7 +47892,7 @@ async function describeFiles(context, scanned) {
47624
47892
  const sha256 = await fileSha256(file2.absolute);
47625
47893
  local.set(file2.path, { oid: (await import_isomorphic_git.default.hashBlob({ object: cloudPointer(sha256, file2.size) })).oid, mode: file2.mode, size: file2.size, mtimeMs: file2.mtimeMs, sha256 });
47626
47894
  } else {
47627
- local.set(file2.path, { oid: (await import_isomorphic_git.default.hashBlob({ object: await readFile7(file2.absolute) })).oid, mode: file2.mode, size: file2.size, mtimeMs: file2.mtimeMs });
47895
+ local.set(file2.path, { oid: (await import_isomorphic_git.default.hashBlob({ object: await readFile8(file2.absolute) })).oid, mode: file2.mode, size: file2.size, mtimeMs: file2.mtimeMs });
47628
47896
  }
47629
47897
  }
47630
47898
  return local;
@@ -47688,14 +47956,14 @@ ${entries.join(",\n")}
47688
47956
  }}
47689
47957
  `;
47690
47958
  }
47691
- async function token(context, access3) {
47692
- const result = await context.client.request(`/api/v1/cloud/${context.link.projectId}/token`, { method: "POST", body: { access: access3 } });
47959
+ async function token(context, access4) {
47960
+ const result = await context.client.request(`/api/v1/cloud/${context.link.projectId}/token`, { method: "POST", body: { access: access4 } });
47693
47961
  const remote = new URL(result.remote);
47694
47962
  if (remote.protocol !== "https:" && !(remote.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(remote.hostname))) throw new Error("The server returned an unsafe cloud remote.");
47695
47963
  return { ...result, onAuth: () => ({ username: "x", password: result.token.split("?expires=")[0] }) };
47696
47964
  }
47697
47965
  async function ensureRepo(gitdir) {
47698
- if (await exists(join8(gitdir, "HEAD"))) return;
47966
+ if (await exists(join9(gitdir, "HEAD"))) return;
47699
47967
  await import_isomorphic_git.default.init({ fs, gitdir, bare: true, defaultBranch: CLOUD_BRANCH });
47700
47968
  await import_isomorphic_git.default.setConfig({ fs, gitdir, path: "remote.origin.fetch", value: `+refs/heads/${CLOUD_BRANCH}:refs/remotes/origin/${CLOUD_BRANCH}` });
47701
47969
  }
@@ -47761,12 +48029,12 @@ async function target(root, path) {
47761
48029
  if (builtIn.ignores(path)) throw new Error(`Refusing to write ${path}: cloud sync never writes ignored or secret files.`);
47762
48030
  let current = root;
47763
48031
  for (const part of parts.slice(0, -1)) {
47764
- current = join8(current, part);
48032
+ current = join9(current, part);
47765
48033
  const info2 = await lstat(current).catch(() => null);
47766
- if (!info2) await mkdir7(current);
48034
+ if (!info2) await mkdir8(current);
47767
48035
  else if (info2.isSymbolicLink() || !info2.isDirectory()) throw new Error(`Cannot write ${path}: ${part} is not a folder.`);
47768
48036
  }
47769
- const file2 = join8(current, parts.at(-1));
48037
+ const file2 = join9(current, parts.at(-1));
47770
48038
  const info = await lstat(file2).catch(() => null);
47771
48039
  if (info && !info.isFile()) throw new Error(`Cannot replace ${path}: it is a link or folder.`);
47772
48040
  return file2;
@@ -47792,7 +48060,7 @@ async function materialize(context, path, entry) {
47792
48060
  } else await writeFile7(temp, blob2, { mode: 420 });
47793
48061
  await chmod3(temp, entry.mode === "100755" ? 493 : 420);
47794
48062
  await rename4(temp, file2);
47795
- const info = await stat2(file2);
48063
+ const info = await stat3(file2);
47796
48064
  return { ...entry, size: info.size, mtimeMs: info.mtimeMs, ...pointer ? { sha256: pointer.sha256 } : {} };
47797
48065
  } catch (error62) {
47798
48066
  await rm2(temp, { force: true });
@@ -47800,10 +48068,10 @@ async function materialize(context, path, entry) {
47800
48068
  }
47801
48069
  }
47802
48070
  async function backup(context, folder, path) {
47803
- const source = join8(context.root, path);
48071
+ const source = join9(context.root, path);
47804
48072
  if (!await exists(source)) return;
47805
- const destination = join8(context.root, SYNC_DIR, "backups", folder, path);
47806
- await mkdir7(dirname6(destination), { recursive: true });
48073
+ const destination = join9(context.root, SYNC_DIR, "backups", folder, path);
48074
+ await mkdir8(dirname7(destination), { recursive: true });
47807
48075
  await fs.promises.copyFile(source, destination);
47808
48076
  }
47809
48077
  async function remove(context, path) {
@@ -47823,24 +48091,24 @@ async function apply(context, local, writes, deletes) {
47823
48091
  }
47824
48092
  async function linkCloud(client, options) {
47825
48093
  const root = await cloudRootFor(options.dir);
47826
- if (await exists(join8(root, LINK_FILE))) throw new Error(`This folder is already linked. Run animaguru cloud status, or animaguru cloud unlink first.`);
48094
+ if (await exists(join9(root, LINK_FILE))) throw new Error(`This folder is already linked. Run animaguru cloud status, or animaguru cloud unlink first.`);
47827
48095
  const id2 = options.project ?? (await client.request("/api/v1/sprites", { method: "POST", body: { owner: options.workspace, name: (options.name ?? root.split(/[\\/]/).pop() ?? "Animations").slice(0, 80) } })).project.id;
47828
48096
  const { project } = await client.request("/api/v1/cloud", { method: "POST", body: { project: id2 } });
47829
- await writeJson2(join8(root, LINK_FILE), { schemaVersion: 1, origin: client.origin, projectId: project.id, name: project.name, workspace: project.workspaceId });
48097
+ await writeJson2(join9(root, LINK_FILE), { schemaVersion: 1, origin: client.origin, projectId: project.id, name: project.name, workspace: project.workspaceId });
47830
48098
  await keepOutOfGit(root);
47831
48099
  return { project, path: root, nextSteps: project.head ? ["Run animaguru cloud pull to combine the cloud versions with this folder."] : ['Run animaguru cloud push -m "First version" to save the first version.'] };
47832
48100
  }
47833
48101
  async function keepOutOfGit(root) {
47834
- const path = join8(root, ".gitignore");
47835
- const text2 = await readFile7(path, "utf8").catch(() => null);
48102
+ const path = join9(root, ".gitignore");
48103
+ const text2 = await readFile8(path, "utf8").catch(() => null);
47836
48104
  if (text2 !== null && !/^\/?\.animaguru\/(?:sync\/?)?$/m.test(text2)) await writeFile7(path, `${text2}${text2.endsWith("\n") || !text2 ? "" : "\n"}.animaguru/sync/
47837
48105
  `);
47838
48106
  }
47839
48107
  async function unlinkCloud(options) {
47840
48108
  const root = await cloudRootFor(options.dir);
47841
- if (!await exists(join8(root, LINK_FILE))) throw new Error("This folder is not linked to a project.");
48109
+ if (!await exists(join9(root, LINK_FILE))) throw new Error("This folder is not linked to a project.");
47842
48110
  await withLock(root, async () => {
47843
- for (const name2 of ["link.json", "state.json", "repo.git"]) await rm2(join8(root, SYNC_DIR, name2), { recursive: true, force: true });
48111
+ for (const name2 of ["link.json", "state.json", "repo.git"]) await rm2(join9(root, SYNC_DIR, name2), { recursive: true, force: true });
47844
48112
  });
47845
48113
  return { unlinked: true, path: root, note: "The project's animations and versions were not changed." };
47846
48114
  }
@@ -47890,13 +48158,13 @@ async function pushCloud(options) {
47890
48158
  if (!local.size) throw new Error("There are no files to save.");
47891
48159
  for (const [path, entry] of local) {
47892
48160
  if (base2.get(path)?.oid === entry.oid) continue;
47893
- const bytes = entry.sha256 ? new TextEncoder().encode(cloudPointer(entry.sha256, entry.size)) : await readFile7(join8(context.root, path));
48161
+ const bytes = entry.sha256 ? new TextEncoder().encode(cloudPointer(entry.sha256, entry.size)) : await readFile8(join9(context.root, path));
47894
48162
  if (await import_isomorphic_git.default.writeBlob({ fs, gitdir: context.gitdir, blob: bytes }) !== entry.oid) throw new Error(`${path} changed while saving. Run the push again.`);
47895
48163
  }
47896
- const uploaded = await uploadObjects(context, local, (path) => join8(context.root, path));
48164
+ const uploaded = await uploadObjects(context, local, (path) => join9(context.root, path));
47897
48165
  for (const [path, entry] of local) {
47898
48166
  if (!entry.sha256 || base2.get(path)?.oid === entry.oid) continue;
47899
- const info = await stat2(join8(context.root, path));
48167
+ const info = await stat3(join9(context.root, path));
47900
48168
  if (info.size !== entry.size || info.mtimeMs !== entry.mtimeMs) throw new Error(`${path} changed while saving. Run the push again.`);
47901
48169
  }
47902
48170
  const tree = await writeTree(context.gitdir, local, manifestText(local));
@@ -47971,10 +48239,10 @@ async function pullCloud(options) {
47971
48239
  async function cloneCloud(client, options) {
47972
48240
  const { project } = await client.request(`/api/v1/cloud/${encodeURIComponent(options.id)}`);
47973
48241
  if (!project) throw new Error("This project has no synced animations yet. Link a folder with animaguru cloud link --project ID, then push.");
47974
- const root = resolve8(options.dir ?? (project.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || project.id));
47975
- if (await exists(root) && (await readdir4(root)).length) throw new Error(`Destination is not empty: ${root}. Choose a new folder; existing files were not changed.`);
47976
- await mkdir7(root, { recursive: true });
47977
- await writeJson2(join8(root, LINK_FILE), { schemaVersion: 1, origin: client.origin, projectId: project.id, name: project.name, workspace: project.workspaceId });
48242
+ const root = resolve9(options.dir ?? (project.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || project.id));
48243
+ if (await exists(root) && (await readdir5(root)).length) throw new Error(`Destination is not empty: ${root}. Choose a new folder; existing files were not changed.`);
48244
+ await mkdir8(root, { recursive: true });
48245
+ await writeJson2(join9(root, LINK_FILE), { schemaVersion: 1, origin: client.origin, projectId: project.id, name: project.name, workspace: project.workspaceId });
47978
48246
  const pulled = await pullCloud({ ...options, dir: root });
47979
48247
  return { project, path: root, version: pulled.version, files: "written" in pulled ? pulled.written : 0, nextSteps: ["Run npm install in the folder before previewing or exporting."] };
47980
48248
  }
@@ -48024,8 +48292,8 @@ function sharingEmails(value) {
48024
48292
  return value ? value.split(",").map((email3) => email3.trim()).filter(Boolean) : [];
48025
48293
  }
48026
48294
  async function exportHtml(root, animation, signal, log) {
48027
- const script = join9(root, "scripts/export-html.mjs");
48028
- await stat3(script).catch(() => {
48295
+ const script = join10(root, "scripts/export-html.mjs");
48296
+ await stat4(script).catch(() => {
48029
48297
  throw new Error("Choose an AnimaGuru video project, or pass --file for an exported HTML5 ZIP or MP4.");
48030
48298
  });
48031
48299
  log?.("Building the portable HTML5 animation\u2026");
@@ -48058,23 +48326,23 @@ async function exportHtml(root, animation, signal, log) {
48058
48326
  async function submitVideo(client, options) {
48059
48327
  if (!options.yes && !options["dry-run"]) throw new Error("Pass --yes to authorize uploading and sharing this animation, or --dry-run to inspect the submission.");
48060
48328
  const workspace3 = options.workspace === void 0 ? void 0 : libraryWorkspace(options.workspace);
48061
- const root = resolve9(options.directory ?? ".");
48329
+ const root = resolve10(options.directory ?? ".");
48062
48330
  const context = await localProject(root, options.animation);
48063
- const file2 = options.file ? resolve9(root, options.file) : await exportHtml(context.toolsRoot, context.animation, options.signal, options.log);
48064
- const info = await stat3(file2);
48331
+ const file2 = options.file ? resolve10(root, options.file) : await exportHtml(context.toolsRoot, context.animation, options.signal, options.log);
48332
+ const info = await stat4(file2);
48065
48333
  const format = file2.toLowerCase().endsWith(".zip") ? "html5" : file2.toLowerCase().endsWith(".mp4") ? "mp4" : null;
48066
48334
  if (!info.isFile() || !format) throw new Error("Submit an AnimaGuru HTML5 .zip export or an .mp4 file.");
48067
48335
  videoSubmission.parse({ title: options.title ?? "Video", format, bytes: info.size });
48068
48336
  let metadata = {};
48069
48337
  let receipt = {};
48070
48338
  if (format === "html5") {
48071
- const files = unzipSync(new Uint8Array(await readFile8(file2)), { filter: (entry) => entry.name === "manifest.json" && entry.originalSize <= 256 * 1024 });
48339
+ const files = unzipSync(new Uint8Array(await readFile9(file2)), { filter: (entry) => entry.name === "manifest.json" && entry.originalSize <= 256 * 1024 });
48072
48340
  if (!files["manifest.json"]) throw new Error("Use an AnimaGuru HTML5 export with manifest.json.");
48073
48341
  metadata = JSON.parse(strFromU8(files["manifest.json"]));
48074
48342
  if (metadata.format !== "spriteguru-html5" || metadata.version !== 1) throw new Error("Use an AnimaGuru HTML5 export.");
48075
48343
  } else {
48076
48344
  try {
48077
- metadata = JSON.parse(await readFile8(join9(context.projectRoot, "film.config.json"), "utf8"));
48345
+ metadata = JSON.parse(await readFile9(join10(context.projectRoot, "film.config.json"), "utf8"));
48078
48346
  } catch {
48079
48347
  }
48080
48348
  const handle2 = await open4(file2, "r");
@@ -48117,10 +48385,10 @@ async function submitVideo(client, options) {
48117
48385
  }
48118
48386
  let poster;
48119
48387
  if (options.poster) {
48120
- const path = resolve9(root, options.poster);
48121
- const info2 = await stat3(path);
48388
+ const path = resolve10(root, options.poster);
48389
+ const info2 = await stat4(path);
48122
48390
  if (!info2.isFile() || info2.size > 2 * 1024 * 1024) throw new Error("Poster must be a PNG, JPEG or WebP up to 2 MiB.");
48123
- poster = await readFile8(path);
48391
+ poster = await readFile9(path);
48124
48392
  videoPosterType(poster);
48125
48393
  }
48126
48394
  if (options["dry-run"]) return { dryRun: true, file: file2, submission: body, poster: options.poster ?? null, subtitles: subtitles && { file: subtitles.file, language: subtitles.language } };
@@ -48170,7 +48438,7 @@ async function changeVideoSubtitles(client, id2, options) {
48170
48438
  return client.request(path + encodeURIComponent(options.remove), { method: "DELETE" });
48171
48439
  }
48172
48440
  if (!options.file) throw new Error("Pass --file with a .srt or .vtt subtitle file, or --remove LANGUAGE.");
48173
- const file2 = resolve9(options.file);
48441
+ const file2 = resolve10(options.file);
48174
48442
  const { content, language: header } = await readSubtitleFile(file2, SUBTITLE_MAX_BYTES);
48175
48443
  const language = subtitleLanguage.safeParse(options.language ?? header ?? (await exportReceipt(file2.replace(/\.(?:srt|vtt)$/i, ".mp4"))).language);
48176
48444
  if (!language.success) throw new Error("Pass --language with the subtitles' language code, such as en or tr.");
@@ -48204,8 +48472,8 @@ async function moveVideos(client, ids, options) {
48204
48472
  }
48205
48473
 
48206
48474
  // cli/src/assets.ts
48207
- import { mkdir as mkdir8, readFile as readFile9, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
48208
- import { dirname as dirname7, join as join10, resolve as resolve10 } from "node:path";
48475
+ import { mkdir as mkdir9, readFile as readFile10, stat as stat5, writeFile as writeFile8 } from "node:fs/promises";
48476
+ import { dirname as dirname8, join as join11, resolve as resolve11 } from "node:path";
48209
48477
  import { setTimeout as delay3 } from "node:timers/promises";
48210
48478
  var extras = {
48211
48479
  image: ["aspect-ratio", "transparent", "references", "mask"],
@@ -48242,7 +48510,9 @@ var extras = {
48242
48510
  ],
48243
48511
  music: [],
48244
48512
  speech: ["voice", "delivery"],
48245
- sfx: ["seconds", "loop", "prompt-influence"]
48513
+ sfx: ["seconds", "loop", "prompt-influence"],
48514
+ model: ["reference", "texture", "pbr", "faces", "rig", "animations", "height"],
48515
+ vector: ["workflow", "references", "source", "aspect-ratio", "quality", "shapes", "seed"]
48246
48516
  };
48247
48517
  var list2 = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
48248
48518
  function accepted(type) {
@@ -48263,6 +48533,7 @@ var regions = (value) => value?.split(";").filter((region) => region.trim()).map
48263
48533
  if (rest.length || [x2, y, width, height].some((n) => !Number.isFinite(n))) throw new Error(`--mask regions are x,y,width,height in fractions of Image 1, separated by semicolons; "${region.trim()}" is not.`);
48264
48534
  return { x: x2, y, width, height };
48265
48535
  });
48536
+ var clips = (value) => value?.split(",").map((clip) => clip.trim()).filter(Boolean).map((clip) => /^\d+$/.test(clip) ? Number(clip) : clip);
48266
48537
  var cameraKeyframes = (value) => value?.split(";").filter((frame) => frame.trim()).map((frame) => {
48267
48538
  const [time3, azimuth, elevation, distance, ...rest] = frame.split(",").map((n) => n.trim() ? Number(n) : NaN);
48268
48539
  if (rest.length || [time3, azimuth, elevation, distance].some((n) => !Number.isFinite(n))) throw new Error(`--camera keyframes are time,azimuth,elevation,distance (time 0\u20131, angles in degrees), separated by semicolons; "${frame.trim()}" is not.`);
@@ -48277,9 +48548,10 @@ function assetFromFlags(flags) {
48277
48548
  if (flags.type === "image" && flags.model) throw new Error("Image models are managed by staff. Omit --model.");
48278
48549
  for (const name2 of Object.values(extras).flat()) {
48279
48550
  const value = flags[name2];
48280
- if (value !== void 0 && value !== false && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${flags.type} assets. ${accepted(flags.type)}`.trim());
48551
+ const given2 = value !== void 0 && (value !== false || (name2 === "texture" || name2 === "pbr") && flags.type !== "model");
48552
+ if (given2 && !extras[flags.type]?.includes(name2)) throw new Error(`--${name2} is not an option for ${flags.type} assets. ${accepted(flags.type)}`.trim());
48281
48553
  }
48282
- if (flags.type !== "video" && flags.prompt === void 0) throw new Error(`--prompt is required for ${flags.type} assets.`);
48554
+ if (flags.type !== "video" && flags.type !== "vector" && !(flags.type === "model" && flags.reference) && flags.prompt === void 0) throw new Error(`--prompt is required for ${flags.type} assets${flags.type === "model" ? " unless --reference names an image to build from" : ""}.`);
48283
48555
  const base2 = { type: flags.type, name: flags.name, prompt: flags.prompt, model: flags.model };
48284
48556
  if (flags.type === "image") return parseAsset({ ...base2, aspectRatio: flags["aspect-ratio"], transparent: flags.transparent, references: shas(flags.references), mask: regions(flags.mask) });
48285
48557
  if (flags.type === "video") {
@@ -48317,10 +48589,12 @@ function assetFromFlags(flags) {
48317
48589
  });
48318
48590
  }
48319
48591
  if (flags.type === "sfx") return parseAsset({ ...base2, seconds: flags.seconds, loop: flags.loop, promptInfluence: flags["prompt-influence"] });
48592
+ if (flags.type === "model") return parseAsset({ ...base2, reference: flags.reference, texture: flags.texture, pbr: flags.pbr, faces: flags.faces, rig: flags.rig, animations: clips(flags.animations), height: flags.height });
48593
+ if (flags.type === "vector") return parseAsset({ ...base2, workflow: flags.workflow, quality: flags.quality, aspectRatio: flags["aspect-ratio"], references: shas(flags.references), source: flags.source, shapes: flags.shapes, seed: flags.seed });
48320
48594
  return parseAsset({ ...base2, ...flags.type === "speech" ? { voice: flags.voice, delivery: flags.delivery } : {} });
48321
48595
  }
48322
48596
  async function assetFromFile(file2, model) {
48323
- const input2 = JSON.parse(await readFile9(file2, "utf8"));
48597
+ const input2 = JSON.parse(await readFile10(file2, "utf8"));
48324
48598
  if (!input2 || typeof input2 !== "object" || Array.isArray(input2)) throw new Error("The asset file must contain a JSON object.");
48325
48599
  return parseAsset({ ...input2, ...model ? { model } : {} });
48326
48600
  }
@@ -48328,7 +48602,7 @@ var MAX_BATCH = 50;
48328
48602
  var isObject2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
48329
48603
  var fileName = (name2) => name2.normalize("NFKD").replace(/[^\w.-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, 80);
48330
48604
  async function assetsFromBatchFile(file2, model) {
48331
- const input2 = JSON.parse(await readFile9(file2, "utf8"));
48605
+ const input2 = JSON.parse(await readFile10(file2, "utf8"));
48332
48606
  let defaults = {};
48333
48607
  let items;
48334
48608
  if (Array.isArray(input2)) items = input2;
@@ -48412,19 +48686,21 @@ var MIME_EXTENSIONS = {
48412
48686
  "image/png": "png",
48413
48687
  "image/jpeg": "jpg",
48414
48688
  "image/webp": "webp",
48689
+ "image/svg+xml": "svg",
48415
48690
  "video/mp4": "mp4",
48416
- "video/webm": "webm"
48691
+ "video/webm": "webm",
48692
+ "model/gltf-binary": "glb"
48417
48693
  };
48418
48694
  var extensionFor = (asset) => MIME_EXTENSIONS[asset.mimeType?.split(";")[0].trim().toLowerCase()] ?? /\.([a-z0-9]{1,5})$/i.exec(asset.file ?? "")?.[1]?.toLowerCase() ?? "bin";
48419
48695
  async function downloadTarget(output2, asset, folder = false) {
48420
- const path = resolve10(output2);
48421
- const isFolder = folder || /[\\/]$/.test(output2) || await stat4(path).then((info) => info.isDirectory(), () => false);
48696
+ const path = resolve11(output2);
48697
+ const isFolder = folder || /[\\/]$/.test(output2) || await stat5(path).then((info) => info.isDirectory(), () => false);
48422
48698
  if (!isFolder) {
48423
- await mkdir8(dirname7(path), { recursive: true });
48699
+ await mkdir9(dirname8(path), { recursive: true });
48424
48700
  return path;
48425
48701
  }
48426
- await mkdir8(path, { recursive: true });
48427
- return join10(path, `${fileName(asset.name) || asset.id}.${extensionFor(asset)}`);
48702
+ await mkdir9(path, { recursive: true });
48703
+ return join11(path, `${fileName(asset.name) || asset.id}.${extensionFor(asset)}`);
48428
48704
  }
48429
48705
  async function saveAsset(client, library, asset, path) {
48430
48706
  if (!library.ownerId || !/^[a-f0-9]{64}$/.test(asset.file)) throw new Error("Invalid asset download metadata.");
@@ -48448,7 +48724,7 @@ async function downloadAsset(client, project, assetId, output2) {
48448
48724
  const library = await client.request(assetsPath(project));
48449
48725
  const asset = library.assets.find((a) => a.id === assetId);
48450
48726
  if (!asset) throw new Error("Asset not found in this project. Run assets list to find its id.");
48451
- return saveAsset(client, library, asset, resolve10(output2));
48727
+ return saveAsset(client, library, asset, resolve11(output2));
48452
48728
  }
48453
48729
  async function generateAsset(client, project, body, options, signal) {
48454
48730
  const waiting = !options["dry-run"] && Boolean(options.wait || options.download);
@@ -48525,7 +48801,7 @@ async function submitAssetBatch(client, project, requests, options, signal) {
48525
48801
  }
48526
48802
 
48527
48803
  // cli/src/avatars.ts
48528
- import { readFile as readFile10, stat as stat5 } from "node:fs/promises";
48804
+ import { readFile as readFile11, stat as stat6 } from "node:fs/promises";
48529
48805
  import { extname } from "node:path";
48530
48806
  import { setTimeout as delay4 } from "node:timers/promises";
48531
48807
 
@@ -48631,8 +48907,8 @@ async function avatarFile2(value) {
48631
48907
  if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) throw new Error("Use an HTTPS URL, a project file SHA or a local file.");
48632
48908
  const mediaType = MEDIA_TYPES[extname(value).toLowerCase()];
48633
48909
  if (!mediaType) throw new Error("Use a PNG, JPEG or WebP image, or MP4, MOV or WebM footage.");
48634
- if ((await stat5(value)).size > AVATAR_INLINE_MAX_BYTES) throw new Error("Local files must be at most 8 MB. Use an HTTPS URL for larger footage.");
48635
- return { type: "base64", mediaType, data: (await readFile10(value)).toString("base64") };
48910
+ if ((await stat6(value)).size > AVATAR_INLINE_MAX_BYTES) throw new Error("Local files must be at most 8 MB. Use an HTTPS URL for larger footage.");
48911
+ return { type: "base64", mediaType, data: (await readFile11(value)).toString("base64") };
48636
48912
  }
48637
48913
  async function avatarFromFlags(flags) {
48638
48914
  const prompt = flags.type === "prompt";
@@ -48680,11 +48956,11 @@ async function waitForLook(client, project, lookId, timeout, signal, interval =
48680
48956
  // cli/src/youtube-auth.ts
48681
48957
  import { createServer } from "node:http";
48682
48958
  import { randomBytes, createHash as createHash5, timingSafeEqual } from "node:crypto";
48683
- import { readFile as readFile12, stat as stat6 } from "node:fs/promises";
48959
+ import { readFile as readFile13, stat as stat7 } from "node:fs/promises";
48684
48960
 
48685
48961
  // cli/src/youtube-store.ts
48686
- import { chmod as chmod4, lstat as lstat2, mkdir as mkdir9, open as open5, readFile as readFile11, readdir as readdir5, rename as rename5, unlink, writeFile as writeFile9 } from "node:fs/promises";
48687
- import { dirname as dirname8, join as join11 } from "node:path";
48962
+ import { chmod as chmod4, lstat as lstat2, mkdir as mkdir10, open as open5, readFile as readFile12, readdir as readdir6, rename as rename5, unlink, writeFile as writeFile9 } from "node:fs/promises";
48963
+ import { dirname as dirname9, join as join12 } from "node:path";
48688
48964
  import { randomUUID as randomUUID3 } from "node:crypto";
48689
48965
  function refuseInAnimator() {
48690
48966
  if (process.env.ANIMAGURU_CLOUD_WORKSPACE) throw new Error("YouTube uploads are not available in the cloud Animator. The user exports the MP4 from the draft page in their browser, then uploads it under Publishing \u2192 YouTube.");
@@ -48742,14 +49018,14 @@ var youtubeJob = external_exports.object({
48742
49018
  captions: youtubeCaptions.optional(),
48743
49019
  thumbnail: youtubeThumbnail.optional()
48744
49020
  }).strict();
48745
- var youtubeDirectory = () => join11(dirname8(credentialsPath()), "youtube");
48746
- var credentialFile = () => join11(youtubeDirectory(), "credentials.json");
49021
+ var youtubeDirectory = () => join12(dirname9(credentialsPath()), "youtube");
49022
+ var credentialFile = () => join12(youtubeDirectory(), "credentials.json");
48747
49023
  function jobFile(id2) {
48748
49024
  if (!external_exports.uuid().safeParse(id2).success) throw new Error("Use an upload ID returned by animaguru youtube uploads list.");
48749
- return join11(youtubeDirectory(), "uploads", `${id2}.json`);
49025
+ return join12(youtubeDirectory(), "uploads", `${id2}.json`);
48750
49026
  }
48751
49027
  async function privateDirectory(directory) {
48752
- await mkdir9(directory, { recursive: true, mode: 448 });
49028
+ await mkdir10(directory, { recursive: true, mode: 448 });
48753
49029
  const info = await lstat2(directory);
48754
49030
  if (info.isSymbolicLink() || !info.isDirectory()) throw new Error("YouTube configuration must use a real directory.");
48755
49031
  await chmod4(directory, 448);
@@ -48766,11 +49042,11 @@ async function existingDirectory(directory) {
48766
49042
  }
48767
49043
  async function readPrivate(file2) {
48768
49044
  try {
48769
- if (!await existingDirectory(youtubeDirectory()) || !await existingDirectory(dirname8(file2))) return null;
49045
+ if (!await existingDirectory(youtubeDirectory()) || !await existingDirectory(dirname9(file2))) return null;
48770
49046
  const info = await lstat2(file2);
48771
49047
  if (info.isSymbolicLink() || !info.isFile() || info.size > 65536) throw new Error("Invalid local YouTube state file.");
48772
49048
  await chmod4(file2, 384);
48773
- return JSON.parse(await readFile11(file2, "utf8"));
49049
+ return JSON.parse(await readFile12(file2, "utf8"));
48774
49050
  } catch (error62) {
48775
49051
  if (error62.code === "ENOENT") return null;
48776
49052
  throw new Error("Cannot read local YouTube state. Check your AnimaGuru configuration directory.");
@@ -48778,7 +49054,7 @@ async function readPrivate(file2) {
48778
49054
  }
48779
49055
  async function writePrivate(file2, value) {
48780
49056
  await privateDirectory(youtubeDirectory());
48781
- await privateDirectory(dirname8(file2));
49057
+ await privateDirectory(dirname9(file2));
48782
49058
  const temporary = `${file2}.${randomUUID3()}.tmp`;
48783
49059
  try {
48784
49060
  await writeFile9(temporary, JSON.stringify(value) + "\n", { flag: "wx", mode: 384 });
@@ -48810,11 +49086,11 @@ function youtubeJobSummary(job) {
48810
49086
  return { id: job.id, file: job.file, title: job.metadata.title, channelId: job.channelId, status: job.status, bytes: job.bytes, bytesUploaded: job.bytesUploaded, privacy: job.privacy, youtubeId: job.youtubeId, url: job.youtubeId ? `https://www.youtube.com/watch?v=${job.youtubeId}` : null, createdAt: job.createdAt, captions: job.captions ? { file: job.captions.file, language: job.captions.language, name: job.captions.name, status: job.captions.status } : null, thumbnail: job.thumbnail ? { file: job.thumbnail.file, status: job.thumbnail.status } : null };
48811
49087
  }
48812
49088
  async function localYouTubeJobs() {
48813
- const directory = join11(youtubeDirectory(), "uploads");
49089
+ const directory = join12(youtubeDirectory(), "uploads");
48814
49090
  if (!await existingDirectory(directory)) return [];
48815
49091
  let files;
48816
49092
  try {
48817
- files = await readdir5(directory);
49093
+ files = await readdir6(directory);
48818
49094
  } catch (error62) {
48819
49095
  if (error62.code === "ENOENT") return [];
48820
49096
  throw error62;
@@ -48831,10 +49107,10 @@ async function clearYouTubeData() {
48831
49107
  await unlink(credentialFile()).catch((error62) => {
48832
49108
  if (error62.code !== "ENOENT") throw error62;
48833
49109
  });
48834
- if (!await existingDirectory(join11(youtubeDirectory(), "uploads"))) return;
49110
+ if (!await existingDirectory(join12(youtubeDirectory(), "uploads"))) return;
48835
49111
  let files;
48836
49112
  try {
48837
- files = await readdir5(join11(youtubeDirectory(), "uploads"));
49113
+ files = await readdir6(join12(youtubeDirectory(), "uploads"));
48838
49114
  } catch (error62) {
48839
49115
  if (error62.code === "ENOENT") return;
48840
49116
  throw error62;
@@ -48844,7 +49120,7 @@ async function clearYouTubeData() {
48844
49120
  async function withYouTubeLock(operation) {
48845
49121
  refuseInAnimator();
48846
49122
  await privateDirectory(youtubeDirectory());
48847
- const file2 = join11(youtubeDirectory(), "operation.lock");
49123
+ const file2 = join12(youtubeDirectory(), "operation.lock");
48848
49124
  const lock = await open5(file2, "wx", 384).catch((error62) => {
48849
49125
  if (error62.code === "EEXIST") throw new Error(`Another YouTube command is running. If a previous command was killed, verify it has stopped before removing ${file2}.`);
48850
49126
  throw error62;
@@ -48992,11 +49268,11 @@ async function youtubeLogin(options) {
48992
49268
  if (existing && options.captions && !canAddCaptions(existing)) throw new Error("A YouTube channel is already connected without caption permission. Run youtube auth logout --yes, then youtube auth login --captions.");
48993
49269
  if (existing) throw new Error("A YouTube channel is already connected. Run youtube auth logout --yes before switching channels.");
48994
49270
  const file2 = options["client-file"];
48995
- const info = await stat6(file2);
49271
+ const info = await stat7(file2);
48996
49272
  if (!info.isFile() || info.size > 65536) throw new Error("Use Google's downloaded Desktop app OAuth client JSON.");
48997
49273
  let installed;
48998
49274
  try {
48999
- installed = JSON.parse(await readFile12(file2, "utf8")).installed;
49275
+ installed = JSON.parse(await readFile13(file2, "utf8")).installed;
49000
49276
  } catch {
49001
49277
  }
49002
49278
  if (!installed || typeof installed.client_id !== "string" || !installed.client_id.endsWith(".apps.googleusercontent.com") || installed.client_secret !== void 0 && typeof installed.client_secret !== "string") throw new Error("Use Google's Desktop app OAuth client JSON (installed), not a web client or API key.");
@@ -49006,8 +49282,8 @@ async function youtubeLogin(options) {
49006
49282
  let receive;
49007
49283
  let fail;
49008
49284
  let used = false;
49009
- const callback = new Promise((resolve18, reject) => {
49010
- receive = resolve18;
49285
+ const callback = new Promise((resolve19, reject) => {
49286
+ receive = resolve19;
49011
49287
  fail = reject;
49012
49288
  });
49013
49289
  void callback.catch(() => {
@@ -49045,9 +49321,9 @@ async function youtubeLogin(options) {
49045
49321
  response.end("Authorization received. Return to your AnimaGuru terminal.");
49046
49322
  receive(code);
49047
49323
  });
49048
- await new Promise((resolve18, reject) => {
49324
+ await new Promise((resolve19, reject) => {
49049
49325
  server.once("error", reject);
49050
- server.listen(0, "127.0.0.1", resolve18);
49326
+ server.listen(0, "127.0.0.1", resolve19);
49051
49327
  });
49052
49328
  const address = server.address();
49053
49329
  if (!address || typeof address === "string") throw new Error("Cannot open the local YouTube authorization listener.");
@@ -49082,7 +49358,7 @@ async function youtubeLogin(options) {
49082
49358
  clearTimeout(timer);
49083
49359
  options.signal?.removeEventListener("abort", abort);
49084
49360
  server.closeAllConnections();
49085
- await new Promise((resolve18) => server.close(() => resolve18()));
49361
+ await new Promise((resolve19) => server.close(() => resolve19()));
49086
49362
  }
49087
49363
  });
49088
49364
  }
@@ -49113,8 +49389,8 @@ async function youtubeLogout(options) {
49113
49389
 
49114
49390
  // cli/src/youtube-upload.ts
49115
49391
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
49116
- import { open as open6, readFile as readFile13, stat as stat7 } from "node:fs/promises";
49117
- import { basename as basename5, join as join12, resolve as resolve11 } from "node:path";
49392
+ import { open as open6, readFile as readFile14, stat as stat8 } from "node:fs/promises";
49393
+ import { basename as basename5, join as join13, resolve as resolve12 } from "node:path";
49118
49394
  import { setTimeout as delay5 } from "node:timers/promises";
49119
49395
  var YOUTUBE_CHUNK_BYTES = 8 * 1024 * 1024;
49120
49396
  function safeYouTubeSession(value) {
@@ -49165,23 +49441,23 @@ async function planCaptions(root, mp4, options, fallbackLanguage) {
49165
49441
  async function readThumbnail(file2) {
49166
49442
  let info;
49167
49443
  try {
49168
- info = await stat7(file2);
49444
+ info = await stat8(file2);
49169
49445
  } catch {
49170
49446
  throw new Error(`Cannot read the thumbnail ${file2}.`);
49171
49447
  }
49172
49448
  if (!info.isFile() || info.size < 8 || info.size > 2 * 1024 * 1024) throw new Error("Use a JPEG or PNG thumbnail up to 2 MiB, such as the one npm run export:mp4 saves beside the MP4.");
49173
- const content = await readFile13(file2);
49449
+ const content = await readFile14(file2);
49174
49450
  const type = content[0] === 255 && content[1] === 216 && content[2] === 255 ? "image/jpeg" : content.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) ? "image/png" : null;
49175
49451
  if (!type) throw new Error("Use a JPEG or PNG thumbnail.");
49176
49452
  return { content, type, sha256: createHash6("sha256").update(content).digest("hex") };
49177
49453
  }
49178
49454
  async function planThumbnail(root, mp4, choice = "auto") {
49179
49455
  if (choice === "none") return null;
49180
- let file2 = choice === "auto" ? void 0 : resolve11(root, choice);
49456
+ let file2 = choice === "auto" ? void 0 : resolve12(root, choice);
49181
49457
  if (!file2) {
49182
49458
  for (const extension of [".jpg", ".jpeg", ".png"]) {
49183
49459
  const candidate = mp4.replace(/\.mp4$/i, extension);
49184
- if (await stat7(candidate).then((info) => info.isFile(), () => false)) {
49460
+ if (await stat8(candidate).then((info) => info.isFile(), () => false)) {
49185
49461
  file2 = candidate;
49186
49462
  break;
49187
49463
  }
@@ -49366,12 +49642,12 @@ async function uploadYouTube(options) {
49366
49642
  refuseInAnimator();
49367
49643
  if (!options.yes && !options["dry-run"]) throw new Error("Pass --yes to authorize the YouTube upload, or --dry-run to inspect it locally.");
49368
49644
  if (!["yes", "no"].includes(options["made-for-kids"]) || !["yes", "no"].includes(options["synthetic-media"])) throw new Error("Set --made-for-kids yes|no and --synthetic-media yes|no explicitly.");
49369
- const root = resolve11(options.directory ?? ".");
49370
- const file2 = resolve11(root, options.file);
49645
+ const root = resolve12(options.directory ?? ".");
49646
+ const file2 = resolve12(root, options.file);
49371
49647
  const context = await localProject(root, options.animation);
49372
49648
  let config2 = {};
49373
49649
  try {
49374
- config2 = JSON.parse(await readFile13(join12(context.projectRoot, "film.config.json"), "utf8"));
49650
+ config2 = JSON.parse(await readFile14(join13(context.projectRoot, "film.config.json"), "utf8"));
49375
49651
  } catch {
49376
49652
  }
49377
49653
  const receipt = await exportReceipt(file2);
@@ -49448,8 +49724,8 @@ async function resumeYouTube(id2, options) {
49448
49724
  var listYouTubeUploads = () => withYouTubeLock(async () => ({ uploads: (await localYouTubeJobs()).map(youtubeJobSummary) }));
49449
49725
 
49450
49726
  // cli/src/motion.ts
49451
- import { access as access2, mkdir as mkdir10, writeFile as writeFile10 } from "node:fs/promises";
49452
- import { dirname as dirname9, join as join13, relative as relative2, resolve as resolve12, sep as sep2 } from "node:path";
49727
+ import { access as access3, mkdir as mkdir11, writeFile as writeFile10 } from "node:fs/promises";
49728
+ import { dirname as dirname10, join as join14, relative as relative2, resolve as resolve13, sep as sep2 } from "node:path";
49453
49729
 
49454
49730
  // shared/motion-library.ts
49455
49731
  var MOTION_CATEGORIES = [
@@ -49498,18 +49774,18 @@ async function searchMotion(client, query, flags) {
49498
49774
  if (flags.code) params.set("include_code", "true");
49499
49775
  return client.request(`/api/v1/motion-library/search?${params}`);
49500
49776
  }
49501
- var exists2 = (path) => access2(path).then(() => true, () => false);
49777
+ var exists2 = (path) => access3(path).then(() => true, () => false);
49502
49778
  async function workspaceRoot(start) {
49503
- for (let directory = start; ; directory = dirname9(directory)) {
49504
- if (await exists2(join13(directory, "animaguru.workspace.json"))) return directory;
49505
- if (dirname9(directory) === directory) return null;
49779
+ for (let directory = start; ; directory = dirname10(directory)) {
49780
+ if (await exists2(join14(directory, "animaguru.workspace.json"))) return directory;
49781
+ if (dirname10(directory) === directory) return null;
49506
49782
  }
49507
49783
  }
49508
49784
  async function writeMotionExample(example, directory, { force = false } = {}) {
49509
- const target2 = resolve12(directory);
49785
+ const target2 = resolve13(directory);
49510
49786
  const workspace3 = await workspaceRoot(target2);
49511
49787
  const inAnimation = Boolean(workspace3 && relative2(workspace3, target2).split(sep2)[0] === "animations");
49512
- const files = example.files.map((file2) => ({ path: join13(target2, safeProjectPath(file2.path)), code: inAnimation ? motionCodeForWorkspace(file2.code) : file2.code }));
49788
+ const files = example.files.map((file2) => ({ path: join14(target2, safeProjectPath(file2.path)), code: inAnimation ? motionCodeForWorkspace(file2.code) : file2.code }));
49513
49789
  if (!force) {
49514
49790
  const clashes = [];
49515
49791
  for (const [index2, file2] of files.entries()) if (index2 === 0 && await exists2(file2.path)) clashes.push(file2.path);
@@ -49521,7 +49797,7 @@ async function writeMotionExample(example, directory, { force = false } = {}) {
49521
49797
  kept.push(file2.path);
49522
49798
  continue;
49523
49799
  }
49524
- await mkdir10(dirname9(file2.path), { recursive: true });
49800
+ await mkdir11(dirname10(file2.path), { recursive: true });
49525
49801
  await writeFile10(file2.path, file2.code);
49526
49802
  written.push(file2.path);
49527
49803
  }
@@ -49529,8 +49805,8 @@ async function writeMotionExample(example, directory, { force = false } = {}) {
49529
49805
  }
49530
49806
 
49531
49807
  // cli/src/library.ts
49532
- import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile11 } from "node:fs/promises";
49533
- import { dirname as dirname10, join as join14, resolve as resolve13 } from "node:path";
49808
+ import { mkdir as mkdir12, readFile as readFile15, writeFile as writeFile11 } from "node:fs/promises";
49809
+ import { dirname as dirname11, join as join15, resolve as resolve14 } from "node:path";
49534
49810
 
49535
49811
  // shared/asset-library.ts
49536
49812
  var LIBRARY_ASSET_TYPES = ["sprite", "image", "video", "music", "speech", "sfx"];
@@ -49617,23 +49893,23 @@ function searchLibrary(client, query, flags) {
49617
49893
  var getLibraryAsset = (client, id2) => client.request(libraryPath(id2));
49618
49894
  async function downloadLibraryAsset(client, id2, output2, { force = false } = {}) {
49619
49895
  const asset = await getLibraryAsset(client, id2);
49620
- const root = resolve13(output2, asset.type === "sprite" ? safeProjectPath(asset.id) : ".");
49896
+ const root = resolve14(output2, asset.type === "sprite" ? safeProjectPath(asset.id) : ".");
49621
49897
  const written = [];
49622
49898
  for (const file2 of asset.files) {
49623
- const path = join14(root, safeProjectPath(file2.path));
49899
+ const path = join15(root, safeProjectPath(file2.path));
49624
49900
  const response = await client.request(`${libraryPath(asset.id)}/files/${file2.path.split("/").map(encodeURIComponent).join("/")}?download=1`, { raw: true, timeout: 3e5 });
49625
49901
  const bytes = new Uint8Array(await response.arrayBuffer());
49626
- await mkdir11(dirname10(path), { recursive: true });
49902
+ await mkdir12(dirname11(path), { recursive: true });
49627
49903
  await writeFile11(path, bytes, { flag: force ? "w" : "wx" }).catch((error62) => {
49628
49904
  throw error62.code === "EEXIST" ? new Error(`Already exists: ${path}. Pass --force to replace it.`) : error62;
49629
49905
  });
49630
49906
  written.push({ path, size: bytes.byteLength, mimeType: file2.mimeType });
49631
49907
  }
49632
- return { id: asset.id, type: asset.type, files: written, ...asset.type === "sprite" ? { animations: asset.details.animations?.map(({ action, sheet, data, frames, fps, loop }) => ({ action, sheet: join14(root, sheet), data: join14(root, data), frames, fps, loop })) } : {} };
49908
+ return { id: asset.id, type: asset.type, files: written, ...asset.type === "sprite" ? { animations: asset.details.animations?.map(({ action, sheet, data, frames, fps, loop }) => ({ action, sheet: join15(root, sheet), data: join15(root, data), frames, fps, loop })) } : {} };
49633
49909
  }
49634
49910
  var importLibraryAsset = (client, id2, project, name2) => client.request(`${libraryPath(id2)}/import`, { method: "POST", body: { projectId: project, ...name2 ? { name: name2 } : {} } });
49635
49911
  async function words(flags) {
49636
- const input2 = flags.file ? JSON.parse(await readFile14(flags.file, "utf8")) : {};
49912
+ const input2 = flags.file ? JSON.parse(await readFile15(flags.file, "utf8")) : {};
49637
49913
  if (!input2 || typeof input2 !== "object" || Array.isArray(input2)) throw new Error("The file must contain a JSON object.");
49638
49914
  return {
49639
49915
  ...input2,
@@ -49678,8 +49954,8 @@ async function moveWork(client, project, flags) {
49678
49954
  }
49679
49955
 
49680
49956
  // cli/src/release.ts
49681
- import { mkdir as mkdir12, readFile as readFile15, rename as rename6, unlink as unlink2, writeFile as writeFile12 } from "node:fs/promises";
49682
- import { dirname as dirname11, join as join15 } from "node:path";
49957
+ import { mkdir as mkdir13, readFile as readFile16, rename as rename6, unlink as unlink2, writeFile as writeFile12 } from "node:fs/promises";
49958
+ import { dirname as dirname12, join as join16 } from "node:path";
49683
49959
  import { randomUUID as randomUUID5 } from "node:crypto";
49684
49960
  var releaseUrl = "https://animaguru.com/cli/release.json";
49685
49961
  function versionParts(version3) {
@@ -49700,10 +49976,10 @@ function parseRelease(value) {
49700
49976
  versionParts(value.version);
49701
49977
  return { name: value.name, version: value.version };
49702
49978
  }
49703
- var cachePath = () => join15(dirname11(credentialsPath()), "cli-release.json");
49979
+ var cachePath = () => join16(dirname12(credentialsPath()), "cli-release.json");
49704
49980
  async function cachedRelease() {
49705
49981
  try {
49706
- return parseRelease(JSON.parse(await readFile15(cachePath(), "utf8")));
49982
+ return parseRelease(JSON.parse(await readFile16(cachePath(), "utf8")));
49707
49983
  } catch {
49708
49984
  return void 0;
49709
49985
  }
@@ -49719,7 +49995,7 @@ async function latestRelease(signal) {
49719
49995
  const path = cachePath();
49720
49996
  const temp = `${path}.${randomUUID5()}.tmp`;
49721
49997
  try {
49722
- await mkdir12(dirname11(path), { recursive: true, mode: 448 });
49998
+ await mkdir13(dirname12(path), { recursive: true, mode: 448 });
49723
49999
  await writeFile12(temp, JSON.stringify(release) + "\n", { flag: "wx", mode: 384 });
49724
50000
  await rename6(temp, path);
49725
50001
  } catch {
@@ -49761,8 +50037,8 @@ async function requireCurrentRelease(argv, stderr) {
49761
50037
  }
49762
50038
 
49763
50039
  // cli/src/brands.ts
49764
- import { lstat as lstat3, mkdir as mkdir13, readFile as readFile16, stat as stat8, writeFile as writeFile13 } from "node:fs/promises";
49765
- import { basename as basename6, dirname as dirname12, extname as extname2, join as join16, resolve as resolve14 } from "node:path";
50040
+ import { lstat as lstat3, mkdir as mkdir14, readFile as readFile17, stat as stat9, writeFile as writeFile13 } from "node:fs/promises";
50041
+ import { basename as basename6, dirname as dirname13, extname as extname2, join as join17, resolve as resolve15 } from "node:path";
49766
50042
 
49767
50043
  // shared/brands.ts
49768
50044
  var brandId = external_exports.string().regex(/^br_[a-f0-9]{24}$/);
@@ -49881,7 +50157,7 @@ async function listBrands(client, selected) {
49881
50157
  }
49882
50158
  var getBrand = (client, id2) => client.request(brandPath(id2));
49883
50159
  async function jsonObject(file2) {
49884
- const value = JSON.parse(await readFile16(file2, "utf8"));
50160
+ const value = JSON.parse(await readFile17(file2, "utf8"));
49885
50161
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("The file must contain a JSON object.");
49886
50162
  return value;
49887
50163
  }
@@ -49908,12 +50184,12 @@ async function uploadBrandFile(client, id2, file2, flags) {
49908
50184
  const path = brandPath(id2);
49909
50185
  if (!flags.yes) throw new Error("Pass --yes to authorize uploading this file to the brand.");
49910
50186
  if (flags.kind && !BRAND_FILE_KINDS.includes(flags.kind)) throw new Error("Unknown brand file kind.");
49911
- const info = await stat8(file2);
50187
+ const info = await stat9(file2);
49912
50188
  if (!info.isFile() || !info.size || info.size > BRAND_FILE_BYTES) throw new Error("A brand file must be between 1 byte and 32 MiB.");
49913
50189
  const extension = extname2(file2).slice(1).toLowerCase().replace(/^jpeg$/, "jpg");
49914
50190
  const mimeType = flags["mime-type"] ?? Object.entries(BRAND_FILE_TYPES).find(([, ext]) => ext === extension)?.[0];
49915
50191
  if (!mimeType || !BRAND_FILE_TYPES[mimeType]) throw new Error("Unsupported brand file type. Use a web image, font, audio or video file, or specify --mime-type.");
49916
- const bytes = new Uint8Array(await readFile16(file2));
50192
+ const bytes = new Uint8Array(await readFile17(file2));
49917
50193
  if (!bytes.byteLength || bytes.byteLength > BRAND_FILE_BYTES) throw new Error("A brand file must be between 1 byte and 32 MiB.");
49918
50194
  const form = new FormData();
49919
50195
  form.set("file", new File([bytes], basename6(file2), { type: mimeType }));
@@ -49931,22 +50207,22 @@ async function downloadBrand(client, id2, output2, flags = {}) {
49931
50207
  `, mimeType: "application/json" },
49932
50208
  { path: "branding/brand-guide.md", content: brandGuide(brand), mimeType: "text/markdown" }
49933
50209
  ];
49934
- const root = resolve14(output2);
49935
- const targets = [...textFiles.map((file2) => file2.path), ...files.map(brandFilePath)].map((path) => join16(root, safeProjectPath(path)));
50210
+ const root = resolve15(output2);
50211
+ const targets = [...textFiles.map((file2) => file2.path), ...files.map(brandFilePath)].map((path) => join17(root, safeProjectPath(path)));
49936
50212
  if (new Set(targets).size !== targets.length) throw new Error("The brand contains conflicting file names.");
49937
- for (const path of [join16(root, "branding"), ...targets]) {
50213
+ for (const path of [join17(root, "branding"), ...targets]) {
49938
50214
  const info = await lstat3(path).catch((error62) => {
49939
50215
  if (error62.code !== "ENOENT") throw error62;
49940
50216
  return null;
49941
50217
  });
49942
50218
  if (info?.isSymbolicLink()) throw new Error(`Refusing to write through a symbolic link: ${path}`);
49943
- if (path !== join16(root, "branding") && info && (!flags.force || !info.isFile())) throw new Error(`Already exists: ${path}. Pass --force to replace files.`);
50219
+ if (path !== join17(root, "branding") && info && (!flags.force || !info.isFile())) throw new Error(`Already exists: ${path}. Pass --force to replace files.`);
49944
50220
  }
49945
- await mkdir13(join16(root, "branding"), { recursive: true });
50221
+ await mkdir14(join17(root, "branding"), { recursive: true });
49946
50222
  const written = [];
49947
50223
  const save = async (path, bytes, mimeType) => {
49948
- const target2 = join16(root, safeProjectPath(path));
49949
- await mkdir13(dirname12(target2), { recursive: true });
50224
+ const target2 = join17(root, safeProjectPath(path));
50225
+ await mkdir14(dirname13(target2), { recursive: true });
49950
50226
  await writeFile13(target2, bytes, { flag: flags.force ? "w" : "wx" });
49951
50227
  written.push({ path: target2, size: bytes.byteLength, mimeType });
49952
50228
  };
@@ -49959,8 +50235,8 @@ async function downloadBrand(client, id2, output2, flags = {}) {
49959
50235
  }
49960
50236
 
49961
50237
  // cli/src/review-check.ts
49962
- import { readFile as readFile17, readdir as readdir6, stat as stat9, writeFile as writeFile14 } from "node:fs/promises";
49963
- import { join as join17 } from "node:path";
50238
+ import { readFile as readFile18, readdir as readdir7, stat as stat10, writeFile as writeFile14 } from "node:fs/promises";
50239
+ import { join as join18 } from "node:path";
49964
50240
 
49965
50241
  // shared/frame-checks.ts
49966
50242
  var FRAME_CHECK_MAX_FRAMES = 12;
@@ -50004,17 +50280,17 @@ var frameCheckRequest = external_exports.object({
50004
50280
  // cli/src/review-check.ts
50005
50281
  async function framesToCheck(projectRoot, source, report2) {
50006
50282
  if (source === "frames") {
50007
- const folder2 = join17(projectRoot, "production/review/frames");
50008
- const names2 = await readdir6(folder2).catch(() => []);
50009
- const frames = names2.map((name2) => /^frame-([\d.]+)s\.jpg$/.exec(name2)).filter((match) => match !== null).map((match) => ({ file: join17(folder2, match[0]), time: Number(match[1]) })).sort((a, b) => a.time - b.time);
50283
+ const folder2 = join18(projectRoot, "production/review/frames");
50284
+ const names2 = await readdir7(folder2).catch(() => []);
50285
+ const frames = names2.map((name2) => /^frame-([\d.]+)s\.jpg$/.exec(name2)).filter((match) => match !== null).map((match) => ({ file: join18(folder2, match[0]), time: Number(match[1]) })).sort((a, b) => a.time - b.time);
50010
50286
  if (!frames.length) throw new Error("No full-size frames found. Run npm run frames -- --full (with --at TIMES for the moments to check) first.");
50011
50287
  return frames;
50012
50288
  }
50013
- const folder = join17(projectRoot, "production/review");
50289
+ const folder = join18(projectRoot, "production/review");
50014
50290
  const times = [...report2.seekMatches ?? []].map((match) => match.time).sort((a, b) => a - b);
50015
- const names = (await readdir6(folder)).filter((name2) => /^frame-\d{2}\.jpg$/.test(name2)).sort();
50291
+ const names = (await readdir7(folder)).filter((name2) => /^frame-\d{2}\.jpg$/.test(name2)).sort();
50016
50292
  if (!names.length || names.length !== times.length) throw new Error("The review frames do not match report.json. Run npm run review again.");
50017
- return names.map((name2, index2) => ({ file: join17(folder, name2), time: times[index2] }));
50293
+ return names.map((name2, index2) => ({ file: join18(folder, name2), time: times[index2] }));
50018
50294
  }
50019
50295
  function filmContext(report2, brief, extra) {
50020
50296
  const config2 = report2.config ?? {};
@@ -50029,29 +50305,29 @@ function compact(answers) {
50029
50305
  async function reviewCheck(client, options) {
50030
50306
  if (!options.yes && !options["dry-run"]) throw new Error("Pass --yes to authorize spending credits on the check, or --dry-run for its most.");
50031
50307
  const context = await localProject(options.directory, options.animation);
50032
- const reportFile = join17(context.projectRoot, "production/review/report.json");
50033
- const report2 = JSON.parse(await readFile17(reportFile, "utf8").catch(() => {
50308
+ const reportFile = join18(context.projectRoot, "production/review/report.json");
50309
+ const report2 = JSON.parse(await readFile18(reportFile, "utf8").catch(() => {
50034
50310
  throw new Error("Run npm run review first: production/review/report.json is missing.");
50035
50311
  }));
50036
50312
  const frames = await framesToCheck(context.projectRoot, options.source ?? "review", report2);
50037
50313
  if (frames.length > FRAME_CHECK_MAX_FRAMES) throw new Error(`Check at most ${FRAME_CHECK_MAX_FRAMES} frames at once; run npm run frames -- --full --at with fewer times.`);
50038
- const sizes = await Promise.all(frames.map(async (frame) => (await stat9(frame.file)).size));
50314
+ const sizes = await Promise.all(frames.map(async (frame) => (await stat10(frame.file)).size));
50039
50315
  if (sizes.reduce((sum, size) => sum + size, 0) > FRAME_CHECK_TOTAL_BYTES) throw new Error("These frames total over 8 MiB. Check fewer at once.");
50040
- const questions = options.questions ? frameCheckQuestions.parse(JSON.parse(await readFile17(options.questions, "utf8"))) : void 0;
50041
- const brief = await readFile17(join17(context.projectRoot, "production/brief.md"), "utf8").catch(() => "");
50316
+ const questions = options.questions ? frameCheckQuestions.parse(JSON.parse(await readFile18(options.questions, "utf8"))) : void 0;
50317
+ const brief = await readFile18(join18(context.projectRoot, "production/brief.md"), "utf8").catch(() => "");
50042
50318
  const link2 = await cloudLink(context.toolsRoot).catch(() => null);
50043
50319
  const body = {
50044
50320
  ...link2?.origin === client.origin ? { projectId: link2.projectId } : { workspace: await selectedWorkspace(client.origin) },
50045
50321
  context: filmContext(report2, brief, options.context),
50046
50322
  ...report2.config?.duration ? { duration: report2.config.duration } : {},
50047
- frames: await Promise.all(frames.map(async (frame) => ({ time: Math.round(frame.time * 1e3) / 1e3, image: `data:image/jpeg;base64,${(await readFile17(frame.file)).toString("base64")}` }))),
50323
+ frames: await Promise.all(frames.map(async (frame) => ({ time: Math.round(frame.time * 1e3) / 1e3, image: `data:image/jpeg;base64,${(await readFile18(frame.file)).toString("base64")}` }))),
50048
50324
  ...questions ? { questions } : {},
50049
50325
  ...options["dry-run"] ? { dryRun: true } : {}
50050
50326
  };
50051
50327
  if (options["dry-run"]) return client.request("/api/v1/frame-checks", { method: "POST", body, timeout: 12e4 });
50052
50328
  const result = await client.request("/api/v1/frame-checks", { method: "POST", body, timeout: 12e4 });
50053
50329
  const fileAt = (time3) => frames.find((frame) => Math.abs(frame.time - time3) < 1e-3)?.file.slice(context.projectRoot.length + 1);
50054
- const saved = join17(context.projectRoot, "production/review/check.json");
50330
+ const saved = join18(context.projectRoot, "production/review/check.json");
50055
50331
  await writeFile14(saved, `${JSON.stringify({ ...result, frames: result.frames.map((frame) => ({ file: fileAt(frame.time), ...frame })) }, null, 2)}
50056
50332
  `);
50057
50333
  return {
@@ -50065,8 +50341,8 @@ async function reviewCheck(client, options) {
50065
50341
 
50066
50342
  // cli/src/reference.ts
50067
50343
  import { openAsBlob } from "node:fs";
50068
- import { mkdir as mkdir14, stat as stat10, writeFile as writeFile15 } from "node:fs/promises";
50069
- import { basename as basename7, dirname as dirname13, extname as extname3, join as join18, relative as relative3, resolve as resolve15 } from "node:path";
50344
+ import { mkdir as mkdir15, stat as stat11, writeFile as writeFile15 } from "node:fs/promises";
50345
+ import { basename as basename7, dirname as dirname14, extname as extname3, join as join19, relative as relative3, resolve as resolve16 } from "node:path";
50070
50346
 
50071
50347
  // shared/video-analysis.ts
50072
50348
  var VIDEO_ANALYSIS_MAX_BYTES = 100 * 1024 * 1024;
@@ -50097,11 +50373,11 @@ var TYPES = { ".mp4": "video/mp4", ".m4v": "video/mp4", ".mov": "video/quicktime
50097
50373
  var POLL_MS = 1e4;
50098
50374
  var WAIT_LIMIT_MS = 70 * 6e4;
50099
50375
  async function briefFile(options, name2) {
50100
- if (options.out) return resolve15(options.out);
50376
+ if (options.out) return resolve16(options.out);
50101
50377
  const slug2 = name2.replace(/\.[a-z0-9]{2,4}$/i, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60) || "reference";
50102
50378
  const project = await localProject(options.directory ?? ".", options.animation).catch(() => null);
50103
- const film = project && await stat10(join18(project.projectRoot, "animaguru.project.json")).then(() => true, () => false);
50104
- return film ? join18(project.projectRoot, "production/references", `${slug2}.json`) : resolve15(options.directory ?? ".", `${slug2}.analysis.json`);
50379
+ const film = project && await stat11(join19(project.projectRoot, "animaguru.project.json")).then(() => true, () => false);
50380
+ return film ? join19(project.projectRoot, "production/references", `${slug2}.json`) : resolve16(options.directory ?? ".", `${slug2}.analysis.json`);
50105
50381
  }
50106
50382
  var pause = (ms, signal) => new Promise((done, fail) => {
50107
50383
  signal?.throwIfAborted();
@@ -50140,7 +50416,7 @@ async function report(analysis, options) {
50140
50416
  if (analysis.status === "failed") throw new Error(analysis.error ?? "The analysis failed.");
50141
50417
  if (analysis.status !== "completed") return { id: analysis.id, status: analysis.status, next: `animaguru reference get ${analysis.id} --wait` };
50142
50418
  const file2 = await briefFile(options, sourceName(analysis));
50143
- await mkdir14(dirname13(file2), { recursive: true });
50419
+ await mkdir15(dirname14(file2), { recursive: true });
50144
50420
  await writeFile15(file2, `${JSON.stringify(analysis, null, 2)}
50145
50421
  `);
50146
50422
  const brief = analysis.result?.brief;
@@ -50165,8 +50441,8 @@ async function referenceAnalyze(client, options) {
50165
50441
  if (!options.video.startsWith("https://")) throw new Error("Use an HTTPS link to the video file.");
50166
50442
  started = await client.request("/api/v1/video-analyses", { method: "POST", body: { ...settings2, url: options.video, ...dryRun ? { dryRun: true } : {} }, timeout: 12e4 });
50167
50443
  } else {
50168
- const file2 = resolve15(options.video);
50169
- const info = await stat10(file2).catch(() => null);
50444
+ const file2 = resolve16(options.video);
50445
+ const info = await stat11(file2).catch(() => null);
50170
50446
  if (!info?.isFile()) throw new Error(`No video file at ${options.video}. Give a file path, or an HTTPS link to the video file itself.`);
50171
50447
  const type = TYPES[extname3(file2).toLowerCase()];
50172
50448
  if (!type) throw new Error("Analyse an MP4, MOV or WebM video.");
@@ -50188,8 +50464,8 @@ async function referenceGet(client, id2, options) {
50188
50464
 
50189
50465
  // cli/src/curl.ts
50190
50466
  import { createWriteStream as createWriteStream2, openAsBlob as openAsBlob2 } from "node:fs";
50191
- import { readFile as readFile18, unlink as unlink3 } from "node:fs/promises";
50192
- import { extname as extname4, resolve as resolve16 } from "node:path";
50467
+ import { readFile as readFile19, unlink as unlink3 } from "node:fs/promises";
50468
+ import { extname as extname4, resolve as resolve17 } from "node:path";
50193
50469
  import { Readable as Readable2 } from "node:stream";
50194
50470
  import { pipeline as pipeline2 } from "node:stream/promises";
50195
50471
  function curlUrl(server, target2) {
@@ -50229,7 +50505,7 @@ var readStdin = async () => {
50229
50505
  };
50230
50506
  async function bodyValue(value, io) {
50231
50507
  if (value === "@-") return (io.stdin ?? readStdin)();
50232
- return value.startsWith("@") ? readFile18(resolve16(value.slice(1))) : value;
50508
+ return value.startsWith("@") ? readFile19(resolve17(value.slice(1))) : value;
50233
50509
  }
50234
50510
  var parsesAsJson = (body) => {
50235
50511
  try {
@@ -50245,7 +50521,7 @@ async function curl(options, io) {
50245
50521
  if ([options.data, options.json, options.uploadFile].filter((value) => value !== void 0).length > 1) throw new Error("Use one of --data, --json and --upload-file.");
50246
50522
  let body;
50247
50523
  if (options.uploadFile !== void 0) {
50248
- const file2 = resolve16(options.uploadFile);
50524
+ const file2 = resolve17(options.uploadFile);
50249
50525
  body = await openAsBlob2(file2);
50250
50526
  if (!headers.has("content-type")) headers.set("Content-Type", UPLOAD_TYPES[extname4(file2).toLowerCase()] ?? "application/octet-stream");
50251
50527
  } else if (options.json !== void 0) {
@@ -50265,7 +50541,7 @@ async function curl(options, io) {
50265
50541
  const type = response.headers.get("content-type") ?? "";
50266
50542
  const failed = options.fail && response.status >= 400;
50267
50543
  if (options.output !== void 0 && !failed) {
50268
- const file2 = resolve16(options.output);
50544
+ const file2 = resolve17(options.output);
50269
50545
  let bytes = 0;
50270
50546
  try {
50271
50547
  await pipeline2(
@@ -50295,7 +50571,7 @@ async function curl(options, io) {
50295
50571
  }
50296
50572
 
50297
50573
  // cli/src/commands.ts
50298
- var assetModelIds = [...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id)];
50574
+ var assetModelIds = [...SPRITE_VIDEO_MODEL_IDS, ...MUSIC_MODELS.map((m) => m.id), ...SPEECH_MODELS.map((m) => m.id), ...SFX_MODELS.map((m) => m.id), ...VECTOR_MODELS.map((m) => m.id), ...MODEL_GENERATOR_IDS];
50299
50575
  var assetAuthorizationFlags = [
50300
50576
  { name: "yes", type: "boolean", description: "Authorize spending credits" },
50301
50577
  { name: "dry-run", type: "boolean", description: "Validate and estimate locally without submitting a paid job" }
@@ -50307,13 +50583,15 @@ var assetWaitFlags = [
50307
50583
  var catalogSections = {
50308
50584
  sprites: ["styles", "kinds", "actions", "videoModels"],
50309
50585
  image: ["assetTypes"],
50586
+ vector: ["assetTypes", "vectorModels", "vectorOptions"],
50310
50587
  video: ["assetTypes", "videoModels", "videoProfiles"],
50311
50588
  music: ["assetTypes", "musicModels"],
50312
50589
  speech: ["assetTypes", "speechModels", "voices", "elevenLabsVoices"],
50313
50590
  sfx: ["assetTypes", "sfxModels"],
50591
+ model: ["assetTypes", "modelGenerators", "modelAnimations"],
50314
50592
  avatars: ["avatarTypes", "avatarLookTypes"]
50315
50593
  };
50316
- var catalog = () => ({ styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS, avatarTypes: AVATAR_CREATE_TYPES, avatarLookTypes: AVATAR_LOOK_TYPES });
50594
+ var catalog = () => ({ styles: SPRITE_STYLES, kinds: SPRITE_KINDS, actions: ALL_SPRITE_ACTIONS, videoModels: SPRITE_VIDEO_MODEL_IDS, assetTypes: ASSET_TYPES, musicModels: MUSIC_MODELS.map((m) => m.id), speechModels: SPEECH_MODELS.map((m) => m.id), sfxModels: SFX_MODELS.map((m) => m.id), modelGenerators: MODEL_GENERATORS.map(({ id: id2, label: label3, faces }) => ({ id: id2, label: label3, faces })), modelAnimations: MODEL_ANIMATIONS, vectorModels: VECTOR_MODELS.map((m) => m.id), vectorOptions: { workflows: VECTOR_WORKFLOWS, qualities: VECTOR_QUALITIES, aspectRatios: VECTOR_ASPECT_RATIOS, shapes: VECTOR_SHAPES, maxReferences: MAX_VECTOR_REFERENCES }, elevenLabsVoices: ELEVENLABS_VOICES, voices: SPEECH_VOICES, videoProfiles: SPRITE_VIDEO_MODELS, avatarTypes: AVATAR_CREATE_TYPES, avatarLookTypes: AVATAR_LOOK_TYPES });
50317
50595
  var modelFlags = [
50318
50596
  { name: "video-model", type: "string", choices: SPRITE_VIDEO_MODEL_IDS, description: "Video model override for this operation" }
50319
50597
  ];
@@ -50344,12 +50622,12 @@ var workspaceQuery = async (client) => {
50344
50622
  async function openBrowser(url3) {
50345
50623
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
50346
50624
  const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url3] : [url3];
50347
- await new Promise((resolve18) => {
50625
+ await new Promise((resolve19) => {
50348
50626
  const child = spawn3(command, args, { stdio: "ignore", detached: true });
50349
- child.once("error", () => resolve18());
50627
+ child.once("error", () => resolve19());
50350
50628
  child.once("spawn", () => {
50351
50629
  child.unref();
50352
- resolve18();
50630
+ resolve19();
50353
50631
  });
50354
50632
  });
50355
50633
  }
@@ -50375,7 +50653,10 @@ function createCli() {
50375
50653
  { name: "thumbnail", type: "string", default: "auto", description: "JPEG or PNG thumbnail up to 2 MiB to set after the upload; auto uses FILM.jpg beside FILM.mp4; none skips" },
50376
50654
  { name: "yes", type: "boolean", description: "Authorize sending this video, its subtitles and thumbnail to the selected channel and privacy; confirm upload rights" },
50377
50655
  { name: "dry-run", type: "boolean", description: "Inspect MP4 and metadata locally without Google authorization or network calls" }
50378
- ).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await uploadYouTube({ ...flags, directory: args.directory, log: stderr, signal }))))).command("uploads", (uploads) => uploads.add(defineCommand("list", { description: "List local upload receipts without tokens or session URLs" }, (command) => command.action(async ({ stdout }) => out(stdout, await listYouTubeUploads())))).add(defineCommand("resume", { description: "Resume the same local MP4 and Google upload session after an interruption" }, (command) => command.args({ name: "id", type: "string", required: true, description: "Upload ID returned by youtube uploads list" }).flags({ name: "yes", type: "boolean", description: "Authorize resuming the saved upload and visibility" }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await resumeYouTube(args.id, { ...flags, log: stderr, signal }))))))).add(defineCommand("init", { description: "Install or refresh the AnimaGuru agent skill in the current folder" }, (command) => command.action(async ({ stdout }) => out(stdout, await initializeSkill())))).add(defineCommand("templates", { description: "List video categories and available starter templates" }, (command) => command.action(({ stdout }) => out(stdout, listTemplates())))).command("review", (review) => review.add(defineCommand("check", { description: "Check a film's review frames for visual faults with AnimaGuru's frame checker (Clef-omni): empty frames, cut-off or overlapping text, broken letters, hard-to-read text and a polish score. Run npm run review first; open the flagged frames first. Charges a fraction of a credit per frame" }, (command) => command.args({ name: "directory", type: "string", default: ".", description: "Video workspace or animation folder" }).flags(
50656
+ ).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await uploadYouTube({ ...flags, directory: args.directory, log: stderr, signal }))))).command("uploads", (uploads) => uploads.add(defineCommand("list", { description: "List local upload receipts without tokens or session URLs" }, (command) => command.action(async ({ stdout }) => out(stdout, await listYouTubeUploads())))).add(defineCommand("resume", { description: "Resume the same local MP4 and Google upload session after an interruption" }, (command) => command.args({ name: "id", type: "string", required: true, description: "Upload ID returned by youtube uploads list" }).flags({ name: "yes", type: "boolean", description: "Authorize resuming the saved upload and visibility" }).action(async ({ args, flags, stdout, stderr, signal }) => out(stdout, await resumeYouTube(args.id, { ...flags, log: stderr, signal }))))))).add(defineCommand("init", { description: "Install or refresh the core AnimaGuru agent skill in the current folder; skills install adds every skill" }, (command) => command.action(async ({ stdout }) => out(stdout, await initializeSkill())))).command("skills", (skills) => skills.add(defineCommand("install", { description: "Install the latest AnimaGuru and PixiJS agent skills into a folder for Claude Code (.claude/skills) and Codex (.agents/skills), with npx skills; npx skills update refreshes them" }, (command) => command.flags(
50657
+ { name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin whose skills to install" },
50658
+ { name: "dir", type: "string", default: ".", description: "Folder to install into (created if missing)" }
50659
+ ).action(async ({ flags, stdout, stderr, signal }) => out(stdout, await installSkills({ url: flags.url, dir: flags.dir, signal, log: stderr })))))).add(defineCommand("templates", { description: "List video categories and available starter templates" }, (command) => command.action(({ stdout }) => out(stdout, listTemplates())))).command("review", (review) => review.add(defineCommand("check", { description: "Check a film's review frames for visual faults with AnimaGuru's frame checker (Clef-omni): empty frames, cut-off or overlapping text, broken letters, hard-to-read text and a polish score. Run npm run review first; open the flagged frames first. Charges a fraction of a credit per frame" }, (command) => command.args({ name: "directory", type: "string", default: ".", description: "Video workspace or animation folder" }).flags(
50379
50660
  { name: "animation", type: "string", description: "Animation in a multi-animation workspace" },
50380
50661
  { name: "source", type: "string", choices: ["review", "frames"], default: "review", description: "review: npm run review's five frames; frames: the full-size frames of npm run frames -- --full" },
50381
50662
  { name: "context", type: "string", description: "What the film should show, added to its title, format and brief" },
@@ -50542,7 +50823,7 @@ Confirm code: ${device.user_code}`);
50542
50823
  })))).add(defineCommand("credits", { description: "Show the selected workspace's remaining credit balance" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).action(async ({ flags, stdout, signal }) => {
50543
50824
  const client = new Client(flags.url, signal);
50544
50825
  out(stdout, await client.request(`/api/v1/credits${await workspaceQuery(client)}`));
50545
- }))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings, speech voices and avatar types" }, (command) => command.flags({ name: "type", type: "string", choices: Object.keys(catalogSections), description: "Only one part: sprites, image, video, music, speech, sfx or avatars" }).action(({ flags, stdout }) => {
50826
+ }))).add(defineCommand("catalog", { description: "List styles, kinds, actions, asset types, models, video settings, speech voices and avatar types" }, (command) => command.flags({ name: "type", type: "string", choices: Object.keys(catalogSections), description: "Only one part: sprites, image, vector, video, music, speech, sfx, model or avatars" }).action(({ flags, stdout }) => {
50546
50827
  const full = catalog();
50547
50828
  const section = flags.type;
50548
50829
  out(stdout, section ? Object.fromEntries(catalogSections[section].map((key) => [key, full[key]])) : full);
@@ -50609,21 +50890,21 @@ Confirm code: ${device.user_code}`);
50609
50890
  ).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 }) => {
50610
50891
  if (!flags.yes) throw new Error("Pass --yes to delete this project.");
50611
50892
  out(stdout, await new Client(flags.url, signal).request(`/api/v1/projects/${encodeURIComponent(args.id)}`, { method: "DELETE" }));
50612
- })))).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(
50893
+ })))).command("assets", (assets) => assets.add(defineCommand("list", { description: "List saved assets, their blob hashes, and configured generation models" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).action(async ({ args, flags, stdout, signal }) => out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/assets`))))).add(defineCommand("generate", { description: "Generate an image, vector (SVG), video, music, speech, sound-effect or 3D model asset; --yes authorizes credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" } }).args(id).flags(
50613
50894
  { name: "type", type: "string", required: true, choices: ASSET_TYPES },
50614
50895
  { name: "name", type: "string", required: true },
50615
- { name: "prompt", type: "string", description: "Description to generate, or verbatim transcript for speech; video lip-sync and relight take none, and camera-controls, insert-video, recast and 3d-to-video may leave it out" },
50896
+ { name: "prompt", type: "string", description: "Description to generate, or verbatim transcript for speech; video lip-sync and relight and vector image-to-vector take none, and camera-controls, insert-video, recast and 3d-to-video may leave it out" },
50616
50897
  { name: "model", type: "string", choices: assetModelIds, description: "Model for the chosen asset type" },
50617
- { name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video, relight and keyframes-to-video: auto (default, the inputs' shape) or a ratio to crop to" },
50898
+ { name: "aspect-ratio", type: "string", choices: [.../* @__PURE__ */ new Set([...IMAGE_ASPECT_RATIOS, "auto", ...VIDEO_ASPECT_RATIOS, ...VECTOR_ASPECT_RATIOS])], description: "Image: 1:1 (default), 3:2, 2:3, 16:9 or 9:16; a masked edit keeps Image 1's shape. Video from text or a style without --reference: 21:9\u20139:16 (default 16:9). Extension, reference-to-video, relight and keyframes-to-video: auto (default, the inputs' shape) or a ratio to crop to. Vector from text: auto (default, the model's choice) or 1:4\u20134:1" },
50618
50899
  { name: "transparent", type: "boolean", description: "Image: transparent background" },
50619
- { name: "references", type: "string", description: "Comma-separated blob SHAs of this project's images or sprite art, named Image 1, Image 2\u2026 in the prompt. Image: up to 4 to follow, such as a character sheet. Video: subjects or looks to keep (reference-to-video and insert-video up to 9, 3d-to-video up to 8), or recast's new people, one photo each (up to 4)" },
50900
+ { name: "references", type: "string", description: "Comma-separated blob SHAs of this project's images or sprite art, named Image 1, Image 2\u2026 in the prompt. Image: up to 4 to follow, such as a character sheet. Vector from text: up to 5, the first leading. Video: subjects or looks to keep (reference-to-video and insert-video up to 9, 3d-to-video up to 8), or recast's new people, one photo each (up to 4)" },
50620
50901
  { name: "mask", type: "string", description: "Image: edit only these rectangles of Image 1 (the first --references image), as x,y,width,height fractions of its size from the top left, up to 8 separated by semicolons; the rest is kept" },
50621
- { name: "workflow", type: "string", choices: VIDEO_WORKFLOWS, description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, FLUX 3 Max keyframes-to-video, as the model's videoProfiles allow" },
50622
- { name: "reference", type: "string", description: "Video: starting image blob SHA from this project's assets or sprites; the speaking face for lip-sync, the image the camera moves around for camera-controls" },
50902
+ { name: "workflow", type: "string", choices: [...VIDEO_WORKFLOWS, ...VECTOR_WORKFLOWS], description: "Video: image-to-video (default), text-to-video or extend-video; H3 Max also reference-to-video, lip-sync, camera-controls, insert-video, recast, relight, 3d-to-video and style, FLUX 3 Max keyframes-to-video, as the model's videoProfiles allow. Vector: text-to-vector (default) or image-to-vector, which traces --source" },
50903
+ { name: "reference", type: "string", description: "Video: starting image blob SHA from this project's assets or sprites; the speaking face for lip-sync, the image the camera moves around for camera-controls. Model: the image or sprite art to build it from (image-to-3D); without one the prompt describes it" },
50623
50904
  { name: "end-reference", type: "string", description: "Video: ending image blob SHA; models with endFrameOnly accept it without --reference" },
50624
50905
  { name: "middle-reference", type: "string", description: "Reference-to-video: image blob SHA reached at --middle-seconds; needs --reference and --end-reference, 480p or 768p" },
50625
50906
  { name: "middle-seconds", type: "number", description: "Reference-to-video: when the middle image is reached, in seconds" },
50626
- { name: "source", type: "string", description: "Blob SHA of a video asset in this project: the clip to extend, insert a scene into, recast or relight, or the 3D render 3d-to-video turns into footage" },
50907
+ { name: "source", type: "string", description: "Blob SHA of a video asset in this project: the clip to extend, insert a scene into, recast or relight, or the 3D render 3d-to-video turns into footage. Vector image-to-vector: the image or sprite art to trace" },
50627
50908
  { name: "soundtrack", type: "string", description: "Video from an image or text: blob SHA of a music, speech or SFX asset pinned as the soundtrack (2 s or longer). Lip-sync: the speech to say (5 s\u201315 min), which sets the length" },
50628
50909
  { name: "audio-references", type: "string", description: "Extension or reference-to-video: up to 3 comma-separated audio asset blob SHAs (2\u201315 s each, 15 s total) guiding the sound; refer to them as Audio 1, Audio 2\u2026 in the prompt" },
50629
50910
  { name: "reference-videos", type: "string", description: "Reference-to-video or insert-video: up to 3 comma-separated video asset blob SHAs (2\u201315 s each, 15 s total) guiding motion, named Video 1, Video 2\u2026 in the prompt" },
@@ -50643,12 +50924,20 @@ Confirm code: ${device.user_code}`);
50643
50924
  { name: "resolution", type: "string", choices: VIDEO_RESOLUTIONS, description: "Video resolution supported by the selected model and workflow (default its first)" },
50644
50925
  { name: "mode", type: "string", choices: ["cost", "speed", "quality"], description: "Video generation mode supported by the selected model (default its first)" },
50645
50926
  { name: "prompt-expansion", type: "string", choices: PROMPT_EXPANSION_MODES, description: "Video: how much the model may rewrite the prompt (default disabled); extensions treat any other value as on. FLUX 3 Max always reasons about the prompt: quality thoroughly, any other value lightly" },
50646
- { name: "seed", type: "number", description: "Video: seed for repeatable results, where the model takes one" },
50927
+ { name: "seed", type: "number", description: "Video or vector from text: seed for repeatable results, where the model takes one" },
50647
50928
  { name: "extend-output", type: "string", choices: EXTEND_OUTPUTS, description: "Extension: extended (default, source plus new footage) or continuation (a 1.625 s overlap, then the new footage)" },
50648
50929
  { name: "voice", type: "string", description: "Gemini voice name or ElevenLabs voice ID (defaults: Kore / George)" },
50649
50930
  { name: "delivery", type: "string", description: "Gemini speech delivery instructions; ElevenLabs uses audio tags in the transcript" },
50650
50931
  { name: "loop", type: "boolean", description: "SFX: create a seamless loop" },
50932
+ { name: "texture", type: "boolean", description: "Model: textured (default on); --no-texture makes a plain white model" },
50933
+ { name: "pbr", type: "boolean", description: "Model: metallic, roughness and normal maps with the texture (default on); --no-pbr leaves them out" },
50934
+ { name: "faces", type: "number", description: "Model: target face count; Tripo H3.1 1,000\u20132,000,000, Hunyuan3D 40,000\u20131,500,000 (charged extra). Rigged models take at most 300,000" },
50935
+ { name: "rig", type: "boolean", description: "Model: auto-rig a humanoid character standing in a T- or A-pose, with animation clips" },
50936
+ { name: "animations", type: "string", description: "Model with --rig: up to 10 comma-separated clips, by name (catalog --type model) or Meshy library number 0\u2013696; default idle,walk" },
50937
+ { name: "height", type: "number", description: "Model with --rig: the character's height in metres (default 1.7)" },
50651
50938
  { name: "prompt-influence", type: "number", description: "SFX: prompt adherence, 0\u20131 (default 0.3)" },
50939
+ { name: "quality", type: "string", choices: VECTOR_QUALITIES, description: "Vector from text: low (fastest, 13 credits), medium (default, 16) or high (20, or 32 with --references)" },
50940
+ { name: "shapes", type: "string", choices: VECTOR_SHAPES, description: "Vector image-to-vector (10 credits): detailed (default) keeps fine detail, simple uses fewer, simpler shapes" },
50652
50941
  ...assetAuthorizationFlags,
50653
50942
  ...assetWaitFlags,
50654
50943
  { name: "download", type: "string", description: "Wait, then save the result to this file, or into this folder (existing, or ending in /) as NAME.EXT; never overwrites" }
@@ -50694,7 +50983,7 @@ Confirm code: ${device.user_code}`);
50694
50983
  out(stdout, await new Client(flags.url, signal).request(flags.look ? `${avatarsPath(args.id)}/looks/${encodeURIComponent(flags.look)}` : `${avatarsPath(args.id)}/${encodeURIComponent(flags.group)}`, { method: "DELETE" }));
50695
50984
  })))).command("jobs", (jobs) => jobs.add(defineCommand("list", { description: "List the latest generation jobs" }, (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)}/jobs`))))).add(defineCommand("submit", { description: "Submit a typed JSON job: character, animate, custom or redraw; spends credits" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).flags({ name: "file", type: "string", required: true, description: "JSON job file" }, { name: "yes", type: "boolean", required: true, description: "Authorize spending credits" }).action(async ({ args, flags, stdout, signal }) => {
50696
50985
  if (!flags.yes) throw new Error("Pass --yes to authorize spending credits.");
50697
- const body = spriteJobBody.parse({ ...JSON.parse(await readFile19(flags.file, "utf8")), ...selectedModels(flags) });
50986
+ const body = spriteJobBody.parse({ ...JSON.parse(await readFile20(flags.file, "utf8")), ...selectedModels(flags) });
50698
50987
  out(stdout, await new Client(flags.url, signal).request(`${projectPath(args.id)}/jobs`, { method: "POST", body }));
50699
50988
  }))).add(defineCommand("wait", { description: "Wait for a specific job; fail if generation fails or times out" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id, { name: "job", type: "string", required: true }).flags({ name: "timeout", type: "number", default: 600, description: "Maximum seconds to wait" }).action(async ({ args, flags, stdout, signal }) => out(stdout, { job: await waitForJob(new Client(flags.url, signal), args.id, args.job, flags.timeout, signal) }))))).add(defineCommand("models", { description: "Set the default video model for future project jobs" }, (command) => command.flags({ name: "url", type: "string", default: "https://animaguru.com", env: { name: "ANIMAGURU_URL" }, description: "Server origin" }).args(id).flags(...modelFlags).action(async ({ args, flags, stdout, signal }) => {
50700
50989
  const body = selectedModels(flags);
@@ -50709,7 +50998,7 @@ Confirm code: ${device.user_code}`);
50709
50998
  const extension = flags.file.split(".").at(-1)?.toLowerCase();
50710
50999
  const type = extension === "png" ? "image/png" : extension === "jpg" || extension === "jpeg" ? "image/jpeg" : extension === "webp" ? "image/webp" : void 0;
50711
51000
  if (!type) throw new Error("Use a PNG, JPEG or WebP image.");
50712
- const bytes = await readFile19(flags.file);
51001
+ const bytes = await readFile20(flags.file);
50713
51002
  if (bytes.length > 10 * 1024 * 1024) throw new Error("Image must be at most 10 MB.");
50714
51003
  const form = new FormData();
50715
51004
  form.set("image", new Blob([bytes], { type }), `reference.${extension}`);
@@ -50725,8 +51014,8 @@ Confirm code: ${device.user_code}`);
50725
51014
  if (!file2) throw new Error(`No file at ${args.path} in this project. List them with animaguru files list.`);
50726
51015
  if (!/^[a-f0-9]{64}$/.test(file2.sha)) throw new Error("Invalid blob hash in manifest.");
50727
51016
  const response = await client.request(`/api/v1/blobs/${encodeURIComponent(manifest.ownerId)}/${file2.sha}`, { raw: true });
50728
- const path = resolve17(flags.output);
50729
- await mkdir15(dirname14(path), { recursive: true });
51017
+ const path = resolve18(flags.output);
51018
+ await mkdir16(dirname15(path), { recursive: true });
50730
51019
  const bytes = new Uint8Array(await response.arrayBuffer());
50731
51020
  await writeFile16(path, bytes, { flag: "wx" });
50732
51021
  out(stdout, { path, bytes: bytes.length });
@@ -50740,7 +51029,7 @@ Confirm code: ${device.user_code}`);
50740
51029
  const response = await client.request(`/api/v1/blobs/${encodeURIComponent(manifest.ownerId)}/${file2.sha}`, { raw: true });
50741
51030
  entries[path2] = new Uint8Array(await response.arrayBuffer());
50742
51031
  }
50743
- const path = resolve17(flags.output);
51032
+ const path = resolve18(flags.output);
50744
51033
  await writeFile16(path, zipSync(entries, { level: 0 }), { flag: "wx" });
50745
51034
  out(stdout, { path, files: manifest.files.length });
50746
51035
  })))).extend(version(package_default.version, { format: "plain" })).extend(help()).extend(completion());