@genex-ai/cli-demo 1.15.0-dev.577 → 1.15.1-dev.578

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,27 +2,42 @@
2
2
  import {
3
3
  RENDER_MODES,
4
4
  SHEET_FORMATS,
5
+ acceptUrl,
5
6
  acquireSeat,
6
7
  apiFetch,
7
8
  blenderCall,
8
9
  blenderEndpoint,
9
10
  blenderSetupHint,
11
+ cloneSource,
12
+ fetchCloneGrant,
10
13
  fetchSignedInEmail,
11
14
  getWorkspacePath,
15
+ inHostedSession,
12
16
  isRenderMode,
17
+ isStale,
18
+ isTermsRefusal,
19
+ mayForceOverAnotherDevice,
13
20
  printedStructuredError,
14
21
  readProject,
22
+ readRemoteSource,
15
23
  readUserToken,
16
24
  readWorkspace,
25
+ reportForceRefused,
26
+ reportStale,
27
+ reportTermsRefusal,
17
28
  restrictFilePermissions,
18
29
  rotateRejectedEnv,
30
+ run,
31
+ runAccept,
19
32
  setWorkspaceHeader,
20
33
  sheetExt,
21
34
  sheetOf,
35
+ sourceTreeHash,
36
+ urlHasEmbeddedCredentials,
22
37
  writeProject,
23
38
  writeUserToken,
24
39
  writeWorkspace
25
- } from "./chunk-YSX7SJFM.js";
40
+ } from "./chunk-JGX6YRC7.js";
26
41
  import {
27
42
  CLI_CHANNEL,
28
43
  DEFAULT_API_URL,
@@ -1760,177 +1775,9 @@ async function runInit(opts) {
1760
1775
  }
1761
1776
 
1762
1777
  // src/commands/link.ts
1763
- import fs9 from "fs/promises";
1764
- import os6 from "os";
1765
- import path9 from "path";
1766
-
1767
- // src/lib/source-sync.ts
1768
1778
  import fs8 from "fs/promises";
1769
- import path8 from "path";
1770
1779
  import os5 from "os";
1771
-
1772
- // src/utils/run.ts
1773
- import { spawn as spawn2 } from "child_process";
1774
- var WIN_SHELL_COMMANDS = /* @__PURE__ */ new Set(["npm", "npx"]);
1775
- function run(cmd, args, env) {
1776
- const shell = process.platform === "win32" && WIN_SHELL_COMMANDS.has(cmd);
1777
- return new Promise((resolve) => {
1778
- let child;
1779
- try {
1780
- child = spawn2(cmd, args, { env: env ? { ...process.env, ...env } : process.env, shell });
1781
- } catch {
1782
- resolve({ code: -1, out: "", err: `${cmd} not found` });
1783
- return;
1784
- }
1785
- let out = "";
1786
- let err = "";
1787
- child.stdout?.on("data", (d) => out += String(d));
1788
- child.stderr?.on("data", (d) => err += String(d));
1789
- child.on("error", () => resolve({ code: -1, out, err: `${cmd} not found` }));
1790
- child.on("close", (code) => resolve({ code: code ?? -1, out, err }));
1791
- });
1792
- }
1793
-
1794
- // src/lib/source-sync.ts
1795
- async function readRemoteSource(apiUrl, token, projectId) {
1796
- try {
1797
- const res = await apiFetch(`${apiUrl}/api/projects/${projectId}`, {
1798
- headers: { Authorization: `Bearer ${token}` }
1799
- });
1800
- if (!res.ok) return null;
1801
- const data = await res.json().catch(() => null);
1802
- if (!data?.project) return null;
1803
- return {
1804
- stagingCommit: data.project.stagingCommitSha ?? null,
1805
- commit: data.project.commitSha ?? null
1806
- };
1807
- } catch {
1808
- return null;
1809
- }
1810
- }
1811
- function isStale(local, remote) {
1812
- if (!local || !remote) return false;
1813
- return local !== remote;
1814
- }
1815
- function inHostedSession() {
1816
- return process.env.GENEX_HOSTED_SESSION === "1";
1817
- }
1818
- function mayForceOverAnotherDevice() {
1819
- return !inHostedSession();
1820
- }
1821
- function reportForceRefused(log) {
1822
- log.error("`--force` is not available inside a Genex chat session.");
1823
- log.dim(" It would replace work shipped from another machine, and that is not yours to discard here.");
1824
- log.dim(` Do this instead \u2014 it keeps both sides:`);
1825
- log.dim(` 1. ${c.cyan("npx genex pull --force")} takes the other machine's work (a copy of this folder is kept under .genex/replaced-*)`);
1826
- log.dim(` 2. redo your change on top of it \u2014 you know what you just changed`);
1827
- log.dim(` 3. ${c.cyan("npx genex preview")}`);
1828
- log.dim(" If the user explicitly wants their chat version to win, ask them to run `npx genex preview --force` themselves.");
1829
- }
1830
- function reportStale(log, slug, local, remote) {
1831
- log.error(`${c.cyan(slug)} was updated from another device \u2014 nothing was deployed.`);
1832
- if (remote && local) {
1833
- const short = remote.slice(0, 7) !== local.slice(0, 7);
1834
- const r = short ? remote.slice(0, 7) : remote;
1835
- const l = short ? local.slice(0, 7) : local;
1836
- log.dim(` the draft is on ${r}, this folder last shipped ${l}`);
1837
- }
1838
- log.dim(` ${c.cyan("npx genex pull")} \u2014 take the other device's work (refuses if you have unshipped changes)`);
1839
- log.dim(` ${c.cyan("npx genex preview --force")} \u2014 keep yours and replace theirs`);
1840
- }
1841
- async function sourceTreeHash(cwd) {
1842
- const gitDir = await fs8.mkdtemp(path8.join(os5.tmpdir(), "genex-tree-"));
1843
- const base = { GIT_DIR: gitDir };
1844
- try {
1845
- if ((await run("git", ["init", "-q"], base)).code !== 0) return null;
1846
- await fs8.writeFile(
1847
- path8.join(gitDir, "info", "exclude"),
1848
- ["node_modules/", "dist/", ".git/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n")
1849
- );
1850
- if ((await run("git", ["lfs", "version"], base)).code === 0) {
1851
- const filters = [
1852
- ["filter.lfs.clean", "git-lfs clean -- %f"],
1853
- ["filter.lfs.smudge", "git-lfs smudge -- %f"],
1854
- ["filter.lfs.process", "git-lfs filter-process"],
1855
- ["filter.lfs.required", "true"]
1856
- ];
1857
- for (const [key, value] of filters) await run("git", ["config", key, value], base);
1858
- }
1859
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path8.join(gitDir, "index-tree") };
1860
- if ((await run("git", ["add", "-A"], env)).code !== 0) return null;
1861
- const tree = (await run("git", ["write-tree"], env)).out.trim();
1862
- return /^[0-9a-f]{40}$/.test(tree) ? tree : null;
1863
- } catch {
1864
- return null;
1865
- } finally {
1866
- await fs8.rm(gitDir, { recursive: true, force: true }).catch(() => {
1867
- });
1868
- }
1869
- }
1870
- function urlHasEmbeddedCredentials(url) {
1871
- return /^[a-z][a-z0-9+.-]*:\/\/[^/@]+@/i.test(url);
1872
- }
1873
- function credentialHelperOff(url) {
1874
- if (!urlHasEmbeddedCredentials(url)) return {};
1875
- return { GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: "credential.helper", GIT_CONFIG_VALUE_0: "" };
1876
- }
1877
- async function fetchCloneGrant(apiUrl, token, projectId, log) {
1878
- let res;
1879
- try {
1880
- res = await apiFetch(`${apiUrl}/api/projects/${projectId}/push-token`, {
1881
- method: "POST",
1882
- headers: { Authorization: `Bearer ${token}`, "X-Genex-Source-Intent": "read" }
1883
- });
1884
- } catch (err) {
1885
- log.error(`Couldn't reach the API to authorize the source read: ${String(err)}`);
1886
- return null;
1887
- }
1888
- if (res.status === 401) {
1889
- log.error("Not authorized \u2014 your token may have expired. Re-run `genex auth`.");
1890
- return null;
1891
- }
1892
- if (!res.ok) {
1893
- log.error(`Couldn't authorize the source read (HTTP ${res.status}).`);
1894
- return null;
1895
- }
1896
- const data = await res.json().catch(() => null);
1897
- const url = data?.pushUrl ?? data?.cloneUrl;
1898
- if (!url) {
1899
- log.error("The API didn't return a source URL.");
1900
- return null;
1901
- }
1902
- return { cloneUrl: url, sourceRef: data?.sourceRef ?? null };
1903
- }
1904
- async function cloneSource(grant, dest, log) {
1905
- const args = grant.sourceRef ? ["clone", "--branch", grant.sourceRef, grant.cloneUrl, dest] : ["clone", grant.cloneUrl, dest];
1906
- const env = {
1907
- GIT_LFS_SKIP_SMUDGE: "1",
1908
- GIT_TERMINAL_PROMPT: "0",
1909
- ...credentialHelperOff(grant.cloneUrl)
1910
- };
1911
- const cloned = await run("git", args, env);
1912
- if (cloned.code !== 0) {
1913
- log.error("Couldn't download the game's source.");
1914
- log.dim(` git clone exited ${cloned.code}`);
1915
- return false;
1916
- }
1917
- const repo = { ...env, GIT_DIR: path8.join(dest, ".git"), GIT_WORK_TREE: dest };
1918
- const filters = [
1919
- ["filter.lfs.clean", "git-lfs clean -- %f"],
1920
- ["filter.lfs.smudge", "git-lfs smudge -- %f"],
1921
- ["filter.lfs.process", "git-lfs filter-process"],
1922
- ["filter.lfs.required", "true"]
1923
- ];
1924
- for (const [key, value] of filters) await run("git", ["config", key, value], repo);
1925
- const pulled = await run("git", ["lfs", "pull"], repo);
1926
- if (pulled.code !== 0) {
1927
- log.warn("Binary assets (models, textures, audio) are still pointer files \u2014 `git lfs pull` failed.");
1928
- log.dim(" The source is there; run `git lfs pull` in this folder once git-lfs works.");
1929
- }
1930
- return true;
1931
- }
1932
-
1933
- // src/commands/link.ts
1780
+ import path8 from "path";
1934
1781
  async function runLink(opts) {
1935
1782
  const log = createLogger({ quiet: opts.quiet });
1936
1783
  log.plain(c.bold("genex link"));
@@ -2010,7 +1857,7 @@ async function runLink(opts) {
2010
1857
  }
2011
1858
  async function isEmptyDir(cwd) {
2012
1859
  try {
2013
- const entries = await fs9.readdir(cwd);
1860
+ const entries = await fs8.readdir(cwd);
2014
1861
  return entries.every((e) => e === ".git" || e === ".genex" || e === ".DS_Store");
2015
1862
  } catch {
2016
1863
  return false;
@@ -2024,13 +1871,13 @@ async function downloadSource(apiUrl, token, project, log) {
2024
1871
  return false;
2025
1872
  }
2026
1873
  log.step(`Downloading the ${grant.sourceRef === "preview" ? "draft" : "published"} source\u2026`);
2027
- const staging = await fs9.mkdtemp(path9.join(os6.tmpdir(), "genex-link-"));
2028
- const fresh = path9.join(staging, "source");
1874
+ const staging = await fs8.mkdtemp(path8.join(os5.tmpdir(), "genex-link-"));
1875
+ const fresh = path8.join(staging, "source");
2029
1876
  try {
2030
1877
  if (!await cloneSource(grant, fresh, log)) return false;
2031
- await fs9.rm(path9.join(fresh, ".git"), { recursive: true, force: true });
2032
- for (const entry of await fs9.readdir(fresh)) {
2033
- await fs9.cp(path9.join(fresh, entry), path9.join(process.cwd(), entry), {
1878
+ await fs8.rm(path8.join(fresh, ".git"), { recursive: true, force: true });
1879
+ for (const entry of await fs8.readdir(fresh)) {
1880
+ await fs8.cp(path8.join(fresh, entry), path8.join(process.cwd(), entry), {
2034
1881
  recursive: true,
2035
1882
  force: true
2036
1883
  });
@@ -2038,28 +1885,28 @@ async function downloadSource(apiUrl, token, project, log) {
2038
1885
  log.success("Downloaded.");
2039
1886
  return true;
2040
1887
  } finally {
2041
- await fs9.rm(staging, { recursive: true, force: true }).catch(() => {
1888
+ await fs8.rm(staging, { recursive: true, force: true }).catch(() => {
2042
1889
  });
2043
1890
  }
2044
1891
  }
2045
1892
  async function ensureSlugEnv(slug, log, cwd = process.cwd()) {
2046
- const file = path9.join(cwd, ".env");
1893
+ const file = path8.join(cwd, ".env");
2047
1894
  let content;
2048
1895
  try {
2049
- content = await fs9.readFile(file, "utf8");
1896
+ content = await fs8.readFile(file, "utf8");
2050
1897
  } catch {
2051
1898
  return;
2052
1899
  }
2053
1900
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
2054
1901
  const m = content.match(re);
2055
1902
  if (!m) {
2056
- await fs9.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
1903
+ await fs8.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
2057
1904
  `);
2058
1905
  log.dim(` added VITE_GENEX_SLUG=${slug} to .env`);
2059
1906
  return;
2060
1907
  }
2061
1908
  if (m[2].trim() === slug) return;
2062
- await fs9.writeFile(file, content.replace(re, `$1${slug}`));
1909
+ await fs8.writeFile(file, content.replace(re, `$1${slug}`));
2063
1910
  log.dim(` updated .env: VITE_GENEX_SLUG=${slug} (was ${m[2].trim()})`);
2064
1911
  }
2065
1912
  async function fetchOwnProject(apiUrl, token, slug, log) {
@@ -2113,9 +1960,9 @@ async function listOwnSlugs(apiUrl, token, log) {
2113
1960
  }
2114
1961
 
2115
1962
  // src/commands/pull.ts
2116
- import fs10 from "fs/promises";
2117
- import path10 from "path";
2118
- import os7 from "os";
1963
+ import fs9 from "fs/promises";
1964
+ import path9 from "path";
1965
+ import os6 from "os";
2119
1966
  function isMachineLocal(entry) {
2120
1967
  if (entry === ".genex" || entry === "node_modules" || entry === ".git") return true;
2121
1968
  if (entry === ".env.example") return false;
@@ -2174,23 +2021,23 @@ async function runPull(opts) {
2174
2021
  process.exitCode = 1;
2175
2022
  return;
2176
2023
  }
2177
- const staging = await fs10.mkdtemp(path10.join(os7.tmpdir(), "genex-pull-"));
2178
- const fresh = path10.join(staging, "source");
2024
+ const staging = await fs9.mkdtemp(path9.join(os6.tmpdir(), "genex-pull-"));
2025
+ const fresh = path9.join(staging, "source");
2179
2026
  try {
2180
2027
  if (!await cloneSource(grant, fresh, log)) {
2181
2028
  process.exitCode = 1;
2182
2029
  return;
2183
2030
  }
2184
- await fs10.rm(path10.join(fresh, ".git"), { recursive: true, force: true });
2031
+ await fs9.rm(path9.join(fresh, ".git"), { recursive: true, force: true });
2185
2032
  const kept = await keepReplaced(cwd, log);
2186
2033
  await replaceTree(cwd, fresh);
2187
2034
  if (kept) {
2188
2035
  log.plain("");
2189
- log.info(`What was here is kept at ${c.cyan(path10.relative(cwd, kept) || kept)}`);
2036
+ log.info(`What was here is kept at ${c.cyan(path9.relative(cwd, kept) || kept)}`);
2190
2037
  log.dim(" Nothing was thrown away \u2014 re-apply from there, or delete it when you are done.");
2191
2038
  }
2192
2039
  } finally {
2193
- await fs10.rm(staging, { recursive: true, force: true }).catch(() => {
2040
+ await fs9.rm(staging, { recursive: true, force: true }).catch(() => {
2194
2041
  });
2195
2042
  }
2196
2043
  const remote = await readRemoteSource(apiUrl, token, meta.id);
@@ -2209,13 +2056,13 @@ async function runPull(opts) {
2209
2056
  }
2210
2057
  async function keepReplaced(cwd, log) {
2211
2058
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2212
- const dest = path10.join(cwd, ".genex", `replaced-${stamp}`);
2059
+ const dest = path9.join(cwd, ".genex", `replaced-${stamp}`);
2213
2060
  try {
2214
- const entries = (await fs10.readdir(cwd)).filter((e) => !isMachineLocal(e));
2061
+ const entries = (await fs9.readdir(cwd)).filter((e) => !isMachineLocal(e));
2215
2062
  if (entries.length === 0) return null;
2216
- await fs10.mkdir(dest, { recursive: true });
2063
+ await fs9.mkdir(dest, { recursive: true });
2217
2064
  for (const entry of entries) {
2218
- await fs10.cp(path10.join(cwd, entry), path10.join(dest, entry), { recursive: true });
2065
+ await fs9.cp(path9.join(cwd, entry), path9.join(dest, entry), { recursive: true });
2219
2066
  }
2220
2067
  return dest;
2221
2068
  } catch (err) {
@@ -2225,19 +2072,19 @@ async function keepReplaced(cwd, log) {
2225
2072
  }
2226
2073
  }
2227
2074
  async function replaceTree(dest, src) {
2228
- for (const entry of await fs10.readdir(dest)) {
2075
+ for (const entry of await fs9.readdir(dest)) {
2229
2076
  if (isMachineLocal(entry)) continue;
2230
- await fs10.rm(path10.join(dest, entry), { recursive: true, force: true });
2077
+ await fs9.rm(path9.join(dest, entry), { recursive: true, force: true });
2231
2078
  }
2232
- for (const entry of await fs10.readdir(src)) {
2079
+ for (const entry of await fs9.readdir(src)) {
2233
2080
  if (isMachineLocal(entry)) continue;
2234
- await fs10.cp(path10.join(src, entry), path10.join(dest, entry), { recursive: true });
2081
+ await fs9.cp(path9.join(src, entry), path9.join(dest, entry), { recursive: true });
2235
2082
  }
2236
2083
  }
2237
2084
 
2238
2085
  // src/commands/rename.ts
2239
- import fs11 from "fs/promises";
2240
- import path11 from "path";
2086
+ import fs10 from "fs/promises";
2087
+ import path10 from "path";
2241
2088
  async function runRename(opts) {
2242
2089
  const log = createLogger({ quiet: opts.quiet });
2243
2090
  log.plain(c.bold("genex rename"));
@@ -2325,23 +2172,23 @@ async function runRename(opts) {
2325
2172
  log.info("Run `genex preview` (or `publish`) to rebuild \u2014 the new slug is baked into the bundle.");
2326
2173
  }
2327
2174
  async function rewriteSlugEnv(from, to, log, cwd = process.cwd()) {
2328
- const file = path11.join(cwd, ".env");
2175
+ const file = path10.join(cwd, ".env");
2329
2176
  let content;
2330
2177
  try {
2331
- content = await fs11.readFile(file, "utf8");
2178
+ content = await fs10.readFile(file, "utf8");
2332
2179
  } catch {
2333
2180
  return;
2334
2181
  }
2335
2182
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
2336
2183
  if (!re.test(content)) return;
2337
- await fs11.writeFile(file, content.replace(re, `$1${to}`));
2184
+ await fs10.writeFile(file, content.replace(re, `$1${to}`));
2338
2185
  log.dim(` .env: VITE_GENEX_SLUG=${to} (was ${from})`);
2339
2186
  }
2340
2187
  async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
2341
- const file = path11.join(cwd, "src", "genex.config.ts");
2188
+ const file = path10.join(cwd, "src", "genex.config.ts");
2342
2189
  let content;
2343
2190
  try {
2344
- content = await fs11.readFile(file, "utf8");
2191
+ content = await fs10.readFile(file, "utf8");
2345
2192
  } catch {
2346
2193
  return;
2347
2194
  }
@@ -2351,7 +2198,7 @@ async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
2351
2198
  log.warn(` src/genex.config.ts has no "${from}" literal \u2014 check its slug by hand.`);
2352
2199
  return;
2353
2200
  }
2354
- await fs11.writeFile(file, content.replace(quoted, `"${to}"`));
2201
+ await fs10.writeFile(file, content.replace(quoted, `"${to}"`));
2355
2202
  log.dim(` src/genex.config.ts: baked slug -> ${to}`);
2356
2203
  }
2357
2204
 
@@ -2482,13 +2329,13 @@ function relTime(iso) {
2482
2329
  import "child_process";
2483
2330
 
2484
2331
  // src/lib/generation-ledger.ts
2485
- import fs12 from "fs/promises";
2486
- import path12 from "path";
2487
- var ledgerPath = (cwd) => path12.join(cwd, ".genex", "generations.ndjson");
2332
+ import fs11 from "fs/promises";
2333
+ import path11 from "path";
2334
+ var ledgerPath = (cwd) => path11.join(cwd, ".genex", "generations.ndjson");
2488
2335
  async function append(cwd, event) {
2489
2336
  try {
2490
- await fs12.access(path12.join(cwd, ".genex"));
2491
- await fs12.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
2337
+ await fs11.access(path11.join(cwd, ".genex"));
2338
+ await fs11.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
2492
2339
  `, "utf8");
2493
2340
  } catch {
2494
2341
  }
@@ -2496,7 +2343,7 @@ async function append(cwd, event) {
2496
2343
  async function readLedger(cwd = process.cwd()) {
2497
2344
  let raw;
2498
2345
  try {
2499
- raw = await fs12.readFile(ledgerPath(cwd), "utf8");
2346
+ raw = await fs11.readFile(ledgerPath(cwd), "utf8");
2500
2347
  } catch {
2501
2348
  return [];
2502
2349
  }
@@ -2701,9 +2548,9 @@ function auditGenerationPlan(input) {
2701
2548
 
2702
2549
  // src/lib/deploy.ts
2703
2550
  import crypto3 from "crypto";
2704
- import fs15 from "fs/promises";
2705
- import os8 from "os";
2706
- import path14 from "path";
2551
+ import fs14 from "fs/promises";
2552
+ import os7 from "os";
2553
+ import path13 from "path";
2707
2554
 
2708
2555
  // ../../packages/mobile-scan/src/image-dims.ts
2709
2556
  function u32be(b, o) {
@@ -2967,12 +2814,12 @@ function tierFor(estVramMb) {
2967
2814
  }
2968
2815
 
2969
2816
  // src/commands/ui.ts
2970
- import fs14 from "fs/promises";
2971
- import path13 from "path";
2817
+ import fs13 from "fs/promises";
2818
+ import path12 from "path";
2972
2819
  import { PNG as PNG2 } from "pngjs";
2973
2820
 
2974
2821
  // src/lib/png-tools.ts
2975
- import fs13 from "fs/promises";
2822
+ import fs12 from "fs/promises";
2976
2823
  import { PNG } from "pngjs";
2977
2824
  var ALPHA_TRANSPARENT_MAX = 16;
2978
2825
  var isHttpUrl = (s) => /^https?:\/\//i.test(s);
@@ -2983,12 +2830,12 @@ async function loadPng(input) {
2983
2830
  if (!res.ok) throw new Error(`Couldn't fetch ${input} (HTTP ${res.status}).`);
2984
2831
  buf = Buffer.from(await res.arrayBuffer());
2985
2832
  } else {
2986
- buf = await fs13.readFile(input);
2833
+ buf = await fs12.readFile(input);
2987
2834
  }
2988
2835
  return PNG.sync.read(buf);
2989
2836
  }
2990
2837
  async function writePng(file, png) {
2991
- await fs13.writeFile(file, PNG.sync.write(png));
2838
+ await fs12.writeFile(file, PNG.sync.write(png));
2992
2839
  }
2993
2840
  function cropPng(image, box) {
2994
2841
  const out = new PNG({ width: box.w, height: box.h });
@@ -3288,7 +3135,7 @@ async function uiExtract(opts, log) {
3288
3135
  const dilatePx = opts.dilate ?? 0;
3289
3136
  const sheet = await loadPng(input);
3290
3137
  const { width: W, height: H, data } = sheet;
3291
- await fs14.mkdir(outDir, { recursive: true });
3138
+ await fs13.mkdir(outDir, { recursive: true });
3292
3139
  log.plain(c.bold("genex ui extract"));
3293
3140
  log.dim(` ${input} (${W}x${H}) \u2192 ${outDir}, ${names.length} names`);
3294
3141
  let hasTransparency = false;
@@ -3517,7 +3364,7 @@ async function uiExtract(opts, log) {
3517
3364
  rimPixels: speckle.sampled
3518
3365
  });
3519
3366
  }
3520
- const outPath = path13.join(outDir, `${name}.png`);
3367
+ const outPath = path12.join(outDir, `${name}.png`);
3521
3368
  await writePng(outPath, out);
3522
3369
  const sidecar = {
3523
3370
  name,
@@ -3536,7 +3383,7 @@ async function uiExtract(opts, log) {
3536
3383
  defringed
3537
3384
  };
3538
3385
  const { name: _n, out: _o, ...sidecarBody } = sidecar;
3539
- await fs14.writeFile(
3386
+ await fs13.writeFile(
3540
3387
  outPath.replace(/\.png$/i, "") + ".bbox.json",
3541
3388
  JSON.stringify(sidecarBody, null, 2)
3542
3389
  );
@@ -3545,8 +3392,8 @@ async function uiExtract(opts, log) {
3545
3392
  `${name}.png ${cropW}x${cropH} (ar ${sidecar.aspectRatio}) at sheet ${padX0},${padY0}`
3546
3393
  );
3547
3394
  }
3548
- const debugPath = path13.join(outDir, "extract-debug.json");
3549
- await fs14.writeFile(
3395
+ const debugPath = path12.join(outDir, "extract-debug.json");
3396
+ await fs13.writeFile(
3550
3397
  debugPath,
3551
3398
  JSON.stringify(
3552
3399
  {
@@ -4008,7 +3855,7 @@ async function uiMasks(opts, log) {
4008
3855
  const registrationTolerance = opts.registrationTolerance ?? 0.04;
4009
3856
  const edgeFlushMax = opts.edgeFlushMax ?? 0.04;
4010
3857
  const loosened = registrationTolerance > 0.04 || edgeFlushMax > 0.04 || minCoverage < 0.01 || maxCoverage > 0.85;
4011
- await fs14.mkdir(outDir, { recursive: true });
3858
+ await fs13.mkdir(outDir, { recursive: true });
4012
3859
  log.plain(c.bold("genex ui masks"));
4013
3860
  log.dim(` ${input} (${image.width}x${image.height}), ${pairs.length} pair(s) \u2192 ${outDir}`);
4014
3861
  const sheetComponents = detectSheetComponents(image, opts.minPixels ?? 2e3);
@@ -4061,11 +3908,11 @@ async function uiMasks(opts, log) {
4061
3908
  });
4062
3909
  }
4063
3910
  const overlay = makeOverlay(clean2, converted.png);
4064
- const framePath = path13.join(outDir, `${pair.name}-frame.png`);
4065
- const maskPath = path13.join(outDir, `${pair.name}-mask.png`);
4066
- const annotatedPath = path13.join(outDir, `${pair.name}-annotated-source.png`);
4067
- const overlayPath = path13.join(outDir, `${pair.name}-overlay.png`);
4068
- const metaPath = path13.join(outDir, `${pair.name}.annotated-progress.json`);
3911
+ const framePath = path12.join(outDir, `${pair.name}-frame.png`);
3912
+ const maskPath = path12.join(outDir, `${pair.name}-mask.png`);
3913
+ const annotatedPath = path12.join(outDir, `${pair.name}-annotated-source.png`);
3914
+ const overlayPath = path12.join(outDir, `${pair.name}-overlay.png`);
3915
+ const metaPath = path12.join(outDir, `${pair.name}.annotated-progress.json`);
4069
3916
  await writePng(framePath, clean2);
4070
3917
  await writePng(maskPath, converted.png);
4071
3918
  await writePng(annotatedPath, annotated);
@@ -4112,7 +3959,7 @@ async function uiMasks(opts, log) {
4112
3959
  },
4113
3960
  overlay: overlayPath
4114
3961
  };
4115
- await fs14.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
3962
+ await fs13.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
4116
3963
  `);
4117
3964
  results.push(meta);
4118
3965
  const fb = converted.bbox;
@@ -4128,8 +3975,8 @@ async function uiMasks(opts, log) {
4128
3975
  );
4129
3976
  }
4130
3977
  }
4131
- const indexPath = path13.join(outDir, "annotated-progress.json");
4132
- await fs14.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
3978
+ const indexPath = path12.join(outDir, "annotated-progress.json");
3979
+ await fs13.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
4133
3980
  `);
4134
3981
  log.plain("");
4135
3982
  log.success(`Wrote ${pairs.length} frame+mask pair(s) \u2192 ${outDir} (index: ${indexPath}).`);
@@ -4255,7 +4102,7 @@ async function uiTextColor(opts, log) {
4255
4102
  };
4256
4103
  process.stdout.write(`${JSON.stringify(result, null, 2)}
4257
4104
  `);
4258
- if (opts.out) await fs14.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4105
+ if (opts.out) await fs13.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4259
4106
  `);
4260
4107
  if (opts.cropPath) log.dim(` crop \u2192 ${opts.cropPath}`);
4261
4108
  }
@@ -4287,7 +4134,7 @@ async function uiTrim(opts, log) {
4287
4134
  const sidecar = computeBBoxes(trimmed);
4288
4135
  const speckleAllowed = !!(speckle && speckle.ratio > SPECKLE_MAX_RATIO);
4289
4136
  const sidecarPath = outPath.replace(/\.png$/i, "") + ".bbox.json";
4290
- await fs14.writeFile(
4137
+ await fs13.writeFile(
4291
4138
  sidecarPath,
4292
4139
  JSON.stringify(speckleAllowed ? { ...sidecar, speckleAllowed: true } : sidecar, null, 2)
4293
4140
  );
@@ -4394,7 +4241,7 @@ async function uiPlate(opts, log) {
4394
4241
  fail("No interior found \u2014 the image is fully transparent (or erode ate everything). Check --in / lower --erode.");
4395
4242
  }
4396
4243
  await writePng(outPath, out);
4397
- const name = path13.basename(outPath);
4244
+ const name = path12.basename(outPath);
4398
4245
  log.plain(c.bold("genex ui plate"));
4399
4246
  log.success(`${outPath} ${W}x${H}, interior ${(count / (W * H) * 100).toFixed(1)}% (erode ${erode}px)`);
4400
4247
  log.dim(" Wire it as the plate's silhouette (same box as the frame <img>, plate UNDER the art):");
@@ -4559,13 +4406,13 @@ async function walkFiles(dir) {
4559
4406
  const out = [];
4560
4407
  let entries;
4561
4408
  try {
4562
- entries = await fs14.readdir(dir, { withFileTypes: true });
4409
+ entries = await fs13.readdir(dir, { withFileTypes: true });
4563
4410
  } catch {
4564
4411
  return out;
4565
4412
  }
4566
4413
  for (const entry of entries) {
4567
4414
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
4568
- const p = path13.join(dir, entry.name);
4415
+ const p = path12.join(dir, entry.name);
4569
4416
  if (entry.isDirectory()) out.push(...await walkFiles(p));
4570
4417
  else out.push(p);
4571
4418
  }
@@ -4574,7 +4421,7 @@ async function walkFiles(dir) {
4574
4421
  async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4575
4422
  const viewportFindings = [];
4576
4423
  try {
4577
- const indexHtml = await fs14.readFile(path13.join(cwd, "index.html"), "utf8");
4424
+ const indexHtml = await fs13.readFile(path12.join(cwd, "index.html"), "utf8");
4578
4425
  if (!/<meta[^>]+name=["']viewport["']/i.test(indexHtml)) {
4579
4426
  viewportFindings.push({
4580
4427
  kind: "viewport-meta",
@@ -4588,28 +4435,28 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4588
4435
  }
4589
4436
  } catch {
4590
4437
  }
4591
- const absAssets = path13.resolve(cwd, assetDir);
4438
+ const absAssets = path12.resolve(cwd, assetDir);
4592
4439
  try {
4593
- if (!(await fs14.stat(absAssets)).isDirectory()) {
4440
+ if (!(await fs13.stat(absAssets)).isDirectory()) {
4594
4441
  return viewportFindings.length > 0 ? viewportFindings : null;
4595
4442
  }
4596
4443
  } catch {
4597
4444
  return viewportFindings.length > 0 ? viewportFindings : null;
4598
4445
  }
4599
- const srcFiles = (await walkFiles(path13.resolve(cwd, srcDir))).filter(
4600
- (p) => AUDIT_SRC_EXTS.has(path13.extname(p).toLowerCase())
4446
+ const srcFiles = (await walkFiles(path12.resolve(cwd, srcDir))).filter(
4447
+ (p) => AUDIT_SRC_EXTS.has(path12.extname(p).toLowerCase())
4601
4448
  );
4602
4449
  try {
4603
- for (const name of await fs14.readdir(cwd)) {
4604
- const ext = path13.extname(name).toLowerCase();
4605
- if (ext === ".html" || ext === ".css") srcFiles.push(path13.join(cwd, name));
4450
+ for (const name of await fs13.readdir(cwd)) {
4451
+ const ext = path12.extname(name).toLowerCase();
4452
+ if (ext === ".html" || ext === ".css") srcFiles.push(path12.join(cwd, name));
4606
4453
  }
4607
4454
  } catch {
4608
4455
  }
4609
4456
  const sources = [];
4610
4457
  for (const p of srcFiles) {
4611
4458
  try {
4612
- sources.push({ rel: path13.relative(cwd, p), text: await fs14.readFile(p, "utf8") });
4459
+ sources.push({ rel: path12.relative(cwd, p), text: await fs13.readFile(p, "utf8") });
4613
4460
  } catch {
4614
4461
  }
4615
4462
  }
@@ -4619,11 +4466,11 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4619
4466
  const metaByName = /* @__PURE__ */ new Map();
4620
4467
  const bboxByPng = /* @__PURE__ */ new Map();
4621
4468
  for (const p of assetFiles) {
4622
- const base = path13.basename(p);
4469
+ const base = path12.basename(p);
4623
4470
  const metaMatch = /^(.+)\.annotated-progress\.json$/.exec(base);
4624
4471
  if (metaMatch) {
4625
4472
  try {
4626
- const meta = JSON.parse(await fs14.readFile(p, "utf8"));
4473
+ const meta = JSON.parse(await fs13.readFile(p, "utf8"));
4627
4474
  metaByName.set(metaMatch[1], {
4628
4475
  cleanCrop: meta.clean?.crop ?? null,
4629
4476
  loosened: meta.loosened === true
@@ -4634,7 +4481,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4634
4481
  }
4635
4482
  if (base.endsWith(".bbox.json")) {
4636
4483
  try {
4637
- const sidecar = JSON.parse(await fs14.readFile(p, "utf8"));
4484
+ const sidecar = JSON.parse(await fs13.readFile(p, "utf8"));
4638
4485
  if (sidecar.sheetBBox) bboxByPng.set(base.replace(/\.bbox\.json$/, ".png"), sidecar.sheetBBox);
4639
4486
  } catch {
4640
4487
  }
@@ -4643,7 +4490,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4643
4490
  for (const [name, meta] of metaByName) {
4644
4491
  if (!meta.cleanCrop || !referenced(`${name}-mask.png`)) continue;
4645
4492
  for (const p of assetFiles) {
4646
- const base = path13.basename(p);
4493
+ const base = path12.basename(p);
4647
4494
  if (!base.toLowerCase().endsWith(".png")) continue;
4648
4495
  if (base !== `${name}.png` && !base.startsWith(`${name}-`)) continue;
4649
4496
  if (/-mask\.png$|-frame\.png$|-overlay\.png$|-annotated-source\.png$/.test(base)) continue;
@@ -4667,7 +4514,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4667
4514
  }
4668
4515
  const pngByBase = /* @__PURE__ */ new Map();
4669
4516
  for (const p of assetFiles) {
4670
- const base = path13.basename(p);
4517
+ const base = path12.basename(p);
4671
4518
  if (base.toLowerCase().endsWith(".png")) pngByBase.set(base, p);
4672
4519
  }
4673
4520
  for (const [maskBase, maskPath] of pngByBase) {
@@ -4680,8 +4527,8 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4680
4527
  let frame;
4681
4528
  let mask;
4682
4529
  try {
4683
- frame = PNG2.sync.read(await fs14.readFile(framePath));
4684
- mask = PNG2.sync.read(await fs14.readFile(maskPath));
4530
+ frame = PNG2.sync.read(await fs13.readFile(framePath));
4531
+ mask = PNG2.sync.read(await fs13.readFile(maskPath));
4685
4532
  } catch {
4686
4533
  continue;
4687
4534
  }
@@ -4700,7 +4547,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4700
4547
  if (!referenced(base)) continue;
4701
4548
  let png;
4702
4549
  try {
4703
- png = PNG2.sync.read(await fs14.readFile(p));
4550
+ png = PNG2.sync.read(await fs13.readFile(p));
4704
4551
  } catch {
4705
4552
  continue;
4706
4553
  }
@@ -4720,7 +4567,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4720
4567
  }
4721
4568
  const maskReported = /* @__PURE__ */ new Set();
4722
4569
  for (const p of assetFiles) {
4723
- const m = /^(.+)\.annotated-progress\.json$/.exec(path13.basename(p));
4570
+ const m = /^(.+)\.annotated-progress\.json$/.exec(path12.basename(p));
4724
4571
  if (!m) continue;
4725
4572
  const maskBase = `${m[1]}-mask.png`;
4726
4573
  if (!referenced(maskBase)) {
@@ -4732,14 +4579,14 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4732
4579
  }
4733
4580
  }
4734
4581
  for (const p of assetFiles) {
4735
- const base = path13.basename(p);
4582
+ const base = path12.basename(p);
4736
4583
  if (!base.toLowerCase().endsWith(".png")) continue;
4737
4584
  if (/-annotated-source\.png$|-overlay\.png$/.test(base)) continue;
4738
4585
  if (maskReported.has(base)) continue;
4739
4586
  if (!referenced(base)) {
4740
4587
  findings.push({
4741
4588
  kind: "unwired-sprite",
4742
- message: `${path13.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.)`
4589
+ message: `${path12.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.)`
4743
4590
  });
4744
4591
  }
4745
4592
  }
@@ -4791,78 +4638,6 @@ async function uiAudit(opts, log) {
4791
4638
  if (opts.strict || hard.length > 0) process.exitCode = 1;
4792
4639
  }
4793
4640
 
4794
- // src/lib/terms.ts
4795
- import readline2 from "readline";
4796
- var TERMS_ERROR_CODE = "terms_acceptance_required";
4797
- async function isTermsRefusal(res) {
4798
- if (res.status !== 403) return false;
4799
- try {
4800
- const body = await res.clone().json();
4801
- return body?.error === TERMS_ERROR_CODE;
4802
- } catch {
4803
- return false;
4804
- }
4805
- }
4806
- function reportTermsRefusal(log, authUrl) {
4807
- log.error("Your account needs to accept the current Terms before publishing.");
4808
- log.dim(" Nothing was deployed, and nothing on your game changed.");
4809
- if (!inHostedSession()) {
4810
- log.dim(` Run ${c.cyan("npx genex accept")} to read them and agree here.`);
4811
- }
4812
- log.dim(` Open ${c.cyan(`${getAuthUrl(authUrl)}/terms`)} and agree, then try again.`);
4813
- }
4814
- function askOnStdin(question) {
4815
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4816
- return new Promise((resolve) => {
4817
- rl.question(question, (answer) => {
4818
- rl.close();
4819
- resolve(answer);
4820
- });
4821
- });
4822
- }
4823
- async function runAccept(opts) {
4824
- const { token, log } = opts;
4825
- const interactive = opts.interactive ?? Boolean(process.stdin.isTTY);
4826
- const apiUrl = getApiUrl(opts.apiUrl);
4827
- const web = getAuthUrl();
4828
- const status = await apiFetch(`${apiUrl}/api/legal/status`, {
4829
- headers: { Authorization: `Bearer ${token}` }
4830
- }).catch(() => null);
4831
- if (status?.ok) {
4832
- const body = await status.json().catch(() => null);
4833
- if (body?.accepted) {
4834
- log.success("Already accepted \u2014 nothing to do.");
4835
- return true;
4836
- }
4837
- log.plain("Before publishing, please read and agree to:");
4838
- for (const doc of body?.documents ?? []) log.plain(` ${doc.title} ${c.cyan(doc.url)}`);
4839
- log.plain("");
4840
- }
4841
- if (!interactive) {
4842
- log.error("Accepting the Terms needs a person, so this cannot run unattended.");
4843
- log.dim(" Agreeing is a contract, and an agent cannot do it on your behalf.");
4844
- log.dim(` Open ${c.cyan(`${web}/terms`)} and agree there, then re-run your command.`);
4845
- return false;
4846
- }
4847
- const ask = opts.ask ?? askOnStdin;
4848
- const answer = (await ask("Type 'agree' to accept: ")).trim().toLowerCase();
4849
- if (answer !== "agree") {
4850
- log.error("Not accepted \u2014 nothing was recorded.");
4851
- return false;
4852
- }
4853
- const res = await apiFetch(`${apiUrl}/api/legal/accept`, {
4854
- method: "POST",
4855
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
4856
- body: JSON.stringify({ surface: "cli" })
4857
- }).catch(() => null);
4858
- if (!res?.ok) {
4859
- log.error(`Couldn't record your acceptance (HTTP ${res?.status ?? "no response"}).`);
4860
- return false;
4861
- }
4862
- log.success("Accepted. Re-run your command.");
4863
- return true;
4864
- }
4865
-
4866
4641
  // src/lib/deploy.ts
4867
4642
  function printMobilePreflight(files, log) {
4868
4643
  try {
@@ -4926,7 +4701,7 @@ async function printUiAuditPreflight(log) {
4926
4701
  }
4927
4702
  async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4928
4703
  try {
4929
- const design = await fs15.readFile(path14.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4704
+ const design = await fs14.readFile(path13.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4930
4705
  const warnings = [];
4931
4706
  if (design.length > 0 && !/##\s*build plan & status/i.test(design)) {
4932
4707
  warnings.push(
@@ -4934,7 +4709,7 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4934
4709
  );
4935
4710
  }
4936
4711
  if (!/player character:/i.test(design)) {
4937
- const hasCharacter = await fs15.access(path14.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4712
+ const hasCharacter = await fs14.access(path13.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4938
4713
  if (!hasCharacter && await loadsPlayerBody(cwd)) {
4939
4714
  warnings.push(
4940
4715
  `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).`
@@ -4949,12 +4724,12 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4949
4724
  async function loadsPlayerBody(cwd) {
4950
4725
  const BODY_LOADERS = /\bloadPlayerCharacter\s*\(|\bloadVrm(?:Clone)?\s*\(/;
4951
4726
  try {
4952
- const entries = await fs15.readdir(path14.join(cwd, "src"), { recursive: true });
4727
+ const entries = await fs14.readdir(path13.join(cwd, "src"), { recursive: true });
4953
4728
  for (const rel of entries) {
4954
4729
  if (rel.includes("node_modules")) continue;
4955
- if (rel.split(path14.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4730
+ if (rel.split(path13.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4956
4731
  if (!/\.(ts|tsx|js|jsx)$/.test(rel)) continue;
4957
- const text = await fs15.readFile(path14.join(cwd, "src", rel), "utf8").catch(() => "");
4732
+ const text = await fs14.readFile(path13.join(cwd, "src", rel), "utf8").catch(() => "");
4958
4733
  if (BODY_LOADERS.test(text)) return true;
4959
4734
  }
4960
4735
  } catch {
@@ -4991,9 +4766,9 @@ async function deployGame(ctx, opts, log) {
4991
4766
  }
4992
4767
  log.success("Built.");
4993
4768
  }
4994
- const distDir = path14.join(cwd, "dist");
4769
+ const distDir = path13.join(cwd, "dist");
4995
4770
  const siteDir = await isDir(distDir) ? distDir : cwd;
4996
- const rel = path14.relative(cwd, siteDir) || ".";
4771
+ const rel = path13.relative(cwd, siteDir) || ".";
4997
4772
  if (siteDir === cwd) await writeGitignore(cwd, log);
4998
4773
  const files = await collectFiles(siteDir);
4999
4774
  if (files.length === 0) {
@@ -5082,7 +4857,7 @@ async function deployGame(ctx, opts, log) {
5082
4857
  }
5083
4858
  async function hasBuildScript(cwd) {
5084
4859
  try {
5085
- const pkg = JSON.parse(await fs15.readFile(path14.join(cwd, "package.json"), "utf8"));
4860
+ const pkg = JSON.parse(await fs14.readFile(path13.join(cwd, "package.json"), "utf8"));
5086
4861
  return Boolean(pkg.scripts?.build);
5087
4862
  } catch {
5088
4863
  return false;
@@ -5091,12 +4866,12 @@ async function hasBuildScript(cwd) {
5091
4866
  async function collectFiles(root) {
5092
4867
  const out = [];
5093
4868
  const walk2 = async (dir, prefix) => {
5094
- for (const e of await fs15.readdir(dir, { withFileTypes: true })) {
4869
+ for (const e of await fs14.readdir(dir, { withFileTypes: true })) {
5095
4870
  const relPath = prefix ? `${prefix}/${e.name}` : e.name;
5096
4871
  if (e.isDirectory()) {
5097
- if (!EXCLUDE_DIRS.has(e.name)) await walk2(path14.join(dir, e.name), relPath);
4872
+ if (!EXCLUDE_DIRS.has(e.name)) await walk2(path13.join(dir, e.name), relPath);
5098
4873
  } else if (e.isFile() && !isSecretEnvFile(e.name)) {
5099
- out.push({ relPath, bytes: await fs15.readFile(path14.join(dir, e.name)) });
4874
+ out.push({ relPath, bytes: await fs14.readFile(path13.join(dir, e.name)) });
5100
4875
  }
5101
4876
  }
5102
4877
  };
@@ -5349,7 +5124,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5349
5124
  log.error("Couldn't save your game's source \u2014 please try again.");
5350
5125
  return false;
5351
5126
  };
5352
- const gitDir = await fs15.mkdtemp(path14.join(os8.tmpdir(), "genex-source-"));
5127
+ const gitDir = await fs14.mkdtemp(path13.join(os7.tmpdir(), "genex-source-"));
5353
5128
  const base = { GIT_DIR: gitDir };
5354
5129
  if (urlHasEmbeddedCredentials(pushUrl)) {
5355
5130
  base.GIT_CONFIG_COUNT = "1";
@@ -5364,12 +5139,12 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5364
5139
  };
5365
5140
  try {
5366
5141
  if ((await run("git", ["init", "-q"], base)).code !== 0) return failed();
5367
- await fs15.writeFile(
5368
- path14.join(gitDir, "info", "exclude"),
5142
+ await fs14.writeFile(
5143
+ path13.join(gitDir, "info", "exclude"),
5369
5144
  // .env* are secrets — never publish them; `!` keeps the non-secret template.
5370
5145
  ["node_modules/", "dist/", ".git/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n")
5371
5146
  );
5372
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path14.join(gitDir, "index-source") };
5147
+ const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path13.join(gitDir, "index-source") };
5373
5148
  let lfs = (await run("git", ["lfs", "version"], base)).code !== 0 ? false : true;
5374
5149
  if (!lfs) {
5375
5150
  log.step("Installing git-lfs (keeps large binary assets out of the source push)\u2026");
@@ -5421,7 +5196,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5421
5196
  } catch {
5422
5197
  return failed();
5423
5198
  } finally {
5424
- await fs15.rm(gitDir, { recursive: true, force: true }).catch(() => {
5199
+ await fs14.rm(gitDir, { recursive: true, force: true }).catch(() => {
5425
5200
  });
5426
5201
  }
5427
5202
  }
@@ -5457,7 +5232,7 @@ async function fetchPushUrl(ctx, log, expectStagingCommit) {
5457
5232
  return null;
5458
5233
  }
5459
5234
  if (await isTermsRefusal(res)) {
5460
- reportTermsRefusal(log);
5235
+ if (!printedStructuredError(res)) reportTermsRefusal(log);
5461
5236
  return null;
5462
5237
  }
5463
5238
  if (!res.ok) {
@@ -5475,7 +5250,7 @@ async function fetchPushUrl(ctx, log, expectStagingCommit) {
5475
5250
  }
5476
5251
  async function isDir(p) {
5477
5252
  try {
5478
- return (await fs15.stat(p)).isDirectory();
5253
+ return (await fs14.stat(p)).isDirectory();
5479
5254
  } catch {
5480
5255
  return false;
5481
5256
  }
@@ -5957,11 +5732,11 @@ async function promoteBuild(apiUrl, projectId, token, log) {
5957
5732
  }
5958
5733
 
5959
5734
  // src/lib/detect-features.ts
5960
- import fs16 from "fs/promises";
5961
- import path15 from "path";
5735
+ import fs15 from "fs/promises";
5736
+ import path14 from "path";
5962
5737
  async function detectEmbedSdkVersion(cwd = process.cwd()) {
5963
5738
  try {
5964
- const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
5739
+ const raw = await fs15.readFile(path14.join(cwd, "package.json"), "utf8");
5965
5740
  const pkg = JSON.parse(raw);
5966
5741
  const version = pkg.dependencies?.["@genex-ai/embed-sdk"] ?? pkg.devDependencies?.["@genex-ai/embed-sdk"];
5967
5742
  return typeof version === "string" && version ? version : null;
@@ -5971,7 +5746,7 @@ async function detectEmbedSdkVersion(cwd = process.cwd()) {
5971
5746
  }
5972
5747
  async function detectMultiplayer(cwd = process.cwd()) {
5973
5748
  try {
5974
- const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
5749
+ const raw = await fs15.readFile(path14.join(cwd, "package.json"), "utf8");
5975
5750
  const pkg = JSON.parse(raw);
5976
5751
  return Boolean(
5977
5752
  pkg.dependencies?.["@genex-ai/multiplayer"] ?? pkg.devDependencies?.["@genex-ai/multiplayer"]
@@ -5983,7 +5758,7 @@ async function detectMultiplayer(cwd = process.cwd()) {
5983
5758
  async function detectMatchmaking(log, cwd = process.cwd()) {
5984
5759
  let pkg;
5985
5760
  try {
5986
- pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
5761
+ pkg = JSON.parse(await fs15.readFile(path14.join(cwd, "package.json"), "utf8"));
5987
5762
  } catch (err) {
5988
5763
  log.dim(` (skipping matchmaking \u2014 couldn't read package.json: ${String(err)})`);
5989
5764
  return null;
@@ -6001,15 +5776,15 @@ async function detectMatchmaking(log, cwd = process.cwd()) {
6001
5776
  var TOUCH_KIT_MARKERS = /controllers\/touch\/|controllers\/character\/touch-joystick|TouchJoystick|VirtualButton|DragZone|RotateOverlay/;
6002
5777
  async function detectMobileControls(cwd = process.cwd()) {
6003
5778
  try {
6004
- const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
5779
+ const raw = await fs15.readFile(path14.join(cwd, "package.json"), "utf8");
6005
5780
  const pkg = JSON.parse(raw);
6006
5781
  if (pkg.genex?.mobileControls === true) return true;
6007
5782
  } catch {
6008
5783
  }
6009
- const srcDir = path15.join(cwd, "src");
5784
+ const srcDir = path14.join(cwd, "src");
6010
5785
  let entries;
6011
5786
  try {
6012
- entries = await fs16.readdir(srcDir, { recursive: true });
5787
+ entries = await fs15.readdir(srcDir, { recursive: true });
6013
5788
  } catch {
6014
5789
  return false;
6015
5790
  }
@@ -6017,7 +5792,7 @@ async function detectMobileControls(cwd = process.cwd()) {
6017
5792
  if (rel.includes("node_modules")) continue;
6018
5793
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
6019
5794
  try {
6020
- const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
5795
+ const content = await fs15.readFile(path14.join(srcDir, rel), "utf8");
6021
5796
  if (TOUCH_KIT_MARKERS.test(content)) return true;
6022
5797
  } catch {
6023
5798
  }
@@ -6026,10 +5801,10 @@ async function detectMobileControls(cwd = process.cwd()) {
6026
5801
  }
6027
5802
  var GAME_STATE_CALLS = /savePlayerState|saveWorldState|submitScore/;
6028
5803
  async function detectGameStateUsage(cwd = process.cwd()) {
6029
- const srcDir = path15.join(cwd, "src");
5804
+ const srcDir = path14.join(cwd, "src");
6030
5805
  let entries;
6031
5806
  try {
6032
- entries = await fs16.readdir(srcDir, { recursive: true });
5807
+ entries = await fs15.readdir(srcDir, { recursive: true });
6033
5808
  } catch {
6034
5809
  return false;
6035
5810
  }
@@ -6037,7 +5812,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
6037
5812
  if (rel.includes("node_modules")) continue;
6038
5813
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
6039
5814
  try {
6040
- const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
5815
+ const content = await fs15.readFile(path14.join(srcDir, rel), "utf8");
6041
5816
  if (GAME_STATE_CALLS.test(content)) return true;
6042
5817
  } catch {
6043
5818
  }
@@ -6088,19 +5863,19 @@ async function detectSurfaceScan(cwd = process.cwd()) {
6088
5863
  deferredAudioContext: [],
6089
5864
  usesThree: false
6090
5865
  };
6091
- const srcDir = path15.join(cwd, "src");
5866
+ const srcDir = path14.join(cwd, "src");
6092
5867
  let entries;
6093
5868
  try {
6094
- entries = await fs16.readdir(srcDir, { recursive: true });
5869
+ entries = await fs15.readdir(srcDir, { recursive: true });
6095
5870
  } catch {
6096
5871
  return found;
6097
5872
  }
6098
5873
  for (const nativeRel of entries) {
6099
5874
  if (nativeRel.includes("node_modules")) continue;
6100
5875
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
6101
- const raw = await fs16.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
5876
+ const raw = await fs15.readFile(path14.join(srcDir, nativeRel), "utf8").catch(() => "");
6102
5877
  if (!raw) continue;
6103
- const rel = nativeRel.split(path15.sep).join("/");
5878
+ const rel = nativeRel.split(path14.sep).join("/");
6104
5879
  const content = raw.replace(/\/\*[\s\S]*?\*\//g, (m2) => m2.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/gm, (m2, p1) => p1 + " ".repeat(m2.length - p1.length));
6105
5880
  const repeatRe = /\.repeat\.set\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/g;
6106
5881
  let m;
@@ -6181,25 +5956,25 @@ async function detectGenerationAudit(cwd = process.cwd()) {
6181
5956
  let haystack = "";
6182
5957
  const read = async (file) => {
6183
5958
  try {
6184
- haystack += await fs16.readFile(file, "utf8");
5959
+ haystack += await fs15.readFile(file, "utf8");
6185
5960
  } catch {
6186
5961
  }
6187
5962
  };
6188
5963
  try {
6189
- for (const entry of await fs16.readdir(cwd, { withFileTypes: true })) {
5964
+ for (const entry of await fs15.readdir(cwd, { withFileTypes: true })) {
6190
5965
  if (entry.isFile() && /\.(ts|tsx|js|jsx|css|html|json)$/.test(entry.name)) {
6191
- await read(path15.join(cwd, entry.name));
5966
+ await read(path14.join(cwd, entry.name));
6192
5967
  }
6193
5968
  }
6194
5969
  } catch {
6195
5970
  }
6196
5971
  for (const sub of ["src", "public"]) {
6197
5972
  try {
6198
- const entries = await fs16.readdir(path15.join(cwd, sub), { recursive: true });
5973
+ const entries = await fs15.readdir(path14.join(cwd, sub), { recursive: true });
6199
5974
  for (const rel of entries) {
6200
5975
  if (rel.includes("node_modules")) continue;
6201
5976
  if (!/\.(ts|tsx|js|jsx|css|html|json|txt)$/.test(rel)) continue;
6202
- await read(path15.join(cwd, sub, rel));
5977
+ await read(path14.join(cwd, sub, rel));
6203
5978
  }
6204
5979
  } catch {
6205
5980
  }
@@ -6346,7 +6121,7 @@ async function borrowEvidence(meta, cwd) {
6346
6121
  } catch {
6347
6122
  return true;
6348
6123
  }
6349
- const gitConfig = await fs16.readFile(path15.join(cwd, ".git", "config"), "utf8").catch(() => "");
6124
+ const gitConfig = await fs15.readFile(path14.join(cwd, ".git", "config"), "utf8").catch(() => "");
6350
6125
  for (const m of gitConfig.matchAll(/url\s*=\s*(\S+)/g)) {
6351
6126
  try {
6352
6127
  const u = new URL(m[1]);
@@ -6354,7 +6129,7 @@ async function borrowEvidence(meta, cwd) {
6354
6129
  } catch {
6355
6130
  }
6356
6131
  }
6357
- const readme = await fs16.readFile(path15.join(cwd, "README.md"), "utf8").catch(() => "");
6132
+ const readme = await fs15.readFile(path14.join(cwd, "README.md"), "utf8").catch(() => "");
6358
6133
  return readme.includes(host) || /\b(upstream|originally by|ported from|borrowed from)\b/i.test(readme);
6359
6134
  }
6360
6135
 
@@ -6616,11 +6391,12 @@ async function runAcceptCommand(opts) {
6616
6391
  process.exitCode = 1;
6617
6392
  return;
6618
6393
  }
6619
- const meta = await readProject();
6394
+ const [meta, workspace] = await Promise.all([readProject(), readWorkspace()]);
6395
+ const apiUrl = meta?.apiUrl ?? workspace?.apiUrl;
6620
6396
  const ok = await runAccept({
6621
6397
  token,
6622
6398
  log,
6623
- ...meta?.apiUrl ? { apiUrl: meta.apiUrl } : {}
6399
+ ...apiUrl ? { apiUrl } : {}
6624
6400
  });
6625
6401
  if (!ok) process.exitCode = 1;
6626
6402
  }
@@ -6834,8 +6610,8 @@ async function runRollback(opts) {
6834
6610
  }
6835
6611
 
6836
6612
  // src/commands/generate.ts
6837
- import fs18 from "fs/promises";
6838
- import path17 from "path";
6613
+ import fs17 from "fs/promises";
6614
+ import path16 from "path";
6839
6615
  import { PNG as PNG4 } from "pngjs";
6840
6616
 
6841
6617
  // src/lib/glass.ts
@@ -6916,8 +6692,8 @@ function solveMagentaGlass(source) {
6916
6692
  }
6917
6693
 
6918
6694
  // src/lib/download-assets.ts
6919
- import fs17 from "fs/promises";
6920
- import path16 from "path";
6695
+ import fs16 from "fs/promises";
6696
+ import path15 from "path";
6921
6697
  var RUNG_ROLE = /@\d+$/;
6922
6698
  function isPrimaryRole(role) {
6923
6699
  return !RUNG_ROLE.test(role);
@@ -6937,7 +6713,7 @@ async function downloadAssets(files, opts) {
6937
6713
  const result = { saved: [], failures: [] };
6938
6714
  if (wanted.length === 0) return result;
6939
6715
  try {
6940
- await fs17.mkdir(opts.outDir, { recursive: true });
6716
+ await fs16.mkdir(opts.outDir, { recursive: true });
6941
6717
  } catch (err) {
6942
6718
  result.failures.push(
6943
6719
  `couldn't create ${opts.outDir} (${err instanceof Error ? err.message : String(err)})`
@@ -6946,12 +6722,12 @@ async function downloadAssets(files, opts) {
6946
6722
  }
6947
6723
  const multi = wanted.length > 1;
6948
6724
  for (const file of wanted) {
6949
- const dest = path16.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
6725
+ const dest = path15.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
6950
6726
  try {
6951
6727
  const res = await fetch(file.url);
6952
6728
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
6953
6729
  const buf = Buffer.from(await res.arrayBuffer());
6954
- await fs17.writeFile(dest, buf);
6730
+ await fs16.writeFile(dest, buf);
6955
6731
  result.saved.push({ role: file.role, path: dest, url: file.url, bytes: buf.byteLength });
6956
6732
  } catch (err) {
6957
6733
  result.failures.push(
@@ -7006,7 +6782,7 @@ function mockLaneRefusal(kind, provider) {
7006
6782
  }
7007
6783
 
7008
6784
  // src/lib/open.ts
7009
- import { spawn as spawn4 } from "child_process";
6785
+ import { spawn as spawn3 } from "child_process";
7010
6786
  function tokenize(cmd) {
7011
6787
  return cmd.trim().split(/\s+/).filter(Boolean);
7012
6788
  }
@@ -7035,7 +6811,7 @@ function openUrl(url) {
7035
6811
  }
7036
6812
  }
7037
6813
  try {
7038
- const child = spawn4(command, args, { stdio: "ignore", detached: true });
6814
+ const child = spawn3(command, args, { stdio: "ignore", detached: true });
7039
6815
  child.on("error", () => {
7040
6816
  });
7041
6817
  child.unref();
@@ -7095,7 +6871,7 @@ var isRemoteRef = (value) => /^(https?:\/\/|data:)/i.test(value);
7095
6871
  async function inlineLocalImage(filePath, flag) {
7096
6872
  let bytes;
7097
6873
  try {
7098
- bytes = await fs18.readFile(filePath);
6874
+ bytes = await fs17.readFile(filePath);
7099
6875
  } catch {
7100
6876
  return { ok: false, error: `Couldn't read the ${flag} file at ${filePath}.` };
7101
6877
  }
@@ -7105,7 +6881,7 @@ async function inlineLocalImage(filePath, flag) {
7105
6881
  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.`
7106
6882
  };
7107
6883
  }
7108
- const mime = IMAGE_MIME_BY_EXT[path17.extname(filePath).toLowerCase()] ?? "image/png";
6884
+ const mime = IMAGE_MIME_BY_EXT[path16.extname(filePath).toLowerCase()] ?? "image/png";
7109
6885
  return { ok: true, dataUri: `data:${mime};base64,${bytes.toString("base64")}` };
7110
6886
  }
7111
6887
  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.";
@@ -7263,7 +7039,7 @@ async function runGenerate(kind, opts) {
7263
7039
  let typedPrompt = opts.prompt?.trim();
7264
7040
  if (!typedPrompt && kind === "model" && opts.imageUrl) {
7265
7041
  const ref = opts.imageUrl.startsWith("data:") ? "local image" : opts.imageUrl;
7266
- typedPrompt = `from image: ${path17.basename(ref).slice(0, 120)}`;
7042
+ typedPrompt = `from image: ${path16.basename(ref).slice(0, 120)}`;
7267
7043
  }
7268
7044
  if (!typedPrompt) {
7269
7045
  log.error(`Missing prompt. Usage: ${c.cyan(`genex ${kind} "<prompt>"`)}`);
@@ -7307,7 +7083,7 @@ async function runGenerate(kind, opts) {
7307
7083
  return;
7308
7084
  }
7309
7085
  try {
7310
- const bytes = await fs18.readFile(opts.inpaintUrl);
7086
+ const bytes = await fs17.readFile(opts.inpaintUrl);
7311
7087
  opts = { ...opts, inpaintUrl: `data:image/png;base64,${bytes.toString("base64")}` };
7312
7088
  } catch {
7313
7089
  log.error(`Couldn't read the --inpaint mask at ${opts.inpaintUrl}.`);
@@ -7492,7 +7268,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
7492
7268
  return;
7493
7269
  }
7494
7270
  await recordTerminal(view.id, "completed", files.map((f) => f.url));
7495
- await fs18.mkdir(outDir, { recursive: true });
7271
+ await fs17.mkdir(outDir, { recursive: true });
7496
7272
  const solved = [];
7497
7273
  for (let i = 0; i < files.length; i++) {
7498
7274
  const f = files[i];
@@ -7524,8 +7300,8 @@ async function reportGlassTerminal(view, outDir, log, json) {
7524
7300
  });
7525
7301
  continue;
7526
7302
  }
7527
- const outPath = path17.join(outDir, `glass-${i + 1}.png`);
7528
- await fs18.writeFile(outPath, PNG4.sync.write(r.png));
7303
+ const outPath = path16.join(outDir, `glass-${i + 1}.png`);
7304
+ await fs17.writeFile(outPath, PNG4.sync.write(r.png));
7529
7305
  solved.push({
7530
7306
  path: outPath,
7531
7307
  url: f.url,
@@ -8291,8 +8067,8 @@ async function toRow(e, v, cwd) {
8291
8067
  }
8292
8068
 
8293
8069
  // src/commands/controller.ts
8294
- import fs20 from "fs/promises";
8295
- import path19 from "path";
8070
+ import fs19 from "fs/promises";
8071
+ import path18 from "path";
8296
8072
 
8297
8073
  // ../../packages/meshy-animation-catalog/src/index.ts
8298
8074
  import { createHash } from "crypto";
@@ -17422,9 +17198,9 @@ function searchMeshyAnimations(query, options = {}) {
17422
17198
  }
17423
17199
 
17424
17200
  // src/lib/anims.ts
17425
- import fs19 from "fs/promises";
17426
- import path18 from "path";
17427
- var ANIMS_DEST = path18.join("public", "assets", "anims");
17201
+ import fs18 from "fs/promises";
17202
+ import path17 from "path";
17203
+ var ANIMS_DEST = path17.join("public", "assets", "anims");
17428
17204
  var HIDDEN_TAG = "reference";
17429
17205
  async function runAnims(opts) {
17430
17206
  const log = createLogger({ quiet: opts.quiet });
@@ -17440,7 +17216,7 @@ async function runAnims(opts) {
17440
17216
  printCatalog(log, manifest, selectors);
17441
17217
  return;
17442
17218
  }
17443
- const controllerMarker = path18.join(root, "src", "controllers", "character");
17219
+ const controllerMarker = path17.join(root, "src", "controllers", "character");
17444
17220
  if (!await exists2(controllerMarker)) {
17445
17221
  log.error(
17446
17222
  `No character controller in this game (missing ${c.cyan("src/controllers/character/")}).`
@@ -17449,11 +17225,11 @@ async function runAnims(opts) {
17449
17225
  process.exitCode = 1;
17450
17226
  return;
17451
17227
  }
17452
- const destDir = path18.join(root, ANIMS_DEST);
17453
- const gameManifestPath = path18.join(destDir, "manifest.json");
17228
+ const destDir = path17.join(root, ANIMS_DEST);
17229
+ const gameManifestPath = path17.join(destDir, "manifest.json");
17454
17230
  if (opts.reset) {
17455
- await fs19.rm(destDir, { recursive: true, force: true });
17456
- log.step(`Cleared ${c.cyan(ANIMS_DEST + path18.sep)} (--reset)`);
17231
+ await fs18.rm(destDir, { recursive: true, force: true });
17232
+ log.step(`Cleared ${c.cyan(ANIMS_DEST + path17.sep)} (--reset)`);
17457
17233
  }
17458
17234
  if (selectors.length === 0) {
17459
17235
  const installed = await readGameManifest(gameManifestPath);
@@ -17491,35 +17267,35 @@ async function runAnims(opts) {
17491
17267
  }
17492
17268
  }
17493
17269
  const wanted = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
17494
- const cacheDir = path18.join(
17270
+ const cacheDir = path17.join(
17495
17271
  opts.cacheDir ?? getAnimsCacheDir(),
17496
17272
  `${manifest.library}-v${manifest.version}`
17497
17273
  );
17498
- await fs19.mkdir(cacheDir, { recursive: true });
17499
- await fs19.mkdir(destDir, { recursive: true });
17274
+ await fs18.mkdir(cacheDir, { recursive: true });
17275
+ await fs18.mkdir(destDir, { recursive: true });
17500
17276
  const base = getAnimsBase(opts.animsBase);
17501
17277
  let installedCount = 0;
17502
17278
  let presentCount = 0;
17503
17279
  let addedBytes = 0;
17504
17280
  const failures = [];
17505
17281
  for (const entry of wanted) {
17506
- const dest = path18.join(destDir, entry.file);
17282
+ const dest = path17.join(destDir, entry.file);
17507
17283
  if (await hasSize(dest, entry.bytes)) {
17508
17284
  presentCount++;
17509
17285
  continue;
17510
17286
  }
17511
17287
  try {
17512
- const cached = path18.join(cacheDir, entry.file);
17288
+ const cached = path17.join(cacheDir, entry.file);
17513
17289
  if (!await hasSize(cached, entry.bytes)) {
17514
17290
  const res = await fetch(base + entry.file);
17515
17291
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
17516
17292
  const buf = Buffer.from(await res.arrayBuffer());
17517
- await fs19.writeFile(cached, buf);
17293
+ await fs18.writeFile(cached, buf);
17518
17294
  }
17519
- await fs19.copyFile(cached, dest);
17295
+ await fs18.copyFile(cached, dest);
17520
17296
  installedCount++;
17521
17297
  addedBytes += entry.bytes;
17522
- log.dim(` ${path18.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
17298
+ log.dim(` ${path17.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
17523
17299
  } catch (err) {
17524
17300
  failures.push(`${entry.name} (${err instanceof Error ? err.message : String(err)})`);
17525
17301
  }
@@ -17535,13 +17311,13 @@ async function runAnims(opts) {
17535
17311
  version: manifest.version,
17536
17312
  clips: [...union].sort((a, b) => a.localeCompare(b))
17537
17313
  };
17538
- await fs19.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
17314
+ await fs18.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
17539
17315
  log.plain("");
17540
17316
  const parts = [`${installedCount} clip${installedCount === 1 ? "" : "s"} installed`];
17541
17317
  if (presentCount > 0) parts.push(`${presentCount} already present`);
17542
17318
  if (bundledSkips > 0) parts.push(`${bundledSkips} already bundled in animation-library.glb`);
17543
17319
  log.success(
17544
- `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path18.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
17320
+ `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path17.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
17545
17321
  );
17546
17322
  for (const [selector, entries] of resolved) {
17547
17323
  const names = entries.filter((e) => !coreNames.has(e.name)).map((e) => e.name);
@@ -17573,8 +17349,8 @@ async function loadManifest(baseOverride) {
17573
17349
  }
17574
17350
  } catch {
17575
17351
  }
17576
- const snapshotPath = path18.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
17577
- const manifest = JSON.parse(await fs19.readFile(snapshotPath, "utf8"));
17352
+ const snapshotPath = path17.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
17353
+ const manifest = JSON.parse(await fs18.readFile(snapshotPath, "utf8"));
17578
17354
  return { manifest, source: "snapshot" };
17579
17355
  }
17580
17356
  function resolveSelectors(manifest, selectors) {
@@ -17692,21 +17468,21 @@ function printCatalog(log, manifest, selectors) {
17692
17468
  }
17693
17469
  async function readGameManifest(file) {
17694
17470
  try {
17695
- return JSON.parse(await fs19.readFile(file, "utf8"));
17471
+ return JSON.parse(await fs18.readFile(file, "utf8"));
17696
17472
  } catch {
17697
17473
  return null;
17698
17474
  }
17699
17475
  }
17700
17476
  async function hasSize(file, bytes) {
17701
17477
  try {
17702
- return (await fs19.stat(file)).size === bytes;
17478
+ return (await fs18.stat(file)).size === bytes;
17703
17479
  } catch {
17704
17480
  return false;
17705
17481
  }
17706
17482
  }
17707
17483
  async function exists2(p) {
17708
17484
  try {
17709
- await fs19.access(p);
17485
+ await fs18.access(p);
17710
17486
  return true;
17711
17487
  } catch {
17712
17488
  return false;
@@ -17900,8 +17676,8 @@ var CONTROLLER_FILE_SETS = {
17900
17676
  ]
17901
17677
  }
17902
17678
  };
17903
- var CODE_DEST = path19.join("src", "controllers");
17904
- var ASSETS_DEST = path19.join("public", "assets");
17679
+ var CODE_DEST = path18.join("src", "controllers");
17680
+ var ASSETS_DEST = path18.join("public", "assets");
17905
17681
  async function runController(opts) {
17906
17682
  const log = createLogger({ quiet: opts.quiet });
17907
17683
  if (opts.kind?.trim() === "anims") {
@@ -17918,31 +17694,31 @@ async function runController(opts) {
17918
17694
  process.exitCode = 1;
17919
17695
  return;
17920
17696
  }
17921
- const srcDir = path19.join(getTemplatesDir(), "controllers");
17697
+ const srcDir = path18.join(getTemplatesDir(), "controllers");
17922
17698
  const root = opts.cwd ?? process.cwd();
17923
17699
  const set = CONTROLLER_FILE_SETS[kind];
17924
17700
  log.plain(c.bold(`genex controller ${kind}`));
17925
17701
  log.plain("");
17926
- log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path19.sep)}`);
17702
+ log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path18.sep)}`);
17927
17703
  const plan = [
17928
- ...set.code.map((rel) => ({ from: rel, rel: path19.join(CODE_DEST, rel) })),
17704
+ ...set.code.map((rel) => ({ from: rel, rel: path18.join(CODE_DEST, rel) })),
17929
17705
  ...set.assets.map((rel) => ({
17930
17706
  from: rel,
17931
- rel: path19.join(ASSETS_DEST, path19.basename(rel))
17707
+ rel: path18.join(ASSETS_DEST, path18.basename(rel))
17932
17708
  }))
17933
17709
  ];
17934
17710
  let copied = 0;
17935
17711
  let skipped = 0;
17936
17712
  try {
17937
17713
  for (const file of plan) {
17938
- const dest = path19.join(root, file.rel);
17714
+ const dest = path18.join(root, file.rel);
17939
17715
  if (!opts.force && await exists3(dest)) {
17940
17716
  skipped++;
17941
17717
  log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
17942
17718
  continue;
17943
17719
  }
17944
- await fs20.mkdir(path19.dirname(dest), { recursive: true });
17945
- await fs20.copyFile(path19.join(srcDir, file.from), dest);
17720
+ await fs19.mkdir(path18.dirname(dest), { recursive: true });
17721
+ await fs19.copyFile(path18.join(srcDir, file.from), dest);
17946
17722
  copied++;
17947
17723
  log.dim(` ${file.rel}`);
17948
17724
  }
@@ -17995,7 +17771,7 @@ async function runController(opts) {
17995
17771
  for (const line of set.sketch) {
17996
17772
  log.dim(` ${line}`);
17997
17773
  }
17998
- if (kind === "character" && !await exists3(path19.join(root, ASSETS_DEST, "meshy-character.json"))) {
17774
+ if (kind === "character" && !await exists3(path18.join(root, ASSETS_DEST, "meshy-character.json"))) {
17999
17775
  log.plain("");
18000
17776
  log.plain(
18001
17777
  ` ${stepOffset + 3}. This game has no generated character yet \u2014 ${c.cyan(
@@ -18027,9 +17803,9 @@ async function installMeshyCharacterManifest(args) {
18027
17803
  throw new Error("The API returned an invalid Meshy character manifest.");
18028
17804
  }
18029
17805
  assertCompleteMeshyControllerPack(manifest);
18030
- const destination = path19.join(args.root, ASSETS_DEST, "meshy-character.json");
18031
- await fs20.mkdir(path19.dirname(destination), { recursive: true });
18032
- await fs20.writeFile(
17806
+ const destination = path18.join(args.root, ASSETS_DEST, "meshy-character.json");
17807
+ await fs19.mkdir(path18.dirname(destination), { recursive: true });
17808
+ await fs19.writeFile(
18033
17809
  destination,
18034
17810
  `${JSON.stringify(manifest, null, 2)}
18035
17811
  `
@@ -18167,14 +17943,14 @@ function assertCompleteMeshyControllerPack(manifest) {
18167
17943
  }
18168
17944
  async function installFallbackAvatar(args) {
18169
17945
  const { root, srcDir, log } = args;
18170
- const dest = path19.join(root, ASSETS_DEST, "avatar.vrm");
18171
- await fs20.mkdir(path19.dirname(dest), { recursive: true });
18172
- await fs20.copyFile(path19.join(srcDir, "assets", "default-avatar.vrm"), dest);
17946
+ const dest = path18.join(root, ASSETS_DEST, "avatar.vrm");
17947
+ await fs19.mkdir(path18.dirname(dest), { recursive: true });
17948
+ await fs19.copyFile(path18.join(srcDir, "assets", "default-avatar.vrm"), dest);
18173
17949
  log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
18174
17950
  }
18175
17951
  async function exists3(p) {
18176
17952
  try {
18177
- await fs20.access(p);
17953
+ await fs19.access(p);
18178
17954
  return true;
18179
17955
  } catch {
18180
17956
  return false;
@@ -18182,8 +17958,8 @@ async function exists3(p) {
18182
17958
  }
18183
17959
 
18184
17960
  // src/commands/character.ts
18185
- import fs21 from "fs/promises";
18186
- import path20 from "path";
17961
+ import fs20 from "fs/promises";
17962
+ import path19 from "path";
18187
17963
  function exactAnimation(selector) {
18188
17964
  const trimmed = selector.trim();
18189
17965
  if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
@@ -18263,7 +18039,7 @@ function showAmbiguity(selector, candidates, log, json = false) {
18263
18039
  }
18264
18040
  process.exitCode = 1;
18265
18041
  }
18266
- var INSTALLED_MANIFEST = path20.join("public", "assets", "meshy-character.json");
18042
+ var INSTALLED_MANIFEST = path19.join("public", "assets", "meshy-character.json");
18267
18043
  async function resolveAdoptTarget(selector) {
18268
18044
  const trimmed = selector?.trim();
18269
18045
  if (trimmed && !trimmed.endsWith(".json")) {
@@ -18272,7 +18048,7 @@ async function resolveAdoptTarget(selector) {
18272
18048
  const file = trimmed ?? INSTALLED_MANIFEST;
18273
18049
  let raw;
18274
18050
  try {
18275
- raw = await fs21.readFile(file, "utf8");
18051
+ raw = await fs20.readFile(file, "utf8");
18276
18052
  } catch {
18277
18053
  return {
18278
18054
  ok: false,
@@ -18755,22 +18531,22 @@ async function context2(opts) {
18755
18531
  const project = await readProject();
18756
18532
  return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
18757
18533
  }
18758
- async function readVideo(path27, log) {
18534
+ async function readVideo(path26, log) {
18759
18535
  let bytes;
18760
18536
  try {
18761
- bytes = await readFile(path27);
18537
+ bytes = await readFile(path26);
18762
18538
  } catch {
18763
- log.error(`Can't read ${path27}.`);
18539
+ log.error(`Can't read ${path26}.`);
18764
18540
  return null;
18765
18541
  }
18766
18542
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
18767
- log.error(`${basename(path27)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
18543
+ log.error(`${basename(path26)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
18768
18544
  return null;
18769
18545
  }
18770
18546
  return bytes;
18771
18547
  }
18772
- async function uploadVideo(apiUrl, token, characterId, path27, bytes, log) {
18773
- const contentType = /\.mov$/i.test(path27) ? "video/quicktime" : "video/mp4";
18548
+ async function uploadVideo(apiUrl, token, characterId, path26, bytes, log) {
18549
+ const contentType = /\.mov$/i.test(path26) ? "video/quicktime" : "video/mp4";
18774
18550
  const minted = await apiFetch(
18775
18551
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
18776
18552
  {
@@ -18785,7 +18561,7 @@ async function uploadVideo(apiUrl, token, characterId, path27, bytes, log) {
18785
18561
  return null;
18786
18562
  }
18787
18563
  const { uploadUrl, videoUrl } = await minted.json();
18788
- log.dim(` uploading ${basename(path27)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
18564
+ log.dim(` uploading ${basename(path26)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
18789
18565
  const put = await fetch(uploadUrl, {
18790
18566
  method: "PUT",
18791
18567
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -19101,8 +18877,8 @@ function rank(items, query) {
19101
18877
  }
19102
18878
 
19103
18879
  // src/commands/motion.ts
19104
- import fs22 from "fs/promises";
19105
- import path21 from "path";
18880
+ import fs21 from "fs/promises";
18881
+ import path20 from "path";
19106
18882
 
19107
18883
  // src/lib/motion/npz.ts
19108
18884
  import zlib from "zlib";
@@ -20353,7 +20129,7 @@ async function motionGen(opts, log) {
20353
20129
  }
20354
20130
  if (opts.constraintsPath !== void 0) {
20355
20131
  try {
20356
- const raw = await fs22.readFile(opts.constraintsPath, "utf8");
20132
+ const raw = await fs21.readFile(opts.constraintsPath, "utf8");
20357
20133
  generationOptions.constraints = JSON.parse(raw);
20358
20134
  } catch {
20359
20135
  log.error(`Couldn't read the --constraints JSON at ${opts.constraintsPath}.`);
@@ -20377,10 +20153,10 @@ async function motionGen(opts, log) {
20377
20153
  async function expandTakes(selectors) {
20378
20154
  const out = [];
20379
20155
  for (const sel of selectors) {
20380
- const st = await fs22.stat(sel).catch(() => null);
20156
+ const st = await fs21.stat(sel).catch(() => null);
20381
20157
  if (st?.isDirectory()) {
20382
- const names = await fs22.readdir(sel);
20383
- for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path21.join(sel, n));
20158
+ const names = await fs21.readdir(sel);
20159
+ for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path20.join(sel, n));
20384
20160
  } else if (st?.isFile()) {
20385
20161
  out.push(sel);
20386
20162
  } else {
@@ -20415,7 +20191,7 @@ async function motionVerify(opts, log) {
20415
20191
  let gates = DEFAULT_GATES;
20416
20192
  if (opts.gatesPath) {
20417
20193
  try {
20418
- gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs22.readFile(opts.gatesPath, "utf8")));
20194
+ gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs21.readFile(opts.gatesPath, "utf8")));
20419
20195
  } catch {
20420
20196
  log.error(`Couldn't read the --gates JSON at ${opts.gatesPath}.`);
20421
20197
  process.exitCode = 1;
@@ -20437,9 +20213,9 @@ async function motionVerify(opts, log) {
20437
20213
  }
20438
20214
  const reports = [];
20439
20215
  for (const file of files) {
20440
- const stem = path21.basename(file).replace(/\.npz$/, "");
20216
+ const stem = path20.basename(file).replace(/\.npz$/, "");
20441
20217
  try {
20442
- reports.push(analyzeTake(stem, await fs22.readFile(file), gates));
20218
+ reports.push(analyzeTake(stem, await fs21.readFile(file), gates));
20443
20219
  } catch (err) {
20444
20220
  reports.push({
20445
20221
  take: stem,
@@ -20477,7 +20253,7 @@ async function motionCompile(opts, log) {
20477
20253
  let cfg = DEFAULT_MOTION_CONFIG;
20478
20254
  if (opts.configPath) {
20479
20255
  try {
20480
- const patch = JSON.parse(await fs22.readFile(opts.configPath, "utf8"));
20256
+ const patch = JSON.parse(await fs21.readFile(opts.configPath, "utf8"));
20481
20257
  cfg = { ...DEFAULT_MOTION_CONFIG, ...patch, idleLoop: { ...DEFAULT_MOTION_CONFIG.idleLoop, ...patch.idleLoop } };
20482
20258
  } catch {
20483
20259
  log.error(`Couldn't read the --config JSON at ${opts.configPath}.`);
@@ -20495,16 +20271,16 @@ async function motionCompile(opts, log) {
20495
20271
  }
20496
20272
  const inputs = [];
20497
20273
  for (const file of files) {
20498
- const stem = path21.basename(file).replace(/\.npz$/, "");
20274
+ const stem = path20.basename(file).replace(/\.npz$/, "");
20499
20275
  try {
20500
- inputs.push({ stem, take: loadTake(await fs22.readFile(file)) });
20276
+ inputs.push({ stem, take: loadTake(await fs21.readFile(file)) });
20501
20277
  } catch (err) {
20502
20278
  log.error(`${stem}: ${err instanceof Error ? err.message : String(err)}`);
20503
20279
  process.exitCode = 1;
20504
20280
  return;
20505
20281
  }
20506
20282
  }
20507
- const setName = opts.set ?? path21.basename(opts.out).replace(/\.json$/, "");
20283
+ const setName = opts.set ?? path20.basename(opts.out).replace(/\.json$/, "");
20508
20284
  let result;
20509
20285
  try {
20510
20286
  result = compileSet(inputs, setName, cfg);
@@ -20519,9 +20295,9 @@ async function motionCompile(opts, log) {
20519
20295
  process.exitCode = 1;
20520
20296
  return;
20521
20297
  }
20522
- await fs22.mkdir(path21.dirname(path21.resolve(opts.out)), { recursive: true });
20298
+ await fs21.mkdir(path20.dirname(path20.resolve(opts.out)), { recursive: true });
20523
20299
  const json = JSON.stringify(result.data);
20524
- await fs22.writeFile(opts.out, json);
20300
+ await fs21.writeFile(opts.out, json);
20525
20301
  if (opts.json) {
20526
20302
  writeJson({ out: opts.out, set: setName, tracks: Object.keys(result.data.gaits), bytes: json.length });
20527
20303
  return;
@@ -20539,9 +20315,9 @@ var MOTION_RUNTIME_FILES = [
20539
20315
  var MOTION_PRESETS = {
20540
20316
  rifle: ["sets/rifle.json", "sets/jumps.json"]
20541
20317
  };
20542
- var MOTION_DEST = path21.join("src", "motion");
20318
+ var MOTION_DEST = path20.join("src", "motion");
20543
20319
  async function motionInstall(opts, log) {
20544
- const srcDir = path21.join(getTemplatesDir(), "motion");
20320
+ const srcDir = path20.join(getTemplatesDir(), "motion");
20545
20321
  const root = opts.cwd ?? process.cwd();
20546
20322
  const preset = opts.set;
20547
20323
  if (preset !== void 0 && !MOTION_PRESETS[preset]) {
@@ -20552,21 +20328,21 @@ async function motionInstall(opts, log) {
20552
20328
  const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
20553
20329
  log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
20554
20330
  log.plain("");
20555
- log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path21.sep)}`);
20331
+ log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path20.sep)}`);
20556
20332
  let copied = 0, skipped = 0;
20557
20333
  try {
20558
20334
  for (const rel of files) {
20559
- const dest = path21.join(root, MOTION_DEST, rel);
20560
- const exists5 = await fs22.access(dest).then(() => true, () => false);
20335
+ const dest = path20.join(root, MOTION_DEST, rel);
20336
+ const exists5 = await fs21.access(dest).then(() => true, () => false);
20561
20337
  if (!opts.force && exists5) {
20562
20338
  skipped++;
20563
- log.dim(` skipped ${path21.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
20339
+ log.dim(` skipped ${path20.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
20564
20340
  continue;
20565
20341
  }
20566
- await fs22.mkdir(path21.dirname(dest), { recursive: true });
20567
- await fs22.copyFile(path21.join(srcDir, rel), dest);
20342
+ await fs21.mkdir(path20.dirname(dest), { recursive: true });
20343
+ await fs21.copyFile(path20.join(srcDir, rel), dest);
20568
20344
  copied++;
20569
- log.dim(` ${path21.join(MOTION_DEST, rel)}`);
20345
+ log.dim(` ${path20.join(MOTION_DEST, rel)}`);
20570
20346
  }
20571
20347
  } catch (err) {
20572
20348
  log.error(`Copy failed: ${String(err)}`);
@@ -20607,7 +20383,7 @@ async function motionConstraints(opts, log) {
20607
20383
  }
20608
20384
  const doc = directionConstraint(dir, speed, duration);
20609
20385
  const out = opts.out ?? "constraints.json";
20610
- await fs22.writeFile(out, JSON.stringify(doc));
20386
+ await fs21.writeFile(out, JSON.stringify(doc));
20611
20387
  if (opts.json) {
20612
20388
  writeJson({ out, dir: dirName, speed, duration, waypoints: doc[0].frame_indices.length });
20613
20389
  return;
@@ -20644,14 +20420,14 @@ async function runMotion(opts) {
20644
20420
  }
20645
20421
 
20646
20422
  // src/commands/blender.ts
20647
- import fs23 from "fs/promises";
20648
- import path22 from "path";
20423
+ import fs22 from "fs/promises";
20424
+ import path21 from "path";
20649
20425
  var SUBS2 = ["demo", "exec", "snap", "scene", "import", "export", "reset", "mcp", "serve"];
20650
20426
  var DEFAULT_OUT_DIR = "assets/blender";
20651
20427
  async function writeB64(dir, name, b64) {
20652
- await fs23.mkdir(dir, { recursive: true });
20653
- const p = path22.join(dir, name);
20654
- await fs23.writeFile(p, Buffer.from(b64, "base64"));
20428
+ await fs22.mkdir(dir, { recursive: true });
20429
+ const p = path21.join(dir, name);
20430
+ await fs22.writeFile(p, Buffer.from(b64, "base64"));
20655
20431
  return p;
20656
20432
  }
20657
20433
  function reportScene(log, s) {
@@ -20677,7 +20453,7 @@ async function runBlender(opts) {
20677
20453
  return serveLocalBlender({ port, log });
20678
20454
  }
20679
20455
  if (sub === "mcp") {
20680
- const { runBlenderMcp } = await import("./blender-mcp-5NBIVBE6.js");
20456
+ const { runBlenderMcp } = await import("./blender-mcp-SBNHGDIX.js");
20681
20457
  return runBlenderMcp();
20682
20458
  }
20683
20459
  let base = blenderEndpoint();
@@ -20697,7 +20473,7 @@ async function runBlender(opts) {
20697
20473
  log.plain(rest.join("\n"));
20698
20474
  return 1;
20699
20475
  }
20700
- const outDir = path22.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
20476
+ const outDir = path21.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
20701
20477
  const mode = opts.mode;
20702
20478
  if (mode !== void 0 && !isRenderMode(mode)) {
20703
20479
  log.error(`Unknown --mode ${mode}. Use one of: ${RENDER_MODES.join(", ")}.`);
@@ -20737,14 +20513,14 @@ async function runBlender(opts) {
20737
20513
  return 0;
20738
20514
  }
20739
20515
  case "export": {
20740
- const target = opts.out ?? path22.join(outDir, "scene.glb");
20516
+ const target = opts.out ?? path21.join(outDir, "scene.glb");
20741
20517
  const r = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
20742
20518
  if (!r.glbBase64) {
20743
20519
  log.error(`/export answered with no GLB bytes${r.uploaded ? " (it was uploaded, not inlined)" : ""}`);
20744
20520
  return 1;
20745
20521
  }
20746
- await fs23.mkdir(path22.dirname(target), { recursive: true });
20747
- await fs23.writeFile(target, Buffer.from(r.glbBase64, "base64"));
20522
+ await fs22.mkdir(path21.dirname(target), { recursive: true });
20523
+ await fs22.writeFile(target, Buffer.from(r.glbBase64, "base64"));
20748
20524
  log.success(`Exported ${r.bytes ?? 0} bytes`);
20749
20525
  log.plain(` ${c.cyan(target)}`);
20750
20526
  return 0;
@@ -20777,12 +20553,12 @@ async function runBlender(opts) {
20777
20553
  return 1;
20778
20554
  }
20779
20555
  try {
20780
- script = await fs23.readFile(opts.input, "utf8");
20556
+ script = await fs22.readFile(opts.input, "utf8");
20781
20557
  } catch {
20782
20558
  log.error(`Can't read ${opts.input}`);
20783
20559
  return 1;
20784
20560
  }
20785
- label = path22.basename(opts.input);
20561
+ label = path21.basename(opts.input);
20786
20562
  }
20787
20563
  const r = await blenderCall(base, "/exec", { script, ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
20788
20564
  if (r.stdout?.trim()) log.plain(r.stdout.trimEnd());
@@ -20901,9 +20677,9 @@ print(f"castle: {n} objects")
20901
20677
  `;
20902
20678
 
20903
20679
  // src/commands/asset-new.ts
20904
- import fs24 from "fs";
20680
+ import fs23 from "fs";
20905
20681
  import fsp from "fs/promises";
20906
- import path23 from "path";
20682
+ import path22 from "path";
20907
20683
  import { pathToFileURL } from "url";
20908
20684
  var EXTRA_FILES = [
20909
20685
  "genex-asset.example.json",
@@ -20997,7 +20773,7 @@ Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-buil
20997
20773
  }
20998
20774
  async function runAssetNew(options) {
20999
20775
  const log = createLogger();
21000
- const cwd = options.dir ? path23.resolve(options.dir) : process.cwd();
20776
+ const cwd = options.dir ? path22.resolve(options.dir) : process.cwd();
21001
20777
  const slug = options.assetSlug;
21002
20778
  if (!slug) {
21003
20779
  log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
@@ -21007,14 +20783,14 @@ async function runAssetNew(options) {
21007
20783
  log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
21008
20784
  return 1;
21009
20785
  }
21010
- const templateDir = path23.join(getTemplatesDir(), "asset-viewer");
21011
- if (!fs24.existsSync(templateDir)) {
20786
+ const templateDir = path22.join(getTemplatesDir(), "asset-viewer");
20787
+ if (!fs23.existsSync(templateDir)) {
21012
20788
  log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
21013
20789
  return 1;
21014
20790
  }
21015
- const manifestTools = await import(pathToFileURL(path23.join(templateDir, "tools", "emit-manifest.mjs")).href);
20791
+ const manifestTools = await import(pathToFileURL(path22.join(templateDir, "tools", "emit-manifest.mjs")).href);
21016
20792
  const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
21017
- const lockPath = path23.join(templateDir, "shared-files.sha256.json");
20793
+ const lockPath = path22.join(templateDir, "shared-files.sha256.json");
21018
20794
  const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
21019
20795
  const actual = hashSharedFiles(templateDir);
21020
20796
  const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
@@ -21027,8 +20803,8 @@ async function runAssetNew(options) {
21027
20803
  const triBand = parseBand(options.triBand ?? "500-8000");
21028
20804
  const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
21029
20805
  const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
21030
- const outDir = path23.resolve(cwd, options.out ?? slug);
21031
- if (fs24.existsSync(outDir) && fs24.readdirSync(outDir).length > 0 && !options.force) {
20806
+ const outDir = path22.resolve(cwd, options.out ?? slug);
20807
+ if (fs23.existsSync(outDir) && fs23.readdirSync(outDir).length > 0 && !options.force) {
21032
20808
  log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
21033
20809
  return 1;
21034
20810
  }
@@ -21056,24 +20832,24 @@ async function runAssetNew(options) {
21056
20832
  };
21057
20833
  await fsp.mkdir(outDir, { recursive: true });
21058
20834
  for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
21059
- const to = path23.join(outDir, rel);
21060
- await fsp.mkdir(path23.dirname(to), { recursive: true });
21061
- await fsp.copyFile(path23.join(templateDir, rel), to);
20835
+ const to = path22.join(outDir, rel);
20836
+ await fsp.mkdir(path22.dirname(to), { recursive: true });
20837
+ await fsp.copyFile(path22.join(templateDir, rel), to);
21062
20838
  }
21063
- const pkg = fillTemplate(await fsp.readFile(path23.join(templateDir, "package.json"), "utf8"), {
20839
+ const pkg = fillTemplate(await fsp.readFile(path22.join(templateDir, "package.json"), "utf8"), {
21064
20840
  slug,
21065
20841
  name,
21066
20842
  version
21067
20843
  });
21068
- await fsp.writeFile(path23.join(outDir, "package.json"), pkg, "utf8");
21069
- await fsp.writeFile(path23.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
21070
- await fsp.writeFile(path23.join(outDir, ".gitignore"), GITIGNORE, "utf8");
20844
+ await fsp.writeFile(path22.join(outDir, "package.json"), pkg, "utf8");
20845
+ await fsp.writeFile(path22.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
20846
+ await fsp.writeFile(path22.join(outDir, ".gitignore"), GITIGNORE, "utf8");
21071
20847
  await fsp.writeFile(
21072
- path23.join(outDir, "DESIGN.md"),
20848
+ path22.join(outDir, "DESIGN.md"),
21073
20849
  designDoc({ name, slug, sizeMeters, triBand, holder }),
21074
20850
  "utf8"
21075
20851
  );
21076
- const placeholder = await fsp.readFile(path23.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
20852
+ const placeholder = await fsp.readFile(path22.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
21077
20853
  const seeded = seedAssetSource(placeholder, {
21078
20854
  slug,
21079
20855
  name,
@@ -21084,8 +20860,8 @@ async function runAssetNew(options) {
21084
20860
  pascalCase
21085
20861
  });
21086
20862
  const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
21087
- await fsp.mkdir(path23.join(outDir, "src", "asset"), { recursive: true });
21088
- await fsp.writeFile(path23.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
20863
+ await fsp.mkdir(path22.join(outDir, "src", "asset"), { recursive: true });
20864
+ await fsp.writeFile(path22.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
21089
20865
  const copied = hashSharedFiles(outDir);
21090
20866
  const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
21091
20867
  if (mismatched.length) {
@@ -21093,7 +20869,7 @@ async function runAssetNew(options) {
21093
20869
  return 1;
21094
20870
  }
21095
20871
  await fsp.writeFile(
21096
- path23.join(outDir, PARITY_FILENAME),
20872
+ path22.join(outDir, PARITY_FILENAME),
21097
20873
  JSON.stringify(
21098
20874
  {
21099
20875
  note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
@@ -21137,12 +20913,12 @@ async function runAssetNew(options) {
21137
20913
  }
21138
20914
 
21139
20915
  // src/commands/tools.ts
21140
- import path26 from "path";
20916
+ import path25 from "path";
21141
20917
 
21142
20918
  // src/lib/local-install.ts
21143
- import fs25 from "fs/promises";
21144
- import path24 from "path";
21145
- import { spawn as spawn5 } from "child_process";
20919
+ import fs24 from "fs/promises";
20920
+ import path23 from "path";
20921
+ import { spawn as spawn4 } from "child_process";
21146
20922
  var CLI_PACKAGE = "@genex-ai/cli-demo";
21147
20923
  var FULL_NAME_FALLBACK = `npx ${CLI_PACKAGE}@${CLI_CHANNEL}`;
21148
20924
  var LOCKFILES = [
@@ -21154,17 +20930,17 @@ var LOCKFILES = [
21154
20930
  ];
21155
20931
  async function exists4(p) {
21156
20932
  try {
21157
- await fs25.access(p);
20933
+ await fs24.access(p);
21158
20934
  return true;
21159
20935
  } catch {
21160
20936
  return false;
21161
20937
  }
21162
20938
  }
21163
20939
  async function detectPackageManager(cwd) {
21164
- let dir = path24.resolve(cwd);
20940
+ let dir = path23.resolve(cwd);
21165
20941
  for (; ; ) {
21166
20942
  try {
21167
- const raw = await fs25.readFile(path24.join(dir, "package.json"), "utf8");
20943
+ const raw = await fs24.readFile(path23.join(dir, "package.json"), "utf8");
21168
20944
  const pm = JSON.parse(raw).packageManager;
21169
20945
  if (typeof pm === "string") {
21170
20946
  const name = pm.split("@")[0];
@@ -21173,18 +20949,18 @@ async function detectPackageManager(cwd) {
21173
20949
  } catch {
21174
20950
  }
21175
20951
  for (const [file, pm] of LOCKFILES) {
21176
- if (await exists4(path24.join(dir, file))) return pm;
20952
+ if (await exists4(path23.join(dir, file))) return pm;
21177
20953
  }
21178
- const parent = path24.dirname(dir);
20954
+ const parent = path23.dirname(dir);
21179
20955
  if (parent === dir) return "npm";
21180
20956
  dir = parent;
21181
20957
  }
21182
20958
  }
21183
20959
  async function findLocalCli(cwd) {
21184
- let dir = path24.resolve(cwd);
20960
+ let dir = path23.resolve(cwd);
21185
20961
  for (; ; ) {
21186
- if (await exists4(path24.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
21187
- const parent = path24.dirname(dir);
20962
+ if (await exists4(path23.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
20963
+ const parent = path23.dirname(dir);
21188
20964
  if (parent === dir) return null;
21189
20965
  dir = parent;
21190
20966
  }
@@ -21202,7 +20978,7 @@ function installArgs(pm, spec) {
21202
20978
  }
21203
20979
  }
21204
20980
  function manifestName(cwd) {
21205
- const slug = path24.basename(path24.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
20981
+ const slug = path23.basename(path23.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
21206
20982
  return slug || "genex-tools-workspace";
21207
20983
  }
21208
20984
  function isSourceRun(moduleUrl = import.meta.url) {
@@ -21219,7 +20995,7 @@ async function spawnInstall(command, args, cwd) {
21219
20995
  resolve({ ok, detail });
21220
20996
  };
21221
20997
  try {
21222
- const child = spawn5(command, args, {
20998
+ const child = spawn4(command, args, {
21223
20999
  cwd,
21224
21000
  // npm/pnpm/yarn are .cmd shims on Windows; the args carry no user input.
21225
21001
  shell: process.platform === "win32",
@@ -21260,10 +21036,10 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
21260
21036
  return;
21261
21037
  }
21262
21038
  const pm = await detectPackageManager(cwd);
21263
- const hadManifest = await exists4(path24.join(cwd, "package.json"));
21039
+ const hadManifest = await exists4(path23.join(cwd, "package.json"));
21264
21040
  if (!hadManifest) {
21265
- await fs25.writeFile(
21266
- path24.join(cwd, "package.json"),
21041
+ await fs24.writeFile(
21042
+ path23.join(cwd, "package.json"),
21267
21043
  JSON.stringify({ name: manifestName(cwd), private: true }, null, 2) + "\n"
21268
21044
  );
21269
21045
  await ensureIgnored(cwd, "node_modules/");
@@ -21283,20 +21059,20 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
21283
21059
  }
21284
21060
  }
21285
21061
  async function ensureIgnored(dir, entry) {
21286
- const file = path24.join(dir, ".gitignore");
21062
+ const file = path23.join(dir, ".gitignore");
21287
21063
  let content = "";
21288
21064
  try {
21289
- content = await fs25.readFile(file, "utf8");
21065
+ content = await fs24.readFile(file, "utf8");
21290
21066
  } catch {
21291
21067
  }
21292
21068
  if (content.split("\n").some((l) => l.trim() === entry)) return;
21293
21069
  let next = content;
21294
21070
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
21295
- await fs25.writeFile(file, next + entry + "\n");
21071
+ await fs24.writeFile(file, next + entry + "\n");
21296
21072
  }
21297
21073
 
21298
21074
  // src/commands/doctor.ts
21299
- import path25 from "path";
21075
+ import path24 from "path";
21300
21076
  var LANE_ORDER = [
21301
21077
  "model",
21302
21078
  "image",
@@ -21358,9 +21134,15 @@ async function runDoctor(opts = {}) {
21358
21134
  rows.push(authRow(authState, email, opts.envPath));
21359
21135
  let credits = null;
21360
21136
  let lanes = null;
21137
+ let legal = null;
21361
21138
  if (authState === "ok" && token) {
21362
- [credits, lanes] = await Promise.all([fetchCredits(apiUrl, token), fetchLanes(apiUrl, token)]);
21139
+ [credits, lanes, legal] = await Promise.all([
21140
+ fetchCredits(apiUrl, token),
21141
+ fetchLanes(apiUrl, token),
21142
+ fetchLegalStatus(apiUrl, token)
21143
+ ]);
21363
21144
  }
21145
+ rows.push(termsRow(authState, legal));
21364
21146
  rows.push(creditsRow(authState, credits));
21365
21147
  if (opts.json) {
21366
21148
  process.stdout.write(
@@ -21370,6 +21152,7 @@ async function runDoctor(opts = {}) {
21370
21152
  node: process.versions.node,
21371
21153
  cli: { installed, latest },
21372
21154
  auth: { state: authState, email, apiUrl },
21155
+ terms: legal,
21373
21156
  credits,
21374
21157
  lanes: lanes ? { paused: lanes.paused, lanes: lanes.lanes } : null,
21375
21158
  ok: !rows.some((r) => r.bad)
@@ -21471,6 +21254,17 @@ function authRow(state, email, envPath) {
21471
21254
  };
21472
21255
  }
21473
21256
  }
21257
+ function termsRow(state, info) {
21258
+ if (state !== "ok") return { label: "Terms", value: "\u2014 (sign in first)" };
21259
+ if (!info) return { label: "Terms", value: "couldn't be read from this server" };
21260
+ if (info.accepted) return { label: "Terms", value: `accepted (${info.acceptedVersion ?? info.required})` };
21261
+ return {
21262
+ label: "Terms",
21263
+ value: "changed since you last agreed \u2014 every generate and publish is refused",
21264
+ bad: true,
21265
+ fix: `Open ${acceptUrl(info.acceptUrl)} and agree \u2014 one click; any refused command then continues on its own.`
21266
+ };
21267
+ }
21474
21268
  function creditsRow(state, info) {
21475
21269
  if (state !== "ok") return { label: "Credits", value: "\u2014 (sign in first)" };
21476
21270
  if (!info) return { label: "Credits", value: "couldn't be read from this server" };
@@ -21522,9 +21316,21 @@ async function fetchCredits(apiUrl, token) {
21522
21316
  return null;
21523
21317
  }
21524
21318
  }
21319
+ async function fetchLegalStatus(apiUrl, token) {
21320
+ try {
21321
+ const res = await apiFetch(`${apiUrl}/api/legal/status`, {
21322
+ headers: { Authorization: `Bearer ${token}` },
21323
+ signal: AbortSignal.timeout(6e3)
21324
+ });
21325
+ if (!res.ok) return null;
21326
+ return await res.json();
21327
+ } catch {
21328
+ return null;
21329
+ }
21330
+ }
21525
21331
  async function firstSkillsMarker() {
21526
21332
  for (const target of resolveAgentTargets()) {
21527
- const marker = await readSkillsMarker(path25.join(target.baseDir, "skills"));
21333
+ const marker = await readSkillsMarker(path24.join(target.baseDir, "skills"));
21528
21334
  if (marker) return marker;
21529
21335
  }
21530
21336
  return null;
@@ -21563,8 +21369,8 @@ async function runTools(opts) {
21563
21369
  let totalNew = 0;
21564
21370
  let totalUpdated = 0;
21565
21371
  for (const t of targets) {
21566
- const dest = path26.join(t.baseDir, "skills");
21567
- const { copied, updated } = await copyTemplates(path26.join(templatesDir, "skills"), dest, {
21372
+ const dest = path25.join(t.baseDir, "skills");
21373
+ const { copied, updated } = await copyTemplates(path25.join(templatesDir, "skills"), dest, {
21568
21374
  filter: (rel) => skillFamilyFilter("tools")(`skills/${rel}`)
21569
21375
  });
21570
21376
  await pruneRemovedSkills(dest, log);
@@ -21619,7 +21425,6 @@ async function runTools(opts) {
21619
21425
  await runDoctor({ apiUrl: opts.apiUrl, envPath: opts.envPath, quiet: opts.quiet });
21620
21426
  log.plain("");
21621
21427
  log.success("Genex Tools is ready. \u{1F9F0}");
21622
- log.plain(` Try it: ${c.cyan('npx genex image "weathered treasure map, hand-inked" --transparent')}`);
21623
21428
  log.dim(" Assets land in ./assets/ as files you own \u2014 wire the local path into your game.");
21624
21429
  }
21625
21430