@genex-ai/cli-demo 1.5.2-dev.397 → 1.6.0-dev.399
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 +288 -10
- package/package.json +1 -1
- package/templates/asset-viewer/asset.config.json +25 -0
- package/templates/asset-viewer/genex-asset.example.json +222 -0
- package/templates/asset-viewer/index.html +200 -0
- package/templates/asset-viewer/package.json +24 -0
- package/templates/asset-viewer/public/fonts/Geist-variable.woff2 +0 -0
- package/templates/asset-viewer/public/fonts/GeistMono-variable.woff2 +0 -0
- package/templates/asset-viewer/shared-files.sha256.json +14 -0
- package/templates/asset-viewer/src/asset/PLACEHOLDER.ts +237 -0
- package/templates/asset-viewer/src/main.js +149 -0
- package/templates/asset-viewer/src/viewer/gates-overlay.js +521 -0
- package/templates/asset-viewer/src/viewer/hud.js +73 -0
- package/templates/asset-viewer/src/viewer/stage.js +760 -0
- package/templates/asset-viewer/tools/emit-manifest.mjs +653 -0
- package/templates/asset-viewer/tools/gates.mjs +682 -0
- package/templates/asset-viewer/tools/stamp-manifest.mjs +134 -0
- package/templates/asset-viewer/vite.config.js +24 -0
- package/templates/skills/genex-asset-author/SKILL.md +101 -0
- package/templates/skills/genex-game-director/references/routing-map.md +1 -0
package/dist/index.js
CHANGED
|
@@ -1238,7 +1238,7 @@ async function syncSkillsForTarget(target, templatesDir = getTemplatesDir(), ver
|
|
|
1238
1238
|
if (await readSkillsMarker(skillsDir) === version) return false;
|
|
1239
1239
|
const src = target.full ? templatesDir : path7.join(templatesDir, "skills");
|
|
1240
1240
|
const dest = target.full ? target.baseDir : skillsDir;
|
|
1241
|
-
await copyTemplates(src, dest, { exclude: ["controllers", "motion"] });
|
|
1241
|
+
await copyTemplates(src, dest, { exclude: ["controllers", "motion", "asset-viewer"] });
|
|
1242
1242
|
await pruneRemovedSkills(skillsDir, log);
|
|
1243
1243
|
await writeSkillsMarker(skillsDir, version);
|
|
1244
1244
|
return true;
|
|
@@ -1722,7 +1722,7 @@ async function runInit(opts) {
|
|
|
1722
1722
|
const dest = t.full ? t.baseDir : path10.join(t.baseDir, "skills");
|
|
1723
1723
|
const { copied, updated } = await copyTemplates(src, dest, {
|
|
1724
1724
|
force: opts.force,
|
|
1725
|
-
exclude: ["controllers", "motion"]
|
|
1725
|
+
exclude: ["controllers", "motion", "asset-viewer"]
|
|
1726
1726
|
});
|
|
1727
1727
|
await pruneRemovedSkills(path10.join(t.baseDir, "skills"), log);
|
|
1728
1728
|
await writeSkillsMarker(path10.join(t.baseDir, "skills"));
|
|
@@ -17564,22 +17564,22 @@ async function context2(opts) {
|
|
|
17564
17564
|
const project = await readProject();
|
|
17565
17565
|
return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
|
|
17566
17566
|
}
|
|
17567
|
-
async function readVideo(
|
|
17567
|
+
async function readVideo(path23, log) {
|
|
17568
17568
|
let bytes;
|
|
17569
17569
|
try {
|
|
17570
|
-
bytes = await readFile(
|
|
17570
|
+
bytes = await readFile(path23);
|
|
17571
17571
|
} catch {
|
|
17572
|
-
log.error(`Can't read ${
|
|
17572
|
+
log.error(`Can't read ${path23}.`);
|
|
17573
17573
|
return null;
|
|
17574
17574
|
}
|
|
17575
17575
|
if (bytes.byteLength > MAX_VIDEO_BYTES) {
|
|
17576
|
-
log.error(`${basename(
|
|
17576
|
+
log.error(`${basename(path23)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
|
|
17577
17577
|
return null;
|
|
17578
17578
|
}
|
|
17579
17579
|
return bytes;
|
|
17580
17580
|
}
|
|
17581
|
-
async function uploadVideo(apiUrl, token, characterId,
|
|
17582
|
-
const contentType = /\.mov$/i.test(
|
|
17581
|
+
async function uploadVideo(apiUrl, token, characterId, path23, bytes, log) {
|
|
17582
|
+
const contentType = /\.mov$/i.test(path23) ? "video/quicktime" : "video/mp4";
|
|
17583
17583
|
const minted = await apiFetch(
|
|
17584
17584
|
`${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
|
|
17585
17585
|
{
|
|
@@ -17594,7 +17594,7 @@ async function uploadVideo(apiUrl, token, characterId, path22, bytes, log) {
|
|
|
17594
17594
|
return null;
|
|
17595
17595
|
}
|
|
17596
17596
|
const { uploadUrl, videoUrl } = await minted.json();
|
|
17597
|
-
log.dim(` uploading ${basename(
|
|
17597
|
+
log.dim(` uploading ${basename(path23)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
|
|
17598
17598
|
const put = await fetch(uploadUrl, {
|
|
17599
17599
|
method: "PUT",
|
|
17600
17600
|
headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
|
|
@@ -19452,6 +19452,241 @@ async function runMotion(opts) {
|
|
|
19452
19452
|
}
|
|
19453
19453
|
}
|
|
19454
19454
|
|
|
19455
|
+
// src/commands/asset-new.ts
|
|
19456
|
+
import fs23 from "fs";
|
|
19457
|
+
import fsp from "fs/promises";
|
|
19458
|
+
import path22 from "path";
|
|
19459
|
+
import { pathToFileURL } from "url";
|
|
19460
|
+
var EXTRA_FILES = [
|
|
19461
|
+
"genex-asset.example.json",
|
|
19462
|
+
"public/fonts/Geist-variable.woff2",
|
|
19463
|
+
"public/fonts/GeistMono-variable.woff2"
|
|
19464
|
+
];
|
|
19465
|
+
var HOLDER_PLACEHOLDER = "Your Name";
|
|
19466
|
+
function parseDims(input) {
|
|
19467
|
+
const parts = String(input).toLowerCase().split("x").map((p) => Number(p.trim()));
|
|
19468
|
+
if (parts.length !== 3 || parts.some((n) => !Number.isFinite(n) || n <= 0)) {
|
|
19469
|
+
throw new Error(`--dims must look like 0.36x0.36x0.70 (width x depth x height), got: ${input}`);
|
|
19470
|
+
}
|
|
19471
|
+
const [width, depth, height] = parts;
|
|
19472
|
+
return [width, height, depth];
|
|
19473
|
+
}
|
|
19474
|
+
function parseBand(input) {
|
|
19475
|
+
const parts = String(input).split("-").map((p) => Number(p.replace(/[_,\s]/g, "")));
|
|
19476
|
+
const [lo, hi] = parts;
|
|
19477
|
+
if (parts.length !== 2 || lo === void 0 || hi === void 0 || !Number.isInteger(lo) || !Number.isInteger(hi) || lo <= 0 || lo >= hi) {
|
|
19478
|
+
throw new Error(`--tri-band must look like 800-2000, got: ${input}`);
|
|
19479
|
+
}
|
|
19480
|
+
return [lo, hi];
|
|
19481
|
+
}
|
|
19482
|
+
function titleCase(slug) {
|
|
19483
|
+
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
19484
|
+
}
|
|
19485
|
+
function seedAssetSource(placeholder, opts) {
|
|
19486
|
+
const pascal = opts.pascalCase(opts.slug);
|
|
19487
|
+
let out = placeholder.replaceAll("Placeholder", pascal);
|
|
19488
|
+
const line = (pattern, replacement) => {
|
|
19489
|
+
if (!pattern.test(out)) {
|
|
19490
|
+
throw new Error(`the template's PLACEHOLDER.ts no longer carries the scaffold line ${pattern}`);
|
|
19491
|
+
}
|
|
19492
|
+
out = out.replace(pattern, replacement);
|
|
19493
|
+
};
|
|
19494
|
+
line(/^\/\/ .* - procedural Three\.js asset\.$/m, `// ${opts.name} - procedural Three.js asset.`);
|
|
19495
|
+
line(
|
|
19496
|
+
/^\/\/ MIT License - Copyright \(c\) \d+ .*$/m,
|
|
19497
|
+
`// MIT License - Copyright (c) ${opts.year} ${opts.holder}. Free to use, modify and ship.`
|
|
19498
|
+
);
|
|
19499
|
+
line(/^const SLUG = .*$/m, `const SLUG = '${opts.slug}';`);
|
|
19500
|
+
line(/^const NAME = .*$/m, `const NAME = '${opts.name.replaceAll("'", "\\'")}';`);
|
|
19501
|
+
line(
|
|
19502
|
+
/^const SIZE_METERS: \[number, number, number\] = .*$/m,
|
|
19503
|
+
`const SIZE_METERS: [number, number, number] = [${opts.sizeMeters.join(", ")}];`
|
|
19504
|
+
);
|
|
19505
|
+
line(/^const TARGET_TRIANGLES = .*$/m, `const TARGET_TRIANGLES = ${opts.targetTriangles};`);
|
|
19506
|
+
return out;
|
|
19507
|
+
}
|
|
19508
|
+
function fillTemplate(text, values) {
|
|
19509
|
+
return text.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
19510
|
+
const value = values[key];
|
|
19511
|
+
if (value === void 0) throw new Error(`unfilled placeholder: {{${key}}}`);
|
|
19512
|
+
return value;
|
|
19513
|
+
});
|
|
19514
|
+
}
|
|
19515
|
+
var GITIGNORE = ["node_modules/", "dist/", ".gates/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n");
|
|
19516
|
+
function designDoc(opts) {
|
|
19517
|
+
return `# ${opts.name}
|
|
19518
|
+
|
|
19519
|
+
A free, MIT-licensed procedural Three.js asset for the Genex asset library.
|
|
19520
|
+
One file, one factory, no dependency but \`three\`.
|
|
19521
|
+
|
|
19522
|
+
- **slug:** \`${opts.slug}\`
|
|
19523
|
+
- **size:** ${opts.sizeMeters.map((n) => n.toFixed(2)).join(" \xD7 ")} m (x \xD7 y \xD7 z, y = height)
|
|
19524
|
+
- **triangle band:** ${opts.triBand[0].toLocaleString("en-US")} - ${opts.triBand[1].toLocaleString("en-US")}
|
|
19525
|
+
- **origin:** base centre, y = 0. **Units:** metres. **Facing:** Y-up, +Z forward.
|
|
19526
|
+
- **licence:** MIT, copyright ${opts.holder}. Publishing this asset releases it MIT \u2014
|
|
19527
|
+
the dashboard's Copy code button makes it copyable by anyone.
|
|
19528
|
+
|
|
19529
|
+
## Boundary note
|
|
19530
|
+
|
|
19531
|
+
This asset is **code**, not generated art: the lane is procedural geometry
|
|
19532
|
+
authored in TypeScript. No player character applies \u2014 the project is a viewer,
|
|
19533
|
+
not a game.
|
|
19534
|
+
|
|
19535
|
+
## Build plan & status
|
|
19536
|
+
|
|
19537
|
+
1. [ ] blockout - silhouette and real dimensions
|
|
19538
|
+
2. [ ] form-refinement - the details that make it read as manufactured
|
|
19539
|
+
3. [ ] material-pass - MeshStandardMaterial + DataTexture only
|
|
19540
|
+
4. [ ] interaction-pass - \`assetRuntime.nodes\`, \`dispose()\`, options
|
|
19541
|
+
|
|
19542
|
+
**Now:** milestone 1.
|
|
19543
|
+
|
|
19544
|
+
Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-build\`.
|
|
19545
|
+
|
|
19546
|
+
## Decisions
|
|
19547
|
+
`;
|
|
19548
|
+
}
|
|
19549
|
+
async function runAssetNew(options) {
|
|
19550
|
+
const log = createLogger();
|
|
19551
|
+
const cwd = options.dir ? path22.resolve(options.dir) : process.cwd();
|
|
19552
|
+
const slug = options.assetSlug;
|
|
19553
|
+
if (!slug) {
|
|
19554
|
+
log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
|
|
19555
|
+
return 1;
|
|
19556
|
+
}
|
|
19557
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
|
|
19558
|
+
log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
|
|
19559
|
+
return 1;
|
|
19560
|
+
}
|
|
19561
|
+
const templateDir = path22.join(getTemplatesDir(), "asset-viewer");
|
|
19562
|
+
if (!fs23.existsSync(templateDir)) {
|
|
19563
|
+
log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
|
|
19564
|
+
return 1;
|
|
19565
|
+
}
|
|
19566
|
+
const manifestTools = await import(pathToFileURL(path22.join(templateDir, "tools", "emit-manifest.mjs")).href);
|
|
19567
|
+
const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
|
|
19568
|
+
const lockPath = path22.join(templateDir, "shared-files.sha256.json");
|
|
19569
|
+
const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
|
|
19570
|
+
const actual = hashSharedFiles(templateDir);
|
|
19571
|
+
const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
|
|
19572
|
+
if (drifted.length) {
|
|
19573
|
+
log.error(`The bundled viewer template is damaged (${drifted.join(", ")}) \u2014 reinstall the CLI.`);
|
|
19574
|
+
return 1;
|
|
19575
|
+
}
|
|
19576
|
+
const name = options.title ?? titleCase(slug);
|
|
19577
|
+
const sizeMeters = parseDims(options.dims ?? "1x1x1");
|
|
19578
|
+
const triBand = parseBand(options.triBand ?? "500-8000");
|
|
19579
|
+
const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
|
|
19580
|
+
const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
|
|
19581
|
+
const outDir = path22.resolve(cwd, options.out ?? slug);
|
|
19582
|
+
if (fs23.existsSync(outDir) && fs23.readdirSync(outDir).length > 0 && !options.force) {
|
|
19583
|
+
log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
|
|
19584
|
+
return 1;
|
|
19585
|
+
}
|
|
19586
|
+
const version = "1.0.0";
|
|
19587
|
+
const targetTriangles = Math.round((triBand[0] + triBand[1]) / 2);
|
|
19588
|
+
const config = {
|
|
19589
|
+
slug,
|
|
19590
|
+
name,
|
|
19591
|
+
summary: options.summary ?? `${name} - a free procedural Three.js asset, ${sizeMeters.map((n) => n.toFixed(2)).join(" \xD7 ")} m.`,
|
|
19592
|
+
tags: (options.tags ?? "prop").split(",").map((t) => t.trim()).filter(Boolean),
|
|
19593
|
+
materialClass: "unclassified",
|
|
19594
|
+
geometryClass: "unclassified",
|
|
19595
|
+
difficulty: "easy",
|
|
19596
|
+
version,
|
|
19597
|
+
minThreeRevision: 160,
|
|
19598
|
+
statedSizeMeters: sizeMeters,
|
|
19599
|
+
triBand,
|
|
19600
|
+
drawCallMax: 8,
|
|
19601
|
+
materialMax: 4,
|
|
19602
|
+
textureBytesMax: 4194304,
|
|
19603
|
+
maxTextureDimension: 1024,
|
|
19604
|
+
silhouetteCollapseRatio: 0.15,
|
|
19605
|
+
license: { holder, year },
|
|
19606
|
+
viewer: { cameraDistanceMul: 1 }
|
|
19607
|
+
};
|
|
19608
|
+
await fsp.mkdir(outDir, { recursive: true });
|
|
19609
|
+
for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
|
|
19610
|
+
const to = path22.join(outDir, rel);
|
|
19611
|
+
await fsp.mkdir(path22.dirname(to), { recursive: true });
|
|
19612
|
+
await fsp.copyFile(path22.join(templateDir, rel), to);
|
|
19613
|
+
}
|
|
19614
|
+
const pkg = fillTemplate(await fsp.readFile(path22.join(templateDir, "package.json"), "utf8"), {
|
|
19615
|
+
slug,
|
|
19616
|
+
name,
|
|
19617
|
+
version
|
|
19618
|
+
});
|
|
19619
|
+
await fsp.writeFile(path22.join(outDir, "package.json"), pkg, "utf8");
|
|
19620
|
+
await fsp.writeFile(path22.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
19621
|
+
await fsp.writeFile(path22.join(outDir, ".gitignore"), GITIGNORE, "utf8");
|
|
19622
|
+
await fsp.writeFile(
|
|
19623
|
+
path22.join(outDir, "DESIGN.md"),
|
|
19624
|
+
designDoc({ name, slug, sizeMeters, triBand, holder }),
|
|
19625
|
+
"utf8"
|
|
19626
|
+
);
|
|
19627
|
+
const placeholder = await fsp.readFile(path22.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
|
|
19628
|
+
const seeded = seedAssetSource(placeholder, {
|
|
19629
|
+
slug,
|
|
19630
|
+
name,
|
|
19631
|
+
sizeMeters,
|
|
19632
|
+
targetTriangles,
|
|
19633
|
+
holder,
|
|
19634
|
+
year,
|
|
19635
|
+
pascalCase
|
|
19636
|
+
});
|
|
19637
|
+
const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
|
|
19638
|
+
await fsp.mkdir(path22.join(outDir, "src", "asset"), { recursive: true });
|
|
19639
|
+
await fsp.writeFile(path22.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
|
|
19640
|
+
const copied = hashSharedFiles(outDir);
|
|
19641
|
+
const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
|
|
19642
|
+
if (mismatched.length) {
|
|
19643
|
+
log.error(`Internal error: copy was not verbatim for ${mismatched.join(", ")}`);
|
|
19644
|
+
return 1;
|
|
19645
|
+
}
|
|
19646
|
+
await fsp.writeFile(
|
|
19647
|
+
path22.join(outDir, PARITY_FILENAME),
|
|
19648
|
+
JSON.stringify(
|
|
19649
|
+
{
|
|
19650
|
+
note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
|
|
19651
|
+
templateVersion: lock.templateVersion,
|
|
19652
|
+
scaffoldedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19653
|
+
files: copied
|
|
19654
|
+
},
|
|
19655
|
+
null,
|
|
19656
|
+
2
|
|
19657
|
+
) + "\n",
|
|
19658
|
+
"utf8"
|
|
19659
|
+
);
|
|
19660
|
+
const lines = [
|
|
19661
|
+
"",
|
|
19662
|
+
` ${c.bold(name)} \u2192 ${outDir}`,
|
|
19663
|
+
` ${sizeMeters.map((n) => n.toFixed(2)).join(" \xD7 ")} m \xB7 ${triBand[0].toLocaleString("en-US")} - ${triBand[1].toLocaleString("en-US")} tris \xB7 viewer ${lock.templateVersion}`,
|
|
19664
|
+
""
|
|
19665
|
+
];
|
|
19666
|
+
if (holder === HOLDER_PLACEHOLDER) {
|
|
19667
|
+
lines.push(
|
|
19668
|
+
` ${c.yellow("\u26A0")} No --holder given: the MIT copyright line says "${HOLDER_PLACEHOLDER}".`,
|
|
19669
|
+
` Set the real name in asset.config.json (license.holder) AND line 2 of src/asset/${assetFilename}`,
|
|
19670
|
+
` before publishing \u2014 published assets are copyable by anyone under that name.`,
|
|
19671
|
+
""
|
|
19672
|
+
);
|
|
19673
|
+
}
|
|
19674
|
+
lines.push(
|
|
19675
|
+
" next:",
|
|
19676
|
+
` cd ${outDir}`,
|
|
19677
|
+
" npm install",
|
|
19678
|
+
" npx playwright-core install chromium # once per machine",
|
|
19679
|
+
` # author src/asset/${assetFilename} \u2014 the genex-asset-author skill carries the contract`,
|
|
19680
|
+
" npm run verify # build + 17 gates + manifest stamp",
|
|
19681
|
+
" npx genex init # create the project (sign-in if needed)",
|
|
19682
|
+
" npx genex preview --no-build # draft turntable to show the user",
|
|
19683
|
+
" npx genex publish --no-build --categories assets",
|
|
19684
|
+
""
|
|
19685
|
+
);
|
|
19686
|
+
log.plain(lines.join("\n"));
|
|
19687
|
+
return 0;
|
|
19688
|
+
}
|
|
19689
|
+
|
|
19455
19690
|
// src/index.ts
|
|
19456
19691
|
var GEN_KINDS = /* @__PURE__ */ new Set(["model", "skybox", "sfx", "music", "voice", "texture", "image", "video"]);
|
|
19457
19692
|
var HELP = `${c.bold("genex")} \u2014 set up your project's agent workspace, authorize, and publish 3D games.
|
|
@@ -19564,6 +19799,13 @@ ${c.bold("Usage")}
|
|
|
19564
19799
|
extract | masks | text-color | trim | audit.
|
|
19565
19800
|
Pure local work (no API); the PNG tools read
|
|
19566
19801
|
a file or URL, audit scans the project.
|
|
19802
|
+
genex asset new <slug> Scaffold ONE asset project for the free MIT
|
|
19803
|
+
asset library (its own project \u2014 one asset,
|
|
19804
|
+
one turntable page). Only when the user asks
|
|
19805
|
+
to make or publish an asset \u2014 never offered
|
|
19806
|
+
unprompted. Options: --title --dims WxDxH
|
|
19807
|
+
--tri-band LOW-HIGH --holder "<your name>"
|
|
19808
|
+
--summary --tags --out --force.
|
|
19567
19809
|
|
|
19568
19810
|
${c.bold("Options for the generators (`model` `sfx` `music` `voice` `texture` `image` `video`)")}
|
|
19569
19811
|
--terrain (texture) seamless tiling surface for terrain/ground.
|
|
@@ -19844,7 +20086,13 @@ function parseArgs(argv) {
|
|
|
19844
20086
|
"--config",
|
|
19845
20087
|
"--set",
|
|
19846
20088
|
"--speed",
|
|
19847
|
-
"--video"
|
|
20089
|
+
"--video",
|
|
20090
|
+
// `genex asset new` value flags.
|
|
20091
|
+
"--dims",
|
|
20092
|
+
"--tri-band",
|
|
20093
|
+
"--holder",
|
|
20094
|
+
"--summary",
|
|
20095
|
+
"--tags"
|
|
19848
20096
|
]);
|
|
19849
20097
|
let i = 0;
|
|
19850
20098
|
while (i < argv.length) {
|
|
@@ -20013,6 +20261,13 @@ function parseArgs(argv) {
|
|
|
20013
20261
|
}
|
|
20014
20262
|
} else if (parsed.command === "animations") {
|
|
20015
20263
|
parsed.options.query = parsed.options.query ? `${parsed.options.query} ${arg}` : arg;
|
|
20264
|
+
} else if (parsed.command === "asset") {
|
|
20265
|
+
if (parsed.options.name === "new" && parsed.options.assetSlug === void 0) {
|
|
20266
|
+
parsed.options.assetSlug = arg;
|
|
20267
|
+
} else {
|
|
20268
|
+
parsed.error = `Unexpected argument: ${arg}`;
|
|
20269
|
+
return parsed;
|
|
20270
|
+
}
|
|
20016
20271
|
} else {
|
|
20017
20272
|
parsed.error = `Unexpected argument: ${arg}`;
|
|
20018
20273
|
return parsed;
|
|
@@ -20149,6 +20404,21 @@ function applyValueFlag(options, flag, value) {
|
|
|
20149
20404
|
case "--set":
|
|
20150
20405
|
options.set = value;
|
|
20151
20406
|
break;
|
|
20407
|
+
case "--dims":
|
|
20408
|
+
options.dims = value;
|
|
20409
|
+
break;
|
|
20410
|
+
case "--tri-band":
|
|
20411
|
+
options.triBand = value;
|
|
20412
|
+
break;
|
|
20413
|
+
case "--holder":
|
|
20414
|
+
options.holder = value;
|
|
20415
|
+
break;
|
|
20416
|
+
case "--summary":
|
|
20417
|
+
options.summary = value;
|
|
20418
|
+
break;
|
|
20419
|
+
case "--tags":
|
|
20420
|
+
options.tags = value;
|
|
20421
|
+
break;
|
|
20152
20422
|
case "--speed": {
|
|
20153
20423
|
const n = Number(value);
|
|
20154
20424
|
if (!Number.isFinite(n) || n <= 0) {
|
|
@@ -20435,6 +20705,14 @@ async function main() {
|
|
|
20435
20705
|
await runAnimationsSearch(parsed.options);
|
|
20436
20706
|
}
|
|
20437
20707
|
break;
|
|
20708
|
+
case "asset":
|
|
20709
|
+
if (parsed.options.name !== "new") {
|
|
20710
|
+
log.error("Unknown asset command. Use `genex asset new <slug>`.");
|
|
20711
|
+
process.exitCode = 1;
|
|
20712
|
+
} else {
|
|
20713
|
+
process.exitCode = await runAssetNew(parsed.options);
|
|
20714
|
+
}
|
|
20715
|
+
break;
|
|
20438
20716
|
case "preview":
|
|
20439
20717
|
await runPreview(parsed.options);
|
|
20440
20718
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-dev.399",
|
|
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": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "placeholder",
|
|
3
|
+
"name": "Placeholder Asset",
|
|
4
|
+
"summary": "Scaffold stand-in: a revolved pedestal marker that already satisfies the asset contract.",
|
|
5
|
+
"tags": ["prop", "scaffold"],
|
|
6
|
+
"materialClass": "plastic",
|
|
7
|
+
"geometryClass": "revolved",
|
|
8
|
+
"difficulty": "easy",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"minThreeRevision": 160,
|
|
11
|
+
"statedSizeMeters": [0.4, 0.6, 0.4],
|
|
12
|
+
"triBand": [200, 4000],
|
|
13
|
+
"drawCallMax": 8,
|
|
14
|
+
"materialMax": 4,
|
|
15
|
+
"textureBytesMax": 4194304,
|
|
16
|
+
"maxTextureDimension": 1024,
|
|
17
|
+
"silhouetteCollapseRatio": 0.15,
|
|
18
|
+
"license": {
|
|
19
|
+
"holder": "Genex",
|
|
20
|
+
"year": 2026
|
|
21
|
+
},
|
|
22
|
+
"viewer": {
|
|
23
|
+
"cameraDistanceMul": 1
|
|
24
|
+
}
|
|
25
|
+
}
|