trajectories-sh 1.18.4 → 1.18.6

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
@@ -98,7 +98,7 @@ async function uploadTrajectoryAction(pathArg, opts) {
98
98
  );
99
99
  }
100
100
  if (isZip) {
101
- const { uploadZip } = await import("./upload-XTPRJGN4.js");
101
+ const { uploadZip } = await import("./upload-CW5V2QSS.js");
102
102
  const slug2 = opts.slug ?? pathArg.replace(/\.zip$/i, "").replace(/\/+$/, "").split("/").pop();
103
103
  console.log(chalk.bold(`
104
104
  Uploading ZIP ${pathArg} \u2192 ${chalk.cyan(slug2)}
@@ -127,7 +127,7 @@ async function uploadTrajectoryAction(pathArg, opts) {
127
127
  Uploading trajectory ${pathArg} \u2192 ${chalk.cyan(slug)}
128
128
  `));
129
129
  try {
130
- const { uploadJob } = await import("./upload-XTPRJGN4.js");
130
+ const { uploadJob } = await import("./upload-CW5V2QSS.js");
131
131
  const result = await uploadJob(pathArg, {
132
132
  slug,
133
133
  name: opts.name
@@ -51,59 +51,88 @@ async function apiPost(path, body, timeout = 3e5) {
51
51
  });
52
52
  }
53
53
  async function uploadZip(zipPath, opts) {
54
- const { execSync } = await import("child_process");
55
- const { mkdtempSync, existsSync, readdirSync: readdirSync2, statSync: _statSync } = await import("fs");
56
- const { join: _join } = await import("path");
57
- const { tmpdir: _tmpdir } = await import("os");
54
+ const { readFileSync: readFileSync2, statSync: _statSync } = await import("fs");
58
55
  const fileStat = _statSync(zipPath);
59
- const sizeMB = (fileStat.size / (1024 * 1024)).toFixed(1);
60
- console.log(` ZIP size: ${sizeMB} MB`);
61
- console.log(" Extracting ZIP...");
62
- const tmpDir = mkdtempSync(_join(_tmpdir(), "traj-zip-"));
63
- execSync(`unzip -q -o ${JSON.stringify(zipPath)} -d ${JSON.stringify(tmpDir)}`, {
64
- stdio: "pipe",
65
- timeout: 3e5
56
+ const sizeBytes = fileStat.size;
57
+ const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(1);
58
+ const sizeGB = (sizeBytes / 1024 ** 3).toFixed(2);
59
+ console.log(` ZIP size: ${sizeBytes > 1024 ** 3 ? sizeGB + " GB" : sizeMB + " MB"}`);
60
+ const auth = await getAuthHeader();
61
+ if (!auth) throw new Error("Not authenticated. Run: trajectories auth login");
62
+ const apiUrl = getApiUrl();
63
+ console.log(" Requesting upload URL...");
64
+ const urlResp = await fetch(`${apiUrl}/api/cli/push-zip/upload-url`, {
65
+ method: "POST",
66
+ headers: { Authorization: auth, "Content-Type": "application/json" },
67
+ body: "{}",
68
+ signal: AbortSignal.timeout(3e4)
66
69
  });
67
- const topEntries = readdirSync2(tmpDir, { withFileTypes: true });
68
- const jobDirs = [];
69
- if (existsSync(_join(tmpDir, "config.json")) && existsSync(_join(tmpDir, "result.json"))) {
70
- jobDirs.push({ path: tmpDir, name: opts.name || opts.slug || "job" });
71
- } else {
72
- for (const entry of topEntries) {
73
- if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "__MACOSX") continue;
74
- const jobPath = _join(tmpDir, entry.name);
75
- if (existsSync(_join(jobPath, "config.json")) && existsSync(_join(jobPath, "result.json"))) {
76
- jobDirs.push({ path: jobPath, name: opts.name || entry.name });
77
- }
78
- }
70
+ if (!urlResp.ok) {
71
+ throw new Error(`Failed to get upload URL: ${await urlResp.text()}`);
79
72
  }
80
- if (jobDirs.length === 0) {
81
- throw new Error("No Harbor job directories found in the ZIP. Each job must contain config.json and result.json.");
73
+ const { task_id, signed_url } = await urlResp.json();
74
+ console.log(` Uploading ZIP to storage...`);
75
+ const zipData = readFileSync2(zipPath);
76
+ const putResp = await fetch(signed_url, {
77
+ method: "PUT",
78
+ headers: { "Content-Type": "application/zip" },
79
+ body: zipData,
80
+ signal: AbortSignal.timeout(36e5)
81
+ });
82
+ if (!putResp.ok) {
83
+ const body = await putResp.text().catch(() => "");
84
+ throw new Error(`ZIP upload to storage failed (${putResp.status}): ${body.slice(0, 200)}`);
82
85
  }
83
- console.log(` Found ${jobDirs.length} job(s) in ZIP`);
84
- const results = [];
85
- for (let i = 0; i < jobDirs.length; i++) {
86
- const jobDir = jobDirs[i];
87
- const jobSlug = opts.slug ? jobDirs.length > 1 ? `${opts.slug}-${i + 1}` : opts.slug : jobDir.path.split("/").pop();
88
- console.log(`
89
- Job ${i + 1}/${jobDirs.length}: ${jobDir.name} \u2192 ${jobSlug}`);
90
- const result = await uploadJob(jobDir.path, {
91
- slug: jobSlug,
92
- name: jobDir.name
93
- });
94
- results.push({
95
- jobId: result.jobId,
96
- slug: result.slug,
97
- nTrials: result.nTrials,
98
- meanReward: result.meanReward,
99
- viewerUrl: result.viewerUrl
100
- });
86
+ console.log(" ZIP uploaded to storage.");
87
+ console.log(" Enqueuing for server-side extraction...");
88
+ const enqueueResp = await fetch(`${apiUrl}/api/cli/push-zip/enqueue`, {
89
+ method: "POST",
90
+ headers: { Authorization: auth, "Content-Type": "application/json" },
91
+ body: JSON.stringify({
92
+ task_id,
93
+ slug: opts.slug || "",
94
+ name: opts.name || "",
95
+ visibility: "unlisted"
96
+ }),
97
+ signal: AbortSignal.timeout(3e4)
98
+ });
99
+ if (!enqueueResp.ok) {
100
+ const detail = await enqueueResp.text().catch(() => "");
101
+ throw new Error(`Enqueue failed (${enqueueResp.status}): ${detail.slice(0, 300)}`);
101
102
  }
102
- try {
103
- execSync(`rm -rf ${JSON.stringify(tmpDir)}`, { stdio: "pipe" });
104
- } catch {
103
+ console.log(" Waiting for background extraction...");
104
+ let lastStatus = "";
105
+ while (true) {
106
+ await new Promise((r) => setTimeout(r, 5e3));
107
+ const statusResp = await fetch(`${apiUrl}/api/cli/push-zip/${task_id}/status`, {
108
+ headers: { Authorization: auth },
109
+ signal: AbortSignal.timeout(3e4)
110
+ });
111
+ if (!statusResp.ok) {
112
+ console.log(` \u26A0 Status check failed (${statusResp.status}), retrying...`);
113
+ continue;
114
+ }
115
+ const status = await statusResp.json();
116
+ if (status.status !== lastStatus) {
117
+ const progress = status.total_jobs > 0 ? ` [${status.completed_jobs}/${status.total_jobs}]` : "";
118
+ const jobLabel = status.current_job ? ` (${status.current_job})` : "";
119
+ console.log(` ${status.status}${jobLabel}${progress}`);
120
+ lastStatus = status.status;
121
+ }
122
+ if (status.status === "done") {
123
+ const siteUrl = apiUrl.includes("localhost") ? "http://localhost:3000" : apiUrl.replace("api.", "").replace(/\/$/, "");
124
+ return status.jobs.map((j) => ({
125
+ jobId: j.job_id,
126
+ slug: j.slug,
127
+ nTrials: j.n_trials,
128
+ meanReward: j.mean_reward ?? void 0,
129
+ viewerUrl: `${siteUrl}/t/${j.job_id}`
130
+ }));
131
+ }
132
+ if (status.status === "error") {
133
+ throw new Error(`Server-side extraction failed: ${status.error}`);
134
+ }
105
135
  }
106
- return results;
107
136
  }
108
137
  async function convertToWebP(dir) {
109
138
  const { files } = collectFiles(dir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trajectories-sh",
3
- "version": "1.18.4",
3
+ "version": "1.18.6",
4
4
  "description": "CLI for uploading trajectory jobs to trajectories.sh",
5
5
  "type": "module",
6
6
  "bin": {