@genex-ai/cli-demo 0.77.1-dev.199 → 0.78.0-dev.200
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 +100 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3225,7 +3225,7 @@ async function deployGame(ctx, opts, log) {
|
|
|
3225
3225
|
return false;
|
|
3226
3226
|
}
|
|
3227
3227
|
log.step(`Uploading ${files.length} files\u2026`);
|
|
3228
|
-
if (!await uploadAll(grant
|
|
3228
|
+
if (!await uploadAll(grant, files, log)) {
|
|
3229
3229
|
log.error("Couldn't upload your game \u2014 please try again.");
|
|
3230
3230
|
return false;
|
|
3231
3231
|
}
|
|
@@ -3306,21 +3306,24 @@ async function getUploadToken(ctx, commit, log) {
|
|
|
3306
3306
|
}
|
|
3307
3307
|
return await res.json();
|
|
3308
3308
|
}
|
|
3309
|
-
async function uploadAll(
|
|
3310
|
-
const base = uploadUrl.replace(/\/+$/, "");
|
|
3309
|
+
async function uploadAll(grant, files, log) {
|
|
3310
|
+
const base = grant.uploadUrl.replace(/\/+$/, "");
|
|
3311
|
+
const threshold = grant.limits.singlePutMaxBytes ?? grant.limits.maxFileBytes;
|
|
3312
|
+
const small = files.filter((f) => f.bytes.length <= threshold);
|
|
3313
|
+
const large = files.filter((f) => f.bytes.length > threshold);
|
|
3311
3314
|
let failed = false;
|
|
3312
3315
|
let next = 0;
|
|
3313
3316
|
const worker = async () => {
|
|
3314
3317
|
while (!failed) {
|
|
3315
3318
|
const i = next++;
|
|
3316
|
-
if (i >=
|
|
3317
|
-
const f =
|
|
3319
|
+
if (i >= small.length) return;
|
|
3320
|
+
const f = small[i];
|
|
3318
3321
|
const encoded = f.relPath.split("/").map(encodeURIComponent).join("/");
|
|
3319
3322
|
let res = null;
|
|
3320
3323
|
try {
|
|
3321
3324
|
res = await fetch(`${base}/${encoded}`, {
|
|
3322
3325
|
method: "PUT",
|
|
3323
|
-
headers: { Authorization: `Bearer ${
|
|
3326
|
+
headers: { Authorization: `Bearer ${grant.token}` },
|
|
3324
3327
|
body: f.bytes
|
|
3325
3328
|
});
|
|
3326
3329
|
} catch {
|
|
@@ -3332,8 +3335,97 @@ async function uploadAll(uploadUrl, uploadToken, files, log) {
|
|
|
3332
3335
|
}
|
|
3333
3336
|
}
|
|
3334
3337
|
};
|
|
3335
|
-
await Promise.all(Array.from({ length: Math.min(8,
|
|
3336
|
-
return
|
|
3338
|
+
await Promise.all(Array.from({ length: Math.min(8, small.length) }, () => worker()));
|
|
3339
|
+
if (failed) return false;
|
|
3340
|
+
for (const f of large) {
|
|
3341
|
+
if (!await uploadMultipart(base, grant.token, f, grant.limits.partSizeBytes, log)) return false;
|
|
3342
|
+
}
|
|
3343
|
+
return true;
|
|
3344
|
+
}
|
|
3345
|
+
async function uploadMultipart(base, uploadToken, f, partSize, log) {
|
|
3346
|
+
const encoded = f.relPath.split("/").map(encodeURIComponent).join("/");
|
|
3347
|
+
const auth = { Authorization: `Bearer ${uploadToken}` };
|
|
3348
|
+
const totalParts = Math.ceil(f.bytes.length / partSize);
|
|
3349
|
+
log.step(`Uploading ${f.relPath} (${(f.bytes.length / 1048576).toFixed(1)} MB in ${totalParts} parts)\u2026`);
|
|
3350
|
+
let uploadId;
|
|
3351
|
+
try {
|
|
3352
|
+
const res = await fetch(`${base}/${encoded}?action=mpu-create`, { method: "POST", headers: auth });
|
|
3353
|
+
if (!res.ok) {
|
|
3354
|
+
log.warn(`Upload failed for ${f.relPath} (HTTP ${res.status} starting multipart).`);
|
|
3355
|
+
return false;
|
|
3356
|
+
}
|
|
3357
|
+
uploadId = (await res.json()).uploadId;
|
|
3358
|
+
} catch {
|
|
3359
|
+
log.warn(`Upload failed for ${f.relPath} (network error starting multipart).`);
|
|
3360
|
+
return false;
|
|
3361
|
+
}
|
|
3362
|
+
const abort = async () => {
|
|
3363
|
+
try {
|
|
3364
|
+
await fetch(`${base}/${encoded}?action=mpu-abort&uploadId=${encodeURIComponent(uploadId)}`, {
|
|
3365
|
+
method: "DELETE",
|
|
3366
|
+
headers: auth
|
|
3367
|
+
});
|
|
3368
|
+
} catch {
|
|
3369
|
+
}
|
|
3370
|
+
};
|
|
3371
|
+
const parts = [];
|
|
3372
|
+
let failed = false;
|
|
3373
|
+
let next = 0;
|
|
3374
|
+
const partWorker = async () => {
|
|
3375
|
+
while (!failed) {
|
|
3376
|
+
const i = next++;
|
|
3377
|
+
if (i >= totalParts) return;
|
|
3378
|
+
const partNumber = i + 1;
|
|
3379
|
+
const bytes = f.bytes.subarray(i * partSize, Math.min((i + 1) * partSize, f.bytes.length));
|
|
3380
|
+
const part = await uploadPartWithRetry(base, encoded, auth, uploadId, partNumber, bytes);
|
|
3381
|
+
if (!part) {
|
|
3382
|
+
failed = true;
|
|
3383
|
+
log.warn(`Upload failed for ${f.relPath} (part ${partNumber}/${totalParts}).`);
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
parts.push(part);
|
|
3387
|
+
}
|
|
3388
|
+
};
|
|
3389
|
+
await Promise.all(Array.from({ length: Math.min(4, totalParts) }, () => partWorker()));
|
|
3390
|
+
if (failed) {
|
|
3391
|
+
await abort();
|
|
3392
|
+
return false;
|
|
3393
|
+
}
|
|
3394
|
+
parts.sort((a, b) => a.partNumber - b.partNumber);
|
|
3395
|
+
try {
|
|
3396
|
+
const res = await fetch(`${base}/${encoded}?action=mpu-complete&uploadId=${encodeURIComponent(uploadId)}`, {
|
|
3397
|
+
method: "POST",
|
|
3398
|
+
headers: { ...auth, "Content-Type": "application/json" },
|
|
3399
|
+
body: JSON.stringify({ parts })
|
|
3400
|
+
});
|
|
3401
|
+
if (!res.ok) {
|
|
3402
|
+
log.warn(`Upload failed for ${f.relPath} (HTTP ${res.status} completing multipart).`);
|
|
3403
|
+
await abort();
|
|
3404
|
+
return false;
|
|
3405
|
+
}
|
|
3406
|
+
} catch {
|
|
3407
|
+
log.warn(`Upload failed for ${f.relPath} (network error completing multipart).`);
|
|
3408
|
+
await abort();
|
|
3409
|
+
return false;
|
|
3410
|
+
}
|
|
3411
|
+
return true;
|
|
3412
|
+
}
|
|
3413
|
+
async function uploadPartWithRetry(base, encoded, auth, uploadId, partNumber, bytes) {
|
|
3414
|
+
for (let attempt = 0; ; attempt++) {
|
|
3415
|
+
let res = null;
|
|
3416
|
+
try {
|
|
3417
|
+
res = await fetch(
|
|
3418
|
+
`${base}/${encoded}?action=mpu-part&uploadId=${encodeURIComponent(uploadId)}&partNumber=${partNumber}`,
|
|
3419
|
+
{ method: "PUT", headers: auth, body: bytes }
|
|
3420
|
+
);
|
|
3421
|
+
if (res.ok) return await res.json();
|
|
3422
|
+
} catch {
|
|
3423
|
+
res = null;
|
|
3424
|
+
}
|
|
3425
|
+
if (res && res.status < 500) return null;
|
|
3426
|
+
if (attempt >= 2) return null;
|
|
3427
|
+
await new Promise((resolve) => setTimeout(resolve, attempt === 0 ? 500 : 2e3));
|
|
3428
|
+
}
|
|
3337
3429
|
}
|
|
3338
3430
|
async function callPublish(ctx, commit, opts, log) {
|
|
3339
3431
|
let res;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.0-dev.200",
|
|
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": {
|