@genex-ai/cli-demo 1.11.3-dev.547 → 1.13.0-dev.549

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
@@ -1,15 +1,35 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  BLENDER_SETUP_HINT,
4
+ RENDER_MODES,
5
+ SHEET_FORMATS,
6
+ acquireSeat,
7
+ apiFetch,
8
+ blenderCall,
9
+ blenderEndpoint,
10
+ fetchSignedInEmail,
11
+ getWorkspacePath,
12
+ isRenderMode,
13
+ printedStructuredError,
14
+ readProject,
15
+ readUserToken,
16
+ readWorkspace,
17
+ restrictFilePermissions,
18
+ rotateRejectedEnv,
19
+ setWorkspaceHeader,
20
+ sheetExt,
21
+ sheetOf,
22
+ writeProject,
23
+ writeUserToken,
24
+ writeWorkspace
25
+ } from "./chunk-WXHTNSFB.js";
26
+ import {
4
27
  CLI_CHANNEL,
5
28
  DEFAULT_API_URL,
6
29
  DEFAULT_AUTH_URL,
7
- ENV_FILE_ENV,
8
30
  ENV_TOKEN_KEY,
9
- RENDER_MODES,
10
31
  STANDS,
11
- blenderCall,
12
- blenderEndpoint,
32
+ c,
13
33
  getAnimsBase,
14
34
  getAnimsCacheDir,
15
35
  getApiUrl,
@@ -18,9 +38,8 @@ import {
18
38
  getGenexDir,
19
39
  getGenexEnvPath,
20
40
  getTemplatesDir,
21
- isRenderMode,
22
41
  resolveAgentTargets
23
- } from "./chunk-ZHIP7LCA.js";
42
+ } from "./chunk-HYCSNWYX.js";
24
43
 
25
44
  // src/instrument.ts
26
45
  import * as Sentry from "@sentry/node";
@@ -128,204 +147,18 @@ import http from "http";
128
147
  import crypto from "crypto";
129
148
  import os2 from "os";
130
149
  import readline from "readline";
131
- import { spawn as spawn2 } from "child_process";
150
+ import { spawn } from "child_process";
132
151
  import { URL as URL2 } from "url";
133
152
 
134
- // src/utils/colors.ts
135
- var useColor = Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0 && process.env.TERM !== "dumb";
136
- var ESC = String.fromCharCode(27);
137
- var code = (open, close) => (s) => useColor ? `${ESC}[${open}m${s}${ESC}[${close}m` : s;
138
- var c = {
139
- bold: code(1, 22),
140
- dim: code(2, 22),
141
- red: code(31, 39),
142
- green: code(32, 39),
143
- yellow: code(33, 39),
144
- blue: code(34, 39),
145
- cyan: code(36, 39),
146
- gray: code(90, 39)
147
- };
148
-
149
- // src/lib/api.ts
150
- var CLI_VERSION_HEADER = "x-genex-cli-version";
151
- var WORKSPACE_HEADER = "x-genex-workspace";
152
- var workspaceLabel = null;
153
- function setWorkspaceHeader(label) {
154
- workspaceLabel = label;
155
- }
156
- function formatUpdateRequired(body) {
157
- const action = body.action ?? `npm i -D @genex-ai/cli-demo@${CLI_CHANNEL}`;
158
- const message = body.message ?? `Genex CLI ${body.clientVersion ?? getCliVersion()} is below the minimum supported version${body.minVersion ? ` ${body.minVersion}` : ""}.`;
159
- return [`${c.red("\u2717")} ${message}`, ` Update now \u2014 run: ${action} (then re-run this command)`];
160
- }
161
- function shortDate(iso) {
162
- const d = new Date(iso);
163
- if (Number.isNaN(d.getTime())) return iso;
164
- return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
165
- }
166
- function formatInsufficientCredits(body) {
167
- const message = body.message ?? `this generation costs ${body.price ?? "?"} credits; your balance is ${body.balance ?? 0}.`;
168
- const lines = [`${c.red("\u2717")} Out of credits \u2014 ${lowerFirst(message)}`];
169
- if (body.refillAt && body.refillTo) {
170
- lines.push(` Credits refill to ${body.refillTo} on ${shortDate(body.refillAt)}.`);
171
- }
172
- if (body.url) lines.push(` Get more or check your balance: ${body.url}`);
173
- return lines;
174
- }
175
- function formatVerificationRequired(body) {
176
- const lines = [
177
- `${c.red("\u2717")} Email not verified \u2014 verify your email to unlock your free generation credits.`
178
- ];
179
- if (body.url) lines.push(` Verify here: ${body.url} (then re-run this command)`);
180
- return lines;
181
- }
182
- function lowerFirst(s) {
183
- return s ? s[0].toLowerCase() + s.slice(1) : s;
184
- }
185
- var structuredPrinted = /* @__PURE__ */ new WeakSet();
186
- function printedStructuredError(res) {
187
- return structuredPrinted.has(res);
188
- }
189
- async function apiFetch(url, init2 = {}) {
190
- const headers = new Headers(init2.headers);
191
- if (!headers.has(CLI_VERSION_HEADER)) headers.set(CLI_VERSION_HEADER, getCliVersion());
192
- if (workspaceLabel && !headers.has(WORKSPACE_HEADER)) {
193
- headers.set(WORKSPACE_HEADER, workspaceLabel);
194
- }
195
- const res = await fetch(url, { ...init2, headers });
196
- if (res.status === 426) {
197
- try {
198
- const body = await res.clone().json();
199
- if (body?.error === "cli_update_required") {
200
- for (const line of formatUpdateRequired(body)) process.stderr.write(line + "\n");
201
- }
202
- } catch {
203
- }
204
- }
205
- if (res.status === 402) {
206
- try {
207
- const body = await res.clone().json();
208
- if (body?.error === "insufficient_credits") {
209
- for (const line of formatInsufficientCredits(body)) process.stderr.write(line + "\n");
210
- structuredPrinted.add(res);
211
- }
212
- } catch {
213
- }
214
- }
215
- if (res.status === 403) {
216
- try {
217
- const body = await res.clone().json();
218
- if (body?.error === "email_verification_required") {
219
- for (const line of formatVerificationRequired(body)) process.stderr.write(line + "\n");
220
- structuredPrinted.add(res);
221
- }
222
- } catch {
223
- }
224
- }
225
- if (res.status === 503) {
226
- try {
227
- const body = await res.clone().json();
228
- if (body?.error === "generation_paused") {
229
- process.stderr.write(
230
- `${c.red("\u2717")} ${body.message ?? "Generation is temporarily paused platform-wide. Try again later."}
231
- `
232
- );
233
- structuredPrinted.add(res);
234
- }
235
- } catch {
236
- }
237
- }
238
- return res;
239
- }
240
- async function fetchSignedInEmail(apiUrl, token) {
241
- try {
242
- const res = await apiFetch(`${apiUrl}/api/auth/get-session`, {
243
- headers: { Authorization: `Bearer ${token}` }
244
- });
245
- if (!res.ok) return null;
246
- const data = await res.json().catch(() => null);
247
- return data?.user?.email ?? null;
248
- } catch {
249
- return null;
250
- }
251
- }
252
-
253
153
  // src/lib/pending-auth.ts
254
- import fs2 from "fs/promises";
255
- import path2 from "path";
256
-
257
- // src/lib/env.ts
258
154
  import fs from "fs/promises";
259
155
  import path from "path";
260
- import { spawn } from "child_process";
261
- async function writeEnvVar(envPath, key, value) {
262
- let content = "";
263
- let existed = false;
264
- try {
265
- content = await fs.readFile(envPath, "utf8");
266
- existed = true;
267
- } catch {
268
- }
269
- const assignment = `${key}=${formatValue(value)}`;
270
- const keyPattern = new RegExp(
271
- `^(\\s*export\\s+)?${escapeRegExp(key)}=.*$`,
272
- "gm"
273
- );
274
- let next;
275
- let mode;
276
- if (keyPattern.test(content)) {
277
- next = content.replace(keyPattern, assignment);
278
- mode = "updated";
279
- } else {
280
- let prefix = content;
281
- if (prefix.length > 0 && !prefix.endsWith("\n")) prefix += "\n";
282
- next = prefix + assignment + "\n";
283
- mode = existed ? "appended" : "created";
284
- }
285
- await fs.mkdir(path.dirname(envPath), { recursive: true });
286
- await fs.writeFile(envPath, next, { mode: 384 });
287
- await restrictFilePermissions(envPath);
288
- return { mode, path: envPath };
289
- }
290
- async function restrictFilePermissions(filePath) {
291
- if (process.platform !== "win32") {
292
- await fs.chmod(filePath, 384).catch(() => {
293
- });
294
- return;
295
- }
296
- const user = process.env.USERNAME ?? process.env.USER;
297
- if (!user) return;
298
- await new Promise((resolve) => {
299
- try {
300
- const child = spawn(
301
- "icacls",
302
- [filePath, "/inheritance:r", "/grant:r", `${user}:F`],
303
- { stdio: "ignore" }
304
- );
305
- child.on("error", () => resolve());
306
- child.on("close", () => resolve());
307
- } catch {
308
- resolve();
309
- }
310
- });
311
- }
312
- function formatValue(value) {
313
- if (/[\s#"'$`\\]/.test(value)) {
314
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
315
- }
316
- return value;
317
- }
318
- function escapeRegExp(s) {
319
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
320
- }
321
-
322
- // src/lib/pending-auth.ts
323
156
  function pendingAuthPath() {
324
- return path2.join(getGenexDir(), "pending-auth.json");
157
+ return path.join(getGenexDir(), "pending-auth.json");
325
158
  }
326
159
  async function readPendingAuth(apiUrl) {
327
160
  try {
328
- const raw = JSON.parse(await fs2.readFile(pendingAuthPath(), "utf8"));
161
+ const raw = JSON.parse(await fs.readFile(pendingAuthPath(), "utf8"));
329
162
  if (!raw?.deviceCode || !raw.userCode || raw.apiUrl !== apiUrl) return null;
330
163
  if (Date.now() >= raw.expiresAt) return null;
331
164
  return raw;
@@ -335,12 +168,12 @@ async function readPendingAuth(apiUrl) {
335
168
  }
336
169
  async function writePendingAuth(pending) {
337
170
  const file = pendingAuthPath();
338
- await fs2.mkdir(path2.dirname(file), { recursive: true });
339
- await fs2.writeFile(file, JSON.stringify(pending, null, 2), { mode: 384 });
171
+ await fs.mkdir(path.dirname(file), { recursive: true });
172
+ await fs.writeFile(file, JSON.stringify(pending, null, 2), { mode: 384 });
340
173
  await restrictFilePermissions(file);
341
174
  }
342
175
  async function clearPendingAuth() {
343
- await fs2.rm(pendingAuthPath(), { force: true }).catch(() => {
176
+ await fs.rm(pendingAuthPath(), { force: true }).catch(() => {
344
177
  });
345
178
  }
346
179
 
@@ -360,8 +193,8 @@ var AuthPendingError = class extends Error {
360
193
  this.verifyUrl = verifyUrl;
361
194
  }
362
195
  };
363
- function formatUserCode(code2) {
364
- return code2.length === 8 ? `${code2.slice(0, 4)}-${code2.slice(4)}` : code2;
196
+ function formatUserCode(code) {
197
+ return code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
365
198
  }
366
199
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
367
200
  async function authorize(apiBaseUrl, authBaseUrl, options) {
@@ -709,7 +542,7 @@ function openBrowser(url, onError = () => {
709
542
  }
710
543
  }
711
544
  try {
712
- const child = spawn2(command, args, {
545
+ const child = spawn(command, args, {
713
546
  stdio: "ignore",
714
547
  detached: true
715
548
  });
@@ -747,88 +580,6 @@ function tokenizeCommand(input) {
747
580
  return tokens2;
748
581
  }
749
582
 
750
- // src/lib/store.ts
751
- import fs3 from "fs/promises";
752
- import path3 from "path";
753
- function getProjectMetadataPath(cwd = process.cwd()) {
754
- return path3.join(cwd, ".genex", "project.json");
755
- }
756
- function getWorkspacePath(cwd = process.cwd()) {
757
- return path3.join(cwd, ".genex", "workspace.json");
758
- }
759
- async function readWorkspace(cwd = process.cwd()) {
760
- try {
761
- const raw = await fs3.readFile(getWorkspacePath(cwd), "utf8");
762
- return JSON.parse(raw);
763
- } catch {
764
- return null;
765
- }
766
- }
767
- async function writeWorkspace(meta, cwd = process.cwd()) {
768
- const file = getWorkspacePath(cwd);
769
- await fs3.mkdir(path3.dirname(file), { recursive: true });
770
- await fs3.writeFile(file, JSON.stringify(meta, null, 2) + "\n", { mode: 384 });
771
- await fs3.chmod(file, 384).catch(() => {
772
- });
773
- return { path: file };
774
- }
775
- async function writeUserToken(token, envPath) {
776
- const { path: written } = await writeEnvVar(getGenexEnvPath(envPath), ENV_TOKEN_KEY, token);
777
- return { path: written };
778
- }
779
- async function rotateRejectedEnv(envPath) {
780
- const file = getGenexEnvPath(envPath);
781
- const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
782
- const aside = `${file}.rejected-${stamp}`;
783
- try {
784
- await fs3.rename(file, aside);
785
- return aside;
786
- } catch {
787
- return null;
788
- }
789
- }
790
- async function readUserToken(envPath) {
791
- const fromGenex = await readTokenFromFile(getGenexEnvPath(envPath));
792
- if (fromGenex) return fromGenex;
793
- if (!envPath && !process.env[ENV_FILE_ENV]) {
794
- return readTokenFromFile(path3.join(process.cwd(), ".env"));
795
- }
796
- return null;
797
- }
798
- async function readTokenFromFile(file) {
799
- let content;
800
- try {
801
- content = await fs3.readFile(file, "utf8");
802
- } catch {
803
- return null;
804
- }
805
- const m = content.match(/^\s*(?:export\s+)?GENEX_TOKEN=(.*)$/m);
806
- if (!m) return null;
807
- return stripQuotes(m[1].trim()) || null;
808
- }
809
- function stripQuotes(v) {
810
- if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'")) {
811
- return v.slice(1, -1);
812
- }
813
- return v;
814
- }
815
- async function readProject(cwd = process.cwd()) {
816
- try {
817
- const raw = await fs3.readFile(getProjectMetadataPath(cwd), "utf8");
818
- return JSON.parse(raw);
819
- } catch {
820
- return null;
821
- }
822
- }
823
- async function writeProject(meta, cwd = process.cwd()) {
824
- const file = getProjectMetadataPath(cwd);
825
- await fs3.mkdir(path3.dirname(file), { recursive: true });
826
- await fs3.writeFile(file, JSON.stringify(meta, null, 2) + "\n", { mode: 384 });
827
- await fs3.chmod(file, 384).catch(() => {
828
- });
829
- return { path: file };
830
- }
831
-
832
583
  // src/utils/logger.ts
833
584
  function createLogger(opts = {}) {
834
585
  const out = (s) => {
@@ -889,14 +640,14 @@ async function runAuth(opts) {
889
640
  }
890
641
 
891
642
  // src/commands/init.ts
892
- import fs9 from "fs/promises";
893
- import path9 from "path";
643
+ import fs7 from "fs/promises";
644
+ import path7 from "path";
894
645
 
895
646
  // src/lib/copy-templates.ts
896
- import fs4 from "fs/promises";
897
- import path4 from "path";
647
+ import fs2 from "fs/promises";
648
+ import path2 from "path";
898
649
  function isGenexManaged(rel) {
899
- return rel.split(path4.sep).some((seg) => seg.startsWith("genex"));
650
+ return rel.split(path2.sep).some((seg) => seg.startsWith("genex"));
900
651
  }
901
652
  async function copyTemplates(srcDir, destDir, opts = {}) {
902
653
  const result = { copied: [], updated: [], skipped: [] };
@@ -904,15 +655,15 @@ async function copyTemplates(srcDir, destDir, opts = {}) {
904
655
  return result;
905
656
  }
906
657
  async function walk(rootSrc, src, dest, opts, result) {
907
- const entries = await fs4.readdir(src, { withFileTypes: true });
658
+ const entries = await fs2.readdir(src, { withFileTypes: true });
908
659
  for (const entry of entries) {
909
- const srcPath = path4.join(src, entry.name);
910
- const destPath = path4.join(dest, entry.name);
911
- const rel = path4.relative(rootSrc, srcPath);
660
+ const srcPath = path2.join(src, entry.name);
661
+ const destPath = path2.join(dest, entry.name);
662
+ const rel = path2.relative(rootSrc, srcPath);
912
663
  if (opts.exclude?.includes(rel)) continue;
913
664
  if (opts.filter && !opts.filter(rel)) continue;
914
665
  if (entry.isDirectory()) {
915
- await fs4.mkdir(destPath, { recursive: true });
666
+ await fs2.mkdir(destPath, { recursive: true });
916
667
  await walk(rootSrc, srcPath, destPath, opts, result);
917
668
  continue;
918
669
  }
@@ -925,15 +676,15 @@ async function walk(rootSrc, src, dest, opts, result) {
925
676
  result.skipped.push(rel);
926
677
  continue;
927
678
  }
928
- await fs4.mkdir(path4.dirname(destPath), { recursive: true });
929
- await fs4.copyFile(srcPath, destPath);
679
+ await fs2.mkdir(path2.dirname(destPath), { recursive: true });
680
+ await fs2.copyFile(srcPath, destPath);
930
681
  result.copied.push(rel);
931
682
  if (present) result.updated.push(rel);
932
683
  }
933
684
  }
934
685
  async function exists(p) {
935
686
  try {
936
- await fs4.access(p);
687
+ await fs2.access(p);
937
688
  return true;
938
689
  } catch {
939
690
  return false;
@@ -941,9 +692,9 @@ async function exists(p) {
941
692
  }
942
693
 
943
694
  // src/lib/agents-contract.ts
944
- import fs5 from "fs/promises";
695
+ import fs3 from "fs/promises";
945
696
  import os3 from "os";
946
- import path5 from "path";
697
+ import path3 from "path";
947
698
  var CONTRACT_BEGIN = "<!-- genex:contract:begin (managed by genex \u2014 edits inside this block are overwritten on sync) -->";
948
699
  var CONTRACT_END = "<!-- genex:contract:end -->";
949
700
  var GENEX_CONTRACT_BLOCK = `${CONTRACT_BEGIN}
@@ -1008,32 +759,32 @@ ${block}
1008
759
  async function writeAgentsContract(projectDir, contractBlock = GENEX_CONTRACT_BLOCK) {
1009
760
  let changed = false;
1010
761
  try {
1011
- const agentsPath = path5.join(projectDir, "AGENTS.md");
762
+ const agentsPath = path3.join(projectDir, "AGENTS.md");
1012
763
  let existing = null;
1013
764
  try {
1014
- existing = await fs5.readFile(agentsPath, "utf8");
765
+ existing = await fs3.readFile(agentsPath, "utf8");
1015
766
  } catch {
1016
767
  existing = null;
1017
768
  }
1018
769
  const next = mergeContractBlock(existing, contractBlock);
1019
770
  if (next !== existing) {
1020
- await fs5.writeFile(agentsPath, next, "utf8");
771
+ await fs3.writeFile(agentsPath, next, "utf8");
1021
772
  changed = true;
1022
773
  }
1023
- const claudePath = path5.join(projectDir, "CLAUDE.md");
774
+ const claudePath = path3.join(projectDir, "CLAUDE.md");
1024
775
  let claude = null;
1025
776
  try {
1026
- claude = await fs5.readFile(claudePath, "utf8");
777
+ claude = await fs3.readFile(claudePath, "utf8");
1027
778
  } catch {
1028
779
  claude = null;
1029
780
  }
1030
781
  if (claude === null) {
1031
- await fs5.writeFile(claudePath, `${CLAUDE_IMPORT_LINE}
782
+ await fs3.writeFile(claudePath, `${CLAUDE_IMPORT_LINE}
1032
783
  `, "utf8");
1033
784
  changed = true;
1034
785
  } else if (!claude.split("\n").some((line) => line.trim() === CLAUDE_IMPORT_LINE)) {
1035
786
  const sep = claude.endsWith("\n") ? "" : "\n";
1036
- await fs5.writeFile(claudePath, `${claude}${sep}
787
+ await fs3.writeFile(claudePath, `${claude}${sep}
1037
788
  ${CLAUDE_IMPORT_LINE}
1038
789
  `, "utf8");
1039
790
  changed = true;
@@ -1047,24 +798,24 @@ async function writeToolsContract(projectDir) {
1047
798
  }
1048
799
  async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1049
800
  const findings = [];
1050
- const stop = path5.resolve(stopDir);
1051
- let dir = path5.dirname(path5.resolve(projectDir));
801
+ const stop = path3.resolve(stopDir);
802
+ let dir = path3.dirname(path3.resolve(projectDir));
1052
803
  for (let depth = 0; depth < 20; depth++) {
1053
804
  try {
1054
805
  let isWorkspace = false;
1055
806
  for (const marker of ["project.json", "workspace.json"]) {
1056
807
  try {
1057
- await fs5.access(path5.join(dir, ".genex", marker));
808
+ await fs3.access(path3.join(dir, ".genex", marker));
1058
809
  isWorkspace = true;
1059
810
  break;
1060
811
  } catch {
1061
812
  }
1062
813
  }
1063
814
  if (!isWorkspace) {
1064
- const agentsPath = path5.join(dir, "AGENTS.md");
815
+ const agentsPath = path3.join(dir, "AGENTS.md");
1065
816
  let content = null;
1066
817
  try {
1067
- content = await fs5.readFile(agentsPath, "utf8");
818
+ content = await fs3.readFile(agentsPath, "utf8");
1068
819
  } catch {
1069
820
  }
1070
821
  if (content !== null && CONTRACT_REGION.test(content)) {
@@ -1073,11 +824,11 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1073
824
  remainder = remainder.replace(CONTRACT_REGION, "");
1074
825
  }
1075
826
  if (remainder.trim() === "") {
1076
- await fs5.unlink(agentsPath);
827
+ await fs3.unlink(agentsPath);
1077
828
  try {
1078
- const claudePath = path5.join(dir, "CLAUDE.md");
1079
- if ((await fs5.readFile(claudePath, "utf8")).trim() === CLAUDE_IMPORT_LINE) {
1080
- await fs5.unlink(claudePath);
829
+ const claudePath = path3.join(dir, "CLAUDE.md");
830
+ if ((await fs3.readFile(claudePath, "utf8")).trim() === CLAUDE_IMPORT_LINE) {
831
+ await fs3.unlink(claudePath);
1081
832
  }
1082
833
  } catch {
1083
834
  }
@@ -1090,7 +841,7 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1090
841
  } catch {
1091
842
  }
1092
843
  if (dir === stop) break;
1093
- const parent = path5.dirname(dir);
844
+ const parent = path3.dirname(dir);
1094
845
  if (parent === dir) break;
1095
846
  dir = parent;
1096
847
  }
@@ -1099,7 +850,7 @@ async function healAncestorContracts(projectDir, stopDir = os3.homedir()) {
1099
850
  async function healAncestorContractsAndReport(log, projectDir) {
1100
851
  try {
1101
852
  for (const f of await healAncestorContracts(projectDir)) {
1102
- const file = path5.join(f.dir, "AGENTS.md");
853
+ const file = path3.join(f.dir, "AGENTS.md");
1103
854
  if (f.removed) {
1104
855
  log.plain(
1105
856
  `\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.`
@@ -1115,9 +866,9 @@ async function healAncestorContractsAndReport(log, projectDir) {
1115
866
  }
1116
867
 
1117
868
  // src/lib/updates.ts
1118
- import fs6 from "fs/promises";
869
+ import fs4 from "fs/promises";
1119
870
  import os4 from "os";
1120
- import path6 from "path";
871
+ import path4 from "path";
1121
872
 
1122
873
  // src/lib/workspace.ts
1123
874
  async function workspaceMode(cwd = process.cwd()) {
@@ -1175,7 +926,7 @@ function isNewerVersion(a, b) {
1175
926
  var SKILLS_VERSION_MARKER = "genex-skills-version.json";
1176
927
  async function readSkillsMarker(skillsDir) {
1177
928
  try {
1178
- const raw = await fs6.readFile(path6.join(skillsDir, SKILLS_VERSION_MARKER), "utf8");
929
+ const raw = await fs4.readFile(path4.join(skillsDir, SKILLS_VERSION_MARKER), "utf8");
1179
930
  const parsed = JSON.parse(raw);
1180
931
  return typeof parsed.version === "string" ? parsed.version : null;
1181
932
  } catch {
@@ -1183,15 +934,15 @@ async function readSkillsMarker(skillsDir) {
1183
934
  }
1184
935
  }
1185
936
  async function writeSkillsMarker(skillsDir, version = getCliVersion()) {
1186
- await fs6.mkdir(skillsDir, { recursive: true });
1187
- await fs6.writeFile(
1188
- path6.join(skillsDir, SKILLS_VERSION_MARKER),
937
+ await fs4.mkdir(skillsDir, { recursive: true });
938
+ await fs4.writeFile(
939
+ path4.join(skillsDir, SKILLS_VERSION_MARKER),
1189
940
  JSON.stringify({ version, syncedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
1190
941
  );
1191
942
  }
1192
943
  async function hasGenexSkills(skillsDir) {
1193
944
  try {
1194
- const entries = await fs6.readdir(skillsDir);
945
+ const entries = await fs4.readdir(skillsDir);
1195
946
  return entries.some((name) => name.startsWith("genex-"));
1196
947
  } catch {
1197
948
  return false;
@@ -1254,14 +1005,14 @@ var REMOVED_SKILLS = [
1254
1005
  async function pruneRemovedSkills(skillsDir, log) {
1255
1006
  const removed = [];
1256
1007
  for (const name of REMOVED_SKILLS) {
1257
- const target = path6.join(skillsDir, name);
1008
+ const target = path4.join(skillsDir, name);
1258
1009
  try {
1259
- await fs6.access(target);
1010
+ await fs4.access(target);
1260
1011
  } catch {
1261
1012
  continue;
1262
1013
  }
1263
1014
  try {
1264
- await fs6.rm(target, { recursive: true });
1015
+ await fs4.rm(target, { recursive: true });
1265
1016
  removed.push(name);
1266
1017
  } catch {
1267
1018
  }
@@ -1276,16 +1027,16 @@ async function pruneRemovedSkills(skillsDir, log) {
1276
1027
  async function pruneToolCards(log, overrides) {
1277
1028
  let removedAny = false;
1278
1029
  for (const target of resolveAgentTargets(overrides ?? {})) {
1279
- const skillsDir = path6.join(target.baseDir, "skills");
1030
+ const skillsDir = path4.join(target.baseDir, "skills");
1280
1031
  for (const name of TOOL_SKILLS) {
1281
- const dir = path6.join(skillsDir, name);
1032
+ const dir = path4.join(skillsDir, name);
1282
1033
  try {
1283
- await fs6.access(dir);
1034
+ await fs4.access(dir);
1284
1035
  } catch {
1285
1036
  continue;
1286
1037
  }
1287
1038
  try {
1288
- await fs6.rm(dir, { recursive: true });
1039
+ await fs4.rm(dir, { recursive: true });
1289
1040
  removedAny = true;
1290
1041
  } catch {
1291
1042
  }
@@ -1299,15 +1050,15 @@ async function pruneToolCards(log, overrides) {
1299
1050
  return removedAny;
1300
1051
  }
1301
1052
  async function syncSkillsForTarget(target, templatesDir = getTemplatesDir(), version = getCliVersion(), log, family = "game") {
1302
- const skillsDir = path6.join(target.baseDir, "skills");
1053
+ const skillsDir = path4.join(target.baseDir, "skills");
1303
1054
  if (!await hasGenexSkills(skillsDir)) return false;
1304
1055
  if (await readSkillsMarker(skillsDir) === version) return false;
1305
- const src = target.full ? templatesDir : path6.join(templatesDir, "skills");
1056
+ const src = target.full ? templatesDir : path4.join(templatesDir, "skills");
1306
1057
  const dest = target.full ? target.baseDir : skillsDir;
1307
1058
  const familyFilter = skillFamilyFilter(family);
1308
1059
  const filter = target.full ? familyFilter : (rel) => familyFilter(`skills/${rel}`);
1309
1060
  await copyTemplates(src, dest, {
1310
- exclude: ["controllers", "motion", "asset-viewer"],
1061
+ exclude: ["controllers", "motion", "asset-viewer", "blender-service"],
1311
1062
  filter
1312
1063
  });
1313
1064
  await pruneRemovedSkills(skillsDir, log);
@@ -1318,34 +1069,34 @@ async function cleanupLegacyGlobalSkills(log) {
1318
1069
  try {
1319
1070
  const home = os4.homedir();
1320
1071
  const [realCwd, realHome] = await Promise.all([
1321
- fs6.realpath(process.cwd()).catch(() => path6.resolve(process.cwd())),
1322
- fs6.realpath(home).catch(() => path6.resolve(home))
1072
+ fs4.realpath(process.cwd()).catch(() => path4.resolve(process.cwd())),
1073
+ fs4.realpath(home).catch(() => path4.resolve(home))
1323
1074
  ]);
1324
1075
  if (realCwd === realHome) return false;
1325
1076
  let removed = false;
1326
1077
  for (const dirName of [".claude", ".codex", ".cursor"]) {
1327
- const skillsDir = path6.join(home, dirName, "skills");
1078
+ const skillsDir = path4.join(home, dirName, "skills");
1328
1079
  let entries = [];
1329
1080
  try {
1330
- entries = await fs6.readdir(skillsDir);
1081
+ entries = await fs4.readdir(skillsDir);
1331
1082
  } catch {
1332
1083
  continue;
1333
1084
  }
1334
1085
  for (const name of entries) {
1335
1086
  if (!name.startsWith("genex-")) continue;
1336
1087
  try {
1337
- await fs6.rm(path6.join(skillsDir, name), { recursive: true });
1088
+ await fs4.rm(path4.join(skillsDir, name), { recursive: true });
1338
1089
  removed = true;
1339
1090
  } catch {
1340
1091
  }
1341
1092
  }
1342
1093
  }
1343
1094
  for (const rel of [
1344
- path6.join("agents", "genex-helper.md"),
1345
- path6.join("commands", "genex-status.md")
1095
+ path4.join("agents", "genex-helper.md"),
1096
+ path4.join("commands", "genex-status.md")
1346
1097
  ]) {
1347
1098
  try {
1348
- await fs6.rm(path6.join(home, ".claude", rel));
1099
+ await fs4.rm(path4.join(home, ".claude", rel));
1349
1100
  removed = true;
1350
1101
  } catch {
1351
1102
  }
@@ -1403,7 +1154,7 @@ var PUBLISHED_PACKAGES = [
1403
1154
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1404
1155
  var REGISTRY_TIMEOUT_MS = 1500;
1405
1156
  function getUpdateCachePath() {
1406
- return path6.join(getGenexDir(), "update-check.json");
1157
+ return path4.join(getGenexDir(), "update-check.json");
1407
1158
  }
1408
1159
  function isCacheFresh(cache, nowMs) {
1409
1160
  const at = Date.parse(cache.checkedAt);
@@ -1416,7 +1167,7 @@ function wasRecentlyNotified(cache, name, latest, nowMs) {
1416
1167
  }
1417
1168
  async function readUpdateCache() {
1418
1169
  try {
1419
- const raw = await fs6.readFile(getUpdateCachePath(), "utf8");
1170
+ const raw = await fs4.readFile(getUpdateCachePath(), "utf8");
1420
1171
  const parsed = JSON.parse(raw);
1421
1172
  if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "object") return null;
1422
1173
  return parsed;
@@ -1455,8 +1206,8 @@ function startUpdateCheck() {
1455
1206
  ...cached?.notified ? { notified: cached.notified } : {},
1456
1207
  ...cached?.notifiedAt ? { notifiedAt: cached.notifiedAt } : {}
1457
1208
  };
1458
- await fs6.mkdir(getGenexDir(), { recursive: true });
1459
- await fs6.writeFile(getUpdateCachePath(), JSON.stringify(next, null, 2) + "\n");
1209
+ await fs4.mkdir(getGenexDir(), { recursive: true });
1210
+ await fs4.writeFile(getUpdateCachePath(), JSON.stringify(next, null, 2) + "\n");
1460
1211
  return next;
1461
1212
  } catch {
1462
1213
  return null;
@@ -1465,7 +1216,7 @@ function startUpdateCheck() {
1465
1216
  }
1466
1217
  async function installedPackageVersion(cwd, name) {
1467
1218
  try {
1468
- const raw = await fs6.readFile(path6.join(cwd, "node_modules", name, "package.json"), "utf8");
1219
+ const raw = await fs4.readFile(path4.join(cwd, "node_modules", name, "package.json"), "utf8");
1469
1220
  const pkg = JSON.parse(raw);
1470
1221
  return typeof pkg.version === "string" ? pkg.version : null;
1471
1222
  } catch {
@@ -1512,8 +1263,8 @@ async function recordNotified(cache, announced, cachePath) {
1512
1263
  notified: { ...cache.notified ?? {}, ...announced },
1513
1264
  notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1514
1265
  };
1515
- await fs6.mkdir(path6.dirname(cachePath), { recursive: true });
1516
- await fs6.writeFile(cachePath, JSON.stringify(next, null, 2) + "\n");
1266
+ await fs4.mkdir(path4.dirname(cachePath), { recursive: true });
1267
+ await fs4.writeFile(cachePath, JSON.stringify(next, null, 2) + "\n");
1517
1268
  } catch {
1518
1269
  }
1519
1270
  }
@@ -1612,13 +1363,13 @@ async function fetchProjectStatus(apiUrl, token, slug) {
1612
1363
  }
1613
1364
 
1614
1365
  // src/lib/ssh.ts
1615
- import fs7 from "fs/promises";
1616
- import path7 from "path";
1366
+ import fs5 from "fs/promises";
1367
+ import path5 from "path";
1617
1368
  async function writeGitignore(dir, log) {
1618
- const file = path7.join(dir, ".gitignore");
1369
+ const file = path5.join(dir, ".gitignore");
1619
1370
  let content = "";
1620
1371
  try {
1621
- content = await fs7.readFile(file, "utf8");
1372
+ content = await fs5.readFile(file, "utf8");
1622
1373
  } catch {
1623
1374
  }
1624
1375
  const present = new Set(content.split("\n").map((l) => l.trim()));
@@ -1628,7 +1379,7 @@ async function writeGitignore(dir, log) {
1628
1379
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
1629
1380
  if (!content.trim()) next += "# genex local metadata + secrets \u2014 never publish\n";
1630
1381
  next += toAdd.join("\n") + "\n";
1631
- await fs7.writeFile(file, next);
1382
+ await fs5.writeFile(file, next);
1632
1383
  log.dim(`Updated .gitignore (${toAdd.join(", ")}).`);
1633
1384
  }
1634
1385
  var LFS_PATTERNS = [
@@ -1658,10 +1409,10 @@ var LFS_PATTERNS = [
1658
1409
  "*.webm"
1659
1410
  ];
1660
1411
  async function writeGitattributes(dir, log) {
1661
- const file = path7.join(dir, ".gitattributes");
1412
+ const file = path5.join(dir, ".gitattributes");
1662
1413
  let content = "";
1663
1414
  try {
1664
- content = await fs7.readFile(file, "utf8");
1415
+ content = await fs5.readFile(file, "utf8");
1665
1416
  } catch {
1666
1417
  }
1667
1418
  const present = new Set(content.split("\n").map((l) => l.trim().split(/\s+/)[0]));
@@ -1671,13 +1422,13 @@ async function writeGitattributes(dir, log) {
1671
1422
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
1672
1423
  if (!content.trim()) next += "# Binary game assets go to Git LFS (R2) \u2014 keeps the source push small\n";
1673
1424
  next += toAdd.map((p) => `${p} filter=lfs diff=lfs merge=lfs -text`).join("\n") + "\n";
1674
- await fs7.writeFile(file, next);
1425
+ await fs5.writeFile(file, next);
1675
1426
  log.dim(`Updated .gitattributes (${toAdd.length} binary globs \u2192 Git LFS).`);
1676
1427
  }
1677
1428
 
1678
1429
  // src/lib/game-config.ts
1679
- import fs8 from "fs/promises";
1680
- import path8 from "path";
1430
+ import fs6 from "fs/promises";
1431
+ import path6 from "path";
1681
1432
  var DEFAULT_DASHBOARD_ORIGIN = new URL(DEFAULT_AUTH_URL).origin;
1682
1433
  function renderGenexConfig(slug) {
1683
1434
  return `// src/genex.config.ts \u2014 written by \`genex init\`. DO NOT hardcode URLs here.
@@ -1734,7 +1485,7 @@ ${overrides.join("\n")}
1734
1485
  }
1735
1486
  async function writeIfAbsent(file, content, log) {
1736
1487
  try {
1737
- await fs8.writeFile(file, content, { flag: "wx" });
1488
+ await fs6.writeFile(file, content, { flag: "wx" });
1738
1489
  log.dim(` wrote ${c.cyan(file)}`);
1739
1490
  return true;
1740
1491
  } catch (err) {
@@ -1748,7 +1499,7 @@ async function writeIfAbsent(file, content, log) {
1748
1499
  async function ensureEnvVar(file, key, value, log) {
1749
1500
  let existing = null;
1750
1501
  try {
1751
- existing = await fs8.readFile(file, "utf8");
1502
+ existing = await fs6.readFile(file, "utf8");
1752
1503
  } catch {
1753
1504
  existing = null;
1754
1505
  }
@@ -1759,23 +1510,23 @@ async function ensureEnvVar(file, key, value, log) {
1759
1510
  return false;
1760
1511
  }
1761
1512
  const sep = existing.endsWith("\n") || existing === "" ? "" : "\n";
1762
- await fs8.writeFile(file, `${existing}${sep}${key}=${value}
1513
+ await fs6.writeFile(file, `${existing}${sep}${key}=${value}
1763
1514
  `, "utf8");
1764
1515
  log.dim(` appended ${c.cyan(`${key}=${value}`)} to ${c.cyan(file)}`);
1765
1516
  return true;
1766
1517
  }
1767
- await fs8.writeFile(file, `${key}=${value}
1518
+ await fs6.writeFile(file, `${key}=${value}
1768
1519
  `, "utf8");
1769
1520
  log.dim(` wrote ${c.cyan(file)} (${key})`);
1770
1521
  return true;
1771
1522
  }
1772
1523
  async function writeGameConfigFiles(meta, log, cwd = process.cwd()) {
1773
- await fs8.mkdir(path8.join(cwd, "src"), { recursive: true });
1774
- await writeIfAbsent(path8.join(cwd, "src", "genex.config.ts"), renderGenexConfig(meta.slug), log);
1775
- await writeIfAbsent(path8.join(cwd, ".env"), renderSlugEnv(meta.slug), log);
1524
+ await fs6.mkdir(path6.join(cwd, "src"), { recursive: true });
1525
+ await writeIfAbsent(path6.join(cwd, "src", "genex.config.ts"), renderGenexConfig(meta.slug), log);
1526
+ await writeIfAbsent(path6.join(cwd, ".env"), renderSlugEnv(meta.slug), log);
1776
1527
  const overrides = renderDevOverrides(meta);
1777
1528
  if (overrides) {
1778
- await writeIfAbsent(path8.join(cwd, ".env.development.local"), overrides, log);
1529
+ await writeIfAbsent(path6.join(cwd, ".env.development.local"), overrides, log);
1779
1530
  }
1780
1531
  }
1781
1532
 
@@ -1808,7 +1559,7 @@ var TOOLING_ENTRIES = /* @__PURE__ */ new Set([
1808
1559
  ]);
1809
1560
  async function listPreexistingEntries(dir) {
1810
1561
  try {
1811
- return (await fs9.readdir(dir)).filter((name) => !TOOLING_ENTRIES.has(name)).sort();
1562
+ return (await fs7.readdir(dir)).filter((name) => !TOOLING_ENTRIES.has(name)).sort();
1812
1563
  } catch {
1813
1564
  return [];
1814
1565
  }
@@ -1860,15 +1611,15 @@ async function runInit(opts) {
1860
1611
  let totalNew = 0;
1861
1612
  let totalUpdated = 0;
1862
1613
  for (const t of targets) {
1863
- const src = t.full ? templatesDir : path9.join(templatesDir, "skills");
1864
- const dest = t.full ? t.baseDir : path9.join(t.baseDir, "skills");
1614
+ const src = t.full ? templatesDir : path7.join(templatesDir, "skills");
1615
+ const dest = t.full ? t.baseDir : path7.join(t.baseDir, "skills");
1865
1616
  const { copied, updated } = await copyTemplates(src, dest, {
1866
1617
  force: opts.force,
1867
- exclude: ["controllers", "motion", "asset-viewer"],
1618
+ exclude: ["controllers", "motion", "asset-viewer", "blender-service"],
1868
1619
  filter: t.full ? skillFamilyFilter("game") : (rel) => skillFamilyFilter("game")(`skills/${rel}`)
1869
1620
  });
1870
- await pruneRemovedSkills(path9.join(t.baseDir, "skills"), log);
1871
- await writeSkillsMarker(path9.join(t.baseDir, "skills"));
1621
+ await pruneRemovedSkills(path7.join(t.baseDir, "skills"), log);
1622
+ await writeSkillsMarker(path7.join(t.baseDir, "skills"));
1872
1623
  const added = copied.length - updated.length;
1873
1624
  totalNew += added;
1874
1625
  totalUpdated += updated.length;
@@ -1919,7 +1670,7 @@ async function runInit(opts) {
1919
1670
  if (email) log.plain(` signed in as ${c.cyan(email)}`);
1920
1671
  };
1921
1672
  await echoIdentity();
1922
- const projectName = opts.name?.trim() || path9.basename(process.cwd());
1673
+ const projectName = opts.name?.trim() || path7.basename(process.cwd());
1923
1674
  const create = (bearer) => createDraftProject({
1924
1675
  apiUrl,
1925
1676
  token: bearer,
@@ -1967,10 +1718,10 @@ async function runInit(opts) {
1967
1718
  log.dim(` saved ${c.cyan(metaPath)}`);
1968
1719
  await writeGameConfigFiles(meta, log);
1969
1720
  if (converting) {
1970
- await fs9.rm(getWorkspacePath(process.cwd()), { force: true }).catch(() => {
1721
+ await fs7.rm(getWorkspacePath(process.cwd()), { force: true }).catch(() => {
1971
1722
  });
1972
1723
  await pruneToolCards(log, { dir: opts.dir, agents: opts.agents });
1973
- await ensureEnvVar(path9.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
1724
+ await ensureEnvVar(path7.join(process.cwd(), ".env"), "VITE_GENEX_SLUG", meta.slug, log);
1974
1725
  await reportCliEvent(apiUrl, token, "tools_converted");
1975
1726
  }
1976
1727
  warnPreexisting(log, preexisting);
@@ -1995,24 +1746,24 @@ async function runInit(opts) {
1995
1746
  }
1996
1747
 
1997
1748
  // src/commands/link.ts
1998
- import fs11 from "fs/promises";
1749
+ import fs9 from "fs/promises";
1999
1750
  import os6 from "os";
2000
- import path11 from "path";
1751
+ import path9 from "path";
2001
1752
 
2002
1753
  // src/lib/source-sync.ts
2003
- import fs10 from "fs/promises";
2004
- import path10 from "path";
1754
+ import fs8 from "fs/promises";
1755
+ import path8 from "path";
2005
1756
  import os5 from "os";
2006
1757
 
2007
1758
  // src/utils/run.ts
2008
- import { spawn as spawn3 } from "child_process";
1759
+ import { spawn as spawn2 } from "child_process";
2009
1760
  var WIN_SHELL_COMMANDS = /* @__PURE__ */ new Set(["npm", "npx"]);
2010
1761
  function run(cmd, args, env) {
2011
1762
  const shell = process.platform === "win32" && WIN_SHELL_COMMANDS.has(cmd);
2012
1763
  return new Promise((resolve) => {
2013
1764
  let child;
2014
1765
  try {
2015
- child = spawn3(cmd, args, { env: env ? { ...process.env, ...env } : process.env, shell });
1766
+ child = spawn2(cmd, args, { env: env ? { ...process.env, ...env } : process.env, shell });
2016
1767
  } catch {
2017
1768
  resolve({ code: -1, out: "", err: `${cmd} not found` });
2018
1769
  return;
@@ -2022,7 +1773,7 @@ function run(cmd, args, env) {
2022
1773
  child.stdout?.on("data", (d) => out += String(d));
2023
1774
  child.stderr?.on("data", (d) => err += String(d));
2024
1775
  child.on("error", () => resolve({ code: -1, out, err: `${cmd} not found` }));
2025
- child.on("close", (code2) => resolve({ code: code2 ?? -1, out, err }));
1776
+ child.on("close", (code) => resolve({ code: code ?? -1, out, err }));
2026
1777
  });
2027
1778
  }
2028
1779
 
@@ -2074,12 +1825,12 @@ function reportStale(log, slug, local, remote) {
2074
1825
  log.dim(` ${c.cyan("npx genex preview --force")} \u2014 keep yours and replace theirs`);
2075
1826
  }
2076
1827
  async function sourceTreeHash(cwd) {
2077
- const gitDir = await fs10.mkdtemp(path10.join(os5.tmpdir(), "genex-tree-"));
1828
+ const gitDir = await fs8.mkdtemp(path8.join(os5.tmpdir(), "genex-tree-"));
2078
1829
  const base = { GIT_DIR: gitDir };
2079
1830
  try {
2080
1831
  if ((await run("git", ["init", "-q"], base)).code !== 0) return null;
2081
- await fs10.writeFile(
2082
- path10.join(gitDir, "info", "exclude"),
1832
+ await fs8.writeFile(
1833
+ path8.join(gitDir, "info", "exclude"),
2083
1834
  ["node_modules/", "dist/", ".git/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n")
2084
1835
  );
2085
1836
  if ((await run("git", ["lfs", "version"], base)).code === 0) {
@@ -2091,14 +1842,14 @@ async function sourceTreeHash(cwd) {
2091
1842
  ];
2092
1843
  for (const [key, value] of filters) await run("git", ["config", key, value], base);
2093
1844
  }
2094
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path10.join(gitDir, "index-tree") };
1845
+ const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path8.join(gitDir, "index-tree") };
2095
1846
  if ((await run("git", ["add", "-A"], env)).code !== 0) return null;
2096
1847
  const tree = (await run("git", ["write-tree"], env)).out.trim();
2097
1848
  return /^[0-9a-f]{40}$/.test(tree) ? tree : null;
2098
1849
  } catch {
2099
1850
  return null;
2100
1851
  } finally {
2101
- await fs10.rm(gitDir, { recursive: true, force: true }).catch(() => {
1852
+ await fs8.rm(gitDir, { recursive: true, force: true }).catch(() => {
2102
1853
  });
2103
1854
  }
2104
1855
  }
@@ -2149,7 +1900,7 @@ async function cloneSource(grant, dest, log) {
2149
1900
  log.dim(` git clone exited ${cloned.code}`);
2150
1901
  return false;
2151
1902
  }
2152
- const repo = { ...env, GIT_DIR: path10.join(dest, ".git"), GIT_WORK_TREE: dest };
1903
+ const repo = { ...env, GIT_DIR: path8.join(dest, ".git"), GIT_WORK_TREE: dest };
2153
1904
  const filters = [
2154
1905
  ["filter.lfs.clean", "git-lfs clean -- %f"],
2155
1906
  ["filter.lfs.smudge", "git-lfs smudge -- %f"],
@@ -2245,7 +1996,7 @@ async function runLink(opts) {
2245
1996
  }
2246
1997
  async function isEmptyDir(cwd) {
2247
1998
  try {
2248
- const entries = await fs11.readdir(cwd);
1999
+ const entries = await fs9.readdir(cwd);
2249
2000
  return entries.every((e) => e === ".git" || e === ".genex" || e === ".DS_Store");
2250
2001
  } catch {
2251
2002
  return false;
@@ -2259,13 +2010,13 @@ async function downloadSource(apiUrl, token, project, log) {
2259
2010
  return false;
2260
2011
  }
2261
2012
  log.step(`Downloading the ${grant.sourceRef === "preview" ? "draft" : "published"} source\u2026`);
2262
- const staging = await fs11.mkdtemp(path11.join(os6.tmpdir(), "genex-link-"));
2263
- const fresh = path11.join(staging, "source");
2013
+ const staging = await fs9.mkdtemp(path9.join(os6.tmpdir(), "genex-link-"));
2014
+ const fresh = path9.join(staging, "source");
2264
2015
  try {
2265
2016
  if (!await cloneSource(grant, fresh, log)) return false;
2266
- await fs11.rm(path11.join(fresh, ".git"), { recursive: true, force: true });
2267
- for (const entry of await fs11.readdir(fresh)) {
2268
- await fs11.cp(path11.join(fresh, entry), path11.join(process.cwd(), entry), {
2017
+ await fs9.rm(path9.join(fresh, ".git"), { recursive: true, force: true });
2018
+ for (const entry of await fs9.readdir(fresh)) {
2019
+ await fs9.cp(path9.join(fresh, entry), path9.join(process.cwd(), entry), {
2269
2020
  recursive: true,
2270
2021
  force: true
2271
2022
  });
@@ -2273,28 +2024,28 @@ async function downloadSource(apiUrl, token, project, log) {
2273
2024
  log.success("Downloaded.");
2274
2025
  return true;
2275
2026
  } finally {
2276
- await fs11.rm(staging, { recursive: true, force: true }).catch(() => {
2027
+ await fs9.rm(staging, { recursive: true, force: true }).catch(() => {
2277
2028
  });
2278
2029
  }
2279
2030
  }
2280
2031
  async function ensureSlugEnv(slug, log, cwd = process.cwd()) {
2281
- const file = path11.join(cwd, ".env");
2032
+ const file = path9.join(cwd, ".env");
2282
2033
  let content;
2283
2034
  try {
2284
- content = await fs11.readFile(file, "utf8");
2035
+ content = await fs9.readFile(file, "utf8");
2285
2036
  } catch {
2286
2037
  return;
2287
2038
  }
2288
2039
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
2289
2040
  const m = content.match(re);
2290
2041
  if (!m) {
2291
- await fs11.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
2042
+ await fs9.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
2292
2043
  `);
2293
2044
  log.dim(` added VITE_GENEX_SLUG=${slug} to .env`);
2294
2045
  return;
2295
2046
  }
2296
2047
  if (m[2].trim() === slug) return;
2297
- await fs11.writeFile(file, content.replace(re, `$1${slug}`));
2048
+ await fs9.writeFile(file, content.replace(re, `$1${slug}`));
2298
2049
  log.dim(` updated .env: VITE_GENEX_SLUG=${slug} (was ${m[2].trim()})`);
2299
2050
  }
2300
2051
  async function fetchOwnProject(apiUrl, token, slug, log) {
@@ -2348,8 +2099,8 @@ async function listOwnSlugs(apiUrl, token, log) {
2348
2099
  }
2349
2100
 
2350
2101
  // src/commands/pull.ts
2351
- import fs12 from "fs/promises";
2352
- import path12 from "path";
2102
+ import fs10 from "fs/promises";
2103
+ import path10 from "path";
2353
2104
  import os7 from "os";
2354
2105
  function isMachineLocal(entry) {
2355
2106
  if (entry === ".genex" || entry === "node_modules" || entry === ".git") return true;
@@ -2409,23 +2160,23 @@ async function runPull(opts) {
2409
2160
  process.exitCode = 1;
2410
2161
  return;
2411
2162
  }
2412
- const staging = await fs12.mkdtemp(path12.join(os7.tmpdir(), "genex-pull-"));
2413
- const fresh = path12.join(staging, "source");
2163
+ const staging = await fs10.mkdtemp(path10.join(os7.tmpdir(), "genex-pull-"));
2164
+ const fresh = path10.join(staging, "source");
2414
2165
  try {
2415
2166
  if (!await cloneSource(grant, fresh, log)) {
2416
2167
  process.exitCode = 1;
2417
2168
  return;
2418
2169
  }
2419
- await fs12.rm(path12.join(fresh, ".git"), { recursive: true, force: true });
2170
+ await fs10.rm(path10.join(fresh, ".git"), { recursive: true, force: true });
2420
2171
  const kept = await keepReplaced(cwd, log);
2421
2172
  await replaceTree(cwd, fresh);
2422
2173
  if (kept) {
2423
2174
  log.plain("");
2424
- log.info(`What was here is kept at ${c.cyan(path12.relative(cwd, kept) || kept)}`);
2175
+ log.info(`What was here is kept at ${c.cyan(path10.relative(cwd, kept) || kept)}`);
2425
2176
  log.dim(" Nothing was thrown away \u2014 re-apply from there, or delete it when you are done.");
2426
2177
  }
2427
2178
  } finally {
2428
- await fs12.rm(staging, { recursive: true, force: true }).catch(() => {
2179
+ await fs10.rm(staging, { recursive: true, force: true }).catch(() => {
2429
2180
  });
2430
2181
  }
2431
2182
  const remote = await readRemoteSource(apiUrl, token, meta.id);
@@ -2444,13 +2195,13 @@ async function runPull(opts) {
2444
2195
  }
2445
2196
  async function keepReplaced(cwd, log) {
2446
2197
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2447
- const dest = path12.join(cwd, ".genex", `replaced-${stamp}`);
2198
+ const dest = path10.join(cwd, ".genex", `replaced-${stamp}`);
2448
2199
  try {
2449
- const entries = (await fs12.readdir(cwd)).filter((e) => !isMachineLocal(e));
2200
+ const entries = (await fs10.readdir(cwd)).filter((e) => !isMachineLocal(e));
2450
2201
  if (entries.length === 0) return null;
2451
- await fs12.mkdir(dest, { recursive: true });
2202
+ await fs10.mkdir(dest, { recursive: true });
2452
2203
  for (const entry of entries) {
2453
- await fs12.cp(path12.join(cwd, entry), path12.join(dest, entry), { recursive: true });
2204
+ await fs10.cp(path10.join(cwd, entry), path10.join(dest, entry), { recursive: true });
2454
2205
  }
2455
2206
  return dest;
2456
2207
  } catch (err) {
@@ -2460,19 +2211,19 @@ async function keepReplaced(cwd, log) {
2460
2211
  }
2461
2212
  }
2462
2213
  async function replaceTree(dest, src) {
2463
- for (const entry of await fs12.readdir(dest)) {
2214
+ for (const entry of await fs10.readdir(dest)) {
2464
2215
  if (isMachineLocal(entry)) continue;
2465
- await fs12.rm(path12.join(dest, entry), { recursive: true, force: true });
2216
+ await fs10.rm(path10.join(dest, entry), { recursive: true, force: true });
2466
2217
  }
2467
- for (const entry of await fs12.readdir(src)) {
2218
+ for (const entry of await fs10.readdir(src)) {
2468
2219
  if (isMachineLocal(entry)) continue;
2469
- await fs12.cp(path12.join(src, entry), path12.join(dest, entry), { recursive: true });
2220
+ await fs10.cp(path10.join(src, entry), path10.join(dest, entry), { recursive: true });
2470
2221
  }
2471
2222
  }
2472
2223
 
2473
2224
  // src/commands/rename.ts
2474
- import fs13 from "fs/promises";
2475
- import path13 from "path";
2225
+ import fs11 from "fs/promises";
2226
+ import path11 from "path";
2476
2227
  async function runRename(opts) {
2477
2228
  const log = createLogger({ quiet: opts.quiet });
2478
2229
  log.plain(c.bold("genex rename"));
@@ -2560,23 +2311,23 @@ async function runRename(opts) {
2560
2311
  log.info("Run `genex preview` (or `publish`) to rebuild \u2014 the new slug is baked into the bundle.");
2561
2312
  }
2562
2313
  async function rewriteSlugEnv(from, to, log, cwd = process.cwd()) {
2563
- const file = path13.join(cwd, ".env");
2314
+ const file = path11.join(cwd, ".env");
2564
2315
  let content;
2565
2316
  try {
2566
- content = await fs13.readFile(file, "utf8");
2317
+ content = await fs11.readFile(file, "utf8");
2567
2318
  } catch {
2568
2319
  return;
2569
2320
  }
2570
2321
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
2571
2322
  if (!re.test(content)) return;
2572
- await fs13.writeFile(file, content.replace(re, `$1${to}`));
2323
+ await fs11.writeFile(file, content.replace(re, `$1${to}`));
2573
2324
  log.dim(` .env: VITE_GENEX_SLUG=${to} (was ${from})`);
2574
2325
  }
2575
2326
  async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
2576
- const file = path13.join(cwd, "src", "genex.config.ts");
2327
+ const file = path11.join(cwd, "src", "genex.config.ts");
2577
2328
  let content;
2578
2329
  try {
2579
- content = await fs13.readFile(file, "utf8");
2330
+ content = await fs11.readFile(file, "utf8");
2580
2331
  } catch {
2581
2332
  return;
2582
2333
  }
@@ -2586,7 +2337,7 @@ async function rewriteBakedSlug(from, to, log, cwd = process.cwd()) {
2586
2337
  log.warn(` src/genex.config.ts has no "${from}" literal \u2014 check its slug by hand.`);
2587
2338
  return;
2588
2339
  }
2589
- await fs13.writeFile(file, content.replace(quoted, `"${to}"`));
2340
+ await fs11.writeFile(file, content.replace(quoted, `"${to}"`));
2590
2341
  log.dim(` src/genex.config.ts: baked slug -> ${to}`);
2591
2342
  }
2592
2343
 
@@ -2716,9 +2467,9 @@ function relTime(iso) {
2716
2467
  // src/lib/deploy.ts
2717
2468
  import "child_process";
2718
2469
  import crypto3 from "crypto";
2719
- import fs16 from "fs/promises";
2470
+ import fs14 from "fs/promises";
2720
2471
  import os8 from "os";
2721
- import path15 from "path";
2472
+ import path13 from "path";
2722
2473
 
2723
2474
  // ../../packages/mobile-scan/src/image-dims.ts
2724
2475
  function u32be(b, o) {
@@ -2982,12 +2733,12 @@ function tierFor(estVramMb) {
2982
2733
  }
2983
2734
 
2984
2735
  // src/commands/ui.ts
2985
- import fs15 from "fs/promises";
2986
- import path14 from "path";
2736
+ import fs13 from "fs/promises";
2737
+ import path12 from "path";
2987
2738
  import { PNG as PNG2 } from "pngjs";
2988
2739
 
2989
2740
  // src/lib/png-tools.ts
2990
- import fs14 from "fs/promises";
2741
+ import fs12 from "fs/promises";
2991
2742
  import { PNG } from "pngjs";
2992
2743
  var ALPHA_TRANSPARENT_MAX = 16;
2993
2744
  var isHttpUrl = (s) => /^https?:\/\//i.test(s);
@@ -2998,12 +2749,12 @@ async function loadPng(input) {
2998
2749
  if (!res.ok) throw new Error(`Couldn't fetch ${input} (HTTP ${res.status}).`);
2999
2750
  buf = Buffer.from(await res.arrayBuffer());
3000
2751
  } else {
3001
- buf = await fs14.readFile(input);
2752
+ buf = await fs12.readFile(input);
3002
2753
  }
3003
2754
  return PNG.sync.read(buf);
3004
2755
  }
3005
2756
  async function writePng(file, png) {
3006
- await fs14.writeFile(file, PNG.sync.write(png));
2757
+ await fs12.writeFile(file, PNG.sync.write(png));
3007
2758
  }
3008
2759
  function cropPng(image, box) {
3009
2760
  const out = new PNG({ width: box.w, height: box.h });
@@ -3303,7 +3054,7 @@ async function uiExtract(opts, log) {
3303
3054
  const dilatePx = opts.dilate ?? 0;
3304
3055
  const sheet = await loadPng(input);
3305
3056
  const { width: W, height: H, data } = sheet;
3306
- await fs15.mkdir(outDir, { recursive: true });
3057
+ await fs13.mkdir(outDir, { recursive: true });
3307
3058
  log.plain(c.bold("genex ui extract"));
3308
3059
  log.dim(` ${input} (${W}x${H}) \u2192 ${outDir}, ${names.length} names`);
3309
3060
  let hasTransparency = false;
@@ -3532,7 +3283,7 @@ async function uiExtract(opts, log) {
3532
3283
  rimPixels: speckle.sampled
3533
3284
  });
3534
3285
  }
3535
- const outPath = path14.join(outDir, `${name}.png`);
3286
+ const outPath = path12.join(outDir, `${name}.png`);
3536
3287
  await writePng(outPath, out);
3537
3288
  const sidecar = {
3538
3289
  name,
@@ -3551,7 +3302,7 @@ async function uiExtract(opts, log) {
3551
3302
  defringed
3552
3303
  };
3553
3304
  const { name: _n, out: _o, ...sidecarBody } = sidecar;
3554
- await fs15.writeFile(
3305
+ await fs13.writeFile(
3555
3306
  outPath.replace(/\.png$/i, "") + ".bbox.json",
3556
3307
  JSON.stringify(sidecarBody, null, 2)
3557
3308
  );
@@ -3560,8 +3311,8 @@ async function uiExtract(opts, log) {
3560
3311
  `${name}.png ${cropW}x${cropH} (ar ${sidecar.aspectRatio}) at sheet ${padX0},${padY0}`
3561
3312
  );
3562
3313
  }
3563
- const debugPath = path14.join(outDir, "extract-debug.json");
3564
- await fs15.writeFile(
3314
+ const debugPath = path12.join(outDir, "extract-debug.json");
3315
+ await fs13.writeFile(
3565
3316
  debugPath,
3566
3317
  JSON.stringify(
3567
3318
  {
@@ -4023,7 +3774,7 @@ async function uiMasks(opts, log) {
4023
3774
  const registrationTolerance = opts.registrationTolerance ?? 0.04;
4024
3775
  const edgeFlushMax = opts.edgeFlushMax ?? 0.04;
4025
3776
  const loosened = registrationTolerance > 0.04 || edgeFlushMax > 0.04 || minCoverage < 0.01 || maxCoverage > 0.85;
4026
- await fs15.mkdir(outDir, { recursive: true });
3777
+ await fs13.mkdir(outDir, { recursive: true });
4027
3778
  log.plain(c.bold("genex ui masks"));
4028
3779
  log.dim(` ${input} (${image.width}x${image.height}), ${pairs.length} pair(s) \u2192 ${outDir}`);
4029
3780
  const sheetComponents = detectSheetComponents(image, opts.minPixels ?? 2e3);
@@ -4076,11 +3827,11 @@ async function uiMasks(opts, log) {
4076
3827
  });
4077
3828
  }
4078
3829
  const overlay = makeOverlay(clean2, converted.png);
4079
- const framePath = path14.join(outDir, `${pair.name}-frame.png`);
4080
- const maskPath = path14.join(outDir, `${pair.name}-mask.png`);
4081
- const annotatedPath = path14.join(outDir, `${pair.name}-annotated-source.png`);
4082
- const overlayPath = path14.join(outDir, `${pair.name}-overlay.png`);
4083
- const metaPath = path14.join(outDir, `${pair.name}.annotated-progress.json`);
3830
+ const framePath = path12.join(outDir, `${pair.name}-frame.png`);
3831
+ const maskPath = path12.join(outDir, `${pair.name}-mask.png`);
3832
+ const annotatedPath = path12.join(outDir, `${pair.name}-annotated-source.png`);
3833
+ const overlayPath = path12.join(outDir, `${pair.name}-overlay.png`);
3834
+ const metaPath = path12.join(outDir, `${pair.name}.annotated-progress.json`);
4084
3835
  await writePng(framePath, clean2);
4085
3836
  await writePng(maskPath, converted.png);
4086
3837
  await writePng(annotatedPath, annotated);
@@ -4127,7 +3878,7 @@ async function uiMasks(opts, log) {
4127
3878
  },
4128
3879
  overlay: overlayPath
4129
3880
  };
4130
- await fs15.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
3881
+ await fs13.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
4131
3882
  `);
4132
3883
  results.push(meta);
4133
3884
  const fb = converted.bbox;
@@ -4143,8 +3894,8 @@ async function uiMasks(opts, log) {
4143
3894
  );
4144
3895
  }
4145
3896
  }
4146
- const indexPath = path14.join(outDir, "annotated-progress.json");
4147
- await fs15.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
3897
+ const indexPath = path12.join(outDir, "annotated-progress.json");
3898
+ await fs13.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
4148
3899
  `);
4149
3900
  log.plain("");
4150
3901
  log.success(`Wrote ${pairs.length} frame+mask pair(s) \u2192 ${outDir} (index: ${indexPath}).`);
@@ -4270,7 +4021,7 @@ async function uiTextColor(opts, log) {
4270
4021
  };
4271
4022
  process.stdout.write(`${JSON.stringify(result, null, 2)}
4272
4023
  `);
4273
- if (opts.out) await fs15.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4024
+ if (opts.out) await fs13.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
4274
4025
  `);
4275
4026
  if (opts.cropPath) log.dim(` crop \u2192 ${opts.cropPath}`);
4276
4027
  }
@@ -4302,7 +4053,7 @@ async function uiTrim(opts, log) {
4302
4053
  const sidecar = computeBBoxes(trimmed);
4303
4054
  const speckleAllowed = !!(speckle && speckle.ratio > SPECKLE_MAX_RATIO);
4304
4055
  const sidecarPath = outPath.replace(/\.png$/i, "") + ".bbox.json";
4305
- await fs15.writeFile(
4056
+ await fs13.writeFile(
4306
4057
  sidecarPath,
4307
4058
  JSON.stringify(speckleAllowed ? { ...sidecar, speckleAllowed: true } : sidecar, null, 2)
4308
4059
  );
@@ -4409,7 +4160,7 @@ async function uiPlate(opts, log) {
4409
4160
  fail("No interior found \u2014 the image is fully transparent (or erode ate everything). Check --in / lower --erode.");
4410
4161
  }
4411
4162
  await writePng(outPath, out);
4412
- const name = path14.basename(outPath);
4163
+ const name = path12.basename(outPath);
4413
4164
  log.plain(c.bold("genex ui plate"));
4414
4165
  log.success(`${outPath} ${W}x${H}, interior ${(count / (W * H) * 100).toFixed(1)}% (erode ${erode}px)`);
4415
4166
  log.dim(" Wire it as the plate's silhouette (same box as the frame <img>, plate UNDER the art):");
@@ -4574,13 +4325,13 @@ async function walkFiles(dir) {
4574
4325
  const out = [];
4575
4326
  let entries;
4576
4327
  try {
4577
- entries = await fs15.readdir(dir, { withFileTypes: true });
4328
+ entries = await fs13.readdir(dir, { withFileTypes: true });
4578
4329
  } catch {
4579
4330
  return out;
4580
4331
  }
4581
4332
  for (const entry of entries) {
4582
4333
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
4583
- const p = path14.join(dir, entry.name);
4334
+ const p = path12.join(dir, entry.name);
4584
4335
  if (entry.isDirectory()) out.push(...await walkFiles(p));
4585
4336
  else out.push(p);
4586
4337
  }
@@ -4589,7 +4340,7 @@ async function walkFiles(dir) {
4589
4340
  async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4590
4341
  const viewportFindings = [];
4591
4342
  try {
4592
- const indexHtml = await fs15.readFile(path14.join(cwd, "index.html"), "utf8");
4343
+ const indexHtml = await fs13.readFile(path12.join(cwd, "index.html"), "utf8");
4593
4344
  if (!/<meta[^>]+name=["']viewport["']/i.test(indexHtml)) {
4594
4345
  viewportFindings.push({
4595
4346
  kind: "viewport-meta",
@@ -4603,28 +4354,28 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4603
4354
  }
4604
4355
  } catch {
4605
4356
  }
4606
- const absAssets = path14.resolve(cwd, assetDir);
4357
+ const absAssets = path12.resolve(cwd, assetDir);
4607
4358
  try {
4608
- if (!(await fs15.stat(absAssets)).isDirectory()) {
4359
+ if (!(await fs13.stat(absAssets)).isDirectory()) {
4609
4360
  return viewportFindings.length > 0 ? viewportFindings : null;
4610
4361
  }
4611
4362
  } catch {
4612
4363
  return viewportFindings.length > 0 ? viewportFindings : null;
4613
4364
  }
4614
- const srcFiles = (await walkFiles(path14.resolve(cwd, srcDir))).filter(
4615
- (p) => AUDIT_SRC_EXTS.has(path14.extname(p).toLowerCase())
4365
+ const srcFiles = (await walkFiles(path12.resolve(cwd, srcDir))).filter(
4366
+ (p) => AUDIT_SRC_EXTS.has(path12.extname(p).toLowerCase())
4616
4367
  );
4617
4368
  try {
4618
- for (const name of await fs15.readdir(cwd)) {
4619
- const ext = path14.extname(name).toLowerCase();
4620
- if (ext === ".html" || ext === ".css") srcFiles.push(path14.join(cwd, name));
4369
+ for (const name of await fs13.readdir(cwd)) {
4370
+ const ext = path12.extname(name).toLowerCase();
4371
+ if (ext === ".html" || ext === ".css") srcFiles.push(path12.join(cwd, name));
4621
4372
  }
4622
4373
  } catch {
4623
4374
  }
4624
4375
  const sources = [];
4625
4376
  for (const p of srcFiles) {
4626
4377
  try {
4627
- sources.push({ rel: path14.relative(cwd, p), text: await fs15.readFile(p, "utf8") });
4378
+ sources.push({ rel: path12.relative(cwd, p), text: await fs13.readFile(p, "utf8") });
4628
4379
  } catch {
4629
4380
  }
4630
4381
  }
@@ -4634,11 +4385,11 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4634
4385
  const metaByName = /* @__PURE__ */ new Map();
4635
4386
  const bboxByPng = /* @__PURE__ */ new Map();
4636
4387
  for (const p of assetFiles) {
4637
- const base = path14.basename(p);
4388
+ const base = path12.basename(p);
4638
4389
  const metaMatch = /^(.+)\.annotated-progress\.json$/.exec(base);
4639
4390
  if (metaMatch) {
4640
4391
  try {
4641
- const meta = JSON.parse(await fs15.readFile(p, "utf8"));
4392
+ const meta = JSON.parse(await fs13.readFile(p, "utf8"));
4642
4393
  metaByName.set(metaMatch[1], {
4643
4394
  cleanCrop: meta.clean?.crop ?? null,
4644
4395
  loosened: meta.loosened === true
@@ -4649,7 +4400,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4649
4400
  }
4650
4401
  if (base.endsWith(".bbox.json")) {
4651
4402
  try {
4652
- const sidecar = JSON.parse(await fs15.readFile(p, "utf8"));
4403
+ const sidecar = JSON.parse(await fs13.readFile(p, "utf8"));
4653
4404
  if (sidecar.sheetBBox) bboxByPng.set(base.replace(/\.bbox\.json$/, ".png"), sidecar.sheetBBox);
4654
4405
  } catch {
4655
4406
  }
@@ -4658,7 +4409,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4658
4409
  for (const [name, meta] of metaByName) {
4659
4410
  if (!meta.cleanCrop || !referenced(`${name}-mask.png`)) continue;
4660
4411
  for (const p of assetFiles) {
4661
- const base = path14.basename(p);
4412
+ const base = path12.basename(p);
4662
4413
  if (!base.toLowerCase().endsWith(".png")) continue;
4663
4414
  if (base !== `${name}.png` && !base.startsWith(`${name}-`)) continue;
4664
4415
  if (/-mask\.png$|-frame\.png$|-overlay\.png$|-annotated-source\.png$/.test(base)) continue;
@@ -4682,7 +4433,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4682
4433
  }
4683
4434
  const pngByBase = /* @__PURE__ */ new Map();
4684
4435
  for (const p of assetFiles) {
4685
- const base = path14.basename(p);
4436
+ const base = path12.basename(p);
4686
4437
  if (base.toLowerCase().endsWith(".png")) pngByBase.set(base, p);
4687
4438
  }
4688
4439
  for (const [maskBase, maskPath] of pngByBase) {
@@ -4695,8 +4446,8 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4695
4446
  let frame;
4696
4447
  let mask;
4697
4448
  try {
4698
- frame = PNG2.sync.read(await fs15.readFile(framePath));
4699
- mask = PNG2.sync.read(await fs15.readFile(maskPath));
4449
+ frame = PNG2.sync.read(await fs13.readFile(framePath));
4450
+ mask = PNG2.sync.read(await fs13.readFile(maskPath));
4700
4451
  } catch {
4701
4452
  continue;
4702
4453
  }
@@ -4715,7 +4466,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4715
4466
  if (!referenced(base)) continue;
4716
4467
  let png;
4717
4468
  try {
4718
- png = PNG2.sync.read(await fs15.readFile(p));
4469
+ png = PNG2.sync.read(await fs13.readFile(p));
4719
4470
  } catch {
4720
4471
  continue;
4721
4472
  }
@@ -4735,7 +4486,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4735
4486
  }
4736
4487
  const maskReported = /* @__PURE__ */ new Set();
4737
4488
  for (const p of assetFiles) {
4738
- const m = /^(.+)\.annotated-progress\.json$/.exec(path14.basename(p));
4489
+ const m = /^(.+)\.annotated-progress\.json$/.exec(path12.basename(p));
4739
4490
  if (!m) continue;
4740
4491
  const maskBase = `${m[1]}-mask.png`;
4741
4492
  if (!referenced(maskBase)) {
@@ -4747,14 +4498,14 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
4747
4498
  }
4748
4499
  }
4749
4500
  for (const p of assetFiles) {
4750
- const base = path14.basename(p);
4501
+ const base = path12.basename(p);
4751
4502
  if (!base.toLowerCase().endsWith(".png")) continue;
4752
4503
  if (/-annotated-source\.png$|-overlay\.png$/.test(base)) continue;
4753
4504
  if (maskReported.has(base)) continue;
4754
4505
  if (!referenced(base)) {
4755
4506
  findings.push({
4756
4507
  kind: "unwired-sprite",
4757
- message: `${path14.relative(cwd, p)} is on disk but never referenced in ${srcDir}/index.html/CSS \u2014 wire it, or record the one-line reason it was cut. (Computed-string references are invisible here \u2014 check.)`
4508
+ message: `${path12.relative(cwd, p)} is on disk but never referenced in ${srcDir}/index.html/CSS \u2014 wire it, or record the one-line reason it was cut. (Computed-string references are invisible here \u2014 check.)`
4758
4509
  });
4759
4510
  }
4760
4511
  }
@@ -4941,7 +4692,7 @@ async function printUiAuditPreflight(log) {
4941
4692
  }
4942
4693
  async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4943
4694
  try {
4944
- const design = await fs16.readFile(path15.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4695
+ const design = await fs14.readFile(path13.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4945
4696
  const warnings = [];
4946
4697
  if (design.length > 0 && !/##\s*build plan & status/i.test(design)) {
4947
4698
  warnings.push(
@@ -4949,7 +4700,7 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4949
4700
  );
4950
4701
  }
4951
4702
  if (!/player character:/i.test(design)) {
4952
- const hasCharacter = await fs16.access(path15.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4703
+ const hasCharacter = await fs14.access(path13.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4953
4704
  if (!hasCharacter && await loadsPlayerBody(cwd)) {
4954
4705
  warnings.push(
4955
4706
  `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).`
@@ -4963,12 +4714,12 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4963
4714
  async function loadsPlayerBody(cwd) {
4964
4715
  const BODY_LOADERS = /\bloadPlayerCharacter\s*\(|\bloadVrm(?:Clone)?\s*\(/;
4965
4716
  try {
4966
- const entries = await fs16.readdir(path15.join(cwd, "src"), { recursive: true });
4717
+ const entries = await fs14.readdir(path13.join(cwd, "src"), { recursive: true });
4967
4718
  for (const rel of entries) {
4968
4719
  if (rel.includes("node_modules")) continue;
4969
- if (rel.split(path15.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4720
+ if (rel.split(path13.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4970
4721
  if (!/\.(ts|tsx|js|jsx)$/.test(rel)) continue;
4971
- const text = await fs16.readFile(path15.join(cwd, "src", rel), "utf8").catch(() => "");
4722
+ const text = await fs14.readFile(path13.join(cwd, "src", rel), "utf8").catch(() => "");
4972
4723
  if (BODY_LOADERS.test(text)) return true;
4973
4724
  }
4974
4725
  } catch {
@@ -5005,9 +4756,9 @@ async function deployGame(ctx, opts, log) {
5005
4756
  }
5006
4757
  log.success("Built.");
5007
4758
  }
5008
- const distDir = path15.join(cwd, "dist");
4759
+ const distDir = path13.join(cwd, "dist");
5009
4760
  const siteDir = await isDir(distDir) ? distDir : cwd;
5010
- const rel = path15.relative(cwd, siteDir) || ".";
4761
+ const rel = path13.relative(cwd, siteDir) || ".";
5011
4762
  if (siteDir === cwd) await writeGitignore(cwd, log);
5012
4763
  const files = await collectFiles(siteDir);
5013
4764
  if (files.length === 0) {
@@ -5096,7 +4847,7 @@ async function deployGame(ctx, opts, log) {
5096
4847
  }
5097
4848
  async function hasBuildScript(cwd) {
5098
4849
  try {
5099
- const pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
4850
+ const pkg = JSON.parse(await fs14.readFile(path13.join(cwd, "package.json"), "utf8"));
5100
4851
  return Boolean(pkg.scripts?.build);
5101
4852
  } catch {
5102
4853
  return false;
@@ -5105,12 +4856,12 @@ async function hasBuildScript(cwd) {
5105
4856
  async function collectFiles(root) {
5106
4857
  const out = [];
5107
4858
  const walk2 = async (dir, prefix) => {
5108
- for (const e of await fs16.readdir(dir, { withFileTypes: true })) {
4859
+ for (const e of await fs14.readdir(dir, { withFileTypes: true })) {
5109
4860
  const relPath = prefix ? `${prefix}/${e.name}` : e.name;
5110
4861
  if (e.isDirectory()) {
5111
- if (!EXCLUDE_DIRS.has(e.name)) await walk2(path15.join(dir, e.name), relPath);
4862
+ if (!EXCLUDE_DIRS.has(e.name)) await walk2(path13.join(dir, e.name), relPath);
5112
4863
  } else if (e.isFile() && !isSecretEnvFile(e.name)) {
5113
- out.push({ relPath, bytes: await fs16.readFile(path15.join(dir, e.name)) });
4864
+ out.push({ relPath, bytes: await fs14.readFile(path13.join(dir, e.name)) });
5114
4865
  }
5115
4866
  }
5116
4867
  };
@@ -5352,7 +5103,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5352
5103
  log.error("Couldn't save your game's source \u2014 please try again.");
5353
5104
  return false;
5354
5105
  };
5355
- const gitDir = await fs16.mkdtemp(path15.join(os8.tmpdir(), "genex-source-"));
5106
+ const gitDir = await fs14.mkdtemp(path13.join(os8.tmpdir(), "genex-source-"));
5356
5107
  const base = { GIT_DIR: gitDir };
5357
5108
  if (urlHasEmbeddedCredentials(pushUrl)) {
5358
5109
  base.GIT_CONFIG_COUNT = "1";
@@ -5367,12 +5118,12 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5367
5118
  };
5368
5119
  try {
5369
5120
  if ((await run("git", ["init", "-q"], base)).code !== 0) return failed();
5370
- await fs16.writeFile(
5371
- path15.join(gitDir, "info", "exclude"),
5121
+ await fs14.writeFile(
5122
+ path13.join(gitDir, "info", "exclude"),
5372
5123
  // .env* are secrets — never publish them; `!` keeps the non-secret template.
5373
5124
  ["node_modules/", "dist/", ".git/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n")
5374
5125
  );
5375
- const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path15.join(gitDir, "index-source") };
5126
+ const env = { ...base, GIT_WORK_TREE: cwd, GIT_INDEX_FILE: path13.join(gitDir, "index-source") };
5376
5127
  let lfs = (await run("git", ["lfs", "version"], base)).code !== 0 ? false : true;
5377
5128
  if (!lfs) {
5378
5129
  log.step("Installing git-lfs (keeps large binary assets out of the source push)\u2026");
@@ -5424,7 +5175,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main", onCommi
5424
5175
  } catch {
5425
5176
  return failed();
5426
5177
  } finally {
5427
- await fs16.rm(gitDir, { recursive: true, force: true }).catch(() => {
5178
+ await fs14.rm(gitDir, { recursive: true, force: true }).catch(() => {
5428
5179
  });
5429
5180
  }
5430
5181
  }
@@ -5476,7 +5227,7 @@ async function fetchPushUrl(ctx, log, expectStagingCommit) {
5476
5227
  }
5477
5228
  async function isDir(p) {
5478
5229
  try {
5479
- return (await fs16.stat(p)).isDirectory();
5230
+ return (await fs14.stat(p)).isDirectory();
5480
5231
  } catch {
5481
5232
  return false;
5482
5233
  }
@@ -5958,17 +5709,17 @@ async function promoteBuild(apiUrl, projectId, token, log) {
5958
5709
  }
5959
5710
 
5960
5711
  // src/lib/detect-features.ts
5961
- import fs18 from "fs/promises";
5962
- import path17 from "path";
5712
+ import fs16 from "fs/promises";
5713
+ import path15 from "path";
5963
5714
 
5964
5715
  // src/lib/generation-ledger.ts
5965
- import fs17 from "fs/promises";
5966
- import path16 from "path";
5967
- var ledgerPath = (cwd) => path16.join(cwd, ".genex", "generations.ndjson");
5716
+ import fs15 from "fs/promises";
5717
+ import path14 from "path";
5718
+ var ledgerPath = (cwd) => path14.join(cwd, ".genex", "generations.ndjson");
5968
5719
  async function append(cwd, event) {
5969
5720
  try {
5970
- await fs17.access(path16.join(cwd, ".genex"));
5971
- await fs17.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
5721
+ await fs15.access(path14.join(cwd, ".genex"));
5722
+ await fs15.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
5972
5723
  `, "utf8");
5973
5724
  } catch {
5974
5725
  }
@@ -5976,7 +5727,7 @@ async function append(cwd, event) {
5976
5727
  async function readLedger(cwd = process.cwd()) {
5977
5728
  let raw;
5978
5729
  try {
5979
- raw = await fs17.readFile(ledgerPath(cwd), "utf8");
5730
+ raw = await fs15.readFile(ledgerPath(cwd), "utf8");
5980
5731
  } catch {
5981
5732
  return [];
5982
5733
  }
@@ -6037,7 +5788,7 @@ async function countOutcomes(kind, cwd = process.cwd()) {
6037
5788
  // src/lib/detect-features.ts
6038
5789
  async function detectEmbedSdkVersion(cwd = process.cwd()) {
6039
5790
  try {
6040
- const raw = await fs18.readFile(path17.join(cwd, "package.json"), "utf8");
5791
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
6041
5792
  const pkg = JSON.parse(raw);
6042
5793
  const version = pkg.dependencies?.["@genex-ai/embed-sdk"] ?? pkg.devDependencies?.["@genex-ai/embed-sdk"];
6043
5794
  return typeof version === "string" && version ? version : null;
@@ -6047,7 +5798,7 @@ async function detectEmbedSdkVersion(cwd = process.cwd()) {
6047
5798
  }
6048
5799
  async function detectMultiplayer(cwd = process.cwd()) {
6049
5800
  try {
6050
- const raw = await fs18.readFile(path17.join(cwd, "package.json"), "utf8");
5801
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
6051
5802
  const pkg = JSON.parse(raw);
6052
5803
  return Boolean(
6053
5804
  pkg.dependencies?.["@genex-ai/multiplayer"] ?? pkg.devDependencies?.["@genex-ai/multiplayer"]
@@ -6059,7 +5810,7 @@ async function detectMultiplayer(cwd = process.cwd()) {
6059
5810
  async function detectMatchmaking(log, cwd = process.cwd()) {
6060
5811
  let pkg;
6061
5812
  try {
6062
- pkg = JSON.parse(await fs18.readFile(path17.join(cwd, "package.json"), "utf8"));
5813
+ pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
6063
5814
  } catch (err) {
6064
5815
  log.dim(` (skipping matchmaking \u2014 couldn't read package.json: ${String(err)})`);
6065
5816
  return null;
@@ -6077,15 +5828,15 @@ async function detectMatchmaking(log, cwd = process.cwd()) {
6077
5828
  var TOUCH_KIT_MARKERS = /controllers\/touch\/|controllers\/character\/touch-joystick|TouchJoystick|VirtualButton|DragZone|RotateOverlay/;
6078
5829
  async function detectMobileControls(cwd = process.cwd()) {
6079
5830
  try {
6080
- const raw = await fs18.readFile(path17.join(cwd, "package.json"), "utf8");
5831
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
6081
5832
  const pkg = JSON.parse(raw);
6082
5833
  if (pkg.genex?.mobileControls === true) return true;
6083
5834
  } catch {
6084
5835
  }
6085
- const srcDir = path17.join(cwd, "src");
5836
+ const srcDir = path15.join(cwd, "src");
6086
5837
  let entries;
6087
5838
  try {
6088
- entries = await fs18.readdir(srcDir, { recursive: true });
5839
+ entries = await fs16.readdir(srcDir, { recursive: true });
6089
5840
  } catch {
6090
5841
  return false;
6091
5842
  }
@@ -6093,7 +5844,7 @@ async function detectMobileControls(cwd = process.cwd()) {
6093
5844
  if (rel.includes("node_modules")) continue;
6094
5845
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
6095
5846
  try {
6096
- const content = await fs18.readFile(path17.join(srcDir, rel), "utf8");
5847
+ const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
6097
5848
  if (TOUCH_KIT_MARKERS.test(content)) return true;
6098
5849
  } catch {
6099
5850
  }
@@ -6102,10 +5853,10 @@ async function detectMobileControls(cwd = process.cwd()) {
6102
5853
  }
6103
5854
  var GAME_STATE_CALLS = /savePlayerState|saveWorldState|submitScore/;
6104
5855
  async function detectGameStateUsage(cwd = process.cwd()) {
6105
- const srcDir = path17.join(cwd, "src");
5856
+ const srcDir = path15.join(cwd, "src");
6106
5857
  let entries;
6107
5858
  try {
6108
- entries = await fs18.readdir(srcDir, { recursive: true });
5859
+ entries = await fs16.readdir(srcDir, { recursive: true });
6109
5860
  } catch {
6110
5861
  return false;
6111
5862
  }
@@ -6113,7 +5864,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
6113
5864
  if (rel.includes("node_modules")) continue;
6114
5865
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
6115
5866
  try {
6116
- const content = await fs18.readFile(path17.join(srcDir, rel), "utf8");
5867
+ const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
6117
5868
  if (GAME_STATE_CALLS.test(content)) return true;
6118
5869
  } catch {
6119
5870
  }
@@ -6164,19 +5915,19 @@ async function detectSurfaceScan(cwd = process.cwd()) {
6164
5915
  deferredAudioContext: [],
6165
5916
  usesThree: false
6166
5917
  };
6167
- const srcDir = path17.join(cwd, "src");
5918
+ const srcDir = path15.join(cwd, "src");
6168
5919
  let entries;
6169
5920
  try {
6170
- entries = await fs18.readdir(srcDir, { recursive: true });
5921
+ entries = await fs16.readdir(srcDir, { recursive: true });
6171
5922
  } catch {
6172
5923
  return found;
6173
5924
  }
6174
5925
  for (const nativeRel of entries) {
6175
5926
  if (nativeRel.includes("node_modules")) continue;
6176
5927
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
6177
- const raw = await fs18.readFile(path17.join(srcDir, nativeRel), "utf8").catch(() => "");
5928
+ const raw = await fs16.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
6178
5929
  if (!raw) continue;
6179
- const rel = nativeRel.split(path17.sep).join("/");
5930
+ const rel = nativeRel.split(path15.sep).join("/");
6180
5931
  const content = raw.replace(/\/\*[\s\S]*?\*\//g, (m2) => m2.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/gm, (m2, p1) => p1 + " ".repeat(m2.length - p1.length));
6181
5932
  const repeatRe = /\.repeat\.set\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/g;
6182
5933
  let m;
@@ -6257,25 +6008,25 @@ async function detectGenerationAudit(cwd = process.cwd()) {
6257
6008
  let haystack = "";
6258
6009
  const read = async (file) => {
6259
6010
  try {
6260
- haystack += await fs18.readFile(file, "utf8");
6011
+ haystack += await fs16.readFile(file, "utf8");
6261
6012
  } catch {
6262
6013
  }
6263
6014
  };
6264
6015
  try {
6265
- for (const entry of await fs18.readdir(cwd, { withFileTypes: true })) {
6016
+ for (const entry of await fs16.readdir(cwd, { withFileTypes: true })) {
6266
6017
  if (entry.isFile() && /\.(ts|tsx|js|jsx|css|html|json)$/.test(entry.name)) {
6267
- await read(path17.join(cwd, entry.name));
6018
+ await read(path15.join(cwd, entry.name));
6268
6019
  }
6269
6020
  }
6270
6021
  } catch {
6271
6022
  }
6272
6023
  for (const sub of ["src", "public"]) {
6273
6024
  try {
6274
- const entries = await fs18.readdir(path17.join(cwd, sub), { recursive: true });
6025
+ const entries = await fs16.readdir(path15.join(cwd, sub), { recursive: true });
6275
6026
  for (const rel of entries) {
6276
6027
  if (rel.includes("node_modules")) continue;
6277
6028
  if (!/\.(ts|tsx|js|jsx|css|html|json|txt)$/.test(rel)) continue;
6278
- await read(path17.join(cwd, sub, rel));
6029
+ await read(path15.join(cwd, sub, rel));
6279
6030
  }
6280
6031
  } catch {
6281
6032
  }
@@ -6422,7 +6173,7 @@ async function borrowEvidence(meta, cwd) {
6422
6173
  } catch {
6423
6174
  return true;
6424
6175
  }
6425
- const gitConfig = await fs18.readFile(path17.join(cwd, ".git", "config"), "utf8").catch(() => "");
6176
+ const gitConfig = await fs16.readFile(path15.join(cwd, ".git", "config"), "utf8").catch(() => "");
6426
6177
  for (const m of gitConfig.matchAll(/url\s*=\s*(\S+)/g)) {
6427
6178
  try {
6428
6179
  const u = new URL(m[1]);
@@ -6430,7 +6181,7 @@ async function borrowEvidence(meta, cwd) {
6430
6181
  } catch {
6431
6182
  }
6432
6183
  }
6433
- const readme = await fs18.readFile(path17.join(cwd, "README.md"), "utf8").catch(() => "");
6184
+ const readme = await fs16.readFile(path15.join(cwd, "README.md"), "utf8").catch(() => "");
6434
6185
  return readme.includes(host) || /\b(upstream|originally by|ported from|borrowed from)\b/i.test(readme);
6435
6186
  }
6436
6187
 
@@ -6910,8 +6661,8 @@ async function runRollback(opts) {
6910
6661
  }
6911
6662
 
6912
6663
  // src/commands/generate.ts
6913
- import fs20 from "fs/promises";
6914
- import path19 from "path";
6664
+ import fs18 from "fs/promises";
6665
+ import path17 from "path";
6915
6666
  import { PNG as PNG4 } from "pngjs";
6916
6667
 
6917
6668
  // src/lib/glass.ts
@@ -6992,8 +6743,8 @@ function solveMagentaGlass(source) {
6992
6743
  }
6993
6744
 
6994
6745
  // src/lib/download-assets.ts
6995
- import fs19 from "fs/promises";
6996
- import path18 from "path";
6746
+ import fs17 from "fs/promises";
6747
+ import path16 from "path";
6997
6748
  var RUNG_ROLE = /@\d+$/;
6998
6749
  function isPrimaryRole(role) {
6999
6750
  return !RUNG_ROLE.test(role);
@@ -7013,7 +6764,7 @@ async function downloadAssets(files, opts) {
7013
6764
  const result = { saved: [], failures: [] };
7014
6765
  if (wanted.length === 0) return result;
7015
6766
  try {
7016
- await fs19.mkdir(opts.outDir, { recursive: true });
6767
+ await fs17.mkdir(opts.outDir, { recursive: true });
7017
6768
  } catch (err) {
7018
6769
  result.failures.push(
7019
6770
  `couldn't create ${opts.outDir} (${err instanceof Error ? err.message : String(err)})`
@@ -7022,12 +6773,12 @@ async function downloadAssets(files, opts) {
7022
6773
  }
7023
6774
  const multi = wanted.length > 1;
7024
6775
  for (const file of wanted) {
7025
- const dest = path18.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
6776
+ const dest = path16.join(opts.outDir, localAssetName(opts.kind, opts.prompt, opts.id, file, multi));
7026
6777
  try {
7027
6778
  const res = await fetch(file.url);
7028
6779
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
7029
6780
  const buf = Buffer.from(await res.arrayBuffer());
7030
- await fs19.writeFile(dest, buf);
6781
+ await fs17.writeFile(dest, buf);
7031
6782
  result.saved.push({ role: file.role, path: dest, url: file.url, bytes: buf.byteLength });
7032
6783
  } catch (err) {
7033
6784
  result.failures.push(
@@ -7082,7 +6833,7 @@ function mockLaneRefusal(kind, provider) {
7082
6833
  }
7083
6834
 
7084
6835
  // src/lib/open.ts
7085
- import { spawn as spawn5 } from "child_process";
6836
+ import { spawn as spawn4 } from "child_process";
7086
6837
  function tokenize(cmd) {
7087
6838
  return cmd.trim().split(/\s+/).filter(Boolean);
7088
6839
  }
@@ -7111,7 +6862,7 @@ function openUrl(url) {
7111
6862
  }
7112
6863
  }
7113
6864
  try {
7114
- const child = spawn5(command, args, { stdio: "ignore", detached: true });
6865
+ const child = spawn4(command, args, { stdio: "ignore", detached: true });
7115
6866
  child.on("error", () => {
7116
6867
  });
7117
6868
  child.unref();
@@ -7171,7 +6922,7 @@ var isRemoteRef = (value) => /^(https?:\/\/|data:)/i.test(value);
7171
6922
  async function inlineLocalImage(filePath, flag) {
7172
6923
  let bytes;
7173
6924
  try {
7174
- bytes = await fs20.readFile(filePath);
6925
+ bytes = await fs18.readFile(filePath);
7175
6926
  } catch {
7176
6927
  return { ok: false, error: `Couldn't read the ${flag} file at ${filePath}.` };
7177
6928
  }
@@ -7181,7 +6932,7 @@ async function inlineLocalImage(filePath, flag) {
7181
6932
  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.`
7182
6933
  };
7183
6934
  }
7184
- const mime = IMAGE_MIME_BY_EXT[path19.extname(filePath).toLowerCase()] ?? "image/png";
6935
+ const mime = IMAGE_MIME_BY_EXT[path17.extname(filePath).toLowerCase()] ?? "image/png";
7185
6936
  return { ok: true, dataUri: `data:${mime};base64,${bytes.toString("base64")}` };
7186
6937
  }
7187
6938
  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.";
@@ -7339,7 +7090,7 @@ async function runGenerate(kind, opts) {
7339
7090
  let typedPrompt = opts.prompt?.trim();
7340
7091
  if (!typedPrompt && kind === "model" && opts.imageUrl) {
7341
7092
  const ref = opts.imageUrl.startsWith("data:") ? "local image" : opts.imageUrl;
7342
- typedPrompt = `from image: ${path19.basename(ref).slice(0, 120)}`;
7093
+ typedPrompt = `from image: ${path17.basename(ref).slice(0, 120)}`;
7343
7094
  }
7344
7095
  if (!typedPrompt) {
7345
7096
  log.error(`Missing prompt. Usage: ${c.cyan(`genex ${kind} "<prompt>"`)}`);
@@ -7383,7 +7134,7 @@ async function runGenerate(kind, opts) {
7383
7134
  return;
7384
7135
  }
7385
7136
  try {
7386
- const bytes = await fs20.readFile(opts.inpaintUrl);
7137
+ const bytes = await fs18.readFile(opts.inpaintUrl);
7387
7138
  opts = { ...opts, inpaintUrl: `data:image/png;base64,${bytes.toString("base64")}` };
7388
7139
  } catch {
7389
7140
  log.error(`Couldn't read the --inpaint mask at ${opts.inpaintUrl}.`);
@@ -7568,7 +7319,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
7568
7319
  return;
7569
7320
  }
7570
7321
  await recordTerminal(view.id, "completed", files.map((f) => f.url));
7571
- await fs20.mkdir(outDir, { recursive: true });
7322
+ await fs18.mkdir(outDir, { recursive: true });
7572
7323
  const solved = [];
7573
7324
  for (let i = 0; i < files.length; i++) {
7574
7325
  const f = files[i];
@@ -7600,8 +7351,8 @@ async function reportGlassTerminal(view, outDir, log, json) {
7600
7351
  });
7601
7352
  continue;
7602
7353
  }
7603
- const outPath = path19.join(outDir, `glass-${i + 1}.png`);
7604
- await fs20.writeFile(outPath, PNG4.sync.write(r.png));
7354
+ const outPath = path17.join(outDir, `glass-${i + 1}.png`);
7355
+ await fs18.writeFile(outPath, PNG4.sync.write(r.png));
7605
7356
  solved.push({
7606
7357
  path: outPath,
7607
7358
  url: f.url,
@@ -8367,8 +8118,8 @@ async function toRow(e, v, cwd) {
8367
8118
  }
8368
8119
 
8369
8120
  // src/commands/controller.ts
8370
- import fs22 from "fs/promises";
8371
- import path21 from "path";
8121
+ import fs20 from "fs/promises";
8122
+ import path19 from "path";
8372
8123
 
8373
8124
  // ../../packages/meshy-animation-catalog/src/index.ts
8374
8125
  import { createHash } from "crypto";
@@ -17498,9 +17249,9 @@ function searchMeshyAnimations(query, options = {}) {
17498
17249
  }
17499
17250
 
17500
17251
  // src/lib/anims.ts
17501
- import fs21 from "fs/promises";
17502
- import path20 from "path";
17503
- var ANIMS_DEST = path20.join("public", "assets", "anims");
17252
+ import fs19 from "fs/promises";
17253
+ import path18 from "path";
17254
+ var ANIMS_DEST = path18.join("public", "assets", "anims");
17504
17255
  var HIDDEN_TAG = "reference";
17505
17256
  async function runAnims(opts) {
17506
17257
  const log = createLogger({ quiet: opts.quiet });
@@ -17516,7 +17267,7 @@ async function runAnims(opts) {
17516
17267
  printCatalog(log, manifest, selectors);
17517
17268
  return;
17518
17269
  }
17519
- const controllerMarker = path20.join(root, "src", "controllers", "character");
17270
+ const controllerMarker = path18.join(root, "src", "controllers", "character");
17520
17271
  if (!await exists2(controllerMarker)) {
17521
17272
  log.error(
17522
17273
  `No character controller in this game (missing ${c.cyan("src/controllers/character/")}).`
@@ -17525,11 +17276,11 @@ async function runAnims(opts) {
17525
17276
  process.exitCode = 1;
17526
17277
  return;
17527
17278
  }
17528
- const destDir = path20.join(root, ANIMS_DEST);
17529
- const gameManifestPath = path20.join(destDir, "manifest.json");
17279
+ const destDir = path18.join(root, ANIMS_DEST);
17280
+ const gameManifestPath = path18.join(destDir, "manifest.json");
17530
17281
  if (opts.reset) {
17531
- await fs21.rm(destDir, { recursive: true, force: true });
17532
- log.step(`Cleared ${c.cyan(ANIMS_DEST + path20.sep)} (--reset)`);
17282
+ await fs19.rm(destDir, { recursive: true, force: true });
17283
+ log.step(`Cleared ${c.cyan(ANIMS_DEST + path18.sep)} (--reset)`);
17533
17284
  }
17534
17285
  if (selectors.length === 0) {
17535
17286
  const installed = await readGameManifest(gameManifestPath);
@@ -17567,35 +17318,35 @@ async function runAnims(opts) {
17567
17318
  }
17568
17319
  }
17569
17320
  const wanted = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
17570
- const cacheDir = path20.join(
17321
+ const cacheDir = path18.join(
17571
17322
  opts.cacheDir ?? getAnimsCacheDir(),
17572
17323
  `${manifest.library}-v${manifest.version}`
17573
17324
  );
17574
- await fs21.mkdir(cacheDir, { recursive: true });
17575
- await fs21.mkdir(destDir, { recursive: true });
17325
+ await fs19.mkdir(cacheDir, { recursive: true });
17326
+ await fs19.mkdir(destDir, { recursive: true });
17576
17327
  const base = getAnimsBase(opts.animsBase);
17577
17328
  let installedCount = 0;
17578
17329
  let presentCount = 0;
17579
17330
  let addedBytes = 0;
17580
17331
  const failures = [];
17581
17332
  for (const entry of wanted) {
17582
- const dest = path20.join(destDir, entry.file);
17333
+ const dest = path18.join(destDir, entry.file);
17583
17334
  if (await hasSize(dest, entry.bytes)) {
17584
17335
  presentCount++;
17585
17336
  continue;
17586
17337
  }
17587
17338
  try {
17588
- const cached = path20.join(cacheDir, entry.file);
17339
+ const cached = path18.join(cacheDir, entry.file);
17589
17340
  if (!await hasSize(cached, entry.bytes)) {
17590
17341
  const res = await fetch(base + entry.file);
17591
17342
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
17592
17343
  const buf = Buffer.from(await res.arrayBuffer());
17593
- await fs21.writeFile(cached, buf);
17344
+ await fs19.writeFile(cached, buf);
17594
17345
  }
17595
- await fs21.copyFile(cached, dest);
17346
+ await fs19.copyFile(cached, dest);
17596
17347
  installedCount++;
17597
17348
  addedBytes += entry.bytes;
17598
- log.dim(` ${path20.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
17349
+ log.dim(` ${path18.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
17599
17350
  } catch (err) {
17600
17351
  failures.push(`${entry.name} (${err instanceof Error ? err.message : String(err)})`);
17601
17352
  }
@@ -17611,13 +17362,13 @@ async function runAnims(opts) {
17611
17362
  version: manifest.version,
17612
17363
  clips: [...union].sort((a, b) => a.localeCompare(b))
17613
17364
  };
17614
- await fs21.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
17365
+ await fs19.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
17615
17366
  log.plain("");
17616
17367
  const parts = [`${installedCount} clip${installedCount === 1 ? "" : "s"} installed`];
17617
17368
  if (presentCount > 0) parts.push(`${presentCount} already present`);
17618
17369
  if (bundledSkips > 0) parts.push(`${bundledSkips} already bundled in animation-library.glb`);
17619
17370
  log.success(
17620
- `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path20.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
17371
+ `${parts.join(", ")} \u2192 ${c.cyan(ANIMS_DEST + path18.sep)}${addedBytes > 0 ? ` (+${formatMb(addedBytes)})` : ""}`
17621
17372
  );
17622
17373
  for (const [selector, entries] of resolved) {
17623
17374
  const names = entries.filter((e) => !coreNames.has(e.name)).map((e) => e.name);
@@ -17649,8 +17400,8 @@ async function loadManifest(baseOverride) {
17649
17400
  }
17650
17401
  } catch {
17651
17402
  }
17652
- const snapshotPath = path20.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
17653
- const manifest = JSON.parse(await fs21.readFile(snapshotPath, "utf8"));
17403
+ const snapshotPath = path18.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
17404
+ const manifest = JSON.parse(await fs19.readFile(snapshotPath, "utf8"));
17654
17405
  return { manifest, source: "snapshot" };
17655
17406
  }
17656
17407
  function resolveSelectors(manifest, selectors) {
@@ -17768,21 +17519,21 @@ function printCatalog(log, manifest, selectors) {
17768
17519
  }
17769
17520
  async function readGameManifest(file) {
17770
17521
  try {
17771
- return JSON.parse(await fs21.readFile(file, "utf8"));
17522
+ return JSON.parse(await fs19.readFile(file, "utf8"));
17772
17523
  } catch {
17773
17524
  return null;
17774
17525
  }
17775
17526
  }
17776
17527
  async function hasSize(file, bytes) {
17777
17528
  try {
17778
- return (await fs21.stat(file)).size === bytes;
17529
+ return (await fs19.stat(file)).size === bytes;
17779
17530
  } catch {
17780
17531
  return false;
17781
17532
  }
17782
17533
  }
17783
17534
  async function exists2(p) {
17784
17535
  try {
17785
- await fs21.access(p);
17536
+ await fs19.access(p);
17786
17537
  return true;
17787
17538
  } catch {
17788
17539
  return false;
@@ -17976,8 +17727,8 @@ var CONTROLLER_FILE_SETS = {
17976
17727
  ]
17977
17728
  }
17978
17729
  };
17979
- var CODE_DEST = path21.join("src", "controllers");
17980
- var ASSETS_DEST = path21.join("public", "assets");
17730
+ var CODE_DEST = path19.join("src", "controllers");
17731
+ var ASSETS_DEST = path19.join("public", "assets");
17981
17732
  async function runController(opts) {
17982
17733
  const log = createLogger({ quiet: opts.quiet });
17983
17734
  if (opts.kind?.trim() === "anims") {
@@ -17994,31 +17745,31 @@ async function runController(opts) {
17994
17745
  process.exitCode = 1;
17995
17746
  return;
17996
17747
  }
17997
- const srcDir = path21.join(getTemplatesDir(), "controllers");
17748
+ const srcDir = path19.join(getTemplatesDir(), "controllers");
17998
17749
  const root = opts.cwd ?? process.cwd();
17999
17750
  const set = CONTROLLER_FILE_SETS[kind];
18000
17751
  log.plain(c.bold(`genex controller ${kind}`));
18001
17752
  log.plain("");
18002
- log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path21.sep)}`);
17753
+ log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path19.sep)}`);
18003
17754
  const plan = [
18004
- ...set.code.map((rel) => ({ from: rel, rel: path21.join(CODE_DEST, rel) })),
17755
+ ...set.code.map((rel) => ({ from: rel, rel: path19.join(CODE_DEST, rel) })),
18005
17756
  ...set.assets.map((rel) => ({
18006
17757
  from: rel,
18007
- rel: path21.join(ASSETS_DEST, path21.basename(rel))
17758
+ rel: path19.join(ASSETS_DEST, path19.basename(rel))
18008
17759
  }))
18009
17760
  ];
18010
17761
  let copied = 0;
18011
17762
  let skipped = 0;
18012
17763
  try {
18013
17764
  for (const file of plan) {
18014
- const dest = path21.join(root, file.rel);
17765
+ const dest = path19.join(root, file.rel);
18015
17766
  if (!opts.force && await exists3(dest)) {
18016
17767
  skipped++;
18017
17768
  log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
18018
17769
  continue;
18019
17770
  }
18020
- await fs22.mkdir(path21.dirname(dest), { recursive: true });
18021
- await fs22.copyFile(path21.join(srcDir, file.from), dest);
17771
+ await fs20.mkdir(path19.dirname(dest), { recursive: true });
17772
+ await fs20.copyFile(path19.join(srcDir, file.from), dest);
18022
17773
  copied++;
18023
17774
  log.dim(` ${file.rel}`);
18024
17775
  }
@@ -18071,7 +17822,7 @@ async function runController(opts) {
18071
17822
  for (const line of set.sketch) {
18072
17823
  log.dim(` ${line}`);
18073
17824
  }
18074
- if (kind === "character" && !await exists3(path21.join(root, ASSETS_DEST, "meshy-character.json"))) {
17825
+ if (kind === "character" && !await exists3(path19.join(root, ASSETS_DEST, "meshy-character.json"))) {
18075
17826
  log.plain("");
18076
17827
  log.plain(
18077
17828
  ` ${stepOffset + 3}. This game has no generated character yet \u2014 ${c.cyan(
@@ -18103,9 +17854,9 @@ async function installMeshyCharacterManifest(args) {
18103
17854
  throw new Error("The API returned an invalid Meshy character manifest.");
18104
17855
  }
18105
17856
  assertCompleteMeshyControllerPack(manifest);
18106
- const destination = path21.join(args.root, ASSETS_DEST, "meshy-character.json");
18107
- await fs22.mkdir(path21.dirname(destination), { recursive: true });
18108
- await fs22.writeFile(
17857
+ const destination = path19.join(args.root, ASSETS_DEST, "meshy-character.json");
17858
+ await fs20.mkdir(path19.dirname(destination), { recursive: true });
17859
+ await fs20.writeFile(
18109
17860
  destination,
18110
17861
  `${JSON.stringify(manifest, null, 2)}
18111
17862
  `
@@ -18243,14 +17994,14 @@ function assertCompleteMeshyControllerPack(manifest) {
18243
17994
  }
18244
17995
  async function installFallbackAvatar(args) {
18245
17996
  const { root, srcDir, log } = args;
18246
- const dest = path21.join(root, ASSETS_DEST, "avatar.vrm");
18247
- await fs22.mkdir(path21.dirname(dest), { recursive: true });
18248
- await fs22.copyFile(path21.join(srcDir, "assets", "default-avatar.vrm"), dest);
17997
+ const dest = path19.join(root, ASSETS_DEST, "avatar.vrm");
17998
+ await fs20.mkdir(path19.dirname(dest), { recursive: true });
17999
+ await fs20.copyFile(path19.join(srcDir, "assets", "default-avatar.vrm"), dest);
18249
18000
  log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
18250
18001
  }
18251
18002
  async function exists3(p) {
18252
18003
  try {
18253
- await fs22.access(p);
18004
+ await fs20.access(p);
18254
18005
  return true;
18255
18006
  } catch {
18256
18007
  return false;
@@ -18258,8 +18009,8 @@ async function exists3(p) {
18258
18009
  }
18259
18010
 
18260
18011
  // src/commands/character.ts
18261
- import fs23 from "fs/promises";
18262
- import path22 from "path";
18012
+ import fs21 from "fs/promises";
18013
+ import path20 from "path";
18263
18014
  function exactAnimation(selector) {
18264
18015
  const trimmed = selector.trim();
18265
18016
  if (/^-?\d+$/.test(trimmed)) return animationById(Number(trimmed));
@@ -18339,7 +18090,7 @@ function showAmbiguity(selector, candidates, log, json = false) {
18339
18090
  }
18340
18091
  process.exitCode = 1;
18341
18092
  }
18342
- var INSTALLED_MANIFEST = path22.join("public", "assets", "meshy-character.json");
18093
+ var INSTALLED_MANIFEST = path20.join("public", "assets", "meshy-character.json");
18343
18094
  async function resolveAdoptTarget(selector) {
18344
18095
  const trimmed = selector?.trim();
18345
18096
  if (trimmed && !trimmed.endsWith(".json")) {
@@ -18348,7 +18099,7 @@ async function resolveAdoptTarget(selector) {
18348
18099
  const file = trimmed ?? INSTALLED_MANIFEST;
18349
18100
  let raw;
18350
18101
  try {
18351
- raw = await fs23.readFile(file, "utf8");
18102
+ raw = await fs21.readFile(file, "utf8");
18352
18103
  } catch {
18353
18104
  return {
18354
18105
  ok: false,
@@ -18831,22 +18582,22 @@ async function context2(opts) {
18831
18582
  const project = await readProject();
18832
18583
  return { token, apiUrl: getApiUrl(opts.apiUrl ?? project?.apiUrl) };
18833
18584
  }
18834
- async function readVideo(path29, log) {
18585
+ async function readVideo(path27, log) {
18835
18586
  let bytes;
18836
18587
  try {
18837
- bytes = await readFile(path29);
18588
+ bytes = await readFile(path27);
18838
18589
  } catch {
18839
- log.error(`Can't read ${path29}.`);
18590
+ log.error(`Can't read ${path27}.`);
18840
18591
  return null;
18841
18592
  }
18842
18593
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
18843
- log.error(`${basename(path29)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
18594
+ log.error(`${basename(path27)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
18844
18595
  return null;
18845
18596
  }
18846
18597
  return bytes;
18847
18598
  }
18848
- async function uploadVideo(apiUrl, token, characterId, path29, bytes, log) {
18849
- const contentType = /\.mov$/i.test(path29) ? "video/quicktime" : "video/mp4";
18599
+ async function uploadVideo(apiUrl, token, characterId, path27, bytes, log) {
18600
+ const contentType = /\.mov$/i.test(path27) ? "video/quicktime" : "video/mp4";
18850
18601
  const minted = await apiFetch(
18851
18602
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
18852
18603
  {
@@ -18861,7 +18612,7 @@ async function uploadVideo(apiUrl, token, characterId, path29, bytes, log) {
18861
18612
  return null;
18862
18613
  }
18863
18614
  const { uploadUrl, videoUrl } = await minted.json();
18864
- log.dim(` uploading ${basename(path29)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
18615
+ log.dim(` uploading ${basename(path27)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
18865
18616
  const put = await fetch(uploadUrl, {
18866
18617
  method: "PUT",
18867
18618
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -19177,8 +18928,8 @@ function rank(items, query) {
19177
18928
  }
19178
18929
 
19179
18930
  // src/commands/motion.ts
19180
- import fs24 from "fs/promises";
19181
- import path23 from "path";
18931
+ import fs22 from "fs/promises";
18932
+ import path21 from "path";
19182
18933
 
19183
18934
  // src/lib/motion/npz.ts
19184
18935
  import zlib from "zlib";
@@ -20429,7 +20180,7 @@ async function motionGen(opts, log) {
20429
20180
  }
20430
20181
  if (opts.constraintsPath !== void 0) {
20431
20182
  try {
20432
- const raw = await fs24.readFile(opts.constraintsPath, "utf8");
20183
+ const raw = await fs22.readFile(opts.constraintsPath, "utf8");
20433
20184
  generationOptions.constraints = JSON.parse(raw);
20434
20185
  } catch {
20435
20186
  log.error(`Couldn't read the --constraints JSON at ${opts.constraintsPath}.`);
@@ -20453,10 +20204,10 @@ async function motionGen(opts, log) {
20453
20204
  async function expandTakes(selectors) {
20454
20205
  const out = [];
20455
20206
  for (const sel of selectors) {
20456
- const st = await fs24.stat(sel).catch(() => null);
20207
+ const st = await fs22.stat(sel).catch(() => null);
20457
20208
  if (st?.isDirectory()) {
20458
- const names = await fs24.readdir(sel);
20459
- for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path23.join(sel, n));
20209
+ const names = await fs22.readdir(sel);
20210
+ for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path21.join(sel, n));
20460
20211
  } else if (st?.isFile()) {
20461
20212
  out.push(sel);
20462
20213
  } else {
@@ -20491,7 +20242,7 @@ async function motionVerify(opts, log) {
20491
20242
  let gates = DEFAULT_GATES;
20492
20243
  if (opts.gatesPath) {
20493
20244
  try {
20494
- gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs24.readFile(opts.gatesPath, "utf8")));
20245
+ gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs22.readFile(opts.gatesPath, "utf8")));
20495
20246
  } catch {
20496
20247
  log.error(`Couldn't read the --gates JSON at ${opts.gatesPath}.`);
20497
20248
  process.exitCode = 1;
@@ -20513,9 +20264,9 @@ async function motionVerify(opts, log) {
20513
20264
  }
20514
20265
  const reports = [];
20515
20266
  for (const file of files) {
20516
- const stem = path23.basename(file).replace(/\.npz$/, "");
20267
+ const stem = path21.basename(file).replace(/\.npz$/, "");
20517
20268
  try {
20518
- reports.push(analyzeTake(stem, await fs24.readFile(file), gates));
20269
+ reports.push(analyzeTake(stem, await fs22.readFile(file), gates));
20519
20270
  } catch (err) {
20520
20271
  reports.push({
20521
20272
  take: stem,
@@ -20553,7 +20304,7 @@ async function motionCompile(opts, log) {
20553
20304
  let cfg = DEFAULT_MOTION_CONFIG;
20554
20305
  if (opts.configPath) {
20555
20306
  try {
20556
- const patch = JSON.parse(await fs24.readFile(opts.configPath, "utf8"));
20307
+ const patch = JSON.parse(await fs22.readFile(opts.configPath, "utf8"));
20557
20308
  cfg = { ...DEFAULT_MOTION_CONFIG, ...patch, idleLoop: { ...DEFAULT_MOTION_CONFIG.idleLoop, ...patch.idleLoop } };
20558
20309
  } catch {
20559
20310
  log.error(`Couldn't read the --config JSON at ${opts.configPath}.`);
@@ -20571,16 +20322,16 @@ async function motionCompile(opts, log) {
20571
20322
  }
20572
20323
  const inputs = [];
20573
20324
  for (const file of files) {
20574
- const stem = path23.basename(file).replace(/\.npz$/, "");
20325
+ const stem = path21.basename(file).replace(/\.npz$/, "");
20575
20326
  try {
20576
- inputs.push({ stem, take: loadTake(await fs24.readFile(file)) });
20327
+ inputs.push({ stem, take: loadTake(await fs22.readFile(file)) });
20577
20328
  } catch (err) {
20578
20329
  log.error(`${stem}: ${err instanceof Error ? err.message : String(err)}`);
20579
20330
  process.exitCode = 1;
20580
20331
  return;
20581
20332
  }
20582
20333
  }
20583
- const setName = opts.set ?? path23.basename(opts.out).replace(/\.json$/, "");
20334
+ const setName = opts.set ?? path21.basename(opts.out).replace(/\.json$/, "");
20584
20335
  let result;
20585
20336
  try {
20586
20337
  result = compileSet(inputs, setName, cfg);
@@ -20595,9 +20346,9 @@ async function motionCompile(opts, log) {
20595
20346
  process.exitCode = 1;
20596
20347
  return;
20597
20348
  }
20598
- await fs24.mkdir(path23.dirname(path23.resolve(opts.out)), { recursive: true });
20349
+ await fs22.mkdir(path21.dirname(path21.resolve(opts.out)), { recursive: true });
20599
20350
  const json = JSON.stringify(result.data);
20600
- await fs24.writeFile(opts.out, json);
20351
+ await fs22.writeFile(opts.out, json);
20601
20352
  if (opts.json) {
20602
20353
  writeJson({ out: opts.out, set: setName, tracks: Object.keys(result.data.gaits), bytes: json.length });
20603
20354
  return;
@@ -20615,9 +20366,9 @@ var MOTION_RUNTIME_FILES = [
20615
20366
  var MOTION_PRESETS = {
20616
20367
  rifle: ["sets/rifle.json", "sets/jumps.json"]
20617
20368
  };
20618
- var MOTION_DEST = path23.join("src", "motion");
20369
+ var MOTION_DEST = path21.join("src", "motion");
20619
20370
  async function motionInstall(opts, log) {
20620
- const srcDir = path23.join(getTemplatesDir(), "motion");
20371
+ const srcDir = path21.join(getTemplatesDir(), "motion");
20621
20372
  const root = opts.cwd ?? process.cwd();
20622
20373
  const preset = opts.set;
20623
20374
  if (preset !== void 0 && !MOTION_PRESETS[preset]) {
@@ -20628,21 +20379,21 @@ async function motionInstall(opts, log) {
20628
20379
  const files = [...MOTION_RUNTIME_FILES, ...preset ? MOTION_PRESETS[preset] : []];
20629
20380
  log.plain(c.bold(`genex motion install${preset ? ` --set ${preset}` : ""}`));
20630
20381
  log.plain("");
20631
- log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path23.sep)}`);
20382
+ log.step(`Vendoring the motion runtime into ${c.cyan(MOTION_DEST + path21.sep)}`);
20632
20383
  let copied = 0, skipped = 0;
20633
20384
  try {
20634
20385
  for (const rel of files) {
20635
- const dest = path23.join(root, MOTION_DEST, rel);
20636
- const exists5 = await fs24.access(dest).then(() => true, () => false);
20386
+ const dest = path21.join(root, MOTION_DEST, rel);
20387
+ const exists5 = await fs22.access(dest).then(() => true, () => false);
20637
20388
  if (!opts.force && exists5) {
20638
20389
  skipped++;
20639
- log.dim(` skipped ${path23.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
20390
+ log.dim(` skipped ${path21.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
20640
20391
  continue;
20641
20392
  }
20642
- await fs24.mkdir(path23.dirname(dest), { recursive: true });
20643
- await fs24.copyFile(path23.join(srcDir, rel), dest);
20393
+ await fs22.mkdir(path21.dirname(dest), { recursive: true });
20394
+ await fs22.copyFile(path21.join(srcDir, rel), dest);
20644
20395
  copied++;
20645
- log.dim(` ${path23.join(MOTION_DEST, rel)}`);
20396
+ log.dim(` ${path21.join(MOTION_DEST, rel)}`);
20646
20397
  }
20647
20398
  } catch (err) {
20648
20399
  log.error(`Copy failed: ${String(err)}`);
@@ -20683,7 +20434,7 @@ async function motionConstraints(opts, log) {
20683
20434
  }
20684
20435
  const doc = directionConstraint(dir, speed, duration);
20685
20436
  const out = opts.out ?? "constraints.json";
20686
- await fs24.writeFile(out, JSON.stringify(doc));
20437
+ await fs22.writeFile(out, JSON.stringify(doc));
20687
20438
  if (opts.json) {
20688
20439
  writeJson({ out, dir: dirName, speed, duration, waypoints: doc[0].frame_indices.length });
20689
20440
  return;
@@ -20720,14 +20471,14 @@ async function runMotion(opts) {
20720
20471
  }
20721
20472
 
20722
20473
  // src/commands/blender.ts
20723
- import fs25 from "fs/promises";
20724
- import path24 from "path";
20725
- var SUBS2 = ["demo", "exec", "snap", "scene", "export", "reset", "mcp"];
20474
+ import fs23 from "fs/promises";
20475
+ import path22 from "path";
20476
+ var SUBS2 = ["demo", "exec", "snap", "scene", "export", "reset", "mcp", "serve"];
20726
20477
  var DEFAULT_OUT_DIR = "assets/blender";
20727
20478
  async function writeB64(dir, name, b64) {
20728
- await fs25.mkdir(dir, { recursive: true });
20729
- const p = path24.join(dir, name);
20730
- await fs25.writeFile(p, Buffer.from(b64, "base64"));
20479
+ await fs23.mkdir(dir, { recursive: true });
20480
+ const p = path22.join(dir, name);
20481
+ await fs23.writeFile(p, Buffer.from(b64, "base64"));
20731
20482
  return p;
20732
20483
  }
20733
20484
  function reportScene(log, s) {
@@ -20743,18 +20494,37 @@ async function runBlender(opts) {
20743
20494
  log.error(`Usage: genex blender <${SUBS2.join("|")}>`);
20744
20495
  return 1;
20745
20496
  }
20497
+ if (sub === "serve") {
20498
+ const { serveLocalBlender } = await import("./blender-serve-BF4FZ55Z.js");
20499
+ const port = Number(process.env.GENEX_BLENDER_PORT ?? 8088);
20500
+ log.step(`Starting a local Blender service on port ${port}`);
20501
+ log.plain(
20502
+ ` ${c.dim("Then, in another terminal:")} ${c.cyan(`export GENEX_BLENDER_URL=http://localhost:${port}`)}`
20503
+ );
20504
+ return serveLocalBlender({ port, log });
20505
+ }
20746
20506
  if (sub === "mcp") {
20747
- const { runBlenderMcp } = await import("./blender-mcp-OXFN3RVI.js");
20507
+ const { runBlenderMcp } = await import("./blender-mcp-LLN4FPA4.js");
20748
20508
  return runBlenderMcp();
20749
20509
  }
20750
- const base = blenderEndpoint();
20510
+ let base = blenderEndpoint();
20511
+ const seat = await acquireSeat({ log });
20512
+ if (seat.kind === "granted") {
20513
+ base = seat.grant.url;
20514
+ } else if (seat.kind === "warming") {
20515
+ log.warn("The Blender seat is still warming up. Run the same command again in a minute.");
20516
+ return 0;
20517
+ } else if (seat.kind === "refused") {
20518
+ log.error(`No Blender seat: ${seat.reason}`);
20519
+ return 1;
20520
+ }
20751
20521
  if (!base) {
20752
20522
  const [first, ...rest] = BLENDER_SETUP_HINT.split("\n");
20753
20523
  log.error(first ?? "GENEX_BLENDER_URL is not set");
20754
20524
  log.plain(rest.join("\n"));
20755
20525
  return 1;
20756
20526
  }
20757
- const outDir = path24.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
20527
+ const outDir = path22.resolve(opts.outDir ?? DEFAULT_OUT_DIR);
20758
20528
  const mode = opts.mode;
20759
20529
  if (mode !== void 0 && !isRenderMode(mode)) {
20760
20530
  log.error(`Unknown --mode ${mode}. Use one of: ${RENDER_MODES.join(", ")}.`);
@@ -20781,18 +20551,23 @@ async function runBlender(opts) {
20781
20551
  return 0;
20782
20552
  }
20783
20553
  case "snap": {
20784
- const r = await blenderCall(base, "/render", mode ? { mode } : {});
20554
+ const r = await blenderCall(base, "/render", { ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
20785
20555
  const used = String(r.mode ?? mode ?? "default");
20786
- const p = await writeB64(outDir, `snap-${used}.png`, r.contactSheetPng ?? "");
20787
- log.success(`${used} sheet in ${r.ms ?? "?"}ms`);
20556
+ const sheet = sheetOf(r);
20557
+ if (!sheet) {
20558
+ log.error(`no sheet returned${r.contactSheetTooLarge ? ` \u2014 ${r.contactSheetTooLarge.detail}` : ""}`);
20559
+ return 1;
20560
+ }
20561
+ const p = await writeB64(outDir, `snap-${used}.${sheetExt(sheet.mime)}`, sheet.b64);
20562
+ log.success(`${used} sheet in ${r.ms ?? "?"}ms ${c.dim(`(${sheet.mime})`)}`);
20788
20563
  log.plain(` ${c.cyan(p)}`);
20789
20564
  return 0;
20790
20565
  }
20791
20566
  case "export": {
20792
- const target = opts.out ?? path24.join(outDir, "scene.glb");
20567
+ const target = opts.out ?? path22.join(outDir, "scene.glb");
20793
20568
  const r = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
20794
- await fs25.mkdir(path24.dirname(target), { recursive: true });
20795
- await fs25.writeFile(target, Buffer.from(r.glbBase64 ?? "", "base64"));
20569
+ await fs23.mkdir(path22.dirname(target), { recursive: true });
20570
+ await fs23.writeFile(target, Buffer.from(r.glbBase64 ?? "", "base64"));
20796
20571
  log.success(`Exported ${r.bytes ?? 0} bytes`);
20797
20572
  log.plain(` ${c.cyan(target)}`);
20798
20573
  return 0;
@@ -20810,21 +20585,22 @@ async function runBlender(opts) {
20810
20585
  return 1;
20811
20586
  }
20812
20587
  try {
20813
- script = await fs25.readFile(opts.input, "utf8");
20588
+ script = await fs23.readFile(opts.input, "utf8");
20814
20589
  } catch {
20815
20590
  log.error(`Can't read ${opts.input}`);
20816
20591
  return 1;
20817
20592
  }
20818
- label = path24.basename(opts.input);
20593
+ label = path22.basename(opts.input);
20819
20594
  }
20820
- const r = await blenderCall(base, "/exec", mode ? { script, mode } : { script });
20595
+ const r = await blenderCall(base, "/exec", { script, ...mode ? { mode } : {}, sheet: { formats: SHEET_FORMATS } });
20821
20596
  if (r.stdout?.trim()) log.plain(r.stdout.trimEnd());
20822
20597
  if (r.stderr?.trim()) log.dim(r.stderr.trimEnd());
20823
20598
  if (r.error) {
20824
20599
  log.error(`${label} raised:`);
20825
20600
  log.plain(r.error.trimEnd());
20826
- if (r.contactSheetPng) {
20827
- const p = await writeB64(outDir, "contact-sheet.png", r.contactSheetPng);
20601
+ const failSheet = sheetOf(r);
20602
+ if (failSheet) {
20603
+ const p = await writeB64(outDir, `contact-sheet.${sheetExt(failSheet.mime)}`, failSheet.b64);
20828
20604
  log.plain(` ${c.dim("scene at failure:")} ${c.cyan(p)}`);
20829
20605
  }
20830
20606
  return 1;
@@ -20837,8 +20613,13 @@ async function runBlender(opts) {
20837
20613
  }
20838
20614
  reportScene(log, r.scene);
20839
20615
  const written = [];
20840
- if (r.contactSheetPng) {
20841
- written.push(await writeB64(outDir, "contact-sheet.png", r.contactSheetPng));
20616
+ const sheet = sheetOf(r);
20617
+ if (sheet) {
20618
+ written.push(await writeB64(outDir, `contact-sheet.${sheetExt(sheet.mime)}`, sheet.b64));
20619
+ } else if (r.contactSheetUnchanged) {
20620
+ log.dim(" scene unchanged \u2014 no new sheet rendered");
20621
+ } else if (r.contactSheetTooLarge) {
20622
+ log.warn(`sheet too large to inline (${r.contactSheetTooLarge.b64Bytes} b64 bytes)`);
20842
20623
  }
20843
20624
  if (sub === "demo") {
20844
20625
  const glb = await blenderCall(base, "/export", { path: "/tmp/genex-export.glb" });
@@ -20928,9 +20709,9 @@ print(f"castle: {n} objects")
20928
20709
  `;
20929
20710
 
20930
20711
  // src/commands/asset-new.ts
20931
- import fs26 from "fs";
20712
+ import fs24 from "fs";
20932
20713
  import fsp from "fs/promises";
20933
- import path25 from "path";
20714
+ import path23 from "path";
20934
20715
  import { pathToFileURL } from "url";
20935
20716
  var EXTRA_FILES = [
20936
20717
  "genex-asset.example.json",
@@ -21024,7 +20805,7 @@ Every milestone ends with \`npm run verify\`, then \`npx genex preview --no-buil
21024
20805
  }
21025
20806
  async function runAssetNew(options) {
21026
20807
  const log = createLogger();
21027
- const cwd = options.dir ? path25.resolve(options.dir) : process.cwd();
20808
+ const cwd = options.dir ? path23.resolve(options.dir) : process.cwd();
21028
20809
  const slug = options.assetSlug;
21029
20810
  if (!slug) {
21030
20811
  log.error('Usage: genex asset new <slug> [--title "<Name>"] [--dims WxDxH] [--tri-band LOW-HIGH] [--holder "<your name>"] [--out <dir>]');
@@ -21034,14 +20815,14 @@ async function runAssetNew(options) {
21034
20815
  log.error(`The slug must be kebab-case (letters, digits, dashes), got: ${slug}`);
21035
20816
  return 1;
21036
20817
  }
21037
- const templateDir = path25.join(getTemplatesDir(), "asset-viewer");
21038
- if (!fs26.existsSync(templateDir)) {
20818
+ const templateDir = path23.join(getTemplatesDir(), "asset-viewer");
20819
+ if (!fs24.existsSync(templateDir)) {
21039
20820
  log.error(`Vendored asset-viewer template not found at ${templateDir} \u2014 reinstall the CLI.`);
21040
20821
  return 1;
21041
20822
  }
21042
- const manifestTools = await import(pathToFileURL(path25.join(templateDir, "tools", "emit-manifest.mjs")).href);
20823
+ const manifestTools = await import(pathToFileURL(path23.join(templateDir, "tools", "emit-manifest.mjs")).href);
21043
20824
  const { SHARED_FILES, PARITY_FILENAME, hashSharedFiles, pascalCase } = manifestTools;
21044
- const lockPath = path25.join(templateDir, "shared-files.sha256.json");
20825
+ const lockPath = path23.join(templateDir, "shared-files.sha256.json");
21045
20826
  const lock = JSON.parse(await fsp.readFile(lockPath, "utf8"));
21046
20827
  const actual = hashSharedFiles(templateDir);
21047
20828
  const drifted = SHARED_FILES.filter((rel) => lock.files[rel] !== actual[rel]);
@@ -21054,8 +20835,8 @@ async function runAssetNew(options) {
21054
20835
  const triBand = parseBand(options.triBand ?? "500-8000");
21055
20836
  const holder = options.holder?.trim() || HOLDER_PLACEHOLDER;
21056
20837
  const year = (/* @__PURE__ */ new Date()).getUTCFullYear();
21057
- const outDir = path25.resolve(cwd, options.out ?? slug);
21058
- if (fs26.existsSync(outDir) && fs26.readdirSync(outDir).length > 0 && !options.force) {
20838
+ const outDir = path23.resolve(cwd, options.out ?? slug);
20839
+ if (fs24.existsSync(outDir) && fs24.readdirSync(outDir).length > 0 && !options.force) {
21059
20840
  log.error(`${outDir} already holds files. Nothing was touched \u2014 pass --force to write into it anyway.`);
21060
20841
  return 1;
21061
20842
  }
@@ -21083,24 +20864,24 @@ async function runAssetNew(options) {
21083
20864
  };
21084
20865
  await fsp.mkdir(outDir, { recursive: true });
21085
20866
  for (const rel of [...SHARED_FILES, ...EXTRA_FILES]) {
21086
- const to = path25.join(outDir, rel);
21087
- await fsp.mkdir(path25.dirname(to), { recursive: true });
21088
- await fsp.copyFile(path25.join(templateDir, rel), to);
20867
+ const to = path23.join(outDir, rel);
20868
+ await fsp.mkdir(path23.dirname(to), { recursive: true });
20869
+ await fsp.copyFile(path23.join(templateDir, rel), to);
21089
20870
  }
21090
- const pkg = fillTemplate(await fsp.readFile(path25.join(templateDir, "package.json"), "utf8"), {
20871
+ const pkg = fillTemplate(await fsp.readFile(path23.join(templateDir, "package.json"), "utf8"), {
21091
20872
  slug,
21092
20873
  name,
21093
20874
  version
21094
20875
  });
21095
- await fsp.writeFile(path25.join(outDir, "package.json"), pkg, "utf8");
21096
- await fsp.writeFile(path25.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
21097
- await fsp.writeFile(path25.join(outDir, ".gitignore"), GITIGNORE, "utf8");
20876
+ await fsp.writeFile(path23.join(outDir, "package.json"), pkg, "utf8");
20877
+ await fsp.writeFile(path23.join(outDir, "asset.config.json"), JSON.stringify(config, null, 2) + "\n", "utf8");
20878
+ await fsp.writeFile(path23.join(outDir, ".gitignore"), GITIGNORE, "utf8");
21098
20879
  await fsp.writeFile(
21099
- path25.join(outDir, "DESIGN.md"),
20880
+ path23.join(outDir, "DESIGN.md"),
21100
20881
  designDoc({ name, slug, sizeMeters, triBand, holder }),
21101
20882
  "utf8"
21102
20883
  );
21103
- const placeholder = await fsp.readFile(path25.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
20884
+ const placeholder = await fsp.readFile(path23.join(templateDir, "src", "asset", "PLACEHOLDER.ts"), "utf8");
21104
20885
  const seeded = seedAssetSource(placeholder, {
21105
20886
  slug,
21106
20887
  name,
@@ -21111,8 +20892,8 @@ async function runAssetNew(options) {
21111
20892
  pascalCase
21112
20893
  });
21113
20894
  const assetFilename = `${slug.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase())}.ts`;
21114
- await fsp.mkdir(path25.join(outDir, "src", "asset"), { recursive: true });
21115
- await fsp.writeFile(path25.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
20895
+ await fsp.mkdir(path23.join(outDir, "src", "asset"), { recursive: true });
20896
+ await fsp.writeFile(path23.join(outDir, "src", "asset", assetFilename), seeded, "utf8");
21116
20897
  const copied = hashSharedFiles(outDir);
21117
20898
  const mismatched = SHARED_FILES.filter((rel) => copied[rel] !== lock.files[rel]);
21118
20899
  if (mismatched.length) {
@@ -21120,7 +20901,7 @@ async function runAssetNew(options) {
21120
20901
  return 1;
21121
20902
  }
21122
20903
  await fsp.writeFile(
21123
- path25.join(outDir, PARITY_FILENAME),
20904
+ path23.join(outDir, PARITY_FILENAME),
21124
20905
  JSON.stringify(
21125
20906
  {
21126
20907
  note: "Recorded by `genex asset new`. Gate G13 recomputes these; do not hand-edit the viewer.",
@@ -21164,12 +20945,12 @@ async function runAssetNew(options) {
21164
20945
  }
21165
20946
 
21166
20947
  // src/commands/tools.ts
21167
- import path28 from "path";
20948
+ import path26 from "path";
21168
20949
 
21169
20950
  // src/lib/local-install.ts
21170
- import fs27 from "fs/promises";
21171
- import path26 from "path";
21172
- import { spawn as spawn6 } from "child_process";
20951
+ import fs25 from "fs/promises";
20952
+ import path24 from "path";
20953
+ import { spawn as spawn5 } from "child_process";
21173
20954
  var CLI_PACKAGE = "@genex-ai/cli-demo";
21174
20955
  var FULL_NAME_FALLBACK = `npx ${CLI_PACKAGE}@${CLI_CHANNEL}`;
21175
20956
  var LOCKFILES = [
@@ -21181,17 +20962,17 @@ var LOCKFILES = [
21181
20962
  ];
21182
20963
  async function exists4(p) {
21183
20964
  try {
21184
- await fs27.access(p);
20965
+ await fs25.access(p);
21185
20966
  return true;
21186
20967
  } catch {
21187
20968
  return false;
21188
20969
  }
21189
20970
  }
21190
20971
  async function detectPackageManager(cwd) {
21191
- let dir = path26.resolve(cwd);
20972
+ let dir = path24.resolve(cwd);
21192
20973
  for (; ; ) {
21193
20974
  try {
21194
- const raw = await fs27.readFile(path26.join(dir, "package.json"), "utf8");
20975
+ const raw = await fs25.readFile(path24.join(dir, "package.json"), "utf8");
21195
20976
  const pm = JSON.parse(raw).packageManager;
21196
20977
  if (typeof pm === "string") {
21197
20978
  const name = pm.split("@")[0];
@@ -21200,18 +20981,18 @@ async function detectPackageManager(cwd) {
21200
20981
  } catch {
21201
20982
  }
21202
20983
  for (const [file, pm] of LOCKFILES) {
21203
- if (await exists4(path26.join(dir, file))) return pm;
20984
+ if (await exists4(path24.join(dir, file))) return pm;
21204
20985
  }
21205
- const parent = path26.dirname(dir);
20986
+ const parent = path24.dirname(dir);
21206
20987
  if (parent === dir) return "npm";
21207
20988
  dir = parent;
21208
20989
  }
21209
20990
  }
21210
20991
  async function findLocalCli(cwd) {
21211
- let dir = path26.resolve(cwd);
20992
+ let dir = path24.resolve(cwd);
21212
20993
  for (; ; ) {
21213
- if (await exists4(path26.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
21214
- const parent = path26.dirname(dir);
20994
+ if (await exists4(path24.join(dir, "node_modules", CLI_PACKAGE, "package.json"))) return dir;
20995
+ const parent = path24.dirname(dir);
21215
20996
  if (parent === dir) return null;
21216
20997
  dir = parent;
21217
20998
  }
@@ -21229,7 +21010,7 @@ function installArgs(pm, spec) {
21229
21010
  }
21230
21011
  }
21231
21012
  function manifestName(cwd) {
21232
- const slug = path26.basename(path26.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
21013
+ const slug = path24.basename(path24.resolve(cwd)).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
21233
21014
  return slug || "genex-tools-workspace";
21234
21015
  }
21235
21016
  function isSourceRun(moduleUrl = import.meta.url) {
@@ -21246,7 +21027,7 @@ async function spawnInstall(command, args, cwd) {
21246
21027
  resolve({ ok, detail });
21247
21028
  };
21248
21029
  try {
21249
- const child = spawn6(command, args, {
21030
+ const child = spawn5(command, args, {
21250
21031
  cwd,
21251
21032
  // npm/pnpm/yarn are .cmd shims on Windows; the args carry no user input.
21252
21033
  shell: process.platform === "win32",
@@ -21262,9 +21043,9 @@ async function spawnInstall(command, args, cwd) {
21262
21043
  clearTimeout(timer);
21263
21044
  done(false, err.message);
21264
21045
  });
21265
- child.on("close", (code2) => {
21046
+ child.on("close", (code) => {
21266
21047
  clearTimeout(timer);
21267
- if (code2 === 0) done(true);
21048
+ if (code === 0) done(true);
21268
21049
  else done(false, output.trim().split("\n").slice(-3).join("\n"));
21269
21050
  });
21270
21051
  } catch (err) {
@@ -21287,10 +21068,10 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
21287
21068
  return;
21288
21069
  }
21289
21070
  const pm = await detectPackageManager(cwd);
21290
- const hadManifest = await exists4(path26.join(cwd, "package.json"));
21071
+ const hadManifest = await exists4(path24.join(cwd, "package.json"));
21291
21072
  if (!hadManifest) {
21292
- await fs27.writeFile(
21293
- path26.join(cwd, "package.json"),
21073
+ await fs25.writeFile(
21074
+ path24.join(cwd, "package.json"),
21294
21075
  JSON.stringify({ name: manifestName(cwd), private: true }, null, 2) + "\n"
21295
21076
  );
21296
21077
  await ensureIgnored(cwd, "node_modules/");
@@ -21310,20 +21091,20 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
21310
21091
  }
21311
21092
  }
21312
21093
  async function ensureIgnored(dir, entry) {
21313
- const file = path26.join(dir, ".gitignore");
21094
+ const file = path24.join(dir, ".gitignore");
21314
21095
  let content = "";
21315
21096
  try {
21316
- content = await fs27.readFile(file, "utf8");
21097
+ content = await fs25.readFile(file, "utf8");
21317
21098
  } catch {
21318
21099
  }
21319
21100
  if (content.split("\n").some((l) => l.trim() === entry)) return;
21320
21101
  let next = content;
21321
21102
  if (next.length > 0 && !next.endsWith("\n")) next += "\n";
21322
- await fs27.writeFile(file, next + entry + "\n");
21103
+ await fs25.writeFile(file, next + entry + "\n");
21323
21104
  }
21324
21105
 
21325
21106
  // src/commands/doctor.ts
21326
- import path27 from "path";
21107
+ import path25 from "path";
21327
21108
  var LANE_ORDER = [
21328
21109
  "model",
21329
21110
  "image",
@@ -21510,7 +21291,7 @@ function creditsRow(state, info) {
21510
21291
  fix: "Verify your email to unlock your free credits \u2014 then re-run."
21511
21292
  };
21512
21293
  }
21513
- const refill = info.refillAt ? ` \xB7 refills to ${info.refillTo} on ${shortDate2(info.refillAt)}` : "";
21294
+ const refill = info.refillAt ? ` \xB7 refills to ${info.refillTo} on ${shortDate(info.refillAt)}` : "";
21514
21295
  return {
21515
21296
  label: "Credits",
21516
21297
  value: `${info.balance}${refill}`,
@@ -21518,7 +21299,7 @@ function creditsRow(state, info) {
21518
21299
  fix: info.balance <= 0 ? "Out of credits \u2014 generation is refused until the refill or a top-up." : void 0
21519
21300
  };
21520
21301
  }
21521
- function shortDate2(iso) {
21302
+ function shortDate(iso) {
21522
21303
  const d = new Date(iso);
21523
21304
  return Number.isNaN(d.getTime()) ? iso : d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
21524
21305
  }
@@ -21551,7 +21332,7 @@ async function fetchCredits(apiUrl, token) {
21551
21332
  }
21552
21333
  async function firstSkillsMarker() {
21553
21334
  for (const target of resolveAgentTargets()) {
21554
- const marker = await readSkillsMarker(path27.join(target.baseDir, "skills"));
21335
+ const marker = await readSkillsMarker(path25.join(target.baseDir, "skills"));
21555
21336
  if (marker) return marker;
21556
21337
  }
21557
21338
  return null;
@@ -21590,8 +21371,8 @@ async function runTools(opts) {
21590
21371
  let totalNew = 0;
21591
21372
  let totalUpdated = 0;
21592
21373
  for (const t of targets) {
21593
- const dest = path28.join(t.baseDir, "skills");
21594
- const { copied, updated } = await copyTemplates(path28.join(templatesDir, "skills"), dest, {
21374
+ const dest = path26.join(t.baseDir, "skills");
21375
+ const { copied, updated } = await copyTemplates(path26.join(templatesDir, "skills"), dest, {
21595
21376
  filter: (rel) => skillFamilyFilter("tools")(`skills/${rel}`)
21596
21377
  });
21597
21378
  await pruneRemovedSkills(dest, log);