@genex-ai/cli-demo 1.29.1-dev.634 → 1.30.0-dev.636

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.
Files changed (2) hide show
  1. package/dist/index.js +415 -311
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -659,8 +659,8 @@ async function runAuth(opts) {
659
659
  }
660
660
 
661
661
  // src/commands/remix.ts
662
- import fs17 from "fs/promises";
663
- import path16 from "path";
662
+ import fs18 from "fs/promises";
663
+ import path17 from "path";
664
664
  import crypto4 from "crypto";
665
665
  import { spawn as spawn4 } from "child_process";
666
666
 
@@ -2798,9 +2798,63 @@ function auditGenerationPlan(input) {
2798
2798
 
2799
2799
  // src/lib/deploy.ts
2800
2800
  import crypto2 from "crypto";
2801
- import fs16 from "fs/promises";
2801
+ import fs17 from "fs/promises";
2802
2802
  import os5 from "os";
2803
- import path15 from "path";
2803
+ import path16 from "path";
2804
+
2805
+ // src/lib/source-size.ts
2806
+ import fs14 from "fs/promises";
2807
+ import path14 from "path";
2808
+ var FALLBACK_SOURCE_LIMITS = {
2809
+ maxTotalBytes: 2 * 1024 * 1024 * 1024,
2810
+ maxFiles: 5e3
2811
+ };
2812
+ function isOversized(p, limits) {
2813
+ return p.bytes >= limits.maxTotalBytes || p.files >= limits.maxFiles;
2814
+ }
2815
+ function formatBytes2(n) {
2816
+ if (n >= 1024 ** 3) return `${(n / 1024 ** 3).toFixed(1)} GB`;
2817
+ if (n >= 1024 ** 2) return `${Math.round(n / 1024 ** 2)} MB`;
2818
+ if (n >= 1024) return `${Math.round(n / 1024)} KB`;
2819
+ return `${n} B`;
2820
+ }
2821
+ function groupByDir(entries, limit = 5) {
2822
+ const acc = /* @__PURE__ */ new Map();
2823
+ for (const [file, bytes] of entries) {
2824
+ const dirParts = file.split("/").slice(0, -1).slice(0, 2);
2825
+ const dir = dirParts.length > 0 ? dirParts.join("/") : "(root)";
2826
+ const row = acc.get(dir) ?? { dir, files: 0, bytes: 0 };
2827
+ row.files += 1;
2828
+ row.bytes += bytes;
2829
+ acc.set(dir, row);
2830
+ }
2831
+ return [...acc.values()].sort((a, b) => b.bytes - a.bytes).slice(0, limit);
2832
+ }
2833
+ function oversizedLines(p, limits) {
2834
+ return [
2835
+ `This is ${formatBytes2(p.bytes)} across ${p.files} files, past the ${formatBytes2(limits.maxTotalBytes)} / ${limits.maxFiles} file limit on this account.`,
2836
+ "Heaviest directories:",
2837
+ ...p.top.map((d) => ` ${formatBytes2(d.bytes).padStart(8)} ${String(d.files).padStart(6)} files ${d.dir}`),
2838
+ "Screenshots, traces and reports made while building are not part of the game \u2014",
2839
+ "put them in .genex/scratch/ (already ignored), or add the folders above to .gitignore.",
2840
+ "If this really is the game, ask an admin to raise the source limit on your account."
2841
+ ];
2842
+ }
2843
+ async function measurePayload(cwd, staged) {
2844
+ const entries = [];
2845
+ let bytes = 0;
2846
+ for (const file of staged) {
2847
+ let size = 0;
2848
+ try {
2849
+ size = (await fs14.stat(path14.join(cwd, file))).size;
2850
+ } catch {
2851
+ size = 0;
2852
+ }
2853
+ bytes += size;
2854
+ entries.push([file, size]);
2855
+ }
2856
+ return { files: staged.length, bytes, top: groupByDir(entries) };
2857
+ }
2804
2858
 
2805
2859
  // ../../packages/mobile-scan/src/image-dims.ts
2806
2860
  function u32be(b, o) {
@@ -3064,12 +3118,12 @@ function tierFor(estVramMb) {
3064
3118
  }
3065
3119
 
3066
3120
  // src/commands/ui.ts
3067
- import fs15 from "fs/promises";
3068
- import path14 from "path";
3121
+ import fs16 from "fs/promises";
3122
+ import path15 from "path";
3069
3123
  import { PNG as PNG2 } from "pngjs";
3070
3124
 
3071
3125
  // src/lib/png-tools.ts
3072
- import fs14 from "fs/promises";
3126
+ import fs15 from "fs/promises";
3073
3127
  import { PNG } from "pngjs";
3074
3128
  var ALPHA_TRANSPARENT_MAX = 16;
3075
3129
  var isHttpUrl = (s) => /^https?:\/\//i.test(s);
@@ -3080,12 +3134,12 @@ async function loadPng(input) {
3080
3134
  if (!res.ok) throw new Error(`Couldn't fetch ${input} (HTTP ${res.status}).`);
3081
3135
  buf = Buffer.from(await res.arrayBuffer());
3082
3136
  } else {
3083
- buf = await fs14.readFile(input);
3137
+ buf = await fs15.readFile(input);
3084
3138
  }
3085
3139
  return PNG.sync.read(buf);
3086
3140
  }
3087
3141
  async function writePng(file, png) {
3088
- await fs14.writeFile(file, PNG.sync.write(png));
3142
+ await fs15.writeFile(file, PNG.sync.write(png));
3089
3143
  }
3090
3144
  function cropPng(image, box) {
3091
3145
  const out = new PNG({ width: box.w, height: box.h });
@@ -3385,7 +3439,7 @@ async function uiExtract(opts, log) {
3385
3439
  const dilatePx = opts.dilate ?? 0;
3386
3440
  const sheet = await loadPng(input);
3387
3441
  const { width: W, height: H, data } = sheet;
3388
- await fs15.mkdir(outDir, { recursive: true });
3442
+ await fs16.mkdir(outDir, { recursive: true });
3389
3443
  log.plain(c.bold("genex ui extract"));
3390
3444
  log.dim(` ${input} (${W}x${H}) \u2192 ${outDir}, ${names.length} names`);
3391
3445
  let hasTransparency = false;
@@ -3614,7 +3668,7 @@ async function uiExtract(opts, log) {
3614
3668
  rimPixels: speckle.sampled
3615
3669
  });
3616
3670
  }
3617
- const outPath = path14.join(outDir, `${name}.png`);
3671
+ const outPath = path15.join(outDir, `${name}.png`);
3618
3672
  await writePng(outPath, out);
3619
3673
  const sidecar = {
3620
3674
  name,
@@ -3633,7 +3687,7 @@ async function uiExtract(opts, log) {
3633
3687
  defringed
3634
3688
  };
3635
3689
  const { name: _n, out: _o, ...sidecarBody } = sidecar;
3636
- await fs15.writeFile(
3690
+ await fs16.writeFile(
3637
3691
  outPath.replace(/\.png$/i, "") + ".bbox.json",
3638
3692
  JSON.stringify(sidecarBody, null, 2)
3639
3693
  );
@@ -3642,8 +3696,8 @@ async function uiExtract(opts, log) {
3642
3696
  `${name}.png ${cropW}x${cropH} (ar ${sidecar.aspectRatio}) at sheet ${padX0},${padY0}`
3643
3697
  );
3644
3698
  }
3645
- const debugPath = path14.join(outDir, "extract-debug.json");
3646
- await fs15.writeFile(
3699
+ const debugPath = path15.join(outDir, "extract-debug.json");
3700
+ await fs16.writeFile(
3647
3701
  debugPath,
3648
3702
  JSON.stringify(
3649
3703
  {
@@ -4105,7 +4159,7 @@ async function uiMasks(opts, log) {
4105
4159
  const registrationTolerance = opts.registrationTolerance ?? 0.04;
4106
4160
  const edgeFlushMax = opts.edgeFlushMax ?? 0.04;
4107
4161
  const loosened = registrationTolerance > 0.04 || edgeFlushMax > 0.04 || minCoverage < 0.01 || maxCoverage > 0.85;
4108
- await fs15.mkdir(outDir, { recursive: true });
4162
+ await fs16.mkdir(outDir, { recursive: true });
4109
4163
  log.plain(c.bold("genex ui masks"));
4110
4164
  log.dim(` ${input} (${image.width}x${image.height}), ${pairs.length} pair(s) \u2192 ${outDir}`);
4111
4165
  const sheetComponents = detectSheetComponents(image, opts.minPixels ?? 2e3);
@@ -4158,11 +4212,11 @@ async function uiMasks(opts, log) {
4158
4212
  });
4159
4213
  }
4160
4214
  const overlay = makeOverlay(clean2, converted.png);
4161
- const framePath = path14.join(outDir, `${pair.name}-frame.png`);
4162
- const maskPath = path14.join(outDir, `${pair.name}-mask.png`);
4163
- const annotatedPath = path14.join(outDir, `${pair.name}-annotated-source.png`);
4164
- const overlayPath = path14.join(outDir, `${pair.name}-overlay.png`);
4165
- const metaPath = path14.join(outDir, `${pair.name}.annotated-progress.json`);
4215
+ const framePath = path15.join(outDir, `${pair.name}-frame.png`);
4216
+ const maskPath = path15.join(outDir, `${pair.name}-mask.png`);
4217
+ const annotatedPath = path15.join(outDir, `${pair.name}-annotated-source.png`);
4218
+ const overlayPath = path15.join(outDir, `${pair.name}-overlay.png`);
4219
+ const metaPath = path15.join(outDir, `${pair.name}.annotated-progress.json`);
4166
4220
  await writePng(framePath, clean2);
4167
4221
  await writePng(maskPath, converted.png);
4168
4222
  await writePng(annotatedPath, annotated);
@@ -4209,7 +4263,7 @@ async function uiMasks(opts, log) {
4209
4263
  },
4210
4264
  overlay: overlayPath
4211
4265
  };
4212
- await fs15.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
4266
+ await fs16.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
4213
4267
  `);
4214
4268
  results.push(meta);
4215
4269
  const fb = converted.bbox;
@@ -4225,8 +4279,8 @@ async function uiMasks(opts, log) {
4225
4279
  );
4226
4280
  }
4227
4281
  }
4228
- const indexPath = path14.join(outDir, "annotated-progress.json");
4229
- await fs15.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
4282
+ const indexPath = path15.join(outDir, "annotated-progress.json");
4283
+ await fs16.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
4230
4284
  `);
4231
4285
  log.plain("");
4232
4286
  log.success(`Wrote ${pairs.length} frame+mask pair(s) \u2192 ${outDir} (index: ${indexPath}).`);
@@ -4352,7 +4406,7 @@ async function uiTextColor(opts, log) {
4352
4406
  };
4353
4407
  process.stdout.write(`${JSON.stringify(result, null, 2)}
4354
4408
  `);
4355
- if (opts.out) await fs15.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4409
+ if (opts.out) await fs16.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4356
4410
  `);
4357
4411
  if (opts.cropPath) log.dim(` crop \u2192 ${opts.cropPath}`);
4358
4412
  }
@@ -4384,7 +4438,7 @@ async function uiTrim(opts, log) {
4384
4438
  const sidecar = computeBBoxes(trimmed);
4385
4439
  const speckleAllowed = !!(speckle && speckle.ratio > SPECKLE_MAX_RATIO);
4386
4440
  const sidecarPath = outPath.replace(/\.png$/i, "") + ".bbox.json";
4387
- await fs15.writeFile(
4441
+ await fs16.writeFile(
4388
4442
  sidecarPath,
4389
4443
  JSON.stringify(speckleAllowed ? { ...sidecar, speckleAllowed: true } : sidecar, null, 2)
4390
4444
  );
@@ -4491,7 +4545,7 @@ async function uiPlate(opts, log) {
4491
4545
  fail("No interior found \u2014 the image is fully transparent (or erode ate everything). Check --in / lower --erode.");
4492
4546
  }
4493
4547
  await writePng(outPath, out);
4494
- const name = path14.basename(outPath);
4548
+ const name = path15.basename(outPath);
4495
4549
  log.plain(c.bold("genex ui plate"));
4496
4550
  log.success(`${outPath} ${W}x${H}, interior ${(count / (W * H) * 100).toFixed(1)}% (erode ${erode}px)`);
4497
4551
  log.dim(" Wire it as the plate's silhouette (same box as the frame <img>, plate UNDER the art):");
@@ -4656,13 +4710,13 @@ async function walkFiles(dir) {
4656
4710
  const out = [];
4657
4711
  let entries;
4658
4712
  try {
4659
- entries = await fs15.readdir(dir, { withFileTypes: true });
4713
+ entries = await fs16.readdir(dir, { withFileTypes: true });
4660
4714
  } catch {
4661
4715
  return out;
4662
4716
  }
4663
4717
  for (const entry of entries) {
4664
4718
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
4665
- const p = path14.join(dir, entry.name);
4719
+ const p = path15.join(dir, entry.name);
4666
4720
  if (entry.isDirectory()) out.push(...await walkFiles(p));
4667
4721
  else out.push(p);
4668
4722
  }
@@ -4671,7 +4725,7 @@ async function walkFiles(dir) {
4671
4725
  async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4672
4726
  const viewportFindings = [];
4673
4727
  try {
4674
- const indexHtml = await fs15.readFile(path14.join(cwd, "index.html"), "utf8");
4728
+ const indexHtml = await fs16.readFile(path15.join(cwd, "index.html"), "utf8");
4675
4729
  if (!/<meta[^>]+name=["']viewport["']/i.test(indexHtml)) {
4676
4730
  viewportFindings.push({
4677
4731
  kind: "viewport-meta",
@@ -4685,28 +4739,28 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4685
4739
  }
4686
4740
  } catch {
4687
4741
  }
4688
- const absAssets = path14.resolve(cwd, assetDir);
4742
+ const absAssets = path15.resolve(cwd, assetDir);
4689
4743
  try {
4690
- if (!(await fs15.stat(absAssets)).isDirectory()) {
4744
+ if (!(await fs16.stat(absAssets)).isDirectory()) {
4691
4745
  return viewportFindings.length > 0 ? viewportFindings : null;
4692
4746
  }
4693
4747
  } catch {
4694
4748
  return viewportFindings.length > 0 ? viewportFindings : null;
4695
4749
  }
4696
- const srcFiles = (await walkFiles(path14.resolve(cwd, srcDir))).filter(
4697
- (p) => AUDIT_SRC_EXTS.has(path14.extname(p).toLowerCase())
4750
+ const srcFiles = (await walkFiles(path15.resolve(cwd, srcDir))).filter(
4751
+ (p) => AUDIT_SRC_EXTS.has(path15.extname(p).toLowerCase())
4698
4752
  );
4699
4753
  try {
4700
- for (const name of await fs15.readdir(cwd)) {
4701
- const ext = path14.extname(name).toLowerCase();
4702
- if (ext === ".html" || ext === ".css") srcFiles.push(path14.join(cwd, name));
4754
+ for (const name of await fs16.readdir(cwd)) {
4755
+ const ext = path15.extname(name).toLowerCase();
4756
+ if (ext === ".html" || ext === ".css") srcFiles.push(path15.join(cwd, name));
4703
4757
  }
4704
4758
  } catch {
4705
4759
  }
4706
4760
  const sources = [];
4707
4761
  for (const p of srcFiles) {
4708
4762
  try {
4709
- sources.push({ rel: path14.relative(cwd, p), text: await fs15.readFile(p, "utf8") });
4763
+ sources.push({ rel: path15.relative(cwd, p), text: await fs16.readFile(p, "utf8") });
4710
4764
  } catch {
4711
4765
  }
4712
4766
  }
@@ -4716,11 +4770,11 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4716
4770
  const metaByName = /* @__PURE__ */ new Map();
4717
4771
  const bboxByPng = /* @__PURE__ */ new Map();
4718
4772
  for (const p of assetFiles) {
4719
- const base = path14.basename(p);
4773
+ const base = path15.basename(p);
4720
4774
  const metaMatch = /^(.+)\.annotated-progress\.json$/.exec(base);
4721
4775
  if (metaMatch) {
4722
4776
  try {
4723
- const meta = JSON.parse(await fs15.readFile(p, "utf8"));
4777
+ const meta = JSON.parse(await fs16.readFile(p, "utf8"));
4724
4778
  metaByName.set(metaMatch[1], {
4725
4779
  cleanCrop: meta.clean?.crop ?? null,
4726
4780
  loosened: meta.loosened === true
@@ -4731,7 +4785,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4731
4785
  }
4732
4786
  if (base.endsWith(".bbox.json")) {
4733
4787
  try {
4734
- const sidecar = JSON.parse(await fs15.readFile(p, "utf8"));
4788
+ const sidecar = JSON.parse(await fs16.readFile(p, "utf8"));
4735
4789
  if (sidecar.sheetBBox) bboxByPng.set(base.replace(/\.bbox\.json$/, ".png"), sidecar.sheetBBox);
4736
4790
  } catch {
4737
4791
  }
@@ -4740,7 +4794,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4740
4794
  for (const [name, meta] of metaByName) {
4741
4795
  if (!meta.cleanCrop || !referenced(`${name}-mask.png`)) continue;
4742
4796
  for (const p of assetFiles) {
4743
- const base = path14.basename(p);
4797
+ const base = path15.basename(p);
4744
4798
  if (!base.toLowerCase().endsWith(".png")) continue;
4745
4799
  if (base !== `${name}.png` && !base.startsWith(`${name}-`)) continue;
4746
4800
  if (/-mask\.png$|-frame\.png$|-overlay\.png$|-annotated-source\.png$/.test(base)) continue;
@@ -4764,7 +4818,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4764
4818
  }
4765
4819
  const pngByBase = /* @__PURE__ */ new Map();
4766
4820
  for (const p of assetFiles) {
4767
- const base = path14.basename(p);
4821
+ const base = path15.basename(p);
4768
4822
  if (base.toLowerCase().endsWith(".png")) pngByBase.set(base, p);
4769
4823
  }
4770
4824
  for (const [maskBase, maskPath] of pngByBase) {
@@ -4777,8 +4831,8 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4777
4831
  let frame;
4778
4832
  let mask;
4779
4833
  try {
4780
- frame = PNG2.sync.read(await fs15.readFile(framePath));
4781
- mask = PNG2.sync.read(await fs15.readFile(maskPath));
4834
+ frame = PNG2.sync.read(await fs16.readFile(framePath));
4835
+ mask = PNG2.sync.read(await fs16.readFile(maskPath));
4782
4836
  } catch {
4783
4837
  continue;
4784
4838
  }
@@ -4797,7 +4851,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4797
4851
  if (!referenced(base)) continue;
4798
4852
  let png;
4799
4853
  try {
4800
- png = PNG2.sync.read(await fs15.readFile(p));
4854
+ png = PNG2.sync.read(await fs16.readFile(p));
4801
4855
  } catch {
4802
4856
  continue;
4803
4857
  }
@@ -4817,7 +4871,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4817
4871
  }
4818
4872
  const maskReported = /* @__PURE__ */ new Set();
4819
4873
  for (const p of assetFiles) {
4820
- const m = /^(.+)\.annotated-progress\.json$/.exec(path14.basename(p));
4874
+ const m = /^(.+)\.annotated-progress\.json$/.exec(path15.basename(p));
4821
4875
  if (!m) continue;
4822
4876
  const maskBase = `${m[1]}-mask.png`;
4823
4877
  if (!referenced(maskBase)) {
@@ -4829,14 +4883,14 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4829
4883
  }
4830
4884
  }
4831
4885
  for (const p of assetFiles) {
4832
- const base = path14.basename(p);
4886
+ const base = path15.basename(p);
4833
4887
  if (!base.toLowerCase().endsWith(".png")) continue;
4834
4888
  if (/-annotated-source\.png$|-overlay\.png$/.test(base)) continue;
4835
4889
  if (maskReported.has(base)) continue;
4836
4890
  if (!referenced(base)) {
4837
4891
  findings.push({
4838
4892
  kind: "unwired-sprite",
4839
- message: `${path14.relative(cwd, p)} is on disk but never referenced in ${srcDir}/index.html/CSS \u2014 wire it, or record the one-line reason it was cut. (Computed-string references are invisible here \u2014 check.)`
4893
+ message: `${path15.relative(cwd, p)} is on disk but never referenced in ${srcDir}/index.html/CSS \u2014 wire it, or record the one-line reason it was cut. (Computed-string references are invisible here \u2014 check.)`
4840
4894
  });
4841
4895
  }
4842
4896
  }
@@ -4951,7 +5005,7 @@ async function printUiAuditPreflight(log) {
4951
5005
  }
4952
5006
  async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4953
5007
  try {
4954
- const design = await fs16.readFile(path15.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
5008
+ const design = await fs17.readFile(path16.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4955
5009
  const warnings = [];
4956
5010
  if (design.length > 0 && !/##\s*build plan & status/i.test(design)) {
4957
5011
  warnings.push(
@@ -4959,7 +5013,7 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4959
5013
  );
4960
5014
  }
4961
5015
  if (!/player character:/i.test(design)) {
4962
- const hasCharacter = await fs16.access(path15.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
5016
+ const hasCharacter = await fs17.access(path16.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4963
5017
  if (!hasCharacter && await loadsPlayerBody(cwd)) {
4964
5018
  warnings.push(
4965
5019
  `Player character is the stock avatar \u2014 no generated character is wired. The game's own generated character is the player's body wherever a human body appears on screen, first-person included (genex-ai-character). Generate it, or record "Player character: VRM \u2014 <reason>" in DESIGN.md (no human body in this game / out of credits / player declined).`
@@ -4976,12 +5030,12 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4976
5030
  async function loadsPlayerBody(cwd) {
4977
5031
  const BODY_LOADERS = /\bloadPlayerCharacter\s*\(|\bloadVrm(?:Clone)?\s*\(/;
4978
5032
  try {
4979
- const entries = await fs16.readdir(path15.join(cwd, "src"), { recursive: true });
5033
+ const entries = await fs17.readdir(path16.join(cwd, "src"), { recursive: true });
4980
5034
  for (const rel of entries) {
4981
5035
  if (rel.includes("node_modules")) continue;
4982
- if (rel.split(path15.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
5036
+ if (rel.split(path16.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4983
5037
  if (!/\.(ts|tsx|js|jsx)$/.test(rel)) continue;
4984
- const text = await fs16.readFile(path15.join(cwd, "src", rel), "utf8").catch(() => "");
5038
+ const text = await fs17.readFile(path16.join(cwd, "src", rel), "utf8").catch(() => "");
4985
5039
  if (BODY_LOADERS.test(text)) return true;
4986
5040
  }
4987
5041
  } catch {
@@ -5018,9 +5072,9 @@ async function deployGame(ctx, opts, log) {
5018
5072
  }
5019
5073
  log.success("Built.");
5020
5074
  }
5021
- const distDir = path15.join(cwd, "dist");
5075
+ const distDir = path16.join(cwd, "dist");
5022
5076
  const siteDir = await isDir(distDir) ? distDir : cwd;
5023
- const rel = path15.relative(cwd, siteDir) || ".";
5077
+ const rel = path16.relative(cwd, siteDir) || ".";
5024
5078
  if (siteDir === cwd) await writeGitignore(cwd, log);
5025
5079
  const files = await collectFiles(siteDir);
5026
5080
  if (files.length === 0) {
@@ -5111,7 +5165,7 @@ async function deployGame(ctx, opts, log) {
5111
5165
  }
5112
5166
  async function hasBuildScript(cwd) {
5113
5167
  try {
5114
- const pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
5168
+ const pkg = JSON.parse(await fs17.readFile(path16.join(cwd, "package.json"), "utf8"));
5115
5169
  return Boolean(pkg.scripts?.build);
5116
5170
  } catch {
5117
5171
  return false;
@@ -5120,12 +5174,12 @@ async function hasBuildScript(cwd) {
5120
5174
  async function collectFiles(root) {
5121
5175
  const out = [];
5122
5176
  const walk2 = async (dir, prefix) => {
5123
- for (const e of await fs16.readdir(dir, { withFileTypes: true })) {
5177
+ for (const e of await fs17.readdir(dir, { withFileTypes: true })) {
5124
5178
  const relPath = prefix ? `${prefix}/${e.name}` : e.name;
5125
5179
  if (e.isDirectory()) {
5126
- if (!EXCLUDE_DIRS.has(e.name)) await walk2(path15.join(dir, e.name), relPath);
5180
+ if (!EXCLUDE_DIRS.has(e.name)) await walk2(path16.join(dir, e.name), relPath);
5127
5181
  } else if (e.isFile() && !isSecretEnvFile(e.name)) {
5128
- out.push({ relPath, bytes: await fs16.readFile(path15.join(dir, e.name)) });
5182
+ out.push({ relPath, bytes: await fs17.readFile(path16.join(dir, e.name)) });
5129
5183
  }
5130
5184
  }
5131
5185
  };
@@ -5362,23 +5416,43 @@ async function pushSource(cwd, ctx, log, branch = "main", expectStagingCommit, o
5362
5416
  if (target === "stale") return "stale";
5363
5417
  if (!target) return false;
5364
5418
  const ref = target.managed ? branch : "main";
5365
- if (await pushWorktree(cwd, target.pushUrl, target.managed, log, ref, onCommit)) return true;
5419
+ const first = await pushWorktree(
5420
+ cwd,
5421
+ target.pushUrl,
5422
+ target.managed,
5423
+ log,
5424
+ ref,
5425
+ onCommit,
5426
+ false,
5427
+ target.sourceLimits
5428
+ );
5429
+ if (first === true) return true;
5430
+ if (first === "oversize") return false;
5366
5431
  if (!target.managed) return false;
5367
5432
  log.info("Retrying the source push\u2026");
5368
5433
  await new Promise((r) => setTimeout(r, 2e3));
5369
5434
  const fresh = await fetchPushUrl(ctx, log, expectStagingCommit);
5370
5435
  if (fresh === "stale") return "stale";
5371
5436
  if (!fresh) return false;
5372
- return pushWorktree(cwd, fresh.pushUrl, fresh.managed, log, ref, onCommit);
5437
+ return await pushWorktree(
5438
+ cwd,
5439
+ fresh.pushUrl,
5440
+ fresh.managed,
5441
+ log,
5442
+ ref,
5443
+ onCommit,
5444
+ false,
5445
+ fresh.sourceLimits
5446
+ ) === true;
5373
5447
  }
5374
5448
  var WORKSPACE_SOURCE_REF = "workspace";
5375
- async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommit) {
5449
+ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommit, skipSizeCheck = false, limits) {
5376
5450
  const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
5377
5451
  const failed = () => {
5378
5452
  log.error("Couldn't save your game's source \u2014 please try again.");
5379
- return false;
5453
+ return "failed";
5380
5454
  };
5381
- const gitDir = await fs16.mkdtemp(path15.join(os5.tmpdir(), "genex-source-"));
5455
+ const gitDir = await fs17.mkdtemp(path16.join(os5.tmpdir(), "genex-source-"));
5382
5456
  const base = { GIT_DIR: gitDir };
5383
5457
  if (urlHasEmbeddedCredentials(pushUrl)) {
5384
5458
  base.GIT_CONFIG_COUNT = "1";
@@ -5393,8 +5467,8 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5393
5467
  };
5394
5468
  try {
5395
5469
  if ((await run("git", ["init", "-q"], base)).code !== 0) return failed();
5396
- await fs16.writeFile(path15.join(gitDir, "info", "exclude"), excludeFile());
5397
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path15.join(gitDir, "index-source") };
5470
+ await fs17.writeFile(path16.join(gitDir, "info", "exclude"), excludeFile());
5471
+ const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path16.join(gitDir, "index-source") };
5398
5472
  let lfs = (await run("git", ["lfs", "version"], base)).code !== 0 ? false : true;
5399
5473
  if (!lfs) {
5400
5474
  log.step("Installing git-lfs (keeps large binary assets out of the source push)\u2026");
@@ -5418,17 +5492,31 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5418
5492
  for (const [key, value] of filters) await run("git", ["config", key, value], base);
5419
5493
  }
5420
5494
  await run("git", ["add", "-A"], env);
5421
- const tree = (await run("git", ["ls-files"], env)).out.trim() ? (await run("git", ["write-tree"], env)).out.trim() : "";
5495
+ const staged = (await run("git", ["ls-files", "-z"], env)).out.split("\0").filter(Boolean);
5496
+ if (staged.length === 0) {
5497
+ log.error("Nothing to publish \u2014 the project has no files.");
5498
+ return "failed";
5499
+ }
5500
+ const payload = await measurePayload(cwd, staged);
5501
+ log.dim(`Source: ${payload.files} files, ${formatBytes2(payload.bytes)}.`);
5502
+ const ceiling = limits ?? FALLBACK_SOURCE_LIMITS;
5503
+ if (!skipSizeCheck && isOversized(payload, ceiling)) {
5504
+ const [head, ...rest] = oversizedLines(payload, ceiling);
5505
+ log.error(head ?? "Source too large.");
5506
+ for (const line of rest) log.dim(` ${line}`);
5507
+ return "oversize";
5508
+ }
5509
+ const tree = (await run("git", ["write-tree"], env)).out.trim();
5422
5510
  if (!tree || tree === EMPTY_TREE) {
5423
5511
  log.error("Nothing to publish \u2014 the project has no files.");
5424
- return false;
5512
+ return "failed";
5425
5513
  }
5426
5514
  const commit = (await run("git", ["commit-tree", tree, "-m", "source"], { ...base, ...ident })).out.trim();
5427
5515
  if (commit) onCommit?.(commit);
5428
5516
  await run("git", ["update-ref", "refs/heads/main", commit], base);
5429
5517
  if (lfs && (await run("git", ["lfs", "push", "--all", pushUrl, "main"], base)).code !== 0) {
5430
5518
  log.error("Couldn't upload your game's binary assets to the source store.");
5431
- return false;
5519
+ return "failed";
5432
5520
  }
5433
5521
  const push = await run(
5434
5522
  "git",
@@ -5440,13 +5528,13 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5440
5528
  log.error(
5441
5529
  "Couldn't push to your repo. Check that you have push access (SSH key or git credentials) and that the repo exists."
5442
5530
  );
5443
- return false;
5531
+ return "failed";
5444
5532
  }
5445
5533
  return failed();
5446
5534
  } catch {
5447
5535
  return failed();
5448
5536
  } finally {
5449
- await fs16.rm(gitDir, { recursive: true, force: true }).catch(() => {
5537
+ await fs17.rm(gitDir, { recursive: true, force: true }).catch(() => {
5450
5538
  });
5451
5539
  }
5452
5540
  }
@@ -5496,11 +5584,18 @@ async function fetchPushUrl(ctx, log, expectStagingCommit) {
5496
5584
  log.error("The API didn't return a push URL.");
5497
5585
  return null;
5498
5586
  }
5499
- return { pushUrl: data.pushUrl, managed: data.managed !== false, sourceRef: data.sourceRef ?? null };
5587
+ const l = data.sourceLimits;
5588
+ const sourceLimits = typeof l?.maxTotalBytes === "number" && typeof l?.maxFiles === "number" ? { maxTotalBytes: l.maxTotalBytes, maxFiles: l.maxFiles } : void 0;
5589
+ return {
5590
+ pushUrl: data.pushUrl,
5591
+ managed: data.managed !== false,
5592
+ sourceRef: data.sourceRef ?? null,
5593
+ sourceLimits
5594
+ };
5500
5595
  }
5501
5596
  async function isDir(p) {
5502
5597
  try {
5503
- return (await fs16.stat(p)).isDirectory();
5598
+ return (await fs17.stat(p)).isDirectory();
5504
5599
  } catch {
5505
5600
  return false;
5506
5601
  }
@@ -5712,28 +5807,28 @@ function loggerFor(opts) {
5712
5807
  }
5713
5808
  async function readState(directory) {
5714
5809
  try {
5715
- const marker = path16.join(directory, ".genex", "remix-operation.json");
5716
- if ((await fs17.lstat(marker)).isSymbolicLink()) return null;
5717
- const value = JSON.parse(await fs17.readFile(marker, "utf8"));
5810
+ const marker = path17.join(directory, ".genex", "remix-operation.json");
5811
+ if ((await fs18.lstat(marker)).isSymbolicLink()) return null;
5812
+ const value = JSON.parse(await fs18.readFile(marker, "utf8"));
5718
5813
  return value.schema === 1 && value.directory === directory && typeof value.operationId === "string" && /^[a-zA-Z0-9_-]{8,128}$/.test(value.operationId) && value.source && /^[a-f0-9]{40}$/.test(value.source.sourceCommitSha) ? value : null;
5719
5814
  } catch {
5720
5815
  return null;
5721
5816
  }
5722
5817
  }
5723
5818
  async function saveState(state) {
5724
- const dir = path16.join(state.directory, ".genex");
5725
- await fs17.mkdir(dir, { recursive: true, mode: 448 });
5726
- const temp = path16.join(dir, "remix-operation.next.json");
5727
- await fs17.writeFile(temp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
5728
- await fs17.rename(temp, path16.join(dir, "remix-operation.json"));
5819
+ const dir = path17.join(state.directory, ".genex");
5820
+ await fs18.mkdir(dir, { recursive: true, mode: 448 });
5821
+ const temp = path17.join(dir, "remix-operation.next.json");
5822
+ await fs18.writeFile(temp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
5823
+ await fs18.rename(temp, path17.join(dir, "remix-operation.json"));
5729
5824
  }
5730
5825
  async function checkDestination(directory) {
5731
5826
  try {
5732
- const stat = await fs17.lstat(directory);
5827
+ const stat = await fs18.lstat(directory);
5733
5828
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new RemixError("destination_occupied", "The destination is not a fresh directory. Choose a new folder.");
5734
- const entries = await fs17.readdir(directory);
5829
+ const entries = await fs18.readdir(directory);
5735
5830
  if (!entries.length) return null;
5736
- const genex = await fs17.lstat(path16.join(directory, ".genex")).catch(() => null);
5831
+ const genex = await fs18.lstat(path17.join(directory, ".genex")).catch(() => null);
5737
5832
  const state = genex?.isDirectory() && !genex.isSymbolicLink() ? await readState(directory) : null;
5738
5833
  if (state) return state;
5739
5834
  throw new RemixError("destination_occupied", "The destination already contains files. Choose a fresh folder; remix never clears an existing project.");
@@ -5744,7 +5839,7 @@ async function checkDestination(directory) {
5744
5839
  }
5745
5840
  async function assertReferenceParents(cwd) {
5746
5841
  for (const rel of [".genex", ".genex/refs"]) {
5747
- const stat = await fs17.lstat(path16.join(cwd, rel)).catch((err) => {
5842
+ const stat = await fs18.lstat(path17.join(cwd, rel)).catch((err) => {
5748
5843
  if (err.code === "ENOENT") return null;
5749
5844
  throw err;
5750
5845
  });
@@ -5752,41 +5847,41 @@ async function assertReferenceParents(cwd) {
5752
5847
  }
5753
5848
  }
5754
5849
  async function archiveSourceInstructions(directory) {
5755
- const archive = path16.join(directory, ".genex", "source-instructions");
5850
+ const archive = path17.join(directory, ".genex", "source-instructions");
5756
5851
  const rootArtifacts = /* @__PURE__ */ new Set([".claude", ".codex", ".cursor", ".agents", ".mcp.json", ".cursorrules", "DESIGN.md"]);
5757
5852
  const instructionNames = /* @__PURE__ */ new Set(["AGENTS.md", "CLAUDE.md", "GEMINI.md"]);
5758
5853
  const visit = async (dir) => {
5759
- for (const entry of await fs17.readdir(dir, { withFileTypes: true })) {
5854
+ for (const entry of await fs18.readdir(dir, { withFileTypes: true })) {
5760
5855
  if (entry.name === ".genex" || entry.name === ".git" || entry.name === "node_modules") continue;
5761
- const from = path16.join(dir, entry.name), rel = path16.relative(directory, from);
5856
+ const from = path17.join(dir, entry.name), rel = path17.relative(directory, from);
5762
5857
  if (instructionNames.has(entry.name) || dir === directory && rootArtifacts.has(entry.name)) {
5763
- const to = path16.join(archive, rel);
5764
- await fs17.mkdir(path16.dirname(to), { recursive: true });
5765
- if (await fs17.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `Source instructions at ${rel} changed during preparation; both copies were retained.`);
5766
- await fs17.rename(from, to);
5858
+ const to = path17.join(archive, rel);
5859
+ await fs18.mkdir(path17.dirname(to), { recursive: true });
5860
+ if (await fs18.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `Source instructions at ${rel} changed during preparation; both copies were retained.`);
5861
+ await fs18.rename(from, to);
5767
5862
  } else if (entry.isDirectory()) await visit(from);
5768
5863
  }
5769
5864
  };
5770
5865
  await visit(directory);
5771
5866
  }
5772
5867
  async function acquireLock(directory) {
5773
- const lock = path16.join(directory, ".genex", "remix.lock");
5868
+ const lock = path17.join(directory, ".genex", "remix.lock");
5774
5869
  for (let attempt = 0; attempt < 2; attempt++) {
5775
5870
  try {
5776
- const handle = await fs17.open(lock, "wx", 384);
5871
+ const handle = await fs18.open(lock, "wx", 384);
5777
5872
  await handle.writeFile(String(process.pid));
5778
5873
  await handle.close();
5779
- return () => fs17.unlink(lock).catch(() => {
5874
+ return () => fs18.unlink(lock).catch(() => {
5780
5875
  });
5781
5876
  } catch (err) {
5782
5877
  if (err.code !== "EEXIST") throw err;
5783
- const pid = Number(await fs17.readFile(lock, "utf8").catch(() => ""));
5878
+ const pid = Number(await fs18.readFile(lock, "utf8").catch(() => ""));
5784
5879
  if (!Number.isInteger(pid) || pid <= 0) throw new RemixError("remix_in_progress", "Another remix owns this destination. Retry after it finishes.");
5785
5880
  try {
5786
5881
  process.kill(pid, 0);
5787
5882
  } catch (probe) {
5788
5883
  if (probe.code === "ESRCH") {
5789
- await fs17.unlink(lock);
5884
+ await fs18.unlink(lock);
5790
5885
  continue;
5791
5886
  }
5792
5887
  }
@@ -5872,13 +5967,13 @@ function pinRemixDependencies(pkg, version) {
5872
5967
  return warnings;
5873
5968
  }
5874
5969
  async function installRemixDependencies(cwd, pm, version, log, runner = childRun) {
5875
- const pkgPath = path16.join(cwd, "package.json");
5876
- const pkg = JSON.parse(await fs17.readFile(pkgPath, "utf8"));
5970
+ const pkgPath = path17.join(cwd, "package.json");
5971
+ const pkg = JSON.parse(await fs18.readFile(pkgPath, "utf8"));
5877
5972
  const warnings = pinRemixDependencies(pkg, version);
5878
- await fs17.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
5973
+ await fs18.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
5879
5974
  log.step(`Restoring dependencies with ${pm}; pinning ${CLI_PACKAGE}@${version} (${CLI_CHANNEL}).`);
5880
5975
  const args = pm === "npm" ? ["install", "--workspaces=false", "--no-audit", "--no-fund"] : pm === "pnpm" ? ["install", "--ignore-workspace", "--no-frozen-lockfile"] : ["install"];
5881
- if (pm === "yarn") await fs17.writeFile(path16.join(cwd, "yarn.lock"), "", { flag: "wx" }).catch((err) => {
5976
+ if (pm === "yarn") await fs18.writeFile(path17.join(cwd, "yarn.lock"), "", { flag: "wx" }).catch((err) => {
5882
5977
  if (err.code !== "EEXIST") throw err;
5883
5978
  });
5884
5979
  await runner(cwd, pm, args);
@@ -5912,27 +6007,27 @@ function rebindRemixConfig(content, slug) {
5912
6007
  async function configureIdentity(state, log) {
5913
6008
  const cwd = state.directory, meta = state.project;
5914
6009
  await writeProject(meta, cwd);
5915
- const configFile = path16.join(cwd, "src", "genex.config.ts");
5916
- const config = await fs17.readFile(configFile, "utf8").catch((err) => {
6010
+ const configFile = path17.join(cwd, "src", "genex.config.ts");
6011
+ const config = await fs18.readFile(configFile, "utf8").catch((err) => {
5917
6012
  if (err.code === "ENOENT") return null;
5918
6013
  throw err;
5919
6014
  });
5920
- if (config !== null) await fs17.writeFile(configFile, rebindRemixConfig(config, meta.slug));
6015
+ if (config !== null) await fs18.writeFile(configFile, rebindRemixConfig(config, meta.slug));
5921
6016
  await writeGameConfigFiles(meta, log, cwd);
5922
6017
  await writeGitignore(cwd, log);
5923
6018
  await installRemixProfile(cwd, state.apiUrl, log);
5924
- const manifestFile = path16.join(cwd, "package.json");
5925
- const manifest = JSON.parse(await fs17.readFile(manifestFile, "utf8"));
6019
+ const manifestFile = path17.join(cwd, "package.json");
6020
+ const manifest = JSON.parse(await fs18.readFile(manifestFile, "utf8"));
5926
6021
  manifest.genex.remixSource = { projectId: state.source.projectId, slug: state.source.slug, sourceCommitSha: state.source.sourceCommitSha, version: state.source.version, versionCertainty: state.source.versionCertainty };
5927
- await fs17.writeFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
6022
+ await fs18.writeFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
5928
6023
  const possible = [];
5929
6024
  const quoted = new RegExp(`(["'\`])(?:${state.source.slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|${state.source.projectId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})\\1`);
5930
6025
  const scan = async (dir) => {
5931
- for (const entry of await fs17.readdir(dir, { withFileTypes: true })) {
6026
+ for (const entry of await fs18.readdir(dir, { withFileTypes: true })) {
5932
6027
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
5933
- const file = path16.join(dir, entry.name);
6028
+ const file = path17.join(dir, entry.name);
5934
6029
  if (entry.isDirectory()) await scan(file);
5935
- else if (entry.isFile() && /\.(?:[cm]?[jt]sx?|html)$/.test(entry.name) && (await fs17.stat(file)).size < 1024 * 1024 && quoted.test(await fs17.readFile(file, "utf8"))) possible.push(path16.relative(cwd, file));
6030
+ else if (entry.isFile() && /\.(?:[cm]?[jt]sx?|html)$/.test(entry.name) && (await fs18.stat(file)).size < 1024 * 1024 && quoted.test(await fs18.readFile(file, "utf8"))) possible.push(path17.relative(cwd, file));
5936
6031
  }
5937
6032
  };
5938
6033
  await scan(cwd);
@@ -5957,7 +6052,7 @@ async function runRemix(opts, deps = {}) {
5957
6052
  const apiUrl = getApiUrl(opts.apiUrl), dashboardUrl = new URL(getAuthUrl(opts.authUrl)).origin;
5958
6053
  if (opts.sourceOnly && !opts.destination) await assertReferenceParents(originalCwd);
5959
6054
  const selected = parseRemixSource(opts.remixSource, apiUrl, dashboardUrl);
5960
- let directory = opts.destination ? path16.resolve(originalCwd, opts.destination) : !opts.sourceOnly ? path16.resolve(originalCwd, `${selected.slug}-remix`) : void 0;
6055
+ let directory = opts.destination ? path17.resolve(originalCwd, opts.destination) : !opts.sourceOnly ? path17.resolve(originalCwd, `${selected.slug}-remix`) : void 0;
5961
6056
  if (directory) state = await checkDestination(directory);
5962
6057
  resumed = state !== null;
5963
6058
  if (state && (state.apiUrl !== apiUrl || state.sourceOnly !== Boolean(opts.sourceOnly) || ![state.requestedSlug, state.source.slug].includes(selected.slug) || opts.operationId && state.operationId !== opts.operationId || opts.name && state.name !== opts.name || selected.version && selected.version !== state.source.version)) throw new RemixError("operation_mismatch", "This folder belongs to a different remix operation. Resume with its original source/options or choose a fresh folder.");
@@ -5975,7 +6070,7 @@ async function runRemix(opts, deps = {}) {
5975
6070
  log.step(`Resolving source for ${selected.slug}\u2026`);
5976
6071
  const body = await responseJson(await request(`${apiUrl}/api/projects/${encodeURIComponent(selected.slug)}/source?${query}`, { headers, redirect: "error" }));
5977
6072
  validateSource(body, Boolean(opts.sourceOnly));
5978
- directory ??= path16.resolve(originalCwd, ".genex", "refs", `${body.source.slug}-${body.source.sourceCommitSha.slice(0, 12)}`);
6073
+ directory ??= path17.resolve(originalCwd, ".genex", "refs", `${body.source.slug}-${body.source.sourceCommitSha.slice(0, 12)}`);
5979
6074
  const existing = await checkDestination(directory);
5980
6075
  if (existing) {
5981
6076
  if (!existing.sourceOnly || !opts.sourceOnly || existing.apiUrl !== apiUrl || existing.source.sourceCommitSha !== body.source.sourceCommitSha || existing.source.projectId !== body.source.projectId) throw new RemixError("destination_occupied", "This reference destination already belongs to another operation. Choose a fresh folder.");
@@ -5983,21 +6078,21 @@ async function runRemix(opts, deps = {}) {
5983
6078
  resumed = true;
5984
6079
  } else {
5985
6080
  state = { schema: 1, operationId: opts.operationId ?? crypto4.randomUUID(), directory, apiUrl, dashboardUrl, requestedSlug: selected.slug, source: body.source, sourceToken: body.sourceToken, sourceOnly: Boolean(opts.sourceOnly), cliVersion: getCliVersion(), name: opts.name?.trim() || `${body.source.title.slice(0, 74)} remix`, stage: "download", warnings: [...body.source.warnings] };
5986
- await fs17.mkdir(directory, { recursive: true });
5987
- if ((await fs17.readdir(directory)).length) throw new RemixError("destination_occupied", "The destination changed during setup. Choose a fresh folder.");
5988
- await fs17.mkdir(path16.join(directory, ".genex"), { mode: 448 });
6081
+ await fs18.mkdir(directory, { recursive: true });
6082
+ if ((await fs18.readdir(directory)).length) throw new RemixError("destination_occupied", "The destination changed during setup. Choose a fresh folder.");
6083
+ await fs18.mkdir(path17.join(directory, ".genex"), { mode: 448 });
5989
6084
  await saveState(state);
5990
6085
  }
5991
6086
  }
5992
6087
  unlock = await acquireLock(state.directory);
5993
6088
  state = await readState(state.directory) ?? state;
5994
6089
  if (resumed && state.stage === "download") await renewSource(state, request, headers);
5995
- const staging = path16.join(state.directory, ".genex", "remix-stage");
6090
+ const staging = path17.join(state.directory, ".genex", "remix-stage");
5996
6091
  if (state.stage === "download") {
5997
- await fs17.rm(staging, { recursive: true, force: true });
5998
- await fs17.mkdir(staging, { recursive: true });
5999
- const archive = path16.join(staging, "source.zip"), sourceDir = path16.join(staging, "source");
6000
- await fs17.mkdir(sourceDir);
6092
+ await fs18.rm(staging, { recursive: true, force: true });
6093
+ await fs18.mkdir(staging, { recursive: true });
6094
+ const archive = path17.join(staging, "source.zip"), sourceDir = path17.join(staging, "source");
6095
+ await fs18.mkdir(sourceDir);
6001
6096
  const archiveUrl = new URL(state.source.archiveUrl, state.apiUrl);
6002
6097
  if (archiveUrl.origin !== new URL(state.apiUrl).origin || !archiveUrl.pathname.startsWith("/api/projects/")) throw new RemixError("invalid_source_response", "Source archive must be served by the selected Genex API.");
6003
6098
  log.step(`Downloading ${state.source.slug} at ${state.source.sourceCommitSha.slice(0, 12)}\u2026`);
@@ -6012,26 +6107,26 @@ async function runRemix(opts, deps = {}) {
6012
6107
  if (!state.sourceOnly) {
6013
6108
  let pkg;
6014
6109
  try {
6015
- pkg = JSON.parse(await fs17.readFile(path16.join(sourceDir, "package.json"), "utf8"));
6110
+ pkg = JSON.parse(await fs18.readFile(path17.join(sourceDir, "package.json"), "utf8"));
6016
6111
  } catch (error) {
6017
- const staticEntry = await fs17.stat(path16.join(sourceDir, "index.html")).catch(() => null);
6112
+ const staticEntry = await fs18.stat(path17.join(sourceDir, "index.html")).catch(() => null);
6018
6113
  if (error.code !== "ENOENT" || !staticEntry?.isFile()) throw new RemixError("source_unavailable", "Source has neither a readable package.json nor a static index.html. Use --source-only to inspect it.");
6019
6114
  pkg = { name: manifestName(state.directory), private: true };
6020
- await fs17.writeFile(path16.join(sourceDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
6115
+ await fs18.writeFile(path17.join(sourceDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
6021
6116
  state.warnings.push("This static game had no package.json; added a minimal private manifest for the local CLI. Its source and index.html remain unchanged.");
6022
6117
  }
6023
6118
  if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) throw new RemixError("source_unavailable", "Source package.json is not a project manifest.");
6024
6119
  }
6025
- state.transferEntries = await fs17.readdir(sourceDir);
6120
+ state.transferEntries = await fs18.readdir(sourceDir);
6026
6121
  state.stage = "transfer";
6027
6122
  await saveState(state);
6028
6123
  }
6029
6124
  if (state.stage === "transfer") {
6030
6125
  for (const entry of state.transferEntries ?? []) {
6031
- const from = path16.join(staging, "source", entry), to = path16.join(state.directory, entry);
6032
- if (await fs17.lstat(from).catch(() => null)) {
6033
- if (await fs17.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `A file appeared at ${entry} during source preparation. It was left untouched.`);
6034
- await fs17.rename(from, to);
6126
+ const from = path17.join(staging, "source", entry), to = path17.join(state.directory, entry);
6127
+ if (await fs18.lstat(from).catch(() => null)) {
6128
+ if (await fs18.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `A file appeared at ${entry} during source preparation. It was left untouched.`);
6129
+ await fs18.rename(from, to);
6035
6130
  }
6036
6131
  }
6037
6132
  if (!state.sourceOnly) {
@@ -6040,7 +6135,7 @@ async function runRemix(opts, deps = {}) {
6040
6135
  }
6041
6136
  state.stage = state.sourceOnly ? "prepared" : "configure";
6042
6137
  await saveState(state);
6043
- await fs17.rm(staging, { recursive: true, force: true });
6138
+ await fs18.rm(staging, { recursive: true, force: true });
6044
6139
  }
6045
6140
  process.chdir(state.directory);
6046
6141
  if (state.stage === "configure") {
@@ -6111,8 +6206,8 @@ async function runRemix(opts, deps = {}) {
6111
6206
  }
6112
6207
 
6113
6208
  // src/commands/init.ts
6114
- import fs18 from "fs/promises";
6115
- import path17 from "path";
6209
+ import fs19 from "fs/promises";
6210
+ import path18 from "path";
6116
6211
 
6117
6212
  // src/lib/printed.ts
6118
6213
  var printed = /* @__PURE__ */ new WeakSet();
@@ -6153,7 +6248,7 @@ var TOOLING_ENTRIES = /* @__PURE__ */ new Set([
6153
6248
  ]);
6154
6249
  async function listPreexistingEntries(dir) {
6155
6250
  try {
6156
- return (await fs18.readdir(dir)).filter((name) => !TOOLING_ENTRIES.has(name)).sort();
6251
+ return (await fs19.readdir(dir)).filter((name) => !TOOLING_ENTRIES.has(name)).sort();
6157
6252
  } catch {
6158
6253
  return [];
6159
6254
  }
@@ -6220,8 +6315,8 @@ async function runInit(opts) {
6220
6315
  let totalNew = 0;
6221
6316
  let totalUpdated = 0;
6222
6317
  for (const t of targets) {
6223
- const src = t.full ? templatesDir : path17.join(templatesDir, "skills");
6224
- const dest = t.full ? t.baseDir : path17.join(t.baseDir, "skills");
6318
+ const src = t.full ? templatesDir : path18.join(templatesDir, "skills");
6319
+ const dest = t.full ? t.baseDir : path18.join(t.baseDir, "skills");
6225
6320
  const family = remixing ? skillFamilyFilter("remix") : converting ? skillFamilyFilter("tools", { hosted: true }) : skillFamilyFilter("game");
6226
6321
  if (remixing) await pruneRemixWorkflow(t.baseDir);
6227
6322
  const { copied, updated } = await copyTemplates(src, dest, {
@@ -6229,8 +6324,8 @@ async function runInit(opts) {
6229
6324
  exclude: ["controllers", "motion", "asset-viewer", "blender-service"],
6230
6325
  filter: t.full ? family : (rel) => family(`skills/${rel}`)
6231
6326
  });
6232
- await pruneRemovedSkills(path17.join(t.baseDir, "skills"), log);
6233
- await writeSkillsMarker(path17.join(t.baseDir, "skills"));
6327
+ await pruneRemovedSkills(path18.join(t.baseDir, "skills"), log);
6328
+ await writeSkillsMarker(path18.join(t.baseDir, "skills"));
6234
6329
  const added = copied.length - updated.length;
6235
6330
  totalNew += added;
6236
6331
  totalUpdated += updated.length;
@@ -6287,7 +6382,7 @@ async function runInit(opts) {
6287
6382
  if (email) log.plain(` signed in as ${c.cyan(email)}`);
6288
6383
  };
6289
6384
  await echoIdentity();
6290
- const projectName = opts.name?.trim() || path17.basename(process.cwd());
6385
+ const projectName = opts.name?.trim() || path18.basename(process.cwd());
6291
6386
  const create = (bearer) => createDraftProject({
6292
6387
  apiUrl,
6293
6388
  token: bearer,
@@ -6338,7 +6433,7 @@ async function runInit(opts) {
6338
6433
  if (await writeToolsContract(process.cwd(), { hosted: true })) {
6339
6434
  log.dim(" wrote the Genex Tools publishing rules into AGENTS.md (managed block)");
6340
6435
  }
6341
- await ensureEnvVar(path17.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
6436
+ await ensureEnvVar(path18.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
6342
6437
  await reportCliEvent(apiUrl, token, "tools_converted");
6343
6438
  }
6344
6439
  if (!remixing) warnPreexisting(log, preexisting);
@@ -6365,9 +6460,9 @@ async function runInit(opts) {
6365
6460
  }
6366
6461
 
6367
6462
  // src/commands/link.ts
6368
- import fs19 from "fs/promises";
6463
+ import fs20 from "fs/promises";
6369
6464
  import os6 from "os";
6370
- import path18 from "path";
6465
+ import path19 from "path";
6371
6466
  async function runLink(opts) {
6372
6467
  const log = createLogger({ quiet: opts.quiet });
6373
6468
  log.plain(c.bold("genex link"));
@@ -6448,7 +6543,7 @@ async function runLink(opts) {
6448
6543
  }
6449
6544
  async function isEmptyDir(cwd) {
6450
6545
  try {
6451
- const entries = await fs19.readdir(cwd);
6546
+ const entries = await fs20.readdir(cwd);
6452
6547
  return entries.every((e) => e === ".git" || e === ".genex" || e === ".DS_Store");
6453
6548
  } catch {
6454
6549
  return false;
@@ -6462,13 +6557,13 @@ async function downloadSource(apiUrl, token, project, log) {
6462
6557
  return false;
6463
6558
  }
6464
6559
  log.step(`Downloading the ${grant.sourceRef === "preview" ? "draft" : "published"} source\u2026`);
6465
- const staging = await fs19.mkdtemp(path18.join(os6.tmpdir(), "genex-link-"));
6466
- const fresh = path18.join(staging, "source");
6560
+ const staging = await fs20.mkdtemp(path19.join(os6.tmpdir(), "genex-link-"));
6561
+ const fresh = path19.join(staging, "source");
6467
6562
  try {
6468
6563
  if (!await cloneSource(grant, fresh, log)) return false;
6469
- await fs19.rm(path18.join(fresh, ".git"), { recursive: true, force: true });
6470
- for (const entry of await fs19.readdir(fresh)) {
6471
- await fs19.cp(path18.join(fresh, entry), path18.join(process.cwd(), entry), {
6564
+ await fs20.rm(path19.join(fresh, ".git"), { recursive: true, force: true });
6565
+ for (const entry of await fs20.readdir(fresh)) {
6566
+ await fs20.cp(path19.join(fresh, entry), path19.join(process.cwd(), entry), {
6472
6567
  recursive: true,
6473
6568
  force: true
6474
6569
  });
@@ -6476,28 +6571,28 @@ async function downloadSource(apiUrl, token, project, log) {
6476
6571
  log.success("Downloaded.");
6477
6572
  return true;
6478
6573
  } finally {
6479
- await fs19.rm(staging, { recursive: true, force: true }).catch(() => {
6574
+ await fs20.rm(staging, { recursive: true, force: true }).catch(() => {
6480
6575
  });
6481
6576
  }
6482
6577
  }
6483
6578
  async function ensureSlugEnv(slug, log, cwd = process.cwd()) {
6484
- const file = path18.join(cwd, ".env");
6579
+ const file = path19.join(cwd, ".env");
6485
6580
  let content;
6486
6581
  try {
6487
- content = await fs19.readFile(file, "utf8");
6582
+ content = await fs20.readFile(file, "utf8");
6488
6583
  } catch {
6489
6584
  return;
6490
6585
  }
6491
6586
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
6492
6587
  const m = content.match(re);
6493
6588
  if (!m) {
6494
- await fs19.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
6589
+ await fs20.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
6495
6590
  `);
6496
6591
  log.dim(` added VITE_GENEX_SLUG=${slug} to .env`);
6497
6592
  return;
6498
6593
  }
6499
6594
  if (m[2].trim() === slug) return;
6500
- await fs19.writeFile(file, content.replace(re, `$1${slug}`));
6595
+ await fs20.writeFile(file, content.replace(re, `$1${slug}`));
6501
6596
  log.dim(` updated .env: VITE_GENEX_SLUG=${slug} (was ${m[2].trim()})`);
6502
6597
  }
6503
6598
  async function fetchOwnProject(apiUrl, token, slug, log) {
@@ -6551,8 +6646,8 @@ async function listOwnSlugs(apiUrl, token, log) {
6551
6646
  }
6552
6647
 
6553
6648
  // src/commands/pull.ts
6554
- import fs20 from "fs/promises";
6555
- import path19 from "path";
6649
+ import fs21 from "fs/promises";
6650
+ import path20 from "path";
6556
6651
  import os7 from "os";
6557
6652
  function isMachineLocal(entry) {
6558
6653
  if (entry === ".genex" || entry === "node_modules" || entry === ".git") return true;
@@ -6612,23 +6707,23 @@ async function runPull(opts) {
6612
6707
  process.exitCode = 1;
6613
6708
  return;
6614
6709
  }
6615
- const staging = await fs20.mkdtemp(path19.join(os7.tmpdir(), "genex-pull-"));
6616
- const fresh = path19.join(staging, "source");
6710
+ const staging = await fs21.mkdtemp(path20.join(os7.tmpdir(), "genex-pull-"));
6711
+ const fresh = path20.join(staging, "source");
6617
6712
  try {
6618
6713
  if (!await cloneSource(grant, fresh, log)) {
6619
6714
  process.exitCode = 1;
6620
6715
  return;
6621
6716
  }
6622
- await fs20.rm(path19.join(fresh, ".git"), { recursive: true, force: true });
6717
+ await fs21.rm(path20.join(fresh, ".git"), { recursive: true, force: true });
6623
6718
  const kept = await keepReplaced(cwd, log);
6624
6719
  await replaceTree(cwd, fresh);
6625
6720
  if (kept) {
6626
6721
  log.plain("");
6627
- log.info(`What was here is kept at ${c.cyan(path19.relative(cwd, kept) || kept)}`);
6722
+ log.info(`What was here is kept at ${c.cyan(path20.relative(cwd, kept) || kept)}`);
6628
6723
  log.dim(" Nothing was thrown away \u2014 re-apply from there, or delete it when you are done.");
6629
6724
  }
6630
6725
  } finally {
6631
- await fs20.rm(staging, { recursive: true, force: true }).catch(() => {
6726
+ await fs21.rm(staging, { recursive: true, force: true }).catch(() => {
6632
6727
  });
6633
6728
  }
6634
6729
  if (await hasRemixProfile(cwd)) await installRemixProfile(cwd, apiUrl, log);
@@ -6648,13 +6743,13 @@ async function runPull(opts) {
6648
6743
  }
6649
6744
  async function keepReplaced(cwd, log) {
6650
6745
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6651
- const dest = path19.join(cwd, ".genex", `replaced-${stamp}`);
6746
+ const dest = path20.join(cwd, ".genex", `replaced-${stamp}`);
6652
6747
  try {
6653
- const entries = (await fs20.readdir(cwd)).filter((e) => !isMachineLocal(e));
6748
+ const entries = (await fs21.readdir(cwd)).filter((e) => !isMachineLocal(e));
6654
6749
  if (entries.length === 0) return null;
6655
- await fs20.mkdir(dest, { recursive: true });
6750
+ await fs21.mkdir(dest, { recursive: true });
6656
6751
  for (const entry of entries) {
6657
- await fs20.cp(path19.join(cwd, entry), path19.join(dest, entry), { recursive: true });
6752
+ await fs21.cp(path20.join(cwd, entry), path20.join(dest, entry), { recursive: true });
6658
6753
  }
6659
6754
  return dest;
6660
6755
  } catch (err) {
@@ -6664,19 +6759,19 @@ async function keepReplaced(cwd, log) {
6664
6759
  }
6665
6760
  }
6666
6761
  async function replaceTree(dest, src) {
6667
- for (const entry of await fs20.readdir(dest)) {
6762
+ for (const entry of await fs21.readdir(dest)) {
6668
6763
  if (isMachineLocal(entry)) continue;
6669
- await fs20.rm(path19.join(dest, entry), { recursive: true, force: true });
6764
+ await fs21.rm(path20.join(dest, entry), { recursive: true, force: true });
6670
6765
  }
6671
- for (const entry of await fs20.readdir(src)) {
6766
+ for (const entry of await fs21.readdir(src)) {
6672
6767
  if (isMachineLocal(entry)) continue;
6673
- await fs20.cp(path19.join(src, entry), path19.join(dest, entry), { recursive: true });
6768
+ await fs21.cp(path20.join(src, entry), path20.join(dest, entry), { recursive: true });
6674
6769
  }
6675
6770
  }
6676
6771
 
6677
6772
  // src/commands/rename.ts
6678
- import fs21 from "fs/promises";
6679
- import path20 from "path";
6773
+ import fs22 from "fs/promises";
6774
+ import path21 from "path";
6680
6775
  async function runRename(opts) {
6681
6776
  const log = createLogger({ quiet: opts.quiet });
6682
6777
  log.plain(c.bold("genex rename"));
@@ -6764,23 +6859,23 @@ async function runRename(opts) {
6764
6859
  log.info("Run `genex preview` (or `publish`) to rebuild \u2014 the new slug is baked into the bundle.");
6765
6860
  }
6766
6861
  async function rewriteSlugEnv(from, to, log, cwd = process.cwd()) {
6767
- const file = path20.join(cwd, ".env");
6862
+ const file = path21.join(cwd, ".env");
6768
6863
  let content;
6769
6864
  try {
6770
- content = await fs21.readFile(file, "utf8");
6865
+ content = await fs22.readFile(file, "utf8");
6771
6866
  } catch {
6772
6867
  return;
6773
6868
  }
6774
6869
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
6775
6870
  if (!re.test(content)) return;
6776
- await fs21.writeFile(file, content.replace(re, `$1${to}`));
6871
+ await fs22.writeFile(file, content.replace(re, `$1${to}`));
6777
6872
  log.dim(` .env: VITE_GENEX_SLUG=${to} (was ${from})`);
6778
6873
  }
6779
6874
  async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
6780
- const file = path20.join(cwd, "src", "genex.config.ts");
6875
+ const file = path21.join(cwd, "src", "genex.config.ts");
6781
6876
  let content;
6782
6877
  try {
6783
- content = await fs21.readFile(file, "utf8");
6878
+ content = await fs22.readFile(file, "utf8");
6784
6879
  } catch {
6785
6880
  return;
6786
6881
  }
@@ -6790,7 +6885,7 @@ async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
6790
6885
  log.warn(` src/genex.config.ts has no "${from}" literal \u2014 check its slug by hand.`);
6791
6886
  return;
6792
6887
  }
6793
- await fs21.writeFile(file, content.replace(quoted, `"${to}"`));
6888
+ await fs22.writeFile(file, content.replace(quoted, `"${to}"`));
6794
6889
  log.dim(` src/genex.config.ts: baked slug -> ${to}`);
6795
6890
  }
6796
6891
 
@@ -6969,7 +7064,7 @@ async function runMakeRemixable(opts) {
6969
7064
  return;
6970
7065
  }
6971
7066
  log.step("Copying your game's source to the public repo\u2026");
6972
- if (!await pushWorktree(process.cwd(), data.pushUrl, true, log)) {
7067
+ if (await pushWorktree(process.cwd(), data.pushUrl, true, log) !== true) {
6973
7068
  process.exitCode = 1;
6974
7069
  return;
6975
7070
  }
@@ -7614,7 +7709,16 @@ async function runSave(opts) {
7614
7709
  process.exitCode = 1;
7615
7710
  return;
7616
7711
  }
7617
- if (!await pushWorktree(process.cwd(), data.pushUrl, true, log, WORKSPACE_SOURCE_REF)) {
7712
+ const saved = await pushWorktree(
7713
+ process.cwd(),
7714
+ data.pushUrl,
7715
+ true,
7716
+ log,
7717
+ WORKSPACE_SOURCE_REF,
7718
+ void 0,
7719
+ true
7720
+ );
7721
+ if (saved !== true) {
7618
7722
  process.exitCode = 1;
7619
7723
  return;
7620
7724
  }
@@ -7775,8 +7879,8 @@ async function runRollback(opts) {
7775
7879
  }
7776
7880
 
7777
7881
  // src/commands/generate.ts
7778
- import fs22 from "fs/promises";
7779
- import path21 from "path";
7882
+ import fs23 from "fs/promises";
7883
+ import path22 from "path";
7780
7884
  import { PNG as PNG4 } from "pngjs";
7781
7885
 
7782
7886
  // src/lib/glass.ts
@@ -8080,7 +8184,7 @@ var isRemoteRef = (value) => /^(https?:\/\/|data:)/i.test(value);
8080
8184
  async function inlineLocalImage(filePath, flag) {
8081
8185
  let bytes;
8082
8186
  try {
8083
- bytes = await fs22.readFile(filePath);
8187
+ bytes = await fs23.readFile(filePath);
8084
8188
  } catch {
8085
8189
  return { ok: false, error: `Couldn't read the ${flag} file at ${filePath}.` };
8086
8190
  }
@@ -8090,7 +8194,7 @@ async function inlineLocalImage(filePath, flag) {
8090
8194
  error: `${flag} file is ${(bytes.length / 1048576).toFixed(1)} MB \u2014 over the ~4 MB inline limit. Downscale/compress it first, or pass an asset URL instead.`
8091
8195
  };
8092
8196
  }
8093
- const mime = IMAGE_MIME_BY_EXT[path21.extname(filePath).toLowerCase()] ?? "image/png";
8197
+ const mime = IMAGE_MIME_BY_EXT[path22.extname(filePath).toLowerCase()] ?? "image/png";
8094
8198
  return { ok: true, dataUri: `data:${mime};base64,${bytes.toString("base64")}` };
8095
8199
  }
8096
8200
  var SKYBOX_ENVIRONMENT_SUFFIX = ". The image contains ONLY sky: cloud, atmosphere, light, weather and distant haze at the horizon. Every structure, object, plant and ground surface is outside the frame.";
@@ -8276,7 +8380,7 @@ async function runGenerate(kind, opts) {
8276
8380
  let typedPrompt = opts.prompt?.trim();
8277
8381
  if (!typedPrompt && kind === "model" && opts.imageUrl) {
8278
8382
  const ref = opts.imageUrl.startsWith("data:") ? "local image" : opts.imageUrl;
8279
- typedPrompt = `from image: ${path21.basename(ref).slice(0, 120)}`;
8383
+ typedPrompt = `from image: ${path22.basename(ref).slice(0, 120)}`;
8280
8384
  }
8281
8385
  if (kind === "model" && opts.texture !== void 0 && !MODEL_TEXTURE_TIERS.includes(opts.texture)) {
8282
8386
  log.error(`--texture ${opts.texture} is a character texture size. \`genex model\` takes a texture TIER: ${MODEL_TEXTURE_TIERS.join("|")} (default detailed).`);
@@ -8325,7 +8429,7 @@ async function runGenerate(kind, opts) {
8325
8429
  return;
8326
8430
  }
8327
8431
  try {
8328
- const bytes = await fs22.readFile(opts.inpaintUrl);
8432
+ const bytes = await fs23.readFile(opts.inpaintUrl);
8329
8433
  opts = { ...opts, inpaintUrl: `data:image/png;base64,${bytes.toString("base64")}` };
8330
8434
  } catch {
8331
8435
  log.error(`Couldn't read the --inpaint mask at ${opts.inpaintUrl}.`);
@@ -8519,7 +8623,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
8519
8623
  return;
8520
8624
  }
8521
8625
  await recordTerminal(view.id, "completed", files.map((f) => f.url));
8522
- await fs22.mkdir(outDir, { recursive: true });
8626
+ await fs23.mkdir(outDir, { recursive: true });
8523
8627
  const solved = [];
8524
8628
  for (let i = 0; i < files.length; i++) {
8525
8629
  const f = files[i];
@@ -8551,8 +8655,8 @@ async function reportGlassTerminal(view, outDir, log, json) {
8551
8655
  });
8552
8656
  continue;
8553
8657
  }
8554
- const outPath = path21.join(outDir, `glass-${i + 1}.png`);
8555
- await fs22.writeFile(outPath, PNG4.sync.write(r.png));
8658
+ const outPath = path22.join(outDir, `glass-${i + 1}.png`);
8659
+ await fs23.writeFile(outPath, PNG4.sync.write(r.png));
8556
8660
  solved.push({
8557
8661
  path: outPath,
8558
8662
  url: f.url,
@@ -9070,8 +9174,8 @@ function writeJson(value) {
9070
9174
  }
9071
9175
 
9072
9176
  // src/commands/model-sub.ts
9073
- import fs23 from "fs/promises";
9074
- import path22 from "path";
9177
+ import fs24 from "fs/promises";
9178
+ import path23 from "path";
9075
9179
  var MODEL_SUBCOMMANDS = ["segment", "rig", "animate", "import"];
9076
9180
  function apiErrorMessage(data, fallback) {
9077
9181
  if (typeof data !== "object" || data === null) return fallback;
@@ -9086,29 +9190,29 @@ async function importModelFile(args) {
9086
9190
  const { log } = args;
9087
9191
  const filePath = args.filePath.trim();
9088
9192
  if (!/\.glb$/i.test(filePath)) {
9089
- log.error(`\`import\` takes a .glb file (binary glTF 2.0). Export ${path22.basename(filePath) || "the model"} as GLB first \u2014 Blender: File \u2192 Export \u2192 glTF 2.0, format "glTF Binary".`);
9193
+ log.error(`\`import\` takes a .glb file (binary glTF 2.0). Export ${path23.basename(filePath) || "the model"} as GLB first \u2014 Blender: File \u2192 Export \u2192 glTF 2.0, format "glTF Binary".`);
9090
9194
  return null;
9091
9195
  }
9092
9196
  let bytes;
9093
9197
  try {
9094
- bytes = await fs23.readFile(filePath);
9198
+ bytes = await fs24.readFile(filePath);
9095
9199
  } catch {
9096
9200
  log.error(`Couldn't read ${filePath}.`);
9097
9201
  return null;
9098
9202
  }
9099
9203
  if (bytes.byteLength > MODEL_IMPORT_MAX_BYTES) {
9100
- log.error(`${path22.basename(filePath)} is ${(bytes.byteLength / 1e6).toFixed(1)} MB; imports are capped at ${MODEL_IMPORT_MAX_BYTES / 1024 / 1024} MB. Shrink its textures (they are usually the bulk) and try again.`);
9204
+ log.error(`${path23.basename(filePath)} is ${(bytes.byteLength / 1e6).toFixed(1)} MB; imports are capped at ${MODEL_IMPORT_MAX_BYTES / 1024 / 1024} MB. Shrink its textures (they are usually the bulk) and try again.`);
9101
9205
  return null;
9102
9206
  }
9103
9207
  if (bytes.byteLength < 12 || bytes.readUInt32LE(0) !== GLB_MAGIC) {
9104
- log.error(`${path22.basename(filePath)} is not a GLB (no glTF magic). A .gltf + .bin pair must be exported as one binary .glb.`);
9208
+ log.error(`${path23.basename(filePath)} is not a GLB (no glTF magic). A .gltf + .bin pair must be exported as one binary .glb.`);
9105
9209
  return null;
9106
9210
  }
9107
9211
  const headers = { "Content-Type": "application/json", Authorization: `Bearer ${args.token}` };
9108
9212
  const minted = await apiFetch(`${args.apiUrl}/api/generations/import`, {
9109
9213
  method: "POST",
9110
9214
  headers,
9111
- body: JSON.stringify({ filename: path22.basename(filePath), bytes: bytes.byteLength, contentType: "model/gltf-binary" })
9215
+ body: JSON.stringify({ filename: path23.basename(filePath), bytes: bytes.byteLength, contentType: "model/gltf-binary" })
9112
9216
  });
9113
9217
  if (printedStructuredError(minted)) return null;
9114
9218
  if (!minted.ok) {
@@ -9117,7 +9221,7 @@ async function importModelFile(args) {
9117
9221
  return null;
9118
9222
  }
9119
9223
  const { id, uploadUrl, url } = await minted.json();
9120
- log.dim(` uploading ${path22.basename(filePath)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
9224
+ log.dim(` uploading ${path23.basename(filePath)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
9121
9225
  const put = await fetch(uploadUrl, {
9122
9226
  method: "PUT",
9123
9227
  headers: { "Content-Type": "model/gltf-binary", "Content-Length": String(bytes.byteLength) },
@@ -9265,7 +9369,7 @@ async function runModelAnimate(opts) {
9265
9369
  }
9266
9370
 
9267
9371
  // src/commands/wait.ts
9268
- import path23 from "path";
9372
+ import path24 from "path";
9269
9373
  var TERMINAL2 = /* @__PURE__ */ new Set(["completed", "failed"]);
9270
9374
  async function runWait(opts) {
9271
9375
  if (opts.all) return runWaitAll(opts);
@@ -9396,7 +9500,7 @@ async function runWaitAll(opts) {
9396
9500
  if (v?.status !== "completed" || !v.files?.length) continue;
9397
9501
  const local = localDeliveryFor("tools", opts, e.prompt || e.kind);
9398
9502
  if (!local) continue;
9399
- const outDir = path23.join(path23.relative(process.cwd(), cwd) || ".", local.outDir);
9503
+ const outDir = path24.join(path24.relative(process.cwd(), cwd) || ".", local.outDir);
9400
9504
  const target = { kind: e.kind, prompt: local.prompt, id: e.id, outDir };
9401
9505
  const missing = await undeliveredFiles(v.files, target);
9402
9506
  if (missing.length === 0) continue;
@@ -9491,8 +9595,8 @@ async function toRow(e, v, cwd) {
9491
9595
  }
9492
9596
 
9493
9597
  // src/commands/controller.ts
9494
- import fs25 from "fs/promises";
9495
- import path25 from "path";
9598
+ import fs26 from "fs/promises";
9599
+ import path26 from "path";
9496
9600
 
9497
9601
  // ../../packages/meshy-animation-catalog/src/index.ts
9498
9602
  import { createHash } from "crypto";
@@ -18622,9 +18726,9 @@ function searchMeshyAnimations(query, options = {}) {
18622
18726
  }
18623
18727
 
18624
18728
  // src/lib/anims.ts
18625
- import fs24 from "fs/promises";
18626
- import path24 from "path";
18627
- var ANIMS_DEST = path24.join("public", "assets", "anims");
18729
+ import fs25 from "fs/promises";
18730
+ import path25 from "path";
18731
+ var ANIMS_DEST = path25.join("public", "assets", "anims");
18628
18732
  var HIDDEN_TAG = "reference";
18629
18733
  async function runAnims(opts) {
18630
18734
  const log = createLogger({ quiet: opts.quiet });
@@ -18640,7 +18744,7 @@ async function runAnims(opts) {
18640
18744
  printCatalog(log, manifest, selectors);
18641
18745
  return;
18642
18746
  }
18643
- const controllerMarker = path24.join(root, "src", "controllers", "character");
18747
+ const controllerMarker = path25.join(root, "src", "controllers", "character");
18644
18748
  if (!await exists3(controllerMarker)) {
18645
18749
  log.error(
18646
18750
  `No character controller in this game (missing ${c.cyan("src/controllers/character/")}).`
@@ -18649,11 +18753,11 @@ async function runAnims(opts) {
18649
18753
  process.exitCode = 1;
18650
18754
  return;
18651
18755
  }
18652
- const destDir = path24.join(root, ANIMS_DEST);
18653
- const gameManifestPath = path24.join(destDir, "manifest.json");
18756
+ const destDir = path25.join(root, ANIMS_DEST);
18757
+ const gameManifestPath = path25.join(destDir, "manifest.json");
18654
18758
  if (opts.reset) {
18655
- await fs24.rm(destDir, { recursive: true, force: true });
18656
- log.step(`Cleared ${c.cyan(ANIMS_DEST + path24.sep)} (--reset)`);
18759
+ await fs25.rm(destDir, { recursive: true, force: true });
18760
+ log.step(`Cleared ${c.cyan(ANIMS_DEST + path25.sep)} (--reset)`);
18657
18761
  }
18658
18762
  if (selectors.length === 0) {
18659
18763
  const installed = await readGameManifest(gameManifestPath);
@@ -18691,35 +18795,35 @@ async function runAnims(opts) {
18691
18795
  }
18692
18796
  }
18693
18797
  const wanted = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
18694
- const cacheDir = path24.join(
18798
+ const cacheDir = path25.join(
18695
18799
  opts.cacheDir ?? getAnimsCacheDir(),
18696
18800
  `${manifest.library}-v${manifest.version}`
18697
18801
  );
18698
- await fs24.mkdir(cacheDir, { recursive: true });
18699
- await fs24.mkdir(destDir, { recursive: true });
18802
+ await fs25.mkdir(cacheDir, { recursive: true });
18803
+ await fs25.mkdir(destDir, { recursive: true });
18700
18804
  const base = getAnimsBase(opts.animsBase);
18701
18805
  let installedCount = 0;
18702
18806
  let presentCount = 0;
18703
18807
  let addedBytes = 0;
18704
18808
  const failures = [];
18705
18809
  for (const entry of wanted) {
18706
- const dest = path24.join(destDir, entry.file);
18810
+ const dest = path25.join(destDir, entry.file);
18707
18811
  if (await hasSize(dest, entry.bytes)) {
18708
18812
  presentCount++;
18709
18813
  continue;
18710
18814
  }
18711
18815
  try {
18712
- const cached = path24.join(cacheDir, entry.file);
18816
+ const cached = path25.join(cacheDir, entry.file);
18713
18817
  if (!await hasSize(cached, entry.bytes)) {
18714
18818
  const res = await fetch(base + entry.file);
18715
18819
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
18716
18820
  const buf = Buffer.from(await res.arrayBuffer());
18717
- await fs24.writeFile(cached, buf);
18821
+ await fs25.writeFile(cached, buf);
18718
18822
  }
18719
- await fs24.copyFile(cached, dest);
18823
+ await fs25.copyFile(cached, dest);
18720
18824
  installedCount++;
18721
18825
  addedBytes += entry.bytes;
18722
- log.dim(` ${path24.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
18826
+ log.dim(` ${path25.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
18723
18827
  } catch (err) {
18724
18828
  failures.push(`${entry.name} (${err instanceof Error ? err.message : String(err)})`);
18725
18829
  }
@@ -18735,13 +18839,13 @@ async function runAnims(opts) {
18735
18839
  version: manifest.version,
18736
18840
  clips: [...union].sort((a, b) => a.localeCompare(b))
18737
18841
  };
18738
- await fs24.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
18842
+ await fs25.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
18739
18843
  log.plain("");
18740
18844
  const parts = [`${installedCount} clip${installedCount === 1 ? "" : "s"} installed`];
18741
18845
  if (presentCount > 0) parts.push(`${presentCount} already present`);
18742
18846
  if (bundledSkips > 0) parts.push(`${bundledSkips} already bundled in animation-library.glb`);
18743
18847
  log.success(
18744
- `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path24.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
18848
+ `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path25.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
18745
18849
  );
18746
18850
  for (const [selector, entries] of resolved) {
18747
18851
  const names = entries.filter((e) => !coreNames.has(e.name)).map((e) => e.name);
@@ -18773,8 +18877,8 @@ async function loadManifest(baseOverride) {
18773
18877
  }
18774
18878
  } catch {
18775
18879
  }
18776
- const snapshotPath = path24.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
18777
- const manifest = JSON.parse(await fs24.readFile(snapshotPath, "utf8"));
18880
+ const snapshotPath = path25.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
18881
+ const manifest = JSON.parse(await fs25.readFile(snapshotPath, "utf8"));
18778
18882
  return { manifest, source: "snapshot" };
18779
18883
  }
18780
18884
  function resolveSelectors(manifest, selectors) {
@@ -18892,21 +18996,21 @@ function printCatalog(log, manifest, selectors) {
18892
18996
  }
18893
18997
  async function readGameManifest(file) {
18894
18998
  try {
18895
- return JSON.parse(await fs24.readFile(file, "utf8"));
18999
+ return JSON.parse(await fs25.readFile(file, "utf8"));
18896
19000
  } catch {
18897
19001
  return null;
18898
19002
  }
18899
19003
  }
18900
19004
  async function hasSize(file, bytes) {
18901
19005
  try {
18902
- return (await fs24.stat(file)).size === bytes;
19006
+ return (await fs25.stat(file)).size === bytes;
18903
19007
  } catch {
18904
19008
  return false;
18905
19009
  }
18906
19010
  }
18907
19011
  async function exists3(p) {
18908
19012
  try {
18909
- await fs24.access(p);
19013
+ await fs25.access(p);
18910
19014
  return true;
18911
19015
  } catch {
18912
19016
  return false;
@@ -19107,8 +19211,8 @@ var CONTROLLER_FILE_SETS = {
19107
19211
  ]
19108
19212
  }
19109
19213
  };
19110
- var CODE_DEST = path25.join("src", "controllers");
19111
- var ASSETS_DEST = path25.join("public", "assets");
19214
+ var CODE_DEST = path26.join("src", "controllers");
19215
+ var ASSETS_DEST = path26.join("public", "assets");
19112
19216
  async function runController(opts) {
19113
19217
  const log = createLogger({ quiet: opts.quiet });
19114
19218
  if (opts.kind?.trim() === "anims") {
@@ -19125,31 +19229,31 @@ async function runController(opts) {
19125
19229
  process.exitCode = 1;
19126
19230
  return;
19127
19231
  }
19128
- const srcDir = path25.join(getTemplatesDir(), "controllers");
19232
+ const srcDir = path26.join(getTemplatesDir(), "controllers");
19129
19233
  const root = opts.cwd ?? process.cwd();
19130
19234
  const set = CONTROLLER_FILE_SETS[kind];
19131
19235
  log.plain(c.bold(`genex controller ${kind}`));
19132
19236
  log.plain("");
19133
- log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path25.sep)}`);
19237
+ log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path26.sep)}`);
19134
19238
  const plan = [
19135
- ...set.code.map((rel) => ({ from: rel, rel: path25.join(CODE_DEST, rel) })),
19239
+ ...set.code.map((rel) => ({ from: rel, rel: path26.join(CODE_DEST, rel) })),
19136
19240
  ...set.assets.map((rel) => ({
19137
19241
  from: rel,
19138
- rel: path25.join(ASSETS_DEST, path25.basename(rel))
19242
+ rel: path26.join(ASSETS_DEST, path26.basename(rel))
19139
19243
  }))
19140
19244
  ];
19141
19245
  let copied = 0;
19142
19246
  let skipped = 0;
19143
19247
  try {
19144
19248
  for (const file of plan) {
19145
- const dest = path25.join(root, file.rel);
19249
+ const dest = path26.join(root, file.rel);
19146
19250
  if (!opts.force && await exists4(dest)) {
19147
19251
  skipped++;
19148
19252
  log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
19149
19253
  continue;
19150
19254
  }
19151
- await fs25.mkdir(path25.dirname(dest), { recursive: true });
19152
- await fs25.copyFile(path25.join(srcDir, file.from), dest);
19255
+ await fs26.mkdir(path26.dirname(dest), { recursive: true });
19256
+ await fs26.copyFile(path26.join(srcDir, file.from), dest);
19153
19257
  copied++;
19154
19258
  log.dim(` ${file.rel}`);
19155
19259
  }
@@ -19202,7 +19306,7 @@ async function runController(opts) {
19202
19306
  for (const line of set.sketch) {
19203
19307
  log.dim(` ${line}`);
19204
19308
  }
19205
- if (kind === "character" && !await exists4(path25.join(root, ASSETS_DEST, "meshy-character.json"))) {
19309
+ if (kind === "character" && !await exists4(path26.join(root, ASSETS_DEST, "meshy-character.json"))) {
19206
19310
  log.plain("");
19207
19311
  log.plain(
19208
19312
  ` ${stepOffset + 3}. This game has no generated character yet \u2014 ${c.cyan(
@@ -19234,9 +19338,9 @@ async function installMeshyCharacterManifest(args) {
19234
19338
  throw new Error("The API returned an invalid Meshy character manifest.");
19235
19339
  }
19236
19340
  assertCompleteMeshyControllerPack(manifest);
19237
- const destination = path25.join(args.root, ASSETS_DEST, "meshy-character.json");
19238
- await fs25.mkdir(path25.dirname(destination), { recursive: true });
19239
- await fs25.writeFile(
19341
+ const destination = path26.join(args.root, ASSETS_DEST, "meshy-character.json");
19342
+ await fs26.mkdir(path26.dirname(destination), { recursive: true });
19343
+ await fs26.writeFile(
19240
19344
  destination,
19241
19345
  `${JSON.stringify(manifest, null, 2)}
19242
19346
  `
@@ -19381,14 +19485,14 @@ function assertCompleteMeshyControllerPack(manifest) {
19381
19485
  }
19382
19486
  async function installFallbackAvatar(args) {
19383
19487
  const { root, srcDir, log } = args;
19384
- const dest = path25.join(root, ASSETS_DEST, "avatar.vrm");
19385
- await fs25.mkdir(path25.dirname(dest), { recursive: true });
19386
- await fs25.copyFile(path25.join(srcDir, "assets", "default-avatar.vrm"), dest);
19488
+ const dest = path26.join(root, ASSETS_DEST, "avatar.vrm");
19489
+ await fs26.mkdir(path26.dirname(dest), { recursive: true });
19490
+ await fs26.copyFile(path26.join(srcDir, "assets", "default-avatar.vrm"), dest);
19387
19491
  log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
19388
19492
  }
19389
19493
  async function exists4(p) {
19390
19494
  try {
19391
- await fs25.access(p);
19495
+ await fs26.access(p);
19392
19496
  return true;
19393
19497
  } catch {
19394
19498
  return false;
@@ -19396,8 +19500,8 @@ async function exists4(p) {
19396
19500
  }
19397
19501
 
19398
19502
  // src/commands/character.ts
19399
- import fs26 from "fs/promises";
19400
- import path26 from "path";
19503
+ import fs27 from "fs/promises";
19504
+ import path27 from "path";
19401
19505
  function exactAnimation(selector) {
19402
19506
  const trimmed = selector.trim();
19403
19507
  if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
@@ -19477,7 +19581,7 @@ function showAmbiguity(selector, candidates, log, json = false) {
19477
19581
  }
19478
19582
  process.exitCode = 1;
19479
19583
  }
19480
- var INSTALLED_MANIFEST = path26.join("public", "assets", "meshy-character.json");
19584
+ var INSTALLED_MANIFEST = path27.join("public", "assets", "meshy-character.json");
19481
19585
  async function resolveAdoptTarget(selector) {
19482
19586
  const trimmed = selector?.trim();
19483
19587
  if (trimmed && !trimmed.endsWith(".json")) {
@@ -19486,7 +19590,7 @@ async function resolveAdoptTarget(selector) {
19486
19590
  const file = trimmed ?? INSTALLED_MANIFEST;
19487
19591
  let raw;
19488
19592
  try {
19489
- raw = await fs26.readFile(file, "utf8");
19593
+ raw = await fs27.readFile(file, "utf8");
19490
19594
  } catch {
19491
19595
  return {
19492
19596
  ok: false,
@@ -19607,7 +19711,7 @@ async function runCharacterImport(opts) {
19607
19711
  opts,
19608
19712
  ctx,
19609
19713
  kind: "character",
19610
- prompt: `Import ${path26.basename(filePath)} as a rigged character`,
19714
+ prompt: `Import ${path27.basename(filePath)} as a rigged character`,
19611
19715
  createPath: "/api/characters/import",
19612
19716
  body,
19613
19717
  quote: price,
@@ -20087,22 +20191,22 @@ async function context2(opts) {
20087
20191
  const project = await readProject();
20088
20192
  return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
20089
20193
  }
20090
- async function readVideo(path32, log) {
20194
+ async function readVideo(path33, log) {
20091
20195
  let bytes;
20092
20196
  try {
20093
- bytes = await readFile(path32);
20197
+ bytes = await readFile(path33);
20094
20198
  } catch {
20095
- log.error(`Can't read ${path32}.`);
20199
+ log.error(`Can't read ${path33}.`);
20096
20200
  return null;
20097
20201
  }
20098
20202
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
20099
- log.error(`${basename(path32)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
20203
+ log.error(`${basename(path33)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
20100
20204
  return null;
20101
20205
  }
20102
20206
  return bytes;
20103
20207
  }
20104
- async function uploadVideo(apiUrl, token, characterId, path32, bytes, log) {
20105
- const contentType = /\.mov$/i.test(path32) ? "video/quicktime" : "video/mp4";
20208
+ async function uploadVideo(apiUrl, token, characterId, path33, bytes, log) {
20209
+ const contentType = /\.mov$/i.test(path33) ? "video/quicktime" : "video/mp4";
20106
20210
  const minted = await apiFetch(
20107
20211
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
20108
20212
  {
@@ -20117,7 +20221,7 @@ async function uploadVideo(apiUrl, token, characterId, path32, bytes, log) {
20117
20221
  return null;
20118
20222
  }
20119
20223
  const { uploadUrl, videoUrl } = await minted.json();
20120
- log.dim(` uploading ${basename(path32)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
20224
+ log.dim(` uploading ${basename(path33)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
20121
20225
  const put = await fetch(uploadUrl, {
20122
20226
  method: "PUT",
20123
20227
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -20433,8 +20537,8 @@ function rank(items, query) {
20433
20537
  }
20434
20538
 
20435
20539
  // src/commands/motion.ts
20436
- import fs27 from "fs/promises";
20437
- import path27 from "path";
20540
+ import fs28 from "fs/promises";
20541
+ import path28 from "path";
20438
20542
 
20439
20543
  // src/lib/motion/npz.ts
20440
20544
  import zlib from "zlib";
@@ -21685,7 +21789,7 @@ async function motionGen(opts, log) {
21685
21789
  }
21686
21790
  if (opts.constraintsPath !== void 0) {
21687
21791
  try {
21688
- const raw = await fs27.readFile(opts.constraintsPath, "utf8");
21792
+ const raw = await fs28.readFile(opts.constraintsPath, "utf8");
21689
21793
  generationOptions.constraints = JSON.parse(raw);
21690
21794
  } catch {
21691
21795
  log.error(`Couldn't read the --constraints JSON at ${opts.constraintsPath}.`);
@@ -21709,10 +21813,10 @@ async function motionGen(opts, log) {
21709
21813
  async function expandTakes(selectors) {
21710
21814
  const out = [];
21711
21815
  for (const sel of selectors) {
21712
- const st = await fs27.stat(sel).catch(() => null);
21816
+ const st = await fs28.stat(sel).catch(() => null);
21713
21817
  if (st?.isDirectory()) {
21714
- const names = await fs27.readdir(sel);
21715
- for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path27.join(sel, n));
21818
+ const names = await fs28.readdir(sel);
21819
+ for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path28.join(sel, n));
21716
21820
  } else if (st?.isFile()) {
21717
21821
  out.push(sel);
21718
21822
  } else {
@@ -21747,7 +21851,7 @@ async function motionVerify(opts, log) {
21747
21851
  let gates = DEFAULT_GATES;
21748
21852
  if (opts.gatesPath) {
21749
21853
  try {
21750
- gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs27.readFile(opts.gatesPath, "utf8")));
21854
+ gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs28.readFile(opts.gatesPath, "utf8")));
21751
21855
  } catch {
21752
21856
  log.error(`Couldn't read the --gates JSON at ${opts.gatesPath}.`);
21753
21857
  process.exitCode = 1;
@@ -21769,9 +21873,9 @@ async function motionVerify(opts, log) {
21769
21873
  }
21770
21874
  const reports = [];
21771
21875
  for (const file of files) {
21772
- const stem = path27.basename(file).replace(/\.npz$/, "");
21876
+ const stem = path28.basename(file).replace(/\.npz$/, "");
21773
21877
  try {
21774
- reports.push(analyzeTake(stem, await fs27.readFile(file), gates));
21878
+ reports.push(analyzeTake(stem, await fs28.readFile(file), gates));
21775
21879
  } catch (err) {
21776
21880
  reports.push({
21777
21881
  take: stem,
@@ -21809,7 +21913,7 @@ async function motionCompile(opts, log) {
21809
21913
  let cfg = DEFAULT_MOTION_CONFIG;
21810
21914
  if (opts.configPath) {
21811
21915
  try {
21812
- const patch = JSON.parse(await fs27.readFile(opts.configPath, "utf8"));
21916
+ const patch = JSON.parse(await fs28.readFile(opts.configPath, "utf8"));
21813
21917
  cfg = { ...DEFAULT_MOTION_CONFIG, ...patch, idleLoop: { ...DEFAULT_MOTION_CONFIG.idleLoop, ...patch.idleLoop } };
21814
21918
  } catch {
21815
21919
  log.error(`Couldn't read the --config JSON at ${opts.configPath}.`);
@@ -21827,16 +21931,16 @@ async function motionCompile(opts, log) {
21827
21931
  }
21828
21932
  const inputs = [];
21829
21933
  for (const file of files) {
21830
- const stem = path27.basename(file).replace(/\.npz$/, "");
21934
+ const stem = path28.basename(file).replace(/\.npz$/, "");
21831
21935
  try {
21832
- inputs.push({ stem, take: loadTake(await fs27.readFile(file)) });
21936
+ inputs.push({ stem, take: loadTake(await fs28.readFile(file)) });
21833
21937
  } catch (err) {
21834
21938
  log.error(`${stem}: ${err instanceof Error ? err.message : String(err)}`);
21835
21939
  process.exitCode = 1;
21836
21940
  return;
21837
21941
  }
21838
21942
  }
21839
- const setName = opts.set ?? path27.basename(opts.out).replace(/\.json$/, "");
21943
+ const setName = opts.set ?? path28.basename(opts.out).replace(/\.json$/, "");
21840
21944
  let result;
21841
21945
  try {
21842
21946
  result = compileSet(inputs, setName, cfg);
@@ -21851,9 +21955,9 @@ async function motionCompile(opts, log) {
21851
21955
  process.exitCode = 1;
21852
21956
  return;
21853
21957
  }
21854
- await fs27.mkdir(path27.dirname(path27.resolve(opts.out)), { recursive: true });
21958
+ await fs28.mkdir(path28.dirname(path28.resolve(opts.out)), { recursive: true });
21855
21959
  const json = JSON.stringify(result.data);
21856
- await fs27.writeFile(opts.out, json);
21960
+ await fs28.writeFile(opts.out, json);
21857
21961
  if (opts.json) {
21858
21962
  writeJson({ out: opts.out, set: setName, tracks: Object.keys(result.data.gaits), bytes: json.length });
21859
21963
  return;
@@ -21871,9 +21975,9 @@ var MOTION_RUNTIME_FILES = [
21871
21975
  var MOTION_PRESETS = {
21872
21976
  rifle: ["sets/rifle.json", "sets/jumps.json"]
21873
21977
  };
21874
- var MOTION_DEST = path27.join("src", "motion");
21978
+ var MOTION_DEST = path28.join("src", "motion");
21875
21979
  async function motionInstall(opts, log) {
21876
- const srcDir = path27.join(getTemplatesDir(), "motion");
21980
+ const srcDir = path28.join(getTemplatesDir(), "motion");
21877
21981
  const root = opts.cwd ?? process.cwd();
21878
21982
  const preset = opts.set;
21879
21983
  if (preset !== void 0 && !MOTION_PRESETS[preset]) {
@@ -21884,21 +21988,21 @@ async function motionInstall(opts, log) {
21884
21988
  const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
21885
21989
  log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
21886
21990
  log.plain("");
21887
- log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path27.sep)}`);
21991
+ log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path28.sep)}`);
21888
21992
  let copied = 0, skipped = 0;
21889
21993
  try {
21890
21994
  for (const rel of files) {
21891
- const dest = path27.join(root, MOTION_DEST, rel);
21892
- const exists5 = await fs27.access(dest).then(() => true, () => false);
21995
+ const dest = path28.join(root, MOTION_DEST, rel);
21996
+ const exists5 = await fs28.access(dest).then(() => true, () => false);
21893
21997
  if (!opts.force && exists5) {
21894
21998
  skipped++;
21895
- log.dim(` skipped ${path27.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
21999
+ log.dim(` skipped ${path28.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
21896
22000
  continue;
21897
22001
  }
21898
- await fs27.mkdir(path27.dirname(dest), { recursive: true });
21899
- await fs27.copyFile(path27.join(srcDir, rel), dest);
22002
+ await fs28.mkdir(path28.dirname(dest), { recursive: true });
22003
+ await fs28.copyFile(path28.join(srcDir, rel), dest);
21900
22004
  copied++;
21901
- log.dim(` ${path27.join(MOTION_DEST, rel)}`);
22005
+ log.dim(` ${path28.join(MOTION_DEST, rel)}`);
21902
22006
  }
21903
22007
  } catch (err) {
21904
22008
  log.error(`Copy failed: ${String(err)}`);
@@ -21939,7 +22043,7 @@ async function motionConstraints(opts, log) {
21939
22043
  }
21940
22044
  const doc = directionConstraint(dir, speed, duration);
21941
22045
  const out = opts.out ?? "constraints.json";
21942
- await fs27.writeFile(out, JSON.stringify(doc));
22046
+ await fs28.writeFile(out, JSON.stringify(doc));
21943
22047
  if (opts.json) {
21944
22048
  writeJson({ out, dir: dirName, speed, duration, waypoints: doc[0].frame_indices.length });
21945
22049
  return;
@@ -21976,14 +22080,14 @@ async function runMotion(opts) {
21976
22080
  }
21977
22081
 
21978
22082
  // src/commands/blender.ts
21979
- import fs28 from "fs/promises";
21980
- import path28 from "path";
22083
+ import fs29 from "fs/promises";
22084
+ import path29 from "path";
21981
22085
  var SUBS2 = ["demo", "exec", "snap", "scene", "import", "export", "reset", "mcp", "serve", "seat", "release"];
21982
22086
  var DEFAULT_OUT_DIR = "assets/blender";
21983
22087
  async function writeB64(dir, name, b64) {
21984
- await fs28.mkdir(dir, { recursive: true });
21985
- const p = path28.join(dir, name);
21986
- await fs28.writeFile(p, Buffer.from(b64, "base64"));
22088
+ await fs29.mkdir(dir, { recursive: true });
22089
+ const p = path29.join(dir, name);
22090
+ await fs29.writeFile(p, Buffer.from(b64, "base64"));
21987
22091
  return p;
21988
22092
  }
21989
22093
  function reportScene(log, s) {
@@ -22067,7 +22171,7 @@ async function runBlender(opts) {
22067
22171
  log.plain(rest.join("\n"));
22068
22172
  return 1;
22069
22173
  }
22070
- const outDir = path28.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
22174
+ const outDir = path29.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
22071
22175
  const mode = opts.mode;
22072
22176
  if (mode !== void 0 && !isRenderMode(mode)) {
22073
22177
  log.error(`Unknown --mode ${mode}. Use one of: ${RENDER_MODES.join(", ")}.`);
@@ -22107,14 +22211,14 @@ async function runBlender(opts) {
22107
22211
  return 0;
22108
22212
  }
22109
22213
  case "export": {
22110
- const target = opts.out ?? path28.join(outDir, "scene.glb");
22214
+ const target = opts.out ?? path29.join(outDir, "scene.glb");
22111
22215
  const r = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
22112
22216
  if (!r.glbBase64) {
22113
22217
  log.error(`/export answered with no GLB bytes${r.uploaded ? " (it was uploaded, not inlined)" : ""}`);
22114
22218
  return 1;
22115
22219
  }
22116
- await fs28.mkdir(path28.dirname(target), { recursive: true });
22117
- await fs28.writeFile(target, Buffer.from(r.glbBase64, "base64"));
22220
+ await fs29.mkdir(path29.dirname(target), { recursive: true });
22221
+ await fs29.writeFile(target, Buffer.from(r.glbBase64, "base64"));
22118
22222
  log.success(`Exported ${r.bytes ?? 0} bytes`);
22119
22223
  log.plain(` ${c.cyan(target)}`);
22120
22224
  return 0;
@@ -22147,12 +22251,12 @@ async function runBlender(opts) {
22147
22251
  return 1;
22148
22252
  }
22149
22253
  try {
22150
- script = await fs28.readFile(opts.input, "utf8");
22254
+ script = await fs29.readFile(opts.input, "utf8");
22151
22255
  } catch {
22152
22256
  log.error(`Can't read ${opts.input}`);
22153
22257
  return 1;
22154
22258
  }
22155
- label = path28.basename(opts.input);
22259
+ label = path29.basename(opts.input);
22156
22260
  }
22157
22261
  const r = await blenderCall(base, "/exec", { script, ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
22158
22262
  if (r.stdout?.trim()) log.plain(r.stdout.trimEnd());
@@ -22271,9 +22375,9 @@ print(f"castle: {n} objects")
22271
22375
  `;
22272
22376
 
22273
22377
  // src/commands/asset-new.ts
22274
- import fs29 from "fs";
22378
+ import fs30 from "fs";
22275
22379
  import fsp from "fs/promises";
22276
- import path29 from "path";
22380
+ import path30 from "path";
22277
22381
  import { pathToFileURL } from "url";
22278
22382
  var EXTRA_FILES = [
22279
22383
  "genex-asset.example.json",
@@ -22367,7 +22471,7 @@ Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-buil
22367
22471
  }
22368
22472
  async function runAssetNew(options) {
22369
22473
  const log = createLogger();
22370
- const cwd = options.dir ? path29.resolve(options.dir) : process.cwd();
22474
+ const cwd = options.dir ? path30.resolve(options.dir) : process.cwd();
22371
22475
  const slug = options.assetSlug;
22372
22476
  if (!slug) {
22373
22477
  log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
@@ -22377,14 +22481,14 @@ async function runAssetNew(options) {
22377
22481
  log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
22378
22482
  return 1;
22379
22483
  }
22380
- const templateDir = path29.join(getTemplatesDir(), "asset-viewer");
22381
- if (!fs29.existsSync(templateDir)) {
22484
+ const templateDir = path30.join(getTemplatesDir(), "asset-viewer");
22485
+ if (!fs30.existsSync(templateDir)) {
22382
22486
  log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
22383
22487
  return 1;
22384
22488
  }
22385
- const manifestTools = await import(pathToFileURL(path29.join(templateDir, "tools", "emit-manifest.mjs")).href);
22489
+ const manifestTools = await import(pathToFileURL(path30.join(templateDir, "tools", "emit-manifest.mjs")).href);
22386
22490
  const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
22387
- const lockPath = path29.join(templateDir, "shared-files.sha256.json");
22491
+ const lockPath = path30.join(templateDir, "shared-files.sha256.json");
22388
22492
  const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
22389
22493
  const actual = hashSharedFiles(templateDir);
22390
22494
  const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
@@ -22397,8 +22501,8 @@ async function runAssetNew(options) {
22397
22501
  const triBand = parseBand(options.triBand ?? "500-8000");
22398
22502
  const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
22399
22503
  const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
22400
- const outDir = path29.resolve(cwd, options.out ?? slug);
22401
- if (fs29.existsSync(outDir) && fs29.readdirSync(outDir).length > 0 && !options.force) {
22504
+ const outDir = path30.resolve(cwd, options.out ?? slug);
22505
+ if (fs30.existsSync(outDir) && fs30.readdirSync(outDir).length > 0 && !options.force) {
22402
22506
  log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
22403
22507
  return 1;
22404
22508
  }
@@ -22426,24 +22530,24 @@ async function runAssetNew(options) {
22426
22530
  };
22427
22531
  await fsp.mkdir(outDir, { recursive: true });
22428
22532
  for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
22429
- const to = path29.join(outDir, rel);
22430
- await fsp.mkdir(path29.dirname(to), { recursive: true });
22431
- await fsp.copyFile(path29.join(templateDir, rel), to);
22533
+ const to = path30.join(outDir, rel);
22534
+ await fsp.mkdir(path30.dirname(to), { recursive: true });
22535
+ await fsp.copyFile(path30.join(templateDir, rel), to);
22432
22536
  }
22433
- const pkg = fillTemplate(await fsp.readFile(path29.join(templateDir, "package.json"), "utf8"), {
22537
+ const pkg = fillTemplate(await fsp.readFile(path30.join(templateDir, "package.json"), "utf8"), {
22434
22538
  slug,
22435
22539
  name,
22436
22540
  version
22437
22541
  });
22438
- await fsp.writeFile(path29.join(outDir, "package.json"), pkg, "utf8");
22439
- await fsp.writeFile(path29.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
22440
- await fsp.writeFile(path29.join(outDir, ".gitignore"), GITIGNORE, "utf8");
22542
+ await fsp.writeFile(path30.join(outDir, "package.json"), pkg, "utf8");
22543
+ await fsp.writeFile(path30.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
22544
+ await fsp.writeFile(path30.join(outDir, ".gitignore"), GITIGNORE, "utf8");
22441
22545
  await fsp.writeFile(
22442
- path29.join(outDir, "DESIGN.md"),
22546
+ path30.join(outDir, "DESIGN.md"),
22443
22547
  designDoc({ name, slug, sizeMeters, triBand, holder }),
22444
22548
  "utf8"
22445
22549
  );
22446
- const placeholder = await fsp.readFile(path29.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
22550
+ const placeholder = await fsp.readFile(path30.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
22447
22551
  const seeded = seedAssetSource(placeholder, {
22448
22552
  slug,
22449
22553
  name,
@@ -22454,8 +22558,8 @@ async function runAssetNew(options) {
22454
22558
  pascalCase
22455
22559
  });
22456
22560
  const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
22457
- await fsp.mkdir(path29.join(outDir, "src", "asset"), { recursive: true });
22458
- await fsp.writeFile(path29.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
22561
+ await fsp.mkdir(path30.join(outDir, "src", "asset"), { recursive: true });
22562
+ await fsp.writeFile(path30.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
22459
22563
  const copied = hashSharedFiles(outDir);
22460
22564
  const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
22461
22565
  if (mismatched.length) {
@@ -22463,7 +22567,7 @@ async function runAssetNew(options) {
22463
22567
  return 1;
22464
22568
  }
22465
22569
  await fsp.writeFile(
22466
- path29.join(outDir, PARITY_FILENAME),
22570
+ path30.join(outDir, PARITY_FILENAME),
22467
22571
  JSON.stringify(
22468
22572
  {
22469
22573
  note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
@@ -22507,10 +22611,10 @@ async function runAssetNew(options) {
22507
22611
  }
22508
22612
 
22509
22613
  // src/commands/tools.ts
22510
- import path31 from "path";
22614
+ import path32 from "path";
22511
22615
 
22512
22616
  // src/commands/doctor.ts
22513
- import path30 from "path";
22617
+ import path31 from "path";
22514
22618
  var LANE_ORDER = [
22515
22619
  "model",
22516
22620
  "image",
@@ -22791,7 +22895,7 @@ async function fetchLegalStatus(apiUrl, token) {
22791
22895
  }
22792
22896
  async function firstSkillsMarker() {
22793
22897
  for (const target of resolveAgentTargets()) {
22794
- const marker = await readSkillsMarker(path30.join(target.baseDir, "skills"));
22898
+ const marker = await readSkillsMarker(path31.join(target.baseDir, "skills"));
22795
22899
  if (marker) return marker;
22796
22900
  }
22797
22901
  return null;
@@ -22842,8 +22946,8 @@ async function runTools(opts) {
22842
22946
  let totalNew = 0;
22843
22947
  let totalUpdated = 0;
22844
22948
  for (const t of targets) {
22845
- const dest = path31.join(t.baseDir, "skills");
22846
- const { copied, updated } = await copyTemplates(path31.join(templatesDir, "skills"), dest, {
22949
+ const dest = path32.join(t.baseDir, "skills");
22950
+ const { copied, updated } = await copyTemplates(path32.join(templatesDir, "skills"), dest, {
22847
22951
  filter: (rel) => skillFamilyFilter("tools")(`skills/${rel}`)
22848
22952
  });
22849
22953
  await pruneRemovedSkills(dest, log);