@genex-ai/cli-demo 1.34.2 → 1.34.3-dev.704

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
@@ -30,7 +30,6 @@ import {
30
30
  reportForceRefused,
31
31
  reportStale,
32
32
  reportTermsRefusal,
33
- restrictFilePermissions,
34
33
  rotateRejectedEnv,
35
34
  run,
36
35
  runAccept,
@@ -40,15 +39,18 @@ import {
40
39
  sourceTreeHash,
41
40
  urlHasEmbeddedCredentials,
42
41
  writeProject,
42
+ writeSecretFile,
43
43
  writeUserToken,
44
44
  writeWorkspace
45
- } from "./chunk-OJAXYQ5L.js";
45
+ } from "./chunk-MRV3Q3WU.js";
46
46
  import {
47
47
  CLI_CHANNEL,
48
48
  DEFAULT_API_URL,
49
49
  DEFAULT_AUTH_URL,
50
- ENV_TOKEN_KEY,
50
+ ENV_FILE_ENV,
51
51
  STANDS,
52
+ assertAuthorizationOrigin,
53
+ bindToken,
52
54
  c,
53
55
  getAnimsBase,
54
56
  getAnimsCacheDir,
@@ -58,8 +60,10 @@ import {
58
60
  getGenexDir,
59
61
  getGenexEnvPath,
60
62
  getTemplatesDir,
63
+ normalizeApiOrigin,
64
+ originKey,
61
65
  resolveAgentTargets
62
- } from "./chunk-5FA2WLM7.js";
66
+ } from "./chunk-AQW7HUPV.js";
63
67
 
64
68
  // src/instrument.ts
65
69
  import * as Sentry from "@sentry/node";
@@ -169,32 +173,45 @@ import os2 from "os";
169
173
  import readline from "readline";
170
174
  import { spawn } from "child_process";
171
175
  import { URL as URL2 } from "url";
176
+ import path2 from "path";
172
177
 
173
178
  // src/lib/pending-auth.ts
174
179
  import fs from "fs/promises";
175
180
  import path from "path";
176
- function pendingAuthPath() {
181
+ function pendingAuthPath(apiUrl = getApiUrl(), envPath) {
182
+ return path.join(`${getGenexEnvPath(envPath)}.auth`, `${originKey(apiUrl)}.json`);
183
+ }
184
+ function legacyPath() {
177
185
  return path.join(getGenexDir(), "pending-auth.json");
178
186
  }
179
- async function readPendingAuth(apiUrl) {
187
+ function defaultProfile(envPath) {
188
+ return !envPath && !process.env[ENV_FILE_ENV];
189
+ }
190
+ async function readRecord(file, apiUrl) {
180
191
  try {
181
- const raw = JSON.parse(await fs.readFile(pendingAuthPath(), "utf8"));
182
- if (!raw?.deviceCode || !raw.userCode || raw.apiUrl !== apiUrl) return null;
183
- if (Date.now() >= raw.expiresAt) return null;
184
- return raw;
192
+ const raw = JSON.parse(await fs.readFile(file, "utf8"));
193
+ if (!raw?.deviceCode || !raw.userCode || !raw.verifyUrl || normalizeApiOrigin(raw.apiUrl) !== normalizeApiOrigin(apiUrl) || !Number.isFinite(raw.expiresAt) || Date.now() >= raw.expiresAt) return null;
194
+ return { ...raw, apiUrl: normalizeApiOrigin(raw.apiUrl) };
185
195
  } catch {
186
196
  return null;
187
197
  }
188
198
  }
189
- async function writePendingAuth(pending) {
190
- const file = pendingAuthPath();
191
- await fs.mkdir(path.dirname(file), { recursive: true });
192
- await fs.writeFile(file, JSON.stringify(pending, null, 2), { mode: 384 });
193
- await restrictFilePermissions(file);
199
+ async function readPendingAuth(apiUrl, envPath) {
200
+ const current = await readRecord(pendingAuthPath(apiUrl, envPath), apiUrl);
201
+ if (current) return current;
202
+ return defaultProfile(envPath) ? readRecord(legacyPath(), apiUrl) : null;
194
203
  }
195
- async function clearPendingAuth() {
196
- await fs.rm(pendingAuthPath(), { force: true }).catch(() => {
204
+ async function writePendingAuth(pending, envPath) {
205
+ const apiUrl = normalizeApiOrigin(pending.apiUrl);
206
+ await writeSecretFile(pendingAuthPath(apiUrl, envPath), JSON.stringify({ ...pending, apiUrl }) + "\n");
207
+ }
208
+ async function clearPendingAuth(apiUrl = getApiUrl(), envPath) {
209
+ await fs.rm(pendingAuthPath(apiUrl, envPath), { force: true }).catch(() => {
197
210
  });
211
+ if (defaultProfile(envPath) && await readRecord(legacyPath(), apiUrl)) {
212
+ await fs.rm(legacyPath(), { force: true }).catch(() => {
213
+ });
214
+ }
198
215
  }
199
216
 
200
217
  // src/lib/auth.ts
@@ -206,11 +223,15 @@ var AuthPendingError = class extends Error {
206
223
  // .ts sources directly by stripping types) forbids the shorthand.
207
224
  userCode;
208
225
  verifyUrl;
209
- constructor(userCode, verifyUrl) {
226
+ apiUrl;
227
+ envPath;
228
+ constructor(userCode, verifyUrl, apiUrl, envPath) {
210
229
  super("Authorization is still pending.");
211
230
  this.name = "AuthPendingError";
212
231
  this.userCode = userCode;
213
232
  this.verifyUrl = verifyUrl;
233
+ this.apiUrl = apiUrl;
234
+ this.envPath = envPath;
214
235
  }
215
236
  };
216
237
  function formatUserCode(code) {
@@ -218,12 +239,13 @@ function formatUserCode(code) {
218
239
  }
219
240
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
220
241
  async function authorize(apiBaseUrl, authBaseUrl, options) {
221
- const { log, inlineWaitMs = DEFAULT_INLINE_WAIT_MS, open = openBrowser, label } = options;
222
- const resumed = await readPendingAuth(apiBaseUrl);
242
+ apiBaseUrl = normalizeApiOrigin(apiBaseUrl);
243
+ const { log, inlineWaitMs = DEFAULT_INLINE_WAIT_MS, open = openBrowser, label, envPath } = options;
244
+ const resumed = await readPendingAuth(apiBaseUrl, options.envPath);
223
245
  if (resumed) {
224
246
  log.step("Picking up where the last sign-in left off\u2026");
225
247
  printCode(log, resumed.userCode, resumed.verifyUrl);
226
- return await pollForToken(apiBaseUrl, resumed, { log, inlineWaitMs });
248
+ return await pollForToken(apiBaseUrl, resumed, { log, inlineWaitMs, envPath: options.envPath });
227
249
  }
228
250
  let started;
229
251
  try {
@@ -231,7 +253,7 @@ async function authorize(apiBaseUrl, authBaseUrl, options) {
231
253
  } catch (err) {
232
254
  if (err instanceof LegacyApiError) {
233
255
  log.dim("This Genex API doesn't support device sign-in yet \u2014 using the legacy browser flow.");
234
- return await authorizeLoopback(authBaseUrl, { log, open, timeoutMs: inlineWaitMs });
256
+ return bindToken(await authorizeLoopback(authBaseUrl, { log, open, timeoutMs: inlineWaitMs }), apiBaseUrl);
235
257
  }
236
258
  throw err;
237
259
  }
@@ -242,7 +264,7 @@ async function authorize(apiBaseUrl, authBaseUrl, options) {
242
264
  expiresAt: Date.now() + started.expiresIn * 1e3,
243
265
  apiUrl: apiBaseUrl
244
266
  };
245
- await writePendingAuth(pending);
267
+ await writePendingAuth(pending, envPath);
246
268
  printCode(log, started.userCode, started.verifyUrl);
247
269
  let warned = false;
248
270
  const warnManual = () => {
@@ -251,7 +273,7 @@ async function authorize(apiBaseUrl, authBaseUrl, options) {
251
273
  log.dim(" (couldn't open a browser here \u2014 open the link above yourself)");
252
274
  };
253
275
  if (!open(started.verifyUrl, warnManual)) warnManual();
254
- return await pollForToken(apiBaseUrl, pending, { log, inlineWaitMs, interval: started.interval });
276
+ return await pollForToken(apiBaseUrl, pending, { log, inlineWaitMs, interval: started.interval, envPath });
255
277
  }
256
278
  var LegacyApiError = class extends Error {
257
279
  };
@@ -314,23 +336,28 @@ async function pollForToken(apiBaseUrl, pending, opts) {
314
336
  res = new Response(null, { status: 599 });
315
337
  }
316
338
  if (res.status === 410) {
317
- await clearPendingAuth();
339
+ await clearPendingAuth(apiBaseUrl, opts.envPath);
318
340
  throw new Error("That sign-in code expired. Re-run this command for a fresh one.");
319
341
  }
320
342
  if (res.ok) {
321
343
  const body = await res.json().catch(() => null);
322
344
  if (body?.status === "approved" && body.token) {
323
- await clearPendingAuth();
324
- return body.token;
345
+ await clearPendingAuth(apiBaseUrl, opts.envPath);
346
+ return bindToken(body.token, apiBaseUrl);
325
347
  }
326
348
  if (body?.status === "denied") {
327
- await clearPendingAuth();
349
+ await clearPendingAuth(apiBaseUrl, opts.envPath);
328
350
  throw new Error("Sign-in was cancelled in the browser. Re-run this command to try again.");
329
351
  }
330
352
  if (body?.interval) intervalMs = Math.max(body.interval * 1e3, MIN_POLL_INTERVAL_MS);
331
353
  }
332
354
  if (Date.now() + intervalMs > deadline) {
333
- throw new AuthPendingError(pending.userCode, pending.verifyUrl);
355
+ throw new AuthPendingError(
356
+ pending.userCode,
357
+ pending.verifyUrl,
358
+ apiBaseUrl,
359
+ opts.envPath || process.env[ENV_FILE_ENV] ? getGenexEnvPath(opts.envPath) : void 0
360
+ );
334
361
  }
335
362
  if (Date.now() >= nextReassureAt) {
336
363
  log.dim(" still waiting for you to approve\u2026");
@@ -340,22 +367,24 @@ async function pollForToken(apiBaseUrl, pending, opts) {
340
367
  }
341
368
  }
342
369
  function printAuthHandoff(log, err) {
370
+ const quote2 = (value) => /^[A-Za-z0-9_./:@-]+$/.test(value) ? value : process.platform === "win32" ? "'" + value.replace(/'/g, "''") + "'" : "'" + value.replace(/'/g, "'\\''") + "'";
371
+ const command = "npx genex auth" + (err.apiUrl ? ` --api-url ${quote2(err.apiUrl)}` : "") + (err.envPath ? ` --env ${quote2(path2.resolve(err.envPath))}` : "");
343
372
  log.plain("");
344
373
  log.warn("Not approved yet \u2014 nothing is broken, the sign-in is still waiting for you.");
345
374
  log.plain(
346
375
  [
347
376
  ` ${c.cyan("\u2192")} Approve at ${c.cyan(err.verifyUrl)} (code ${c.bold(formatUserCode(err.userCode))})`,
348
- ` ${c.cyan("\u2192")} Then run ${c.cyan("npx genex auth")} \u2014 it picks up exactly where this left off.`,
377
+ ` ${c.cyan("\u2192")} Then run ${c.cyan(command)} \u2014 it picks up exactly where this left off.`,
349
378
  ""
350
379
  ].join("\n")
351
380
  );
352
381
  }
353
382
  async function resumeAuthorization(apiBaseUrl, options) {
354
- const pending = await readPendingAuth(apiBaseUrl);
383
+ const pending = await readPendingAuth(apiBaseUrl, options.envPath);
355
384
  if (!pending) return null;
356
385
  const { log, inlineWaitMs = DEFAULT_INLINE_WAIT_MS } = options;
357
386
  printCode(log, pending.userCode, pending.verifyUrl);
358
- return await pollForToken(apiBaseUrl, pending, { log, inlineWaitMs });
387
+ return await pollForToken(apiBaseUrl, pending, { log, inlineWaitMs, envPath: options.envPath });
359
388
  }
360
389
  var SUCCESS_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>Genex</title>
361
390
  <style>body{font-family:system-ui,sans-serif;background:#0b0b0f;color:#eaeaea;display:grid;place-items:center;height:100vh;margin:0}
@@ -626,7 +655,7 @@ async function runAuth(opts) {
626
655
  const authUrl = getAuthUrl(opts.authUrl);
627
656
  const inlineWaitMs = opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0;
628
657
  if (!opts.force) {
629
- const existing = await readUserToken(opts.envPath);
658
+ const existing = await readUserToken(opts.envPath, apiUrl);
630
659
  if (existing) {
631
660
  const email2 = await fetchSignedInEmail(apiUrl, existing);
632
661
  if (email2) {
@@ -639,8 +668,8 @@ async function runAuth(opts) {
639
668
  }
640
669
  let token;
641
670
  try {
642
- token = await resumeAuthorization(apiUrl, { log, inlineWaitMs }) ?? "";
643
- if (!token) token = await authorize(apiUrl, authUrl, { log, inlineWaitMs });
671
+ token = await resumeAuthorization(apiUrl, { log, inlineWaitMs, envPath: opts.envPath }) ?? "";
672
+ if (!token) token = await authorize(apiUrl, authUrl, { log, inlineWaitMs, envPath: opts.envPath });
644
673
  } catch (err) {
645
674
  if (err instanceof AuthPendingError) {
646
675
  printAuthHandoff(log, err);
@@ -650,7 +679,7 @@ async function runAuth(opts) {
650
679
  process.exitCode = 1;
651
680
  return;
652
681
  }
653
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
682
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, apiUrl);
654
683
  log.success(`Connected. Saved your token to ${c.cyan(tokenPath)}.`);
655
684
  const email = await fetchSignedInEmail(apiUrl, token);
656
685
  if (email) log.plain(` signed in as ${c.cyan(email)}`);
@@ -660,13 +689,13 @@ async function runAuth(opts) {
660
689
 
661
690
  // src/commands/remix.ts
662
691
  import fs18 from "fs/promises";
663
- import path17 from "path";
692
+ import path18 from "path";
664
693
  import crypto4 from "crypto";
665
694
  import { spawn as spawn4 } from "child_process";
666
695
 
667
696
  // src/lib/game-config.ts
668
697
  import fs2 from "fs/promises";
669
- import path2 from "path";
698
+ import path3 from "path";
670
699
  var DEFAULT_DASHBOARD_ORIGIN = new URL(DEFAULT_AUTH_URL).origin;
671
700
  function renderGenexConfig(slug) {
672
701
  return `// src/genex.config.ts \u2014 written by \`genex init\`. DO NOT hardcode URLs here.
@@ -759,20 +788,20 @@ async function ensureEnvVar(file, key, value, log) {
759
788
  return true;
760
789
  }
761
790
  async function writeGameConfigFiles(meta, log, cwd = process.cwd()) {
762
- await fs2.mkdir(path2.join(cwd, "src"), { recursive: true });
763
- await writeIfAbsent(path2.join(cwd, "src", "genex.config.ts"), renderGenexConfig(meta.slug), log);
764
- await writeIfAbsent(path2.join(cwd, ".env"), renderSlugEnv(meta.slug), log);
791
+ await fs2.mkdir(path3.join(cwd, "src"), { recursive: true });
792
+ await writeIfAbsent(path3.join(cwd, "src", "genex.config.ts"), renderGenexConfig(meta.slug), log);
793
+ await writeIfAbsent(path3.join(cwd, ".env"), renderSlugEnv(meta.slug), log);
765
794
  const overrides = renderDevOverrides(meta);
766
795
  if (overrides) {
767
- await writeIfAbsent(path2.join(cwd, ".env.development.local"), overrides, log);
796
+ await writeIfAbsent(path3.join(cwd, ".env.development.local"), overrides, log);
768
797
  }
769
798
  }
770
799
 
771
800
  // src/lib/ssh.ts
772
801
  import fs3 from "fs/promises";
773
- import path3 from "path";
802
+ import path4 from "path";
774
803
  async function writeGitignore(dir, log) {
775
- const file = path3.join(dir, ".gitignore");
804
+ const file = path4.join(dir, ".gitignore");
776
805
  let content = "";
777
806
  try {
778
807
  content = await fs3.readFile(file, "utf8");
@@ -815,7 +844,7 @@ var LFS_PATTERNS = [
815
844
  "*.webm"
816
845
  ];
817
846
  async function writeGitattributes(dir, log) {
818
- const file = path3.join(dir, ".gitattributes");
847
+ const file = path4.join(dir, ".gitattributes");
819
848
  let content = "";
820
849
  try {
821
850
  content = await fs3.readFile(file, "utf8");
@@ -834,12 +863,12 @@ async function writeGitattributes(dir, log) {
834
863
 
835
864
  // src/lib/remix-profile.ts
836
865
  import fs8 from "fs/promises";
837
- import path8 from "path";
866
+ import path9 from "path";
838
867
 
839
868
  // src/lib/agents-contract.ts
840
869
  import fs4 from "fs/promises";
841
870
  import os3 from "os";
842
- import path4 from "path";
871
+ import path5 from "path";
843
872
  var CONTRACT_BEGIN = "<!-- genex:contract:begin (managed by genex \u2014 edits inside this block are overwritten on sync) -->";
844
873
  var CONTRACT_END = "<!-- genex:contract:end -->";
845
874
  var GENEX_CONTRACT_BLOCK = `${CONTRACT_BEGIN}
@@ -933,7 +962,7 @@ ${block}
933
962
  async function writeAgentsContract(projectDir, contractBlock = GENEX_CONTRACT_BLOCK) {
934
963
  let changed = false;
935
964
  try {
936
- const agentsPath = path4.join(projectDir, "AGENTS.md");
965
+ const agentsPath = path5.join(projectDir, "AGENTS.md");
937
966
  let existing = null;
938
967
  try {
939
968
  existing = await fs4.readFile(agentsPath, "utf8");
@@ -945,7 +974,7 @@ async function writeAgentsContract(projectDir, contractBlock = GENEX_CONTRACT_BL
945
974
  await fs4.writeFile(agentsPath, next, "utf8");
946
975
  changed = true;
947
976
  }
948
- const claudePath = path4.join(projectDir, "CLAUDE.md");
977
+ const claudePath = path5.join(projectDir, "CLAUDE.md");
949
978
  let claude = null;
950
979
  try {
951
980
  claude = await fs4.readFile(claudePath, "utf8");
@@ -972,21 +1001,21 @@ async function writeToolsContract(projectDir, opts = {}) {
972
1001
  }
973
1002
  async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
974
1003
  const findings = [];
975
- const stop = path4.resolve(stopDir);
976
- let dir = path4.dirname(path4.resolve(projectDir));
1004
+ const stop = path5.resolve(stopDir);
1005
+ let dir = path5.dirname(path5.resolve(projectDir));
977
1006
  for (let depth = 0; depth < 20; depth++) {
978
1007
  try {
979
1008
  let isWorkspace = false;
980
1009
  for (const marker of ["project.json", "workspace.json"]) {
981
1010
  try {
982
- await fs4.access(path4.join(dir, ".genex", marker));
1011
+ await fs4.access(path5.join(dir, ".genex", marker));
983
1012
  isWorkspace = true;
984
1013
  break;
985
1014
  } catch {
986
1015
  }
987
1016
  }
988
1017
  if (!isWorkspace) {
989
- const agentsPath = path4.join(dir, "AGENTS.md");
1018
+ const agentsPath = path5.join(dir, "AGENTS.md");
990
1019
  let content = null;
991
1020
  try {
992
1021
  content = await fs4.readFile(agentsPath, "utf8");
@@ -1000,7 +1029,7 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1000
1029
  if (remainder.trim() === "") {
1001
1030
  await fs4.unlink(agentsPath);
1002
1031
  try {
1003
- const claudePath = path4.join(dir, "CLAUDE.md");
1032
+ const claudePath = path5.join(dir, "CLAUDE.md");
1004
1033
  if ((await fs4.readFile(claudePath, "utf8")).trim() === CLAUDE_IMPORT_LINE) {
1005
1034
  await fs4.unlink(claudePath);
1006
1035
  }
@@ -1015,7 +1044,7 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1015
1044
  } catch {
1016
1045
  }
1017
1046
  if (dir === stop) break;
1018
- const parent = path4.dirname(dir);
1047
+ const parent = path5.dirname(dir);
1019
1048
  if (parent === dir) break;
1020
1049
  dir = parent;
1021
1050
  }
@@ -1024,7 +1053,7 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1024
1053
  async function healAncestorContractsAndReport(log, projectDir) {
1025
1054
  try {
1026
1055
  for (const f of await healAncestorContracts(projectDir)) {
1027
- const file = path4.join(f.dir, "AGENTS.md");
1056
+ const file = path5.join(f.dir, "AGENTS.md");
1028
1057
  if (f.removed) {
1029
1058
  log.plain(
1030
1059
  `\u{1F9F9} Removed a stray genex build contract from ${file} \u2014 it belongs in each game's folder, and a stale copy above the project misleads agents.`
@@ -1041,9 +1070,9 @@ async function healAncestorContractsAndReport(log, projectDir) {
1041
1070
 
1042
1071
  // src/lib/copy-templates.ts
1043
1072
  import fs5 from "fs/promises";
1044
- import path5 from "path";
1073
+ import path6 from "path";
1045
1074
  function isGenexManaged(rel) {
1046
- return rel.split(path5.sep).some((seg) => seg.startsWith("genex"));
1075
+ return rel.split(path6.sep).some((seg) => seg.startsWith("genex"));
1047
1076
  }
1048
1077
  async function copyTemplates(srcDir, destDir, opts = {}) {
1049
1078
  const result = { copied: [], updated: [], skipped: [] };
@@ -1053,9 +1082,9 @@ async function copyTemplates(srcDir, destDir, opts = {}) {
1053
1082
  async function walk(rootSrc, src, dest, opts, result) {
1054
1083
  const entries = await fs5.readdir(src, { withFileTypes: true });
1055
1084
  for (const entry of entries) {
1056
- const srcPath = path5.join(src, entry.name);
1057
- const destPath = path5.join(dest, entry.name);
1058
- const rel = path5.relative(rootSrc, srcPath);
1085
+ const srcPath = path6.join(src, entry.name);
1086
+ const destPath = path6.join(dest, entry.name);
1087
+ const rel = path6.relative(rootSrc, srcPath);
1059
1088
  if (opts.exclude?.includes(rel)) continue;
1060
1089
  if (opts.filter && !opts.filter(rel)) continue;
1061
1090
  if (entry.isDirectory()) {
@@ -1072,7 +1101,7 @@ async function walk(rootSrc, src, dest, opts, result) {
1072
1101
  result.skipped.push(rel);
1073
1102
  continue;
1074
1103
  }
1075
- await fs5.mkdir(path5.dirname(destPath), { recursive: true });
1104
+ await fs5.mkdir(path6.dirname(destPath), { recursive: true });
1076
1105
  await fs5.copyFile(srcPath, destPath);
1077
1106
  result.copied.push(rel);
1078
1107
  if (present) result.updated.push(rel);
@@ -1090,11 +1119,11 @@ async function exists(p) {
1090
1119
  // src/lib/updates.ts
1091
1120
  import fs7 from "fs/promises";
1092
1121
  import os4 from "os";
1093
- import path7 from "path";
1122
+ import path8 from "path";
1094
1123
 
1095
1124
  // src/lib/workspace.ts
1096
1125
  import fs6 from "fs/promises";
1097
- import path6 from "path";
1126
+ import path7 from "path";
1098
1127
  async function resolveWorkspace(cwd = process.cwd()) {
1099
1128
  const project = await readProject(cwd);
1100
1129
  const slug = project && typeof project.slug === "string" && project.slug.trim() ? project.slug.trim() : null;
@@ -1148,7 +1177,7 @@ function reportPlatformRefused(log, command) {
1148
1177
  }
1149
1178
  async function hasRemixProfile(cwd = process.cwd()) {
1150
1179
  try {
1151
- const pkg = JSON.parse(await fs6.readFile(path6.join(cwd, "package.json"), "utf8"));
1180
+ const pkg = JSON.parse(await fs6.readFile(path7.join(cwd, "package.json"), "utf8"));
1152
1181
  return pkg.genex?.agentProfile === "remix";
1153
1182
  } catch {
1154
1183
  return false;
@@ -1173,7 +1202,7 @@ function isNewerVersion(a, b) {
1173
1202
  var SKILLS_VERSION_MARKER = "genex-skills-version.json";
1174
1203
  async function readSkillsMarker(skillsDir) {
1175
1204
  try {
1176
- const raw = await fs7.readFile(path7.join(skillsDir, SKILLS_VERSION_MARKER), "utf8");
1205
+ const raw = await fs7.readFile(path8.join(skillsDir, SKILLS_VERSION_MARKER), "utf8");
1177
1206
  const parsed = JSON.parse(raw);
1178
1207
  return typeof parsed.version === "string" ? parsed.version : null;
1179
1208
  } catch {
@@ -1183,7 +1212,7 @@ async function readSkillsMarker(skillsDir) {
1183
1212
  async function writeSkillsMarker(skillsDir, version = getCliVersion()) {
1184
1213
  await fs7.mkdir(skillsDir, { recursive: true });
1185
1214
  await fs7.writeFile(
1186
- path7.join(skillsDir, SKILLS_VERSION_MARKER),
1215
+ path8.join(skillsDir, SKILLS_VERSION_MARKER),
1187
1216
  JSON.stringify({ version, syncedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
1188
1217
  );
1189
1218
  }
@@ -1259,7 +1288,7 @@ var REMOVED_SKILLS = [
1259
1288
  async function pruneRemovedSkills(skillsDir, log) {
1260
1289
  const removed = [];
1261
1290
  for (const name of REMOVED_SKILLS) {
1262
- const target = path7.join(skillsDir, name);
1291
+ const target = path8.join(skillsDir, name);
1263
1292
  try {
1264
1293
  await fs7.access(target);
1265
1294
  } catch {
@@ -1279,11 +1308,11 @@ async function pruneRemovedSkills(skillsDir, log) {
1279
1308
  return removed.length > 0;
1280
1309
  }
1281
1310
  async function syncSkillsForTarget(target, templatesDir = getTemplatesDir(), version = getCliVersion(), log, family = "game", hosted = false) {
1282
- const skillsDir = path7.join(target.baseDir, "skills");
1311
+ const skillsDir = path8.join(target.baseDir, "skills");
1283
1312
  if (!await hasGenexSkills(skillsDir)) return false;
1284
1313
  if (family === "remix") await pruneRemixWorkflow(target.baseDir);
1285
1314
  if (await readSkillsMarker(skillsDir) === version) return false;
1286
- const src = target.full ? templatesDir : path7.join(templatesDir, "skills");
1315
+ const src = target.full ? templatesDir : path8.join(templatesDir, "skills");
1287
1316
  const dest = target.full ? target.baseDir : skillsDir;
1288
1317
  const familyFilter = skillFamilyFilter(family, { hosted });
1289
1318
  const filter = target.full ? familyFilter : (rel) => familyFilter(`skills/${rel}`);
@@ -1299,13 +1328,13 @@ async function cleanupLegacyGlobalSkills(log) {
1299
1328
  try {
1300
1329
  const home = os4.homedir();
1301
1330
  const [realCwd, realHome] = await Promise.all([
1302
- fs7.realpath(process.cwd()).catch(() => path7.resolve(process.cwd())),
1303
- fs7.realpath(home).catch(() => path7.resolve(home))
1331
+ fs7.realpath(process.cwd()).catch(() => path8.resolve(process.cwd())),
1332
+ fs7.realpath(home).catch(() => path8.resolve(home))
1304
1333
  ]);
1305
1334
  if (realCwd === realHome) return false;
1306
1335
  let removed = false;
1307
1336
  for (const dirName of [".claude", ".codex", ".cursor"]) {
1308
- const skillsDir = path7.join(home, dirName, "skills");
1337
+ const skillsDir = path8.join(home, dirName, "skills");
1309
1338
  let entries = [];
1310
1339
  try {
1311
1340
  entries = await fs7.readdir(skillsDir);
@@ -1315,18 +1344,18 @@ async function cleanupLegacyGlobalSkills(log) {
1315
1344
  for (const name of entries) {
1316
1345
  if (!name.startsWith("genex-")) continue;
1317
1346
  try {
1318
- await fs7.rm(path7.join(skillsDir, name), { recursive: true });
1347
+ await fs7.rm(path8.join(skillsDir, name), { recursive: true });
1319
1348
  removed = true;
1320
1349
  } catch {
1321
1350
  }
1322
1351
  }
1323
1352
  }
1324
1353
  for (const rel of [
1325
- path7.join("agents", "genex-helper.md"),
1326
- path7.join("commands", "genex-status.md")
1354
+ path8.join("agents", "genex-helper.md"),
1355
+ path8.join("commands", "genex-status.md")
1327
1356
  ]) {
1328
1357
  try {
1329
- await fs7.rm(path7.join(home, ".claude", rel));
1358
+ await fs7.rm(path8.join(home, ".claude", rel));
1330
1359
  removed = true;
1331
1360
  } catch {
1332
1361
  }
@@ -1387,7 +1416,7 @@ var PUBLISHED_PACKAGES = [
1387
1416
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1388
1417
  var REGISTRY_TIMEOUT_MS = 1500;
1389
1418
  function getUpdateCachePath() {
1390
- return path7.join(getGenexDir(), "update-check.json");
1419
+ return path8.join(getGenexDir(), "update-check.json");
1391
1420
  }
1392
1421
  function isCacheFresh(cache, nowMs) {
1393
1422
  const at = Date.parse(cache.checkedAt);
@@ -1449,7 +1478,7 @@ function startUpdateCheck() {
1449
1478
  }
1450
1479
  async function installedPackageVersion(cwd, name) {
1451
1480
  try {
1452
- const raw = await fs7.readFile(path7.join(cwd, "node_modules", name, "package.json"), "utf8");
1481
+ const raw = await fs7.readFile(path8.join(cwd, "node_modules", name, "package.json"), "utf8");
1453
1482
  const pkg = JSON.parse(raw);
1454
1483
  return typeof pkg.version === "string" ? pkg.version : null;
1455
1484
  } catch {
@@ -1496,23 +1525,23 @@ async function recordNotified(cache, announced, cachePath) {
1496
1525
  notified: { ...cache.notified ?? {}, ...announced },
1497
1526
  notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1498
1527
  };
1499
- await fs7.mkdir(path7.dirname(cachePath), { recursive: true });
1528
+ await fs7.mkdir(path8.dirname(cachePath), { recursive: true });
1500
1529
  await fs7.writeFile(cachePath, JSON.stringify(next, null, 2) + "\n");
1501
1530
  } catch {
1502
1531
  }
1503
1532
  }
1504
1533
  async function pruneRemixWorkflow(baseDir) {
1505
1534
  for (const name of REMIX_EXCLUDED_SKILLS) {
1506
- await fs7.rm(path7.join(baseDir, "skills", name), { recursive: true, force: true });
1535
+ await fs7.rm(path8.join(baseDir, "skills", name), { recursive: true, force: true });
1507
1536
  }
1508
1537
  for (const rel of ["agents/genex-helper.md", "commands/genex-status.md"]) {
1509
- await fs7.rm(path7.join(baseDir, rel), { force: true });
1538
+ await fs7.rm(path8.join(baseDir, rel), { force: true });
1510
1539
  }
1511
1540
  }
1512
1541
 
1513
1542
  // src/lib/remix-profile.ts
1514
1543
  async function persistRemixProfile(cwd, apiUrl) {
1515
- const file = path8.join(cwd, "package.json");
1544
+ const file = path9.join(cwd, "package.json");
1516
1545
  let pkg = {};
1517
1546
  try {
1518
1547
  pkg = JSON.parse(await fs8.readFile(file, "utf8"));
@@ -1526,25 +1555,25 @@ async function persistRemixProfile(cwd, apiUrl) {
1526
1555
  }
1527
1556
  async function installRemixProfile(cwd, apiUrl, log) {
1528
1557
  await persistRemixProfile(cwd, apiUrl);
1529
- if (path8.resolve(cwd) !== path8.resolve(process.cwd())) throw new Error("Remix profile must be installed from its workspace.");
1558
+ if (path9.resolve(cwd) !== path9.resolve(process.cwd())) throw new Error("Remix profile must be installed from its workspace.");
1530
1559
  const family = skillFamilyFilter("remix");
1531
1560
  for (const target of resolveAgentTargets()) {
1532
- const src = target.full ? getTemplatesDir() : path8.join(getTemplatesDir(), "skills");
1533
- const dest = target.full ? target.baseDir : path8.join(target.baseDir, "skills");
1561
+ const src = target.full ? getTemplatesDir() : path9.join(getTemplatesDir(), "skills");
1562
+ const dest = target.full ? target.baseDir : path9.join(target.baseDir, "skills");
1534
1563
  await pruneRemixWorkflow(target.baseDir);
1535
1564
  await copyTemplates(src, dest, {
1536
1565
  exclude: ["controllers", "motion", "asset-viewer", "blender-service"],
1537
1566
  filter: target.full ? family : (rel) => family(`skills/${rel}`)
1538
1567
  });
1539
- await pruneRemovedSkills(path8.join(target.baseDir, "skills"), log);
1540
- await writeSkillsMarker(path8.join(target.baseDir, "skills"));
1568
+ await pruneRemovedSkills(path9.join(target.baseDir, "skills"), log);
1569
+ await writeSkillsMarker(path9.join(target.baseDir, "skills"));
1541
1570
  }
1542
1571
  await writeRemixContract(cwd);
1543
1572
  }
1544
1573
 
1545
1574
  // src/lib/local-install.ts
1546
1575
  import fs9 from "fs/promises";
1547
- import path9 from "path";
1576
+ import path10 from "path";
1548
1577
  import { spawn as spawn2 } from "child_process";
1549
1578
  var CLI_PACKAGE = "@genex-ai/cli-demo";
1550
1579
  var FULL_NAME_FALLBACK = `npx ${CLI_PACKAGE}@${CLI_CHANNEL}`;
@@ -1564,10 +1593,10 @@ async function exists2(p) {
1564
1593
  }
1565
1594
  }
1566
1595
  async function detectPackageManager(cwd, opts = {}) {
1567
- let dir = path9.resolve(cwd);
1596
+ let dir = path10.resolve(cwd);
1568
1597
  for (; ; ) {
1569
1598
  try {
1570
- const raw = await fs9.readFile(path9.join(dir, "package.json"), "utf8");
1599
+ const raw = await fs9.readFile(path10.join(dir, "package.json"), "utf8");
1571
1600
  const pm = JSON.parse(raw).packageManager;
1572
1601
  if (typeof pm === "string") {
1573
1602
  const name = pm.split("@")[0];
@@ -1576,18 +1605,18 @@ async function detectPackageManager(cwd, opts = {}) {
1576
1605
  } catch {
1577
1606
  }
1578
1607
  for (const [file, pm] of LOCKFILES) {
1579
- if (await exists2(path9.join(dir, file))) return pm;
1608
+ if (await exists2(path10.join(dir, file))) return pm;
1580
1609
  }
1581
- const parent = path9.dirname(dir);
1610
+ const parent = path10.dirname(dir);
1582
1611
  if (opts.walkParents === false || parent === dir) return "npm";
1583
1612
  dir = parent;
1584
1613
  }
1585
1614
  }
1586
1615
  async function findLocalCli(cwd) {
1587
- let dir = path9.resolve(cwd);
1616
+ let dir = path10.resolve(cwd);
1588
1617
  for (; ; ) {
1589
- if (await exists2(path9.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
1590
- const parent = path9.dirname(dir);
1618
+ if (await exists2(path10.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
1619
+ const parent = path10.dirname(dir);
1591
1620
  if (parent === dir) return null;
1592
1621
  dir = parent;
1593
1622
  }
@@ -1605,7 +1634,7 @@ function installArgs(pm, spec) {
1605
1634
  }
1606
1635
  }
1607
1636
  function manifestName(cwd) {
1608
- const slug = path9.basename(path9.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
1637
+ const slug = path10.basename(path10.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
1609
1638
  return slug || "genex-tools-workspace";
1610
1639
  }
1611
1640
  function isSourceRun(moduleUrl = import.meta.url) {
@@ -1663,10 +1692,10 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
1663
1692
  return;
1664
1693
  }
1665
1694
  const pm = await detectPackageManager(cwd);
1666
- const hadManifest = await exists2(path9.join(cwd, "package.json"));
1695
+ const hadManifest = await exists2(path10.join(cwd, "package.json"));
1667
1696
  if (!hadManifest) {
1668
1697
  await fs9.writeFile(
1669
- path9.join(cwd, "package.json"),
1698
+ path10.join(cwd, "package.json"),
1670
1699
  JSON.stringify({ name: manifestName(cwd), private: true }, null, 2) + "\n"
1671
1700
  );
1672
1701
  await ensureIgnored(cwd, "node_modules/");
@@ -1686,7 +1715,7 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
1686
1715
  }
1687
1716
  }
1688
1717
  async function ensureIgnored(dir, entry) {
1689
- const file = path9.join(dir, ".gitignore");
1718
+ const file = path10.join(dir, ".gitignore");
1690
1719
  let content = "";
1691
1720
  try {
1692
1721
  content = await fs9.readFile(file, "utf8");
@@ -1700,7 +1729,7 @@ async function ensureIgnored(dir, entry) {
1700
1729
 
1701
1730
  // src/lib/remix-archive.ts
1702
1731
  import fs10 from "fs/promises";
1703
- import path10 from "path";
1732
+ import path11 from "path";
1704
1733
  var MAX_SOURCE_ARCHIVE_BYTES = 2 * 1024 * 1024 * 1024;
1705
1734
  var MAX_ENTRIES = 12e3;
1706
1735
  var CHUNK_BYTES = 64 * 1024;
@@ -1850,8 +1879,8 @@ async function extractSourceArchive(file, dest, rootFolder) {
1850
1879
  if (crc !== e.crc) badArchive("Source archive checksum mismatch. Retry the same remix command.");
1851
1880
  if (e.symlink) {
1852
1881
  const target = linkTargets.get(e.name) ?? "";
1853
- if (!target || target.includes("\\") || target.includes("\0") || path10.posix.isAbsolute(target) || target.includes(":")) badArchive("Source archive link points outside the source.");
1854
- const resolved = path10.posix.normalize(path10.posix.join(path10.posix.dirname(e.name), target));
1882
+ if (!target || target.includes("\\") || target.includes("\0") || path11.posix.isAbsolute(target) || target.includes(":")) badArchive("Source archive link points outside the source.");
1883
+ const resolved = path11.posix.normalize(path11.posix.join(path11.posix.dirname(e.name), target));
1855
1884
  if (resolved === ".." || resolved.startsWith("../") || excludedSourcePath(resolved) || !entries.some((f) => f.name === resolved && !f.symlink || f.name.startsWith(resolved + "/"))) badArchive("Source archive link has an unsafe or missing target.");
1856
1885
  }
1857
1886
  }
@@ -1861,12 +1890,12 @@ async function extractSourceArchive(file, dest, rootFolder) {
1861
1890
  excluded.push(e.name);
1862
1891
  continue;
1863
1892
  }
1864
- const target = path10.join(dest, e.name);
1893
+ const target = path11.join(dest, e.name);
1865
1894
  if (e.dir) {
1866
1895
  await fs10.mkdir(target, { recursive: true });
1867
1896
  continue;
1868
1897
  }
1869
- await fs10.mkdir(path10.dirname(target), { recursive: true });
1898
+ await fs10.mkdir(path11.dirname(target), { recursive: true });
1870
1899
  const output = await fs10.open(target, "wx", e.mode & 73 ? 493 : 420);
1871
1900
  try {
1872
1901
  for (let offset = 0; offset < e.size; offset += CHUNK_BYTES) await output.writeFile(await readAt(handle, e.dataOffset + offset, Math.min(CHUNK_BYTES, e.size - offset)));
@@ -1880,8 +1909,8 @@ async function extractSourceArchive(file, dest, rootFolder) {
1880
1909
  excluded.push(e.name);
1881
1910
  continue;
1882
1911
  }
1883
- await fs10.mkdir(path10.dirname(path10.join(dest, e.name)), { recursive: true });
1884
- await fs10.symlink(linkTargets.get(e.name), path10.join(dest, e.name));
1912
+ await fs10.mkdir(path11.dirname(path11.join(dest, e.name)), { recursive: true });
1913
+ await fs10.symlink(linkTargets.get(e.name), path11.join(dest, e.name));
1885
1914
  files.push(e.name);
1886
1915
  }
1887
1916
  return { files, excluded };
@@ -1895,8 +1924,8 @@ import "child_process";
1895
1924
 
1896
1925
  // src/lib/generation-ledger.ts
1897
1926
  import fs11 from "fs/promises";
1898
- import path11 from "path";
1899
- var ledgerPath = (cwd) => path11.join(cwd, ".genex", "generations.ndjson");
1927
+ import path12 from "path";
1928
+ var ledgerPath = (cwd) => path12.join(cwd, ".genex", "generations.ndjson");
1900
1929
  async function appendAdmissionRow(cwd, event) {
1901
1930
  const file = await fs11.open(ledgerPath(cwd), "a");
1902
1931
  try {
@@ -1923,7 +1952,7 @@ async function readAdmissionRows(cwd) {
1923
1952
  }
1924
1953
  async function append(cwd, event) {
1925
1954
  try {
1926
- await fs11.access(path11.join(cwd, ".genex"));
1955
+ await fs11.access(path12.join(cwd, ".genex"));
1927
1956
  await fs11.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
1928
1957
  `, "utf8");
1929
1958
  } catch {
@@ -2013,11 +2042,11 @@ async function countOutcomes(kind, cwd = process.cwd()) {
2013
2042
 
2014
2043
  // src/lib/detect-features.ts
2015
2044
  import fs13 from "fs/promises";
2016
- import path13 from "path";
2045
+ import path14 from "path";
2017
2046
 
2018
2047
  // src/lib/download-assets.ts
2019
2048
  import fs12 from "fs/promises";
2020
- import path12 from "path";
2049
+ import path13 from "path";
2021
2050
  var RUNG_ROLE = /@\d+$/;
2022
2051
  function isPrimaryRole(role) {
2023
2052
  return !RUNG_ROLE.test(role);
@@ -2050,7 +2079,7 @@ async function downloadAssets(files, opts) {
2050
2079
  }
2051
2080
  const multi = wanted.length > 1;
2052
2081
  for (const file of wanted) {
2053
- const dest = path12.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
2082
+ const dest = path13.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
2054
2083
  try {
2055
2084
  const res = await fetch(file.url);
2056
2085
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -2080,7 +2109,7 @@ async function undeliveredFiles(files, opts) {
2080
2109
  const multi = wanted.length > 1;
2081
2110
  const missing = [];
2082
2111
  for (const file of wanted) {
2083
- const dest = path12.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
2112
+ const dest = path13.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
2084
2113
  try {
2085
2114
  await fs12.access(dest);
2086
2115
  } catch {
@@ -2093,7 +2122,7 @@ async function undeliveredFiles(files, opts) {
2093
2122
  // src/lib/detect-features.ts
2094
2123
  async function detectEmbedSdkVersion(cwd = process.cwd()) {
2095
2124
  try {
2096
- const raw = await fs13.readFile(path13.join(cwd, "package.json"), "utf8");
2125
+ const raw = await fs13.readFile(path14.join(cwd, "package.json"), "utf8");
2097
2126
  const pkg = JSON.parse(raw);
2098
2127
  const version = pkg.dependencies?.["@genex-ai/embed-sdk"] ?? pkg.devDependencies?.["@genex-ai/embed-sdk"];
2099
2128
  return typeof version === "string" && version ? version : null;
@@ -2103,7 +2132,7 @@ async function detectEmbedSdkVersion(cwd = process.cwd()) {
2103
2132
  }
2104
2133
  async function detectMultiplayer(cwd = process.cwd()) {
2105
2134
  try {
2106
- const raw = await fs13.readFile(path13.join(cwd, "package.json"), "utf8");
2135
+ const raw = await fs13.readFile(path14.join(cwd, "package.json"), "utf8");
2107
2136
  const pkg = JSON.parse(raw);
2108
2137
  return Boolean(
2109
2138
  pkg.dependencies?.["@genex-ai/multiplayer"] ?? pkg.devDependencies?.["@genex-ai/multiplayer"]
@@ -2115,7 +2144,7 @@ async function detectMultiplayer(cwd = process.cwd()) {
2115
2144
  async function detectMatchmaking(log, cwd = process.cwd()) {
2116
2145
  let pkg;
2117
2146
  try {
2118
- pkg = JSON.parse(await fs13.readFile(path13.join(cwd, "package.json"), "utf8"));
2147
+ pkg = JSON.parse(await fs13.readFile(path14.join(cwd, "package.json"), "utf8"));
2119
2148
  } catch (err) {
2120
2149
  log.dim(` (skipping matchmaking \u2014 couldn't read package.json: ${String(err)})`);
2121
2150
  return null;
@@ -2133,12 +2162,12 @@ async function detectMatchmaking(log, cwd = process.cwd()) {
2133
2162
  var TOUCH_KIT_MARKERS = /controllers\/touch\/|controllers\/character\/touch-joystick|TouchJoystick|VirtualButton|DragZone|RotateOverlay/;
2134
2163
  async function detectMobileControls(cwd = process.cwd()) {
2135
2164
  try {
2136
- const raw = await fs13.readFile(path13.join(cwd, "package.json"), "utf8");
2165
+ const raw = await fs13.readFile(path14.join(cwd, "package.json"), "utf8");
2137
2166
  const pkg = JSON.parse(raw);
2138
2167
  if (pkg.genex?.mobileControls === true) return true;
2139
2168
  } catch {
2140
2169
  }
2141
- const srcDir = path13.join(cwd, "src");
2170
+ const srcDir = path14.join(cwd, "src");
2142
2171
  let entries;
2143
2172
  try {
2144
2173
  entries = await fs13.readdir(srcDir, { recursive: true });
@@ -2149,7 +2178,7 @@ async function detectMobileControls(cwd = process.cwd()) {
2149
2178
  if (rel.includes("node_modules")) continue;
2150
2179
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
2151
2180
  try {
2152
- const content = await fs13.readFile(path13.join(srcDir, rel), "utf8");
2181
+ const content = await fs13.readFile(path14.join(srcDir, rel), "utf8");
2153
2182
  if (TOUCH_KIT_MARKERS.test(content)) return true;
2154
2183
  } catch {
2155
2184
  }
@@ -2158,7 +2187,7 @@ async function detectMobileControls(cwd = process.cwd()) {
2158
2187
  }
2159
2188
  var GAME_STATE_CALLS = /savePlayerState|saveWorldState|submitScore/;
2160
2189
  async function detectGameStateUsage(cwd = process.cwd()) {
2161
- const srcDir = path13.join(cwd, "src");
2190
+ const srcDir = path14.join(cwd, "src");
2162
2191
  let entries;
2163
2192
  try {
2164
2193
  entries = await fs13.readdir(srcDir, { recursive: true });
@@ -2169,7 +2198,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
2169
2198
  if (rel.includes("node_modules")) continue;
2170
2199
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
2171
2200
  try {
2172
- const content = await fs13.readFile(path13.join(srcDir, rel), "utf8");
2201
+ const content = await fs13.readFile(path14.join(srcDir, rel), "utf8");
2173
2202
  if (GAME_STATE_CALLS.test(content)) return true;
2174
2203
  } catch {
2175
2204
  }
@@ -2219,7 +2248,7 @@ var LOOP_START = /\b(?:requestAnimationFrame|setAnimationLoop)\s*\((?!\s*null\b)
2219
2248
  var INIT_EMBED_CALL = /\binitEmbed\s*\(/;
2220
2249
  async function detectEmbedBoot(cwd = process.cwd()) {
2221
2250
  const found = { awaitsIdentity: [], callsInitEmbed: false, rendererAfterAwait: [], loopAfterAwait: [] };
2222
- const srcDir = path13.join(cwd, "src");
2251
+ const srcDir = path14.join(cwd, "src");
2223
2252
  let entries;
2224
2253
  try {
2225
2254
  entries = await fs13.readdir(srcDir, { recursive: true });
@@ -2228,11 +2257,11 @@ async function detectEmbedBoot(cwd = process.cwd()) {
2228
2257
  }
2229
2258
  for (const nativeRel of entries) {
2230
2259
  if (nativeRel.includes("node_modules")) continue;
2231
- if (nativeRel.split(path13.sep)[0] === "controllers") continue;
2260
+ if (nativeRel.split(path14.sep)[0] === "controllers") continue;
2232
2261
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
2233
- const raw = await fs13.readFile(path13.join(srcDir, nativeRel), "utf8").catch(() => "");
2262
+ const raw = await fs13.readFile(path14.join(srcDir, nativeRel), "utf8").catch(() => "");
2234
2263
  if (!raw) continue;
2235
- const rel = nativeRel.split(path13.sep).join("/");
2264
+ const rel = nativeRel.split(path14.sep).join("/");
2236
2265
  const content = blankComments(raw);
2237
2266
  if (INIT_EMBED_CALL.test(content)) found.callsInitEmbed = true;
2238
2267
  const awaits = [];
@@ -2327,7 +2356,7 @@ async function detectSurfaceScan(cwd = process.cwd()) {
2327
2356
  deferredAudioContext: [],
2328
2357
  usesThree: false
2329
2358
  };
2330
- const srcDir = path13.join(cwd, "src");
2359
+ const srcDir = path14.join(cwd, "src");
2331
2360
  let entries;
2332
2361
  try {
2333
2362
  entries = await fs13.readdir(srcDir, { recursive: true });
@@ -2337,9 +2366,9 @@ async function detectSurfaceScan(cwd = process.cwd()) {
2337
2366
  for (const nativeRel of entries) {
2338
2367
  if (nativeRel.includes("node_modules")) continue;
2339
2368
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
2340
- const raw = await fs13.readFile(path13.join(srcDir, nativeRel), "utf8").catch(() => "");
2369
+ const raw = await fs13.readFile(path14.join(srcDir, nativeRel), "utf8").catch(() => "");
2341
2370
  if (!raw) continue;
2342
- const rel = nativeRel.split(path13.sep).join("/");
2371
+ const rel = nativeRel.split(path14.sep).join("/");
2343
2372
  const content = blankComments(raw);
2344
2373
  const repeatRe = /\.repeat\.set\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/g;
2345
2374
  let m;
@@ -2432,7 +2461,7 @@ async function detectGenerationAudit(cwd = process.cwd()) {
2432
2461
  const read = async (file) => {
2433
2462
  try {
2434
2463
  const raw = await fs13.readFile(file, "utf8");
2435
- const ext = path13.extname(file).toLowerCase();
2464
+ const ext = path14.extname(file).toLowerCase();
2436
2465
  if (ext === ".txt") return;
2437
2466
  haystack += ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".css" ? blankComments(raw) : ext === ".html" ? raw.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, " ")) : raw;
2438
2467
  } catch {
@@ -2441,18 +2470,18 @@ async function detectGenerationAudit(cwd = process.cwd()) {
2441
2470
  try {
2442
2471
  for (const entry of await fs13.readdir(cwd, { withFileTypes: true })) {
2443
2472
  if (entry.isFile() && /\.(ts|tsx|js|jsx|css|html|json)$/.test(entry.name)) {
2444
- await read(path13.join(cwd, entry.name));
2473
+ await read(path14.join(cwd, entry.name));
2445
2474
  }
2446
2475
  }
2447
2476
  } catch {
2448
2477
  }
2449
2478
  for (const sub of ["src", "public"]) {
2450
2479
  try {
2451
- const entries = await fs13.readdir(path13.join(cwd, sub), { recursive: true });
2480
+ const entries = await fs13.readdir(path14.join(cwd, sub), { recursive: true });
2452
2481
  for (const rel of entries) {
2453
2482
  if (rel.includes("node_modules")) continue;
2454
2483
  if (!/\.(ts|tsx|js|jsx|css|html|json|txt)$/.test(rel)) continue;
2455
- await read(path13.join(cwd, sub, rel));
2484
+ await read(path14.join(cwd, sub, rel));
2456
2485
  }
2457
2486
  } catch {
2458
2487
  }
@@ -2623,7 +2652,7 @@ async function borrowEvidence(meta, cwd) {
2623
2652
  } catch {
2624
2653
  return true;
2625
2654
  }
2626
- const gitConfig = await fs13.readFile(path13.join(cwd, ".git", "config"), "utf8").catch(() => "");
2655
+ const gitConfig = await fs13.readFile(path14.join(cwd, ".git", "config"), "utf8").catch(() => "");
2627
2656
  for (const m of gitConfig.matchAll(/url\s*=\s*(\S+)/g)) {
2628
2657
  try {
2629
2658
  const u = new URL(m[1]);
@@ -2631,7 +2660,7 @@ async function borrowEvidence(meta, cwd) {
2631
2660
  } catch {
2632
2661
  }
2633
2662
  }
2634
- const readme = await fs13.readFile(path13.join(cwd, "README.md"), "utf8").catch(() => "");
2663
+ const readme = await fs13.readFile(path14.join(cwd, "README.md"), "utf8").catch(() => "");
2635
2664
  return readme.includes(host) || /\b(upstream|originally by|ported from|borrowed from)\b/i.test(readme);
2636
2665
  }
2637
2666
 
@@ -2816,11 +2845,11 @@ function auditGenerationPlan(input) {
2816
2845
  import crypto2 from "crypto";
2817
2846
  import fs17 from "fs/promises";
2818
2847
  import os5 from "os";
2819
- import path16 from "path";
2848
+ import path17 from "path";
2820
2849
 
2821
2850
  // src/lib/source-size.ts
2822
2851
  import fs14 from "fs/promises";
2823
- import path14 from "path";
2852
+ import path15 from "path";
2824
2853
  var FALLBACK_SOURCE_LIMITS = {
2825
2854
  maxTotalBytes: 2 * 1024 * 1024 * 1024,
2826
2855
  maxFiles: 5e3
@@ -2862,7 +2891,7 @@ async function measurePayload(cwd, staged) {
2862
2891
  for (const file of staged) {
2863
2892
  let size = 0;
2864
2893
  try {
2865
- size = (await fs14.stat(path14.join(cwd, file))).size;
2894
+ size = (await fs14.stat(path15.join(cwd, file))).size;
2866
2895
  } catch {
2867
2896
  size = 0;
2868
2897
  }
@@ -3135,7 +3164,7 @@ function tierFor(estVramMb) {
3135
3164
 
3136
3165
  // src/commands/ui.ts
3137
3166
  import fs16 from "fs/promises";
3138
- import path15 from "path";
3167
+ import path16 from "path";
3139
3168
  import { PNG as PNG2 } from "pngjs";
3140
3169
 
3141
3170
  // src/lib/png-tools.ts
@@ -3684,7 +3713,7 @@ async function uiExtract(opts, log) {
3684
3713
  rimPixels: speckle.sampled
3685
3714
  });
3686
3715
  }
3687
- const outPath = path15.join(outDir, `${name}.png`);
3716
+ const outPath = path16.join(outDir, `${name}.png`);
3688
3717
  await writePng(outPath, out);
3689
3718
  const sidecar = {
3690
3719
  name,
@@ -3712,7 +3741,7 @@ async function uiExtract(opts, log) {
3712
3741
  `${name}.png ${cropW}x${cropH} (ar ${sidecar.aspectRatio}) at sheet ${padX0},${padY0}`
3713
3742
  );
3714
3743
  }
3715
- const debugPath = path15.join(outDir, "extract-debug.json");
3744
+ const debugPath = path16.join(outDir, "extract-debug.json");
3716
3745
  await fs16.writeFile(
3717
3746
  debugPath,
3718
3747
  JSON.stringify(
@@ -4228,11 +4257,11 @@ async function uiMasks(opts, log) {
4228
4257
  });
4229
4258
  }
4230
4259
  const overlay = makeOverlay(clean2, converted.png);
4231
- const framePath = path15.join(outDir, `${pair.name}-frame.png`);
4232
- const maskPath = path15.join(outDir, `${pair.name}-mask.png`);
4233
- const annotatedPath = path15.join(outDir, `${pair.name}-annotated-source.png`);
4234
- const overlayPath = path15.join(outDir, `${pair.name}-overlay.png`);
4235
- const metaPath = path15.join(outDir, `${pair.name}.annotated-progress.json`);
4260
+ const framePath = path16.join(outDir, `${pair.name}-frame.png`);
4261
+ const maskPath = path16.join(outDir, `${pair.name}-mask.png`);
4262
+ const annotatedPath = path16.join(outDir, `${pair.name}-annotated-source.png`);
4263
+ const overlayPath = path16.join(outDir, `${pair.name}-overlay.png`);
4264
+ const metaPath = path16.join(outDir, `${pair.name}.annotated-progress.json`);
4236
4265
  await writePng(framePath, clean2);
4237
4266
  await writePng(maskPath, converted.png);
4238
4267
  await writePng(annotatedPath, annotated);
@@ -4295,7 +4324,7 @@ async function uiMasks(opts, log) {
4295
4324
  );
4296
4325
  }
4297
4326
  }
4298
- const indexPath = path15.join(outDir, "annotated-progress.json");
4327
+ const indexPath = path16.join(outDir, "annotated-progress.json");
4299
4328
  await fs16.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
4300
4329
  `);
4301
4330
  log.plain("");
@@ -4561,7 +4590,7 @@ async function uiPlate(opts, log) {
4561
4590
  fail("No interior found \u2014 the image is fully transparent (or erode ate everything). Check --in / lower --erode.");
4562
4591
  }
4563
4592
  await writePng(outPath, out);
4564
- const name = path15.basename(outPath);
4593
+ const name = path16.basename(outPath);
4565
4594
  log.plain(c.bold("genex ui plate"));
4566
4595
  log.success(`${outPath} ${W}x${H}, interior ${(count / (W * H) * 100).toFixed(1)}% (erode ${erode}px)`);
4567
4596
  log.dim(" Wire it as the plate's silhouette (same box as the frame <img>, plate UNDER the art):");
@@ -4732,7 +4761,7 @@ async function walkFiles(dir) {
4732
4761
  }
4733
4762
  for (const entry of entries) {
4734
4763
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
4735
- const p = path15.join(dir, entry.name);
4764
+ const p = path16.join(dir, entry.name);
4736
4765
  if (entry.isDirectory()) out.push(...await walkFiles(p));
4737
4766
  else out.push(p);
4738
4767
  }
@@ -4741,7 +4770,7 @@ async function walkFiles(dir) {
4741
4770
  async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4742
4771
  const viewportFindings = [];
4743
4772
  try {
4744
- const indexHtml = await fs16.readFile(path15.join(cwd, "index.html"), "utf8");
4773
+ const indexHtml = await fs16.readFile(path16.join(cwd, "index.html"), "utf8");
4745
4774
  if (!/<meta[^>]+name=["']viewport["']/i.test(indexHtml)) {
4746
4775
  viewportFindings.push({
4747
4776
  kind: "viewport-meta",
@@ -4755,7 +4784,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4755
4784
  }
4756
4785
  } catch {
4757
4786
  }
4758
- const absAssets = path15.resolve(cwd, assetDir);
4787
+ const absAssets = path16.resolve(cwd, assetDir);
4759
4788
  try {
4760
4789
  if (!(await fs16.stat(absAssets)).isDirectory()) {
4761
4790
  return viewportFindings.length > 0 ? viewportFindings : null;
@@ -4763,20 +4792,20 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4763
4792
  } catch {
4764
4793
  return viewportFindings.length > 0 ? viewportFindings : null;
4765
4794
  }
4766
- const srcFiles = (await walkFiles(path15.resolve(cwd, srcDir))).filter(
4767
- (p) => AUDIT_SRC_EXTS.has(path15.extname(p).toLowerCase())
4795
+ const srcFiles = (await walkFiles(path16.resolve(cwd, srcDir))).filter(
4796
+ (p) => AUDIT_SRC_EXTS.has(path16.extname(p).toLowerCase())
4768
4797
  );
4769
4798
  try {
4770
4799
  for (const name of await fs16.readdir(cwd)) {
4771
- const ext = path15.extname(name).toLowerCase();
4772
- if (ext === ".html" || ext === ".css") srcFiles.push(path15.join(cwd, name));
4800
+ const ext = path16.extname(name).toLowerCase();
4801
+ if (ext === ".html" || ext === ".css") srcFiles.push(path16.join(cwd, name));
4773
4802
  }
4774
4803
  } catch {
4775
4804
  }
4776
4805
  const sources = [];
4777
4806
  for (const p of srcFiles) {
4778
4807
  try {
4779
- sources.push({ rel: path15.relative(cwd, p), text: await fs16.readFile(p, "utf8") });
4808
+ sources.push({ rel: path16.relative(cwd, p), text: await fs16.readFile(p, "utf8") });
4780
4809
  } catch {
4781
4810
  }
4782
4811
  }
@@ -4786,7 +4815,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4786
4815
  const metaByName = /* @__PURE__ */ new Map();
4787
4816
  const bboxByPng = /* @__PURE__ */ new Map();
4788
4817
  for (const p of assetFiles) {
4789
- const base = path15.basename(p);
4818
+ const base = path16.basename(p);
4790
4819
  const metaMatch = /^(.+)\.annotated-progress\.json$/.exec(base);
4791
4820
  if (metaMatch) {
4792
4821
  try {
@@ -4810,7 +4839,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4810
4839
  for (const [name, meta] of metaByName) {
4811
4840
  if (!meta.cleanCrop || !referenced(`${name}-mask.png`)) continue;
4812
4841
  for (const p of assetFiles) {
4813
- const base = path15.basename(p);
4842
+ const base = path16.basename(p);
4814
4843
  if (!base.toLowerCase().endsWith(".png")) continue;
4815
4844
  if (base !== `${name}.png` && !base.startsWith(`${name}-`)) continue;
4816
4845
  if (/-mask\.png$|-frame\.png$|-overlay\.png$|-annotated-source\.png$/.test(base)) continue;
@@ -4834,7 +4863,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4834
4863
  }
4835
4864
  const pngByBase = /* @__PURE__ */ new Map();
4836
4865
  for (const p of assetFiles) {
4837
- const base = path15.basename(p);
4866
+ const base = path16.basename(p);
4838
4867
  if (base.toLowerCase().endsWith(".png")) pngByBase.set(base, p);
4839
4868
  }
4840
4869
  for (const [maskBase, maskPath] of pngByBase) {
@@ -4887,7 +4916,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4887
4916
  }
4888
4917
  const maskReported = /* @__PURE__ */ new Set();
4889
4918
  for (const p of assetFiles) {
4890
- const m = /^(.+)\.annotated-progress\.json$/.exec(path15.basename(p));
4919
+ const m = /^(.+)\.annotated-progress\.json$/.exec(path16.basename(p));
4891
4920
  if (!m) continue;
4892
4921
  const maskBase = `${m[1]}-mask.png`;
4893
4922
  if (!referenced(maskBase)) {
@@ -4899,14 +4928,14 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4899
4928
  }
4900
4929
  }
4901
4930
  for (const p of assetFiles) {
4902
- const base = path15.basename(p);
4931
+ const base = path16.basename(p);
4903
4932
  if (!base.toLowerCase().endsWith(".png")) continue;
4904
4933
  if (/-annotated-source\.png$|-overlay\.png$/.test(base)) continue;
4905
4934
  if (maskReported.has(base)) continue;
4906
4935
  if (!referenced(base)) {
4907
4936
  findings.push({
4908
4937
  kind: "unwired-sprite",
4909
- message: `${path15.relative(cwd, p)} is on disk but never referenced in ${srcDir}/index.html/CSS \u2014 wire it, or record the one-line reason it was cut. (Computed-string references are invisible here \u2014 check.)`
4938
+ message: `${path16.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.)`
4910
4939
  });
4911
4940
  }
4912
4941
  }
@@ -5021,7 +5050,7 @@ async function printUiAuditPreflight(log) {
5021
5050
  }
5022
5051
  async function printPipelineStatePreflight(log, cwd = process.cwd()) {
5023
5052
  try {
5024
- const design = await fs17.readFile(path16.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
5053
+ const design = await fs17.readFile(path17.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
5025
5054
  const warnings = [];
5026
5055
  if (design.length > 0 && !/##\s*build plan & status/i.test(design)) {
5027
5056
  warnings.push(
@@ -5029,7 +5058,7 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
5029
5058
  );
5030
5059
  }
5031
5060
  if (!/player character:/i.test(design)) {
5032
- const hasCharacter = await fs17.access(path16.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
5061
+ const hasCharacter = await fs17.access(path17.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
5033
5062
  if (!hasCharacter && await loadsPlayerBody(cwd)) {
5034
5063
  warnings.push(
5035
5064
  `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).`
@@ -5046,12 +5075,12 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
5046
5075
  async function loadsPlayerBody(cwd) {
5047
5076
  const BODY_LOADERS = /\bloadPlayerCharacter\s*\(|\bloadVrm(?:Clone)?\s*\(/;
5048
5077
  try {
5049
- const entries = await fs17.readdir(path16.join(cwd, "src"), { recursive: true });
5078
+ const entries = await fs17.readdir(path17.join(cwd, "src"), { recursive: true });
5050
5079
  for (const rel of entries) {
5051
5080
  if (rel.includes("node_modules")) continue;
5052
- if (rel.split(path16.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
5081
+ if (rel.split(path17.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
5053
5082
  if (!/\.(ts|tsx|js|jsx)$/.test(rel)) continue;
5054
- const text = await fs17.readFile(path16.join(cwd, "src", rel), "utf8").catch(() => "");
5083
+ const text = await fs17.readFile(path17.join(cwd, "src", rel), "utf8").catch(() => "");
5055
5084
  if (BODY_LOADERS.test(text)) return true;
5056
5085
  }
5057
5086
  } catch {
@@ -5088,9 +5117,9 @@ async function deployGame(ctx, opts, log) {
5088
5117
  }
5089
5118
  log.success("Built.");
5090
5119
  }
5091
- const distDir = path16.join(cwd, "dist");
5120
+ const distDir = path17.join(cwd, "dist");
5092
5121
  const siteDir = await isDir(distDir) ? distDir : cwd;
5093
- const rel = path16.relative(cwd, siteDir) || ".";
5122
+ const rel = path17.relative(cwd, siteDir) || ".";
5094
5123
  if (siteDir === cwd) await writeGitignore(cwd, log);
5095
5124
  const files = await collectFiles(siteDir);
5096
5125
  if (files.length === 0) {
@@ -5181,7 +5210,7 @@ async function deployGame(ctx, opts, log) {
5181
5210
  }
5182
5211
  async function hasBuildScript(cwd) {
5183
5212
  try {
5184
- const pkg = JSON.parse(await fs17.readFile(path16.join(cwd, "package.json"), "utf8"));
5213
+ const pkg = JSON.parse(await fs17.readFile(path17.join(cwd, "package.json"), "utf8"));
5185
5214
  return Boolean(pkg.scripts?.build);
5186
5215
  } catch {
5187
5216
  return false;
@@ -5193,9 +5222,9 @@ async function collectFiles(root) {
5193
5222
  for (const e of await fs17.readdir(dir, { withFileTypes: true })) {
5194
5223
  const relPath = prefix ? `${prefix}/${e.name}` : e.name;
5195
5224
  if (e.isDirectory()) {
5196
- if (!EXCLUDE_DIRS.has(e.name)) await walk2(path16.join(dir, e.name), relPath);
5225
+ if (!EXCLUDE_DIRS.has(e.name)) await walk2(path17.join(dir, e.name), relPath);
5197
5226
  } else if (e.isFile() && !isSecretEnvFile(e.name)) {
5198
- out.push({ relPath, bytes: await fs17.readFile(path16.join(dir, e.name)) });
5227
+ out.push({ relPath, bytes: await fs17.readFile(path17.join(dir, e.name)) });
5199
5228
  }
5200
5229
  }
5201
5230
  };
@@ -5468,7 +5497,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5468
5497
  log.error("Couldn't save your game's source \u2014 please try again.");
5469
5498
  return "failed";
5470
5499
  };
5471
- const gitDir = await fs17.mkdtemp(path16.join(os5.tmpdir(), "genex-source-"));
5500
+ const gitDir = await fs17.mkdtemp(path17.join(os5.tmpdir(), "genex-source-"));
5472
5501
  const base = { GIT_DIR: gitDir };
5473
5502
  if (urlHasEmbeddedCredentials(pushUrl)) {
5474
5503
  base.GIT_CONFIG_COUNT = "1";
@@ -5483,8 +5512,8 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5483
5512
  };
5484
5513
  try {
5485
5514
  if ((await run("git", ["init", "-q"], base)).code !== 0) return failed();
5486
- await fs17.writeFile(path16.join(gitDir, "info", "exclude"), excludeFile());
5487
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path16.join(gitDir, "index-source") };
5515
+ await fs17.writeFile(path17.join(gitDir, "info", "exclude"), excludeFile());
5516
+ const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path17.join(gitDir, "index-source") };
5488
5517
  let lfs = (await run("git", ["lfs", "version"], base)).code !== 0 ? false : true;
5489
5518
  if (!lfs) {
5490
5519
  log.step("Installing git-lfs (keeps large binary assets out of the source push)\u2026");
@@ -5737,7 +5766,8 @@ async function runPreview(opts) {
5737
5766
  process.exitCode = 1;
5738
5767
  return;
5739
5768
  }
5740
- const token = await readUserToken(opts.envPath);
5769
+ const apiUrl = getApiUrl(meta.apiUrl);
5770
+ const token = await readUserToken(opts.envPath, apiUrl);
5741
5771
  if (!token) {
5742
5772
  log.error("Not authorized. Run `genex init` first to sign in.");
5743
5773
  process.exitCode = 1;
@@ -5746,7 +5776,6 @@ async function runPreview(opts) {
5746
5776
  const detections = await detectFeatures(log);
5747
5777
  advisoryNudges(log, detections);
5748
5778
  await exploreReuseNudge(log, meta);
5749
- const apiUrl = getApiUrl(meta.apiUrl);
5750
5779
  const ok = await deployGame(
5751
5780
  { projectId: meta.id, apiUrl, token, slug: meta.slug },
5752
5781
  // Detections reach the server on every preview too: matchmaking so a draft's
@@ -5823,7 +5852,7 @@ function loggerFor(opts) {
5823
5852
  }
5824
5853
  async function readState(directory) {
5825
5854
  try {
5826
- const marker = path17.join(directory, ".genex", "remix-operation.json");
5855
+ const marker = path18.join(directory, ".genex", "remix-operation.json");
5827
5856
  if ((await fs18.lstat(marker)).isSymbolicLink()) return null;
5828
5857
  const value = JSON.parse(await fs18.readFile(marker, "utf8"));
5829
5858
  return value.schema === 1 && value.directory === directory && typeof value.operationId === "string" && /^[a-zA-Z0-9_-]{8,128}$/.test(value.operationId) && value.source && /^[a-f0-9]{40}$/.test(value.source.sourceCommitSha) ? value : null;
@@ -5832,11 +5861,11 @@ async function readState(directory) {
5832
5861
  }
5833
5862
  }
5834
5863
  async function saveState(state) {
5835
- const dir = path17.join(state.directory, ".genex");
5864
+ const dir = path18.join(state.directory, ".genex");
5836
5865
  await fs18.mkdir(dir, { recursive: true, mode: 448 });
5837
- const temp = path17.join(dir, "remix-operation.next.json");
5866
+ const temp = path18.join(dir, "remix-operation.next.json");
5838
5867
  await fs18.writeFile(temp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
5839
- await fs18.rename(temp, path17.join(dir, "remix-operation.json"));
5868
+ await fs18.rename(temp, path18.join(dir, "remix-operation.json"));
5840
5869
  }
5841
5870
  async function checkDestination(directory) {
5842
5871
  try {
@@ -5844,7 +5873,7 @@ async function checkDestination(directory) {
5844
5873
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new RemixError("destination_occupied", "The destination is not a fresh directory. Choose a new folder.");
5845
5874
  const entries = await fs18.readdir(directory);
5846
5875
  if (!entries.length) return null;
5847
- const genex = await fs18.lstat(path17.join(directory, ".genex")).catch(() => null);
5876
+ const genex = await fs18.lstat(path18.join(directory, ".genex")).catch(() => null);
5848
5877
  const state = genex?.isDirectory() && !genex.isSymbolicLink() ? await readState(directory) : null;
5849
5878
  if (state) return state;
5850
5879
  throw new RemixError("destination_occupied", "The destination already contains files. Choose a fresh folder; remix never clears an existing project.");
@@ -5855,7 +5884,7 @@ async function checkDestination(directory) {
5855
5884
  }
5856
5885
  async function assertReferenceParents(cwd) {
5857
5886
  for (const rel of [".genex", ".genex/refs"]) {
5858
- const stat = await fs18.lstat(path17.join(cwd, rel)).catch((err) => {
5887
+ const stat = await fs18.lstat(path18.join(cwd, rel)).catch((err) => {
5859
5888
  if (err.code === "ENOENT") return null;
5860
5889
  throw err;
5861
5890
  });
@@ -5863,16 +5892,16 @@ async function assertReferenceParents(cwd) {
5863
5892
  }
5864
5893
  }
5865
5894
  async function archiveSourceInstructions(directory) {
5866
- const archive = path17.join(directory, ".genex", "source-instructions");
5895
+ const archive = path18.join(directory, ".genex", "source-instructions");
5867
5896
  const rootArtifacts = /* @__PURE__ */ new Set([".claude", ".codex", ".cursor", ".agents", ".mcp.json", ".cursorrules", "DESIGN.md"]);
5868
5897
  const instructionNames = /* @__PURE__ */ new Set(["AGENTS.md", "CLAUDE.md", "GEMINI.md"]);
5869
5898
  const visit = async (dir) => {
5870
5899
  for (const entry of await fs18.readdir(dir, { withFileTypes: true })) {
5871
5900
  if (entry.name === ".genex" || entry.name === ".git" || entry.name === "node_modules") continue;
5872
- const from = path17.join(dir, entry.name), rel = path17.relative(directory, from);
5901
+ const from = path18.join(dir, entry.name), rel = path18.relative(directory, from);
5873
5902
  if (instructionNames.has(entry.name) || dir === directory && rootArtifacts.has(entry.name)) {
5874
- const to = path17.join(archive, rel);
5875
- await fs18.mkdir(path17.dirname(to), { recursive: true });
5903
+ const to = path18.join(archive, rel);
5904
+ await fs18.mkdir(path18.dirname(to), { recursive: true });
5876
5905
  if (await fs18.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `Source instructions at ${rel} changed during preparation; both copies were retained.`);
5877
5906
  await fs18.rename(from, to);
5878
5907
  } else if (entry.isDirectory()) await visit(from);
@@ -5881,7 +5910,7 @@ async function archiveSourceInstructions(directory) {
5881
5910
  await visit(directory);
5882
5911
  }
5883
5912
  async function acquireLock(directory) {
5884
- const lock = path17.join(directory, ".genex", "remix.lock");
5913
+ const lock = path18.join(directory, ".genex", "remix.lock");
5885
5914
  for (let attempt = 0; attempt < 2; attempt++) {
5886
5915
  try {
5887
5916
  const handle = await fs18.open(lock, "wx", 384);
@@ -5983,13 +6012,13 @@ function pinRemixDependencies(pkg, version) {
5983
6012
  return warnings;
5984
6013
  }
5985
6014
  async function installRemixDependencies(cwd, pm, version, log, runner = childRun) {
5986
- const pkgPath = path17.join(cwd, "package.json");
6015
+ const pkgPath = path18.join(cwd, "package.json");
5987
6016
  const pkg = JSON.parse(await fs18.readFile(pkgPath, "utf8"));
5988
6017
  const warnings = pinRemixDependencies(pkg, version);
5989
6018
  await fs18.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
5990
6019
  log.step(`Restoring dependencies with ${pm}; pinning ${CLI_PACKAGE}@${version} (${CLI_CHANNEL}).`);
5991
6020
  const args = pm === "npm" ? ["install", "--workspaces=false", "--no-audit", "--no-fund"] : pm === "pnpm" ? ["install", "--ignore-workspace", "--no-frozen-lockfile"] : ["install"];
5992
- if (pm === "yarn") await fs18.writeFile(path17.join(cwd, "yarn.lock"), "", { flag: "wx" }).catch((err) => {
6021
+ if (pm === "yarn") await fs18.writeFile(path18.join(cwd, "yarn.lock"), "", { flag: "wx" }).catch((err) => {
5993
6022
  if (err.code !== "EEXIST") throw err;
5994
6023
  });
5995
6024
  await runner(cwd, pm, args);
@@ -6023,7 +6052,7 @@ function rebindRemixConfig(content, slug) {
6023
6052
  async function configureIdentity(state, log) {
6024
6053
  const cwd = state.directory, meta = state.project;
6025
6054
  await writeProject(meta, cwd);
6026
- const configFile = path17.join(cwd, "src", "genex.config.ts");
6055
+ const configFile = path18.join(cwd, "src", "genex.config.ts");
6027
6056
  const config = await fs18.readFile(configFile, "utf8").catch((err) => {
6028
6057
  if (err.code === "ENOENT") return null;
6029
6058
  throw err;
@@ -6032,7 +6061,7 @@ async function configureIdentity(state, log) {
6032
6061
  await writeGameConfigFiles(meta, log, cwd);
6033
6062
  await writeGitignore(cwd, log);
6034
6063
  await installRemixProfile(cwd, state.apiUrl, log);
6035
- const manifestFile = path17.join(cwd, "package.json");
6064
+ const manifestFile = path18.join(cwd, "package.json");
6036
6065
  const manifest = JSON.parse(await fs18.readFile(manifestFile, "utf8"));
6037
6066
  manifest.genex.remixSource = { projectId: state.source.projectId, slug: state.source.slug, sourceCommitSha: state.source.sourceCommitSha, version: state.source.version, versionCertainty: state.source.versionCertainty };
6038
6067
  await fs18.writeFile(manifestFile, JSON.stringify(manifest, null, 2) + "\n");
@@ -6041,9 +6070,9 @@ async function configureIdentity(state, log) {
6041
6070
  const scan = async (dir) => {
6042
6071
  for (const entry of await fs18.readdir(dir, { withFileTypes: true })) {
6043
6072
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
6044
- const file = path17.join(dir, entry.name);
6073
+ const file = path18.join(dir, entry.name);
6045
6074
  if (entry.isDirectory()) await scan(file);
6046
- else if (entry.isFile() && /\.(?:[cm]?[jt]sx?|html)$/.test(entry.name) && (await fs18.stat(file)).size < 1024 * 1024 && quoted.test(await fs18.readFile(file, "utf8"))) possible.push(path17.relative(cwd, file));
6075
+ else if (entry.isFile() && /\.(?:[cm]?[jt]sx?|html)$/.test(entry.name) && (await fs18.stat(file)).size < 1024 * 1024 && quoted.test(await fs18.readFile(file, "utf8"))) possible.push(path18.relative(cwd, file));
6047
6076
  }
6048
6077
  };
6049
6078
  await scan(cwd);
@@ -6068,14 +6097,14 @@ async function runRemix(opts, deps = {}) {
6068
6097
  const apiUrl = getApiUrl(opts.apiUrl), dashboardUrl = new URL(getAuthUrl(opts.authUrl)).origin;
6069
6098
  if (opts.sourceOnly && !opts.destination) await assertReferenceParents(originalCwd);
6070
6099
  const selected = parseRemixSource(opts.remixSource, apiUrl, dashboardUrl);
6071
- let directory = opts.destination ? path17.resolve(originalCwd, opts.destination) : !opts.sourceOnly ? path17.resolve(originalCwd, `${selected.slug}-remix`) : void 0;
6100
+ let directory = opts.destination ? path18.resolve(originalCwd, opts.destination) : !opts.sourceOnly ? path18.resolve(originalCwd, `${selected.slug}-remix`) : void 0;
6072
6101
  if (directory) state = await checkDestination(directory);
6073
6102
  resumed = state !== null;
6074
6103
  if (state && (state.apiUrl !== apiUrl || state.sourceOnly !== Boolean(opts.sourceOnly) || ![state.requestedSlug, state.source.slug].includes(selected.slug) || opts.operationId && state.operationId !== opts.operationId || opts.name && state.name !== opts.name || selected.version && selected.version !== state.source.version)) throw new RemixError("operation_mismatch", "This folder belongs to a different remix operation. Resume with its original source/options or choose a fresh folder.");
6075
- let token = await readUserToken(opts.envPath);
6104
+ let token = await readUserToken(opts.envPath, apiUrl);
6076
6105
  if (!token) {
6077
- token = await authorize(apiUrl, dashboardUrl, { log, inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0 });
6078
- await writeUserToken(token, opts.envPath);
6106
+ token = await authorize(apiUrl, dashboardUrl, { envPath: opts.envPath, log, inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0 });
6107
+ await writeUserToken(token, opts.envPath, apiUrl);
6079
6108
  }
6080
6109
  const headers = { Authorization: `Bearer ${token}` };
6081
6110
  if (!state) {
@@ -6086,7 +6115,7 @@ async function runRemix(opts, deps = {}) {
6086
6115
  log.step(`Resolving source for ${selected.slug}\u2026`);
6087
6116
  const body = await responseJson(await request(`${apiUrl}/api/projects/${encodeURIComponent(selected.slug)}/source?${query}`, { headers, redirect: "error" }));
6088
6117
  validateSource(body, Boolean(opts.sourceOnly));
6089
- directory ??= path17.resolve(originalCwd, ".genex", "refs", `${body.source.slug}-${body.source.sourceCommitSha.slice(0, 12)}`);
6118
+ directory ??= path18.resolve(originalCwd, ".genex", "refs", `${body.source.slug}-${body.source.sourceCommitSha.slice(0, 12)}`);
6090
6119
  const existing = await checkDestination(directory);
6091
6120
  if (existing) {
6092
6121
  if (!existing.sourceOnly || !opts.sourceOnly || existing.apiUrl !== apiUrl || existing.source.sourceCommitSha !== body.source.sourceCommitSha || existing.source.projectId !== body.source.projectId) throw new RemixError("destination_occupied", "This reference destination already belongs to another operation. Choose a fresh folder.");
@@ -6096,18 +6125,18 @@ async function runRemix(opts, deps = {}) {
6096
6125
  state = { schema: 1, operationId: opts.operationId ?? crypto4.randomUUID(), directory, apiUrl, dashboardUrl, requestedSlug: selected.slug, source: body.source, sourceToken: body.sourceToken, sourceOnly: Boolean(opts.sourceOnly), cliVersion: getCliVersion(), name: opts.name?.trim() || `${body.source.title.slice(0, 74)} remix`, stage: "download", warnings: [...body.source.warnings] };
6097
6126
  await fs18.mkdir(directory, { recursive: true });
6098
6127
  if ((await fs18.readdir(directory)).length) throw new RemixError("destination_occupied", "The destination changed during setup. Choose a fresh folder.");
6099
- await fs18.mkdir(path17.join(directory, ".genex"), { mode: 448 });
6128
+ await fs18.mkdir(path18.join(directory, ".genex"), { mode: 448 });
6100
6129
  await saveState(state);
6101
6130
  }
6102
6131
  }
6103
6132
  unlock = await acquireLock(state.directory);
6104
6133
  state = await readState(state.directory) ?? state;
6105
6134
  if (resumed && state.stage === "download") await renewSource(state, request, headers);
6106
- const staging = path17.join(state.directory, ".genex", "remix-stage");
6135
+ const staging = path18.join(state.directory, ".genex", "remix-stage");
6107
6136
  if (state.stage === "download") {
6108
6137
  await fs18.rm(staging, { recursive: true, force: true });
6109
6138
  await fs18.mkdir(staging, { recursive: true });
6110
- const archive = path17.join(staging, "source.zip"), sourceDir = path17.join(staging, "source");
6139
+ const archive = path18.join(staging, "source.zip"), sourceDir = path18.join(staging, "source");
6111
6140
  await fs18.mkdir(sourceDir);
6112
6141
  const archiveUrl = new URL(state.source.archiveUrl, state.apiUrl);
6113
6142
  if (archiveUrl.origin !== new URL(state.apiUrl).origin || !archiveUrl.pathname.startsWith("/api/projects/")) throw new RemixError("invalid_source_response", "Source archive must be served by the selected Genex API.");
@@ -6123,12 +6152,12 @@ async function runRemix(opts, deps = {}) {
6123
6152
  if (!state.sourceOnly) {
6124
6153
  let pkg;
6125
6154
  try {
6126
- pkg = JSON.parse(await fs18.readFile(path17.join(sourceDir, "package.json"), "utf8"));
6155
+ pkg = JSON.parse(await fs18.readFile(path18.join(sourceDir, "package.json"), "utf8"));
6127
6156
  } catch (error) {
6128
- const staticEntry = await fs18.stat(path17.join(sourceDir, "index.html")).catch(() => null);
6157
+ const staticEntry = await fs18.stat(path18.join(sourceDir, "index.html")).catch(() => null);
6129
6158
  if (error.code !== "ENOENT" || !staticEntry?.isFile()) throw new RemixError("source_unavailable", "Source has neither a readable package.json nor a static index.html. Use --source-only to inspect it.");
6130
6159
  pkg = { name: manifestName(state.directory), private: true };
6131
- await fs18.writeFile(path17.join(sourceDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
6160
+ await fs18.writeFile(path18.join(sourceDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
6132
6161
  state.warnings.push("This static game had no package.json; added a minimal private manifest for the local CLI. Its source and index.html remain unchanged.");
6133
6162
  }
6134
6163
  if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) throw new RemixError("source_unavailable", "Source package.json is not a project manifest.");
@@ -6139,7 +6168,7 @@ async function runRemix(opts, deps = {}) {
6139
6168
  }
6140
6169
  if (state.stage === "transfer") {
6141
6170
  for (const entry of state.transferEntries ?? []) {
6142
- const from = path17.join(staging, "source", entry), to = path17.join(state.directory, entry);
6171
+ const from = path18.join(staging, "source", entry), to = path18.join(state.directory, entry);
6143
6172
  if (await fs18.lstat(from).catch(() => null)) {
6144
6173
  if (await fs18.lstat(to).catch(() => null)) throw new RemixError("destination_changed", `A file appeared at ${entry} during source preparation. It was left untouched.`);
6145
6174
  await fs18.rename(from, to);
@@ -6223,7 +6252,7 @@ async function runRemix(opts, deps = {}) {
6223
6252
 
6224
6253
  // src/commands/init.ts
6225
6254
  import fs19 from "fs/promises";
6226
- import path18 from "path";
6255
+ import path19 from "path";
6227
6256
 
6228
6257
  // src/lib/printed.ts
6229
6258
  var printed = /* @__PURE__ */ new WeakSet();
@@ -6331,8 +6360,8 @@ async function runInit(opts) {
6331
6360
  let totalNew = 0;
6332
6361
  let totalUpdated = 0;
6333
6362
  for (const t of targets) {
6334
- const src = t.full ? templatesDir : path18.join(templatesDir, "skills");
6335
- const dest = t.full ? t.baseDir : path18.join(t.baseDir, "skills");
6363
+ const src = t.full ? templatesDir : path19.join(templatesDir, "skills");
6364
+ const dest = t.full ? t.baseDir : path19.join(t.baseDir, "skills");
6336
6365
  const family = remixing ? skillFamilyFilter("remix") : converting ? skillFamilyFilter("tools", { hosted: true }) : skillFamilyFilter("game");
6337
6366
  if (remixing) await pruneRemixWorkflow(t.baseDir);
6338
6367
  const { copied, updated } = await copyTemplates(src, dest, {
@@ -6340,8 +6369,8 @@ async function runInit(opts) {
6340
6369
  exclude: ["controllers", "motion", "asset-viewer", "blender-service"],
6341
6370
  filter: t.full ? family : (rel) => family(`skills/${rel}`)
6342
6371
  });
6343
- await pruneRemovedSkills(path18.join(t.baseDir, "skills"), log);
6344
- await writeSkillsMarker(path18.join(t.baseDir, "skills"));
6372
+ await pruneRemovedSkills(path19.join(t.baseDir, "skills"), log);
6373
+ await writeSkillsMarker(path19.join(t.baseDir, "skills"));
6345
6374
  const added = copied.length - updated.length;
6346
6375
  totalNew += added;
6347
6376
  totalUpdated += updated.length;
@@ -6364,11 +6393,12 @@ async function runInit(opts) {
6364
6393
  }
6365
6394
  const authBaseUrl = getAuthUrl(opts.authUrl);
6366
6395
  const authApiUrl = getApiUrl(opts.apiUrl);
6367
- let token = await readUserToken(opts.envPath);
6396
+ let token = await readUserToken(opts.envPath, authApiUrl);
6368
6397
  let savedToken = Boolean(token);
6369
6398
  if (!token) {
6370
6399
  try {
6371
6400
  token = await authorize(authApiUrl, authBaseUrl, {
6401
+ envPath: opts.envPath,
6372
6402
  log,
6373
6403
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
6374
6404
  });
@@ -6382,8 +6412,8 @@ async function runInit(opts) {
6382
6412
  log.dim("Your workspace files were installed. Re-run `genex init` to finish authorizing.");
6383
6413
  throw markPrinted(err);
6384
6414
  }
6385
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
6386
- log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)} (${ENV_TOKEN_KEY}).`);
6415
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, authApiUrl);
6416
+ log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
6387
6417
  log.plain("");
6388
6418
  savedToken = false;
6389
6419
  }
@@ -6398,7 +6428,7 @@ async function runInit(opts) {
6398
6428
  if (email) log.plain(` signed in as ${c.cyan(email)}`);
6399
6429
  };
6400
6430
  await echoIdentity();
6401
- const projectName = opts.name?.trim() || path18.basename(process.cwd());
6431
+ const projectName = opts.name?.trim() || path19.basename(process.cwd());
6402
6432
  const create = (bearer) => createDraftProject({
6403
6433
  apiUrl,
6404
6434
  token: bearer,
@@ -6413,10 +6443,11 @@ async function runInit(opts) {
6413
6443
  if (!created.meta && created.unauthorized && savedToken) {
6414
6444
  log.plain("");
6415
6445
  log.warn("Your saved sign-in was rejected \u2014 reconnecting in the browser\u2026");
6416
- const aside = await rotateRejectedEnv(opts.envPath);
6446
+ const aside = await rotateRejectedEnv(opts.envPath, authApiUrl);
6417
6447
  if (aside) log.dim(` moved the rejected credential to ${c.cyan(aside)}`);
6418
6448
  try {
6419
6449
  token = await authorize(authApiUrl, authBaseUrl, {
6450
+ envPath: opts.envPath,
6420
6451
  log,
6421
6452
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
6422
6453
  });
@@ -6430,8 +6461,8 @@ async function runInit(opts) {
6430
6461
  log.dim("Your workspace files were installed. Re-run `genex init` to finish authorizing.");
6431
6462
  throw markPrinted(err);
6432
6463
  }
6433
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
6434
- log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)} (${ENV_TOKEN_KEY}).`);
6464
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, authApiUrl);
6465
+ log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
6435
6466
  await echoIdentity();
6436
6467
  created = await create(token);
6437
6468
  }
@@ -6449,7 +6480,7 @@ async function runInit(opts) {
6449
6480
  if (await writeToolsContract(process.cwd(), { hosted: true })) {
6450
6481
  log.dim(" wrote the Genex Tools publishing rules into AGENTS.md (managed block)");
6451
6482
  }
6452
- await ensureEnvVar(path18.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
6483
+ await ensureEnvVar(path19.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
6453
6484
  await reportCliEvent(apiUrl, token, "tools_converted");
6454
6485
  }
6455
6486
  if (!remixing) warnPreexisting(log, preexisting);
@@ -6478,7 +6509,7 @@ async function runInit(opts) {
6478
6509
  // src/commands/link.ts
6479
6510
  import fs20 from "fs/promises";
6480
6511
  import os6 from "os";
6481
- import path19 from "path";
6512
+ import path20 from "path";
6482
6513
  async function runLink(opts) {
6483
6514
  const log = createLogger({ quiet: opts.quiet });
6484
6515
  log.plain(c.bold("genex link"));
@@ -6499,10 +6530,11 @@ async function runLink(opts) {
6499
6530
  }
6500
6531
  const authBaseUrl = getAuthUrl(opts.authUrl);
6501
6532
  const apiUrl = getApiUrl(opts.apiUrl);
6502
- let token = await readUserToken(opts.envPath);
6533
+ let token = await readUserToken(opts.envPath, apiUrl);
6503
6534
  if (!token) {
6504
6535
  try {
6505
6536
  token = await authorize(apiUrl, authBaseUrl, {
6537
+ envPath: opts.envPath,
6506
6538
  log,
6507
6539
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
6508
6540
  });
@@ -6514,7 +6546,7 @@ async function runLink(opts) {
6514
6546
  }
6515
6547
  throw err;
6516
6548
  }
6517
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
6549
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, apiUrl);
6518
6550
  log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
6519
6551
  log.plain("");
6520
6552
  }
@@ -6573,13 +6605,13 @@ async function downloadSource(apiUrl, token, project, log) {
6573
6605
  return false;
6574
6606
  }
6575
6607
  log.step(`Downloading the ${grant.sourceRef === "preview" ? "draft" : "published"} source\u2026`);
6576
- const staging = await fs20.mkdtemp(path19.join(os6.tmpdir(), "genex-link-"));
6577
- const fresh = path19.join(staging, "source");
6608
+ const staging = await fs20.mkdtemp(path20.join(os6.tmpdir(), "genex-link-"));
6609
+ const fresh = path20.join(staging, "source");
6578
6610
  try {
6579
6611
  if (!await cloneSource(grant, fresh, log)) return false;
6580
- await fs20.rm(path19.join(fresh, ".git"), { recursive: true, force: true });
6612
+ await fs20.rm(path20.join(fresh, ".git"), { recursive: true, force: true });
6581
6613
  for (const entry of await fs20.readdir(fresh)) {
6582
- await fs20.cp(path19.join(fresh, entry), path19.join(process.cwd(), entry), {
6614
+ await fs20.cp(path20.join(fresh, entry), path20.join(process.cwd(), entry), {
6583
6615
  recursive: true,
6584
6616
  force: true
6585
6617
  });
@@ -6592,7 +6624,7 @@ async function downloadSource(apiUrl, token, project, log) {
6592
6624
  }
6593
6625
  }
6594
6626
  async function ensureSlugEnv(slug, log, cwd = process.cwd()) {
6595
- const file = path19.join(cwd, ".env");
6627
+ const file = path20.join(cwd, ".env");
6596
6628
  let content;
6597
6629
  try {
6598
6630
  content = await fs20.readFile(file, "utf8");
@@ -6663,7 +6695,7 @@ async function listOwnSlugs(apiUrl, token, log) {
6663
6695
 
6664
6696
  // src/commands/pull.ts
6665
6697
  import fs21 from "fs/promises";
6666
- import path20 from "path";
6698
+ import path21 from "path";
6667
6699
  import os7 from "os";
6668
6700
  function isMachineLocal(entry) {
6669
6701
  if (entry === ".genex" || entry === "node_modules" || entry === ".git") return true;
@@ -6682,13 +6714,13 @@ async function runPull(opts) {
6682
6714
  process.exitCode = 1;
6683
6715
  return;
6684
6716
  }
6685
- const token = await readUserToken(opts.envPath);
6717
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
6718
+ const token = await readUserToken(opts.envPath, apiUrl);
6686
6719
  if (!token) {
6687
6720
  log.error(`Not signed in. Run ${c.cyan("npx genex auth")} first.`);
6688
6721
  process.exitCode = 1;
6689
6722
  return;
6690
6723
  }
6691
- const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
6692
6724
  if (!opts.force) {
6693
6725
  const tree2 = await sourceTreeHash(cwd);
6694
6726
  if (!meta.sourceTree) {
@@ -6723,19 +6755,19 @@ async function runPull(opts) {
6723
6755
  process.exitCode = 1;
6724
6756
  return;
6725
6757
  }
6726
- const staging = await fs21.mkdtemp(path20.join(os7.tmpdir(), "genex-pull-"));
6727
- const fresh = path20.join(staging, "source");
6758
+ const staging = await fs21.mkdtemp(path21.join(os7.tmpdir(), "genex-pull-"));
6759
+ const fresh = path21.join(staging, "source");
6728
6760
  try {
6729
6761
  if (!await cloneSource(grant, fresh, log)) {
6730
6762
  process.exitCode = 1;
6731
6763
  return;
6732
6764
  }
6733
- await fs21.rm(path20.join(fresh, ".git"), { recursive: true, force: true });
6765
+ await fs21.rm(path21.join(fresh, ".git"), { recursive: true, force: true });
6734
6766
  const kept = await keepReplaced(cwd, log);
6735
6767
  await replaceTree(cwd, fresh);
6736
6768
  if (kept) {
6737
6769
  log.plain("");
6738
- log.info(`What was here is kept at ${c.cyan(path20.relative(cwd, kept) || kept)}`);
6770
+ log.info(`What was here is kept at ${c.cyan(path21.relative(cwd, kept) || kept)}`);
6739
6771
  log.dim(" Nothing was thrown away \u2014 re-apply from there, or delete it when you are done.");
6740
6772
  }
6741
6773
  } finally {
@@ -6759,13 +6791,13 @@ async function runPull(opts) {
6759
6791
  }
6760
6792
  async function keepReplaced(cwd, log) {
6761
6793
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6762
- const dest = path20.join(cwd, ".genex", `replaced-${stamp}`);
6794
+ const dest = path21.join(cwd, ".genex", `replaced-${stamp}`);
6763
6795
  try {
6764
6796
  const entries = (await fs21.readdir(cwd)).filter((e) => !isMachineLocal(e));
6765
6797
  if (entries.length === 0) return null;
6766
6798
  await fs21.mkdir(dest, { recursive: true });
6767
6799
  for (const entry of entries) {
6768
- await fs21.cp(path20.join(cwd, entry), path20.join(dest, entry), { recursive: true });
6800
+ await fs21.cp(path21.join(cwd, entry), path21.join(dest, entry), { recursive: true });
6769
6801
  }
6770
6802
  return dest;
6771
6803
  } catch (err) {
@@ -6777,17 +6809,17 @@ async function keepReplaced(cwd, log) {
6777
6809
  async function replaceTree(dest, src) {
6778
6810
  for (const entry of await fs21.readdir(dest)) {
6779
6811
  if (isMachineLocal(entry)) continue;
6780
- await fs21.rm(path20.join(dest, entry), { recursive: true, force: true });
6812
+ await fs21.rm(path21.join(dest, entry), { recursive: true, force: true });
6781
6813
  }
6782
6814
  for (const entry of await fs21.readdir(src)) {
6783
6815
  if (isMachineLocal(entry)) continue;
6784
- await fs21.cp(path20.join(src, entry), path20.join(dest, entry), { recursive: true });
6816
+ await fs21.cp(path21.join(src, entry), path21.join(dest, entry), { recursive: true });
6785
6817
  }
6786
6818
  }
6787
6819
 
6788
6820
  // src/commands/rename.ts
6789
6821
  import fs22 from "fs/promises";
6790
- import path21 from "path";
6822
+ import path22 from "path";
6791
6823
  async function runRename(opts) {
6792
6824
  const log = createLogger({ quiet: opts.quiet });
6793
6825
  log.plain(c.bold("genex rename"));
@@ -6804,13 +6836,13 @@ async function runRename(opts) {
6804
6836
  process.exitCode = 1;
6805
6837
  return;
6806
6838
  }
6807
- const token = await readUserToken(opts.envPath);
6839
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
6840
+ const token = await readUserToken(opts.envPath, apiUrl);
6808
6841
  if (!token) {
6809
6842
  log.error("Not signed in. Run `genex auth` first.");
6810
6843
  process.exitCode = 1;
6811
6844
  return;
6812
6845
  }
6813
- const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
6814
6846
  const from = meta.slug;
6815
6847
  if (meta.status === "published" && !opts.yes) {
6816
6848
  log.warn(`This retires ${c.cyan(from)} for good \u2014 it will redirect here, and nobody can reuse it.`);
@@ -6875,7 +6907,7 @@ async function runRename(opts) {
6875
6907
  log.info("Run `genex preview` (or `publish`) to rebuild \u2014 the new slug is baked into the bundle.");
6876
6908
  }
6877
6909
  async function rewriteSlugEnv(from, to, log, cwd = process.cwd()) {
6878
- const file = path21.join(cwd, ".env");
6910
+ const file = path22.join(cwd, ".env");
6879
6911
  let content;
6880
6912
  try {
6881
6913
  content = await fs22.readFile(file, "utf8");
@@ -6888,7 +6920,7 @@ async function rewriteSlugEnv(from, to, log, cwd = process.cwd()) {
6888
6920
  log.dim(` .env: VITE_GENEX_SLUG=${to} (was ${from})`);
6889
6921
  }
6890
6922
  async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
6891
- const file = path21.join(cwd, "src", "genex.config.ts");
6923
+ const file = path22.join(cwd, "src", "genex.config.ts");
6892
6924
  let content;
6893
6925
  try {
6894
6926
  content = await fs22.readFile(file, "utf8");
@@ -6919,7 +6951,7 @@ async function runList(opts) {
6919
6951
  const meta = await readProject();
6920
6952
  const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
6921
6953
  const dashOrigin = (meta?.dashboardOrigins?.[0] ?? getAuthUrl(opts.authUrl)).replace(/\/+$/, "");
6922
- let token = opts.token ?? await readUserToken(opts.envPath);
6954
+ let token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
6923
6955
  if (!token) {
6924
6956
  if (opts.noAuth) {
6925
6957
  log.error("Not signed in. Re-run without --no-auth to connect.");
@@ -6928,7 +6960,9 @@ async function runList(opts) {
6928
6960
  }
6929
6961
  log.plain("Not signed in \u2014 connecting\u2026");
6930
6962
  try {
6963
+ assertAuthorizationOrigin(apiUrl, getApiUrl(opts.apiUrl));
6931
6964
  token = await authorize(apiUrl, getAuthUrl(opts.authUrl), {
6965
+ envPath: opts.envPath,
6932
6966
  log,
6933
6967
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
6934
6968
  });
@@ -6942,7 +6976,7 @@ async function runList(opts) {
6942
6976
  process.exitCode = 1;
6943
6977
  return;
6944
6978
  }
6945
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
6979
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, apiUrl);
6946
6980
  log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
6947
6981
  log.plain("");
6948
6982
  }
@@ -6954,7 +6988,9 @@ async function runList(opts) {
6954
6988
  if (res.status === 401 && !opts.token && !opts.noAuth) {
6955
6989
  log.plain("Your saved sign-in was rejected \u2014 reconnecting\u2026");
6956
6990
  try {
6991
+ assertAuthorizationOrigin(apiUrl, getApiUrl(opts.apiUrl));
6957
6992
  token = await authorize(apiUrl, getAuthUrl(opts.authUrl), {
6993
+ envPath: opts.envPath,
6958
6994
  log,
6959
6995
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
6960
6996
  });
@@ -6968,7 +7004,7 @@ async function runList(opts) {
6968
7004
  process.exitCode = 1;
6969
7005
  return;
6970
7006
  }
6971
- await writeUserToken(token, opts.envPath);
7007
+ await writeUserToken(token, opts.envPath, apiUrl);
6972
7008
  res = await fetchProjects(token);
6973
7009
  }
6974
7010
  } catch (err) {
@@ -7039,13 +7075,13 @@ async function runMakeRemixable(opts) {
7039
7075
  process.exitCode = 1;
7040
7076
  return;
7041
7077
  }
7042
- const token = await readUserToken(opts.envPath);
7078
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
7079
+ const token = await readUserToken(opts.envPath, apiUrl);
7043
7080
  if (!token) {
7044
7081
  log.error("Not authorized. Run `genex init` first to sign in.");
7045
7082
  process.exitCode = 1;
7046
7083
  return;
7047
7084
  }
7048
- const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
7049
7085
  log.step("Preparing a public repo for your game\u2026");
7050
7086
  let res;
7051
7087
  try {
@@ -7127,7 +7163,7 @@ async function runDomain(opts) {
7127
7163
  return;
7128
7164
  }
7129
7165
  const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
7130
- let token = opts.token ?? await readUserToken(opts.envPath);
7166
+ let token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
7131
7167
  if (!token) {
7132
7168
  if (opts.noAuth) {
7133
7169
  log.error("Not signed in. Re-run without --no-auth to connect.");
@@ -7136,7 +7172,9 @@ async function runDomain(opts) {
7136
7172
  }
7137
7173
  log.plain("Not signed in \u2014 connecting\u2026");
7138
7174
  try {
7175
+ assertAuthorizationOrigin(apiUrl, getApiUrl(opts.apiUrl));
7139
7176
  token = await authorize(apiUrl, getAuthUrl(opts.authUrl), {
7177
+ envPath: opts.envPath,
7140
7178
  log,
7141
7179
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
7142
7180
  });
@@ -7150,7 +7188,7 @@ async function runDomain(opts) {
7150
7188
  process.exitCode = 1;
7151
7189
  return;
7152
7190
  }
7153
- await writeUserToken(token, opts.envPath);
7191
+ await writeUserToken(token, opts.envPath, apiUrl);
7154
7192
  }
7155
7193
  const base = `${apiUrl}/api/projects/${encodeURIComponent(meta.id)}/domains`;
7156
7194
  const auth = { Authorization: `Bearer ${token}` };
@@ -7274,7 +7312,7 @@ async function runShop(opts) {
7274
7312
  return;
7275
7313
  }
7276
7314
  const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
7277
- let token = opts.token ?? await readUserToken(opts.envPath);
7315
+ let token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
7278
7316
  if (!token) {
7279
7317
  if (opts.noAuth) {
7280
7318
  log.error("Not signed in. Re-run without --no-auth to connect.");
@@ -7283,7 +7321,9 @@ async function runShop(opts) {
7283
7321
  }
7284
7322
  log.plain("Not signed in \u2014 connecting\u2026");
7285
7323
  try {
7324
+ assertAuthorizationOrigin(apiUrl, getApiUrl(opts.apiUrl));
7286
7325
  token = await authorize(apiUrl, getAuthUrl(opts.authUrl), {
7326
+ envPath: opts.envPath,
7287
7327
  log,
7288
7328
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
7289
7329
  });
@@ -7297,7 +7337,7 @@ async function runShop(opts) {
7297
7337
  process.exitCode = 1;
7298
7338
  return;
7299
7339
  }
7300
- await writeUserToken(token, opts.envPath);
7340
+ await writeUserToken(token, opts.envPath, apiUrl);
7301
7341
  }
7302
7342
  const auth = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
7303
7343
  const call = (url, init2) => apiFetch(url, { ...init2, headers: { ...auth, ...init2?.headers ?? {} } });
@@ -7522,12 +7562,6 @@ async function runPublish(opts) {
7522
7562
  const log = createLogger({ quiet: opts.quiet });
7523
7563
  log.plain(c.bold("genex publish"));
7524
7564
  log.plain("");
7525
- const token = opts.token ?? await readUserToken(opts.envPath);
7526
- if (!token) {
7527
- log.error("Not authorized. Run `genex init` first to sign in.");
7528
- process.exitCode = 1;
7529
- return;
7530
- }
7531
7565
  const meta = await readProject();
7532
7566
  if (!meta) {
7533
7567
  log.error("No genex project here. Run `genex init` in this directory first.");
@@ -7535,6 +7569,12 @@ async function runPublish(opts) {
7535
7569
  return;
7536
7570
  }
7537
7571
  const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
7572
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
7573
+ if (!token) {
7574
+ log.error("Not authorized. Run `genex init` first to sign in.");
7575
+ process.exitCode = 1;
7576
+ return;
7577
+ }
7538
7578
  const detections = await detectFeatures(log);
7539
7579
  advisoryNudges(log, detections);
7540
7580
  if (!opts.noPush) {
@@ -7630,13 +7670,13 @@ async function runPromote(opts) {
7630
7670
  process.exitCode = 1;
7631
7671
  return;
7632
7672
  }
7633
- const token = await readUserToken(opts.envPath);
7673
+ const apiUrl = getApiUrl(meta.apiUrl);
7674
+ const token = await readUserToken(opts.envPath, apiUrl);
7634
7675
  if (!token) {
7635
7676
  log.error("Not authorized. Run `genex init` first to sign in.");
7636
7677
  process.exitCode = 1;
7637
7678
  return;
7638
7679
  }
7639
- const apiUrl = getApiUrl(meta.apiUrl);
7640
7680
  log.step("Making your previewed build live\u2026");
7641
7681
  const result = await promoteBuild(apiUrl, meta.id, token, log);
7642
7682
  if (!result) {
@@ -7661,14 +7701,14 @@ async function runAcceptCommand(opts) {
7661
7701
  const log = createLogger({ quiet: opts.quiet });
7662
7702
  log.plain(c.bold("genex accept"));
7663
7703
  log.plain("");
7664
- const token = await readUserToken(opts.envPath);
7704
+ const [meta, workspace] = await Promise.all([readProject(), readWorkspace()]);
7705
+ const apiUrl = getApiUrl(meta?.apiUrl ?? workspace?.apiUrl);
7706
+ const token = await readUserToken(opts.envPath, apiUrl);
7665
7707
  if (!token) {
7666
7708
  log.error(`Not signed in. Run ${c.cyan("npx genex auth")} first.`);
7667
7709
  process.exitCode = 1;
7668
7710
  return;
7669
7711
  }
7670
- const [meta, workspace] = await Promise.all([readProject(), readWorkspace()]);
7671
- const apiUrl = meta?.apiUrl ?? workspace?.apiUrl;
7672
7712
  const ok = await runAccept({
7673
7713
  token,
7674
7714
  log,
@@ -7686,13 +7726,13 @@ async function runSave(opts) {
7686
7726
  process.exitCode = 1;
7687
7727
  return;
7688
7728
  }
7689
- const token = await readUserToken(opts.envPath);
7729
+ const apiUrl = meta.apiUrl ?? getApiUrl();
7730
+ const token = await readUserToken(opts.envPath, apiUrl);
7690
7731
  if (!token) {
7691
7732
  log.error("Not signed in.");
7692
7733
  process.exitCode = 1;
7693
7734
  return;
7694
7735
  }
7695
- const apiUrl = meta.apiUrl ?? getApiUrl();
7696
7736
  let res;
7697
7737
  try {
7698
7738
  res = await apiFetch(`${apiUrl}/api/projects/${meta.id}/push-token`, {
@@ -7859,13 +7899,13 @@ async function runRollback(opts) {
7859
7899
  process.exitCode = 1;
7860
7900
  return;
7861
7901
  }
7862
- const token = await readUserToken(opts.envPath);
7902
+ const apiUrl = getApiUrl(meta.apiUrl);
7903
+ const token = await readUserToken(opts.envPath, apiUrl);
7863
7904
  if (!token) {
7864
7905
  log.error("Not authorized. Run `genex init` first to sign in.");
7865
7906
  process.exitCode = 1;
7866
7907
  return;
7867
7908
  }
7868
- const apiUrl = getApiUrl(meta.apiUrl);
7869
7909
  const previous = await readPreviousVersion(apiUrl, meta.id, token, log);
7870
7910
  if (!previous) {
7871
7911
  process.exitCode = 1;
@@ -7896,7 +7936,7 @@ async function runRollback(opts) {
7896
7936
 
7897
7937
  // src/commands/generate.ts
7898
7938
  import fs25 from "fs/promises";
7899
- import path24 from "path";
7939
+ import path25 from "path";
7900
7940
  import { PNG as PNG4 } from "pngjs";
7901
7941
 
7902
7942
  // src/lib/glass.ts
@@ -8079,12 +8119,12 @@ function ceilingVerdict(input) {
8079
8119
 
8080
8120
  // src/lib/generation-admission.ts
8081
8121
  import fs24 from "fs/promises";
8082
- import path23 from "path";
8122
+ import path24 from "path";
8083
8123
  import { createHash, randomUUID } from "crypto";
8084
8124
 
8085
8125
  // src/lib/asset-budget.ts
8086
8126
  import fs23 from "fs/promises";
8087
- import path22 from "path";
8127
+ import path23 from "path";
8088
8128
  var DEFAULT_ASSET_BUDGET_SHARE = 0.5;
8089
8129
  var DEFAULT_ASSET_BUDGET_CAP = 500;
8090
8130
  function envShare(raw, fallback) {
@@ -8111,7 +8151,7 @@ function allowanceFor(input) {
8111
8151
  if (!Number.isFinite(spendable) || spendable <= 0) return 0;
8112
8152
  return Math.max(0, Math.min(Math.floor(spendable * config.share), config.cap));
8113
8153
  }
8114
- var storePath = (cwd) => path22.join(cwd, ".genex", "asset-budget.json");
8154
+ var storePath = (cwd) => path23.join(cwd, ".genex", "asset-budget.json");
8115
8155
  async function readBudgetStore(cwd = process.cwd()) {
8116
8156
  try {
8117
8157
  const raw = await fs23.readFile(storePath(cwd), "utf8");
@@ -8133,7 +8173,7 @@ async function readBudgetStore(cwd = process.cwd()) {
8133
8173
  }
8134
8174
  async function writeBudgetStore(cwd, store) {
8135
8175
  try {
8136
- await fs23.access(path22.join(cwd, ".genex"));
8176
+ await fs23.access(path23.join(cwd, ".genex"));
8137
8177
  await fs23.writeFile(storePath(cwd), `${JSON.stringify(store, null, 2)}
8138
8178
  `, "utf8");
8139
8179
  return true;
@@ -8249,7 +8289,7 @@ async function assetBudgetGate(input) {
8249
8289
 
8250
8290
  // src/lib/generation-admission.ts
8251
8291
  async function withAdmissionLock(cwd, work, waitMs = 3e4) {
8252
- const lock = path23.join(cwd, ".genex", "generation-admission.lock");
8292
+ const lock = path24.join(cwd, ".genex", "generation-admission.lock");
8253
8293
  const deadline = Date.now() + waitMs;
8254
8294
  let file;
8255
8295
  while (!file) {
@@ -8524,7 +8564,7 @@ async function inlineLocalImage(filePath, flag) {
8524
8564
  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.`
8525
8565
  };
8526
8566
  }
8527
- const mime = IMAGE_MIME_BY_EXT[path24.extname(filePath).toLowerCase()] ?? "image/png";
8567
+ const mime = IMAGE_MIME_BY_EXT[path25.extname(filePath).toLowerCase()] ?? "image/png";
8528
8568
  return { ok: true, dataUri: `data:${mime};base64,${bytes.toString("base64")}` };
8529
8569
  }
8530
8570
  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.";
@@ -8711,7 +8751,7 @@ async function runGenerate(kind, opts) {
8711
8751
  let typedPrompt = opts.prompt?.trim();
8712
8752
  if (!typedPrompt && kind === "model" && opts.imageUrl) {
8713
8753
  const ref = opts.imageUrl.startsWith("data:") ? "local image" : opts.imageUrl;
8714
- typedPrompt = `from image: ${path24.basename(ref).slice(0, 120)}`;
8754
+ typedPrompt = `from image: ${path25.basename(ref).slice(0, 120)}`;
8715
8755
  }
8716
8756
  if (kind === "model" && opts.texture !== void 0 && !MODEL_TEXTURE_TIERS.includes(opts.texture)) {
8717
8757
  log.error(`--texture ${opts.texture} is a character texture size. \`genex model\` takes a texture TIER: ${MODEL_TEXTURE_TIERS.join("|")} (default detailed).`);
@@ -8831,14 +8871,14 @@ async function runGenerate(kind, opts) {
8831
8871
  `Voice text is ${prompt.length} chars \u2014 the server clamps at 1000 (billing follows the clamp). Split longer copy into separate lines.`
8832
8872
  );
8833
8873
  }
8834
- const token = opts.token ?? await readUserToken(opts.envPath);
8874
+ const meta = await readProject();
8875
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
8876
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
8835
8877
  if (!token) {
8836
8878
  log.error("Not authorized. Run `genex init` first to sign in.");
8837
8879
  process.exitCode = 1;
8838
8880
  return;
8839
8881
  }
8840
- const meta = await readProject();
8841
- const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
8842
8882
  const mode = await workspaceMode();
8843
8883
  if (mode === "tools") {
8844
8884
  const lane = laneFor(await fetchLanes(apiUrl, token), kind);
@@ -8961,7 +9001,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
8961
9001
  });
8962
9002
  continue;
8963
9003
  }
8964
- const outPath = path24.join(outDir, `glass-${i + 1}.png`);
9004
+ const outPath = path25.join(outDir, `glass-${i + 1}.png`);
8965
9005
  await fs25.writeFile(outPath, PNG4.sync.write(r.png));
8966
9006
  solved.push({
8967
9007
  path: outPath,
@@ -9582,7 +9622,7 @@ function writeJson(value) {
9582
9622
 
9583
9623
  // src/commands/model-sub.ts
9584
9624
  import fs26 from "fs/promises";
9585
- import path25 from "path";
9625
+ import path26 from "path";
9586
9626
  var MODEL_SUBCOMMANDS = ["segment", "rig", "animate", "import"];
9587
9627
  function apiErrorMessage(data, fallback) {
9588
9628
  if (typeof data !== "object" || data === null) return fallback;
@@ -9597,7 +9637,7 @@ async function importModelFile(args) {
9597
9637
  const { log } = args;
9598
9638
  const filePath = args.filePath.trim();
9599
9639
  if (!/\.glb$/i.test(filePath)) {
9600
- log.error(`\`import\` takes a .glb file (binary glTF 2.0). Export ${path25.basename(filePath) || "the model"} as GLB first \u2014 Blender: File \u2192 Export \u2192 glTF 2.0, format "glTF Binary".`);
9640
+ log.error(`\`import\` takes a .glb file (binary glTF 2.0). Export ${path26.basename(filePath) || "the model"} as GLB first \u2014 Blender: File \u2192 Export \u2192 glTF 2.0, format "glTF Binary".`);
9601
9641
  return null;
9602
9642
  }
9603
9643
  let bytes;
@@ -9608,18 +9648,18 @@ async function importModelFile(args) {
9608
9648
  return null;
9609
9649
  }
9610
9650
  if (bytes.byteLength > MODEL_IMPORT_MAX_BYTES) {
9611
- log.error(`${path25.basename(filePath)} is ${(bytes.byteLength / 1e6).toFixed(1)} MB; imports are capped at ${MODEL_IMPORT_MAX_BYTES / 1024 / 1024} MB. Shrink its textures (they are usually the bulk) and try again.`);
9651
+ log.error(`${path26.basename(filePath)} is ${(bytes.byteLength / 1e6).toFixed(1)} MB; imports are capped at ${MODEL_IMPORT_MAX_BYTES / 1024 / 1024} MB. Shrink its textures (they are usually the bulk) and try again.`);
9612
9652
  return null;
9613
9653
  }
9614
9654
  if (bytes.byteLength < 12 || bytes.readUInt32LE(0) !== GLB_MAGIC) {
9615
- log.error(`${path25.basename(filePath)} is not a GLB (no glTF magic). A .gltf + .bin pair must be exported as one binary .glb.`);
9655
+ log.error(`${path26.basename(filePath)} is not a GLB (no glTF magic). A .gltf + .bin pair must be exported as one binary .glb.`);
9616
9656
  return null;
9617
9657
  }
9618
9658
  const headers = { "Content-Type": "application/json", Authorization: `Bearer ${args.token}` };
9619
9659
  const minted = await apiFetch(`${args.apiUrl}/api/generations/import`, {
9620
9660
  method: "POST",
9621
9661
  headers,
9622
- body: JSON.stringify({ filename: path25.basename(filePath), bytes: bytes.byteLength, contentType: "model/gltf-binary" })
9662
+ body: JSON.stringify({ filename: path26.basename(filePath), bytes: bytes.byteLength, contentType: "model/gltf-binary" })
9623
9663
  });
9624
9664
  if (printedStructuredError(minted)) return null;
9625
9665
  if (!minted.ok) {
@@ -9628,7 +9668,7 @@ async function importModelFile(args) {
9628
9668
  return null;
9629
9669
  }
9630
9670
  const { id, uploadUrl, url } = await minted.json();
9631
- log.dim(` uploading ${path25.basename(filePath)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
9671
+ log.dim(` uploading ${path26.basename(filePath)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
9632
9672
  const put = await fetch(uploadUrl, {
9633
9673
  method: "PUT",
9634
9674
  headers: { "Content-Type": "model/gltf-binary", "Content-Length": String(bytes.byteLength) },
@@ -9659,14 +9699,14 @@ async function runModelImport(opts) {
9659
9699
  process.exitCode = 1;
9660
9700
  return;
9661
9701
  }
9662
- const token = opts.token ?? await readUserToken(opts.envPath);
9702
+ const project = await readProject();
9703
+ const apiUrl = getApiUrl(opts.apiUrl ?? project?.apiUrl);
9704
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
9663
9705
  if (!token) {
9664
9706
  log.error("Not authorized. Run `genex init` first to sign in.");
9665
9707
  process.exitCode = 1;
9666
9708
  return;
9667
9709
  }
9668
- const project = await readProject();
9669
- const apiUrl = getApiUrl(opts.apiUrl ?? project?.apiUrl);
9670
9710
  const imported = await importModelFile({ apiUrl, token, filePath, log });
9671
9711
  if (!imported) {
9672
9712
  process.exitCode = 1;
@@ -9776,7 +9816,7 @@ async function runModelAnimate(opts) {
9776
9816
  }
9777
9817
 
9778
9818
  // src/commands/wait.ts
9779
- import path26 from "path";
9819
+ import path27 from "path";
9780
9820
  var TERMINAL2 = /* @__PURE__ */ new Set(["completed", "failed"]);
9781
9821
  async function runWait(opts) {
9782
9822
  if (opts.all) return runWaitAll(opts);
@@ -9789,14 +9829,14 @@ async function runWait(opts) {
9789
9829
  process.exitCode = 1;
9790
9830
  return;
9791
9831
  }
9792
- const token = opts.token ?? await readUserToken(opts.envPath);
9832
+ const meta = await readProject();
9833
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
9834
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
9793
9835
  if (!token) {
9794
9836
  log.error("Not authorized. Run `genex init` first to sign in.");
9795
9837
  process.exitCode = 1;
9796
9838
  return;
9797
9839
  }
9798
- const meta = await readProject();
9799
- const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
9800
9840
  let view;
9801
9841
  try {
9802
9842
  const res = await apiFetch(`${apiUrl}/api/generations/${id}`, {
@@ -9862,14 +9902,14 @@ async function runWaitAll(opts) {
9862
9902
  );
9863
9903
  return;
9864
9904
  }
9865
- const token = opts.token ?? await readUserToken(opts.envPath);
9905
+ const meta = await readProject(cwd);
9906
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
9907
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
9866
9908
  if (!token) {
9867
9909
  log.error("Not authorized. Run `genex init` first to sign in.");
9868
9910
  process.exitCode = 1;
9869
9911
  return;
9870
9912
  }
9871
- const meta = await readProject(cwd);
9872
- const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
9873
9913
  const views = /* @__PURE__ */ new Map();
9874
9914
  try {
9875
9915
  const res = await apiFetch(`${apiUrl}/api/generations`, {
@@ -9915,7 +9955,7 @@ async function runWaitAll(opts) {
9915
9955
  if (v?.status !== "completed" || !v.files?.length) continue;
9916
9956
  const local = localDeliveryFor("tools", { ...opts, outDir: opts.outDir ?? e.outDir }, e.prompt || e.kind);
9917
9957
  if (!local) continue;
9918
- const outDir = path26.join(path26.relative(process.cwd(), cwd) || ".", local.outDir);
9958
+ const outDir = path27.join(path27.relative(process.cwd(), cwd) || ".", local.outDir);
9919
9959
  const target = { kind: e.kind, prompt: local.prompt, id: e.id, outDir };
9920
9960
  const missing = await undeliveredFiles(v.files, target);
9921
9961
  if (missing.length === 0) continue;
@@ -10024,7 +10064,7 @@ async function toRow(e, v, cwd) {
10024
10064
 
10025
10065
  // src/commands/controller.ts
10026
10066
  import fs28 from "fs/promises";
10027
- import path28 from "path";
10067
+ import path29 from "path";
10028
10068
 
10029
10069
  // ../../packages/meshy-animation-catalog/src/index.ts
10030
10070
  import { createHash as createHash2 } from "crypto";
@@ -19155,8 +19195,8 @@ function searchMeshyAnimations(query, options = {}) {
19155
19195
 
19156
19196
  // src/lib/anims.ts
19157
19197
  import fs27 from "fs/promises";
19158
- import path27 from "path";
19159
- var ANIMS_DEST = path27.join("public", "assets", "anims");
19198
+ import path28 from "path";
19199
+ var ANIMS_DEST = path28.join("public", "assets", "anims");
19160
19200
  var HIDDEN_TAG = "reference";
19161
19201
  async function runAnims(opts) {
19162
19202
  const log = createLogger({ quiet: opts.quiet });
@@ -19172,7 +19212,7 @@ async function runAnims(opts) {
19172
19212
  printCatalog(log, manifest, selectors);
19173
19213
  return;
19174
19214
  }
19175
- const controllerMarker = path27.join(root, "src", "controllers", "character");
19215
+ const controllerMarker = path28.join(root, "src", "controllers", "character");
19176
19216
  if (!await exists3(controllerMarker)) {
19177
19217
  log.error(
19178
19218
  `No character controller in this game (missing ${c.cyan("src/controllers/character/")}).`
@@ -19181,11 +19221,11 @@ async function runAnims(opts) {
19181
19221
  process.exitCode = 1;
19182
19222
  return;
19183
19223
  }
19184
- const destDir = path27.join(root, ANIMS_DEST);
19185
- const gameManifestPath = path27.join(destDir, "manifest.json");
19224
+ const destDir = path28.join(root, ANIMS_DEST);
19225
+ const gameManifestPath = path28.join(destDir, "manifest.json");
19186
19226
  if (opts.reset) {
19187
19227
  await fs27.rm(destDir, { recursive: true, force: true });
19188
- log.step(`Cleared ${c.cyan(ANIMS_DEST + path27.sep)} (--reset)`);
19228
+ log.step(`Cleared ${c.cyan(ANIMS_DEST + path28.sep)} (--reset)`);
19189
19229
  }
19190
19230
  if (selectors.length === 0) {
19191
19231
  const installed = await readGameManifest(gameManifestPath);
@@ -19223,7 +19263,7 @@ async function runAnims(opts) {
19223
19263
  }
19224
19264
  }
19225
19265
  const wanted = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
19226
- const cacheDir = path27.join(
19266
+ const cacheDir = path28.join(
19227
19267
  opts.cacheDir ?? getAnimsCacheDir(),
19228
19268
  `${manifest.library}-v${manifest.version}`
19229
19269
  );
@@ -19235,13 +19275,13 @@ async function runAnims(opts) {
19235
19275
  let addedBytes = 0;
19236
19276
  const failures = [];
19237
19277
  for (const entry of wanted) {
19238
- const dest = path27.join(destDir, entry.file);
19278
+ const dest = path28.join(destDir, entry.file);
19239
19279
  if (await hasSize(dest, entry.bytes)) {
19240
19280
  presentCount++;
19241
19281
  continue;
19242
19282
  }
19243
19283
  try {
19244
- const cached = path27.join(cacheDir, entry.file);
19284
+ const cached = path28.join(cacheDir, entry.file);
19245
19285
  if (!await hasSize(cached, entry.bytes)) {
19246
19286
  const res = await fetch(base + entry.file);
19247
19287
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -19251,7 +19291,7 @@ async function runAnims(opts) {
19251
19291
  await fs27.copyFile(cached, dest);
19252
19292
  installedCount++;
19253
19293
  addedBytes += entry.bytes;
19254
- log.dim(` ${path27.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
19294
+ log.dim(` ${path28.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
19255
19295
  } catch (err) {
19256
19296
  failures.push(`${entry.name} (${err instanceof Error ? err.message : String(err)})`);
19257
19297
  }
@@ -19273,7 +19313,7 @@ async function runAnims(opts) {
19273
19313
  if (presentCount > 0) parts.push(`${presentCount} already present`);
19274
19314
  if (bundledSkips > 0) parts.push(`${bundledSkips} already bundled in animation-library.glb`);
19275
19315
  log.success(
19276
- `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path27.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
19316
+ `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path28.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
19277
19317
  );
19278
19318
  for (const [selector, entries] of resolved) {
19279
19319
  const names = entries.filter((e) => !coreNames.has(e.name)).map((e) => e.name);
@@ -19305,7 +19345,7 @@ async function loadManifest(baseOverride) {
19305
19345
  }
19306
19346
  } catch {
19307
19347
  }
19308
- const snapshotPath = path27.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
19348
+ const snapshotPath = path28.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
19309
19349
  const manifest = JSON.parse(await fs27.readFile(snapshotPath, "utf8"));
19310
19350
  return { manifest, source: "snapshot" };
19311
19351
  }
@@ -19639,8 +19679,8 @@ var CONTROLLER_FILE_SETS = {
19639
19679
  ]
19640
19680
  }
19641
19681
  };
19642
- var CODE_DEST = path28.join("src", "controllers");
19643
- var ASSETS_DEST = path28.join("public", "assets");
19682
+ var CODE_DEST = path29.join("src", "controllers");
19683
+ var ASSETS_DEST = path29.join("public", "assets");
19644
19684
  async function runController(opts) {
19645
19685
  const log = createLogger({ quiet: opts.quiet });
19646
19686
  if (opts.kind?.trim() === "anims") {
@@ -19657,31 +19697,31 @@ async function runController(opts) {
19657
19697
  process.exitCode = 1;
19658
19698
  return;
19659
19699
  }
19660
- const srcDir = path28.join(getTemplatesDir(), "controllers");
19700
+ const srcDir = path29.join(getTemplatesDir(), "controllers");
19661
19701
  const root = opts.cwd ?? process.cwd();
19662
19702
  const set = CONTROLLER_FILE_SETS[kind];
19663
19703
  log.plain(c.bold(`genex controller ${kind}`));
19664
19704
  log.plain("");
19665
- log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path28.sep)}`);
19705
+ log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path29.sep)}`);
19666
19706
  const plan = [
19667
- ...set.code.map((rel) => ({ from: rel, rel: path28.join(CODE_DEST, rel) })),
19707
+ ...set.code.map((rel) => ({ from: rel, rel: path29.join(CODE_DEST, rel) })),
19668
19708
  ...set.assets.map((rel) => ({
19669
19709
  from: rel,
19670
- rel: path28.join(ASSETS_DEST, path28.basename(rel))
19710
+ rel: path29.join(ASSETS_DEST, path29.basename(rel))
19671
19711
  }))
19672
19712
  ];
19673
19713
  let copied = 0;
19674
19714
  let skipped = 0;
19675
19715
  try {
19676
19716
  for (const file of plan) {
19677
- const dest = path28.join(root, file.rel);
19717
+ const dest = path29.join(root, file.rel);
19678
19718
  if (!opts.force && await exists4(dest)) {
19679
19719
  skipped++;
19680
19720
  log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
19681
19721
  continue;
19682
19722
  }
19683
- await fs28.mkdir(path28.dirname(dest), { recursive: true });
19684
- await fs28.copyFile(path28.join(srcDir, file.from), dest);
19723
+ await fs28.mkdir(path29.dirname(dest), { recursive: true });
19724
+ await fs28.copyFile(path29.join(srcDir, file.from), dest);
19685
19725
  copied++;
19686
19726
  log.dim(` ${file.rel}`);
19687
19727
  }
@@ -19697,7 +19737,7 @@ async function runController(opts) {
19697
19737
  if (kind === "character") {
19698
19738
  if (opts.character) {
19699
19739
  try {
19700
- const token = opts.token !== void 0 ? opts.token : await readUserToken();
19740
+ const token = opts.token !== void 0 ? opts.token : await readUserToken(void 0, getApiUrl(opts.apiUrl));
19701
19741
  if (!token) throw new Error("Not authorized. Run `genex init` before installing a Meshy character.");
19702
19742
  await installMeshyCharacterManifest({
19703
19743
  root,
@@ -19734,7 +19774,7 @@ async function runController(opts) {
19734
19774
  for (const line of set.sketch) {
19735
19775
  log.dim(` ${line}`);
19736
19776
  }
19737
- if (kind === "character" && !await exists4(path28.join(root, ASSETS_DEST, "meshy-character.json"))) {
19777
+ if (kind === "character" && !await exists4(path29.join(root, ASSETS_DEST, "meshy-character.json"))) {
19738
19778
  log.plain("");
19739
19779
  log.plain(
19740
19780
  ` ${stepOffset + 3}. This game has no generated character yet \u2014 ${c.cyan(
@@ -19766,8 +19806,8 @@ async function installMeshyCharacterManifest(args) {
19766
19806
  throw new Error("The API returned an invalid Meshy character manifest.");
19767
19807
  }
19768
19808
  assertCompleteMeshyControllerPack(manifest);
19769
- const destination = path28.join(args.root, ASSETS_DEST, "meshy-character.json");
19770
- await fs28.mkdir(path28.dirname(destination), { recursive: true });
19809
+ const destination = path29.join(args.root, ASSETS_DEST, "meshy-character.json");
19810
+ await fs28.mkdir(path29.dirname(destination), { recursive: true });
19771
19811
  await fs28.writeFile(
19772
19812
  destination,
19773
19813
  `${JSON.stringify(manifest, null, 2)}
@@ -19913,9 +19953,9 @@ function assertCompleteMeshyControllerPack(manifest) {
19913
19953
  }
19914
19954
  async function installFallbackAvatar(args) {
19915
19955
  const { root, srcDir, log } = args;
19916
- const dest = path28.join(root, ASSETS_DEST, "avatar.vrm");
19917
- await fs28.mkdir(path28.dirname(dest), { recursive: true });
19918
- await fs28.copyFile(path28.join(srcDir, "assets", "default-avatar.vrm"), dest);
19956
+ const dest = path29.join(root, ASSETS_DEST, "avatar.vrm");
19957
+ await fs28.mkdir(path29.dirname(dest), { recursive: true });
19958
+ await fs28.copyFile(path29.join(srcDir, "assets", "default-avatar.vrm"), dest);
19919
19959
  log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
19920
19960
  }
19921
19961
  async function exists4(p) {
@@ -19929,7 +19969,7 @@ async function exists4(p) {
19929
19969
 
19930
19970
  // src/commands/character.ts
19931
19971
  import fs29 from "fs/promises";
19932
- import path29 from "path";
19972
+ import path30 from "path";
19933
19973
  function exactAnimation(selector) {
19934
19974
  const trimmed = selector.trim();
19935
19975
  if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
@@ -19955,10 +19995,11 @@ function resolveActionSelectors(selectors) {
19955
19995
  return { ok: true, actionIds: [...new Set(actionIds)] };
19956
19996
  }
19957
19997
  async function context(opts) {
19958
- const token = opts.token ?? await readUserToken(opts.envPath);
19959
- if (!token) return null;
19960
19998
  const project = await readProject();
19961
- return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
19999
+ const apiUrl = getApiUrl(opts.apiUrl ?? project?.apiUrl);
20000
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
20001
+ if (!token) return null;
20002
+ return { token, apiUrl };
19962
20003
  }
19963
20004
  var CHARACTER_BRIEF_MAX_CHARS = 600;
19964
20005
  function apiErrorMessage2(data, fallback) {
@@ -20009,7 +20050,7 @@ function showAmbiguity(selector, candidates, log, json = false) {
20009
20050
  }
20010
20051
  process.exitCode = 1;
20011
20052
  }
20012
- var INSTALLED_MANIFEST = path29.join("public", "assets", "meshy-character.json");
20053
+ var INSTALLED_MANIFEST = path30.join("public", "assets", "meshy-character.json");
20013
20054
  async function resolveAdoptTarget(selector) {
20014
20055
  const trimmed = selector?.trim();
20015
20056
  if (trimmed && !trimmed.endsWith(".json")) {
@@ -20141,7 +20182,7 @@ async function runCharacterImport(opts) {
20141
20182
  opts,
20142
20183
  ctx,
20143
20184
  kind: "character",
20144
- prompt: `Import ${path29.basename(filePath)} as a rigged character`,
20185
+ prompt: `Import ${path30.basename(filePath)} as a rigged character`,
20145
20186
  createPath: "/api/characters/import",
20146
20187
  body,
20147
20188
  quote: price,
@@ -20611,27 +20652,28 @@ import { basename } from "path";
20611
20652
  var VIDEO_SECONDS = [5, 6, 7, 8];
20612
20653
  var MAX_VIDEO_BYTES = 200 * 1024 * 1024;
20613
20654
  async function context2(opts) {
20614
- const token = opts.token ?? await readUserToken(opts.envPath);
20615
- if (!token) return null;
20616
20655
  const project = await readProject();
20617
- return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
20656
+ const apiUrl = getApiUrl(opts.apiUrl ?? project?.apiUrl);
20657
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
20658
+ if (!token) return null;
20659
+ return { token, apiUrl };
20618
20660
  }
20619
- async function readVideo(path36, log) {
20661
+ async function readVideo(path37, log) {
20620
20662
  let bytes;
20621
20663
  try {
20622
- bytes = await readFile(path36);
20664
+ bytes = await readFile(path37);
20623
20665
  } catch {
20624
- log.error(`Can't read ${path36}.`);
20666
+ log.error(`Can't read ${path37}.`);
20625
20667
  return null;
20626
20668
  }
20627
20669
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
20628
- log.error(`${basename(path36)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
20670
+ log.error(`${basename(path37)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
20629
20671
  return null;
20630
20672
  }
20631
20673
  return bytes;
20632
20674
  }
20633
- async function uploadVideo(apiUrl, token, characterId, path36, bytes, log) {
20634
- const contentType = /\.mov$/i.test(path36) ? "video/quicktime" : "video/mp4";
20675
+ async function uploadVideo(apiUrl, token, characterId, path37, bytes, log) {
20676
+ const contentType = /\.mov$/i.test(path37) ? "video/quicktime" : "video/mp4";
20635
20677
  const minted = await apiFetch(
20636
20678
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
20637
20679
  {
@@ -20646,7 +20688,7 @@ async function uploadVideo(apiUrl, token, characterId, path36, bytes, log) {
20646
20688
  return null;
20647
20689
  }
20648
20690
  const { uploadUrl, videoUrl } = await minted.json();
20649
- log.dim(` uploading ${basename(path36)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
20691
+ log.dim(` uploading ${basename(path37)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
20650
20692
  const put = await fetch(uploadUrl, {
20651
20693
  method: "PUT",
20652
20694
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -20979,7 +21021,7 @@ function rank(items, query) {
20979
21021
 
20980
21022
  // src/commands/motion.ts
20981
21023
  import fs30 from "fs/promises";
20982
- import path30 from "path";
21024
+ import path31 from "path";
20983
21025
 
20984
21026
  // src/lib/motion/npz.ts
20985
21027
  import zlib from "zlib";
@@ -22257,7 +22299,7 @@ async function expandTakes(selectors) {
22257
22299
  const st = await fs30.stat(sel).catch(() => null);
22258
22300
  if (st?.isDirectory()) {
22259
22301
  const names = await fs30.readdir(sel);
22260
- for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path30.join(sel, n));
22302
+ for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path31.join(sel, n));
22261
22303
  } else if (st?.isFile()) {
22262
22304
  out.push(sel);
22263
22305
  } else {
@@ -22314,7 +22356,7 @@ async function motionVerify(opts, log) {
22314
22356
  }
22315
22357
  const reports = [];
22316
22358
  for (const file of files) {
22317
- const stem = path30.basename(file).replace(/\.npz$/, "");
22359
+ const stem = path31.basename(file).replace(/\.npz$/, "");
22318
22360
  try {
22319
22361
  reports.push(analyzeTake(stem, await fs30.readFile(file), gates));
22320
22362
  } catch (err) {
@@ -22372,7 +22414,7 @@ async function motionCompile(opts, log) {
22372
22414
  }
22373
22415
  const inputs = [];
22374
22416
  for (const file of files) {
22375
- const stem = path30.basename(file).replace(/\.npz$/, "");
22417
+ const stem = path31.basename(file).replace(/\.npz$/, "");
22376
22418
  try {
22377
22419
  inputs.push({ stem, take: loadTake(await fs30.readFile(file)) });
22378
22420
  } catch (err) {
@@ -22381,7 +22423,7 @@ async function motionCompile(opts, log) {
22381
22423
  return;
22382
22424
  }
22383
22425
  }
22384
- const setName = opts.set ?? path30.basename(opts.out).replace(/\.json$/, "");
22426
+ const setName = opts.set ?? path31.basename(opts.out).replace(/\.json$/, "");
22385
22427
  let result;
22386
22428
  try {
22387
22429
  result = compileSet(inputs, setName, cfg);
@@ -22396,7 +22438,7 @@ async function motionCompile(opts, log) {
22396
22438
  process.exitCode = 1;
22397
22439
  return;
22398
22440
  }
22399
- await fs30.mkdir(path30.dirname(path30.resolve(opts.out)), { recursive: true });
22441
+ await fs30.mkdir(path31.dirname(path31.resolve(opts.out)), { recursive: true });
22400
22442
  const json = JSON.stringify(result.data);
22401
22443
  await fs30.writeFile(opts.out, json);
22402
22444
  if (opts.json) {
@@ -22416,9 +22458,9 @@ var MOTION_RUNTIME_FILES = [
22416
22458
  var MOTION_PRESETS = {
22417
22459
  rifle: ["sets/rifle.json", "sets/jumps.json"]
22418
22460
  };
22419
- var MOTION_DEST = path30.join("src", "motion");
22461
+ var MOTION_DEST = path31.join("src", "motion");
22420
22462
  async function motionInstall(opts, log) {
22421
- const srcDir = path30.join(getTemplatesDir(), "motion");
22463
+ const srcDir = path31.join(getTemplatesDir(), "motion");
22422
22464
  const root = opts.cwd ?? process.cwd();
22423
22465
  const preset = opts.set;
22424
22466
  if (preset !== void 0 && !MOTION_PRESETS[preset]) {
@@ -22429,21 +22471,21 @@ async function motionInstall(opts, log) {
22429
22471
  const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
22430
22472
  log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
22431
22473
  log.plain("");
22432
- log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path30.sep)}`);
22474
+ log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path31.sep)}`);
22433
22475
  let copied = 0, skipped = 0;
22434
22476
  try {
22435
22477
  for (const rel of files) {
22436
- const dest = path30.join(root, MOTION_DEST, rel);
22478
+ const dest = path31.join(root, MOTION_DEST, rel);
22437
22479
  const exists5 = await fs30.access(dest).then(() => true, () => false);
22438
22480
  if (!opts.force && exists5) {
22439
22481
  skipped++;
22440
- log.dim(` skipped ${path30.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
22482
+ log.dim(` skipped ${path31.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
22441
22483
  continue;
22442
22484
  }
22443
- await fs30.mkdir(path30.dirname(dest), { recursive: true });
22444
- await fs30.copyFile(path30.join(srcDir, rel), dest);
22485
+ await fs30.mkdir(path31.dirname(dest), { recursive: true });
22486
+ await fs30.copyFile(path31.join(srcDir, rel), dest);
22445
22487
  copied++;
22446
- log.dim(` ${path30.join(MOTION_DEST, rel)}`);
22488
+ log.dim(` ${path31.join(MOTION_DEST, rel)}`);
22447
22489
  }
22448
22490
  } catch (err) {
22449
22491
  log.error(`Copy failed: ${String(err)}`);
@@ -22522,12 +22564,12 @@ async function runMotion(opts) {
22522
22564
 
22523
22565
  // src/commands/blender.ts
22524
22566
  import fs31 from "fs/promises";
22525
- import path31 from "path";
22567
+ import path32 from "path";
22526
22568
  var SUBS2 = ["demo", "exec", "snap", "scene", "import", "export", "reset", "mcp", "serve", "seat", "release"];
22527
22569
  var DEFAULT_OUT_DIR = "assets/blender";
22528
22570
  async function writeB64(dir, name, b64) {
22529
22571
  await fs31.mkdir(dir, { recursive: true });
22530
- const p = path31.join(dir, name);
22572
+ const p = path32.join(dir, name);
22531
22573
  await fs31.writeFile(p, Buffer.from(b64, "base64"));
22532
22574
  return p;
22533
22575
  }
@@ -22545,7 +22587,7 @@ async function runBlender(opts) {
22545
22587
  return 1;
22546
22588
  }
22547
22589
  if (sub === "serve") {
22548
- const { serveLocalBlender } = await import("./blender-serve-PVVCH3S6.js");
22590
+ const { serveLocalBlender } = await import("./blender-serve-GW3LTFOX.js");
22549
22591
  const port = Number(process.env.GENEX_BLENDER_PORT ?? 8088);
22550
22592
  log.step(`Starting a local Blender service on port ${port}`);
22551
22593
  log.plain(
@@ -22554,7 +22596,7 @@ async function runBlender(opts) {
22554
22596
  return serveLocalBlender({ port, log });
22555
22597
  }
22556
22598
  if (sub === "mcp") {
22557
- const { runBlenderMcp } = await import("./blender-mcp-X66ZZ4TN.js");
22599
+ const { runBlenderMcp } = await import("./blender-mcp-XLEK6SLM.js");
22558
22600
  return runBlenderMcp();
22559
22601
  }
22560
22602
  if (sub === "seat") {
@@ -22612,7 +22654,7 @@ async function runBlender(opts) {
22612
22654
  log.plain(rest.join("\n"));
22613
22655
  return 1;
22614
22656
  }
22615
- const outDir = path31.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
22657
+ const outDir = path32.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
22616
22658
  const mode = opts.mode;
22617
22659
  if (mode !== void 0 && !isRenderMode(mode)) {
22618
22660
  log.error(`Unknown --mode ${mode}. Use one of: ${RENDER_MODES.join(", ")}.`);
@@ -22652,13 +22694,13 @@ async function runBlender(opts) {
22652
22694
  return 0;
22653
22695
  }
22654
22696
  case "export": {
22655
- const target = opts.out ?? path31.join(outDir, "scene.glb");
22697
+ const target = opts.out ?? path32.join(outDir, "scene.glb");
22656
22698
  const r = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
22657
22699
  if (!r.glbBase64) {
22658
22700
  log.error(`/export answered with no GLB bytes${r.uploaded ? " (it was uploaded, not inlined)" : ""}`);
22659
22701
  return 1;
22660
22702
  }
22661
- await fs31.mkdir(path31.dirname(target), { recursive: true });
22703
+ await fs31.mkdir(path32.dirname(target), { recursive: true });
22662
22704
  await fs31.writeFile(target, Buffer.from(r.glbBase64, "base64"));
22663
22705
  log.success(`Exported ${r.bytes ?? 0} bytes`);
22664
22706
  log.plain(` ${c.cyan(target)}`);
@@ -22697,7 +22739,7 @@ async function runBlender(opts) {
22697
22739
  log.error(`Can't read ${opts.input}`);
22698
22740
  return 1;
22699
22741
  }
22700
- label = path31.basename(opts.input);
22742
+ label = path32.basename(opts.input);
22701
22743
  }
22702
22744
  const r = await blenderCall(base, "/exec", { script, ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
22703
22745
  if (r.stdout?.trim()) log.plain(r.stdout.trimEnd());
@@ -22818,7 +22860,7 @@ print(f"castle: {n} objects")
22818
22860
  // src/commands/asset-new.ts
22819
22861
  import fs32 from "fs";
22820
22862
  import fsp from "fs/promises";
22821
- import path32 from "path";
22863
+ import path33 from "path";
22822
22864
  import { pathToFileURL } from "url";
22823
22865
  var EXTRA_FILES = [
22824
22866
  "genex-asset.example.json",
@@ -22912,7 +22954,7 @@ Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-buil
22912
22954
  }
22913
22955
  async function runAssetNew(options) {
22914
22956
  const log = createLogger();
22915
- const cwd = options.dir ? path32.resolve(options.dir) : process.cwd();
22957
+ const cwd = options.dir ? path33.resolve(options.dir) : process.cwd();
22916
22958
  const slug = options.assetSlug;
22917
22959
  if (!slug) {
22918
22960
  log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
@@ -22922,14 +22964,14 @@ async function runAssetNew(options) {
22922
22964
  log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
22923
22965
  return 1;
22924
22966
  }
22925
- const templateDir = path32.join(getTemplatesDir(), "asset-viewer");
22967
+ const templateDir = path33.join(getTemplatesDir(), "asset-viewer");
22926
22968
  if (!fs32.existsSync(templateDir)) {
22927
22969
  log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
22928
22970
  return 1;
22929
22971
  }
22930
- const manifestTools = await import(pathToFileURL(path32.join(templateDir, "tools", "emit-manifest.mjs")).href);
22972
+ const manifestTools = await import(pathToFileURL(path33.join(templateDir, "tools", "emit-manifest.mjs")).href);
22931
22973
  const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
22932
- const lockPath = path32.join(templateDir, "shared-files.sha256.json");
22974
+ const lockPath = path33.join(templateDir, "shared-files.sha256.json");
22933
22975
  const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
22934
22976
  const actual = hashSharedFiles(templateDir);
22935
22977
  const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
@@ -22942,7 +22984,7 @@ async function runAssetNew(options) {
22942
22984
  const triBand = parseBand(options.triBand ?? "500-8000");
22943
22985
  const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
22944
22986
  const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
22945
- const outDir = path32.resolve(cwd, options.out ?? slug);
22987
+ const outDir = path33.resolve(cwd, options.out ?? slug);
22946
22988
  if (fs32.existsSync(outDir) && fs32.readdirSync(outDir).length > 0 && !options.force) {
22947
22989
  log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
22948
22990
  return 1;
@@ -22971,24 +23013,24 @@ async function runAssetNew(options) {
22971
23013
  };
22972
23014
  await fsp.mkdir(outDir, { recursive: true });
22973
23015
  for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
22974
- const to = path32.join(outDir, rel);
22975
- await fsp.mkdir(path32.dirname(to), { recursive: true });
22976
- await fsp.copyFile(path32.join(templateDir, rel), to);
23016
+ const to = path33.join(outDir, rel);
23017
+ await fsp.mkdir(path33.dirname(to), { recursive: true });
23018
+ await fsp.copyFile(path33.join(templateDir, rel), to);
22977
23019
  }
22978
- const pkg = fillTemplate(await fsp.readFile(path32.join(templateDir, "package.json"), "utf8"), {
23020
+ const pkg = fillTemplate(await fsp.readFile(path33.join(templateDir, "package.json"), "utf8"), {
22979
23021
  slug,
22980
23022
  name,
22981
23023
  version
22982
23024
  });
22983
- await fsp.writeFile(path32.join(outDir, "package.json"), pkg, "utf8");
22984
- await fsp.writeFile(path32.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
22985
- await fsp.writeFile(path32.join(outDir, ".gitignore"), GITIGNORE, "utf8");
23025
+ await fsp.writeFile(path33.join(outDir, "package.json"), pkg, "utf8");
23026
+ await fsp.writeFile(path33.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
23027
+ await fsp.writeFile(path33.join(outDir, ".gitignore"), GITIGNORE, "utf8");
22986
23028
  await fsp.writeFile(
22987
- path32.join(outDir, "DESIGN.md"),
23029
+ path33.join(outDir, "DESIGN.md"),
22988
23030
  designDoc({ name, slug, sizeMeters, triBand, holder }),
22989
23031
  "utf8"
22990
23032
  );
22991
- const placeholder = await fsp.readFile(path32.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
23033
+ const placeholder = await fsp.readFile(path33.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
22992
23034
  const seeded = seedAssetSource(placeholder, {
22993
23035
  slug,
22994
23036
  name,
@@ -22999,8 +23041,8 @@ async function runAssetNew(options) {
22999
23041
  pascalCase
23000
23042
  });
23001
23043
  const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
23002
- await fsp.mkdir(path32.join(outDir, "src", "asset"), { recursive: true });
23003
- await fsp.writeFile(path32.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
23044
+ await fsp.mkdir(path33.join(outDir, "src", "asset"), { recursive: true });
23045
+ await fsp.writeFile(path33.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
23004
23046
  const copied = hashSharedFiles(outDir);
23005
23047
  const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
23006
23048
  if (mismatched.length) {
@@ -23008,7 +23050,7 @@ async function runAssetNew(options) {
23008
23050
  return 1;
23009
23051
  }
23010
23052
  await fsp.writeFile(
23011
- path32.join(outDir, PARITY_FILENAME),
23053
+ path33.join(outDir, PARITY_FILENAME),
23012
23054
  JSON.stringify(
23013
23055
  {
23014
23056
  note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
@@ -23052,10 +23094,10 @@ async function runAssetNew(options) {
23052
23094
  }
23053
23095
 
23054
23096
  // src/commands/tools.ts
23055
- import path34 from "path";
23097
+ import path35 from "path";
23056
23098
 
23057
23099
  // src/commands/doctor.ts
23058
- import path33 from "path";
23100
+ import path34 from "path";
23059
23101
  var LANE_ORDER = [
23060
23102
  "model",
23061
23103
  "image",
@@ -23114,7 +23156,7 @@ async function runDoctor(opts = {}) {
23114
23156
  fix: latest && isNewerVersion(latest, installed) ? "npm i -D @genex-ai/cli-demo@latest (apply at a safe moment, never mid-task)" : void 0
23115
23157
  });
23116
23158
  const apiUrl = getApiUrl(opts.apiUrl ?? workspace?.apiUrl ?? project?.apiUrl);
23117
- const token = await readUserToken(opts.envPath);
23159
+ const token = await readUserToken(opts.envPath, apiUrl);
23118
23160
  let email = null;
23119
23161
  let authState = "none";
23120
23162
  if (token) {
@@ -23336,7 +23378,7 @@ async function fetchLegalStatus(apiUrl, token) {
23336
23378
  }
23337
23379
  async function firstSkillsMarker() {
23338
23380
  for (const target of resolveAgentTargets()) {
23339
- const marker = await readSkillsMarker(path33.join(target.baseDir, "skills"));
23381
+ const marker = await readSkillsMarker(path34.join(target.baseDir, "skills"));
23340
23382
  if (marker) return marker;
23341
23383
  }
23342
23384
  return null;
@@ -23387,8 +23429,8 @@ async function runTools(opts) {
23387
23429
  let totalNew = 0;
23388
23430
  let totalUpdated = 0;
23389
23431
  for (const t of targets) {
23390
- const dest = path34.join(t.baseDir, "skills");
23391
- const { copied, updated } = await copyTemplates(path34.join(templatesDir, "skills"), dest, {
23432
+ const dest = path35.join(t.baseDir, "skills");
23433
+ const { copied, updated } = await copyTemplates(path35.join(templatesDir, "skills"), dest, {
23392
23434
  filter: (rel) => skillFamilyFilter("tools")(`skills/${rel}`)
23393
23435
  });
23394
23436
  await pruneRemovedSkills(dest, log);
@@ -23416,10 +23458,11 @@ async function runTools(opts) {
23416
23458
  await ensureLocalCli(log, process.cwd(), { skip: opts.noInstall });
23417
23459
  log.plain("");
23418
23460
  const authBaseUrl = getAuthUrl(opts.authUrl);
23419
- let token = await readUserToken(opts.envPath);
23461
+ let token = await readUserToken(opts.envPath, apiUrl);
23420
23462
  if (!token) {
23421
23463
  try {
23422
23464
  token = await authorize(apiUrl, authBaseUrl, {
23465
+ envPath: opts.envPath,
23423
23466
  log,
23424
23467
  inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
23425
23468
  });
@@ -23435,8 +23478,8 @@ async function runTools(opts) {
23435
23478
  log.dim("The workspace is set up. Re-run `genex tools` to finish authorizing.");
23436
23479
  throw markPrinted(err);
23437
23480
  }
23438
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
23439
- log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)} (${ENV_TOKEN_KEY}).`);
23481
+ const { path: tokenPath } = await writeUserToken(token, opts.envPath, apiUrl);
23482
+ log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
23440
23483
  log.plain("");
23441
23484
  }
23442
23485
  await reportToolsInit(apiUrl, token);
@@ -23448,7 +23491,7 @@ async function runTools(opts) {
23448
23491
 
23449
23492
  // src/commands/budget.ts
23450
23493
  import fs33 from "fs/promises";
23451
- import path35 from "path";
23494
+ import path36 from "path";
23452
23495
  var BUDGET_SOURCE_LINE = "Live from your account; prices can change without a deploy.";
23453
23496
  var BUDGET_APPROVAL_REQUIRED = "STOP: raising the asset allowance requires the player's explicit approval. Re-run with --user-approved only after they agreed to the number.";
23454
23497
  function internalKind(kind) {
@@ -23456,7 +23499,7 @@ function internalKind(kind) {
23456
23499
  }
23457
23500
  async function isGenexProject(cwd) {
23458
23501
  try {
23459
- await fs33.access(path35.join(cwd, ".genex"));
23502
+ await fs33.access(path36.join(cwd, ".genex"));
23460
23503
  return true;
23461
23504
  } catch {
23462
23505
  return false;
@@ -23491,13 +23534,13 @@ async function runBudget(opts = {}) {
23491
23534
  fail3(`--assets takes a whole number of credits, 0 or more (got ${String(opts.assets)}).`);
23492
23535
  return;
23493
23536
  }
23494
- const token = opts.token ?? await readUserToken(opts.envPath);
23537
+ const meta = await readProject(cwd);
23538
+ const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
23539
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
23495
23540
  if (!token) {
23496
23541
  fail3("Not authorized. Run `genex init` first to sign in.");
23497
23542
  return;
23498
23543
  }
23499
- const meta = await readProject(cwd);
23500
- const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
23501
23544
  const snapshot = await fetchCreditsSnapshot(apiUrl, token);
23502
23545
  let store;
23503
23546
  let persisted = true;