trajectories-sh 1.10.0 → 1.10.2
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/cli.js
CHANGED
|
@@ -81,7 +81,7 @@ async function uploadTrajectoryAction(directory, opts) {
|
|
|
81
81
|
Uploading trajectory ${directory} \u2192 ${chalk.cyan(slug)}
|
|
82
82
|
`));
|
|
83
83
|
try {
|
|
84
|
-
const { uploadJob } = await import("./upload-
|
|
84
|
+
const { uploadJob } = await import("./upload-K2GBG3J3.js");
|
|
85
85
|
const result = await uploadJob(directory, {
|
|
86
86
|
slug,
|
|
87
87
|
name: opts.name,
|
|
@@ -355,8 +355,6 @@ async function uploadJob(dir, opts) {
|
|
|
355
355
|
}
|
|
356
356
|
async function uploadArchive(jobId, dir) {
|
|
357
357
|
const tar = await import("tar");
|
|
358
|
-
const { createReadStream } = await import("fs");
|
|
359
|
-
const { ProgressBar } = await import("./progress-H7WNKHDR.js");
|
|
360
358
|
const tmpArchive = join(tmpdir(), `trajectories-archive-${randomBytes(4).toString("hex")}.tar.gz`);
|
|
361
359
|
const entries = collectFiles(dir).map((f) => f.relPath);
|
|
362
360
|
await tar.create(
|
|
@@ -371,98 +369,110 @@ async function uploadArchive(jobId, dir) {
|
|
|
371
369
|
const archiveSize = statSync(tmpArchive).size;
|
|
372
370
|
console.log(` Archive: ${(archiveSize / 1024 / 1024).toFixed(1)} MB (gzipped, ${entries.length} entries)`);
|
|
373
371
|
try {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
throw new Error("Server returned no signed URL for archive upload");
|
|
381
|
-
}
|
|
382
|
-
let putError = null;
|
|
383
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
384
|
-
const bar = new ProgressBar({ total: 1, totalBytes: archiveSize, label: "\u2191 archive" });
|
|
385
|
-
const stream = createReadStream(tmpArchive);
|
|
386
|
-
let bytesSent = 0;
|
|
387
|
-
stream.on("data", (chunk) => {
|
|
388
|
-
bytesSent += chunk.length;
|
|
389
|
-
bar.setBytes(bytesSent);
|
|
390
|
-
});
|
|
372
|
+
const maxAttempts = 3;
|
|
373
|
+
let lastError = null;
|
|
374
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
375
|
+
if (attempt > 1) {
|
|
376
|
+
console.log(` Retrying archive upload (attempt ${attempt}/${maxAttempts})\u2026`);
|
|
377
|
+
}
|
|
391
378
|
try {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
body: stream,
|
|
400
|
-
duplex: "half",
|
|
401
|
-
signal: AbortSignal.timeout(30 * 60 * 1e3)
|
|
402
|
-
});
|
|
403
|
-
bar.setCount(1);
|
|
404
|
-
bar.done();
|
|
405
|
-
if (resp.ok) {
|
|
406
|
-
putError = null;
|
|
407
|
-
break;
|
|
379
|
+
await uploadArchiveOnce(jobId, tmpArchive, archiveSize, entries.length);
|
|
380
|
+
return;
|
|
381
|
+
} catch (e) {
|
|
382
|
+
lastError = e;
|
|
383
|
+
const msg = e?.message ?? String(e);
|
|
384
|
+
if (!/failed to store|extraction failed|extraction timed out/i.test(msg)) {
|
|
385
|
+
throw e;
|
|
408
386
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
if (body) putError += ` \u2014 ${body.slice(0, 200).replace(/\s+/g, " ")}`;
|
|
413
|
-
} catch {
|
|
387
|
+
if (attempt < maxAttempts) {
|
|
388
|
+
console.log(` \u26A0 ${msg}`);
|
|
389
|
+
await sleep(3e3 * attempt);
|
|
414
390
|
}
|
|
415
|
-
} catch (e) {
|
|
416
|
-
bar.done();
|
|
417
|
-
putError = `fetch error: ${e?.name ?? "Error"} ${e?.message ?? e}`;
|
|
418
|
-
}
|
|
419
|
-
try {
|
|
420
|
-
stream.destroy?.();
|
|
421
|
-
} catch {
|
|
422
391
|
}
|
|
423
|
-
if (attempt < 2) await sleep(2e3 * (attempt + 1));
|
|
424
|
-
}
|
|
425
|
-
if (putError) {
|
|
426
|
-
throw new Error(`Archive upload failed: ${putError}`);
|
|
427
392
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
393
|
+
throw lastError instanceof Error ? lastError : new Error(`Archive upload failed after ${maxAttempts} attempts`);
|
|
394
|
+
} finally {
|
|
395
|
+
try {
|
|
396
|
+
unlinkSync(tmpArchive);
|
|
397
|
+
} catch {
|
|
431
398
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async function uploadArchiveOnce(jobId, archivePath, archiveSize, entryCount) {
|
|
402
|
+
const { ProgressBar } = await import("./progress-H7WNKHDR.js");
|
|
403
|
+
const urlResp = await apiPost(`/api/cli/push/${jobId}/upload-url`, {});
|
|
404
|
+
if (!urlResp.ok) {
|
|
405
|
+
throw new Error(`Failed to get archive upload URL: ${await urlResp.text()}`);
|
|
406
|
+
}
|
|
407
|
+
const { signed_url: signedUrl } = await urlResp.json();
|
|
408
|
+
if (!signedUrl) {
|
|
409
|
+
throw new Error("Server returned no signed URL for archive upload");
|
|
410
|
+
}
|
|
411
|
+
const archiveData = readFileSync(archivePath);
|
|
412
|
+
let putError = null;
|
|
413
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
414
|
+
const bar = new ProgressBar({ total: 1, totalBytes: archiveSize, label: "\u2191 archive" });
|
|
415
|
+
bar.setBytes(archiveSize);
|
|
416
|
+
try {
|
|
417
|
+
const resp = await fetch(signedUrl, {
|
|
418
|
+
method: "PUT",
|
|
419
|
+
headers: { "Content-Type": "application/gzip" },
|
|
420
|
+
body: archiveData,
|
|
421
|
+
signal: AbortSignal.timeout(30 * 60 * 1e3)
|
|
422
|
+
});
|
|
423
|
+
bar.setCount(1);
|
|
424
|
+
bar.done();
|
|
425
|
+
if (resp.ok) {
|
|
426
|
+
putError = null;
|
|
427
|
+
break;
|
|
441
428
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (lastStatus?.status === "error") {
|
|
448
|
-
throw new Error(`Archive extraction failed: ${lastStatus?.detail ?? "unknown"}`);
|
|
429
|
+
putError = `HTTP ${resp.status} ${resp.statusText}`;
|
|
430
|
+
try {
|
|
431
|
+
const body = await resp.text();
|
|
432
|
+
if (body) putError += ` \u2014 ${body.slice(0, 200).replace(/\s+/g, " ")}`;
|
|
433
|
+
} catch {
|
|
449
434
|
}
|
|
435
|
+
} catch (e) {
|
|
436
|
+
bar.done();
|
|
437
|
+
putError = `fetch error: ${e?.name ?? "Error"} ${e?.message ?? e}`;
|
|
450
438
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
439
|
+
if (attempt < 2) await sleep(2e3 * (attempt + 1));
|
|
440
|
+
}
|
|
441
|
+
if (putError) {
|
|
442
|
+
throw new Error(`Archive upload failed: ${putError}`);
|
|
443
|
+
}
|
|
444
|
+
const extractResp = await apiPost(`/api/cli/push/${jobId}/extract`, {});
|
|
445
|
+
if (!extractResp.ok) {
|
|
446
|
+
throw new Error(`Failed to start extraction: ${await extractResp.text()}`);
|
|
447
|
+
}
|
|
448
|
+
const extractBar = new ProgressBar({ total: entryCount, totalBytes: 0, label: "\u2699 extract" });
|
|
449
|
+
const pollStart = Date.now();
|
|
450
|
+
const pollTimeoutMs = 30 * 60 * 1e3;
|
|
451
|
+
let lastStatus = null;
|
|
452
|
+
while (Date.now() - pollStart < pollTimeoutMs) {
|
|
453
|
+
await sleep(1500);
|
|
454
|
+
const statusResp = await apiGet(`/api/cli/push/${jobId}/extract-status`);
|
|
455
|
+
if (!statusResp.ok) {
|
|
456
|
+
throw new Error(`Failed to poll extract status: ${await statusResp.text()}`);
|
|
459
457
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
458
|
+
lastStatus = await statusResp.json();
|
|
459
|
+
const uploaded = Number(lastStatus?.uploaded ?? 0);
|
|
460
|
+
const errors = Number(lastStatus?.errors ?? 0);
|
|
461
|
+
extractBar.setCount(uploaded + errors);
|
|
462
|
+
if (lastStatus?.status === "done") break;
|
|
463
|
+
if (lastStatus?.status === "error") {
|
|
464
|
+
throw new Error(`Archive extraction failed: ${lastStatus?.detail ?? "unknown"}`);
|
|
464
465
|
}
|
|
465
466
|
}
|
|
467
|
+
extractBar.done();
|
|
468
|
+
if (!lastStatus || lastStatus.status !== "done") {
|
|
469
|
+
throw new Error("Archive extraction timed out");
|
|
470
|
+
}
|
|
471
|
+
if (lastStatus.errors && lastStatus.errors > 0) {
|
|
472
|
+
throw new Error(
|
|
473
|
+
`Archive extraction completed but ${lastStatus.errors} file(s) failed to store. Retry the upload or check server logs.`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
466
476
|
}
|
|
467
477
|
async function apiGet(path, timeout = 6e4) {
|
|
468
478
|
const auth = await getAuthHeader();
|