trajectories-sh 1.18.0 → 1.18.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.
@@ -0,0 +1,55 @@
1
+ import {
2
+ getApiUrl,
3
+ getAuthHeader
4
+ } from "./chunk-Q2ZEN43L.js";
5
+
6
+ // src/quota.ts
7
+ async function fetchOrgUsage() {
8
+ const auth = await getAuthHeader();
9
+ if (!auth) return null;
10
+ try {
11
+ const resp = await fetch(`${getApiUrl()}/api/cli/usage`, {
12
+ headers: { Authorization: auth },
13
+ signal: AbortSignal.timeout(1e4)
14
+ });
15
+ if (!resp.ok) return null;
16
+ return await resp.json();
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ function planUpgradeLabel(plan) {
22
+ return plan === "free" ? "Team" : "Premiere";
23
+ }
24
+ async function preflightQuota(check) {
25
+ const usage = await fetchOrgUsage();
26
+ if (!usage) return;
27
+ if (usage.plan === "premiere") return;
28
+ const plan = planUpgradeLabel(usage.plan);
29
+ if (check.kind === "trajectory") {
30
+ if (usage.trajectory_count >= usage.trajectory_limit) {
31
+ throw new Error(
32
+ `Trajectory limit reached (${usage.trajectory_count.toLocaleString()}/${usage.trajectory_limit.toLocaleString()}). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
33
+ );
34
+ }
35
+ const projected = usage.storage_bytes_used + check.projectedAddedBytes;
36
+ if (projected > usage.storage_limit_bytes) {
37
+ const usedGB = (usage.storage_bytes_used / 1024 ** 3).toFixed(1);
38
+ const uploadGB = (check.projectedAddedBytes / 1024 ** 3).toFixed(1);
39
+ const limitGB = (usage.storage_limit_bytes / 1024 ** 3).toFixed(0);
40
+ throw new Error(
41
+ `Storage limit would be exceeded (${usedGB} GB used + ${uploadGB} GB upload > ${limitGB} GB limit). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
42
+ );
43
+ }
44
+ return;
45
+ }
46
+ if (usage.task_count + check.newTaskCount > usage.task_limit) {
47
+ throw new Error(
48
+ `Task limit reached (${usage.task_count.toLocaleString()}/${usage.task_limit.toLocaleString()}). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
49
+ );
50
+ }
51
+ }
52
+
53
+ export {
54
+ preflightQuota
55
+ };
package/dist/cli.js CHANGED
@@ -95,7 +95,7 @@ async function uploadTrajectoryAction(directory, opts) {
95
95
  Uploading trajectory ${directory} \u2192 ${chalk.cyan(slug)}
96
96
  `));
97
97
  try {
98
- const { uploadJob } = await import("./upload-NR7SFMZZ.js");
98
+ const { uploadJob } = await import("./upload-JHU236SW.js");
99
99
  const result = await uploadJob(directory, {
100
100
  slug,
101
101
  name: opts.name
@@ -141,7 +141,7 @@ async function uploadTaskAction(directory, opts) {
141
141
  Uploading task ${directory} \u2192 ${chalk.cyan(name)}
142
142
  `));
143
143
  try {
144
- const { pushTask } = await import("./push-task-IZZNF3ZA.js");
144
+ const { pushTask } = await import("./push-task-OZTAROKW.js");
145
145
  const result = await pushTask(directory, {
146
146
  name: opts.name,
147
147
  visibility: opts.visibility
@@ -1,3 +1,6 @@
1
+ import {
2
+ preflightQuota
3
+ } from "./chunk-2ELJH3IV.js";
1
4
  import {
2
5
  getApiUrl,
3
6
  getAuthHeader
@@ -116,18 +119,9 @@ async function pushTask(dir, opts) {
116
119
  "No task content found. A task needs at least an instruction.md, Dockerfile, or test file."
117
120
  );
118
121
  }
119
- const apiUrl = getApiUrl();
120
- try {
121
- const usageResp = await fetch(`${apiUrl}/api/cli/usage`, {
122
- headers: { Authorization: auth },
123
- signal: AbortSignal.timeout(1e4)
124
- });
125
- if (usageResp.ok) {
126
- const usage = await usageResp.json();
127
- }
128
- } catch {
129
- }
122
+ await preflightQuota({ kind: "task", newTaskCount: 1 });
130
123
  console.log(" Uploading...");
124
+ const apiUrl = getApiUrl();
131
125
  const resp = await fetch(`${apiUrl}/api/cli/push-task`, {
132
126
  method: "POST",
133
127
  headers: {
@@ -1,3 +1,6 @@
1
+ import {
2
+ preflightQuota
3
+ } from "./chunk-2ELJH3IV.js";
1
4
  import {
2
5
  getApiUrl,
3
6
  getAuthHeader
@@ -10,7 +13,7 @@ import { tmpdir } from "os";
10
13
  import { randomBytes, createHash } from "crypto";
11
14
  var SKIP = /* @__PURE__ */ new Set(["__pycache__", ".DS_Store", ".git", "node_modules"]);
12
15
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
13
- var CONCURRENCY = Number(process.env.TRAJECTORIES_CLI_CONCURRENCY ?? "512");
16
+ var CONCURRENCY = Number(process.env.TRAJECTORIES_CLI_CONCURRENCY ?? "256");
14
17
  var BATCH_SIZE = Number(process.env.TRAJECTORIES_CLI_BATCH_SIZE ?? "500");
15
18
  var WEBP_CONCURRENCY = Number(process.env.TRAJECTORIES_CLI_WEBP_CONCURRENCY ?? "4");
16
19
  var HASH_CONCURRENCY = Number(process.env.TRAJECTORIES_CLI_HASH_CONCURRENCY ?? "16");
@@ -328,40 +331,11 @@ ${samples}${more}
328
331
  `
329
332
  );
330
333
  }
334
+ await preflightQuota({ kind: "trajectory", projectedAddedBytes: totalSize });
331
335
  process.stdout.write(` Hashing...\r`);
332
336
  const t0 = Date.now();
333
337
  const contentHash = await computeContentHash(files);
334
338
  console.log(` Hash: ${contentHash.slice(0, 16)}... (${((Date.now() - t0) / 1e3).toFixed(1)}s)`);
335
- try {
336
- const auth = await getAuthHeader();
337
- const usageResp = await fetch(`${getApiUrl()}/api/cli/usage`, {
338
- headers: auth ? { Authorization: auth } : {},
339
- signal: AbortSignal.timeout(1e4)
340
- });
341
- if (usageResp.status === 200) {
342
- const usage = await usageResp.json();
343
- if (usage.plan !== "premiere") {
344
- if (usage.trajectory_count >= usage.trajectory_limit) {
345
- const plan = usage.plan === "free" ? "Team" : "Premiere";
346
- throw new Error(
347
- `Trajectory limit reached (${usage.trajectory_count.toLocaleString()}/${usage.trajectory_limit.toLocaleString()}). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
348
- );
349
- }
350
- const projectedStorage = usage.storage_bytes_used + totalSize;
351
- if (projectedStorage > usage.storage_limit_bytes) {
352
- const usedGB = (usage.storage_bytes_used / 1024 ** 3).toFixed(1);
353
- const uploadGB = (totalSize / 1024 ** 3).toFixed(1);
354
- const limitGB = (usage.storage_limit_bytes / 1024 ** 3).toFixed(0);
355
- const plan = usage.plan === "free" ? "Team" : "Premiere";
356
- throw new Error(
357
- `Storage limit would be exceeded (${usedGB} GB used + ${uploadGB} GB upload > ${limitGB} GB limit). Upgrade to ${plan} for more: https://trajectories.sh/pricing`
358
- );
359
- }
360
- }
361
- }
362
- } catch (e) {
363
- if (e.message?.includes("limit")) throw e;
364
- }
365
339
  const initResp = await apiPost("/api/cli/push/init", { slug, name, visibility, estimated_bytes: totalSize, content_hash: contentHash });
366
340
  if (!initResp.ok) {
367
341
  throw new Error(`Init failed: ${await initResp.text()}`);
@@ -514,7 +488,7 @@ async function fetchSignedUrlsForBatch(jobId, paths) {
514
488
  async function uploadOneFileWithError(file, signedUrl) {
515
489
  const data = readFileSync(file.absPath);
516
490
  let lastError = "unknown error";
517
- for (let attempt = 0; attempt < 3; attempt++) {
491
+ for (let attempt = 0; attempt < 5; attempt++) {
518
492
  try {
519
493
  const resp = await fetch(signedUrl, {
520
494
  method: "PUT",
@@ -529,10 +503,14 @@ async function uploadOneFileWithError(file, signedUrl) {
529
503
  if (body) lastError += ` \u2014 ${body.slice(0, 150).replace(/\s+/g, " ")}`;
530
504
  } catch {
531
505
  }
506
+ if (resp.status === 503 || resp.status === 429) {
507
+ await sleep(2e3 * (attempt + 1));
508
+ continue;
509
+ }
532
510
  } catch (e) {
533
511
  lastError = `fetch error: ${e?.name ?? "Error"} ${e?.message ?? e}`;
534
512
  }
535
- if (attempt < 2) await sleep(1e3 * (attempt + 1));
513
+ if (attempt < 4) await sleep(1e3 * (attempt + 1));
536
514
  }
537
515
  return { ok: false, error: lastError };
538
516
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trajectories-sh",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "CLI for uploading trajectory jobs to trajectories.sh",
5
5
  "type": "module",
6
6
  "bin": {