@vm0/cli 4.12.0 → 4.13.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.
Files changed (2) hide show
  1. package/index.js +433 -398
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -176,14 +176,22 @@ async function setupToken() {
176
176
  );
177
177
  process.exit(1);
178
178
  }
179
+ console.log(chalk.green("\u2713 Authentication token exported successfully!"));
180
+ console.log("");
181
+ console.log("Your token:");
182
+ console.log("");
179
183
  console.log(token);
184
+ console.log("");
185
+ console.log(
186
+ `Use this token by setting: ${chalk.cyan("export VM0_TOKEN=<token>")}`
187
+ );
180
188
  }
181
189
 
182
190
  // src/commands/compose.ts
183
191
  import { Command } from "commander";
184
192
  import chalk2 from "chalk";
185
193
  import { readFile as readFile3 } from "fs/promises";
186
- import { existsSync as existsSync2 } from "fs";
194
+ import { existsSync as existsSync3 } from "fs";
187
195
  import { dirname as dirname2 } from "path";
188
196
  import { parse as parseYaml } from "yaml";
189
197
 
@@ -446,7 +454,7 @@ var ApiClient = class {
446
454
  /**
447
455
  * Generic GET request
448
456
  */
449
- async get(path13) {
457
+ async get(path12) {
450
458
  const baseUrl = await this.getBaseUrl();
451
459
  const token = await getToken();
452
460
  if (!token) {
@@ -459,7 +467,7 @@ var ApiClient = class {
459
467
  if (bypassSecret) {
460
468
  headers["x-vercel-protection-bypass"] = bypassSecret;
461
469
  }
462
- return fetch(`${baseUrl}${path13}`, {
470
+ return fetch(`${baseUrl}${path12}`, {
463
471
  method: "GET",
464
472
  headers
465
473
  });
@@ -467,7 +475,7 @@ var ApiClient = class {
467
475
  /**
468
476
  * Generic POST request
469
477
  */
470
- async post(path13, options) {
478
+ async post(path12, options) {
471
479
  const baseUrl = await this.getBaseUrl();
472
480
  const token = await getToken();
473
481
  if (!token) {
@@ -483,7 +491,7 @@ var ApiClient = class {
483
491
  if (bypassSecret) {
484
492
  headers["x-vercel-protection-bypass"] = bypassSecret;
485
493
  }
486
- return fetch(`${baseUrl}${path13}`, {
494
+ return fetch(`${baseUrl}${path12}`, {
487
495
  method: "POST",
488
496
  headers,
489
497
  body: options?.body
@@ -492,7 +500,7 @@ var ApiClient = class {
492
500
  /**
493
501
  * Generic DELETE request
494
502
  */
495
- async delete(path13) {
503
+ async delete(path12) {
496
504
  const baseUrl = await this.getBaseUrl();
497
505
  const token = await getToken();
498
506
  if (!token) {
@@ -505,7 +513,7 @@ var ApiClient = class {
505
513
  if (bypassSecret) {
506
514
  headers["x-vercel-protection-bypass"] = bypassSecret;
507
515
  }
508
- return fetch(`${baseUrl}${path13}`, {
516
+ return fetch(`${baseUrl}${path12}`, {
509
517
  method: "DELETE",
510
518
  headers
511
519
  });
@@ -709,10 +717,9 @@ function validateAgentCompose(config2) {
709
717
  }
710
718
 
711
719
  // src/lib/system-storage.ts
712
- import * as fs2 from "fs/promises";
713
- import * as path2 from "path";
714
- import * as os2 from "os";
715
- import * as tar from "tar";
720
+ import * as fs4 from "fs/promises";
721
+ import * as path4 from "path";
722
+ import * as os3 from "os";
716
723
 
717
724
  // src/lib/github-skills.ts
718
725
  import * as fs from "fs/promises";
@@ -787,100 +794,335 @@ async function validateSkillDirectory(skillDir) {
787
794
  }
788
795
  }
789
796
 
797
+ // src/lib/direct-upload.ts
798
+ import { createHash } from "crypto";
799
+ import * as fs3 from "fs";
800
+ import * as path3 from "path";
801
+ import * as os2 from "os";
802
+ import * as tar2 from "tar";
803
+
804
+ // src/lib/file-utils.ts
805
+ import * as fs2 from "fs";
806
+ import * as path2 from "path";
807
+ import * as tar from "tar";
808
+ function excludeVm0Filter(filePath) {
809
+ const shouldExclude = filePath === ".vm0" || filePath.startsWith(".vm0/") || filePath.startsWith("./.vm0");
810
+ return !shouldExclude;
811
+ }
812
+ function listTarFiles(tarPath) {
813
+ return new Promise((resolve2, reject) => {
814
+ const files = [];
815
+ tar.list({
816
+ file: tarPath,
817
+ onReadEntry: (entry) => {
818
+ if (entry.type === "File") {
819
+ files.push(entry.path);
820
+ }
821
+ }
822
+ }).then(() => resolve2(files)).catch(reject);
823
+ });
824
+ }
825
+ async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
826
+ const files = [];
827
+ async function walkDir(currentDir, relativePath = "") {
828
+ const entries = await fs2.promises.readdir(currentDir, {
829
+ withFileTypes: true
830
+ });
831
+ for (const entry of entries) {
832
+ const entryRelativePath = relativePath ? path2.join(relativePath, entry.name) : entry.name;
833
+ if (entry.isDirectory()) {
834
+ if (!excludeDirs.includes(entry.name)) {
835
+ await walkDir(path2.join(currentDir, entry.name), entryRelativePath);
836
+ }
837
+ } else {
838
+ files.push(entryRelativePath);
839
+ }
840
+ }
841
+ }
842
+ await walkDir(dir);
843
+ return files;
844
+ }
845
+ async function removeExtraFiles(dir, remoteFiles, excludeDirs = [".vm0"]) {
846
+ const localFiles = await listLocalFiles(dir, excludeDirs);
847
+ let removedCount = 0;
848
+ for (const localFile of localFiles) {
849
+ const normalizedPath = localFile.replace(/\\/g, "/");
850
+ if (!remoteFiles.has(normalizedPath)) {
851
+ const fullPath = path2.join(dir, localFile);
852
+ await fs2.promises.unlink(fullPath);
853
+ removedCount++;
854
+ }
855
+ }
856
+ await removeEmptyDirs(dir, excludeDirs);
857
+ return removedCount;
858
+ }
859
+ async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
860
+ const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
861
+ let isEmpty = true;
862
+ for (const entry of entries) {
863
+ const fullPath = path2.join(dir, entry.name);
864
+ if (entry.isDirectory()) {
865
+ if (excludeDirs.includes(entry.name)) {
866
+ isEmpty = false;
867
+ } else {
868
+ const subDirEmpty = await removeEmptyDirs(fullPath, excludeDirs);
869
+ if (subDirEmpty) {
870
+ await fs2.promises.rmdir(fullPath);
871
+ } else {
872
+ isEmpty = false;
873
+ }
874
+ }
875
+ } else {
876
+ isEmpty = false;
877
+ }
878
+ }
879
+ return isEmpty;
880
+ }
881
+
882
+ // src/lib/direct-upload.ts
883
+ async function hashFileStream(filePath) {
884
+ return new Promise((resolve2, reject) => {
885
+ const hash2 = createHash("sha256");
886
+ const stream = fs3.createReadStream(filePath);
887
+ stream.on("data", (chunk) => hash2.update(chunk));
888
+ stream.on("end", () => resolve2(hash2.digest("hex")));
889
+ stream.on("error", reject);
890
+ });
891
+ }
892
+ async function getAllFiles(dirPath, baseDir = dirPath) {
893
+ const files = [];
894
+ const entries = await fs3.promises.readdir(dirPath, { withFileTypes: true });
895
+ for (const entry of entries) {
896
+ const fullPath = path3.join(dirPath, entry.name);
897
+ const relativePath = path3.relative(baseDir, fullPath);
898
+ if (relativePath.startsWith(".vm0")) {
899
+ continue;
900
+ }
901
+ if (entry.isDirectory()) {
902
+ const subFiles = await getAllFiles(fullPath, baseDir);
903
+ files.push(...subFiles);
904
+ } else {
905
+ files.push(fullPath);
906
+ }
907
+ }
908
+ return files;
909
+ }
910
+ async function collectFileMetadata(cwd, files, onProgress) {
911
+ const fileEntries = [];
912
+ for (let i = 0; i < files.length; i++) {
913
+ const file2 = files[i];
914
+ const relativePath = path3.relative(cwd, file2);
915
+ const [hash2, stats] = await Promise.all([
916
+ hashFileStream(file2),
917
+ fs3.promises.stat(file2)
918
+ ]);
919
+ fileEntries.push({
920
+ path: relativePath,
921
+ hash: hash2,
922
+ size: stats.size
923
+ });
924
+ if (onProgress && (i + 1) % 100 === 0) {
925
+ onProgress(`Hashing files... ${i + 1}/${files.length}`);
926
+ }
927
+ }
928
+ return fileEntries;
929
+ }
930
+ async function createArchive(cwd, files) {
931
+ const tmpDir = fs3.mkdtempSync(path3.join(os2.tmpdir(), "vm0-"));
932
+ const tarPath = path3.join(tmpDir, "archive.tar.gz");
933
+ try {
934
+ const relativePaths = files.map((file2) => path3.relative(cwd, file2));
935
+ if (relativePaths.length > 0) {
936
+ await tar2.create(
937
+ {
938
+ gzip: true,
939
+ file: tarPath,
940
+ cwd
941
+ },
942
+ relativePaths
943
+ );
944
+ } else {
945
+ await tar2.create(
946
+ {
947
+ gzip: true,
948
+ file: tarPath,
949
+ cwd,
950
+ filter: excludeVm0Filter
951
+ },
952
+ ["."]
953
+ );
954
+ }
955
+ const tarBuffer = await fs3.promises.readFile(tarPath);
956
+ return tarBuffer;
957
+ } finally {
958
+ if (fs3.existsSync(tarPath)) {
959
+ await fs3.promises.unlink(tarPath);
960
+ }
961
+ await fs3.promises.rmdir(tmpDir);
962
+ }
963
+ }
964
+ function createManifest(files) {
965
+ const manifest = {
966
+ version: 1,
967
+ files,
968
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
969
+ };
970
+ return Buffer.from(JSON.stringify(manifest, null, 2));
971
+ }
972
+ function sleep(ms) {
973
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
974
+ }
975
+ async function uploadToPresignedUrl(presignedUrl, data, contentType, maxRetries = 3) {
976
+ let lastError = null;
977
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
978
+ try {
979
+ const response = await fetch(presignedUrl, {
980
+ method: "PUT",
981
+ body: data,
982
+ headers: {
983
+ "Content-Type": contentType
984
+ }
985
+ });
986
+ if (response.ok) {
987
+ return;
988
+ }
989
+ if (response.status >= 400 && response.status < 500) {
990
+ const text2 = await response.text();
991
+ throw new Error(`S3 upload failed: ${response.status} - ${text2}`);
992
+ }
993
+ const text = await response.text();
994
+ lastError = new Error(`S3 upload failed: ${response.status} - ${text}`);
995
+ } catch (error43) {
996
+ lastError = error43 instanceof Error ? error43 : new Error("Unknown upload error");
997
+ if (lastError.message.includes("400") || lastError.message.includes("403")) {
998
+ throw lastError;
999
+ }
1000
+ }
1001
+ if (attempt < maxRetries) {
1002
+ const backoffMs = Math.pow(2, attempt - 1) * 1e3;
1003
+ await sleep(backoffMs);
1004
+ }
1005
+ }
1006
+ throw lastError || new Error("S3 upload failed after retries");
1007
+ }
1008
+ async function directUpload(storageName, storageType, cwd, options) {
1009
+ const { onProgress, force } = options || {};
1010
+ onProgress?.("Collecting files...");
1011
+ const files = await getAllFiles(cwd);
1012
+ onProgress?.("Computing file hashes...");
1013
+ const fileEntries = await collectFileMetadata(cwd, files, onProgress);
1014
+ const totalSize = fileEntries.reduce((sum, f) => sum + f.size, 0);
1015
+ onProgress?.("Preparing upload...");
1016
+ const prepareResponse = await apiClient.post("/api/storages/prepare", {
1017
+ body: JSON.stringify({
1018
+ storageName,
1019
+ storageType,
1020
+ files: fileEntries,
1021
+ force
1022
+ })
1023
+ });
1024
+ if (!prepareResponse.ok) {
1025
+ const error43 = await prepareResponse.json();
1026
+ throw new Error(error43.error?.message || "Prepare failed");
1027
+ }
1028
+ const prepareResult = await prepareResponse.json();
1029
+ if (prepareResult.existing) {
1030
+ return {
1031
+ versionId: prepareResult.versionId,
1032
+ size: totalSize,
1033
+ fileCount: fileEntries.length,
1034
+ deduplicated: true,
1035
+ empty: fileEntries.length === 0
1036
+ };
1037
+ }
1038
+ if (files.length > 0) {
1039
+ onProgress?.("Compressing files...");
1040
+ const archiveBuffer = await createArchive(cwd, files);
1041
+ onProgress?.("Uploading archive to S3...");
1042
+ if (!prepareResult.uploads) {
1043
+ throw new Error("No upload URLs received from prepare endpoint");
1044
+ }
1045
+ await uploadToPresignedUrl(
1046
+ prepareResult.uploads.archive.presignedUrl,
1047
+ archiveBuffer,
1048
+ "application/gzip"
1049
+ );
1050
+ }
1051
+ onProgress?.("Uploading manifest...");
1052
+ if (!prepareResult.uploads) {
1053
+ throw new Error("No upload URLs received from prepare endpoint");
1054
+ }
1055
+ const manifestBuffer = createManifest(fileEntries);
1056
+ await uploadToPresignedUrl(
1057
+ prepareResult.uploads.manifest.presignedUrl,
1058
+ manifestBuffer,
1059
+ "application/json"
1060
+ );
1061
+ onProgress?.("Committing...");
1062
+ const commitResponse = await apiClient.post("/api/storages/commit", {
1063
+ body: JSON.stringify({
1064
+ storageName,
1065
+ storageType,
1066
+ versionId: prepareResult.versionId,
1067
+ files: fileEntries
1068
+ })
1069
+ });
1070
+ if (!commitResponse.ok) {
1071
+ const error43 = await commitResponse.json();
1072
+ throw new Error(error43.error?.message || "Commit failed");
1073
+ }
1074
+ const commitResult = await commitResponse.json();
1075
+ return {
1076
+ versionId: commitResult.versionId,
1077
+ size: commitResult.size,
1078
+ fileCount: commitResult.fileCount,
1079
+ deduplicated: commitResult.deduplicated || false,
1080
+ empty: commitResult.fileCount === 0
1081
+ };
1082
+ }
1083
+
790
1084
  // src/lib/system-storage.ts
791
1085
  async function uploadSystemPrompt(agentName, promptFilePath, basePath) {
792
1086
  const storageName = getSystemPromptStorageName(agentName);
793
- const absolutePath = path2.isAbsolute(promptFilePath) ? promptFilePath : path2.join(basePath, promptFilePath);
794
- const content = await fs2.readFile(absolutePath, "utf8");
795
- const tmpDir = await fs2.mkdtemp(path2.join(os2.tmpdir(), "vm0-prompt-"));
796
- const promptDir = path2.join(tmpDir, "prompt");
797
- await fs2.mkdir(promptDir);
798
- await fs2.writeFile(path2.join(promptDir, "CLAUDE.md"), content);
1087
+ const absolutePath = path4.isAbsolute(promptFilePath) ? promptFilePath : path4.join(basePath, promptFilePath);
1088
+ const content = await fs4.readFile(absolutePath, "utf8");
1089
+ const tmpDir = await fs4.mkdtemp(path4.join(os3.tmpdir(), "vm0-prompt-"));
1090
+ const promptDir = path4.join(tmpDir, "prompt");
1091
+ await fs4.mkdir(promptDir);
1092
+ await fs4.writeFile(path4.join(promptDir, "CLAUDE.md"), content);
799
1093
  try {
800
- const tarPath = path2.join(tmpDir, "prompt.tar.gz");
801
- await tar.create(
802
- {
803
- gzip: true,
804
- file: tarPath,
805
- cwd: promptDir
806
- },
807
- ["."]
808
- );
809
- const tarBuffer = await fs2.readFile(tarPath);
810
- const formData = new FormData();
811
- formData.append("name", storageName);
812
- formData.append("type", "volume");
813
- formData.append(
814
- "file",
815
- new Blob([new Uint8Array(tarBuffer)], { type: "application/gzip" }),
816
- "volume.tar.gz"
817
- );
818
- const response = await apiClient.post("/api/storages", {
819
- body: formData
820
- });
821
- if (!response.ok) {
822
- const errorBody = await response.json();
823
- const errorMessage = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message || "Upload failed";
824
- throw new Error(errorMessage);
825
- }
826
- const result = await response.json();
1094
+ const result = await directUpload(storageName, "volume", promptDir);
827
1095
  return {
828
1096
  name: storageName,
829
1097
  versionId: result.versionId,
830
1098
  action: result.deduplicated ? "deduplicated" : "created"
831
1099
  };
832
1100
  } finally {
833
- await fs2.rm(tmpDir, { recursive: true, force: true });
1101
+ await fs4.rm(tmpDir, { recursive: true, force: true });
834
1102
  }
835
1103
  }
836
1104
  async function uploadSystemSkill(skillUrl) {
837
1105
  const parsed = parseGitHubTreeUrl(skillUrl);
838
1106
  const storageName = getSkillStorageName(parsed);
839
- const tmpDir = await fs2.mkdtemp(path2.join(os2.tmpdir(), "vm0-skill-"));
1107
+ const tmpDir = await fs4.mkdtemp(path4.join(os3.tmpdir(), "vm0-skill-"));
840
1108
  try {
841
1109
  const skillDir = await downloadGitHubSkill(parsed, tmpDir);
842
1110
  await validateSkillDirectory(skillDir);
843
- const tarPath = path2.join(tmpDir, "skill.tar.gz");
844
- await tar.create(
845
- {
846
- gzip: true,
847
- file: tarPath,
848
- cwd: skillDir
849
- },
850
- ["."]
851
- );
852
- const tarBuffer = await fs2.readFile(tarPath);
853
- const formData = new FormData();
854
- formData.append("name", storageName);
855
- formData.append("type", "volume");
856
- formData.append(
857
- "file",
858
- new Blob([new Uint8Array(tarBuffer)], { type: "application/gzip" }),
859
- "volume.tar.gz"
860
- );
861
- const response = await apiClient.post("/api/storages", {
862
- body: formData
863
- });
864
- if (!response.ok) {
865
- const errorBody = await response.json();
866
- const errorMessage = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message || "Upload failed";
867
- throw new Error(errorMessage);
868
- }
869
- const result = await response.json();
1111
+ const result = await directUpload(storageName, "volume", skillDir);
870
1112
  return {
871
1113
  name: storageName,
872
1114
  versionId: result.versionId,
873
1115
  action: result.deduplicated ? "deduplicated" : "created"
874
1116
  };
875
1117
  } finally {
876
- await fs2.rm(tmpDir, { recursive: true, force: true });
1118
+ await fs4.rm(tmpDir, { recursive: true, force: true });
877
1119
  }
878
1120
  }
879
1121
 
880
1122
  // src/commands/compose.ts
881
1123
  var composeCommand = new Command().name("compose").description("Create or update agent compose").argument("<config-file>", "Path to config YAML file").action(async (configFile) => {
882
1124
  try {
883
- if (!existsSync2(configFile)) {
1125
+ if (!existsSync3(configFile)) {
884
1126
  console.error(chalk2.red(`\u2717 Config file not found: ${configFile}`));
885
1127
  process.exit(1);
886
1128
  }
@@ -1003,8 +1245,8 @@ var composeCommand = new Command().name("compose").description("Create or update
1003
1245
  // src/commands/run.ts
1004
1246
  import { Command as Command2 } from "commander";
1005
1247
  import chalk4 from "chalk";
1006
- import * as fs3 from "fs";
1007
- import * as path3 from "path";
1248
+ import * as fs5 from "fs";
1249
+ import * as path5 from "path";
1008
1250
  import { config as dotenvConfig } from "dotenv";
1009
1251
 
1010
1252
  // src/lib/event-parser.ts
@@ -2088,15 +2330,15 @@ function mergeDefs(...defs) {
2088
2330
  function cloneDef(schema) {
2089
2331
  return mergeDefs(schema._zod.def);
2090
2332
  }
2091
- function getElementAtPath(obj, path13) {
2092
- if (!path13)
2333
+ function getElementAtPath(obj, path12) {
2334
+ if (!path12)
2093
2335
  return obj;
2094
- return path13.reduce((acc, key) => acc?.[key], obj);
2336
+ return path12.reduce((acc, key) => acc?.[key], obj);
2095
2337
  }
2096
2338
  function promiseAllObject(promisesObj) {
2097
2339
  const keys = Object.keys(promisesObj);
2098
- const promises6 = keys.map((key) => promisesObj[key]);
2099
- return Promise.all(promises6).then((results) => {
2340
+ const promises5 = keys.map((key) => promisesObj[key]);
2341
+ return Promise.all(promises5).then((results) => {
2100
2342
  const resolvedObj = {};
2101
2343
  for (let i = 0; i < keys.length; i++) {
2102
2344
  resolvedObj[keys[i]] = results[i];
@@ -2450,11 +2692,11 @@ function aborted(x, startIndex = 0) {
2450
2692
  }
2451
2693
  return false;
2452
2694
  }
2453
- function prefixIssues(path13, issues) {
2695
+ function prefixIssues(path12, issues) {
2454
2696
  return issues.map((iss) => {
2455
2697
  var _a;
2456
2698
  (_a = iss).path ?? (_a.path = []);
2457
- iss.path.unshift(path13);
2699
+ iss.path.unshift(path12);
2458
2700
  return iss;
2459
2701
  });
2460
2702
  }
@@ -2622,7 +2864,7 @@ function treeifyError(error43, _mapper) {
2622
2864
  return issue2.message;
2623
2865
  };
2624
2866
  const result = { errors: [] };
2625
- const processError = (error44, path13 = []) => {
2867
+ const processError = (error44, path12 = []) => {
2626
2868
  var _a, _b;
2627
2869
  for (const issue2 of error44.issues) {
2628
2870
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -2632,7 +2874,7 @@ function treeifyError(error43, _mapper) {
2632
2874
  } else if (issue2.code === "invalid_element") {
2633
2875
  processError({ issues: issue2.issues }, issue2.path);
2634
2876
  } else {
2635
- const fullpath = [...path13, ...issue2.path];
2877
+ const fullpath = [...path12, ...issue2.path];
2636
2878
  if (fullpath.length === 0) {
2637
2879
  result.errors.push(mapper(issue2));
2638
2880
  continue;
@@ -2664,8 +2906,8 @@ function treeifyError(error43, _mapper) {
2664
2906
  }
2665
2907
  function toDotPath(_path) {
2666
2908
  const segs = [];
2667
- const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2668
- for (const seg of path13) {
2909
+ const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2910
+ for (const seg of path12) {
2669
2911
  if (typeof seg === "number")
2670
2912
  segs.push(`[${seg}]`);
2671
2913
  else if (typeof seg === "symbol")
@@ -14536,9 +14778,9 @@ function loadValues(cliValues, configNames) {
14536
14778
  const result = { ...cliValues };
14537
14779
  const missingNames = configNames.filter((name) => !(name in result));
14538
14780
  if (missingNames.length > 0) {
14539
- const envFilePath = path3.resolve(process.cwd(), ".env");
14781
+ const envFilePath = path5.resolve(process.cwd(), ".env");
14540
14782
  let dotenvValues = {};
14541
- if (fs3.existsSync(envFilePath)) {
14783
+ if (fs5.existsSync(envFilePath)) {
14542
14784
  const dotenvResult = dotenvConfig({ path: envFilePath });
14543
14785
  if (dotenvResult.parsed) {
14544
14786
  dotenvValues = Object.fromEntries(
@@ -15021,13 +15263,13 @@ import { Command as Command6 } from "commander";
15021
15263
  // src/commands/volume/init.ts
15022
15264
  import { Command as Command3 } from "commander";
15023
15265
  import chalk5 from "chalk";
15024
- import path5 from "path";
15266
+ import path7 from "path";
15025
15267
 
15026
15268
  // src/lib/storage-utils.ts
15027
15269
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
15028
- import { existsSync as existsSync4 } from "fs";
15270
+ import { existsSync as existsSync5 } from "fs";
15029
15271
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
15030
- import path4 from "path";
15272
+ import path6 from "path";
15031
15273
  var CONFIG_DIR2 = ".vm0";
15032
15274
  var CONFIG_FILE2 = "storage.yaml";
15033
15275
  function isValidStorageName(name) {
@@ -15038,12 +15280,12 @@ function isValidStorageName(name) {
15038
15280
  return pattern.test(name) && !name.includes("--");
15039
15281
  }
15040
15282
  async function readStorageConfig(basePath = process.cwd()) {
15041
- const configPath = path4.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
15042
- const legacyConfigPath = path4.join(basePath, CONFIG_DIR2, "volume.yaml");
15283
+ const configPath = path6.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
15284
+ const legacyConfigPath = path6.join(basePath, CONFIG_DIR2, "volume.yaml");
15043
15285
  let actualPath = null;
15044
- if (existsSync4(configPath)) {
15286
+ if (existsSync5(configPath)) {
15045
15287
  actualPath = configPath;
15046
- } else if (existsSync4(legacyConfigPath)) {
15288
+ } else if (existsSync5(legacyConfigPath)) {
15047
15289
  actualPath = legacyConfigPath;
15048
15290
  }
15049
15291
  if (!actualPath) {
@@ -15057,9 +15299,9 @@ async function readStorageConfig(basePath = process.cwd()) {
15057
15299
  return config2;
15058
15300
  }
15059
15301
  async function writeStorageConfig(storageName, basePath = process.cwd(), type = "volume") {
15060
- const configDir = path4.join(basePath, CONFIG_DIR2);
15061
- const configPath = path4.join(configDir, CONFIG_FILE2);
15062
- if (!existsSync4(configDir)) {
15302
+ const configDir = path6.join(basePath, CONFIG_DIR2);
15303
+ const configPath = path6.join(configDir, CONFIG_FILE2);
15304
+ if (!existsSync5(configDir)) {
15063
15305
  await mkdir4(configDir, { recursive: true });
15064
15306
  }
15065
15307
  const config2 = {
@@ -15074,14 +15316,14 @@ async function writeStorageConfig(storageName, basePath = process.cwd(), type =
15074
15316
  var initCommand = new Command3().name("init").description("Initialize a volume in the current directory").action(async () => {
15075
15317
  try {
15076
15318
  const cwd = process.cwd();
15077
- const dirName = path5.basename(cwd);
15319
+ const dirName = path7.basename(cwd);
15078
15320
  const existingConfig = await readStorageConfig(cwd);
15079
15321
  if (existingConfig) {
15080
15322
  console.log(
15081
15323
  chalk5.yellow(`Volume already initialized: ${existingConfig.name}`)
15082
15324
  );
15083
15325
  console.log(
15084
- chalk5.gray(`Config file: ${path5.join(cwd, ".vm0", "storage.yaml")}`)
15326
+ chalk5.gray(`Config file: ${path7.join(cwd, ".vm0", "storage.yaml")}`)
15085
15327
  );
15086
15328
  return;
15087
15329
  }
@@ -15102,7 +15344,7 @@ var initCommand = new Command3().name("init").description("Initialize a volume i
15102
15344
  console.log(chalk5.green(`\u2713 Initialized volume: ${volumeName}`));
15103
15345
  console.log(
15104
15346
  chalk5.gray(
15105
- `\u2713 Config saved to ${path5.join(cwd, ".vm0", "storage.yaml")}`
15347
+ `\u2713 Config saved to ${path7.join(cwd, ".vm0", "storage.yaml")}`
15106
15348
  )
15107
15349
  );
15108
15350
  } catch (error43) {
@@ -15117,108 +15359,6 @@ var initCommand = new Command3().name("init").description("Initialize a volume i
15117
15359
  // src/commands/volume/push.ts
15118
15360
  import { Command as Command4 } from "commander";
15119
15361
  import chalk6 from "chalk";
15120
- import path7 from "path";
15121
- import * as fs5 from "fs";
15122
- import * as os3 from "os";
15123
- import * as tar3 from "tar";
15124
-
15125
- // src/lib/file-utils.ts
15126
- import * as fs4 from "fs";
15127
- import * as path6 from "path";
15128
- import * as tar2 from "tar";
15129
- function excludeVm0Filter(filePath) {
15130
- const shouldExclude = filePath === ".vm0" || filePath.startsWith(".vm0/") || filePath.startsWith("./.vm0");
15131
- return !shouldExclude;
15132
- }
15133
- function listTarFiles(tarPath) {
15134
- return new Promise((resolve2, reject) => {
15135
- const files = [];
15136
- tar2.list({
15137
- file: tarPath,
15138
- onReadEntry: (entry) => {
15139
- if (entry.type === "File") {
15140
- files.push(entry.path);
15141
- }
15142
- }
15143
- }).then(() => resolve2(files)).catch(reject);
15144
- });
15145
- }
15146
- async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
15147
- const files = [];
15148
- async function walkDir(currentDir, relativePath = "") {
15149
- const entries = await fs4.promises.readdir(currentDir, {
15150
- withFileTypes: true
15151
- });
15152
- for (const entry of entries) {
15153
- const entryRelativePath = relativePath ? path6.join(relativePath, entry.name) : entry.name;
15154
- if (entry.isDirectory()) {
15155
- if (!excludeDirs.includes(entry.name)) {
15156
- await walkDir(path6.join(currentDir, entry.name), entryRelativePath);
15157
- }
15158
- } else {
15159
- files.push(entryRelativePath);
15160
- }
15161
- }
15162
- }
15163
- await walkDir(dir);
15164
- return files;
15165
- }
15166
- async function removeExtraFiles(dir, remoteFiles, excludeDirs = [".vm0"]) {
15167
- const localFiles = await listLocalFiles(dir, excludeDirs);
15168
- let removedCount = 0;
15169
- for (const localFile of localFiles) {
15170
- const normalizedPath = localFile.replace(/\\/g, "/");
15171
- if (!remoteFiles.has(normalizedPath)) {
15172
- const fullPath = path6.join(dir, localFile);
15173
- await fs4.promises.unlink(fullPath);
15174
- removedCount++;
15175
- }
15176
- }
15177
- await removeEmptyDirs(dir, excludeDirs);
15178
- return removedCount;
15179
- }
15180
- async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
15181
- const entries = await fs4.promises.readdir(dir, { withFileTypes: true });
15182
- let isEmpty = true;
15183
- for (const entry of entries) {
15184
- const fullPath = path6.join(dir, entry.name);
15185
- if (entry.isDirectory()) {
15186
- if (excludeDirs.includes(entry.name)) {
15187
- isEmpty = false;
15188
- } else {
15189
- const subDirEmpty = await removeEmptyDirs(fullPath, excludeDirs);
15190
- if (subDirEmpty) {
15191
- await fs4.promises.rmdir(fullPath);
15192
- } else {
15193
- isEmpty = false;
15194
- }
15195
- }
15196
- } else {
15197
- isEmpty = false;
15198
- }
15199
- }
15200
- return isEmpty;
15201
- }
15202
-
15203
- // src/commands/volume/push.ts
15204
- async function getAllFiles(dirPath, baseDir = dirPath) {
15205
- const files = [];
15206
- const entries = await fs5.promises.readdir(dirPath, { withFileTypes: true });
15207
- for (const entry of entries) {
15208
- const fullPath = path7.join(dirPath, entry.name);
15209
- const relativePath = path7.relative(baseDir, fullPath);
15210
- if (relativePath.startsWith(".vm0")) {
15211
- continue;
15212
- }
15213
- if (entry.isDirectory()) {
15214
- const subFiles = await getAllFiles(fullPath, baseDir);
15215
- files.push(...subFiles);
15216
- } else {
15217
- files.push(fullPath);
15218
- }
15219
- }
15220
- return files;
15221
- }
15222
15362
  function formatBytes(bytes) {
15223
15363
  if (bytes === 0) return "0 B";
15224
15364
  const k = 1024;
@@ -15239,73 +15379,16 @@ var pushCommand = new Command4().name("push").description("Push local files to c
15239
15379
  process.exit(1);
15240
15380
  }
15241
15381
  console.log(chalk6.cyan(`Pushing volume: ${config2.name}`));
15242
- console.log(chalk6.gray("Collecting files..."));
15243
- const files = await getAllFiles(cwd);
15244
- let totalSize = 0;
15245
- for (const file2 of files) {
15246
- const stats = await fs5.promises.stat(file2);
15247
- totalSize += stats.size;
15248
- }
15249
- if (files.length === 0) {
15250
- console.log(chalk6.gray("No files found (empty volume)"));
15251
- } else {
15252
- console.log(
15253
- chalk6.gray(`Found ${files.length} files (${formatBytes(totalSize)})`)
15254
- );
15255
- }
15256
- console.log(chalk6.gray("Compressing files..."));
15257
- const tmpDir = fs5.mkdtempSync(path7.join(os3.tmpdir(), "vm0-"));
15258
- const tarPath = path7.join(tmpDir, "volume.tar.gz");
15259
- const relativePaths = files.map((file2) => path7.relative(cwd, file2));
15260
- if (relativePaths.length > 0) {
15261
- await tar3.create(
15262
- {
15263
- gzip: true,
15264
- file: tarPath,
15265
- cwd
15266
- },
15267
- relativePaths
15268
- );
15269
- } else {
15270
- await tar3.create(
15271
- {
15272
- gzip: true,
15273
- file: tarPath,
15274
- cwd,
15275
- filter: excludeVm0Filter
15276
- },
15277
- ["."]
15278
- );
15279
- }
15280
- const tarBuffer = await fs5.promises.readFile(tarPath);
15281
- await fs5.promises.unlink(tarPath);
15282
- await fs5.promises.rmdir(tmpDir);
15283
- console.log(
15284
- chalk6.green(`\u2713 Compressed to ${formatBytes(tarBuffer.length)}`)
15285
- );
15286
- console.log(chalk6.gray("Uploading..."));
15287
- const formData = new FormData();
15288
- formData.append("name", config2.name);
15289
- formData.append("type", "volume");
15290
- if (options.force) {
15291
- formData.append("force", "true");
15292
- }
15293
- formData.append(
15294
- "file",
15295
- new Blob([tarBuffer], { type: "application/gzip" }),
15296
- "volume.tar.gz"
15297
- );
15298
- const response = await apiClient.post("/api/storages", {
15299
- body: formData
15382
+ const result = await directUpload(config2.name, "volume", cwd, {
15383
+ onProgress: (message) => {
15384
+ console.log(chalk6.gray(message));
15385
+ },
15386
+ force: options.force
15300
15387
  });
15301
- if (!response.ok) {
15302
- const error43 = await response.json();
15303
- const message = error43.cause ? `${error43.error} (cause: ${error43.cause})` : error43.error || "Upload failed";
15304
- throw new Error(message);
15305
- }
15306
- const result = await response.json();
15307
15388
  const shortVersion = result.versionId.slice(0, 8);
15308
- if (result.deduplicated) {
15389
+ if (result.empty) {
15390
+ console.log(chalk6.yellow("No files found (empty volume)"));
15391
+ } else if (result.deduplicated) {
15309
15392
  console.log(chalk6.green("\u2713 Content unchanged (deduplicated)"));
15310
15393
  } else {
15311
15394
  console.log(chalk6.green("\u2713 Upload complete"));
@@ -15328,7 +15411,7 @@ import chalk8 from "chalk";
15328
15411
  import path8 from "path";
15329
15412
  import * as fs6 from "fs";
15330
15413
  import * as os4 from "os";
15331
- import * as tar4 from "tar";
15414
+ import * as tar3 from "tar";
15332
15415
 
15333
15416
  // src/lib/pull-utils.ts
15334
15417
  import chalk7 from "chalk";
@@ -15366,8 +15449,8 @@ var pullCommand = new Command5().name("pull").description("Pull cloud files to l
15366
15449
  } else {
15367
15450
  console.log(chalk8.cyan(`Pulling volume: ${config2.name}`));
15368
15451
  }
15369
- console.log(chalk8.gray("Downloading..."));
15370
- let url2 = `/api/storages?name=${encodeURIComponent(config2.name)}&type=volume`;
15452
+ console.log(chalk8.gray("Getting download URL..."));
15453
+ let url2 = `/api/storages/download?name=${encodeURIComponent(config2.name)}&type=volume`;
15371
15454
  if (versionId) {
15372
15455
  url2 += `&version=${encodeURIComponent(versionId)}`;
15373
15456
  }
@@ -15389,11 +15472,20 @@ var pullCommand = new Command5().name("pull").description("Pull cloud files to l
15389
15472
  }
15390
15473
  process.exit(1);
15391
15474
  }
15392
- if (response.status === 204) {
15475
+ const downloadInfo = await response.json();
15476
+ if (downloadInfo.empty) {
15393
15477
  await handleEmptyStorageResponse(cwd);
15394
15478
  return;
15395
15479
  }
15396
- const arrayBuffer = await response.arrayBuffer();
15480
+ if (!downloadInfo.url) {
15481
+ throw new Error("No download URL returned");
15482
+ }
15483
+ console.log(chalk8.gray("Downloading from S3..."));
15484
+ const s3Response = await fetch(downloadInfo.url);
15485
+ if (!s3Response.ok) {
15486
+ throw new Error(`S3 download failed: ${s3Response.status}`);
15487
+ }
15488
+ const arrayBuffer = await s3Response.arrayBuffer();
15397
15489
  const tarBuffer = Buffer.from(arrayBuffer);
15398
15490
  console.log(chalk8.green(`\u2713 Downloaded ${formatBytes2(tarBuffer.length)}`));
15399
15491
  const tmpDir = fs6.mkdtempSync(path8.join(os4.tmpdir(), "vm0-"));
@@ -15411,7 +15503,7 @@ var pullCommand = new Command5().name("pull").description("Pull cloud files to l
15411
15503
  );
15412
15504
  }
15413
15505
  console.log(chalk8.gray("Extracting files..."));
15414
- await tar4.extract({
15506
+ await tar3.extract({
15415
15507
  file: tarPath,
15416
15508
  cwd,
15417
15509
  gzip: true
@@ -15499,28 +15591,6 @@ var initCommand2 = new Command7().name("init").description("Initialize an artifa
15499
15591
  // src/commands/artifact/push.ts
15500
15592
  import { Command as Command8 } from "commander";
15501
15593
  import chalk10 from "chalk";
15502
- import path10 from "path";
15503
- import * as fs7 from "fs";
15504
- import * as os5 from "os";
15505
- import * as tar5 from "tar";
15506
- async function getAllFiles2(dirPath, baseDir = dirPath) {
15507
- const files = [];
15508
- const entries = await fs7.promises.readdir(dirPath, { withFileTypes: true });
15509
- for (const entry of entries) {
15510
- const fullPath = path10.join(dirPath, entry.name);
15511
- const relativePath = path10.relative(baseDir, fullPath);
15512
- if (relativePath.startsWith(".vm0")) {
15513
- continue;
15514
- }
15515
- if (entry.isDirectory()) {
15516
- const subFiles = await getAllFiles2(fullPath, baseDir);
15517
- files.push(...subFiles);
15518
- } else {
15519
- files.push(fullPath);
15520
- }
15521
- }
15522
- return files;
15523
- }
15524
15594
  function formatBytes3(bytes) {
15525
15595
  if (bytes === 0) return "0 B";
15526
15596
  const k = 1024;
@@ -15550,72 +15620,16 @@ var pushCommand2 = new Command8().name("push").description("Push local files to
15550
15620
  process.exit(1);
15551
15621
  }
15552
15622
  console.log(chalk10.cyan(`Pushing artifact: ${config2.name}`));
15553
- console.log(chalk10.gray("Collecting files..."));
15554
- const files = await getAllFiles2(cwd);
15555
- let totalSize = 0;
15556
- for (const file2 of files) {
15557
- const stats = await fs7.promises.stat(file2);
15558
- totalSize += stats.size;
15559
- }
15560
- if (files.length === 0) {
15561
- console.log(chalk10.gray("No files found (empty artifact)"));
15562
- } else {
15563
- console.log(
15564
- chalk10.gray(`Found ${files.length} files (${formatBytes3(totalSize)})`)
15565
- );
15566
- }
15567
- console.log(chalk10.gray("Compressing files..."));
15568
- const tmpDir = fs7.mkdtempSync(path10.join(os5.tmpdir(), "vm0-"));
15569
- const tarPath = path10.join(tmpDir, "artifact.tar.gz");
15570
- const relativePaths = files.map((file2) => path10.relative(cwd, file2));
15571
- if (relativePaths.length > 0) {
15572
- await tar5.create(
15573
- {
15574
- gzip: true,
15575
- file: tarPath,
15576
- cwd
15577
- },
15578
- relativePaths
15579
- );
15580
- } else {
15581
- await tar5.create(
15582
- {
15583
- gzip: true,
15584
- file: tarPath,
15585
- cwd,
15586
- filter: excludeVm0Filter
15587
- },
15588
- ["."]
15589
- );
15590
- }
15591
- const tarBuffer = await fs7.promises.readFile(tarPath);
15592
- await fs7.promises.unlink(tarPath);
15593
- await fs7.promises.rmdir(tmpDir);
15594
- console.log(
15595
- chalk10.green(`\u2713 Compressed to ${formatBytes3(tarBuffer.length)}`)
15596
- );
15597
- console.log(chalk10.gray("Uploading..."));
15598
- const formData = new FormData();
15599
- formData.append("name", config2.name);
15600
- formData.append("type", "artifact");
15601
- if (options.force) {
15602
- formData.append("force", "true");
15603
- }
15604
- formData.append(
15605
- "file",
15606
- new Blob([tarBuffer], { type: "application/gzip" }),
15607
- "artifact.tar.gz"
15608
- );
15609
- const response = await apiClient.post("/api/storages", {
15610
- body: formData
15623
+ const result = await directUpload(config2.name, "artifact", cwd, {
15624
+ onProgress: (message) => {
15625
+ console.log(chalk10.gray(message));
15626
+ },
15627
+ force: options.force
15611
15628
  });
15612
- if (!response.ok) {
15613
- const error43 = await response.json();
15614
- throw new Error(error43.error || "Upload failed");
15615
- }
15616
- const result = await response.json();
15617
15629
  const shortVersion = result.versionId.slice(0, 8);
15618
- if (result.deduplicated) {
15630
+ if (result.empty) {
15631
+ console.log(chalk10.yellow("No files found (empty artifact)"));
15632
+ } else if (result.deduplicated) {
15619
15633
  console.log(chalk10.green("\u2713 Content unchanged (deduplicated)"));
15620
15634
  } else {
15621
15635
  console.log(chalk10.green("\u2713 Upload complete"));
@@ -15635,10 +15649,10 @@ var pushCommand2 = new Command8().name("push").description("Push local files to
15635
15649
  // src/commands/artifact/pull.ts
15636
15650
  import { Command as Command9 } from "commander";
15637
15651
  import chalk11 from "chalk";
15638
- import path11 from "path";
15639
- import * as fs8 from "fs";
15640
- import * as os6 from "os";
15641
- import * as tar6 from "tar";
15652
+ import path10 from "path";
15653
+ import * as fs7 from "fs";
15654
+ import * as os5 from "os";
15655
+ import * as tar4 from "tar";
15642
15656
  function formatBytes4(bytes) {
15643
15657
  if (bytes === 0) return "0 B";
15644
15658
  const k = 1024;
@@ -15673,8 +15687,8 @@ var pullCommand2 = new Command9().name("pull").description("Pull cloud artifact
15673
15687
  } else {
15674
15688
  console.log(chalk11.cyan(`Pulling artifact: ${config2.name}`));
15675
15689
  }
15676
- console.log(chalk11.gray("Downloading..."));
15677
- let url2 = `/api/storages?name=${encodeURIComponent(config2.name)}&type=artifact`;
15690
+ console.log(chalk11.gray("Getting download URL..."));
15691
+ let url2 = `/api/storages/download?name=${encodeURIComponent(config2.name)}&type=artifact`;
15678
15692
  if (versionId) {
15679
15693
  url2 += `&version=${encodeURIComponent(versionId)}`;
15680
15694
  }
@@ -15696,16 +15710,25 @@ var pullCommand2 = new Command9().name("pull").description("Pull cloud artifact
15696
15710
  }
15697
15711
  process.exit(1);
15698
15712
  }
15699
- if (response.status === 204) {
15713
+ const downloadInfo = await response.json();
15714
+ if (downloadInfo.empty) {
15700
15715
  await handleEmptyStorageResponse(cwd);
15701
15716
  return;
15702
15717
  }
15703
- const arrayBuffer = await response.arrayBuffer();
15718
+ if (!downloadInfo.url) {
15719
+ throw new Error("No download URL returned");
15720
+ }
15721
+ console.log(chalk11.gray("Downloading from S3..."));
15722
+ const s3Response = await fetch(downloadInfo.url);
15723
+ if (!s3Response.ok) {
15724
+ throw new Error(`S3 download failed: ${s3Response.status}`);
15725
+ }
15726
+ const arrayBuffer = await s3Response.arrayBuffer();
15704
15727
  const tarBuffer = Buffer.from(arrayBuffer);
15705
15728
  console.log(chalk11.green(`\u2713 Downloaded ${formatBytes4(tarBuffer.length)}`));
15706
- const tmpDir = fs8.mkdtempSync(path11.join(os6.tmpdir(), "vm0-"));
15707
- const tarPath = path11.join(tmpDir, "artifact.tar.gz");
15708
- await fs8.promises.writeFile(tarPath, tarBuffer);
15729
+ const tmpDir = fs7.mkdtempSync(path10.join(os5.tmpdir(), "vm0-"));
15730
+ const tarPath = path10.join(tmpDir, "artifact.tar.gz");
15731
+ await fs7.promises.writeFile(tarPath, tarBuffer);
15709
15732
  console.log(chalk11.gray("Syncing local files..."));
15710
15733
  const remoteFiles = await listTarFiles(tarPath);
15711
15734
  const remoteFilesSet = new Set(
@@ -15718,13 +15741,13 @@ var pullCommand2 = new Command9().name("pull").description("Pull cloud artifact
15718
15741
  );
15719
15742
  }
15720
15743
  console.log(chalk11.gray("Extracting files..."));
15721
- await tar6.extract({
15744
+ await tar4.extract({
15722
15745
  file: tarPath,
15723
15746
  cwd,
15724
15747
  gzip: true
15725
15748
  });
15726
- await fs8.promises.unlink(tarPath);
15727
- await fs8.promises.rmdir(tmpDir);
15749
+ await fs7.promises.unlink(tarPath);
15750
+ await fs7.promises.rmdir(tmpDir);
15728
15751
  console.log(chalk11.green(`\u2713 Extracted ${remoteFiles.length} files`));
15729
15752
  } catch (error43) {
15730
15753
  console.error(chalk11.red("\u2717 Pull failed"));
@@ -15742,8 +15765,8 @@ var artifactCommand = new Command10().name("artifact").description("Manage cloud
15742
15765
  import { Command as Command11 } from "commander";
15743
15766
  import chalk13 from "chalk";
15744
15767
  import { readFile as readFile5, mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
15745
- import { existsSync as existsSync5, readFileSync } from "fs";
15746
- import path12 from "path";
15768
+ import { existsSync as existsSync6, readFileSync } from "fs";
15769
+ import path11 from "path";
15747
15770
  import { spawn as spawn2 } from "child_process";
15748
15771
  import { parse as parseYaml3 } from "yaml";
15749
15772
  import { config as dotenvConfig2 } from "dotenv";
@@ -15755,6 +15778,13 @@ import chalk12 from "chalk";
15755
15778
  var PACKAGE_NAME = "@vm0/cli";
15756
15779
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;
15757
15780
  var TIMEOUT_MS = 5e3;
15781
+ function detectPackageManager() {
15782
+ const execPath = process.argv[1] ?? "";
15783
+ if (execPath.includes("pnpm")) {
15784
+ return "pnpm";
15785
+ }
15786
+ return "npm";
15787
+ }
15758
15788
  function escapeForShell(str) {
15759
15789
  return `"${str.replace(/"/g, '\\"')}"`;
15760
15790
  }
@@ -15789,12 +15819,14 @@ function getLatestVersion() {
15789
15819
  });
15790
15820
  });
15791
15821
  }
15792
- function performUpgrade() {
15822
+ function performUpgrade(packageManager) {
15793
15823
  return new Promise((resolve2) => {
15794
- const npm = process.platform === "win32" ? "npm.cmd" : "npm";
15795
- const child = spawn(npm, ["install", "-g", `${PACKAGE_NAME}@latest`], {
15824
+ const isWindows = process.platform === "win32";
15825
+ const command = isWindows ? `${packageManager}.cmd` : packageManager;
15826
+ const args = packageManager === "pnpm" ? ["add", "-g", `${PACKAGE_NAME}@latest`] : ["install", "-g", `${PACKAGE_NAME}@latest`];
15827
+ const child = spawn(command, args, {
15796
15828
  stdio: "inherit",
15797
- shell: process.platform === "win32"
15829
+ shell: isWindows
15798
15830
  });
15799
15831
  child.on("close", (code) => {
15800
15832
  resolve2(code === 0);
@@ -15826,8 +15858,9 @@ async function checkAndUpgrade(currentVersion, prompt) {
15826
15858
  )
15827
15859
  );
15828
15860
  console.log();
15829
- console.log("Upgrading...");
15830
- const success2 = await performUpgrade();
15861
+ const packageManager = detectPackageManager();
15862
+ console.log(`Upgrading via ${packageManager}...`);
15863
+ const success2 = await performUpgrade(packageManager);
15831
15864
  if (success2) {
15832
15865
  console.log(chalk12.green(`Upgraded to ${latestVersion}`));
15833
15866
  console.log();
@@ -15838,6 +15871,8 @@ async function checkAndUpgrade(currentVersion, prompt) {
15838
15871
  console.log();
15839
15872
  console.log(chalk12.red("Upgrade failed. Please run manually:"));
15840
15873
  console.log(chalk12.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
15874
+ console.log(chalk12.gray(" # or"));
15875
+ console.log(chalk12.cyan(` pnpm add -g ${PACKAGE_NAME}@latest`));
15841
15876
  console.log();
15842
15877
  console.log("Then re-run:");
15843
15878
  console.log(chalk12.cyan(` ${buildRerunCommand(prompt)}`));
@@ -15931,7 +15966,7 @@ function extractRequiredVarNames(config2) {
15931
15966
  }
15932
15967
  function checkMissingVariables(varNames, envFilePath) {
15933
15968
  let dotenvValues = {};
15934
- if (existsSync5(envFilePath)) {
15969
+ if (existsSync6(envFilePath)) {
15935
15970
  const result = dotenvConfig2({ path: envFilePath });
15936
15971
  if (result.parsed) {
15937
15972
  dotenvValues = result.parsed;
@@ -15949,7 +15984,7 @@ function checkMissingVariables(varNames, envFilePath) {
15949
15984
  }
15950
15985
  async function generateEnvPlaceholders(missingVars, envFilePath) {
15951
15986
  const placeholders = missingVars.map((name) => `${name}=`).join("\n");
15952
- if (existsSync5(envFilePath)) {
15987
+ if (existsSync6(envFilePath)) {
15953
15988
  const existingContent = readFileSync(envFilePath, "utf8");
15954
15989
  const needsNewline = existingContent.length > 0 && !existingContent.endsWith("\n");
15955
15990
  const prefix = needsNewline ? "\n" : "";
@@ -15961,13 +15996,13 @@ async function generateEnvPlaceholders(missingVars, envFilePath) {
15961
15996
  }
15962
15997
  }
15963
15998
  var cookCommand = new Command11().name("cook").description("One-click agent preparation and execution from vm0.yaml").argument("[prompt]", "Prompt for the agent").action(async (prompt) => {
15964
- const shouldExit = await checkAndUpgrade("4.12.0", prompt);
15999
+ const shouldExit = await checkAndUpgrade("4.13.1", prompt);
15965
16000
  if (shouldExit) {
15966
16001
  process.exit(0);
15967
16002
  }
15968
16003
  const cwd = process.cwd();
15969
16004
  console.log(chalk13.blue(`Reading config: ${CONFIG_FILE3}`));
15970
- if (!existsSync5(CONFIG_FILE3)) {
16005
+ if (!existsSync6(CONFIG_FILE3)) {
15971
16006
  console.error(chalk13.red(`\u2717 Config file not found: ${CONFIG_FILE3}`));
15972
16007
  process.exit(1);
15973
16008
  }
@@ -15995,7 +16030,7 @@ var cookCommand = new Command11().name("cook").description("One-click agent prep
15995
16030
  );
15996
16031
  const requiredVarNames = extractRequiredVarNames(config2);
15997
16032
  if (requiredVarNames.length > 0) {
15998
- const envFilePath = path12.join(cwd, ".env");
16033
+ const envFilePath = path11.join(cwd, ".env");
15999
16034
  const missingVars = checkMissingVariables(requiredVarNames, envFilePath);
16000
16035
  if (missingVars.length > 0) {
16001
16036
  await generateEnvPlaceholders(missingVars, envFilePath);
@@ -16015,9 +16050,9 @@ var cookCommand = new Command11().name("cook").description("One-click agent prep
16015
16050
  console.log();
16016
16051
  console.log(chalk13.blue("Processing volumes..."));
16017
16052
  for (const volumeConfig of Object.values(config2.volumes)) {
16018
- const volumeDir = path12.join(cwd, volumeConfig.name);
16053
+ const volumeDir = path11.join(cwd, volumeConfig.name);
16019
16054
  console.log(chalk13.gray(` ${volumeConfig.name}/`));
16020
- if (!existsSync5(volumeDir)) {
16055
+ if (!existsSync6(volumeDir)) {
16021
16056
  console.error(
16022
16057
  chalk13.red(
16023
16058
  ` \u2717 Directory not found. Create the directory and add files first.`
@@ -16050,10 +16085,10 @@ var cookCommand = new Command11().name("cook").description("One-click agent prep
16050
16085
  }
16051
16086
  console.log();
16052
16087
  console.log(chalk13.blue("Processing artifact..."));
16053
- const artifactDir = path12.join(cwd, ARTIFACT_DIR);
16088
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
16054
16089
  console.log(chalk13.gray(` ${ARTIFACT_DIR}/`));
16055
16090
  try {
16056
- if (!existsSync5(artifactDir)) {
16091
+ if (!existsSync6(artifactDir)) {
16057
16092
  await mkdir5(artifactDir, { recursive: true });
16058
16093
  console.log(chalk13.green(` \u2713 Created directory`));
16059
16094
  }
@@ -16147,12 +16182,12 @@ import { Command as Command15 } from "commander";
16147
16182
  import { Command as Command12 } from "commander";
16148
16183
  import chalk14 from "chalk";
16149
16184
  import { readFile as readFile6 } from "fs/promises";
16150
- import { existsSync as existsSync6 } from "fs";
16151
- var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
16185
+ import { existsSync as existsSync7 } from "fs";
16186
+ var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
16152
16187
  var buildCommand = new Command12().name("build").description("Build a custom image from a Dockerfile").requiredOption("-f, --file <path>", "Path to Dockerfile").requiredOption("-n, --name <name>", "Name for the image").option("--delete-existing", "Delete existing image before building").action(
16153
16188
  async (options) => {
16154
16189
  const { file: file2, name, deleteExisting } = options;
16155
- if (!existsSync6(file2)) {
16190
+ if (!existsSync7(file2)) {
16156
16191
  console.error(chalk14.red(`\u2717 Dockerfile not found: ${file2}`));
16157
16192
  process.exit(1);
16158
16193
  }
@@ -16205,7 +16240,7 @@ var buildCommand = new Command12().name("build").description("Build a custom ima
16205
16240
  logsOffset = statusData.logsOffset;
16206
16241
  status = statusData.status;
16207
16242
  if (status === "building") {
16208
- await sleep(2e3);
16243
+ await sleep2(2e3);
16209
16244
  }
16210
16245
  }
16211
16246
  console.log();
@@ -16598,7 +16633,7 @@ function handleError(error43, runId) {
16598
16633
 
16599
16634
  // src/index.ts
16600
16635
  var program = new Command17();
16601
- program.name("vm0").description("VM0 CLI - A modern build tool").version("4.12.0");
16636
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("4.13.1");
16602
16637
  program.command("info").description("Display environment information").action(async () => {
16603
16638
  console.log(chalk18.cyan("System Information:"));
16604
16639
  console.log(`Node Version: ${process.version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "4.12.0",
3
+ "version": "4.13.1",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",