@genex-ai/cli-demo 1.36.10-dev.965 → 1.36.11-dev.967

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +877 -79
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -219,8 +219,8 @@ var pending = /* @__PURE__ */ new Set();
219
219
  function setSentryTag(key, value) {
220
220
  tags[key] = value;
221
221
  }
222
- function setSentryContext(name, context3) {
223
- contexts[name] = context3;
222
+ function setSentryContext(name, context4) {
223
+ contexts[name] = context4;
224
224
  }
225
225
  function beforeSend(event, hint) {
226
226
  const e = event;
@@ -1924,8 +1924,8 @@ async function ensureLocalCli(log, cwd = process.cwd(), opts = {}) {
1924
1924
  const spec = `${CLI_PACKAGE}@${CLI_CHANNEL}`;
1925
1925
  const { command, args } = installArgs(pm, spec);
1926
1926
  log.step(`Adding ${c.cyan(CLI_PACKAGE)} as a devDependency (${pm}) so npx genex resolves here\u2026`);
1927
- const run4 = opts.runner ?? spawnInstall;
1928
- const result = await run4(command, args, cwd);
1927
+ const run5 = opts.runner ?? spawnInstall;
1928
+ const result = await run5(command, args, cwd);
1929
1929
  if (result.ok) {
1930
1930
  log.success(`${c.cyan("npx genex")} now resolves in this folder.`);
1931
1931
  } else {
@@ -2255,13 +2255,13 @@ async function countFailed(kind, cwd = process.cwd()) {
2255
2255
  }
2256
2256
  async function trailingFailures(kind, cwd = process.cwd()) {
2257
2257
  const rows = (await readLedger(cwd)).filter((e) => e.kind === kind);
2258
- let run4 = 0;
2258
+ let run5 = 0;
2259
2259
  for (let i = rows.length - 1; i >= 0; i -= 1) {
2260
2260
  const status2 = rows[i].status;
2261
2261
  if (status2 === "completed") break;
2262
- if (status2 === "failed") run4 += 1;
2262
+ if (status2 === "failed") run5 += 1;
2263
2263
  }
2264
- return run4;
2264
+ return run5;
2265
2265
  }
2266
2266
  async function countOutcomes(kind, cwd = process.cwd()) {
2267
2267
  const rows = (await readLedger(cwd)).filter((e) => e.kind === kind);
@@ -3076,15 +3076,15 @@ async function detectSurfaceScan(cwd = process.cwd()) {
3076
3076
  detail: `repeat.set(${m[1]}, ${m[2]})`
3077
3077
  });
3078
3078
  }
3079
- for (const call of ctorArgs(content, "PointsMaterial")) {
3080
- if (/\b(map|alphaMap)\s*:/.test(call.text)) continue;
3079
+ for (const call2 of ctorArgs(content, "PointsMaterial")) {
3080
+ if (/\b(map|alphaMap)\s*:/.test(call2.text)) continue;
3081
3081
  found.squarePoints.push({
3082
- where: `${rel}:${lineOf(content, call.index)}`,
3082
+ where: `${rel}:${lineOf(content, call2.index)}`,
3083
3083
  detail: "PointsMaterial with no map/alphaMap"
3084
3084
  });
3085
3085
  }
3086
- for (const call of ctorArgs(content, "PerspectiveCamera")) {
3087
- const args = splitArgs(call.text);
3086
+ for (const call2 of ctorArgs(content, "PerspectiveCamera")) {
3087
+ const args = splitArgs(call2.text);
3088
3088
  if (args.length < 4) continue;
3089
3089
  const near = Number(args[2]);
3090
3090
  const far = Number(args[3]);
@@ -3092,7 +3092,7 @@ async function detectSurfaceScan(cwd = process.cwd()) {
3092
3092
  const ratio = far / near;
3093
3093
  if (ratio <= DEPTH_RATIO_LIMIT) continue;
3094
3094
  found.depthRange.push({
3095
- where: `${rel}:${lineOf(content, call.index)}`,
3095
+ where: `${rel}:${lineOf(content, call2.index)}`,
3096
3096
  detail: `near ${near} / far ${far}`,
3097
3097
  ratio
3098
3098
  });
@@ -3387,7 +3387,7 @@ async function detectShellApis(cwd = process.cwd()) {
3387
3387
  for (const m of content.matchAll(DOWNLOAD_ASSIGN)) add(found.download, m.index, "a.download");
3388
3388
  }
3389
3389
  for (const m of content.matchAll(DOWNLOAD_ATTR)) add(found.download, m.index, "a download link");
3390
- for (const call of dialogCalls(content)) add(found.dialogs, call.index, `${call.name}()`);
3390
+ for (const call2 of dialogCalls(content)) add(found.dialogs, call2.index, `${call2.name}()`);
3391
3391
  for (const m of content.matchAll(SERVICE_WORKER)) {
3392
3392
  add(found.serviceWorker, m.index, "serviceWorker.register");
3393
3393
  }
@@ -4235,32 +4235,32 @@ async function uiExtract(opts, log) {
4235
4235
  if (dilatePx > 0) {
4236
4236
  const tmp = new Uint8Array(W * H);
4237
4237
  for (let y = 0; y < H; y++) {
4238
- let run4 = 0;
4238
+ let run5 = 0;
4239
4239
  for (let x = 0; x < W; x++) {
4240
- if (subject[y * W + x]) run4 = dilatePx;
4241
- else if (run4 > 0) run4--;
4242
- tmp[y * W + x] = run4 > 0 || subject[y * W + x] ? 1 : 0;
4240
+ if (subject[y * W + x]) run5 = dilatePx;
4241
+ else if (run5 > 0) run5--;
4242
+ tmp[y * W + x] = run5 > 0 || subject[y * W + x] ? 1 : 0;
4243
4243
  }
4244
- run4 = 0;
4244
+ run5 = 0;
4245
4245
  for (let x = W - 1; x >= 0; x--) {
4246
- if (subject[y * W + x]) run4 = dilatePx;
4247
- else if (run4 > 0) run4--;
4248
- if (run4 > 0) tmp[y * W + x] = 1;
4246
+ if (subject[y * W + x]) run5 = dilatePx;
4247
+ else if (run5 > 0) run5--;
4248
+ if (run5 > 0) tmp[y * W + x] = 1;
4249
4249
  }
4250
4250
  }
4251
4251
  const tmp2 = new Uint8Array(W * H);
4252
4252
  for (let x = 0; x < W; x++) {
4253
- let run4 = 0;
4253
+ let run5 = 0;
4254
4254
  for (let y = 0; y < H; y++) {
4255
- if (tmp[y * W + x]) run4 = dilatePx;
4256
- else if (run4 > 0) run4--;
4257
- tmp2[y * W + x] = run4 > 0 || tmp[y * W + x] ? 1 : 0;
4255
+ if (tmp[y * W + x]) run5 = dilatePx;
4256
+ else if (run5 > 0) run5--;
4257
+ tmp2[y * W + x] = run5 > 0 || tmp[y * W + x] ? 1 : 0;
4258
4258
  }
4259
- run4 = 0;
4259
+ run5 = 0;
4260
4260
  for (let y = H - 1; y >= 0; y--) {
4261
- if (tmp[y * W + x]) run4 = dilatePx;
4262
- else if (run4 > 0) run4--;
4263
- if (run4 > 0) tmp2[y * W + x] = 1;
4261
+ if (tmp[y * W + x]) run5 = dilatePx;
4262
+ else if (run5 > 0) run5--;
4263
+ if (run5 > 0) tmp2[y * W + x] = 1;
4264
4264
  }
4265
4265
  }
4266
4266
  connect = tmp2;
@@ -8358,14 +8358,14 @@ async function runDomain(opts) {
8358
8358
  }
8359
8359
  const base = `${apiUrl}/api/projects/${encodeURIComponent(meta.id)}/domains`;
8360
8360
  const auth = { Authorization: `Bearer ${token}` };
8361
- const call = (url, init) => apiFetch(url, { ...init, headers: { ...auth, "Content-Type": "application/json", ...init?.headers ?? {} } });
8361
+ const call2 = (url, init) => apiFetch(url, { ...init, headers: { ...auth, "Content-Type": "application/json", ...init?.headers ?? {} } });
8362
8362
  let res;
8363
8363
  try {
8364
- if (sub === "list") res = await call(base);
8365
- else if (sub === "add") res = await call(base, { method: "POST", body: JSON.stringify({ hostname }) });
8364
+ if (sub === "list") res = await call2(base);
8365
+ else if (sub === "add") res = await call2(base, { method: "POST", body: JSON.stringify({ hostname }) });
8366
8366
  else if (sub === "verify")
8367
- res = await call(`${base}/${encodeURIComponent(hostname)}/verify`, { method: "POST" });
8368
- else res = await call(`${base}/${encodeURIComponent(hostname)}`, { method: "DELETE" });
8367
+ res = await call2(`${base}/${encodeURIComponent(hostname)}/verify`, { method: "POST" });
8368
+ else res = await call2(`${base}/${encodeURIComponent(hostname)}`, { method: "DELETE" });
8369
8369
  } catch (err) {
8370
8370
  log.error(`Couldn't reach the API at ${apiUrl}.`);
8371
8371
  log.dim(` ${String(err)}`);
@@ -8506,7 +8506,7 @@ async function runShop(opts) {
8506
8506
  await writeUserToken(token, opts.envPath, apiUrl);
8507
8507
  }
8508
8508
  const auth = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
8509
- const call = (url, init) => apiFetch(url, { ...init, headers: { ...auth, ...init?.headers ?? {} } });
8509
+ const call2 = (url, init) => apiFetch(url, { ...init, headers: { ...auth, ...init?.headers ?? {} } });
8510
8510
  const projectSkus = `${apiUrl}/api/coin/projects/${encodeURIComponent(meta.id)}/skus`;
8511
8511
  async function fail5(res2, what) {
8512
8512
  if (printedStructuredError(res2)) {
@@ -8528,7 +8528,7 @@ async function runShop(opts) {
8528
8528
  }
8529
8529
  }
8530
8530
  if (sub === "list") {
8531
- const res2 = await call(projectSkus);
8531
+ const res2 = await call2(projectSkus);
8532
8532
  if (!res2.ok) return fail5(res2, "read your shop");
8533
8533
  const body = await res2.json();
8534
8534
  if (opts.json) {
@@ -8564,7 +8564,7 @@ async function runShop(opts) {
8564
8564
  process.exitCode = 1;
8565
8565
  return;
8566
8566
  }
8567
- const res2 = await call(projectSkus, {
8567
+ const res2 = await call2(projectSkus, {
8568
8568
  method: "POST",
8569
8569
  body: JSON.stringify({
8570
8570
  name,
@@ -8594,7 +8594,7 @@ async function runShop(opts) {
8594
8594
  }
8595
8595
  const skuUrl = `${apiUrl}/api/coin/skus/${encodeURIComponent(skuId)}`;
8596
8596
  if (sub === "test") {
8597
- const res2 = await call(`${skuUrl}/test-grant`, { method: "POST" });
8597
+ const res2 = await call2(`${skuUrl}/test-grant`, { method: "POST" });
8598
8598
  if (!res2.ok) return fail5(res2, "grant yourself a test copy");
8599
8599
  const got = await res2.json();
8600
8600
  log.success(`You now own ${c.cyan(got.name)} in this game.`);
@@ -8617,14 +8617,14 @@ async function runShop(opts) {
8617
8617
  process.exitCode = 1;
8618
8618
  return;
8619
8619
  }
8620
- const res2 = await call(skuUrl, { method: "PATCH", body: JSON.stringify(patch) });
8620
+ const res2 = await call2(skuUrl, { method: "PATCH", body: JSON.stringify(patch) });
8621
8621
  if (!res2.ok) return fail5(res2, "update that item");
8622
8622
  const sku = await res2.json();
8623
8623
  log.success(`Updated ${c.cyan(sku.name)} \u2014 ${sku.priceCoins} coin.`);
8624
8624
  if (opts.resellable === false) log.dim(" Listings already on the market stay up.");
8625
8625
  return;
8626
8626
  }
8627
- const res = await call(skuUrl, { method: "DELETE" });
8627
+ const res = await call2(skuUrl, { method: "DELETE" });
8628
8628
  if (!res.ok) return fail5(res, "remove that item");
8629
8629
  log.success("Removed from the shop.");
8630
8630
  log.dim(" Players who already bought it keep it.");
@@ -10599,10 +10599,10 @@ function reportLocalFiles(result, log, json) {
10599
10599
  }
10600
10600
  }
10601
10601
  function laneFailureAdvice(kind, outcomes) {
10602
- const run4 = outcomes.trailingFailed ?? (outcomes.completed > 0 ? 0 : outcomes.failed);
10603
- if (run4 < 2) return null;
10602
+ const run5 = outcomes.trailingFailed ?? (outcomes.completed > 0 ? 0 : outcomes.failed);
10603
+ if (run5 < 2) return null;
10604
10604
  const everSucceeded = outcomes.completed > 0;
10605
- const opening = everSucceeded ? `\u270B the last ${run4} ${kind} generations in this project all failed.` : `\u270B ${run4} ${kind} generations attempted in this project, ${run4} failed, none succeeded.`;
10605
+ const opening = everSucceeded ? `\u270B the last ${run5} ${kind} generations in this project all failed.` : `\u270B ${run5} ${kind} generations attempted in this project, ${run5} failed, none succeeded.`;
10606
10606
  return ` ${c.bold(opening)} That pattern is the LANE, not your prompt \u2014 a fourth attempt bills the same and returns the same. Build this one in code, use a different lane, or tell the user in one plain line what is unavailable and carry on with the rest of the game.`;
10607
10607
  }
10608
10608
  function videoFailureAdvice(failures) {
@@ -21453,7 +21453,7 @@ async function runWorkflow(args) {
21453
21453
  created.id,
21454
21454
  args.kind,
21455
21455
  args.opts.json ? () => {
21456
- } : (progress) => log.dim(` ${progress}%`)
21456
+ } : (progress2) => log.dim(` ${progress2}%`)
21457
21457
  );
21458
21458
  if (!view) {
21459
21459
  fail3(args.opts, "Timed out waiting for the character workflow.", args.kind);
@@ -21849,22 +21849,22 @@ async function context2(opts) {
21849
21849
  if (!token) return null;
21850
21850
  return { token, apiUrl };
21851
21851
  }
21852
- async function readVideo(path46, log) {
21852
+ async function readVideo(path47, log) {
21853
21853
  let bytes;
21854
21854
  try {
21855
- bytes = await readFile(path46);
21855
+ bytes = await readFile(path47);
21856
21856
  } catch {
21857
- log.error(`Can't read ${path46}.`);
21857
+ log.error(`Can't read ${path47}.`);
21858
21858
  return null;
21859
21859
  }
21860
21860
  if (bytes.byteLength > MAX_VIDEO_BYTES) {
21861
- log.error(`${basename(path46)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
21861
+ log.error(`${basename(path47)} is ${(bytes.byteLength / 1e6).toFixed(0)} MB \u2014 the limit is ${MAX_VIDEO_BYTES / 1e6} MB.`);
21862
21862
  return null;
21863
21863
  }
21864
21864
  return bytes;
21865
21865
  }
21866
- async function uploadVideo(apiUrl, token, characterId, path46, bytes, log) {
21867
- const contentType = /\.mov$/i.test(path46) ? "video/quicktime" : "video/mp4";
21866
+ async function uploadVideo(apiUrl, token, characterId, path47, bytes, log) {
21867
+ const contentType = /\.mov$/i.test(path47) ? "video/quicktime" : "video/mp4";
21868
21868
  const minted = await apiFetch(
21869
21869
  `${apiUrl}/api/characters/${encodeURIComponent(characterId)}/motions/video-url`,
21870
21870
  {
@@ -21879,7 +21879,7 @@ async function uploadVideo(apiUrl, token, characterId, path46, bytes, log) {
21879
21879
  return null;
21880
21880
  }
21881
21881
  const { uploadUrl, videoUrl } = await minted.json();
21882
- log.dim(` uploading ${basename(path46)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
21882
+ log.dim(` uploading ${basename(path47)} (${(bytes.byteLength / 1e6).toFixed(1)} MB)\u2026`);
21883
21883
  const put = await fetch(uploadUrl, {
21884
21884
  method: "PUT",
21885
21885
  headers: { "Content-Type": contentType, "Content-Length": String(bytes.byteLength) },
@@ -22879,11 +22879,11 @@ function mAimhold(C, w) {
22879
22879
  const r = yawMed * Math.PI / 180;
22880
22880
  const axis = [Math.sin(r), 0, Math.cos(r)];
22881
22881
  const fwdF = fwdSrc.map(({ v, L }) => (v[0] * axis[0] + v[1] * axis[1] + v[2] * axis[2]) / (L + 1e-9));
22882
- let run4 = 0, best = 0;
22882
+ let run5 = 0, best = 0;
22883
22883
  for (let i = 0; i < fwdF.length; i++) {
22884
22884
  const ok = dev[i] < 15 && dyF[i] < -0.22 && gapF[i] > 0.18 && gapF[i] < 0.46 && fwdF[i] > 0.75 && hunchF[i] < 16;
22885
- run4 = ok ? run4 + 1 : 0;
22886
- best = Math.max(best, run4);
22885
+ run5 = ok ? run5 + 1 : 0;
22886
+ best = Math.max(best, run5);
22887
22887
  }
22888
22888
  let holdBest = 0, holdBestScore = -Infinity;
22889
22889
  for (let i = 0; i < fwdF.length; i++) {
@@ -23237,15 +23237,15 @@ function cutOneshot(t, cfg) {
23237
23237
  const cc = folded(t);
23238
23238
  const air = cc.map((c2) => c2[0] === 0 && c2[1] === 0);
23239
23239
  let best = null;
23240
- let run4 = null;
23240
+ let run5 = null;
23241
23241
  for (let f = 0; f < air.length; f++) {
23242
- if (air[f]) run4 = run4 ? [run4[0], f] : [f, f];
23242
+ if (air[f]) run5 = run5 ? [run5[0], f] : [f, f];
23243
23243
  else {
23244
- if (run4 && (!best || run4[1] - run4[0] > best[1] - best[0])) best = run4;
23245
- run4 = null;
23244
+ if (run5 && (!best || run5[1] - run5[0] > best[1] - best[0])) best = run5;
23245
+ run5 = null;
23246
23246
  }
23247
23247
  }
23248
- if (run4 && (!best || run4[1] - run4[0] > best[1] - best[0])) best = run4;
23248
+ if (run5 && (!best || run5[1] - run5[0] > best[1] - best[0])) best = run5;
23249
23249
  let s, e;
23250
23250
  if (best) {
23251
23251
  s = best[0];
@@ -24809,7 +24809,7 @@ async function runBench(args) {
24809
24809
  const samples = opts.samples ?? DEFAULT_BENCH_SAMPLES;
24810
24810
  const prompt = opts.benchPrompt.trim();
24811
24811
  const auth = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
24812
- const call = (url, init) => apiFetch(url, { ...init, headers: { ...auth, ...init?.headers ?? {} } });
24812
+ const call2 = (url, init) => apiFetch(url, { ...init, headers: { ...auth, ...init?.headers ?? {} } });
24813
24813
  let schema;
24814
24814
  if (opts.schemaPath) {
24815
24815
  try {
@@ -24888,7 +24888,7 @@ async function runBench(args) {
24888
24888
  if (!opts.json) log.warn("Stopping \u2014 cancelling the attempt in flight.");
24889
24889
  void (async () => {
24890
24890
  if (target) {
24891
- await call(`${base}/${encodeURIComponent(target)}/cancel`, { method: "POST" }).catch(() => {
24891
+ await call2(`${base}/${encodeURIComponent(target)}/cancel`, { method: "POST" }).catch(() => {
24892
24892
  });
24893
24893
  }
24894
24894
  process.exit(130);
@@ -24897,7 +24897,7 @@ async function runBench(args) {
24897
24897
  process.once("SIGINT", onSigint);
24898
24898
  try {
24899
24899
  for (let i = 0; i < samples; i++) {
24900
- const res = await call(base, {
24900
+ const res = await call2(base, {
24901
24901
  method: "POST",
24902
24902
  body: JSON.stringify({
24903
24903
  maxCredits,
@@ -24911,7 +24911,7 @@ async function runBench(args) {
24911
24911
  })
24912
24912
  });
24913
24913
  if (!res.ok) {
24914
- const verdict = await explainBenchRefusal(res, call, base, log, opts, i, results.length);
24914
+ const verdict = await explainBenchRefusal(res, call2, base, log, opts, i, results.length);
24915
24915
  if (verdict.refusal) refusal2 = verdict.refusal;
24916
24916
  if (verdict.stop) break;
24917
24917
  continue;
@@ -24922,7 +24922,7 @@ async function runBench(args) {
24922
24922
  continue;
24923
24923
  }
24924
24924
  inflight = started.id;
24925
- const settled = await pollSettled(call, `${base}/${encodeURIComponent(started.id)}`, opts.timeoutSec ?? DEFAULT_SAMPLE_TIMEOUT_SEC);
24925
+ const settled = await pollSettled(call2, `${base}/${encodeURIComponent(started.id)}`, opts.timeoutSec ?? DEFAULT_SAMPLE_TIMEOUT_SEC);
24926
24926
  inflight = null;
24927
24927
  const row = sampleFrom(started.id, settled);
24928
24928
  results.push(row);
@@ -25125,7 +25125,7 @@ function printAverage(log, record, attempts) {
25125
25125
  log.dim(` = that average rounded up to a whole credit. disclosure.estimatedCreditsPerPeriod = your calls per period \xD7 ${formatCredits(record.averageCreditsPerCall)}, rounded up.`);
25126
25126
  }
25127
25127
  }
25128
- async function explainBenchRefusal(res, call, base, log, opts, index, settledSoFar) {
25128
+ async function explainBenchRefusal(res, call2, base, log, opts, index, settledSoFar) {
25129
25129
  if (printedStructuredError(res)) {
25130
25130
  const printed = await res.json().catch(() => ({}));
25131
25131
  return { stop: true, refusal: { status: res.status, error: typeof printed.error === "string" ? printed.error : null, slotsHeld: null, slotLimit: null } };
@@ -25133,7 +25133,7 @@ async function explainBenchRefusal(res, call, base, log, opts, index, settledSoF
25133
25133
  const body = await res.json().catch(() => ({}));
25134
25134
  const refusal2 = { status: res.status, error: body.error ?? null, slotsHeld: null, slotLimit: null };
25135
25135
  if (res.status === 429 && body.error === "generation_limit") {
25136
- const counts = serverSlotCounts(body) ?? await readSlotCounts(call, base);
25136
+ const counts = serverSlotCounts(body) ?? await readSlotCounts(call2, base);
25137
25137
  refusal2.slotsHeld = counts?.held ?? null;
25138
25138
  refusal2.slotLimit = counts?.limit ?? null;
25139
25139
  if (!opts.json) log.error(` Sample ${index + 1} ${benchSlotsHeldSentence(counts)}`);
@@ -25174,17 +25174,17 @@ function printProviderRefusal(log, status2, message, awaitingBill = false) {
25174
25174
  log.dim(` A ${status2} here is this stand's provider configuration refusing the model \u2014 a message for the operator, not something to fix in the game.`);
25175
25175
  }
25176
25176
  }
25177
- async function readSlotCounts(call, base) {
25178
- const res = await call(`${base}?scope=open`).catch(() => null);
25177
+ async function readSlotCounts(call2, base) {
25178
+ const res = await call2(`${base}?scope=open`).catch(() => null);
25179
25179
  if (!res?.ok) return null;
25180
25180
  const body = await res.json().catch(() => null);
25181
25181
  return serverSlotCounts(body);
25182
25182
  }
25183
- async function pollSettled(call, url, timeoutSec) {
25183
+ async function pollSettled(call2, url, timeoutSec) {
25184
25184
  const deadline = Date.now() + timeoutSec * 1e3;
25185
25185
  let last = null;
25186
25186
  while (Date.now() < deadline) {
25187
- const res = await call(url).catch(() => null);
25187
+ const res = await call2(url).catch(() => null);
25188
25188
  if (res?.ok) {
25189
25189
  last = await res.json().catch(() => null);
25190
25190
  if (last?.billingStatus === "final") return last;
@@ -25247,9 +25247,9 @@ function emptyBenchJson(apiUrl, projectId, prompt, outputFormat, samples, maxCre
25247
25247
  }
25248
25248
  async function reportStatus(args) {
25249
25249
  const { apiUrl, token, projectId, opts, log } = args;
25250
- const call = (url, init) => apiFetch(url, { ...init, headers: { Authorization: `Bearer ${token}`, ...init?.headers ?? {} } });
25250
+ const call2 = (url, init) => apiFetch(url, { ...init, headers: { Authorization: `Bearer ${token}`, ...init?.headers ?? {} } });
25251
25251
  const base = `${apiUrl}/api/runtime/development/projects/${encodeURIComponent(projectId)}/generations`;
25252
- const res = await call(`${base}?scope=open`).catch(() => null);
25252
+ const res = await call2(`${base}?scope=open`).catch(() => null);
25253
25253
  if (!res || !res.ok) {
25254
25254
  if (res && printedStructuredError(res)) {
25255
25255
  if (opts.json) writeJsonLine(statusJson(apiUrl, projectId, "failed", `http_${res.status}`, [], null, null));
@@ -25340,14 +25340,14 @@ function statusJson(apiUrl, projectId, status2, error, rows, slotsHeld, slotLimi
25340
25340
  }
25341
25341
  async function cancelCall(args) {
25342
25342
  const { apiUrl, token, projectId, id, opts, log } = args;
25343
- const call = (url, init) => apiFetch(url, { ...init, headers: { Authorization: `Bearer ${token}`, ...init?.headers ?? {} } });
25343
+ const call2 = (url, init) => apiFetch(url, { ...init, headers: { Authorization: `Bearer ${token}`, ...init?.headers ?? {} } });
25344
25344
  const base = `${apiUrl}/api/runtime/development/projects/${encodeURIComponent(projectId)}/generations`;
25345
25345
  const rowUrl = `${base}/${encodeURIComponent(id)}`;
25346
25346
  const done = (status2, error, reason, row2) => {
25347
25347
  if (opts.json) writeJsonLine({ command: "llm cancel", status: status2, error, id, cancelled: reason === "cancelled", reason, generation: row2 });
25348
25348
  if (status2 === "failed") process.exitCode = 1;
25349
25349
  };
25350
- const read = await call(rowUrl).catch(() => null);
25350
+ const read = await call2(rowUrl).catch(() => null);
25351
25351
  if (!read || !read.ok) {
25352
25352
  if (read && printedStructuredError(read)) return done("failed", `http_${read.status}`, null, null);
25353
25353
  const body = read ? await read.json().catch(() => ({})) : {};
@@ -25386,7 +25386,7 @@ async function cancelCall(args) {
25386
25386
  }
25387
25387
  return done("ok", null, state === "awaiting bill" ? "awaiting_bill" : "already_final", row);
25388
25388
  }
25389
- const res = await call(`${rowUrl}/cancel`, { method: "POST" }).catch(() => null);
25389
+ const res = await call2(`${rowUrl}/cancel`, { method: "POST" }).catch(() => null);
25390
25390
  if (!res || !res.ok) {
25391
25391
  if (res && printedStructuredError(res)) return done("failed", `http_${res.status}`, null, row);
25392
25392
  const body = res ? await res.json().catch(() => ({})) : {};
@@ -26342,14 +26342,14 @@ var PlayerClient = class {
26342
26342
  this.version = options.version ?? getCliVersion();
26343
26343
  this.timeoutMs = options.timeoutMs ?? 3e4;
26344
26344
  }
26345
- async call(path46, init = {}, provider) {
26345
+ async call(path47, init = {}, provider) {
26346
26346
  const headers = {
26347
26347
  Authorization: `Bearer ${this.token}`,
26348
26348
  [PLAYER_VERSION_HEADER]: this.version,
26349
26349
  ...provider ? { [EXTERNAL_PROVIDER_HEADER]: provider } : {},
26350
26350
  ...init.body === void 0 ? {} : { "Content-Type": "application/json" }
26351
26351
  };
26352
- const res = await apiFetch(`${this.apiUrl}${path46}`, {
26352
+ const res = await apiFetch(`${this.apiUrl}${path47}`, {
26353
26353
  ...init,
26354
26354
  headers,
26355
26355
  signal: AbortSignal.timeout(this.timeoutMs)
@@ -27673,6 +27673,727 @@ function writeJsonLine2(value) {
27673
27673
  `);
27674
27674
  }
27675
27675
 
27676
+ // src/lib/native-build.ts
27677
+ import { createHash as createHash3 } from "crypto";
27678
+ import { createReadStream } from "fs";
27679
+ import fs43 from "fs/promises";
27680
+ import os13 from "os";
27681
+ import path46 from "path";
27682
+ import { execFile as execFile2 } from "child_process";
27683
+ import { promisify as promisify2 } from "util";
27684
+
27685
+ // src/lib/native-zip.ts
27686
+ import fs42 from "fs/promises";
27687
+ import zlib2 from "zlib";
27688
+ var EOCD_SIGNATURE = 101010256;
27689
+ var EOCD64_LOCATOR_SIGNATURE = 117853008;
27690
+ var EOCD64_SIGNATURE = 101075792;
27691
+ var CENTRAL_SIGNATURE = 33639248;
27692
+ var LOCAL_SIGNATURE = 67324752;
27693
+ var EOCD_MIN = 22;
27694
+ var COMMENT_MAX = 65535;
27695
+ var ZIP64_EXTRA = 1;
27696
+ var U32_MAX = 4294967295;
27697
+ var U16_MAX = 65535;
27698
+ var DIRECTORY_MAX_BYTES = 256 * 1024 * 1024;
27699
+ var UNIX_HOST = 3;
27700
+ var INPUT_MIN = 64 * 1024;
27701
+ var INPUT_MAX = 1024 * 1024;
27702
+ var S_IFMT = 61440;
27703
+ var S_IFLNK = 40960;
27704
+ var isDirectory = (entry) => entry.name.endsWith("/");
27705
+ var isSymlink = (entry) => entry.mode !== null && (entry.mode & S_IFMT) === S_IFLNK;
27706
+ async function readAt2(file, position, length) {
27707
+ const buffer = Buffer.alloc(length);
27708
+ const { bytesRead } = await file.read(buffer, 0, length, position);
27709
+ return buffer.subarray(0, bytesRead);
27710
+ }
27711
+ async function findDirectory(file, size) {
27712
+ const tailLength = Math.min(size, EOCD_MIN + COMMENT_MAX);
27713
+ const tail = await readAt2(file, size - tailLength, tailLength);
27714
+ let at = -1;
27715
+ for (let i = tail.length - EOCD_MIN; i >= 0; i--) {
27716
+ if (tail.readUInt32LE(i) === EOCD_SIGNATURE) {
27717
+ at = i;
27718
+ break;
27719
+ }
27720
+ }
27721
+ if (at < 0) throw new Error("not a zip archive (no end-of-directory record)");
27722
+ let count = tail.readUInt16LE(at + 10);
27723
+ let bytes = tail.readUInt32LE(at + 12);
27724
+ let offset = tail.readUInt32LE(at + 16);
27725
+ const zip64 = count === U16_MAX || bytes === U32_MAX || offset === U32_MAX;
27726
+ const locatorAt = at - 20;
27727
+ if (zip64 && locatorAt >= 0 && tail.readUInt32LE(locatorAt) === EOCD64_LOCATOR_SIGNATURE) {
27728
+ const recordOffset = Number(tail.readBigUInt64LE(locatorAt + 8));
27729
+ const record = await readAt2(file, recordOffset, 56);
27730
+ if (record.length < 56 || record.readUInt32LE(0) !== EOCD64_SIGNATURE) throw new Error("broken zip64 directory record");
27731
+ count = Number(record.readBigUInt64LE(32));
27732
+ bytes = Number(record.readBigUInt64LE(40));
27733
+ offset = Number(record.readBigUInt64LE(48));
27734
+ }
27735
+ if (bytes > DIRECTORY_MAX_BYTES || offset + bytes > size) throw new Error("zip directory is out of bounds");
27736
+ return { offset, bytes, count };
27737
+ }
27738
+ function applyZip64(extra, entry, overflowed) {
27739
+ for (let at = 0; at + 4 <= extra.length; ) {
27740
+ const id = extra.readUInt16LE(at);
27741
+ const length = extra.readUInt16LE(at + 2);
27742
+ if (id === ZIP64_EXTRA) {
27743
+ let field = at + 4;
27744
+ const next = () => {
27745
+ const value = Number(extra.readBigUInt64LE(field));
27746
+ field += 8;
27747
+ return value;
27748
+ };
27749
+ if (overflowed.uncompressed) entry.uncompressedSize = next();
27750
+ if (overflowed.compressed) entry.compressedSize = next();
27751
+ if (overflowed.offset) entry.localOffset = next();
27752
+ return;
27753
+ }
27754
+ at += 4 + length;
27755
+ }
27756
+ }
27757
+ async function readZipEntries(filePath) {
27758
+ const file = await fs42.open(filePath, "r");
27759
+ try {
27760
+ const { size } = await file.stat();
27761
+ const directory = await findDirectory(file, size);
27762
+ const data = await readAt2(file, directory.offset, directory.bytes);
27763
+ const entries = [];
27764
+ let at = 0;
27765
+ while (entries.length < directory.count && at + 46 <= data.length) {
27766
+ if (data.readUInt32LE(at) !== CENTRAL_SIGNATURE) throw new Error("broken zip directory entry");
27767
+ const madeBy = data.readUInt16LE(at + 4);
27768
+ const nameLength = data.readUInt16LE(at + 28);
27769
+ const extraLength = data.readUInt16LE(at + 30);
27770
+ const commentLength = data.readUInt16LE(at + 32);
27771
+ const external = data.readUInt32LE(at + 38);
27772
+ const entry = {
27773
+ name: data.toString("utf8", at + 46, at + 46 + nameLength),
27774
+ method: data.readUInt16LE(at + 10),
27775
+ compressedSize: data.readUInt32LE(at + 20),
27776
+ uncompressedSize: data.readUInt32LE(at + 24),
27777
+ localOffset: data.readUInt32LE(at + 42),
27778
+ mode: madeBy >> 8 === UNIX_HOST ? external >>> 16 : null
27779
+ };
27780
+ applyZip64(data.subarray(at + 46 + nameLength, at + 46 + nameLength + extraLength), entry, {
27781
+ uncompressed: entry.uncompressedSize === U32_MAX,
27782
+ compressed: entry.compressedSize === U32_MAX,
27783
+ offset: entry.localOffset === U32_MAX
27784
+ });
27785
+ entries.push(entry);
27786
+ at += 46 + nameLength + extraLength + commentLength;
27787
+ }
27788
+ if (entries.length !== directory.count) throw new Error("zip directory ends early");
27789
+ return entries;
27790
+ } finally {
27791
+ await file.close();
27792
+ }
27793
+ }
27794
+ async function readZipEntryHead(filePath, entry, maxBytes) {
27795
+ const file = await fs42.open(filePath, "r");
27796
+ try {
27797
+ const local = await readAt2(file, entry.localOffset, 30);
27798
+ if (local.length < 30 || local.readUInt32LE(0) !== LOCAL_SIGNATURE) throw new Error(`broken zip entry ${entry.name}`);
27799
+ const start = entry.localOffset + 30 + local.readUInt16LE(26) + local.readUInt16LE(28);
27800
+ if (entry.method === 0) return await readAt2(file, start, Math.min(maxBytes, entry.uncompressedSize));
27801
+ if (entry.method !== 8) throw new Error(`${entry.name} uses a compression this command cannot read`);
27802
+ const input = await readAt2(file, start, Math.min(entry.compressedSize, INPUT_MAX, Math.max(maxBytes, INPUT_MIN)));
27803
+ return await inflateHead(input, maxBytes);
27804
+ } finally {
27805
+ await file.close();
27806
+ }
27807
+ }
27808
+ function inflateHead(input, maxBytes) {
27809
+ return new Promise((resolve) => {
27810
+ const inflater = zlib2.createInflateRaw();
27811
+ const chunks = [];
27812
+ let total = 0;
27813
+ const finish2 = () => resolve(Buffer.concat(chunks).subarray(0, maxBytes));
27814
+ inflater.on("data", (chunk) => {
27815
+ chunks.push(chunk);
27816
+ total += chunk.length;
27817
+ if (total >= maxBytes) inflater.destroy();
27818
+ });
27819
+ inflater.on("error", finish2);
27820
+ inflater.on("close", finish2);
27821
+ inflater.end(input);
27822
+ });
27823
+ }
27824
+
27825
+ // src/lib/native-build.ts
27826
+ var run4 = promisify2(execFile2);
27827
+ var NATIVE_PLATFORMS = ["mac", "windows", "linux"];
27828
+ var NATIVE_ARCHES = ["arm64", "x64", "universal"];
27829
+ var VERSION = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
27830
+ var VERSION_MAX = 64;
27831
+ var OS_VERSION = /^\d{1,4}(\.\d{1,6}){0,3}$/;
27832
+ var ENGINE = /^[a-z][a-z0-9-]{1,39}$/;
27833
+ var HEADER_BYTES = 4096;
27834
+ var PLIST_BYTES = 1024 * 1024;
27835
+ var VERSION_SHA_CHARS = 12;
27836
+ var ENTRY_DEPTH_MAX = 4;
27837
+ var MACHO_64_LE = 4277009103;
27838
+ var FAT_MAGIC = 3405691582;
27839
+ var FAT_MAGIC_64 = 3405691583;
27840
+ var CPU_X86_64 = 16777223;
27841
+ var CPU_ARM64 = 16777228;
27842
+ var PE_X64 = 34404;
27843
+ var PE_ARM64 = 43620;
27844
+ var ELF_X64 = 62;
27845
+ var ELF_ARM64 = 183;
27846
+ var HELPER_EXE = /crash|prereq|redist|setup|unins/i;
27847
+ var NativeBuildError = class extends Error {
27848
+ };
27849
+ var isPlatform = (value) => NATIVE_PLATFORMS.includes(value);
27850
+ var isArch = (value) => NATIVE_ARCHES.includes(value);
27851
+ function rootNames(entries) {
27852
+ return new Set(entries.map((e) => e.name.split("/")[0]).filter(Boolean));
27853
+ }
27854
+ function rootApps(entries) {
27855
+ return [...rootNames(entries)].filter((name) => name.toLowerCase().endsWith(".app") && entries.some((e) => e.name.startsWith(`${name}/`)));
27856
+ }
27857
+ function launchableExes(entries) {
27858
+ const exes = entries.filter((e) => !isDirectory(e) && e.name.toLowerCase().endsWith(".exe"));
27859
+ const games = exes.filter((e) => e.name.split("/").length <= ENTRY_DEPTH_MAX && !HELPER_EXE.test(path46.posix.basename(e.name)));
27860
+ const depth = (name) => name.split("/").length;
27861
+ return games.map((e) => e.name).sort((a, b) => depth(a) - depth(b) || a.localeCompare(b));
27862
+ }
27863
+ function platformOf(entries, asked) {
27864
+ if (asked !== void 0) {
27865
+ if (!isPlatform(asked)) throw new NativeBuildError(`--platform must be one of ${NATIVE_PLATFORMS.join(", ")}.`);
27866
+ return asked;
27867
+ }
27868
+ if (rootApps(entries).length > 0) return "mac";
27869
+ if (launchableExes(entries).length > 0) return "windows";
27870
+ const nested = entries.find((e) => /^[^/]+\/[^/]+\.app\/$/i.test(e.name));
27871
+ if (nested) {
27872
+ throw new NativeBuildError(
27873
+ `The Mac app sits inside a folder (${nested.name.slice(0, -1)}). Pass the .app itself, or zip it so it is at the archive's root.`
27874
+ );
27875
+ }
27876
+ throw new NativeBuildError("Couldn't tell which system this build is for. Pass --platform mac|windows|linux.");
27877
+ }
27878
+ function entryOf(entries, platform, asked) {
27879
+ const files = new Set(entries.map((e) => isDirectory(e) ? e.name.slice(0, -1) : e.name));
27880
+ if (asked !== void 0) {
27881
+ const entry = asked.replace(/\/+$/, "");
27882
+ if (!files.has(entry)) throw new NativeBuildError(`--entry ${asked} is not in the archive.`);
27883
+ return entry;
27884
+ }
27885
+ if (platform === "mac") {
27886
+ const apps = rootApps(entries);
27887
+ if (apps.length === 1) return apps[0];
27888
+ throw new NativeBuildError(
27889
+ apps.length === 0 ? "No .app at the archive's root. Pass the Mac app itself, or zip it so it is at the root." : `More than one app at the archive's root (${apps.join(", ")}). Pass --entry "<name>.app".`
27890
+ );
27891
+ }
27892
+ if (platform === "windows") {
27893
+ const exes = launchableExes(entries);
27894
+ const shallowest = exes.filter((name) => name.split("/").length === exes[0]?.split("/").length);
27895
+ if (shallowest.length === 1) return shallowest[0];
27896
+ throw new NativeBuildError(
27897
+ exes.length === 0 ? "No .exe in the archive. Pass --entry with the program to run." : `More than one program could be the game (${shallowest.join(", ")}). Pass --entry "<path>.exe".`
27898
+ );
27899
+ }
27900
+ throw new NativeBuildError("A Linux build needs --entry: the path of the program to run, from the archive's root.");
27901
+ }
27902
+ function plistString(plist, key) {
27903
+ const match = new RegExp(`<key>${key}</key>\\s*<string>([^<]*)</string>`).exec(plist);
27904
+ return match?.[1]?.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&amp;/g, "&").trim();
27905
+ }
27906
+ function machOArches(head) {
27907
+ const found = /* @__PURE__ */ new Set();
27908
+ const add = (cpu) => {
27909
+ if (cpu === CPU_ARM64) found.add("arm64");
27910
+ if (cpu === CPU_X86_64) found.add("x64");
27911
+ };
27912
+ if (head.length < 8) return found;
27913
+ const magic = head.readUInt32BE(0);
27914
+ if (magic === FAT_MAGIC || magic === FAT_MAGIC_64) {
27915
+ const stride = magic === FAT_MAGIC ? 20 : 32;
27916
+ const count = head.readUInt32BE(4);
27917
+ for (let i = 0; i < count && 8 + i * stride + 4 <= head.length; i++) add(head.readUInt32BE(8 + i * stride));
27918
+ return found;
27919
+ }
27920
+ if (head.readUInt32LE(0) === MACHO_64_LE) add(head.readUInt32LE(4));
27921
+ return found;
27922
+ }
27923
+ function exeArch(head) {
27924
+ if (head.length >= 64 && head.toString("latin1", 0, 2) === "MZ") {
27925
+ const pe = head.readUInt32LE(60);
27926
+ if (pe + 6 > head.length || head.toString("latin1", pe, pe + 4) !== "PE\0\0") return null;
27927
+ const machine = head.readUInt16LE(pe + 4);
27928
+ if (machine === PE_X64) return "x64";
27929
+ if (machine === PE_ARM64) return "arm64";
27930
+ return machine === 332 ? "x86" : null;
27931
+ }
27932
+ if (head.length >= 20 && head.readUInt32BE(0) === 2135247942) {
27933
+ const machine = head.readUInt16LE(18);
27934
+ if (machine === ELF_X64) return "x64";
27935
+ if (machine === ELF_ARM64) return "arm64";
27936
+ }
27937
+ return null;
27938
+ }
27939
+ function engineOf(entries) {
27940
+ const names2 = entries.map((e) => e.name.toLowerCase());
27941
+ const base = (name) => path46.posix.basename(name.replace(/\/$/, ""));
27942
+ if (names2.some((n) => /^unityplayer\.(dll|dylib|so)$/.test(base(n)) || base(n) === "globalgamemanagers")) return "unity";
27943
+ if (names2.some((n) => n.startsWith("engine/binaries/") || /\.app\/contents\/ue[45]?\//.test(n) || n.includes("/engine/binaries/")))
27944
+ return "unreal";
27945
+ if (names2.some((n) => n.endsWith(".pck"))) return "godot";
27946
+ return void 0;
27947
+ }
27948
+ async function describeMacApp(zipPath, entries, app) {
27949
+ const plistEntry = entries.find((e) => e.name === `${app}/Contents/Info.plist`);
27950
+ if (!plistEntry) throw new NativeBuildError(`${app} has no Contents/Info.plist \u2014 is it a complete app?`);
27951
+ const plist = (await readZipEntryHead(zipPath, plistEntry, PLIST_BYTES)).toString("utf8");
27952
+ const readable = plist.includes("<plist");
27953
+ const executable = readable ? plistString(plist, "CFBundleExecutable") : void 0;
27954
+ const binary = executable ? entries.find((e) => e.name === `${app}/Contents/MacOS/${executable}`) : void 0;
27955
+ const arches = binary && !isSymlink(binary) ? machOArches(await readZipEntryHead(zipPath, binary, HEADER_BYTES)) : /* @__PURE__ */ new Set();
27956
+ const arch = arches.size === 2 ? "universal" : [...arches][0];
27957
+ return {
27958
+ arch,
27959
+ minOs: readable ? plistString(plist, "LSMinimumSystemVersion") : void 0,
27960
+ appVersion: readable ? plistString(plist, "CFBundleShortVersionString") : void 0
27961
+ };
27962
+ }
27963
+ async function describeProgram(zipPath, entries, entry) {
27964
+ const file = entries.find((e) => e.name === entry);
27965
+ if (!file || isSymlink(file)) return void 0;
27966
+ const arch = exeArch(await readZipEntryHead(zipPath, file, HEADER_BYTES));
27967
+ if (arch === "x86") throw new NativeBuildError(`${entry} is a 32-bit program. Genex installs 64-bit builds only (x64 or arm64).`);
27968
+ return arch ?? void 0;
27969
+ }
27970
+ function settleArch(read, asked, platform) {
27971
+ if (asked !== void 0 && !isArch(asked)) throw new NativeBuildError(`--arch must be one of ${NATIVE_ARCHES.join(", ")}.`);
27972
+ const arch = asked ?? read;
27973
+ if (!arch) throw new NativeBuildError("Couldn't read which processor this build is for. Pass --arch arm64|x64|universal.");
27974
+ if (arch === "universal" && platform !== "mac") throw new NativeBuildError("Only a Mac app can be universal.");
27975
+ if (read && asked && read !== asked) {
27976
+ throw new NativeBuildError(`--arch ${asked} does not match the build, which is ${read}.`);
27977
+ }
27978
+ return arch;
27979
+ }
27980
+ async function describeNativeArchive(zipPath, asked = {}) {
27981
+ let entries;
27982
+ try {
27983
+ entries = await readZipEntries(zipPath);
27984
+ } catch (err) {
27985
+ throw new NativeBuildError(`Couldn't read ${path46.basename(zipPath)} as a zip: ${err.message}`);
27986
+ }
27987
+ if (entries.length === 0) throw new NativeBuildError("The archive is empty.");
27988
+ const platform = platformOf(entries, asked.platform);
27989
+ const entry = entryOf(entries, platform, asked.entry);
27990
+ const mac = platform === "mac" ? await describeMacApp(zipPath, entries, entry) : void 0;
27991
+ const arch = settleArch(mac ? mac.arch : await describeProgram(zipPath, entries, entry), asked.arch, platform);
27992
+ const minOs = asked.minOs ?? mac?.minOs;
27993
+ if (minOs !== void 0 && !OS_VERSION.test(minOs)) throw new NativeBuildError(`Minimum system "${minOs}" is not a dotted version such as 13.0.`);
27994
+ const engine = (asked.engine ?? engineOf(entries))?.toLowerCase();
27995
+ if (engine !== void 0 && !ENGINE.test(engine)) throw new NativeBuildError(`--engine ${engine} is not an engine id such as unity, godot or unreal.`);
27996
+ const installedBytes = entries.reduce((sum, e) => isDirectory(e) ? sum : sum + e.uncompressedSize, 0);
27997
+ return {
27998
+ platform,
27999
+ arch,
28000
+ entry,
28001
+ installedBytes: Math.max(1, installedBytes),
28002
+ ...minOs ? { minOs } : {},
28003
+ ...engine ? { engine } : {},
28004
+ ...mac?.appVersion ? { appVersion: mac.appVersion } : {}
28005
+ };
28006
+ }
28007
+ function buildVersion(asked, appVersion, sha2562) {
28008
+ if (asked !== void 0) {
28009
+ if (asked.length > VERSION_MAX || !VERSION.test(asked)) {
28010
+ throw new NativeBuildError(`--build-version "${asked}" may hold only letters, digits and . _ + - (at most ${VERSION_MAX}).`);
28011
+ }
28012
+ return asked;
28013
+ }
28014
+ const hash = sha2562.slice(0, VERSION_SHA_CHARS);
28015
+ const app = (appVersion ?? "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[^A-Za-z0-9]+/, "").slice(0, VERSION_MAX - hash.length - 1);
28016
+ return app ? `${app}+${hash}` : hash;
28017
+ }
28018
+ async function hashFile(filePath, onProgress) {
28019
+ const hash = createHash3("sha256");
28020
+ let sizeBytes = 0;
28021
+ for await (const chunk of createReadStream(filePath)) {
28022
+ hash.update(chunk);
28023
+ sizeBytes += chunk.length;
28024
+ onProgress?.(sizeBytes);
28025
+ }
28026
+ return { sha256: hash.digest("hex"), sizeBytes };
28027
+ }
28028
+ async function packTarget(dir) {
28029
+ if (dir.toLowerCase().endsWith(".app")) return { target: dir, keepParent: true };
28030
+ const children = await fs43.readdir(dir, { withFileTypes: true });
28031
+ const apps = children.filter((d) => d.isDirectory() && d.name.toLowerCase().endsWith(".app"));
28032
+ if (apps.length === 1) return { target: path46.join(dir, apps[0].name), keepParent: true };
28033
+ return { target: dir, keepParent: false };
28034
+ }
28035
+ async function packNativeBuild(input) {
28036
+ const stat = await fs43.stat(input).catch(() => null);
28037
+ if (!stat) throw new NativeBuildError(`${input} does not exist.`);
28038
+ if (stat.isFile()) {
28039
+ if (!input.toLowerCase().endsWith(".zip")) throw new NativeBuildError("Pass a .zip, a .app, or the build's folder.");
28040
+ return { zipPath: input, cleanup: async () => {
28041
+ } };
28042
+ }
28043
+ const { target, keepParent } = await packTarget(input);
28044
+ const dir = await fs43.mkdtemp(path46.join(os13.tmpdir(), "genex-native-"));
28045
+ const zipPath = path46.join(dir, `${path46.basename(target).replace(/[^A-Za-z0-9._-]+/g, "-") || "build"}.zip`);
28046
+ const cleanup = () => fs43.rm(dir, { recursive: true, force: true });
28047
+ try {
28048
+ if (process.platform === "darwin") {
28049
+ await run4("ditto", ["-c", "-k", "--sequesterRsrc", ...keepParent ? ["--keepParent"] : [], target, zipPath]);
28050
+ } else if (keepParent) {
28051
+ await run4("zip", ["-q", "-r", "-y", zipPath, path46.basename(target)], { cwd: path46.dirname(target) });
28052
+ } else {
28053
+ await run4("zip", ["-q", "-r", "-y", zipPath, "."], { cwd: target });
28054
+ }
28055
+ } catch (err) {
28056
+ await cleanup();
28057
+ const missing = err.code === "ENOENT";
28058
+ throw new NativeBuildError(
28059
+ missing ? "Couldn't zip the build: no `zip` on this machine. Zip it yourself (keeping links) and pass the .zip." : `Couldn't zip the build: ${err.message}`
28060
+ );
28061
+ }
28062
+ return { zipPath, cleanup };
28063
+ }
28064
+
28065
+ // src/lib/native-upload.ts
28066
+ import fs44 from "fs/promises";
28067
+ var PART_CONCURRENCY = 2;
28068
+ async function call(ctx, route, init, what, log) {
28069
+ let res;
28070
+ try {
28071
+ res = await apiFetch(`${ctx.apiUrl}/api/games/${ctx.projectId}${route}`, {
28072
+ ...init,
28073
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${ctx.token}`, ...init.headers ?? {} }
28074
+ });
28075
+ } catch (err) {
28076
+ log.error(`Couldn't reach the API at ${ctx.apiUrl}: ${String(err)}`);
28077
+ return null;
28078
+ }
28079
+ if (res.ok) return res;
28080
+ if (res.status === 401) {
28081
+ log.error("Not authorized (HTTP 401) \u2014 your sign-in expired or was revoked.");
28082
+ log.plain(" Fix: run `genex link <your-game>` and sign in as the game's owner, then re-run this command.");
28083
+ return null;
28084
+ }
28085
+ if (res.status === 404 && route.startsWith("/native-upload-token")) {
28086
+ log.error("This account doesn't own this game (HTTP 404), or the API predates native builds.");
28087
+ return null;
28088
+ }
28089
+ if (res.status === 429) {
28090
+ log.error(`Rate limited ${what} (HTTP 429).${retryAfterHint(res)}`);
28091
+ return null;
28092
+ }
28093
+ const reason = await serverReason(res);
28094
+ log.error(`Couldn't ${what} (HTTP ${res.status}).`);
28095
+ if (reason) log.dim(` ${reason}`);
28096
+ return null;
28097
+ }
28098
+ async function requestNativeUpload(ctx, body, log) {
28099
+ const res = await call(ctx, "/native-upload-token", { method: "POST", body: JSON.stringify(body) }, "get an upload token", log);
28100
+ return res ? await res.json() : null;
28101
+ }
28102
+ async function readPart(file, position, length) {
28103
+ const buffer = Buffer.alloc(length);
28104
+ const { bytesRead } = await file.read(buffer, 0, length, position);
28105
+ if (bytesRead !== length) throw new Error("the archive changed while it was being uploaded");
28106
+ return buffer;
28107
+ }
28108
+ async function uploadNativeArchive(grant, zipPath, sizeBytes, log, onProgress) {
28109
+ const base = grant.uploadUrl.replace(/\/+$/, "");
28110
+ const encoded = grant.uploadPath.split("/").map(encodeURIComponent).join("/");
28111
+ const auth = { Authorization: `Bearer ${grant.token}` };
28112
+ const file = await fs44.open(zipPath, "r");
28113
+ try {
28114
+ if (sizeBytes <= grant.limits.singlePutMaxBytes) {
28115
+ const body = await readPart(file, 0, sizeBytes);
28116
+ const res = await fetch(`${base}/${encoded}`, { method: "PUT", headers: auth, body }).catch(() => null);
28117
+ if (!res?.ok) {
28118
+ log.error(`Upload failed${res ? ` (HTTP ${res.status})` : " (network error)"}.`);
28119
+ return false;
28120
+ }
28121
+ onProgress?.(sizeBytes);
28122
+ return true;
28123
+ }
28124
+ return await uploadParts(base, encoded, auth, file, sizeBytes, grant.limits.partSizeBytes, log, onProgress);
28125
+ } finally {
28126
+ await file.close();
28127
+ }
28128
+ }
28129
+ async function uploadParts(base, encoded, auth, file, sizeBytes, partSize, log, onProgress) {
28130
+ const created = await fetch(`${base}/${encoded}?action=mpu-create`, { method: "POST", headers: auth }).catch(() => null);
28131
+ if (!created?.ok) {
28132
+ log.error(`Upload failed${created ? ` (HTTP ${created.status} starting multipart)` : " (network error starting multipart)"}.`);
28133
+ return false;
28134
+ }
28135
+ const { uploadId } = await created.json();
28136
+ const totalParts = Math.ceil(sizeBytes / partSize);
28137
+ const parts = [];
28138
+ let sent = 0;
28139
+ let next = 0;
28140
+ let failed = false;
28141
+ const worker = async () => {
28142
+ while (!failed) {
28143
+ const index = next++;
28144
+ if (index >= totalParts) return;
28145
+ const start = index * partSize;
28146
+ const length = Math.min(partSize, sizeBytes - start);
28147
+ const bytes = await readPart(file, start, length);
28148
+ const part = await uploadPartWithRetry(base, encoded, auth, uploadId, index + 1, bytes);
28149
+ if (!part) {
28150
+ failed = true;
28151
+ log.error(`Upload failed (part ${index + 1}/${totalParts}).`);
28152
+ return;
28153
+ }
28154
+ parts.push(part);
28155
+ sent += length;
28156
+ onProgress?.(sent);
28157
+ }
28158
+ };
28159
+ await Promise.all(Array.from({ length: Math.min(PART_CONCURRENCY, totalParts) }, () => worker()));
28160
+ const query = `uploadId=${encodeURIComponent(uploadId)}`;
28161
+ if (!failed) {
28162
+ parts.sort((a, b) => a.partNumber - b.partNumber);
28163
+ const done = await fetch(`${base}/${encoded}?action=mpu-complete&${query}`, {
28164
+ method: "POST",
28165
+ headers: { ...auth, "Content-Type": "application/json" },
28166
+ body: JSON.stringify({ parts })
28167
+ }).catch(() => null);
28168
+ if (done?.ok) return true;
28169
+ log.error(`Upload failed${done ? ` (HTTP ${done.status} completing multipart)` : " (network error completing multipart)"}.`);
28170
+ }
28171
+ await fetch(`${base}/${encoded}?action=mpu-abort&${query}`, { method: "DELETE", headers: auth }).catch(() => null);
28172
+ return false;
28173
+ }
28174
+ async function publishNativeBuild(ctx, body, log) {
28175
+ const res = await call(ctx, "/native-builds", { method: "POST", body: JSON.stringify(body) }, "publish the build", log);
28176
+ if (!res) return null;
28177
+ return (await res.json()).build;
28178
+ }
28179
+ async function listNativeBuilds(ctx, log) {
28180
+ const res = await call(ctx, "/native-builds", { method: "GET" }, "list the builds", log);
28181
+ if (!res) return null;
28182
+ return (await res.json()).builds;
28183
+ }
28184
+ async function removeNativeBuild(ctx, platform, arch, log) {
28185
+ const route = `/native-builds/${encodeURIComponent(platform)}/${encodeURIComponent(arch)}`;
28186
+ return await call(ctx, route, { method: "DELETE" }, `remove the ${platform} ${arch} build`, log) !== null;
28187
+ }
28188
+ async function listInGallery(ctx, body, log) {
28189
+ let res;
28190
+ try {
28191
+ res = await apiFetch(`${ctx.apiUrl}/api/projects/${ctx.projectId}/publish`, {
28192
+ method: "POST",
28193
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${ctx.token}` },
28194
+ body: JSON.stringify(body)
28195
+ });
28196
+ } catch (err) {
28197
+ log.error(`Couldn't reach the API at ${ctx.apiUrl}: ${String(err)}`);
28198
+ return null;
28199
+ }
28200
+ const json = await res.json().catch(() => null);
28201
+ if (res.status === 202) {
28202
+ return { state: "held", category: json?.moderation?.category ?? null, detail: json?.moderation?.detail ?? null };
28203
+ }
28204
+ if (res.status === 403 && json?.error === "moderation_refused") {
28205
+ return { state: "refused", category: json.category ?? null, detail: json.detail ?? null };
28206
+ }
28207
+ if (res.ok) return { state: "published", slug: json?.project?.slug ?? null };
28208
+ log.error(`Couldn't list the game in the gallery (HTTP ${res.status}).`);
28209
+ if (json?.error) log.dim(` ${json.error}`);
28210
+ return null;
28211
+ }
28212
+
28213
+ // src/commands/native.ts
28214
+ var PROGRESS_STEPS = 10;
28215
+ var MB2 = 1024 * 1024;
28216
+ function loggerFor2(opts) {
28217
+ if (!opts.json) return createLogger({ quiet: opts.quiet });
28218
+ const err = (s) => {
28219
+ if (!opts.quiet) writeSafely(process.stderr, `${s}
28220
+ `);
28221
+ };
28222
+ return {
28223
+ info: (m) => err(`i ${m}`),
28224
+ success: (m) => err(`\u2713 ${m}`),
28225
+ warn: (m) => err(`! ${m}`),
28226
+ error: (m) => writeSafely(process.stderr, `\u2717 ${m}
28227
+ `),
28228
+ step: (m) => err(`\u203A ${m}`),
28229
+ dim: err,
28230
+ plain: err
28231
+ };
28232
+ }
28233
+ function progress(log, label, total) {
28234
+ let shown = 0;
28235
+ return (done) => {
28236
+ const step = Math.floor(done / Math.max(total, 1) * PROGRESS_STEPS);
28237
+ if (step <= shown) return;
28238
+ shown = step;
28239
+ log.dim(` ${label} ${step * PROGRESS_STEPS}%`);
28240
+ };
28241
+ }
28242
+ async function context3(opts, log) {
28243
+ const meta = await readProject();
28244
+ if (!meta) {
28245
+ log.error("No genex project here. Run `genex init` or `genex link <game>` in this directory first.");
28246
+ return null;
28247
+ }
28248
+ const apiUrl = opts.apiUrl ?? getApiUrl(meta.apiUrl);
28249
+ const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
28250
+ if (!token) {
28251
+ log.error("Not authorized. Run `genex link <your-game>` to sign in.");
28252
+ return null;
28253
+ }
28254
+ return { apiUrl, projectId: meta.id, token };
28255
+ }
28256
+ function refusedInHostedSession(log) {
28257
+ if (!inHostedSession()) return false;
28258
+ log.error("Native builds are published from the creator's own computer, not from a hosted session.");
28259
+ return true;
28260
+ }
28261
+ function sizeLine(bytes) {
28262
+ return `${(bytes / MB2).toFixed(1)} MB`;
28263
+ }
28264
+ function describeLine(d) {
28265
+ const parts = [`${d.platform} ${d.arch}`, d.entry, `${sizeLine(d.installedBytes)} unpacked`];
28266
+ if (d.minOs) parts.push(`${d.platform === "mac" ? "macOS" : "system"} ${d.minOs}+`);
28267
+ if (d.engine) parts.push(d.engine);
28268
+ return parts.join(" \xB7 ");
28269
+ }
28270
+ async function runPublish2(opts, log) {
28271
+ if (refusedInHostedSession(log)) return false;
28272
+ if (!opts.nativePath) {
28273
+ log.error("Which build? `genex native publish <path>` \u2014 a .zip, a .app, or the build's folder.");
28274
+ return false;
28275
+ }
28276
+ const ctx = await context3(opts, log);
28277
+ if (!ctx) return false;
28278
+ let packed;
28279
+ try {
28280
+ if (!opts.nativePath.toLowerCase().endsWith(".zip")) log.step(`Zipping ${opts.nativePath}\u2026`);
28281
+ packed = await packNativeBuild(opts.nativePath);
28282
+ } catch (err) {
28283
+ if (!(err instanceof NativeBuildError)) throw err;
28284
+ log.error(err.message);
28285
+ return false;
28286
+ }
28287
+ try {
28288
+ const description = await describeNativeArchive(packed.zipPath, opts);
28289
+ log.step(describeLine(description));
28290
+ log.step("Hashing the archive\u2026");
28291
+ const { sha256: sha2562, sizeBytes } = await hashFile(packed.zipPath);
28292
+ const version = buildVersion(opts.buildVersion, description.appVersion, sha2562);
28293
+ const { platform, arch, entry, installedBytes, minOs, engine } = description;
28294
+ const grant = await requestNativeUpload(ctx, { platform, arch, sha256: sha2562, sizeBytes }, log);
28295
+ if (!grant) return false;
28296
+ if (grant.uploaded) {
28297
+ log.step(`Already uploaded (${sizeLine(sizeBytes)}).`);
28298
+ } else {
28299
+ log.step(`Uploading ${sizeLine(sizeBytes)}\u2026`);
28300
+ const sent = await uploadNativeArchive(grant, packed.zipPath, sizeBytes, log, progress(log, "uploaded", sizeBytes));
28301
+ if (!sent) return false;
28302
+ }
28303
+ const build = await publishNativeBuild(
28304
+ ctx,
28305
+ { platform, arch, sha256: sha2562, sizeBytes, installedBytes, version, entry, ...minOs ? { minOs } : {}, ...engine ? { engine } : {} },
28306
+ log
28307
+ );
28308
+ if (!build) return false;
28309
+ log.success(`Live \u2014 Genex desktop players on ${platform} ${arch} now get version ${build.version}.`);
28310
+ log.dim(` ${build.url}`);
28311
+ const listing = opts.gallery ? await listGame(ctx, opts, log) : void 0;
28312
+ if (opts.json) {
28313
+ console.log(JSON.stringify({ build, engine: engine ?? null, ...listing === void 0 ? {} : { listing } }));
28314
+ }
28315
+ return listing === void 0 || listing?.state === "published" || listing?.state === "held";
28316
+ } catch (err) {
28317
+ if (!(err instanceof NativeBuildError)) throw err;
28318
+ log.error(err.message);
28319
+ return false;
28320
+ } finally {
28321
+ await packed.cleanup();
28322
+ }
28323
+ }
28324
+ async function listGame(ctx, opts, log) {
28325
+ log.step("Listing it in the gallery\u2026");
28326
+ const listing = await listInGallery(
28327
+ ctx,
28328
+ {
28329
+ ...opts.title ? { title: opts.title } : {},
28330
+ ...opts.description ? { description: opts.description } : {},
28331
+ ...opts.categories?.length ? { categories: opts.categories } : {}
28332
+ },
28333
+ log
28334
+ );
28335
+ if (!listing) return null;
28336
+ if (listing.state === "published") {
28337
+ log.success("Listed in the gallery.");
28338
+ return listing;
28339
+ }
28340
+ const why = listing.detail ? `: ${listing.detail}` : ".";
28341
+ if (listing.state === "held") log.warn(`Held for review before it is listed${why}`);
28342
+ else log.error(`The listing was refused${why}`);
28343
+ return listing;
28344
+ }
28345
+ function buildLine(b) {
28346
+ return `${c.bold(`${b.platform} ${b.arch}`)} ${b.version} ${b.entry} ${sizeLine(b.sizeBytes)} ${b.publishedAt}`;
28347
+ }
28348
+ async function runList2(opts, log) {
28349
+ const ctx = await context3(opts, log);
28350
+ if (!ctx) return false;
28351
+ const builds = await listNativeBuilds(ctx, log);
28352
+ if (!builds) return false;
28353
+ if (opts.json) {
28354
+ console.log(JSON.stringify({ builds }));
28355
+ return true;
28356
+ }
28357
+ if (builds.length === 0) {
28358
+ log.plain("No native builds. Publish one with `genex native publish <path>`.");
28359
+ return true;
28360
+ }
28361
+ for (const b of builds) log.plain(buildLine(b));
28362
+ return true;
28363
+ }
28364
+ async function runRemove(opts, log) {
28365
+ if (refusedInHostedSession(log)) return false;
28366
+ if (!opts.platform || !opts.arch) {
28367
+ log.error("Which build? `genex native remove --platform <mac|windows|linux> --arch <arm64|x64|universal> --yes`.");
28368
+ return false;
28369
+ }
28370
+ if (!opts.yes) {
28371
+ log.error(`This stops offering the ${opts.platform} ${opts.arch} build to players. Re-run with --yes to remove it.`);
28372
+ return false;
28373
+ }
28374
+ const ctx = await context3(opts, log);
28375
+ if (!ctx) return false;
28376
+ if (!await removeNativeBuild(ctx, opts.platform, opts.arch, log)) return false;
28377
+ if (opts.json) console.log(JSON.stringify({ removed: { platform: opts.platform, arch: opts.arch } }));
28378
+ log.success(`Removed the ${opts.platform} ${opts.arch} build. Copies already installed keep working.`);
28379
+ return true;
28380
+ }
28381
+ var SUBCOMMANDS2 = {
28382
+ publish: runPublish2,
28383
+ list: runList2,
28384
+ remove: runRemove
28385
+ };
28386
+ async function runNative(opts) {
28387
+ const log = loggerFor2(opts);
28388
+ const run5 = opts.name ? SUBCOMMANDS2[opts.name] : void 0;
28389
+ if (!run5) {
28390
+ log.error("Unknown native command. Use `genex native publish <path>`, `genex native list` or `genex native remove`.");
28391
+ process.exitCode = 1;
28392
+ return;
28393
+ }
28394
+ if (!await run5(opts, log)) process.exitCode = 1;
28395
+ }
28396
+
27676
28397
  // src/lib/costs.ts
27677
28398
  var TYPICAL_CREDITS = {
27678
28399
  model: 35,
@@ -27792,6 +28513,11 @@ ${c.bold("Usage")}
27792
28513
  genex rollback [--yes] Put players back on the previous version. One
27793
28514
  version back, instantly \u2014 your code is not
27794
28515
  changed, only what the play URL serves.
28516
+ genex native <sub> [options] This game's Mac/Windows/Linux builds for the Genex
28517
+ desktop app: publish <path> | list | remove.
28518
+ "publish" takes a .zip, a .app or the build's
28519
+ folder and makes it what desktop players install
28520
+ for its platform and processor, live at once.
27795
28521
  genex cover [<file>] Show this game's cover and where it came from, or
27796
28522
  make <file> (PNG/JPEG/WebP, \u22648 MB) the cover: one
27797
28523
  real frame of the game \u2014 its signature moment,
@@ -28594,6 +29320,39 @@ ${c.bold("Content filter")}
28594
29320
  a body) as body horror \u2014 describe the object or machine instead: "a
28595
29321
  statue-like figure threaded into the wall" passes where "cables entering her
28596
29322
  spine" fails. Rejections are never retryable; reword and re-run.
29323
+ `,
29324
+ native: `${c.bold("genex native")} \u2014 a Unity, Godot or Unreal build players install from the Genex desktop app.
29325
+
29326
+ ${c.bold("Usage")}
29327
+ genex native publish <path> Make this build what desktop players on its platform
29328
+ and processor install. <path> is a .zip, a .app, or
29329
+ the build's folder (zipped for you, links and
29330
+ permissions kept). Live as soon as it returns; the
29331
+ build it replaces stays downloadable one more version.
29332
+ genex native list This game's builds: platform, processor, version.
29333
+ genex native remove --platform <p> --arch <a> --yes
29334
+ Stop offering one build. Installed copies keep working.
29335
+
29336
+ ${c.bold("Read from the build, or set")}
29337
+ --platform mac|windows|linux A .app at the archive's root is mac, an .exe windows.
29338
+ --arch arm64|x64|universal From the app's executable header.
29339
+ --entry <path> What runs: the root .app, or the game's .exe.
29340
+ --build-version <id> Default: the app's own version + the archive's hash,
29341
+ so every rebuild is an update.
29342
+ --min-os <version> Default: a Mac app's LSMinimumSystemVersion.
29343
+ --engine unity|godot|unreal From the files the engine ships.
29344
+ --json One JSON object on stdout ({"build": \u2026}).
29345
+
29346
+ ${c.bold("List it in the gallery")}
29347
+ --gallery After the build is live, list the game in the public
29348
+ gallery, the same moderated listing \`genex publish\`
29349
+ does, without building or deploying a web bundle.
29350
+ --title <t> --description <d> The listing's words (kept as they are when omitted).
29351
+ --categories <list> 1-3, comma-separated, e.g. games.
29352
+
29353
+ ${c.bold("Notes")}
29354
+ An archive is at most 4 GB. A Mac app must be signed, at least ad hoc (Unity, Godot
29355
+ and Unreal do this when they build) \u2014 the desktop app refuses an unsigned one.
28597
29356
  `,
28598
29357
  wait: `${c.bold("genex wait")} \u2014 attach to a generation already running, or just ask how it is doing.
28599
29358
 
@@ -28749,7 +29508,15 @@ function parseArgs(argv) {
28749
29508
  "--tags",
28750
29509
  // `genex cover --clip <file> --commit <sha>`: the clip and the build it shows.
28751
29510
  "--clip",
28752
- "--commit"
29511
+ "--commit",
29512
+ // `genex native publish|remove` value flags. `--build-version`, never
29513
+ // `--version`, which prints the CLI's own.
29514
+ "--platform",
29515
+ "--arch",
29516
+ "--entry",
29517
+ "--build-version",
29518
+ "--min-os",
29519
+ "--engine"
28753
29520
  ]);
28754
29521
  let i = 0;
28755
29522
  while (i < argv.length) {
@@ -28899,6 +29666,9 @@ function parseArgs(argv) {
28899
29666
  case "--yes":
28900
29667
  parsed.options.yes = true;
28901
29668
  break;
29669
+ case "--gallery":
29670
+ parsed.options.gallery = true;
29671
+ break;
28902
29672
  // A pair rather than one flag taking a value: `--resellable false` reads
28903
29673
  // as a filename to every shell user who has ever mistyped it, and the two
28904
29674
  // states have to be distinguishable from ABSENT so `shop set` can leave
@@ -29023,6 +29793,13 @@ function parseArgs(argv) {
29023
29793
  parsed.error = `Unexpected argument: ${arg}`;
29024
29794
  return parsed;
29025
29795
  }
29796
+ } else if (parsed.command === "native") {
29797
+ if (parsed.options.name === "publish" && parsed.options.nativePath === void 0) {
29798
+ parsed.options.nativePath = arg;
29799
+ } else {
29800
+ parsed.error = `Unexpected argument: ${arg}`;
29801
+ return parsed;
29802
+ }
29026
29803
  } else if (parsed.command === "blender") {
29027
29804
  if (parsed.options.name === "import" && parsed.options.importUrl === void 0) {
29028
29805
  parsed.options.importUrl = arg;
@@ -29070,6 +29847,24 @@ function applyValueFlag(options, flag, value) {
29070
29847
  case "--dir":
29071
29848
  options.dir = value;
29072
29849
  break;
29850
+ case "--platform":
29851
+ options.platform = value.toLowerCase();
29852
+ break;
29853
+ case "--arch":
29854
+ options.arch = value.toLowerCase();
29855
+ break;
29856
+ case "--entry":
29857
+ options.entry = value;
29858
+ break;
29859
+ case "--build-version":
29860
+ options.buildVersion = value;
29861
+ break;
29862
+ case "--min-os":
29863
+ options.minOs = value;
29864
+ break;
29865
+ case "--engine":
29866
+ options.engine = value.toLowerCase();
29867
+ break;
29073
29868
  case "--env":
29074
29869
  options.envPath = value;
29075
29870
  break;
@@ -29661,6 +30456,9 @@ async function main() {
29661
30456
  case "rollback":
29662
30457
  await runRollback(parsed.options);
29663
30458
  break;
30459
+ case "native":
30460
+ await runNative(parsed.options);
30461
+ break;
29664
30462
  case "cover":
29665
30463
  await runCover({ ...parsed.options, imagePath: parsed.options.name });
29666
30464
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.36.10-dev.965",
3
+ "version": "1.36.11-dev.967",
4
4
  "description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {