@genex-ai/cli-demo 1.24.0-dev.615 → 1.25.1-dev.617
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
|
@@ -5774,11 +5774,87 @@ async function promoteBuild(apiUrl, projectId, token, log) {
|
|
|
5774
5774
|
}
|
|
5775
5775
|
|
|
5776
5776
|
// src/lib/detect-features.ts
|
|
5777
|
+
import fs16 from "fs/promises";
|
|
5778
|
+
import path15 from "path";
|
|
5779
|
+
|
|
5780
|
+
// src/lib/download-assets.ts
|
|
5777
5781
|
import fs15 from "fs/promises";
|
|
5778
5782
|
import path14 from "path";
|
|
5783
|
+
var RUNG_ROLE = /@\d+$/;
|
|
5784
|
+
function isPrimaryRole(role) {
|
|
5785
|
+
return !RUNG_ROLE.test(role);
|
|
5786
|
+
}
|
|
5787
|
+
function primaryFiles(files) {
|
|
5788
|
+
return files.filter((f) => isPrimaryRole(f.role));
|
|
5789
|
+
}
|
|
5790
|
+
function localAssetStem(kind, prompt, id) {
|
|
5791
|
+
const slug = prompt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "");
|
|
5792
|
+
return `${slug || kind}-${id.slice(0, 8)}`;
|
|
5793
|
+
}
|
|
5794
|
+
function localAssetName(kind, prompt, id, file, multi) {
|
|
5795
|
+
const base = localAssetStem(kind, prompt, id);
|
|
5796
|
+
const role = multi ? `-${file.role.replace(/^(texture|image|character|model|model_anim|model-anim)-/, "")}` : "";
|
|
5797
|
+
const ext = file.ext.replace(/^\./, "") || "bin";
|
|
5798
|
+
return `${base}${role}.${ext}`;
|
|
5799
|
+
}
|
|
5800
|
+
async function downloadAssets(files, opts) {
|
|
5801
|
+
const wanted = primaryFiles(files);
|
|
5802
|
+
const result = { saved: [], failures: [] };
|
|
5803
|
+
if (wanted.length === 0) return result;
|
|
5804
|
+
try {
|
|
5805
|
+
await fs15.mkdir(opts.outDir, { recursive: true });
|
|
5806
|
+
} catch (err) {
|
|
5807
|
+
result.failures.push(
|
|
5808
|
+
`couldn't create ${opts.outDir} (${err instanceof Error ? err.message : String(err)})`
|
|
5809
|
+
);
|
|
5810
|
+
return result;
|
|
5811
|
+
}
|
|
5812
|
+
const multi = wanted.length > 1;
|
|
5813
|
+
for (const file of wanted) {
|
|
5814
|
+
const dest = path14.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
|
|
5815
|
+
try {
|
|
5816
|
+
const res = await fetch(file.url);
|
|
5817
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
5818
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
5819
|
+
await fs15.writeFile(dest, buf);
|
|
5820
|
+
result.saved.push({ role: file.role, path: dest, url: file.url, bytes: buf.byteLength });
|
|
5821
|
+
} catch (err) {
|
|
5822
|
+
result.failures.push(
|
|
5823
|
+
`${file.role}: ${err instanceof Error ? err.message : String(err)} \u2014 the asset is still live at ${file.url}`
|
|
5824
|
+
);
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
return result;
|
|
5828
|
+
}
|
|
5829
|
+
function formatBytes(bytes) {
|
|
5830
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
5831
|
+
if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
5832
|
+
return `${bytes} B`;
|
|
5833
|
+
}
|
|
5834
|
+
var DEFAULT_ASSET_DIR = "./assets";
|
|
5835
|
+
function localDeliveryFor(mode, opts, prompt) {
|
|
5836
|
+
if (mode !== "tools" || opts.noDownload) return void 0;
|
|
5837
|
+
return { outDir: opts.outDir ?? DEFAULT_ASSET_DIR, prompt };
|
|
5838
|
+
}
|
|
5839
|
+
async function undeliveredFiles(files, opts) {
|
|
5840
|
+
const wanted = primaryFiles(files);
|
|
5841
|
+
const multi = wanted.length > 1;
|
|
5842
|
+
const missing = [];
|
|
5843
|
+
for (const file of wanted) {
|
|
5844
|
+
const dest = path14.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
|
|
5845
|
+
try {
|
|
5846
|
+
await fs15.access(dest);
|
|
5847
|
+
} catch {
|
|
5848
|
+
missing.push(file);
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
return missing;
|
|
5852
|
+
}
|
|
5853
|
+
|
|
5854
|
+
// src/lib/detect-features.ts
|
|
5779
5855
|
async function detectEmbedSdkVersion(cwd = process.cwd()) {
|
|
5780
5856
|
try {
|
|
5781
|
-
const raw = await
|
|
5857
|
+
const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
|
|
5782
5858
|
const pkg = JSON.parse(raw);
|
|
5783
5859
|
const version = pkg.dependencies?.["@genex-ai/embed-sdk"] ?? pkg.devDependencies?.["@genex-ai/embed-sdk"];
|
|
5784
5860
|
return typeof version === "string" && version ? version : null;
|
|
@@ -5788,7 +5864,7 @@ async function detectEmbedSdkVersion(cwd = process.cwd()) {
|
|
|
5788
5864
|
}
|
|
5789
5865
|
async function detectMultiplayer(cwd = process.cwd()) {
|
|
5790
5866
|
try {
|
|
5791
|
-
const raw = await
|
|
5867
|
+
const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
|
|
5792
5868
|
const pkg = JSON.parse(raw);
|
|
5793
5869
|
return Boolean(
|
|
5794
5870
|
pkg.dependencies?.["@genex-ai/multiplayer"] ?? pkg.devDependencies?.["@genex-ai/multiplayer"]
|
|
@@ -5800,7 +5876,7 @@ async function detectMultiplayer(cwd = process.cwd()) {
|
|
|
5800
5876
|
async function detectMatchmaking(log, cwd = process.cwd()) {
|
|
5801
5877
|
let pkg;
|
|
5802
5878
|
try {
|
|
5803
|
-
pkg = JSON.parse(await
|
|
5879
|
+
pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
|
|
5804
5880
|
} catch (err) {
|
|
5805
5881
|
log.dim(` (skipping matchmaking \u2014 couldn't read package.json: ${String(err)})`);
|
|
5806
5882
|
return null;
|
|
@@ -5818,15 +5894,15 @@ async function detectMatchmaking(log, cwd = process.cwd()) {
|
|
|
5818
5894
|
var TOUCH_KIT_MARKERS = /controllers\/touch\/|controllers\/character\/touch-joystick|TouchJoystick|VirtualButton|DragZone|RotateOverlay/;
|
|
5819
5895
|
async function detectMobileControls(cwd = process.cwd()) {
|
|
5820
5896
|
try {
|
|
5821
|
-
const raw = await
|
|
5897
|
+
const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
|
|
5822
5898
|
const pkg = JSON.parse(raw);
|
|
5823
5899
|
if (pkg.genex?.mobileControls === true) return true;
|
|
5824
5900
|
} catch {
|
|
5825
5901
|
}
|
|
5826
|
-
const srcDir =
|
|
5902
|
+
const srcDir = path15.join(cwd, "src");
|
|
5827
5903
|
let entries;
|
|
5828
5904
|
try {
|
|
5829
|
-
entries = await
|
|
5905
|
+
entries = await fs16.readdir(srcDir, { recursive: true });
|
|
5830
5906
|
} catch {
|
|
5831
5907
|
return false;
|
|
5832
5908
|
}
|
|
@@ -5834,7 +5910,7 @@ async function detectMobileControls(cwd = process.cwd()) {
|
|
|
5834
5910
|
if (rel.includes("node_modules")) continue;
|
|
5835
5911
|
if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
|
|
5836
5912
|
try {
|
|
5837
|
-
const content = await
|
|
5913
|
+
const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
|
|
5838
5914
|
if (TOUCH_KIT_MARKERS.test(content)) return true;
|
|
5839
5915
|
} catch {
|
|
5840
5916
|
}
|
|
@@ -5843,10 +5919,10 @@ async function detectMobileControls(cwd = process.cwd()) {
|
|
|
5843
5919
|
}
|
|
5844
5920
|
var GAME_STATE_CALLS = /savePlayerState|saveWorldState|submitScore/;
|
|
5845
5921
|
async function detectGameStateUsage(cwd = process.cwd()) {
|
|
5846
|
-
const srcDir =
|
|
5922
|
+
const srcDir = path15.join(cwd, "src");
|
|
5847
5923
|
let entries;
|
|
5848
5924
|
try {
|
|
5849
|
-
entries = await
|
|
5925
|
+
entries = await fs16.readdir(srcDir, { recursive: true });
|
|
5850
5926
|
} catch {
|
|
5851
5927
|
return false;
|
|
5852
5928
|
}
|
|
@@ -5854,7 +5930,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
|
|
|
5854
5930
|
if (rel.includes("node_modules")) continue;
|
|
5855
5931
|
if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
|
|
5856
5932
|
try {
|
|
5857
|
-
const content = await
|
|
5933
|
+
const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
|
|
5858
5934
|
if (GAME_STATE_CALLS.test(content)) return true;
|
|
5859
5935
|
} catch {
|
|
5860
5936
|
}
|
|
@@ -5904,20 +5980,20 @@ var LOOP_START = /\b(?:requestAnimationFrame|setAnimationLoop)\s*\((?!\s*null\b)
|
|
|
5904
5980
|
var INIT_EMBED_CALL = /\binitEmbed\s*\(/;
|
|
5905
5981
|
async function detectEmbedBoot(cwd = process.cwd()) {
|
|
5906
5982
|
const found = { awaitsIdentity: [], callsInitEmbed: false, rendererAfterAwait: [], loopAfterAwait: [] };
|
|
5907
|
-
const srcDir =
|
|
5983
|
+
const srcDir = path15.join(cwd, "src");
|
|
5908
5984
|
let entries;
|
|
5909
5985
|
try {
|
|
5910
|
-
entries = await
|
|
5986
|
+
entries = await fs16.readdir(srcDir, { recursive: true });
|
|
5911
5987
|
} catch {
|
|
5912
5988
|
return found;
|
|
5913
5989
|
}
|
|
5914
5990
|
for (const nativeRel of entries) {
|
|
5915
5991
|
if (nativeRel.includes("node_modules")) continue;
|
|
5916
|
-
if (nativeRel.split(
|
|
5992
|
+
if (nativeRel.split(path15.sep)[0] === "controllers") continue;
|
|
5917
5993
|
if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
|
|
5918
|
-
const raw = await
|
|
5994
|
+
const raw = await fs16.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
|
|
5919
5995
|
if (!raw) continue;
|
|
5920
|
-
const rel = nativeRel.split(
|
|
5996
|
+
const rel = nativeRel.split(path15.sep).join("/");
|
|
5921
5997
|
const content = blankComments(raw);
|
|
5922
5998
|
if (INIT_EMBED_CALL.test(content)) found.callsInitEmbed = true;
|
|
5923
5999
|
const awaits = [];
|
|
@@ -6012,19 +6088,19 @@ async function detectSurfaceScan(cwd = process.cwd()) {
|
|
|
6012
6088
|
deferredAudioContext: [],
|
|
6013
6089
|
usesThree: false
|
|
6014
6090
|
};
|
|
6015
|
-
const srcDir =
|
|
6091
|
+
const srcDir = path15.join(cwd, "src");
|
|
6016
6092
|
let entries;
|
|
6017
6093
|
try {
|
|
6018
|
-
entries = await
|
|
6094
|
+
entries = await fs16.readdir(srcDir, { recursive: true });
|
|
6019
6095
|
} catch {
|
|
6020
6096
|
return found;
|
|
6021
6097
|
}
|
|
6022
6098
|
for (const nativeRel of entries) {
|
|
6023
6099
|
if (nativeRel.includes("node_modules")) continue;
|
|
6024
6100
|
if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
|
|
6025
|
-
const raw = await
|
|
6101
|
+
const raw = await fs16.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
|
|
6026
6102
|
if (!raw) continue;
|
|
6027
|
-
const rel = nativeRel.split(
|
|
6103
|
+
const rel = nativeRel.split(path15.sep).join("/");
|
|
6028
6104
|
const content = blankComments(raw);
|
|
6029
6105
|
const repeatRe = /\.repeat\.set\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/g;
|
|
6030
6106
|
let m;
|
|
@@ -6116,28 +6192,28 @@ async function detectGenerationAudit(cwd = process.cwd()) {
|
|
|
6116
6192
|
let haystack = "";
|
|
6117
6193
|
const read = async (file) => {
|
|
6118
6194
|
try {
|
|
6119
|
-
const raw = await
|
|
6120
|
-
const ext =
|
|
6195
|
+
const raw = await fs16.readFile(file, "utf8");
|
|
6196
|
+
const ext = path15.extname(file).toLowerCase();
|
|
6121
6197
|
if (ext === ".txt") return;
|
|
6122
6198
|
haystack += ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".css" ? blankComments(raw) : ext === ".html" ? raw.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, " ")) : raw;
|
|
6123
6199
|
} catch {
|
|
6124
6200
|
}
|
|
6125
6201
|
};
|
|
6126
6202
|
try {
|
|
6127
|
-
for (const entry of await
|
|
6203
|
+
for (const entry of await fs16.readdir(cwd, { withFileTypes: true })) {
|
|
6128
6204
|
if (entry.isFile() && /\.(ts|tsx|js|jsx|css|html|json)$/.test(entry.name)) {
|
|
6129
|
-
await read(
|
|
6205
|
+
await read(path15.join(cwd, entry.name));
|
|
6130
6206
|
}
|
|
6131
6207
|
}
|
|
6132
6208
|
} catch {
|
|
6133
6209
|
}
|
|
6134
6210
|
for (const sub of ["src", "public"]) {
|
|
6135
6211
|
try {
|
|
6136
|
-
const entries = await
|
|
6212
|
+
const entries = await fs16.readdir(path15.join(cwd, sub), { recursive: true });
|
|
6137
6213
|
for (const rel of entries) {
|
|
6138
6214
|
if (rel.includes("node_modules")) continue;
|
|
6139
6215
|
if (!/\.(ts|tsx|js|jsx|css|html|json|txt)$/.test(rel)) continue;
|
|
6140
|
-
await read(
|
|
6216
|
+
await read(path15.join(cwd, sub, rel));
|
|
6141
6217
|
}
|
|
6142
6218
|
} catch {
|
|
6143
6219
|
}
|
|
@@ -6149,20 +6225,21 @@ async function detectGenerationAudit(cwd = process.cwd()) {
|
|
|
6149
6225
|
});
|
|
6150
6226
|
const now = Date.now();
|
|
6151
6227
|
const stale = (e) => e.status === "queued" && now - Date.parse(e.queuedAt) > UNPICKED_AFTER_MS;
|
|
6228
|
+
const wired = (e) => haystack.includes(e.id) || haystack.includes(localAssetStem(e.kind, e.prompt, e.id));
|
|
6152
6229
|
return {
|
|
6153
|
-
unwired: audited.filter((e) => e.status === "completed" && !
|
|
6154
|
-
unpicked: audited.filter((e) => stale(e) && !
|
|
6230
|
+
unwired: audited.filter((e) => e.status === "completed" && !wired(e)).map(ref),
|
|
6231
|
+
unpicked: audited.filter((e) => stale(e) && !wired(e)).map(ref),
|
|
6155
6232
|
// Wired in but never picked up: the deterministic URL let the agent wire
|
|
6156
6233
|
// the asset before it finished, so nobody ever learned whether it DID
|
|
6157
6234
|
// finish — a provider-side failure (credits, moderation) leaves a dead URL
|
|
6158
6235
|
// that renders as a silently missing asset. Kept separate from `unpicked`
|
|
6159
6236
|
// because the fix is a status check, not wiring.
|
|
6160
|
-
unresolved: audited.filter((e) => stale(e) &&
|
|
6237
|
+
unresolved: audited.filter((e) => stale(e) && wired(e)).map(ref),
|
|
6161
6238
|
// Known-failed AND still wired: the worst case, and it must keep firing
|
|
6162
6239
|
// AFTER the status check converges the ledger to "failed" (otherwise the
|
|
6163
6240
|
// audit self-erases the moment the agent runs the recommended command and
|
|
6164
6241
|
// the dead URL ships silently anyway).
|
|
6165
|
-
deadWired: audited.filter((e) => e.status === "failed" &&
|
|
6242
|
+
deadWired: audited.filter((e) => e.status === "failed" && wired(e)).map(ref),
|
|
6166
6243
|
// One regex covers the HTML/JSX attribute and the DOM-built input alike,
|
|
6167
6244
|
// since `.type = "range"` contains `type = "range"`.
|
|
6168
6245
|
musicNoSlider: audited.some((e) => e.kind === "music" && e.status === "completed") && !/type\s*=\s*["'`]range["'`]/.test(haystack)
|
|
@@ -6307,7 +6384,7 @@ async function borrowEvidence(meta, cwd) {
|
|
|
6307
6384
|
} catch {
|
|
6308
6385
|
return true;
|
|
6309
6386
|
}
|
|
6310
|
-
const gitConfig = await
|
|
6387
|
+
const gitConfig = await fs16.readFile(path15.join(cwd, ".git", "config"), "utf8").catch(() => "");
|
|
6311
6388
|
for (const m of gitConfig.matchAll(/url\s*=\s*(\S+)/g)) {
|
|
6312
6389
|
try {
|
|
6313
6390
|
const u = new URL(m[1]);
|
|
@@ -6315,7 +6392,7 @@ async function borrowEvidence(meta, cwd) {
|
|
|
6315
6392
|
} catch {
|
|
6316
6393
|
}
|
|
6317
6394
|
}
|
|
6318
|
-
const readme = await
|
|
6395
|
+
const readme = await fs16.readFile(path15.join(cwd, "README.md"), "utf8").catch(() => "");
|
|
6319
6396
|
return readme.includes(host) || /\b(upstream|originally by|ported from|borrowed from)\b/i.test(readme);
|
|
6320
6397
|
}
|
|
6321
6398
|
|
|
@@ -6964,63 +7041,6 @@ function ceilingVerdict(input) {
|
|
|
6964
7041
|
return { allow: true };
|
|
6965
7042
|
}
|
|
6966
7043
|
|
|
6967
|
-
// src/lib/download-assets.ts
|
|
6968
|
-
import fs16 from "fs/promises";
|
|
6969
|
-
import path15 from "path";
|
|
6970
|
-
var RUNG_ROLE = /@\d+$/;
|
|
6971
|
-
function isPrimaryRole(role) {
|
|
6972
|
-
return !RUNG_ROLE.test(role);
|
|
6973
|
-
}
|
|
6974
|
-
function primaryFiles(files) {
|
|
6975
|
-
return files.filter((f) => isPrimaryRole(f.role));
|
|
6976
|
-
}
|
|
6977
|
-
function localAssetName(kind, prompt, id, file, multi) {
|
|
6978
|
-
const slug = prompt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "");
|
|
6979
|
-
const base = `${slug || kind}-${id.slice(0, 8)}`;
|
|
6980
|
-
const role = multi ? `-${file.role.replace(/^(texture|image|character|model|model_anim|model-anim)-/, "")}` : "";
|
|
6981
|
-
const ext = file.ext.replace(/^\./, "") || "bin";
|
|
6982
|
-
return `${base}${role}.${ext}`;
|
|
6983
|
-
}
|
|
6984
|
-
async function downloadAssets(files, opts) {
|
|
6985
|
-
const wanted = primaryFiles(files);
|
|
6986
|
-
const result = { saved: [], failures: [] };
|
|
6987
|
-
if (wanted.length === 0) return result;
|
|
6988
|
-
try {
|
|
6989
|
-
await fs16.mkdir(opts.outDir, { recursive: true });
|
|
6990
|
-
} catch (err) {
|
|
6991
|
-
result.failures.push(
|
|
6992
|
-
`couldn't create ${opts.outDir} (${err instanceof Error ? err.message : String(err)})`
|
|
6993
|
-
);
|
|
6994
|
-
return result;
|
|
6995
|
-
}
|
|
6996
|
-
const multi = wanted.length > 1;
|
|
6997
|
-
for (const file of wanted) {
|
|
6998
|
-
const dest = path15.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
|
|
6999
|
-
try {
|
|
7000
|
-
const res = await fetch(file.url);
|
|
7001
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
7002
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
7003
|
-
await fs16.writeFile(dest, buf);
|
|
7004
|
-
result.saved.push({ role: file.role, path: dest, url: file.url, bytes: buf.byteLength });
|
|
7005
|
-
} catch (err) {
|
|
7006
|
-
result.failures.push(
|
|
7007
|
-
`${file.role}: ${err instanceof Error ? err.message : String(err)} \u2014 the asset is still live at ${file.url}`
|
|
7008
|
-
);
|
|
7009
|
-
}
|
|
7010
|
-
}
|
|
7011
|
-
return result;
|
|
7012
|
-
}
|
|
7013
|
-
function formatBytes(bytes) {
|
|
7014
|
-
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
7015
|
-
if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
7016
|
-
return `${bytes} B`;
|
|
7017
|
-
}
|
|
7018
|
-
var DEFAULT_ASSET_DIR = "./assets";
|
|
7019
|
-
function localDeliveryFor(mode, opts, prompt) {
|
|
7020
|
-
if (mode !== "tools" || opts.noDownload) return void 0;
|
|
7021
|
-
return { outDir: opts.outDir ?? DEFAULT_ASSET_DIR, prompt };
|
|
7022
|
-
}
|
|
7023
|
-
|
|
7024
7044
|
// src/lib/lanes.ts
|
|
7025
7045
|
var LANE_CREDITS = /* @__PURE__ */ new Set(["ok", "exhausted", "unknown"]);
|
|
7026
7046
|
async function fetchLanes(apiUrl, token, timeoutMs = 4e3) {
|
|
@@ -7141,8 +7161,10 @@ async function* readSSE(body) {
|
|
|
7141
7161
|
|
|
7142
7162
|
// src/commands/generate.ts
|
|
7143
7163
|
var MOCKED_LINE = "placeholder \u2014 this lane is mocked for this run";
|
|
7164
|
+
var FIXTURE_LINE = "fixture \u2014 real bytes replayed from an earlier generation on this account; nothing was generated or billed";
|
|
7144
7165
|
function reportMocked(view, log) {
|
|
7145
|
-
if (view.mocked
|
|
7166
|
+
if (view.mocked !== true) return;
|
|
7167
|
+
log.dim(` ${view.provider === "fixture" ? FIXTURE_LINE : MOCKED_LINE}`);
|
|
7146
7168
|
}
|
|
7147
7169
|
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7148
7170
|
var INLINE_IMAGE_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
@@ -7274,6 +7296,15 @@ function applySkyboxGuard(prompt, raw) {
|
|
|
7274
7296
|
return { prompt: guarded, guarded: true, nouns };
|
|
7275
7297
|
}
|
|
7276
7298
|
var MODEL_TEXTURE_TIERS = ["standard", "detailed", "none"];
|
|
7299
|
+
var LOW_POLY_FACE_LIMIT = { min: 1e3, max: 2e4 };
|
|
7300
|
+
var LOW_POLY_QUAD_FACE_LIMIT = { min: 500, max: 1e4 };
|
|
7301
|
+
var QUAD_FACE_LIMIT_MAX = 15e4;
|
|
7302
|
+
function faceLimitBand(o) {
|
|
7303
|
+
if (o.lowPoly && o.quad) return { ...LOW_POLY_QUAD_FACE_LIMIT, why: "for --low-poly with --quad" };
|
|
7304
|
+
if (o.lowPoly) return { ...LOW_POLY_FACE_LIMIT, why: "for --low-poly" };
|
|
7305
|
+
if (o.quad) return { min: 1e3, max: QUAD_FACE_LIMIT_MAX, why: "for --quad" };
|
|
7306
|
+
return { min: 1e3, max: 2e6, why: "for a raw mesh" };
|
|
7307
|
+
}
|
|
7277
7308
|
function buildGenOptions(kind, opts) {
|
|
7278
7309
|
const options = { ...opts.generationOptions };
|
|
7279
7310
|
if (kind === "model" && opts.imageUrl) options.imageUrl = opts.imageUrl;
|
|
@@ -7284,7 +7315,15 @@ function buildGenOptions(kind, opts) {
|
|
|
7284
7315
|
if (opts.quad) options.quad = true;
|
|
7285
7316
|
if (opts.lowPoly) options.smartLowPoly = true;
|
|
7286
7317
|
if (opts.parts) options.generateParts = true;
|
|
7287
|
-
if (opts.faceLimit !== void 0)
|
|
7318
|
+
if (opts.faceLimit !== void 0) {
|
|
7319
|
+
const band = faceLimitBand({ lowPoly: !!opts.lowPoly, quad: !!opts.quad });
|
|
7320
|
+
if (opts.faceLimit < band.min || opts.faceLimit > band.max) {
|
|
7321
|
+
throw new Error(
|
|
7322
|
+
`--face-limit ${opts.faceLimit} is outside the ${band.min}-${band.max} band ${band.why} \u2014 Tripo refuses anything else for that topology. Drop --face-limit to take ${band.max}, or name a value in the band.`
|
|
7323
|
+
);
|
|
7324
|
+
}
|
|
7325
|
+
options.faceLimit = opts.faceLimit;
|
|
7326
|
+
}
|
|
7288
7327
|
if (opts.autoSize) options.autoSize = true;
|
|
7289
7328
|
}
|
|
7290
7329
|
if (kind === "texture" && opts.terrain) options.terrain = true;
|
|
@@ -7479,7 +7518,7 @@ async function runGenerate(kind, opts) {
|
|
|
7479
7518
|
log.dim(` ${prompt}`);
|
|
7480
7519
|
if (skyboxGuard?.guarded) log.dim(" \u21B3 environment-only guard applied (--raw to disable)");
|
|
7481
7520
|
log.plain("");
|
|
7482
|
-
{
|
|
7521
|
+
if (mode !== "tools") {
|
|
7483
7522
|
const counts = unshippedCounts(await readLedgerRows());
|
|
7484
7523
|
const verdict = ceilingVerdict({ kind, counts, config: ceilingConfig() });
|
|
7485
7524
|
if (!verdict.allow) {
|
|
@@ -8324,6 +8363,7 @@ async function runModelAnimate(opts) {
|
|
|
8324
8363
|
}
|
|
8325
8364
|
|
|
8326
8365
|
// src/commands/wait.ts
|
|
8366
|
+
import path18 from "path";
|
|
8327
8367
|
var TERMINAL2 = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
8328
8368
|
async function runWait(opts) {
|
|
8329
8369
|
if (opts.all) return runWaitAll(opts);
|
|
@@ -8446,8 +8486,26 @@ async function runWaitAll(opts) {
|
|
|
8446
8486
|
if (e.status === "queued" && v?.status === "completed") landed.push({ kind: e.kind, view: v });
|
|
8447
8487
|
rows.push(await toRow(e, v, cwd));
|
|
8448
8488
|
}
|
|
8489
|
+
const delivered = [];
|
|
8490
|
+
if (!opts.noDownload && await workspaceMode(cwd) === "tools") {
|
|
8491
|
+
for (const e of ledger) {
|
|
8492
|
+
const v = views.get(e.id);
|
|
8493
|
+
if (v?.status !== "completed" || !v.files?.length) continue;
|
|
8494
|
+
const local = localDeliveryFor("tools", opts, e.prompt || e.kind);
|
|
8495
|
+
if (!local) continue;
|
|
8496
|
+
const outDir = path18.join(path18.relative(process.cwd(), cwd) || ".", local.outDir);
|
|
8497
|
+
const target = { kind: e.kind, prompt: local.prompt, id: e.id, outDir };
|
|
8498
|
+
const missing = await undeliveredFiles(v.files, target);
|
|
8499
|
+
if (missing.length === 0) continue;
|
|
8500
|
+
delivered.push({ kind: e.kind, id: e.id, result: await downloadAssets(missing, target) });
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8449
8503
|
if (opts.json) {
|
|
8450
|
-
writeJson({
|
|
8504
|
+
writeJson({
|
|
8505
|
+
generations: rows,
|
|
8506
|
+
// Additive: what THIS refresh wrote to disk. Absent when nothing was.
|
|
8507
|
+
...delivered.length > 0 ? { saved: delivered.flatMap((d) => d.result.saved.map((f) => ({ id: d.id, kind: d.kind, ...f }))) } : {}
|
|
8508
|
+
});
|
|
8451
8509
|
return;
|
|
8452
8510
|
}
|
|
8453
8511
|
log.plain(c.bold("genex wait --all"));
|
|
@@ -8482,6 +8540,14 @@ async function runWaitAll(opts) {
|
|
|
8482
8540
|
if (count("running") + count("queued") > 0) {
|
|
8483
8541
|
log.dim(` Attach to any single one with: ${c.cyan("npx genex wait <id>")}`);
|
|
8484
8542
|
}
|
|
8543
|
+
if (delivered.length > 0) {
|
|
8544
|
+
log.plain("");
|
|
8545
|
+
log.plain(` ${c.bold("Picked up")} \u2014 finished generations that were not in your project yet:`);
|
|
8546
|
+
for (const d of delivered) {
|
|
8547
|
+
log.plain(` ${c.dim(`${d.kind} ${d.id}`)}`);
|
|
8548
|
+
reportLocalFiles(d.result, log, false);
|
|
8549
|
+
}
|
|
8550
|
+
}
|
|
8485
8551
|
if (landed.length > 0) {
|
|
8486
8552
|
log.plain("");
|
|
8487
8553
|
log.plain(` ${c.bold("Just landed")} \u2014 how to use each one:`);
|
|
@@ -8523,7 +8589,7 @@ async function toRow(e, v, cwd) {
|
|
|
8523
8589
|
|
|
8524
8590
|
// src/commands/controller.ts
|
|
8525
8591
|
import fs20 from "fs/promises";
|
|
8526
|
-
import
|
|
8592
|
+
import path20 from "path";
|
|
8527
8593
|
|
|
8528
8594
|
// ../../packages/meshy-animation-catalog/src/index.ts
|
|
8529
8595
|
import { createHash } from "crypto";
|
|
@@ -17654,8 +17720,8 @@ function searchMeshyAnimations(query, options = {}) {
|
|
|
17654
17720
|
|
|
17655
17721
|
// src/lib/anims.ts
|
|
17656
17722
|
import fs19 from "fs/promises";
|
|
17657
|
-
import
|
|
17658
|
-
var ANIMS_DEST =
|
|
17723
|
+
import path19 from "path";
|
|
17724
|
+
var ANIMS_DEST = path19.join("public", "assets", "anims");
|
|
17659
17725
|
var HIDDEN_TAG = "reference";
|
|
17660
17726
|
async function runAnims(opts) {
|
|
17661
17727
|
const log = createLogger({ quiet: opts.quiet });
|
|
@@ -17671,7 +17737,7 @@ async function runAnims(opts) {
|
|
|
17671
17737
|
printCatalog(log, manifest, selectors);
|
|
17672
17738
|
return;
|
|
17673
17739
|
}
|
|
17674
|
-
const controllerMarker =
|
|
17740
|
+
const controllerMarker = path19.join(root, "src", "controllers", "character");
|
|
17675
17741
|
if (!await exists2(controllerMarker)) {
|
|
17676
17742
|
log.error(
|
|
17677
17743
|
`No character controller in this game (missing ${c.cyan("src/controllers/character/")}).`
|
|
@@ -17680,11 +17746,11 @@ async function runAnims(opts) {
|
|
|
17680
17746
|
process.exitCode = 1;
|
|
17681
17747
|
return;
|
|
17682
17748
|
}
|
|
17683
|
-
const destDir =
|
|
17684
|
-
const gameManifestPath =
|
|
17749
|
+
const destDir = path19.join(root, ANIMS_DEST);
|
|
17750
|
+
const gameManifestPath = path19.join(destDir, "manifest.json");
|
|
17685
17751
|
if (opts.reset) {
|
|
17686
17752
|
await fs19.rm(destDir, { recursive: true, force: true });
|
|
17687
|
-
log.step(`Cleared ${c.cyan(ANIMS_DEST +
|
|
17753
|
+
log.step(`Cleared ${c.cyan(ANIMS_DEST + path19.sep)} (--reset)`);
|
|
17688
17754
|
}
|
|
17689
17755
|
if (selectors.length === 0) {
|
|
17690
17756
|
const installed = await readGameManifest(gameManifestPath);
|
|
@@ -17722,7 +17788,7 @@ async function runAnims(opts) {
|
|
|
17722
17788
|
}
|
|
17723
17789
|
}
|
|
17724
17790
|
const wanted = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
17725
|
-
const cacheDir =
|
|
17791
|
+
const cacheDir = path19.join(
|
|
17726
17792
|
opts.cacheDir ?? getAnimsCacheDir(),
|
|
17727
17793
|
`${manifest.library}-v${manifest.version}`
|
|
17728
17794
|
);
|
|
@@ -17734,13 +17800,13 @@ async function runAnims(opts) {
|
|
|
17734
17800
|
let addedBytes = 0;
|
|
17735
17801
|
const failures = [];
|
|
17736
17802
|
for (const entry of wanted) {
|
|
17737
|
-
const dest =
|
|
17803
|
+
const dest = path19.join(destDir, entry.file);
|
|
17738
17804
|
if (await hasSize(dest, entry.bytes)) {
|
|
17739
17805
|
presentCount++;
|
|
17740
17806
|
continue;
|
|
17741
17807
|
}
|
|
17742
17808
|
try {
|
|
17743
|
-
const cached =
|
|
17809
|
+
const cached = path19.join(cacheDir, entry.file);
|
|
17744
17810
|
if (!await hasSize(cached, entry.bytes)) {
|
|
17745
17811
|
const res = await fetch(base + entry.file);
|
|
17746
17812
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -17750,7 +17816,7 @@ async function runAnims(opts) {
|
|
|
17750
17816
|
await fs19.copyFile(cached, dest);
|
|
17751
17817
|
installedCount++;
|
|
17752
17818
|
addedBytes += entry.bytes;
|
|
17753
|
-
log.dim(` ${
|
|
17819
|
+
log.dim(` ${path19.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
|
|
17754
17820
|
} catch (err) {
|
|
17755
17821
|
failures.push(`${entry.name} (${err instanceof Error ? err.message : String(err)})`);
|
|
17756
17822
|
}
|
|
@@ -17772,7 +17838,7 @@ async function runAnims(opts) {
|
|
|
17772
17838
|
if (presentCount > 0) parts.push(`${presentCount} already present`);
|
|
17773
17839
|
if (bundledSkips > 0) parts.push(`${bundledSkips} already bundled in animation-library.glb`);
|
|
17774
17840
|
log.success(
|
|
17775
|
-
`${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST +
|
|
17841
|
+
`${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path19.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
|
|
17776
17842
|
);
|
|
17777
17843
|
for (const [selector, entries] of resolved) {
|
|
17778
17844
|
const names = entries.filter((e) => !coreNames.has(e.name)).map((e) => e.name);
|
|
@@ -17804,7 +17870,7 @@ async function loadManifest(baseOverride) {
|
|
|
17804
17870
|
}
|
|
17805
17871
|
} catch {
|
|
17806
17872
|
}
|
|
17807
|
-
const snapshotPath =
|
|
17873
|
+
const snapshotPath = path19.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
|
|
17808
17874
|
const manifest = JSON.parse(await fs19.readFile(snapshotPath, "utf8"));
|
|
17809
17875
|
return { manifest, source: "snapshot" };
|
|
17810
17876
|
}
|
|
@@ -18138,8 +18204,8 @@ var CONTROLLER_FILE_SETS = {
|
|
|
18138
18204
|
]
|
|
18139
18205
|
}
|
|
18140
18206
|
};
|
|
18141
|
-
var CODE_DEST =
|
|
18142
|
-
var ASSETS_DEST =
|
|
18207
|
+
var CODE_DEST = path20.join("src", "controllers");
|
|
18208
|
+
var ASSETS_DEST = path20.join("public", "assets");
|
|
18143
18209
|
async function runController(opts) {
|
|
18144
18210
|
const log = createLogger({ quiet: opts.quiet });
|
|
18145
18211
|
if (opts.kind?.trim() === "anims") {
|
|
@@ -18156,31 +18222,31 @@ async function runController(opts) {
|
|
|
18156
18222
|
process.exitCode = 1;
|
|
18157
18223
|
return;
|
|
18158
18224
|
}
|
|
18159
|
-
const srcDir =
|
|
18225
|
+
const srcDir = path20.join(getTemplatesDir(), "controllers");
|
|
18160
18226
|
const root = opts.cwd ?? process.cwd();
|
|
18161
18227
|
const set = CONTROLLER_FILE_SETS[kind];
|
|
18162
18228
|
log.plain(c.bold(`genex controller ${kind}`));
|
|
18163
18229
|
log.plain("");
|
|
18164
|
-
log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST +
|
|
18230
|
+
log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path20.sep)}`);
|
|
18165
18231
|
const plan = [
|
|
18166
|
-
...set.code.map((rel) => ({ from: rel, rel:
|
|
18232
|
+
...set.code.map((rel) => ({ from: rel, rel: path20.join(CODE_DEST, rel) })),
|
|
18167
18233
|
...set.assets.map((rel) => ({
|
|
18168
18234
|
from: rel,
|
|
18169
|
-
rel:
|
|
18235
|
+
rel: path20.join(ASSETS_DEST, path20.basename(rel))
|
|
18170
18236
|
}))
|
|
18171
18237
|
];
|
|
18172
18238
|
let copied = 0;
|
|
18173
18239
|
let skipped = 0;
|
|
18174
18240
|
try {
|
|
18175
18241
|
for (const file of plan) {
|
|
18176
|
-
const dest =
|
|
18242
|
+
const dest = path20.join(root, file.rel);
|
|
18177
18243
|
if (!opts.force && await exists3(dest)) {
|
|
18178
18244
|
skipped++;
|
|
18179
18245
|
log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
|
|
18180
18246
|
continue;
|
|
18181
18247
|
}
|
|
18182
|
-
await fs20.mkdir(
|
|
18183
|
-
await fs20.copyFile(
|
|
18248
|
+
await fs20.mkdir(path20.dirname(dest), { recursive: true });
|
|
18249
|
+
await fs20.copyFile(path20.join(srcDir, file.from), dest);
|
|
18184
18250
|
copied++;
|
|
18185
18251
|
log.dim(` ${file.rel}`);
|
|
18186
18252
|
}
|
|
@@ -18233,7 +18299,7 @@ async function runController(opts) {
|
|
|
18233
18299
|
for (const line of set.sketch) {
|
|
18234
18300
|
log.dim(` ${line}`);
|
|
18235
18301
|
}
|
|
18236
|
-
if (kind === "character" && !await exists3(
|
|
18302
|
+
if (kind === "character" && !await exists3(path20.join(root, ASSETS_DEST, "meshy-character.json"))) {
|
|
18237
18303
|
log.plain("");
|
|
18238
18304
|
log.plain(
|
|
18239
18305
|
` ${stepOffset + 3}. This game has no generated character yet \u2014 ${c.cyan(
|
|
@@ -18265,8 +18331,8 @@ async function installMeshyCharacterManifest(args) {
|
|
|
18265
18331
|
throw new Error("The API returned an invalid Meshy character manifest.");
|
|
18266
18332
|
}
|
|
18267
18333
|
assertCompleteMeshyControllerPack(manifest);
|
|
18268
|
-
const destination =
|
|
18269
|
-
await fs20.mkdir(
|
|
18334
|
+
const destination = path20.join(args.root, ASSETS_DEST, "meshy-character.json");
|
|
18335
|
+
await fs20.mkdir(path20.dirname(destination), { recursive: true });
|
|
18270
18336
|
await fs20.writeFile(
|
|
18271
18337
|
destination,
|
|
18272
18338
|
`${JSON.stringify(manifest, null, 2)}
|
|
@@ -18412,9 +18478,9 @@ function assertCompleteMeshyControllerPack(manifest) {
|
|
|
18412
18478
|
}
|
|
18413
18479
|
async function installFallbackAvatar(args) {
|
|
18414
18480
|
const { root, srcDir, log } = args;
|
|
18415
|
-
const dest =
|
|
18416
|
-
await fs20.mkdir(
|
|
18417
|
-
await fs20.copyFile(
|
|
18481
|
+
const dest = path20.join(root, ASSETS_DEST, "avatar.vrm");
|
|
18482
|
+
await fs20.mkdir(path20.dirname(dest), { recursive: true });
|
|
18483
|
+
await fs20.copyFile(path20.join(srcDir, "assets", "default-avatar.vrm"), dest);
|
|
18418
18484
|
log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
|
|
18419
18485
|
}
|
|
18420
18486
|
async function exists3(p) {
|
|
@@ -18428,7 +18494,7 @@ async function exists3(p) {
|
|
|
18428
18494
|
|
|
18429
18495
|
// src/commands/character.ts
|
|
18430
18496
|
import fs21 from "fs/promises";
|
|
18431
|
-
import
|
|
18497
|
+
import path21 from "path";
|
|
18432
18498
|
function exactAnimation(selector) {
|
|
18433
18499
|
const trimmed = selector.trim();
|
|
18434
18500
|
if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
|
|
@@ -18508,7 +18574,7 @@ function showAmbiguity(selector, candidates, log, json = false) {
|
|
|
18508
18574
|
}
|
|
18509
18575
|
process.exitCode = 1;
|
|
18510
18576
|
}
|
|
18511
|
-
var INSTALLED_MANIFEST =
|
|
18577
|
+
var INSTALLED_MANIFEST = path21.join("public", "assets", "meshy-character.json");
|
|
18512
18578
|
async function resolveAdoptTarget(selector) {
|
|
18513
18579
|
const trimmed = selector?.trim();
|
|
18514
18580
|
if (trimmed && !trimmed.endsWith(".json")) {
|
|
@@ -18637,7 +18703,7 @@ async function runCharacterImport(opts) {
|
|
|
18637
18703
|
opts,
|
|
18638
18704
|
ctx,
|
|
18639
18705
|
kind: "character",
|
|
18640
|
-
prompt: `Import ${
|
|
18706
|
+
prompt: `Import ${path21.basename(filePath)} as a rigged character`,
|
|
18641
18707
|
createPath: "/api/characters/import",
|
|
18642
18708
|
body,
|
|
18643
18709
|
quote: price,
|
|
@@ -19117,22 +19183,22 @@ async function context2(opts) {
|
|
|
19117
19183
|
const project = await readProject();
|
|
19118
19184
|
return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
|
|
19119
19185
|
}
|
|
19120
|
-
async function readVideo(
|
|
19186
|
+
async function readVideo(path28, log) {
|
|
19121
19187
|
let bytes;
|
|
19122
19188
|
try {
|
|
19123
|
-
bytes = await readFile(
|
|
19189
|
+
bytes = await readFile(path28);
|
|
19124
19190
|
} catch {
|
|
19125
|
-
log.error(`Can't read ${
|
|
19191
|
+
log.error(`Can't read ${path28}.`);
|
|
19126
19192
|
return null;
|
|
19127
19193
|
}
|
|
19128
19194
|
if (bytes.byteLength > MAX_VIDEO_BYTES) {
|
|
19129
|
-
log.error(`${basename(
|
|
19195
|
+
log.error(`${basename(path28)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
|
|
19130
19196
|
return null;
|
|
19131
19197
|
}
|
|
19132
19198
|
return bytes;
|
|
19133
19199
|
}
|
|
19134
|
-
async function uploadVideo(apiUrl, token, characterId,
|
|
19135
|
-
const contentType = /\.mov$/i.test(
|
|
19200
|
+
async function uploadVideo(apiUrl, token, characterId, path28, bytes, log) {
|
|
19201
|
+
const contentType = /\.mov$/i.test(path28) ? "video/quicktime" : "video/mp4";
|
|
19136
19202
|
const minted = await apiFetch(
|
|
19137
19203
|
`${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
|
|
19138
19204
|
{
|
|
@@ -19147,7 +19213,7 @@ async function uploadVideo(apiUrl, token, characterId, path27, bytes, log) {
|
|
|
19147
19213
|
return null;
|
|
19148
19214
|
}
|
|
19149
19215
|
const { uploadUrl, videoUrl } = await minted.json();
|
|
19150
|
-
log.dim(` uploading ${basename(
|
|
19216
|
+
log.dim(` uploading ${basename(path28)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
|
|
19151
19217
|
const put = await fetch(uploadUrl, {
|
|
19152
19218
|
method: "PUT",
|
|
19153
19219
|
headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
|
|
@@ -19464,7 +19530,7 @@ function rank(items, query) {
|
|
|
19464
19530
|
|
|
19465
19531
|
// src/commands/motion.ts
|
|
19466
19532
|
import fs22 from "fs/promises";
|
|
19467
|
-
import
|
|
19533
|
+
import path22 from "path";
|
|
19468
19534
|
|
|
19469
19535
|
// src/lib/motion/npz.ts
|
|
19470
19536
|
import zlib from "zlib";
|
|
@@ -20742,7 +20808,7 @@ async function expandTakes(selectors) {
|
|
|
20742
20808
|
const st = await fs22.stat(sel).catch(() => null);
|
|
20743
20809
|
if (st?.isDirectory()) {
|
|
20744
20810
|
const names = await fs22.readdir(sel);
|
|
20745
|
-
for (const n of names.sort()) if (n.endsWith(".npz")) out.push(
|
|
20811
|
+
for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path22.join(sel, n));
|
|
20746
20812
|
} else if (st?.isFile()) {
|
|
20747
20813
|
out.push(sel);
|
|
20748
20814
|
} else {
|
|
@@ -20799,7 +20865,7 @@ async function motionVerify(opts, log) {
|
|
|
20799
20865
|
}
|
|
20800
20866
|
const reports = [];
|
|
20801
20867
|
for (const file of files) {
|
|
20802
|
-
const stem =
|
|
20868
|
+
const stem = path22.basename(file).replace(/\.npz$/, "");
|
|
20803
20869
|
try {
|
|
20804
20870
|
reports.push(analyzeTake(stem, await fs22.readFile(file), gates));
|
|
20805
20871
|
} catch (err) {
|
|
@@ -20857,7 +20923,7 @@ async function motionCompile(opts, log) {
|
|
|
20857
20923
|
}
|
|
20858
20924
|
const inputs = [];
|
|
20859
20925
|
for (const file of files) {
|
|
20860
|
-
const stem =
|
|
20926
|
+
const stem = path22.basename(file).replace(/\.npz$/, "");
|
|
20861
20927
|
try {
|
|
20862
20928
|
inputs.push({ stem, take: loadTake(await fs22.readFile(file)) });
|
|
20863
20929
|
} catch (err) {
|
|
@@ -20866,7 +20932,7 @@ async function motionCompile(opts, log) {
|
|
|
20866
20932
|
return;
|
|
20867
20933
|
}
|
|
20868
20934
|
}
|
|
20869
|
-
const setName = opts.set ??
|
|
20935
|
+
const setName = opts.set ?? path22.basename(opts.out).replace(/\.json$/, "");
|
|
20870
20936
|
let result;
|
|
20871
20937
|
try {
|
|
20872
20938
|
result = compileSet(inputs, setName, cfg);
|
|
@@ -20881,7 +20947,7 @@ async function motionCompile(opts, log) {
|
|
|
20881
20947
|
process.exitCode = 1;
|
|
20882
20948
|
return;
|
|
20883
20949
|
}
|
|
20884
|
-
await fs22.mkdir(
|
|
20950
|
+
await fs22.mkdir(path22.dirname(path22.resolve(opts.out)), { recursive: true });
|
|
20885
20951
|
const json = JSON.stringify(result.data);
|
|
20886
20952
|
await fs22.writeFile(opts.out, json);
|
|
20887
20953
|
if (opts.json) {
|
|
@@ -20901,9 +20967,9 @@ var MOTION_RUNTIME_FILES = [
|
|
|
20901
20967
|
var MOTION_PRESETS = {
|
|
20902
20968
|
rifle: ["sets/rifle.json", "sets/jumps.json"]
|
|
20903
20969
|
};
|
|
20904
|
-
var MOTION_DEST =
|
|
20970
|
+
var MOTION_DEST = path22.join("src", "motion");
|
|
20905
20971
|
async function motionInstall(opts, log) {
|
|
20906
|
-
const srcDir =
|
|
20972
|
+
const srcDir = path22.join(getTemplatesDir(), "motion");
|
|
20907
20973
|
const root = opts.cwd ?? process.cwd();
|
|
20908
20974
|
const preset = opts.set;
|
|
20909
20975
|
if (preset !== void 0 && !MOTION_PRESETS[preset]) {
|
|
@@ -20914,21 +20980,21 @@ async function motionInstall(opts, log) {
|
|
|
20914
20980
|
const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
|
|
20915
20981
|
log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
|
|
20916
20982
|
log.plain("");
|
|
20917
|
-
log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST +
|
|
20983
|
+
log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path22.sep)}`);
|
|
20918
20984
|
let copied = 0, skipped = 0;
|
|
20919
20985
|
try {
|
|
20920
20986
|
for (const rel of files) {
|
|
20921
|
-
const dest =
|
|
20987
|
+
const dest = path22.join(root, MOTION_DEST, rel);
|
|
20922
20988
|
const exists5 = await fs22.access(dest).then(() => true, () => false);
|
|
20923
20989
|
if (!opts.force && exists5) {
|
|
20924
20990
|
skipped++;
|
|
20925
|
-
log.dim(` skipped ${
|
|
20991
|
+
log.dim(` skipped ${path22.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
|
|
20926
20992
|
continue;
|
|
20927
20993
|
}
|
|
20928
|
-
await fs22.mkdir(
|
|
20929
|
-
await fs22.copyFile(
|
|
20994
|
+
await fs22.mkdir(path22.dirname(dest), { recursive: true });
|
|
20995
|
+
await fs22.copyFile(path22.join(srcDir, rel), dest);
|
|
20930
20996
|
copied++;
|
|
20931
|
-
log.dim(` ${
|
|
20997
|
+
log.dim(` ${path22.join(MOTION_DEST, rel)}`);
|
|
20932
20998
|
}
|
|
20933
20999
|
} catch (err) {
|
|
20934
21000
|
log.error(`Copy failed: ${String(err)}`);
|
|
@@ -21007,12 +21073,12 @@ async function runMotion(opts) {
|
|
|
21007
21073
|
|
|
21008
21074
|
// src/commands/blender.ts
|
|
21009
21075
|
import fs23 from "fs/promises";
|
|
21010
|
-
import
|
|
21076
|
+
import path23 from "path";
|
|
21011
21077
|
var SUBS2 = ["demo", "exec", "snap", "scene", "import", "export", "reset", "mcp", "serve", "seat", "release"];
|
|
21012
21078
|
var DEFAULT_OUT_DIR = "assets/blender";
|
|
21013
21079
|
async function writeB64(dir, name, b64) {
|
|
21014
21080
|
await fs23.mkdir(dir, { recursive: true });
|
|
21015
|
-
const p =
|
|
21081
|
+
const p = path23.join(dir, name);
|
|
21016
21082
|
await fs23.writeFile(p, Buffer.from(b64, "base64"));
|
|
21017
21083
|
return p;
|
|
21018
21084
|
}
|
|
@@ -21097,7 +21163,7 @@ async function runBlender(opts) {
|
|
|
21097
21163
|
log.plain(rest.join("\n"));
|
|
21098
21164
|
return 1;
|
|
21099
21165
|
}
|
|
21100
|
-
const outDir =
|
|
21166
|
+
const outDir = path23.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
|
|
21101
21167
|
const mode = opts.mode;
|
|
21102
21168
|
if (mode !== void 0 && !isRenderMode(mode)) {
|
|
21103
21169
|
log.error(`Unknown --mode ${mode}. Use one of: ${RENDER_MODES.join(", ")}.`);
|
|
@@ -21137,13 +21203,13 @@ async function runBlender(opts) {
|
|
|
21137
21203
|
return 0;
|
|
21138
21204
|
}
|
|
21139
21205
|
case "export": {
|
|
21140
|
-
const target = opts.out ??
|
|
21206
|
+
const target = opts.out ?? path23.join(outDir, "scene.glb");
|
|
21141
21207
|
const r = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
|
|
21142
21208
|
if (!r.glbBase64) {
|
|
21143
21209
|
log.error(`/export answered with no GLB bytes${r.uploaded ? " (it was uploaded, not inlined)" : ""}`);
|
|
21144
21210
|
return 1;
|
|
21145
21211
|
}
|
|
21146
|
-
await fs23.mkdir(
|
|
21212
|
+
await fs23.mkdir(path23.dirname(target), { recursive: true });
|
|
21147
21213
|
await fs23.writeFile(target, Buffer.from(r.glbBase64, "base64"));
|
|
21148
21214
|
log.success(`Exported ${r.bytes ?? 0} bytes`);
|
|
21149
21215
|
log.plain(` ${c.cyan(target)}`);
|
|
@@ -21182,7 +21248,7 @@ async function runBlender(opts) {
|
|
|
21182
21248
|
log.error(`Can't read ${opts.input}`);
|
|
21183
21249
|
return 1;
|
|
21184
21250
|
}
|
|
21185
|
-
label =
|
|
21251
|
+
label = path23.basename(opts.input);
|
|
21186
21252
|
}
|
|
21187
21253
|
const r = await blenderCall(base, "/exec", { script, ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
|
|
21188
21254
|
if (r.stdout?.trim()) log.plain(r.stdout.trimEnd());
|
|
@@ -21303,7 +21369,7 @@ print(f"castle: {n} objects")
|
|
|
21303
21369
|
// src/commands/asset-new.ts
|
|
21304
21370
|
import fs24 from "fs";
|
|
21305
21371
|
import fsp from "fs/promises";
|
|
21306
|
-
import
|
|
21372
|
+
import path24 from "path";
|
|
21307
21373
|
import { pathToFileURL } from "url";
|
|
21308
21374
|
var EXTRA_FILES = [
|
|
21309
21375
|
"genex-asset.example.json",
|
|
@@ -21397,7 +21463,7 @@ Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-buil
|
|
|
21397
21463
|
}
|
|
21398
21464
|
async function runAssetNew(options) {
|
|
21399
21465
|
const log = createLogger();
|
|
21400
|
-
const cwd = options.dir ?
|
|
21466
|
+
const cwd = options.dir ? path24.resolve(options.dir) : process.cwd();
|
|
21401
21467
|
const slug = options.assetSlug;
|
|
21402
21468
|
if (!slug) {
|
|
21403
21469
|
log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
|
|
@@ -21407,14 +21473,14 @@ async function runAssetNew(options) {
|
|
|
21407
21473
|
log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
|
|
21408
21474
|
return 1;
|
|
21409
21475
|
}
|
|
21410
|
-
const templateDir =
|
|
21476
|
+
const templateDir = path24.join(getTemplatesDir(), "asset-viewer");
|
|
21411
21477
|
if (!fs24.existsSync(templateDir)) {
|
|
21412
21478
|
log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
|
|
21413
21479
|
return 1;
|
|
21414
21480
|
}
|
|
21415
|
-
const manifestTools = await import(pathToFileURL(
|
|
21481
|
+
const manifestTools = await import(pathToFileURL(path24.join(templateDir, "tools", "emit-manifest.mjs")).href);
|
|
21416
21482
|
const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
|
|
21417
|
-
const lockPath =
|
|
21483
|
+
const lockPath = path24.join(templateDir, "shared-files.sha256.json");
|
|
21418
21484
|
const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
|
|
21419
21485
|
const actual = hashSharedFiles(templateDir);
|
|
21420
21486
|
const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
|
|
@@ -21427,7 +21493,7 @@ async function runAssetNew(options) {
|
|
|
21427
21493
|
const triBand = parseBand(options.triBand ?? "500-8000");
|
|
21428
21494
|
const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
|
|
21429
21495
|
const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
|
|
21430
|
-
const outDir =
|
|
21496
|
+
const outDir = path24.resolve(cwd, options.out ?? slug);
|
|
21431
21497
|
if (fs24.existsSync(outDir) && fs24.readdirSync(outDir).length > 0 && !options.force) {
|
|
21432
21498
|
log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
|
|
21433
21499
|
return 1;
|
|
@@ -21456,24 +21522,24 @@ async function runAssetNew(options) {
|
|
|
21456
21522
|
};
|
|
21457
21523
|
await fsp.mkdir(outDir, { recursive: true });
|
|
21458
21524
|
for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
|
|
21459
|
-
const to =
|
|
21460
|
-
await fsp.mkdir(
|
|
21461
|
-
await fsp.copyFile(
|
|
21525
|
+
const to = path24.join(outDir, rel);
|
|
21526
|
+
await fsp.mkdir(path24.dirname(to), { recursive: true });
|
|
21527
|
+
await fsp.copyFile(path24.join(templateDir, rel), to);
|
|
21462
21528
|
}
|
|
21463
|
-
const pkg = fillTemplate(await fsp.readFile(
|
|
21529
|
+
const pkg = fillTemplate(await fsp.readFile(path24.join(templateDir, "package.json"), "utf8"), {
|
|
21464
21530
|
slug,
|
|
21465
21531
|
name,
|
|
21466
21532
|
version
|
|
21467
21533
|
});
|
|
21468
|
-
await fsp.writeFile(
|
|
21469
|
-
await fsp.writeFile(
|
|
21470
|
-
await fsp.writeFile(
|
|
21534
|
+
await fsp.writeFile(path24.join(outDir, "package.json"), pkg, "utf8");
|
|
21535
|
+
await fsp.writeFile(path24.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
21536
|
+
await fsp.writeFile(path24.join(outDir, ".gitignore"), GITIGNORE, "utf8");
|
|
21471
21537
|
await fsp.writeFile(
|
|
21472
|
-
|
|
21538
|
+
path24.join(outDir, "DESIGN.md"),
|
|
21473
21539
|
designDoc({ name, slug, sizeMeters, triBand, holder }),
|
|
21474
21540
|
"utf8"
|
|
21475
21541
|
);
|
|
21476
|
-
const placeholder = await fsp.readFile(
|
|
21542
|
+
const placeholder = await fsp.readFile(path24.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
|
|
21477
21543
|
const seeded = seedAssetSource(placeholder, {
|
|
21478
21544
|
slug,
|
|
21479
21545
|
name,
|
|
@@ -21484,8 +21550,8 @@ async function runAssetNew(options) {
|
|
|
21484
21550
|
pascalCase
|
|
21485
21551
|
});
|
|
21486
21552
|
const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
|
|
21487
|
-
await fsp.mkdir(
|
|
21488
|
-
await fsp.writeFile(
|
|
21553
|
+
await fsp.mkdir(path24.join(outDir, "src", "asset"), { recursive: true });
|
|
21554
|
+
await fsp.writeFile(path24.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
|
|
21489
21555
|
const copied = hashSharedFiles(outDir);
|
|
21490
21556
|
const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
|
|
21491
21557
|
if (mismatched.length) {
|
|
@@ -21493,7 +21559,7 @@ async function runAssetNew(options) {
|
|
|
21493
21559
|
return 1;
|
|
21494
21560
|
}
|
|
21495
21561
|
await fsp.writeFile(
|
|
21496
|
-
|
|
21562
|
+
path24.join(outDir, PARITY_FILENAME),
|
|
21497
21563
|
JSON.stringify(
|
|
21498
21564
|
{
|
|
21499
21565
|
note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
|
|
@@ -21537,11 +21603,11 @@ async function runAssetNew(options) {
|
|
|
21537
21603
|
}
|
|
21538
21604
|
|
|
21539
21605
|
// src/commands/tools.ts
|
|
21540
|
-
import
|
|
21606
|
+
import path27 from "path";
|
|
21541
21607
|
|
|
21542
21608
|
// src/lib/local-install.ts
|
|
21543
21609
|
import fs25 from "fs/promises";
|
|
21544
|
-
import
|
|
21610
|
+
import path25 from "path";
|
|
21545
21611
|
import { spawn as spawn4 } from "child_process";
|
|
21546
21612
|
var CLI_PACKAGE = "@genex-ai/cli-demo";
|
|
21547
21613
|
var FULL_NAME_FALLBACK = `npx ${CLI_PACKAGE}@${CLI_CHANNEL}`;
|
|
@@ -21561,10 +21627,10 @@ async function exists4(p) {
|
|
|
21561
21627
|
}
|
|
21562
21628
|
}
|
|
21563
21629
|
async function detectPackageManager(cwd) {
|
|
21564
|
-
let dir =
|
|
21630
|
+
let dir = path25.resolve(cwd);
|
|
21565
21631
|
for (; ; ) {
|
|
21566
21632
|
try {
|
|
21567
|
-
const raw = await fs25.readFile(
|
|
21633
|
+
const raw = await fs25.readFile(path25.join(dir, "package.json"), "utf8");
|
|
21568
21634
|
const pm = JSON.parse(raw).packageManager;
|
|
21569
21635
|
if (typeof pm === "string") {
|
|
21570
21636
|
const name = pm.split("@")[0];
|
|
@@ -21573,18 +21639,18 @@ async function detectPackageManager(cwd) {
|
|
|
21573
21639
|
} catch {
|
|
21574
21640
|
}
|
|
21575
21641
|
for (const [file, pm] of LOCKFILES) {
|
|
21576
|
-
if (await exists4(
|
|
21642
|
+
if (await exists4(path25.join(dir, file))) return pm;
|
|
21577
21643
|
}
|
|
21578
|
-
const parent =
|
|
21644
|
+
const parent = path25.dirname(dir);
|
|
21579
21645
|
if (parent === dir) return "npm";
|
|
21580
21646
|
dir = parent;
|
|
21581
21647
|
}
|
|
21582
21648
|
}
|
|
21583
21649
|
async function findLocalCli(cwd) {
|
|
21584
|
-
let dir =
|
|
21650
|
+
let dir = path25.resolve(cwd);
|
|
21585
21651
|
for (; ; ) {
|
|
21586
|
-
if (await exists4(
|
|
21587
|
-
const parent =
|
|
21652
|
+
if (await exists4(path25.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
|
|
21653
|
+
const parent = path25.dirname(dir);
|
|
21588
21654
|
if (parent === dir) return null;
|
|
21589
21655
|
dir = parent;
|
|
21590
21656
|
}
|
|
@@ -21602,7 +21668,7 @@ function installArgs(pm, spec) {
|
|
|
21602
21668
|
}
|
|
21603
21669
|
}
|
|
21604
21670
|
function manifestName(cwd) {
|
|
21605
|
-
const slug =
|
|
21671
|
+
const slug = path25.basename(path25.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
|
|
21606
21672
|
return slug || "genex-tools-workspace";
|
|
21607
21673
|
}
|
|
21608
21674
|
function isSourceRun(moduleUrl = import.meta.url) {
|
|
@@ -21660,10 +21726,10 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
|
|
|
21660
21726
|
return;
|
|
21661
21727
|
}
|
|
21662
21728
|
const pm = await detectPackageManager(cwd);
|
|
21663
|
-
const hadManifest = await exists4(
|
|
21729
|
+
const hadManifest = await exists4(path25.join(cwd, "package.json"));
|
|
21664
21730
|
if (!hadManifest) {
|
|
21665
21731
|
await fs25.writeFile(
|
|
21666
|
-
|
|
21732
|
+
path25.join(cwd, "package.json"),
|
|
21667
21733
|
JSON.stringify({ name: manifestName(cwd), private: true }, null, 2) + "\n"
|
|
21668
21734
|
);
|
|
21669
21735
|
await ensureIgnored(cwd, "node_modules/");
|
|
@@ -21683,7 +21749,7 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
|
|
|
21683
21749
|
}
|
|
21684
21750
|
}
|
|
21685
21751
|
async function ensureIgnored(dir, entry) {
|
|
21686
|
-
const file =
|
|
21752
|
+
const file = path25.join(dir, ".gitignore");
|
|
21687
21753
|
let content = "";
|
|
21688
21754
|
try {
|
|
21689
21755
|
content = await fs25.readFile(file, "utf8");
|
|
@@ -21696,7 +21762,7 @@ async function ensureIgnored(dir, entry) {
|
|
|
21696
21762
|
}
|
|
21697
21763
|
|
|
21698
21764
|
// src/commands/doctor.ts
|
|
21699
|
-
import
|
|
21765
|
+
import path26 from "path";
|
|
21700
21766
|
var LANE_ORDER = [
|
|
21701
21767
|
"model",
|
|
21702
21768
|
"image",
|
|
@@ -21977,7 +22043,7 @@ async function fetchLegalStatus(apiUrl, token) {
|
|
|
21977
22043
|
}
|
|
21978
22044
|
async function firstSkillsMarker() {
|
|
21979
22045
|
for (const target of resolveAgentTargets()) {
|
|
21980
|
-
const marker = await readSkillsMarker(
|
|
22046
|
+
const marker = await readSkillsMarker(path26.join(target.baseDir, "skills"));
|
|
21981
22047
|
if (marker) return marker;
|
|
21982
22048
|
}
|
|
21983
22049
|
return null;
|
|
@@ -22016,8 +22082,8 @@ async function runTools(opts) {
|
|
|
22016
22082
|
let totalNew = 0;
|
|
22017
22083
|
let totalUpdated = 0;
|
|
22018
22084
|
for (const t of targets) {
|
|
22019
|
-
const dest =
|
|
22020
|
-
const { copied, updated } = await copyTemplates(
|
|
22085
|
+
const dest = path27.join(t.baseDir, "skills");
|
|
22086
|
+
const { copied, updated } = await copyTemplates(path27.join(templatesDir, "skills"), dest, {
|
|
22021
22087
|
filter: (rel) => skillFamilyFilter("tools")(`skills/${rel}`)
|
|
22022
22088
|
});
|
|
22023
22089
|
await pruneRemovedSkills(dest, log);
|
|
@@ -22314,8 +22380,12 @@ ${c.bold("Options for the generators (`model` `sfx` `music` `voice` `texture` `i
|
|
|
22314
22380
|
--geometry <tier> (model) standard (default) | detailed (+20 credits, hero pieces).
|
|
22315
22381
|
--quad (model) quad-dominant mesh (+5; face limit \u2264150000).
|
|
22316
22382
|
--low-poly (model) smart low-poly topology (+10) \u2014 game-ready meshes.
|
|
22383
|
+
Holds --face-limit to 1000-20000 (500-10000 with --quad);
|
|
22384
|
+
omit it to take 20000. Runs a post-process after the mesh:
|
|
22385
|
+
allow up to 30 minutes.
|
|
22317
22386
|
--parts (model) separated, named parts at generation (+20).
|
|
22318
|
-
--face-limit <n> (model) cap on the raw mesh, 1000-2000000 (default 150000
|
|
22387
|
+
--face-limit <n> (model) cap on the raw mesh, 1000-2000000 (default 150000;
|
|
22388
|
+
see --low-poly for its band).
|
|
22319
22389
|
--auto-size (model) scale to real-world metres by AI estimate.
|
|
22320
22390
|
--granularity <g> (model segment) part granularity: simple | balanced |
|
|
22321
22391
|
detailed (default balanced).
|
|
@@ -23301,8 +23371,8 @@ function applyValueFlag(options, flag, value) {
|
|
|
23301
23371
|
break;
|
|
23302
23372
|
case "--face-limit": {
|
|
23303
23373
|
const n = Number(value);
|
|
23304
|
-
if (!Number.isInteger(n) || n <
|
|
23305
|
-
throw new Error(`Invalid --face-limit value: ${value} (expected 1000-2000000)`);
|
|
23374
|
+
if (!Number.isInteger(n) || n < 500 || n > 2e6) {
|
|
23375
|
+
throw new Error(`Invalid --face-limit value: ${value} (expected 1000-2000000; 500-10000 with --low-poly --quad)`);
|
|
23306
23376
|
}
|
|
23307
23377
|
options.faceLimit = n;
|
|
23308
23378
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.1-dev.617",
|
|
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": {
|
|
@@ -283,7 +283,9 @@ scene is a ghost: players and objects pass straight through it.
|
|
|
283
283
|
the camera sits on; never for a crate), `--quad` (+5, quad-dominant mesh
|
|
284
284
|
for anything you will deform or edit further; face limit ≤150000),
|
|
285
285
|
`--low-poly` (+10, smart low-poly topology — the game-ready choice for
|
|
286
|
-
props that appear in numbers
|
|
286
|
+
props that appear in numbers; it holds `--face-limit` to 1000-20000,
|
|
287
|
+
500-10000 with `--quad` — omit the flag to take 20000 — and runs a
|
|
288
|
+
post-process after the mesh, so allow up to 30 minutes), `--parts` (+20, separated named parts at
|
|
287
289
|
generation — cheaper than `model segment` when you know up front you need
|
|
288
290
|
doors, wheels, magazines), `--face-limit <n>` (1000-2000000, default
|
|
289
291
|
150000; the raw cap — the game still loads the @2048/@1024 rungs),
|
|
@@ -42,14 +42,14 @@ npx genex model rig <model-id> --type quadruped
|
|
|
42
42
|
npx genex model animate <rig-id> --preset walk,run
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
- **`segment`** — one GLB
|
|
45
|
+
- **`segment`** — one GLB split into addressable parts: doors, turrets, magazines, destructibles. `--granularity simple|balanced|detailed`. The parts are named by index (`tripo_part_0`, `tripo_part_1`, …), not by what they are — find the one you want by its bounds (the sails are the part with the widest extent, the door the lowest thin one), then keep that index in your manifest.
|
|
46
46
|
- **`rig`** — a skeleton for any mesh, across 7 body plans: `biped`, `quadruped`, `hexapod`, `octopod`, `avian`, `serpentine`, `aquatic`. The plan is auto-detected; `--type` picks it. A mesh that cannot be rigged is refused and refunded before the paid step.
|
|
47
47
|
- **`animate`** — retarget ready-made clips onto a rig, billed per clip. biped: `idle|walk|run|dive|climb|jump|slash|shoot|hurt|fall|turn`; quadruped/hexapod/octopod: `walk`; serpentine/aquatic: `march`.
|
|
48
48
|
|
|
49
49
|
## Options
|
|
50
50
|
|
|
51
51
|
- `--image <path|url>` — build the model from a reference image (local file ≤ 4 MB, or a previous generation's URL). The prompt becomes optional.
|
|
52
|
-
- **Quality knobs** (Tripo H3.1, each priced in the quote — pick per asset, say it in one line): `--texture standard|detailed|none` (detailed default, +10 over standard; none = geometry only), `--geometry detailed` (+20, hero pieces only), `--quad` (+5, for meshes you will edit; face limit ≤150000), `--low-poly` (+10, game-ready topology for props in numbers), `--parts` (+20, named parts at generation), `--face-limit <n>` (1000-2000000, default 150000), `--auto-size` (real-world metres).
|
|
52
|
+
- **Quality knobs** (Tripo H3.1, each priced in the quote — pick per asset, say it in one line): `--texture standard|detailed|none` (detailed default, +10 over standard; none = geometry only), `--geometry detailed` (+20, hero pieces only), `--quad` (+5, for meshes you will edit; face limit ≤150000), `--low-poly` (+10, game-ready topology for props in numbers — it holds `--face-limit` to 1000-20000, 500-10000 with `--quad`; omit the flag to take 20000; it runs a post-process after the mesh, so allow up to 30 minutes), `--parts` (+20, named parts at generation), `--face-limit <n>` (1000-2000000, default 150000; see `--low-poly` for its band), `--auto-size` (real-world metres).
|
|
53
53
|
- `--out-dir <dir>` — where the file lands (default `./assets`).
|
|
54
54
|
- `--no-download` — print the URL only.
|
|
55
55
|
- `--no-wait` — enqueue and return; pick it up with `npx genex wait <id>`.
|
|
@@ -65,9 +65,21 @@ Live prices and your balance: `npx genex doctor`.
|
|
|
65
65
|
|
|
66
66
|
Models take the longest of any lane. `--no-wait` is the normal way to run
|
|
67
67
|
several at once: enqueue them all, keep building, then `npx genex wait --all`
|
|
68
|
-
for one status line each
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
for one status line each — in a tools workspace it also downloads every
|
|
69
|
+
finished model that is not in `./assets` yet, so one call after a break (or a
|
|
70
|
+
restarted session) delivers the whole batch. `npx genex wait <id>` picks one
|
|
71
|
+
up. **Re-running the model command bills a NEW model** — never use it as a
|
|
72
|
+
status check. Five or so in flight at a time is the provider's comfortable
|
|
73
|
+
concurrency; a burst beyond that waits on the server side rather than failing.
|
|
74
|
+
|
|
75
|
+
## Placing a model
|
|
76
|
+
|
|
77
|
+
A generated GLB has no shared "front": one building's door faces −x, the next
|
|
78
|
+
one's +z. Do not guess and do not spend a render per side — read the mesh once
|
|
79
|
+
on load (bounding box, and where the detail is: the door, the counter, the
|
|
80
|
+
opening) or check it in a viewer, then record a per-model `front` (a yaw in
|
|
81
|
+
your manifest) beside its path and apply it when you place it. Ask for a facing
|
|
82
|
+
in the prompt too (`"…front toward +Z"`) — it helps, it does not guarantee.
|
|
71
83
|
|
|
72
84
|
## Troubleshooting
|
|
73
85
|
|