@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(
|
|
17337
|
+
async function readVideo(path22, log) {
|
|
17256
17338
|
let bytes;
|
|
17257
17339
|
try {
|
|
17258
|
-
bytes = await readFile(
|
|
17340
|
+
bytes = await readFile(path22);
|
|
17259
17341
|
} catch {
|
|
17260
|
-
log.error(`Can't read ${
|
|
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(
|
|
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,
|
|
17270
|
-
const contentType = /\.mov$/i.test(
|
|
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(
|
|
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
|
|
17602
|
-
import
|
|
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
|
|
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
|
|
18959
|
+
const st = await fs22.stat(sel).catch(() => null);
|
|
18878
18960
|
if (st?.isDirectory()) {
|
|
18879
|
-
const names = await
|
|
18880
|
-
for (const n of names.sort()) if (n.endsWith(".npz")) out.push(
|
|
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
|
|
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 =
|
|
19019
|
+
const stem = path21.basename(file).replace(/\.npz$/, "");
|
|
18938
19020
|
try {
|
|
18939
|
-
reports.push(analyzeTake(stem, await
|
|
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
|
|
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 =
|
|
19077
|
+
const stem = path21.basename(file).replace(/\.npz$/, "");
|
|
18996
19078
|
try {
|
|
18997
|
-
inputs.push({ stem, take: loadTake(await
|
|
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 ??
|
|
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
|
|
19101
|
+
await fs22.mkdir(path21.dirname(path21.resolve(opts.out)), { recursive: true });
|
|
19020
19102
|
const json = JSON.stringify(result.data);
|
|
19021
|
-
await
|
|
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 =
|
|
19121
|
+
var MOTION_DEST = path21.join("src", "motion");
|
|
19040
19122
|
async function motionInstall(opts, log) {
|
|
19041
|
-
const srcDir =
|
|
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 +
|
|
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 =
|
|
19057
|
-
const exists4 = await
|
|
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 ${
|
|
19142
|
+
log.dim(` skipped ${path21.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
|
|
19061
19143
|
continue;
|
|
19062
19144
|
}
|
|
19063
|
-
await
|
|
19064
|
-
await
|
|
19145
|
+
await fs22.mkdir(path21.dirname(dest), { recursive: true });
|
|
19146
|
+
await fs22.copyFile(path21.join(srcDir, rel), dest);
|
|
19065
19147
|
copied++;
|
|
19066
|
-
log.dim(` ${
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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";
|