@genex-ai/cli-demo 1.4.1-dev.381 → 1.4.2-dev.382

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16766,6 +16766,8 @@ async function exists3(p) {
16766
16766
  }
16767
16767
 
16768
16768
  // src/commands/character.ts
16769
+ import fs21 from "fs/promises";
16770
+ import path20 from "path";
16769
16771
  function exactAnimation(selector) {
16770
16772
  const trimmed = selector.trim();
16771
16773
  if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
@@ -16845,6 +16847,86 @@ function showAmbiguity(selector, candidates, log, json = false) {
16845
16847
  }
16846
16848
  process.exitCode = 1;
16847
16849
  }
16850
+ var INSTALLED_MANIFEST = path20.join("public", "assets", "meshy-character.json");
16851
+ async function resolveAdoptTarget(selector) {
16852
+ const trimmed = selector?.trim();
16853
+ if (trimmed && !trimmed.endsWith(".json")) {
16854
+ return { ok: true, characterId: trimmed, from: "the id you gave" };
16855
+ }
16856
+ const file = trimmed ?? INSTALLED_MANIFEST;
16857
+ let raw;
16858
+ try {
16859
+ raw = await fs21.readFile(file, "utf8");
16860
+ } catch {
16861
+ return {
16862
+ ok: false,
16863
+ message: trimmed ? `Could not read ${file}.` : `No character id given, and this game has no installed character manifest at ${INSTALLED_MANIFEST}. Pass the id from the game you remixed: genex character adopt <character-id>.`
16864
+ };
16865
+ }
16866
+ let parsed;
16867
+ try {
16868
+ parsed = JSON.parse(raw);
16869
+ } catch {
16870
+ return { ok: false, message: `${file} is not valid JSON.` };
16871
+ }
16872
+ const characterId = parsed?.characterId;
16873
+ if (typeof characterId !== "string" || characterId.length === 0) {
16874
+ return { ok: false, message: `${file} carries no characterId.` };
16875
+ }
16876
+ return { ok: true, characterId, from: file };
16877
+ }
16878
+ async function runCharacterAdopt(opts) {
16879
+ const log = createLogger({ quiet: opts.quiet || opts.json });
16880
+ const target = await resolveAdoptTarget(opts.characterId);
16881
+ if (!target.ok) {
16882
+ fail2(opts, target.message, "character");
16883
+ return;
16884
+ }
16885
+ const ctx = await context(opts);
16886
+ if (!ctx) {
16887
+ fail2(opts, "Not authorized. Run `genex init` first to sign in.", "character");
16888
+ return;
16889
+ }
16890
+ const project = await readProject();
16891
+ if (!project?.id) {
16892
+ fail2(
16893
+ opts,
16894
+ "No linked game here. Run this from the game folder that remixed the original (`genex link` if it is not linked yet).",
16895
+ "character"
16896
+ );
16897
+ return;
16898
+ }
16899
+ if (!opts.json) {
16900
+ log.plain(c.bold("Adopting character"));
16901
+ log.plain(` ${target.characterId} \xB7 from ${target.from}`);
16902
+ log.dim(" Copies the body and its clips into your account. No credits \u2014 it generates nothing.");
16903
+ log.plain("");
16904
+ }
16905
+ await runWorkflow({
16906
+ opts,
16907
+ ctx,
16908
+ kind: "character",
16909
+ prompt: `Adopt character ${target.characterId}`,
16910
+ createPath: `/api/characters/${encodeURIComponent(target.characterId)}/adopt`,
16911
+ body: { projectId: project.id },
16912
+ quote: { credits: 0 },
16913
+ completed: (view) => {
16914
+ if (opts.json) {
16915
+ writeJson({
16916
+ kind: "character",
16917
+ status: view.status,
16918
+ characterId: view.id,
16919
+ adoptedFrom: target.characterId,
16920
+ nextCommand: `genex controller character --character ${view.id}`
16921
+ });
16922
+ return;
16923
+ }
16924
+ log.success(`Adopted. This character is yours now: ${c.cyan(view.id)}`);
16925
+ log.plain(` Point the game at it: ${c.cyan(`genex controller character --character ${view.id}`)}`);
16926
+ log.plain(` Then animate it: ${c.cyan(`genex character animate ${view.id} "<what it should do>"`)}`);
16927
+ }
16928
+ });
16929
+ }
16848
16930
  async function runCharacter(opts) {
16849
16931
  if (opts.directText) {
16850
16932
  await runDirectTextCharacter(opts);
@@ -17252,22 +17334,22 @@ async function context2(opts) {
17252
17334
  const project = await readProject();
17253
17335
  return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
17254
17336
  }
17255
- async function readVideo(path21, log) {
17337
+ async function readVideo(path22, log) {
17256
17338
  let bytes;
17257
17339
  try {
17258
- bytes = await readFile(path21);
17340
+ bytes = await readFile(path22);
17259
17341
  } catch {
17260
- log.error(`Can't read ${path21}.`);
17342
+ log.error(`Can't read ${path22}.`);
17261
17343
  return null;
17262
17344
  }
17263
17345
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
17264
- log.error(`${basename(path21)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
17346
+ log.error(`${basename(path22)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
17265
17347
  return null;
17266
17348
  }
17267
17349
  return bytes;
17268
17350
  }
17269
- async function uploadVideo(apiUrl, token, characterId, path21, bytes, log) {
17270
- const contentType = /\.mov$/i.test(path21) ? "video/quicktime" : "video/mp4";
17351
+ async function uploadVideo(apiUrl, token, characterId, path22, bytes, log) {
17352
+ const contentType = /\.mov$/i.test(path22) ? "video/quicktime" : "video/mp4";
17271
17353
  const minted = await apiFetch(
17272
17354
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
17273
17355
  {
@@ -17282,7 +17364,7 @@ async function uploadVideo(apiUrl, token, characterId, path21, bytes, log) {
17282
17364
  return null;
17283
17365
  }
17284
17366
  const { uploadUrl, videoUrl } = await minted.json();
17285
- log.dim(` uploading ${basename(path21)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
17367
+ log.dim(` uploading ${basename(path22)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
17286
17368
  const put = await fetch(uploadUrl, {
17287
17369
  method: "PUT",
17288
17370
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -17598,8 +17680,8 @@ function rank(items, query) {
17598
17680
  }
17599
17681
 
17600
17682
  // src/commands/motion.ts
17601
- import fs21 from "fs/promises";
17602
- import path20 from "path";
17683
+ import fs22 from "fs/promises";
17684
+ import path21 from "path";
17603
17685
 
17604
17686
  // src/lib/motion/npz.ts
17605
17687
  import zlib from "zlib";
@@ -18850,7 +18932,7 @@ async function motionGen(opts, log) {
18850
18932
  }
18851
18933
  if (opts.constraintsPath !== void 0) {
18852
18934
  try {
18853
- const raw = await fs21.readFile(opts.constraintsPath, "utf8");
18935
+ const raw = await fs22.readFile(opts.constraintsPath, "utf8");
18854
18936
  generationOptions.constraints = JSON.parse(raw);
18855
18937
  } catch {
18856
18938
  log.error(`Couldn't read the --constraints JSON at ${opts.constraintsPath}.`);
@@ -18874,10 +18956,10 @@ async function motionGen(opts, log) {
18874
18956
  async function expandTakes(selectors) {
18875
18957
  const out = [];
18876
18958
  for (const sel of selectors) {
18877
- const st = await fs21.stat(sel).catch(() => null);
18959
+ const st = await fs22.stat(sel).catch(() => null);
18878
18960
  if (st?.isDirectory()) {
18879
- const names = await fs21.readdir(sel);
18880
- for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path20.join(sel, n));
18961
+ const names = await fs22.readdir(sel);
18962
+ for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path21.join(sel, n));
18881
18963
  } else if (st?.isFile()) {
18882
18964
  out.push(sel);
18883
18965
  } else {
@@ -18912,7 +18994,7 @@ async function motionVerify(opts, log) {
18912
18994
  let gates = DEFAULT_GATES;
18913
18995
  if (opts.gatesPath) {
18914
18996
  try {
18915
- gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs21.readFile(opts.gatesPath, "utf8")));
18997
+ gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs22.readFile(opts.gatesPath, "utf8")));
18916
18998
  } catch {
18917
18999
  log.error(`Couldn't read the --gates JSON at ${opts.gatesPath}.`);
18918
19000
  process.exitCode = 1;
@@ -18934,9 +19016,9 @@ async function motionVerify(opts, log) {
18934
19016
  }
18935
19017
  const reports = [];
18936
19018
  for (const file of files) {
18937
- const stem = path20.basename(file).replace(/\.npz$/, "");
19019
+ const stem = path21.basename(file).replace(/\.npz$/, "");
18938
19020
  try {
18939
- reports.push(analyzeTake(stem, await fs21.readFile(file), gates));
19021
+ reports.push(analyzeTake(stem, await fs22.readFile(file), gates));
18940
19022
  } catch (err) {
18941
19023
  reports.push({
18942
19024
  take: stem,
@@ -18974,7 +19056,7 @@ async function motionCompile(opts, log) {
18974
19056
  let cfg = DEFAULT_MOTION_CONFIG;
18975
19057
  if (opts.configPath) {
18976
19058
  try {
18977
- const patch = JSON.parse(await fs21.readFile(opts.configPath, "utf8"));
19059
+ const patch = JSON.parse(await fs22.readFile(opts.configPath, "utf8"));
18978
19060
  cfg = { ...DEFAULT_MOTION_CONFIG, ...patch, idleLoop: { ...DEFAULT_MOTION_CONFIG.idleLoop, ...patch.idleLoop } };
18979
19061
  } catch {
18980
19062
  log.error(`Couldn't read the --config JSON at ${opts.configPath}.`);
@@ -18992,16 +19074,16 @@ async function motionCompile(opts, log) {
18992
19074
  }
18993
19075
  const inputs = [];
18994
19076
  for (const file of files) {
18995
- const stem = path20.basename(file).replace(/\.npz$/, "");
19077
+ const stem = path21.basename(file).replace(/\.npz$/, "");
18996
19078
  try {
18997
- inputs.push({ stem, take: loadTake(await fs21.readFile(file)) });
19079
+ inputs.push({ stem, take: loadTake(await fs22.readFile(file)) });
18998
19080
  } catch (err) {
18999
19081
  log.error(`${stem}: ${err instanceof Error ? err.message : String(err)}`);
19000
19082
  process.exitCode = 1;
19001
19083
  return;
19002
19084
  }
19003
19085
  }
19004
- const setName = opts.set ?? path20.basename(opts.out).replace(/\.json$/, "");
19086
+ const setName = opts.set ?? path21.basename(opts.out).replace(/\.json$/, "");
19005
19087
  let result;
19006
19088
  try {
19007
19089
  result = compileSet(inputs, setName, cfg);
@@ -19016,9 +19098,9 @@ async function motionCompile(opts, log) {
19016
19098
  process.exitCode = 1;
19017
19099
  return;
19018
19100
  }
19019
- await fs21.mkdir(path20.dirname(path20.resolve(opts.out)), { recursive: true });
19101
+ await fs22.mkdir(path21.dirname(path21.resolve(opts.out)), { recursive: true });
19020
19102
  const json = JSON.stringify(result.data);
19021
- await fs21.writeFile(opts.out, json);
19103
+ await fs22.writeFile(opts.out, json);
19022
19104
  if (opts.json) {
19023
19105
  writeJson({ out: opts.out, set: setName, tracks: Object.keys(result.data.gaits), bytes: json.length });
19024
19106
  return;
@@ -19036,9 +19118,9 @@ var MOTION_RUNTIME_FILES = [
19036
19118
  var MOTION_PRESETS = {
19037
19119
  rifle: ["sets/rifle.json", "sets/jumps.json"]
19038
19120
  };
19039
- var MOTION_DEST = path20.join("src", "motion");
19121
+ var MOTION_DEST = path21.join("src", "motion");
19040
19122
  async function motionInstall(opts, log) {
19041
- const srcDir = path20.join(getTemplatesDir(), "motion");
19123
+ const srcDir = path21.join(getTemplatesDir(), "motion");
19042
19124
  const root = opts.cwd ?? process.cwd();
19043
19125
  const preset = opts.set;
19044
19126
  if (preset !== void 0 && !MOTION_PRESETS[preset]) {
@@ -19049,21 +19131,21 @@ async function motionInstall(opts, log) {
19049
19131
  const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
19050
19132
  log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
19051
19133
  log.plain("");
19052
- log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path20.sep)}`);
19134
+ log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path21.sep)}`);
19053
19135
  let copied = 0, skipped = 0;
19054
19136
  try {
19055
19137
  for (const rel of files) {
19056
- const dest = path20.join(root, MOTION_DEST, rel);
19057
- const exists4 = await fs21.access(dest).then(() => true, () => false);
19138
+ const dest = path21.join(root, MOTION_DEST, rel);
19139
+ const exists4 = await fs22.access(dest).then(() => true, () => false);
19058
19140
  if (!opts.force && exists4) {
19059
19141
  skipped++;
19060
- log.dim(` skipped ${path20.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
19142
+ log.dim(` skipped ${path21.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
19061
19143
  continue;
19062
19144
  }
19063
- await fs21.mkdir(path20.dirname(dest), { recursive: true });
19064
- await fs21.copyFile(path20.join(srcDir, rel), dest);
19145
+ await fs22.mkdir(path21.dirname(dest), { recursive: true });
19146
+ await fs22.copyFile(path21.join(srcDir, rel), dest);
19065
19147
  copied++;
19066
- log.dim(` ${path20.join(MOTION_DEST, rel)}`);
19148
+ log.dim(` ${path21.join(MOTION_DEST, rel)}`);
19067
19149
  }
19068
19150
  } catch (err) {
19069
19151
  log.error(`Copy failed: ${String(err)}`);
@@ -19104,7 +19186,7 @@ async function motionConstraints(opts, log) {
19104
19186
  }
19105
19187
  const doc = directionConstraint(dir, speed, duration);
19106
19188
  const out = opts.out ?? "constraints.json";
19107
- await fs21.writeFile(out, JSON.stringify(doc));
19189
+ await fs22.writeFile(out, JSON.stringify(doc));
19108
19190
  if (opts.json) {
19109
19191
  writeJson({ out, dir: dirName, speed, duration, waypoints: doc[0].frame_indices.length });
19110
19192
  return;
@@ -19188,7 +19270,7 @@ ${c.bold("Usage")}
19188
19270
  genex video "<prompt>" [options] Generate a video (mp4); prints a public asset URL.
19189
19271
  genex character "<brief>" Generate 3 character concepts for user selection.
19190
19272
  genex character preview <concept-id>
19191
- Generate an unremeshed Meshy 6 preview of one
19273
+ Generate an unremeshed Meshy 7 preview of one
19192
19274
  explicitly user-approved concept candidate.
19193
19275
  genex character finalize <preview-id>
19194
19276
  Finalize an approved preview as a 10,000-face
@@ -19204,6 +19286,10 @@ ${c.bold("Usage")}
19204
19286
  multi-beat moves; --action <id-or-query> still
19205
19287
  picks a catalog clip.
19206
19288
  genex character motions <id> List the clips installed on that character.
19289
+ genex character adopt [<id>] Claim a character from a game you remixed, so it
19290
+ can take new animations. With no id it adopts the
19291
+ body this game already wears. Free \u2014 it copies,
19292
+ it does not generate.
19207
19293
  genex creature "<desc>" One-shot rigged enemy/creature (biped-shaped
19208
19294
  bodies only \u2014 Meshy rig limit): model \u2192 rig \u2192
19209
19295
  bind library clips via --animation. No approval
@@ -19649,7 +19735,7 @@ function parseArgs(argv) {
19649
19735
  } else if (parsed.command === "controller") {
19650
19736
  (parsed.options.selectors ??= []).push(arg);
19651
19737
  } else if (parsed.command === "character") {
19652
- if ((parsed.options.name === "animate" || parsed.options.name === "motions") && !parsed.options.characterId) {
19738
+ if ((parsed.options.name === "animate" || parsed.options.name === "motions" || parsed.options.name === "adopt") && !parsed.options.characterId) {
19653
19739
  parsed.options.characterId = arg;
19654
19740
  } else if (parsed.options.name === "animate") {
19655
19741
  (parsed.options.verbs ??= []).push(arg);
@@ -19657,7 +19743,7 @@ function parseArgs(argv) {
19657
19743
  parsed.options.conceptId = arg;
19658
19744
  } else if (parsed.options.name === "finalize" && !parsed.options.previewId) {
19659
19745
  parsed.options.previewId = arg;
19660
- } else if (!["animate", "motions", "preview", "finalize"].includes(parsed.options.name ?? "")) {
19746
+ } else if (!["animate", "motions", "preview", "finalize", "adopt"].includes(parsed.options.name ?? "")) {
19661
19747
  parsed.options.name = `${parsed.options.name} ${arg}`;
19662
19748
  } else {
19663
19749
  parsed.error = `Unexpected argument: ${arg}`;
@@ -20036,6 +20122,8 @@ async function main() {
20036
20122
  await runCharacterPreview(parsed.options);
20037
20123
  } else if (parsed.options.name === "finalize") {
20038
20124
  await runCharacterFinalize(parsed.options);
20125
+ } else if (parsed.options.name === "adopt") {
20126
+ await runCharacterAdopt(parsed.options);
20039
20127
  } else {
20040
20128
  await runCharacter({ ...parsed.options, prompt: parsed.options.name });
20041
20129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.4.1-dev.381",
3
+ "version": "1.4.2-dev.382",
4
4
  "description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,7 +32,10 @@ export interface MeshyCharacterManifest {
32
32
  };
33
33
  provenance?: {
34
34
  provider: "meshy";
35
- aiModel: "meshy-6";
35
+ /** Open on purpose. This type is vendored into a player's game and frozen
36
+ * there at install time, so a future model would make an older copy of
37
+ * this file describe its own manifest as invalid. */
38
+ aiModel: string;
36
39
  apiVersion?: string;
37
40
  meshyApiVersion?: string;
38
41
  poseMode: "a-pose" | "t-pose";