@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.
- package/dist/index.js +877 -79
- 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,
|
|
223
|
-
contexts[name] =
|
|
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
|
|
1928
|
-
const result = await
|
|
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
|
|
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")
|
|
2262
|
+
if (status2 === "failed") run5 += 1;
|
|
2263
2263
|
}
|
|
2264
|
-
return
|
|
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
|
|
3080
|
-
if (/\b(map|alphaMap)\s*:/.test(
|
|
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,
|
|
3082
|
+
where: `${rel}:${lineOf(content, call2.index)}`,
|
|
3083
3083
|
detail: "PointsMaterial with no map/alphaMap"
|
|
3084
3084
|
});
|
|
3085
3085
|
}
|
|
3086
|
-
for (const
|
|
3087
|
-
const args = splitArgs(
|
|
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,
|
|
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
|
|
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
|
|
4238
|
+
let run5 = 0;
|
|
4239
4239
|
for (let x = 0; x < W; x++) {
|
|
4240
|
-
if (subject[y * W + x])
|
|
4241
|
-
else if (
|
|
4242
|
-
tmp[y * W + x] =
|
|
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
|
-
|
|
4244
|
+
run5 = 0;
|
|
4245
4245
|
for (let x = W - 1; x >= 0; x--) {
|
|
4246
|
-
if (subject[y * W + x])
|
|
4247
|
-
else if (
|
|
4248
|
-
if (
|
|
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
|
|
4253
|
+
let run5 = 0;
|
|
4254
4254
|
for (let y = 0; y < H; y++) {
|
|
4255
|
-
if (tmp[y * W + x])
|
|
4256
|
-
else if (
|
|
4257
|
-
tmp2[y * W + x] =
|
|
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
|
-
|
|
4259
|
+
run5 = 0;
|
|
4260
4260
|
for (let y = H - 1; y >= 0; y--) {
|
|
4261
|
-
if (tmp[y * W + x])
|
|
4262
|
-
else if (
|
|
4263
|
-
if (
|
|
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
|
|
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
|
|
8365
|
-
else if (sub === "add") res = await
|
|
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
|
|
8368
|
-
else res = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10603
|
-
if (
|
|
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 ${
|
|
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
|
-
} : (
|
|
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(
|
|
21852
|
+
async function readVideo(path47, log) {
|
|
21853
21853
|
let bytes;
|
|
21854
21854
|
try {
|
|
21855
|
-
bytes = await readFile(
|
|
21855
|
+
bytes = await readFile(path47);
|
|
21856
21856
|
} catch {
|
|
21857
|
-
log.error(`Can't read ${
|
|
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(
|
|
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,
|
|
21867
|
-
const contentType = /\.mov$/i.test(
|
|
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(
|
|
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
|
|
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
|
-
|
|
22886
|
-
best = Math.max(best,
|
|
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
|
|
23240
|
+
let run5 = null;
|
|
23241
23241
|
for (let f = 0; f < air.length; f++) {
|
|
23242
|
-
if (air[f])
|
|
23242
|
+
if (air[f]) run5 = run5 ? [run5[0], f] : [f, f];
|
|
23243
23243
|
else {
|
|
23244
|
-
if (
|
|
23245
|
-
|
|
23244
|
+
if (run5 && (!best || run5[1] - run5[0] > best[1] - best[0])) best = run5;
|
|
23245
|
+
run5 = null;
|
|
23246
23246
|
}
|
|
23247
23247
|
}
|
|
23248
|
-
if (
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
25178
|
-
const res = await
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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}${
|
|
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(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/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.
|
|
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": {
|