@vm0/cli 1.2.0 → 1.3.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 +319 -17
  2. package/package.json +2 -1
package/index.js CHANGED
@@ -736,15 +736,15 @@ function mergeDefs(...defs) {
736
736
  function cloneDef(schema) {
737
737
  return mergeDefs(schema._zod.def);
738
738
  }
739
- function getElementAtPath(obj, path) {
740
- if (!path)
739
+ function getElementAtPath(obj, path5) {
740
+ if (!path5)
741
741
  return obj;
742
- return path.reduce((acc, key) => acc?.[key], obj);
742
+ return path5.reduce((acc, key) => acc?.[key], obj);
743
743
  }
744
744
  function promiseAllObject(promisesObj) {
745
745
  const keys = Object.keys(promisesObj);
746
- const promises = keys.map((key) => promisesObj[key]);
747
- return Promise.all(promises).then((results) => {
746
+ const promises3 = keys.map((key) => promisesObj[key]);
747
+ return Promise.all(promises3).then((results) => {
748
748
  const resolvedObj = {};
749
749
  for (let i = 0; i < keys.length; i++) {
750
750
  resolvedObj[keys[i]] = results[i];
@@ -1098,11 +1098,11 @@ function aborted(x, startIndex = 0) {
1098
1098
  }
1099
1099
  return false;
1100
1100
  }
1101
- function prefixIssues(path, issues) {
1101
+ function prefixIssues(path5, issues) {
1102
1102
  return issues.map((iss) => {
1103
1103
  var _a;
1104
1104
  (_a = iss).path ?? (_a.path = []);
1105
- iss.path.unshift(path);
1105
+ iss.path.unshift(path5);
1106
1106
  return iss;
1107
1107
  });
1108
1108
  }
@@ -1270,7 +1270,7 @@ function treeifyError(error43, _mapper) {
1270
1270
  return issue2.message;
1271
1271
  };
1272
1272
  const result = { errors: [] };
1273
- const processError = (error44, path = []) => {
1273
+ const processError = (error44, path5 = []) => {
1274
1274
  var _a, _b;
1275
1275
  for (const issue2 of error44.issues) {
1276
1276
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -1280,7 +1280,7 @@ function treeifyError(error43, _mapper) {
1280
1280
  } else if (issue2.code === "invalid_element") {
1281
1281
  processError({ issues: issue2.issues }, issue2.path);
1282
1282
  } else {
1283
- const fullpath = [...path, ...issue2.path];
1283
+ const fullpath = [...path5, ...issue2.path];
1284
1284
  if (fullpath.length === 0) {
1285
1285
  result.errors.push(mapper(issue2));
1286
1286
  continue;
@@ -1312,8 +1312,8 @@ function treeifyError(error43, _mapper) {
1312
1312
  }
1313
1313
  function toDotPath(_path) {
1314
1314
  const segs = [];
1315
- const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1316
- for (const seg of path) {
1315
+ const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1316
+ for (const seg of path5) {
1317
1317
  if (typeof seg === "number")
1318
1318
  segs.push(`[${seg}]`);
1319
1319
  else if (typeof seg === "symbol")
@@ -12147,8 +12147,8 @@ var helloContract = c.router({
12147
12147
  var FOO = "hello";
12148
12148
 
12149
12149
  // src/index.ts
12150
- import { Command as Command3 } from "commander";
12151
- import chalk5 from "chalk";
12150
+ import { Command as Command7 } from "commander";
12151
+ import chalk8 from "chalk";
12152
12152
 
12153
12153
  // src/lib/auth.ts
12154
12154
  import chalk from "chalk";
@@ -12413,6 +12413,49 @@ var ApiClient = class {
12413
12413
  }
12414
12414
  return await response.json();
12415
12415
  }
12416
+ /**
12417
+ * Generic GET request
12418
+ */
12419
+ async get(path5) {
12420
+ const baseUrl = await this.getBaseUrl();
12421
+ const token = await getToken();
12422
+ if (!token) {
12423
+ throw new Error("Not authenticated. Run: vm0 auth login");
12424
+ }
12425
+ const headers = {
12426
+ Authorization: `Bearer ${token}`
12427
+ };
12428
+ const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
12429
+ if (bypassSecret) {
12430
+ headers["x-vercel-protection-bypass"] = bypassSecret;
12431
+ }
12432
+ return fetch(`${baseUrl}${path5}`, {
12433
+ method: "GET",
12434
+ headers
12435
+ });
12436
+ }
12437
+ /**
12438
+ * Generic POST request
12439
+ */
12440
+ async post(path5, options) {
12441
+ const baseUrl = await this.getBaseUrl();
12442
+ const token = await getToken();
12443
+ if (!token) {
12444
+ throw new Error("Not authenticated. Run: vm0 auth login");
12445
+ }
12446
+ const headers = {
12447
+ Authorization: `Bearer ${token}`
12448
+ };
12449
+ const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
12450
+ if (bypassSecret) {
12451
+ headers["x-vercel-protection-bypass"] = bypassSecret;
12452
+ }
12453
+ return fetch(`${baseUrl}${path5}`, {
12454
+ method: "POST",
12455
+ headers,
12456
+ body: options?.body
12457
+ });
12458
+ }
12416
12459
  };
12417
12460
  var apiClient = new ApiClient();
12418
12461
 
@@ -12825,6 +12868,9 @@ var EventRenderer = class {
12825
12868
  }
12826
12869
  static renderVm0Start(event) {
12827
12870
  console.log(chalk3.cyan("[vm0_start]") + " Run starting");
12871
+ if (event.data.runId) {
12872
+ console.log(` Run ID: ${chalk3.gray(String(event.data.runId))}`);
12873
+ }
12828
12874
  const prompt = String(event.data.prompt || "");
12829
12875
  const displayPrompt = prompt.length > 100 ? prompt.substring(0, 100) + "..." : prompt;
12830
12876
  console.log(` Prompt: ${chalk3.gray(displayPrompt)}`);
@@ -13027,15 +13073,270 @@ runCmd.command("resume").description("Resume an agent run from a checkpoint").ar
13027
13073
  });
13028
13074
  var runCommand = runCmd;
13029
13075
 
13076
+ // src/commands/volume/index.ts
13077
+ import { Command as Command6 } from "commander";
13078
+
13079
+ // src/commands/volume/init.ts
13080
+ import { Command as Command3 } from "commander";
13081
+ import chalk5 from "chalk";
13082
+ import path2 from "path";
13083
+
13084
+ // src/lib/volume-utils.ts
13085
+ import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13086
+ import { existsSync as existsSync3 } from "fs";
13087
+ import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
13088
+ import path from "path";
13089
+ var CONFIG_DIR2 = ".vm0";
13090
+ var CONFIG_FILE2 = "volume.yaml";
13091
+ function isValidVolumeName(name) {
13092
+ if (name.length < 3 || name.length > 64) {
13093
+ return false;
13094
+ }
13095
+ const pattern = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
13096
+ return pattern.test(name) && !name.includes("--");
13097
+ }
13098
+ async function readVolumeConfig(basePath = process.cwd()) {
13099
+ const configPath = path.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
13100
+ if (!existsSync3(configPath)) {
13101
+ return null;
13102
+ }
13103
+ const content = await readFile3(configPath, "utf8");
13104
+ const config2 = parseYaml2(content);
13105
+ return config2;
13106
+ }
13107
+ async function writeVolumeConfig(volumeName, basePath = process.cwd()) {
13108
+ const configDir = path.join(basePath, CONFIG_DIR2);
13109
+ const configPath = path.join(configDir, CONFIG_FILE2);
13110
+ if (!existsSync3(configDir)) {
13111
+ await mkdir2(configDir, { recursive: true });
13112
+ }
13113
+ const config2 = {
13114
+ name: volumeName
13115
+ };
13116
+ const yamlContent = stringifyYaml(config2);
13117
+ await writeFile2(configPath, yamlContent, "utf8");
13118
+ }
13119
+
13120
+ // src/commands/volume/init.ts
13121
+ var initCommand = new Command3().name("init").description("Initialize a volume in the current directory").action(async () => {
13122
+ try {
13123
+ const cwd = process.cwd();
13124
+ const dirName = path2.basename(cwd);
13125
+ const existingConfig = await readVolumeConfig(cwd);
13126
+ if (existingConfig) {
13127
+ console.log(
13128
+ chalk5.yellow(`Volume already initialized: ${existingConfig.name}`)
13129
+ );
13130
+ console.log(
13131
+ chalk5.gray(`Config file: ${path2.join(cwd, ".vm0", "volume.yaml")}`)
13132
+ );
13133
+ return;
13134
+ }
13135
+ const volumeName = dirName;
13136
+ if (!isValidVolumeName(volumeName)) {
13137
+ console.error(chalk5.red(`\u2717 Invalid volume name: "${dirName}"`));
13138
+ console.error(
13139
+ chalk5.gray(
13140
+ " Volume names must be 3-64 characters, lowercase alphanumeric with hyphens"
13141
+ )
13142
+ );
13143
+ console.error(
13144
+ chalk5.gray(" Example: my-dataset, user-data-v2, training-set-2024")
13145
+ );
13146
+ process.exit(1);
13147
+ }
13148
+ await writeVolumeConfig(volumeName, cwd);
13149
+ console.log(chalk5.green(`\u2713 Initialized volume: ${volumeName}`));
13150
+ console.log(
13151
+ chalk5.gray(
13152
+ `\u2713 Config saved to ${path2.join(cwd, ".vm0", "volume.yaml")}`
13153
+ )
13154
+ );
13155
+ } catch (error43) {
13156
+ console.error(chalk5.red("\u2717 Failed to initialize volume"));
13157
+ if (error43 instanceof Error) {
13158
+ console.error(chalk5.gray(` ${error43.message}`));
13159
+ }
13160
+ process.exit(1);
13161
+ }
13162
+ });
13163
+
13164
+ // src/commands/volume/push.ts
13165
+ import { Command as Command4 } from "commander";
13166
+ import chalk6 from "chalk";
13167
+ import path3 from "path";
13168
+ import * as fs from "fs";
13169
+ import AdmZip from "adm-zip";
13170
+ async function getAllFiles(dirPath, baseDir = dirPath) {
13171
+ const files = [];
13172
+ const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
13173
+ for (const entry of entries) {
13174
+ const fullPath = path3.join(dirPath, entry.name);
13175
+ const relativePath = path3.relative(baseDir, fullPath);
13176
+ if (relativePath.startsWith(".vm0")) {
13177
+ continue;
13178
+ }
13179
+ if (entry.isDirectory()) {
13180
+ const subFiles = await getAllFiles(fullPath, baseDir);
13181
+ files.push(...subFiles);
13182
+ } else {
13183
+ files.push(fullPath);
13184
+ }
13185
+ }
13186
+ return files;
13187
+ }
13188
+ function formatBytes(bytes) {
13189
+ if (bytes === 0) return "0 B";
13190
+ const k = 1024;
13191
+ const sizes = ["B", "KB", "MB", "GB"];
13192
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
13193
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
13194
+ }
13195
+ var pushCommand = new Command4().name("push").description("Push local files to cloud volume").action(async () => {
13196
+ try {
13197
+ const cwd = process.cwd();
13198
+ const config2 = await readVolumeConfig(cwd);
13199
+ if (!config2) {
13200
+ console.error(chalk6.red("\u2717 No volume initialized in this directory"));
13201
+ console.error(chalk6.gray(" Run: vm0 volume init"));
13202
+ process.exit(1);
13203
+ }
13204
+ console.log(chalk6.cyan(`Pushing volume: ${config2.name}`));
13205
+ console.log(chalk6.gray("Collecting files..."));
13206
+ const files = await getAllFiles(cwd);
13207
+ if (files.length === 0) {
13208
+ console.log(chalk6.yellow("No files to upload"));
13209
+ return;
13210
+ }
13211
+ let totalSize = 0;
13212
+ for (const file2 of files) {
13213
+ const stats = await fs.promises.stat(file2);
13214
+ totalSize += stats.size;
13215
+ }
13216
+ console.log(
13217
+ chalk6.gray(`Found ${files.length} files (${formatBytes(totalSize)})`)
13218
+ );
13219
+ console.log(chalk6.gray("Compressing files..."));
13220
+ const zip = new AdmZip();
13221
+ for (const file2 of files) {
13222
+ const relativePath = path3.relative(cwd, file2);
13223
+ zip.addLocalFile(file2, path3.dirname(relativePath));
13224
+ }
13225
+ const zipBuffer = zip.toBuffer();
13226
+ console.log(
13227
+ chalk6.green(`\u2713 Compressed to ${formatBytes(zipBuffer.length)}`)
13228
+ );
13229
+ console.log(chalk6.gray("Uploading..."));
13230
+ const formData = new FormData();
13231
+ formData.append("volumeName", config2.name);
13232
+ formData.append(
13233
+ "file",
13234
+ new Blob([zipBuffer], { type: "application/zip" }),
13235
+ "volume.zip"
13236
+ );
13237
+ const response = await apiClient.post("/api/volumes", {
13238
+ body: formData
13239
+ });
13240
+ if (!response.ok) {
13241
+ const error43 = await response.json();
13242
+ throw new Error(error43.error || "Upload failed");
13243
+ }
13244
+ const result = await response.json();
13245
+ console.log(chalk6.green("\u2713 Upload complete"));
13246
+ console.log(chalk6.gray(` Files: ${result.fileCount.toLocaleString()}`));
13247
+ console.log(chalk6.gray(` Size: ${formatBytes(result.size)}`));
13248
+ } catch (error43) {
13249
+ console.error(chalk6.red("\u2717 Push failed"));
13250
+ if (error43 instanceof Error) {
13251
+ console.error(chalk6.gray(` ${error43.message}`));
13252
+ }
13253
+ process.exit(1);
13254
+ }
13255
+ });
13256
+
13257
+ // src/commands/volume/pull.ts
13258
+ import { Command as Command5 } from "commander";
13259
+ import chalk7 from "chalk";
13260
+ import path4 from "path";
13261
+ import * as fs2 from "fs";
13262
+ import AdmZip2 from "adm-zip";
13263
+ function formatBytes2(bytes) {
13264
+ if (bytes === 0) return "0 B";
13265
+ const k = 1024;
13266
+ const sizes = ["B", "KB", "MB", "GB"];
13267
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
13268
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
13269
+ }
13270
+ var pullCommand = new Command5().name("pull").description("Pull cloud files to local directory").action(async () => {
13271
+ try {
13272
+ const cwd = process.cwd();
13273
+ const config2 = await readVolumeConfig(cwd);
13274
+ if (!config2) {
13275
+ console.error(chalk7.red("\u2717 No volume initialized in this directory"));
13276
+ console.error(chalk7.gray(" Run: vm0 volume init"));
13277
+ process.exit(1);
13278
+ }
13279
+ console.log(chalk7.cyan(`Pulling volume: ${config2.name}`));
13280
+ console.log(chalk7.gray("Downloading..."));
13281
+ const response = await apiClient.get(
13282
+ `/api/volumes?name=${encodeURIComponent(config2.name)}`
13283
+ );
13284
+ if (!response.ok) {
13285
+ if (response.status === 404) {
13286
+ console.error(chalk7.red(`\u2717 Volume "${config2.name}" not found`));
13287
+ console.error(
13288
+ chalk7.gray(
13289
+ " Make sure the volume name is correct in .vm0/volume.yaml"
13290
+ )
13291
+ );
13292
+ console.error(
13293
+ chalk7.gray(" Or push the volume first with: vm0 volume push")
13294
+ );
13295
+ } else {
13296
+ const error43 = await response.json();
13297
+ throw new Error(error43.error || "Download failed");
13298
+ }
13299
+ process.exit(1);
13300
+ }
13301
+ const arrayBuffer = await response.arrayBuffer();
13302
+ const zipBuffer = Buffer.from(arrayBuffer);
13303
+ console.log(chalk7.green(`\u2713 Downloaded ${formatBytes2(zipBuffer.length)}`));
13304
+ console.log(chalk7.gray("Extracting files..."));
13305
+ const zip = new AdmZip2(zipBuffer);
13306
+ const zipEntries = zip.getEntries();
13307
+ let extractedCount = 0;
13308
+ for (const entry of zipEntries) {
13309
+ if (!entry.isDirectory) {
13310
+ const targetPath = path4.join(cwd, entry.entryName);
13311
+ const dir = path4.dirname(targetPath);
13312
+ await fs2.promises.mkdir(dir, { recursive: true });
13313
+ const data = entry.getData();
13314
+ await fs2.promises.writeFile(targetPath, data);
13315
+ extractedCount++;
13316
+ }
13317
+ }
13318
+ console.log(chalk7.green(`\u2713 Extracted ${extractedCount} files`));
13319
+ } catch (error43) {
13320
+ console.error(chalk7.red("\u2717 Pull failed"));
13321
+ if (error43 instanceof Error) {
13322
+ console.error(chalk7.gray(` ${error43.message}`));
13323
+ }
13324
+ process.exit(1);
13325
+ }
13326
+ });
13327
+
13328
+ // src/commands/volume/index.ts
13329
+ var volumeCommand = new Command6().name("volume").description("Manage cloud volumes").addCommand(initCommand).addCommand(pushCommand).addCommand(pullCommand);
13330
+
13030
13331
  // src/index.ts
13031
- var program = new Command3();
13332
+ var program = new Command7();
13032
13333
  program.name("vm0").description("VM0 CLI - A modern build tool").version("0.1.0");
13033
13334
  program.command("hello").description("Say hello from the App").action(() => {
13034
- console.log(chalk5.blue("Welcome to the VM0 CLI!"));
13035
- console.log(chalk5.green(`Core says: ${FOO}`));
13335
+ console.log(chalk8.blue("Welcome to the VM0 CLI!"));
13336
+ console.log(chalk8.green(`Core says: ${FOO}`));
13036
13337
  });
13037
13338
  program.command("info").description("Display environment information").action(async () => {
13038
- console.log(chalk5.cyan("System Information:"));
13339
+ console.log(chalk8.cyan("System Information:"));
13039
13340
  console.log(`Node Version: ${process.version}`);
13040
13341
  console.log(`Platform: ${process.platform}`);
13041
13342
  console.log(`Architecture: ${process.arch}`);
@@ -13054,6 +13355,7 @@ authCommand.command("status").description("Show current authentication status").
13054
13355
  });
13055
13356
  program.addCommand(buildCommand);
13056
13357
  program.addCommand(runCommand);
13358
+ program.addCommand(volumeCommand);
13057
13359
  if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
13058
13360
  program.parse();
13059
13361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "CLI application",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "."
11
11
  ],
12
12
  "dependencies": {
13
+ "adm-zip": "^0.5.16",
13
14
  "chalk": "^5.6.0",
14
15
  "commander": "^14.0.0",
15
16
  "yaml": "^2.3.4"