trajectories-sh 1.8.0 → 1.8.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-JY5KCP2X.js");
|
|
85
85
|
const result = await uploadJob(directory, {
|
|
86
86
|
slug,
|
|
87
87
|
name: opts.name,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { readdirSync, statSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
8
8
|
import { join, relative, dirname } from "path";
|
|
9
9
|
import { tmpdir } from "os";
|
|
10
|
-
import { randomBytes } from "crypto";
|
|
10
|
+
import { randomBytes, createHash } from "crypto";
|
|
11
11
|
var SKIP = /* @__PURE__ */ new Set(["__pycache__", ".DS_Store", ".git", "node_modules"]);
|
|
12
12
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
13
13
|
var CONCURRENCY = 12;
|
|
@@ -120,6 +120,15 @@ async function convertToWebP(dir) {
|
|
|
120
120
|
}
|
|
121
121
|
return { convertedDir: tmpDir, pngsConverted: converted, savedBytes };
|
|
122
122
|
}
|
|
123
|
+
function computeContentHash(files) {
|
|
124
|
+
const hash = createHash("sha256");
|
|
125
|
+
const sorted = [...files].sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
126
|
+
for (const f of sorted) {
|
|
127
|
+
hash.update(f.relPath);
|
|
128
|
+
hash.update(readFileSync(f.absPath));
|
|
129
|
+
}
|
|
130
|
+
return hash.digest("hex");
|
|
131
|
+
}
|
|
123
132
|
function hasTrialData(dir) {
|
|
124
133
|
const files = collectFiles(dir);
|
|
125
134
|
const hasTopResult = files.some((f) => f.relPath === "result.json");
|
|
@@ -144,7 +153,9 @@ async function uploadJob(dir, opts) {
|
|
|
144
153
|
}
|
|
145
154
|
const files = collectFiles(convertedDir);
|
|
146
155
|
const totalSize = files.reduce((s, f) => s + f.size, 0);
|
|
156
|
+
const contentHash = computeContentHash(files);
|
|
147
157
|
console.log(` ${files.length} files, ${(totalSize / 1024 / 1024).toFixed(1)} MB`);
|
|
158
|
+
console.log(` Hash: ${contentHash.slice(0, 16)}...`);
|
|
148
159
|
try {
|
|
149
160
|
const auth = await getAuthHeader();
|
|
150
161
|
const usageResp = await fetch(`${getApiUrl()}/api/cli/usage`, {
|
|
@@ -153,9 +164,9 @@ async function uploadJob(dir, opts) {
|
|
|
153
164
|
});
|
|
154
165
|
if (usageResp.status === 200) {
|
|
155
166
|
const usage = await usageResp.json();
|
|
156
|
-
if (usage.plan !== "
|
|
167
|
+
if (usage.plan !== "premiere") {
|
|
157
168
|
if (usage.trajectory_count >= usage.trajectory_limit) {
|
|
158
|
-
const plan = usage.plan === "free" ? "Team" : "
|
|
169
|
+
const plan = usage.plan === "free" ? "Team" : "Premiere";
|
|
159
170
|
throw new Error(
|
|
160
171
|
`Trajectory limit reached (${usage.trajectory_count.toLocaleString()}/${usage.trajectory_limit.toLocaleString()}). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
|
|
161
172
|
);
|
|
@@ -165,7 +176,7 @@ async function uploadJob(dir, opts) {
|
|
|
165
176
|
const usedGB = (usage.storage_bytes_used / 1024 ** 3).toFixed(1);
|
|
166
177
|
const uploadGB = (totalSize / 1024 ** 3).toFixed(1);
|
|
167
178
|
const limitGB = (usage.storage_limit_bytes / 1024 ** 3).toFixed(0);
|
|
168
|
-
const plan = usage.plan === "free" ? "Team" : "
|
|
179
|
+
const plan = usage.plan === "free" ? "Team" : "Premiere";
|
|
169
180
|
throw new Error(
|
|
170
181
|
`Storage limit would be exceeded (${usedGB} GB used + ${uploadGB} GB upload > ${limitGB} GB limit). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
|
|
171
182
|
);
|
|
@@ -175,8 +186,12 @@ async function uploadJob(dir, opts) {
|
|
|
175
186
|
} catch (e) {
|
|
176
187
|
if (e.message?.includes("limit")) throw e;
|
|
177
188
|
}
|
|
178
|
-
const initResp = await apiPost("/api/cli/push/init", { slug, name, visibility, estimated_bytes: totalSize });
|
|
189
|
+
const initResp = await apiPost("/api/cli/push/init", { slug, name, visibility, estimated_bytes: totalSize, content_hash: contentHash });
|
|
179
190
|
if (!initResp.ok) {
|
|
191
|
+
if (initResp.status === 409) {
|
|
192
|
+
const err = await initResp.json().catch(() => ({ detail: "Duplicate trajectory" }));
|
|
193
|
+
throw new Error(err.detail);
|
|
194
|
+
}
|
|
180
195
|
throw new Error(`Init failed: ${await initResp.text()}`);
|
|
181
196
|
}
|
|
182
197
|
const { job_id: jobId } = await initResp.json();
|