@vm0/cli 1.1.0 → 1.3.0

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 +384 -23
  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";
@@ -12348,7 +12348,7 @@ var ApiClient = class {
12348
12348
  );
12349
12349
  if (!response.ok) {
12350
12350
  const error43 = await response.json();
12351
- throw new Error(error43.error || `Config not found: ${name}`);
12351
+ throw new Error(error43.error?.message || `Config not found: ${name}`);
12352
12352
  }
12353
12353
  return await response.json();
12354
12354
  }
@@ -12362,7 +12362,7 @@ var ApiClient = class {
12362
12362
  });
12363
12363
  if (!response.ok) {
12364
12364
  const error43 = await response.json();
12365
- throw new Error(error43.error || "Failed to create config");
12365
+ throw new Error(error43.error?.message || "Failed to create config");
12366
12366
  }
12367
12367
  return await response.json();
12368
12368
  }
@@ -12376,7 +12376,8 @@ var ApiClient = class {
12376
12376
  });
12377
12377
  if (!response.ok) {
12378
12378
  const error43 = await response.json();
12379
- throw new Error(error43.error || "Failed to create run");
12379
+ const message = error43.error?.message || "Failed to create run";
12380
+ throw new Error(message);
12380
12381
  }
12381
12382
  return await response.json();
12382
12383
  }
@@ -12394,7 +12395,7 @@ var ApiClient = class {
12394
12395
  );
12395
12396
  if (!response.ok) {
12396
12397
  const error43 = await response.json();
12397
- throw new Error(error43.error || "Failed to fetch events");
12398
+ throw new Error(error43.error?.message || "Failed to fetch events");
12398
12399
  }
12399
12400
  return await response.json();
12400
12401
  }
@@ -12408,10 +12409,53 @@ var ApiClient = class {
12408
12409
  });
12409
12410
  if (!response.ok) {
12410
12411
  const error43 = await response.json();
12411
- throw new Error(error43.error || "Failed to resume run");
12412
+ throw new Error(error43.error?.message || "Failed to resume run");
12412
12413
  }
12413
12414
  return await response.json();
12414
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
+ }
12415
12459
  };
12416
12460
  var apiClient = new ApiClient();
12417
12461
 
@@ -12453,6 +12497,39 @@ function expandEnvVars(value) {
12453
12497
  return process.env[varName] ?? "";
12454
12498
  });
12455
12499
  }
12500
+ function extractEnvVarReferences(obj) {
12501
+ const varNames = /* @__PURE__ */ new Set();
12502
+ function scan(value) {
12503
+ if (typeof value === "string") {
12504
+ const matches = value.matchAll(/\$\{([^}]+)\}/g);
12505
+ for (const match of matches) {
12506
+ const varName = match[1];
12507
+ if (varName) {
12508
+ varNames.add(varName);
12509
+ }
12510
+ }
12511
+ } else if (Array.isArray(value)) {
12512
+ for (const item of value) {
12513
+ scan(item);
12514
+ }
12515
+ } else if (value !== null && typeof value === "object") {
12516
+ for (const val of Object.values(value)) {
12517
+ scan(val);
12518
+ }
12519
+ }
12520
+ }
12521
+ scan(obj);
12522
+ return Array.from(varNames);
12523
+ }
12524
+ function validateEnvVars(varNames) {
12525
+ const missing = [];
12526
+ for (const varName of varNames) {
12527
+ if (process.env[varName] === void 0) {
12528
+ missing.push(varName);
12529
+ }
12530
+ }
12531
+ return missing;
12532
+ }
12456
12533
  function expandEnvVarsInObject(obj) {
12457
12534
  if (typeof obj === "string") {
12458
12535
  return expandEnvVars(obj);
@@ -12488,6 +12565,23 @@ var buildCommand = new Command().name("build").description("Create or update age
12488
12565
  }
12489
12566
  process.exit(1);
12490
12567
  }
12568
+ const referencedVars = extractEnvVarReferences(config2);
12569
+ const missingVars = validateEnvVars(referencedVars);
12570
+ if (missingVars.length > 0) {
12571
+ console.error(chalk2.red("\u2717 Missing required environment variables:"));
12572
+ for (const varName of missingVars) {
12573
+ console.error(chalk2.red(` - ${varName}`));
12574
+ }
12575
+ console.error();
12576
+ console.error(
12577
+ chalk2.gray("Please set these variables before running 'vm0 build'.")
12578
+ );
12579
+ console.error(chalk2.gray("Example:"));
12580
+ for (const varName of missingVars) {
12581
+ console.error(chalk2.gray(` export ${varName}=your-value`));
12582
+ }
12583
+ process.exit(1);
12584
+ }
12491
12585
  config2 = expandEnvVarsInObject(config2);
12492
12586
  const validation = validateAgentConfig(config2);
12493
12587
  if (!validation.valid) {
@@ -12793,7 +12887,18 @@ var EventRenderer = class {
12793
12887
  }
12794
12888
  static renderVm0Error(event) {
12795
12889
  console.log(chalk3.red("[vm0_error]") + " \u2717 Run failed");
12796
- console.log(` Error: ${chalk3.red(String(event.data.error || ""))}`);
12890
+ let errorMessage = "";
12891
+ if (typeof event.data.error === "string") {
12892
+ errorMessage = event.data.error;
12893
+ } else if (event.data.error && typeof event.data.error === "object") {
12894
+ const errorObj = event.data.error;
12895
+ if ("message" in errorObj && typeof errorObj.message === "string") {
12896
+ errorMessage = errorObj.message;
12897
+ } else {
12898
+ errorMessage = JSON.stringify(event.data.error);
12899
+ }
12900
+ }
12901
+ console.log(` Error: ${chalk3.red(errorMessage || "Unknown error")}`);
12797
12902
  if (event.data.errorType) {
12798
12903
  console.log(` Type: ${chalk3.gray(String(event.data.errorType))}`);
12799
12904
  }
@@ -12965,15 +13070,270 @@ runCmd.command("resume").description("Resume an agent run from a checkpoint").ar
12965
13070
  });
12966
13071
  var runCommand = runCmd;
12967
13072
 
13073
+ // src/commands/volume/index.ts
13074
+ import { Command as Command6 } from "commander";
13075
+
13076
+ // src/commands/volume/init.ts
13077
+ import { Command as Command3 } from "commander";
13078
+ import chalk5 from "chalk";
13079
+ import path2 from "path";
13080
+
13081
+ // src/lib/volume-utils.ts
13082
+ import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13083
+ import { existsSync as existsSync3 } from "fs";
13084
+ import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
13085
+ import path from "path";
13086
+ var CONFIG_DIR2 = ".vm0";
13087
+ var CONFIG_FILE2 = "volume.yaml";
13088
+ function isValidVolumeName(name) {
13089
+ if (name.length < 3 || name.length > 64) {
13090
+ return false;
13091
+ }
13092
+ const pattern = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
13093
+ return pattern.test(name) && !name.includes("--");
13094
+ }
13095
+ async function readVolumeConfig(basePath = process.cwd()) {
13096
+ const configPath = path.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
13097
+ if (!existsSync3(configPath)) {
13098
+ return null;
13099
+ }
13100
+ const content = await readFile3(configPath, "utf8");
13101
+ const config2 = parseYaml2(content);
13102
+ return config2;
13103
+ }
13104
+ async function writeVolumeConfig(volumeName, basePath = process.cwd()) {
13105
+ const configDir = path.join(basePath, CONFIG_DIR2);
13106
+ const configPath = path.join(configDir, CONFIG_FILE2);
13107
+ if (!existsSync3(configDir)) {
13108
+ await mkdir2(configDir, { recursive: true });
13109
+ }
13110
+ const config2 = {
13111
+ name: volumeName
13112
+ };
13113
+ const yamlContent = stringifyYaml(config2);
13114
+ await writeFile2(configPath, yamlContent, "utf8");
13115
+ }
13116
+
13117
+ // src/commands/volume/init.ts
13118
+ var initCommand = new Command3().name("init").description("Initialize a volume in the current directory").action(async () => {
13119
+ try {
13120
+ const cwd = process.cwd();
13121
+ const dirName = path2.basename(cwd);
13122
+ const existingConfig = await readVolumeConfig(cwd);
13123
+ if (existingConfig) {
13124
+ console.log(
13125
+ chalk5.yellow(`Volume already initialized: ${existingConfig.name}`)
13126
+ );
13127
+ console.log(
13128
+ chalk5.gray(`Config file: ${path2.join(cwd, ".vm0", "volume.yaml")}`)
13129
+ );
13130
+ return;
13131
+ }
13132
+ const volumeName = dirName;
13133
+ if (!isValidVolumeName(volumeName)) {
13134
+ console.error(chalk5.red(`\u2717 Invalid volume name: "${dirName}"`));
13135
+ console.error(
13136
+ chalk5.gray(
13137
+ " Volume names must be 3-64 characters, lowercase alphanumeric with hyphens"
13138
+ )
13139
+ );
13140
+ console.error(
13141
+ chalk5.gray(" Example: my-dataset, user-data-v2, training-set-2024")
13142
+ );
13143
+ process.exit(1);
13144
+ }
13145
+ await writeVolumeConfig(volumeName, cwd);
13146
+ console.log(chalk5.green(`\u2713 Initialized volume: ${volumeName}`));
13147
+ console.log(
13148
+ chalk5.gray(
13149
+ `\u2713 Config saved to ${path2.join(cwd, ".vm0", "volume.yaml")}`
13150
+ )
13151
+ );
13152
+ } catch (error43) {
13153
+ console.error(chalk5.red("\u2717 Failed to initialize volume"));
13154
+ if (error43 instanceof Error) {
13155
+ console.error(chalk5.gray(` ${error43.message}`));
13156
+ }
13157
+ process.exit(1);
13158
+ }
13159
+ });
13160
+
13161
+ // src/commands/volume/push.ts
13162
+ import { Command as Command4 } from "commander";
13163
+ import chalk6 from "chalk";
13164
+ import path3 from "path";
13165
+ import * as fs from "fs";
13166
+ import AdmZip from "adm-zip";
13167
+ async function getAllFiles(dirPath, baseDir = dirPath) {
13168
+ const files = [];
13169
+ const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
13170
+ for (const entry of entries) {
13171
+ const fullPath = path3.join(dirPath, entry.name);
13172
+ const relativePath = path3.relative(baseDir, fullPath);
13173
+ if (relativePath.startsWith(".vm0")) {
13174
+ continue;
13175
+ }
13176
+ if (entry.isDirectory()) {
13177
+ const subFiles = await getAllFiles(fullPath, baseDir);
13178
+ files.push(...subFiles);
13179
+ } else {
13180
+ files.push(fullPath);
13181
+ }
13182
+ }
13183
+ return files;
13184
+ }
13185
+ function formatBytes(bytes) {
13186
+ if (bytes === 0) return "0 B";
13187
+ const k = 1024;
13188
+ const sizes = ["B", "KB", "MB", "GB"];
13189
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
13190
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
13191
+ }
13192
+ var pushCommand = new Command4().name("push").description("Push local files to cloud volume").action(async () => {
13193
+ try {
13194
+ const cwd = process.cwd();
13195
+ const config2 = await readVolumeConfig(cwd);
13196
+ if (!config2) {
13197
+ console.error(chalk6.red("\u2717 No volume initialized in this directory"));
13198
+ console.error(chalk6.gray(" Run: vm0 volume init"));
13199
+ process.exit(1);
13200
+ }
13201
+ console.log(chalk6.cyan(`Pushing volume: ${config2.name}`));
13202
+ console.log(chalk6.gray("Collecting files..."));
13203
+ const files = await getAllFiles(cwd);
13204
+ if (files.length === 0) {
13205
+ console.log(chalk6.yellow("No files to upload"));
13206
+ return;
13207
+ }
13208
+ let totalSize = 0;
13209
+ for (const file2 of files) {
13210
+ const stats = await fs.promises.stat(file2);
13211
+ totalSize += stats.size;
13212
+ }
13213
+ console.log(
13214
+ chalk6.gray(`Found ${files.length} files (${formatBytes(totalSize)})`)
13215
+ );
13216
+ console.log(chalk6.gray("Compressing files..."));
13217
+ const zip = new AdmZip();
13218
+ for (const file2 of files) {
13219
+ const relativePath = path3.relative(cwd, file2);
13220
+ zip.addLocalFile(file2, path3.dirname(relativePath));
13221
+ }
13222
+ const zipBuffer = zip.toBuffer();
13223
+ console.log(
13224
+ chalk6.green(`\u2713 Compressed to ${formatBytes(zipBuffer.length)}`)
13225
+ );
13226
+ console.log(chalk6.gray("Uploading..."));
13227
+ const formData = new FormData();
13228
+ formData.append("volumeName", config2.name);
13229
+ formData.append(
13230
+ "file",
13231
+ new Blob([zipBuffer], { type: "application/zip" }),
13232
+ "volume.zip"
13233
+ );
13234
+ const response = await apiClient.post("/api/volumes", {
13235
+ body: formData
13236
+ });
13237
+ if (!response.ok) {
13238
+ const error43 = await response.json();
13239
+ throw new Error(error43.error || "Upload failed");
13240
+ }
13241
+ const result = await response.json();
13242
+ console.log(chalk6.green("\u2713 Upload complete"));
13243
+ console.log(chalk6.gray(` Files: ${result.fileCount.toLocaleString()}`));
13244
+ console.log(chalk6.gray(` Size: ${formatBytes(result.size)}`));
13245
+ } catch (error43) {
13246
+ console.error(chalk6.red("\u2717 Push failed"));
13247
+ if (error43 instanceof Error) {
13248
+ console.error(chalk6.gray(` ${error43.message}`));
13249
+ }
13250
+ process.exit(1);
13251
+ }
13252
+ });
13253
+
13254
+ // src/commands/volume/pull.ts
13255
+ import { Command as Command5 } from "commander";
13256
+ import chalk7 from "chalk";
13257
+ import path4 from "path";
13258
+ import * as fs2 from "fs";
13259
+ import AdmZip2 from "adm-zip";
13260
+ function formatBytes2(bytes) {
13261
+ if (bytes === 0) return "0 B";
13262
+ const k = 1024;
13263
+ const sizes = ["B", "KB", "MB", "GB"];
13264
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
13265
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
13266
+ }
13267
+ var pullCommand = new Command5().name("pull").description("Pull cloud files to local directory").action(async () => {
13268
+ try {
13269
+ const cwd = process.cwd();
13270
+ const config2 = await readVolumeConfig(cwd);
13271
+ if (!config2) {
13272
+ console.error(chalk7.red("\u2717 No volume initialized in this directory"));
13273
+ console.error(chalk7.gray(" Run: vm0 volume init"));
13274
+ process.exit(1);
13275
+ }
13276
+ console.log(chalk7.cyan(`Pulling volume: ${config2.name}`));
13277
+ console.log(chalk7.gray("Downloading..."));
13278
+ const response = await apiClient.get(
13279
+ `/api/volumes?name=${encodeURIComponent(config2.name)}`
13280
+ );
13281
+ if (!response.ok) {
13282
+ if (response.status === 404) {
13283
+ console.error(chalk7.red(`\u2717 Volume "${config2.name}" not found`));
13284
+ console.error(
13285
+ chalk7.gray(
13286
+ " Make sure the volume name is correct in .vm0/volume.yaml"
13287
+ )
13288
+ );
13289
+ console.error(
13290
+ chalk7.gray(" Or push the volume first with: vm0 volume push")
13291
+ );
13292
+ } else {
13293
+ const error43 = await response.json();
13294
+ throw new Error(error43.error || "Download failed");
13295
+ }
13296
+ process.exit(1);
13297
+ }
13298
+ const arrayBuffer = await response.arrayBuffer();
13299
+ const zipBuffer = Buffer.from(arrayBuffer);
13300
+ console.log(chalk7.green(`\u2713 Downloaded ${formatBytes2(zipBuffer.length)}`));
13301
+ console.log(chalk7.gray("Extracting files..."));
13302
+ const zip = new AdmZip2(zipBuffer);
13303
+ const zipEntries = zip.getEntries();
13304
+ let extractedCount = 0;
13305
+ for (const entry of zipEntries) {
13306
+ if (!entry.isDirectory) {
13307
+ const targetPath = path4.join(cwd, entry.entryName);
13308
+ const dir = path4.dirname(targetPath);
13309
+ await fs2.promises.mkdir(dir, { recursive: true });
13310
+ const data = entry.getData();
13311
+ await fs2.promises.writeFile(targetPath, data);
13312
+ extractedCount++;
13313
+ }
13314
+ }
13315
+ console.log(chalk7.green(`\u2713 Extracted ${extractedCount} files`));
13316
+ } catch (error43) {
13317
+ console.error(chalk7.red("\u2717 Pull failed"));
13318
+ if (error43 instanceof Error) {
13319
+ console.error(chalk7.gray(` ${error43.message}`));
13320
+ }
13321
+ process.exit(1);
13322
+ }
13323
+ });
13324
+
13325
+ // src/commands/volume/index.ts
13326
+ var volumeCommand = new Command6().name("volume").description("Manage cloud volumes").addCommand(initCommand).addCommand(pushCommand).addCommand(pullCommand);
13327
+
12968
13328
  // src/index.ts
12969
- var program = new Command3();
13329
+ var program = new Command7();
12970
13330
  program.name("vm0").description("VM0 CLI - A modern build tool").version("0.1.0");
12971
13331
  program.command("hello").description("Say hello from the App").action(() => {
12972
- console.log(chalk5.blue("Welcome to the VM0 CLI!"));
12973
- console.log(chalk5.green(`Core says: ${FOO}`));
13332
+ console.log(chalk8.blue("Welcome to the VM0 CLI!"));
13333
+ console.log(chalk8.green(`Core says: ${FOO}`));
12974
13334
  });
12975
13335
  program.command("info").description("Display environment information").action(async () => {
12976
- console.log(chalk5.cyan("System Information:"));
13336
+ console.log(chalk8.cyan("System Information:"));
12977
13337
  console.log(`Node Version: ${process.version}`);
12978
13338
  console.log(`Platform: ${process.platform}`);
12979
13339
  console.log(`Architecture: ${process.arch}`);
@@ -12992,6 +13352,7 @@ authCommand.command("status").description("Show current authentication status").
12992
13352
  });
12993
13353
  program.addCommand(buildCommand);
12994
13354
  program.addCommand(runCommand);
13355
+ program.addCommand(volumeCommand);
12995
13356
  if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
12996
13357
  program.parse();
12997
13358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
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"