@genex-ai/cli-demo 0.55.0-dev.125 → 0.57.0-dev.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -141,7 +141,7 @@ one call (`image-main` + `image-alt-N` URLs) — pick the best instead of re-rol
141
141
  `genex character "<prompt>"` runs a controller-ready Meshy workflow through the
142
142
  same authenticated credit, BullMQ/Redis, and R2 generation pipeline. It defaults
143
143
  to a Meshy 6 textured A-pose humanoid with browser-oriented remeshing, rigging, and the
144
- validated idle/walk/run/jump pack. `--animation <id-or-query>` is repeatable;
144
+ preview-reviewed neutral-v2 idle/walk/run/crouch/jump pack. `--animation <id-or-query>` is repeatable;
145
145
  `--height`, `--polycount`, `--no-controller-pack`, and `--no-wait` adjust the
146
146
  request. The CLI prints an atomic component quote before enqueueing.
147
147
 
package/dist/index.js CHANGED
@@ -3239,6 +3239,19 @@ async function installMeshyCharacterManifest(args) {
3239
3239
  `
3240
3240
  );
3241
3241
  args.log.dim(` public/assets/meshy-character.json (${args.characterId}, current revision)`);
3242
+ const pack = manifest.controllerPack;
3243
+ if (typeof pack?.key === "string" && typeof pack.version === "number") {
3244
+ args.log.success(`Meshy controller pack ${pack.key} v${pack.version}`);
3245
+ } else {
3246
+ args.log.warn("Legacy Meshy manifest \u2014 regenerate the character for the preview-reviewed neutral-v2 locomotion pack.");
3247
+ }
3248
+ const actionIds = (manifest.clips ?? []).map((clip) => clip.actionId).filter((actionId) => typeof actionId === "number");
3249
+ args.log.dim(` installed action ids: ${actionIds.length > 0 ? actionIds.join(", ") : "none"}`);
3250
+ const slots = Object.keys(manifest.locomotion?.bindings ?? manifest.locomotion?.slots ?? {}).sort();
3251
+ args.log.dim(` locomotion slots: ${slots.length > 0 ? slots.join(", ") : "none"}`);
3252
+ const crouchCovered = slots.includes("crouch.idle") && slots.includes("crouch.forward");
3253
+ if (crouchCovered) args.log.success("Visual crouch coverage: idle + move");
3254
+ else args.log.warn("Visual crouch coverage is incomplete; physics crouch may fall back to a standing pose.");
3242
3255
  }
3243
3256
  async function installOwnerAvatar(args) {
3244
3257
  const { root, srcDir, apiUrl, token, log } = args;
@@ -12129,7 +12142,42 @@ var CURATED = {
12129
12142
  [-2]: { loop: true, motionPolicy: "controller-loop", controllerSlots: ["walk.forward"] },
12130
12143
  [-1]: { loop: true, motionPolicy: "controller-loop", controllerSlots: ["run.forward"] },
12131
12144
  [0]: { loop: true, motionPolicy: "controller-loop", controllerSlots: ["idle.default"] },
12132
- [466]: { loop: false, motionPolicy: "anchored-action", controllerSlots: ["jump.full"] }
12145
+ [243]: {
12146
+ loop: true,
12147
+ motionPolicy: "controller-loop",
12148
+ controllerSlots: ["idle.default"],
12149
+ reviewStatus: "preview-reviewed"
12150
+ },
12151
+ [466]: {
12152
+ loop: false,
12153
+ motionPolicy: "anchored-action",
12154
+ controllerSlots: ["jump.full"],
12155
+ reviewStatus: "preview-reviewed"
12156
+ },
12157
+ [613]: {
12158
+ loop: true,
12159
+ motionPolicy: "controller-loop",
12160
+ controllerSlots: ["walk.forward"],
12161
+ reviewStatus: "preview-reviewed"
12162
+ },
12163
+ [616]: {
12164
+ loop: true,
12165
+ motionPolicy: "controller-loop",
12166
+ controllerSlots: ["crouch.forward"],
12167
+ reviewStatus: "preview-reviewed"
12168
+ },
12169
+ [658]: {
12170
+ loop: true,
12171
+ motionPolicy: "controller-loop",
12172
+ controllerSlots: [],
12173
+ reviewStatus: "rejected"
12174
+ },
12175
+ [659]: {
12176
+ loop: true,
12177
+ motionPolicy: "controller-loop",
12178
+ controllerSlots: ["run.forward"],
12179
+ reviewStatus: "preview-reviewed"
12180
+ }
12133
12181
  };
12134
12182
  var LOOP_SUBCATEGORIES = /* @__PURE__ */ new Set(["Idle", "Walking", "Running", "CrouchWalking", "Swimming"]);
12135
12183
  var CHOREOGRAPHY_SUBCATEGORIES = /* @__PURE__ */ new Set([
@@ -12373,6 +12421,12 @@ async function runCharacter(opts) {
12373
12421
  }
12374
12422
  log.plain(c.bold("Character quote"));
12375
12423
  log.plain(` ${price.credits} Genex credits = base ${price.baseCredits} + actions ${price.animationCredits}`);
12424
+ if (price.controllerPackKey) {
12425
+ log.plain(
12426
+ ` controller pack ${c.cyan(price.controllerPackKey)} \xB7 provider actions ${(price.controllerActionIds ?? []).join(", ")}`
12427
+ );
12428
+ }
12429
+ log.dim(` ${price.actionsGenerated} Meshy animation task${price.actionsGenerated === 1 ? "" : "s"} in this request.`);
12376
12430
  log.plain("");
12377
12431
  await runGenerate("character", {
12378
12432
  ...opts,
@@ -12490,7 +12544,7 @@ async function runAnimationsSearch(opts) {
12490
12544
  ];
12491
12545
  log.plain(` ${c.cyan(String(result.actionId))} ${c.bold(result.name)} ${c.dim(result.key)}`);
12492
12546
  log.dim(
12493
- ` ${result.motionPolicy}${result.inPlace ? " \xB7 InPlace" : ""}${slots}${requirements.length > 0 ? ` \xB7 needs ${requirements.join(", ")}` : ""}`
12547
+ ` ${result.motionPolicy}${result.inPlace ? " \xB7 InPlace" : ""}${slots}${requirements.length > 0 ? ` \xB7 needs ${requirements.join(", ")}` : ""} \xB7 ${result.reviewStatus}`
12494
12548
  );
12495
12549
  log.dim(` preview ${result.previewUrl} \xB7 estimated additional cost ${result.estimatedCredits} credits`);
12496
12550
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.55.0-dev.125",
3
+ "version": "0.57.0-dev.129",
4
4
  "description": "Set up your ~/.claude workspace, authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,12 +9,21 @@ export type MeshyMotionPolicy =
9
9
  | "planar-root-action"
10
10
  | "choreography";
11
11
 
12
+ export type MeshyLocomotionBindingMode = "loop" | "one-shot" | "pose";
13
+
14
+ export interface MeshyLocomotionBinding {
15
+ clip: string;
16
+ mode: MeshyLocomotionBindingMode;
17
+ phase?: number;
18
+ }
19
+
12
20
  export interface MeshyCharacterManifest {
13
21
  schema: 1;
14
22
  characterId: string;
15
23
  revision: number;
16
24
  manifestVersion: number;
17
25
  rig: "meshy-biped";
26
+ controllerPack?: { key: string; version: number };
18
27
  model: {
19
28
  url: string;
20
29
  heightMeters: number | null;
@@ -37,6 +46,7 @@ export interface MeshyCharacterManifest {
37
46
  }>;
38
47
  locomotion: {
39
48
  slots: Record<string, string>;
49
+ bindings?: Record<string, MeshyLocomotionBinding>;
40
50
  fallbacks: Record<string, string>;
41
51
  nominalSpeed?: Partial<Record<"walk" | "run" | "crouch", number>>;
42
52
  playbackRate: { min: number; max: number };
@@ -77,6 +87,57 @@ function compatibleTracks(model: THREE.Object3D, clips: THREE.AnimationClip[]):
77
87
  }));
78
88
  }
79
89
 
90
+ type TrackConstructor = new (
91
+ name: string,
92
+ times: ArrayLike<number>,
93
+ values: ArrayLike<number>,
94
+ interpolation?: number,
95
+ ) => THREE.KeyframeTrack;
96
+
97
+ /** Materialize one reviewed frame as an independent mixer clip for crouch-idle. */
98
+ export function createPoseClip(source: THREE.AnimationClip, name: string, phase = 0): THREE.AnimationClip {
99
+ const sampleTime = Math.max(0, Math.min(1, phase)) * Math.max(0, source.duration);
100
+ const tracks = source.tracks.map((track) => {
101
+ const valueSize = track.getValueSize();
102
+ const sampled = new Float32Array(valueSize);
103
+ const interpolant = (track as THREE.KeyframeTrack & {
104
+ createInterpolant(result: Float32Array): { evaluate(time: number): ArrayLike<number> };
105
+ }).createInterpolant(sampled);
106
+ interpolant.evaluate(sampleTime);
107
+ const Track = track.constructor as TrackConstructor;
108
+ return new Track(track.name, [0], sampled, track.getInterpolation());
109
+ });
110
+ // A non-zero duration keeps the static pose well-defined for looped mixer actions.
111
+ return new THREE.AnimationClip(name, 1, tracks);
112
+ }
113
+
114
+ export function resolveMeshyLocomotion(
115
+ manifest: MeshyCharacterManifest,
116
+ sourceClips: readonly THREE.AnimationClip[],
117
+ ): { clips: THREE.AnimationClip[]; slots: Record<string, string>; missingBindings: string[] } {
118
+ const clips = [...sourceClips];
119
+ const slots = { ...manifest.locomotion.slots };
120
+ const missingBindings: string[] = [];
121
+ for (const [slot, binding] of Object.entries(manifest.locomotion.bindings ?? {})) {
122
+ const source = clips.find((clip) => clip.name === binding.clip);
123
+ if (!source) {
124
+ missingBindings.push(slot);
125
+ continue;
126
+ }
127
+ if (binding.mode === "pose") {
128
+ const poseName = `${binding.clip}__${slot.replaceAll(".", "_")}__pose`;
129
+ clips.push(createPoseClip(source, poseName, binding.phase));
130
+ slots[slot] = poseName;
131
+ } else {
132
+ slots[slot] = binding.clip;
133
+ }
134
+ }
135
+ for (const [slot, fallback] of Object.entries(manifest.locomotion.fallbacks)) {
136
+ if (!slots[slot] && slots[fallback]) slots[slot] = slots[fallback];
137
+ }
138
+ return { clips, slots, missingBindings };
139
+ }
140
+
80
141
  export async function loadMeshyCharacter(manifestUrl: string): Promise<MeshyCharacter> {
81
142
  const response = await fetch(manifestUrl);
82
143
  if (!response.ok) throw new Error(`[meshy-character] ${manifestUrl} returned HTTP ${response.status}`);
@@ -106,18 +167,18 @@ export async function loadMeshyCharacter(manifestUrl: string): Promise<MeshyChar
106
167
  if (compatible.length === 1) compatible[0]!.name = item.entry.key;
107
168
  clips.push(...compatible);
108
169
  }
109
- const slots = { ...manifest.locomotion.slots };
110
- for (const [slot, fallback] of Object.entries(manifest.locomotion.fallbacks)) {
111
- if (!slots[slot] && slots[fallback]) slots[slot] = slots[fallback];
170
+ const locomotion = resolveMeshyLocomotion(manifest, clips);
171
+ for (const slot of locomotion.missingBindings) {
172
+ console.warn(`[meshy-character] locomotion binding ${slot} references a clip that did not load.`);
112
173
  }
113
174
  return {
114
175
  kind: "meshy-native",
115
176
  scene: base.scene,
116
- clips,
177
+ clips: locomotion.clips,
117
178
  manifest,
118
179
  rigSignature: manifest.model.skeletonSignature,
119
180
  locomotionProfile: {
120
- slots,
181
+ slots: locomotion.slots,
121
182
  nominalSpeed: manifest.locomotion.nominalSpeed,
122
183
  playbackRate: manifest.locomotion.playbackRate,
123
184
  },
@@ -17,7 +17,8 @@ npx genex character "stylized desert courier, practical layered clothing"
17
17
  ```
18
18
 
19
19
  The default request uses Meshy 6 to generate and texture an A-pose humanoid,
20
- remeshes and rigs it, adds the validated idle/walk/run/jump controller pack,
20
+ remeshes and rigs it, adds the preview-reviewed neutral-v2
21
+ idle/walk/run/crouch/jump controller pack,
21
22
  and stores every successful model
22
23
  and clip at permanent Genex asset URLs. It prints the complete Genex-credit
23
24
  quote before enqueueing. The Meshy API key remains server-side; never ask the
@@ -35,6 +36,12 @@ npx genex wait <generation-id>
35
36
  omits the default locomotion pack. `--no-wait` returns a generation id for
36
37
  `genex wait`; it does not create a second paid request.
37
38
 
39
+ Before handoff, capture idle, walk, run, crouch-idle, crouch-move, and jump.
40
+ Inspect shoulders, elbows, wrists, and hands as well as the feet. Reject
41
+ shrugging palms-up poses, permanently raised elbows, or a gait whose
42
+ upper-body style contradicts the requested character. “No T-pose” is not
43
+ an animation-quality check.
44
+
38
45
  ## Search first; use action IDs
39
46
 
40
47
  ```bash
@@ -205,6 +205,11 @@ jumping. `crouchMode: "hold"` makes it hold-to-crouch;
205
205
  `character.setCrouch(bool)` and `character.crouchActive` are the programmatic
206
206
  hooks (that's also how a touch button wires in — see Mobile below).
207
207
 
208
+ Press every control that the HUD or handoff advertises. For Meshy characters,
209
+ crouch passes only when both the capsule behavior and a visibly crouched pose
210
+ are confirmed. Capture crouch-idle and crouch-move separately; a standing
211
+ animation over a shortened capsule is not working crouch.
212
+
208
213
  ## Mobile: TouchJoystick + VirtualButton (wire by default)
209
214
 
210
215
  **Wire these whenever this controller is installed — not only when asked.**
@@ -44,7 +44,11 @@ everything twice.
44
44
  sign-in gate.
45
45
  2. Press each documented control once (keys, pointer); assert a **visible
46
46
  response** to every one — the player moves, the camera turns, the button
47
- fires.
47
+ fires. For an animated character, capture idle, walk, run, crouch-idle,
48
+ crouch-move, and jump. Inspect shoulders, elbows, wrists, and hands as well
49
+ as the feet: a fully bound non-T-pose can still be stylistically broken.
50
+ For Meshy characters, crouch passes only when the capsule behavior and a
51
+ visibly crouched pose both respond.
48
52
  3. Capture one screenshot of live gameplay — of the **game**, not a sign-in
49
53
  gate or loading screen. A capture of the SDK's "Sign in to play" overlay is
50
54
  NOT gameplay evidence. Evidence captured in local test mode must be labeled