trajectories-sh 1.4.0 → 1.5.1
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 @@ program.command("upload <directory>").description("Upload a trajectory job direc
|
|
|
81
81
|
Pushing ${directory} \u2192 ${chalk.cyan(slug)}
|
|
82
82
|
`));
|
|
83
83
|
try {
|
|
84
|
-
const { uploadJob } = await import("./upload-
|
|
84
|
+
const { uploadJob } = await import("./upload-3VEY2SEN.js");
|
|
85
85
|
const result = await uploadJob(directory, {
|
|
86
86
|
slug,
|
|
87
87
|
name: opts.name,
|
|
@@ -120,10 +120,24 @@ async function convertToWebP(dir) {
|
|
|
120
120
|
}
|
|
121
121
|
return { convertedDir: tmpDir, pngsConverted: converted, savedBytes };
|
|
122
122
|
}
|
|
123
|
+
function hasTrialData(dir) {
|
|
124
|
+
const files = collectFiles(dir);
|
|
125
|
+
const hasTopResult = files.some((f) => f.relPath === "result.json");
|
|
126
|
+
const trialResults = files.filter(
|
|
127
|
+
(f) => f.relPath !== "result.json" && f.relPath.endsWith("/result.json")
|
|
128
|
+
);
|
|
129
|
+
const hasTrajectory = files.some((f) => f.relPath.endsWith("/trajectory.json"));
|
|
130
|
+
return hasTopResult && (trialResults.length > 0 || hasTrajectory);
|
|
131
|
+
}
|
|
123
132
|
async function uploadJob(dir, opts) {
|
|
124
133
|
const slug = opts.slug ?? dir.replace(/\/+$/, "").split("/").pop();
|
|
125
134
|
const name = opts.name ?? slug;
|
|
126
135
|
const visibility = opts.visibility ?? "private";
|
|
136
|
+
if (!hasTrialData(dir)) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"No trial data found. A trajectory job needs a result.json at the root and at least one trial directory with a result.json or trajectory.json."
|
|
139
|
+
);
|
|
140
|
+
}
|
|
127
141
|
const { convertedDir, pngsConverted, savedBytes } = await convertToWebP(dir);
|
|
128
142
|
if (pngsConverted > 0) {
|
|
129
143
|
console.log(` \u2713 Converted ${pngsConverted} PNGs to WebP (saved ${(savedBytes / 1024 / 1024).toFixed(1)} MB)`);
|
|
@@ -131,7 +145,37 @@ async function uploadJob(dir, opts) {
|
|
|
131
145
|
const files = collectFiles(convertedDir);
|
|
132
146
|
const totalSize = files.reduce((s, f) => s + f.size, 0);
|
|
133
147
|
console.log(` ${files.length} files, ${(totalSize / 1024 / 1024).toFixed(1)} MB`);
|
|
134
|
-
|
|
148
|
+
try {
|
|
149
|
+
const auth = await getAuthHeader();
|
|
150
|
+
const usageResp = await fetch(`${getApiUrl()}/api/cli/usage`, {
|
|
151
|
+
headers: auth ? { Authorization: auth } : {},
|
|
152
|
+
signal: AbortSignal.timeout(1e4)
|
|
153
|
+
});
|
|
154
|
+
if (usageResp.status === 200) {
|
|
155
|
+
const usage = await usageResp.json();
|
|
156
|
+
if (usage.plan !== "enterprise") {
|
|
157
|
+
if (usage.trajectory_count >= usage.trajectory_limit) {
|
|
158
|
+
const plan = usage.plan === "free" ? "Team" : "Enterprise";
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Trajectory limit reached (${usage.trajectory_count.toLocaleString()}/${usage.trajectory_limit.toLocaleString()}). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const projectedStorage = usage.storage_bytes_used + totalSize;
|
|
164
|
+
if (projectedStorage > usage.storage_limit_bytes) {
|
|
165
|
+
const usedGB = (usage.storage_bytes_used / 1024 ** 3).toFixed(1);
|
|
166
|
+
const uploadGB = (totalSize / 1024 ** 3).toFixed(1);
|
|
167
|
+
const limitGB = (usage.storage_limit_bytes / 1024 ** 3).toFixed(0);
|
|
168
|
+
const plan = usage.plan === "free" ? "Team" : "Enterprise";
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Storage limit would be exceeded (${usedGB} GB used + ${uploadGB} GB upload > ${limitGB} GB limit). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} catch (e) {
|
|
176
|
+
if (e.message?.includes("limit")) throw e;
|
|
177
|
+
}
|
|
178
|
+
const initResp = await apiPost("/api/cli/push/init", { slug, name, visibility, estimated_bytes: totalSize });
|
|
135
179
|
if (!initResp.ok) {
|
|
136
180
|
throw new Error(`Init failed: ${await initResp.text()}`);
|
|
137
181
|
}
|