@vm0/cli 1.16.0 → 3.0.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 +217 -152
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -12488,44 +12488,56 @@ function validateAgentConfig(config2) {
12488
12488
  if (!cfg.version) {
12489
12489
  return { valid: false, error: "Missing config.version" };
12490
12490
  }
12491
- if (!cfg.agents || !Array.isArray(cfg.agents)) {
12492
- return { valid: false, error: "Missing config.agents array" };
12491
+ if (!cfg.agents || typeof cfg.agents !== "object") {
12492
+ return { valid: false, error: "Missing agents object in config" };
12493
12493
  }
12494
- if (cfg.agents.length === 0) {
12495
- return { valid: false, error: "config.agents array must not be empty" };
12496
- }
12497
- const agent = cfg.agents[0];
12498
- if (!agent || typeof agent !== "object") {
12499
- return { valid: false, error: "First agent must be an object" };
12494
+ if (Array.isArray(cfg.agents)) {
12495
+ return {
12496
+ valid: false,
12497
+ error: "agents must be an object, not an array. Use format: agents: { agent-name: { ... } }"
12498
+ };
12500
12499
  }
12501
- if (!agent.name) {
12502
- return { valid: false, error: "Missing agents[0].name" };
12500
+ const agentKeys = Object.keys(cfg.agents);
12501
+ if (agentKeys.length === 0) {
12502
+ return {
12503
+ valid: false,
12504
+ error: "agents must have at least one agent defined"
12505
+ };
12503
12506
  }
12504
- if (typeof agent.name !== "string") {
12505
- return { valid: false, error: "agents[0].name must be a string" };
12507
+ if (agentKeys.length > 1) {
12508
+ return {
12509
+ valid: false,
12510
+ error: "Multiple agents not supported yet. Only one agent allowed."
12511
+ };
12506
12512
  }
12507
- if (!validateAgentName(agent.name)) {
12513
+ const agentName = agentKeys[0];
12514
+ if (!validateAgentName(agentName)) {
12508
12515
  return {
12509
12516
  valid: false,
12510
- error: "Invalid agents[0].name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number."
12517
+ error: "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number."
12511
12518
  };
12512
12519
  }
12520
+ const agentsObj = cfg.agents;
12521
+ const agent = agentsObj[agentName];
12522
+ if (!agent || typeof agent !== "object") {
12523
+ return { valid: false, error: "Agent definition must be an object" };
12524
+ }
12513
12525
  if (!agent.working_dir || typeof agent.working_dir !== "string") {
12514
12526
  return {
12515
12527
  valid: false,
12516
- error: "Missing or invalid agents[0].working_dir (must be a string)"
12528
+ error: "Missing or invalid agent.working_dir (must be a string)"
12517
12529
  };
12518
12530
  }
12519
12531
  if (!agent.image || typeof agent.image !== "string") {
12520
12532
  return {
12521
12533
  valid: false,
12522
- error: "Missing or invalid agents[0].image (must be a string)"
12534
+ error: "Missing or invalid agent.image (must be a string)"
12523
12535
  };
12524
12536
  }
12525
12537
  if (!agent.provider || typeof agent.provider !== "string") {
12526
12538
  return {
12527
12539
  valid: false,
12528
- error: "Missing or invalid agents[0].provider (must be a string)"
12540
+ error: "Missing or invalid agent.provider (must be a string)"
12529
12541
  };
12530
12542
  }
12531
12543
  const agentVolumes = agent.volumes;
@@ -13446,15 +13458,96 @@ var initCommand = new Command3().name("init").description("Initialize a volume i
13446
13458
  // src/commands/volume/push.ts
13447
13459
  import { Command as Command4 } from "commander";
13448
13460
  import chalk6 from "chalk";
13449
- import path3 from "path";
13461
+ import path4 from "path";
13462
+ import * as fs2 from "fs";
13463
+ import * as os from "os";
13464
+ import * as tar2 from "tar";
13465
+
13466
+ // src/lib/file-utils.ts
13450
13467
  import * as fs from "fs";
13451
- import AdmZip from "adm-zip";
13468
+ import * as path3 from "path";
13469
+ import * as tar from "tar";
13470
+ function excludeVm0Filter(filePath) {
13471
+ const shouldExclude = filePath === ".vm0" || filePath.startsWith(".vm0/") || filePath.startsWith("./.vm0");
13472
+ return !shouldExclude;
13473
+ }
13474
+ function listTarFiles(tarPath) {
13475
+ return new Promise((resolve, reject) => {
13476
+ const files = [];
13477
+ tar.list({
13478
+ file: tarPath,
13479
+ onReadEntry: (entry) => {
13480
+ if (entry.type === "File") {
13481
+ files.push(entry.path);
13482
+ }
13483
+ }
13484
+ }).then(() => resolve(files)).catch(reject);
13485
+ });
13486
+ }
13487
+ async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
13488
+ const files = [];
13489
+ async function walkDir(currentDir, relativePath = "") {
13490
+ const entries = await fs.promises.readdir(currentDir, {
13491
+ withFileTypes: true
13492
+ });
13493
+ for (const entry of entries) {
13494
+ const entryRelativePath = relativePath ? path3.join(relativePath, entry.name) : entry.name;
13495
+ if (entry.isDirectory()) {
13496
+ if (!excludeDirs.includes(entry.name)) {
13497
+ await walkDir(path3.join(currentDir, entry.name), entryRelativePath);
13498
+ }
13499
+ } else {
13500
+ files.push(entryRelativePath);
13501
+ }
13502
+ }
13503
+ }
13504
+ await walkDir(dir);
13505
+ return files;
13506
+ }
13507
+ async function removeExtraFiles(dir, remoteFiles, excludeDirs = [".vm0"]) {
13508
+ const localFiles = await listLocalFiles(dir, excludeDirs);
13509
+ let removedCount = 0;
13510
+ for (const localFile of localFiles) {
13511
+ const normalizedPath = localFile.replace(/\\/g, "/");
13512
+ if (!remoteFiles.has(normalizedPath)) {
13513
+ const fullPath = path3.join(dir, localFile);
13514
+ await fs.promises.unlink(fullPath);
13515
+ removedCount++;
13516
+ }
13517
+ }
13518
+ await removeEmptyDirs(dir, excludeDirs);
13519
+ return removedCount;
13520
+ }
13521
+ async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
13522
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
13523
+ let isEmpty = true;
13524
+ for (const entry of entries) {
13525
+ const fullPath = path3.join(dir, entry.name);
13526
+ if (entry.isDirectory()) {
13527
+ if (excludeDirs.includes(entry.name)) {
13528
+ isEmpty = false;
13529
+ } else {
13530
+ const subDirEmpty = await removeEmptyDirs(fullPath, excludeDirs);
13531
+ if (subDirEmpty) {
13532
+ await fs.promises.rmdir(fullPath);
13533
+ } else {
13534
+ isEmpty = false;
13535
+ }
13536
+ }
13537
+ } else {
13538
+ isEmpty = false;
13539
+ }
13540
+ }
13541
+ return isEmpty;
13542
+ }
13543
+
13544
+ // src/commands/volume/push.ts
13452
13545
  async function getAllFiles(dirPath, baseDir = dirPath) {
13453
13546
  const files = [];
13454
- const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
13547
+ const entries = await fs2.promises.readdir(dirPath, { withFileTypes: true });
13455
13548
  for (const entry of entries) {
13456
- const fullPath = path3.join(dirPath, entry.name);
13457
- const relativePath = path3.relative(baseDir, fullPath);
13549
+ const fullPath = path4.join(dirPath, entry.name);
13550
+ const relativePath = path4.relative(baseDir, fullPath);
13458
13551
  if (relativePath.startsWith(".vm0")) {
13459
13552
  continue;
13460
13553
  }
@@ -13491,7 +13584,7 @@ var pushCommand = new Command4().name("push").description("Push local files to c
13491
13584
  const files = await getAllFiles(cwd);
13492
13585
  let totalSize = 0;
13493
13586
  for (const file2 of files) {
13494
- const stats = await fs.promises.stat(file2);
13587
+ const stats = await fs2.promises.stat(file2);
13495
13588
  totalSize += stats.size;
13496
13589
  }
13497
13590
  if (files.length === 0) {
@@ -13502,14 +13595,34 @@ var pushCommand = new Command4().name("push").description("Push local files to c
13502
13595
  );
13503
13596
  }
13504
13597
  console.log(chalk6.gray("Compressing files..."));
13505
- const zip = new AdmZip();
13506
- for (const file2 of files) {
13507
- const relativePath = path3.relative(cwd, file2);
13508
- zip.addLocalFile(file2, path3.dirname(relativePath));
13598
+ const tmpDir = fs2.mkdtempSync(path4.join(os.tmpdir(), "vm0-"));
13599
+ const tarPath = path4.join(tmpDir, "volume.tar.gz");
13600
+ const relativePaths = files.map((file2) => path4.relative(cwd, file2));
13601
+ if (relativePaths.length > 0) {
13602
+ await tar2.create(
13603
+ {
13604
+ gzip: true,
13605
+ file: tarPath,
13606
+ cwd
13607
+ },
13608
+ relativePaths
13609
+ );
13610
+ } else {
13611
+ await tar2.create(
13612
+ {
13613
+ gzip: true,
13614
+ file: tarPath,
13615
+ cwd,
13616
+ filter: excludeVm0Filter
13617
+ },
13618
+ ["."]
13619
+ );
13509
13620
  }
13510
- const zipBuffer = zip.toBuffer();
13621
+ const tarBuffer = await fs2.promises.readFile(tarPath);
13622
+ await fs2.promises.unlink(tarPath);
13623
+ await fs2.promises.rmdir(tmpDir);
13511
13624
  console.log(
13512
- chalk6.green(`\u2713 Compressed to ${formatBytes(zipBuffer.length)}`)
13625
+ chalk6.green(`\u2713 Compressed to ${formatBytes(tarBuffer.length)}`)
13513
13626
  );
13514
13627
  console.log(chalk6.gray("Uploading..."));
13515
13628
  const formData = new FormData();
@@ -13520,8 +13633,8 @@ var pushCommand = new Command4().name("push").description("Push local files to c
13520
13633
  }
13521
13634
  formData.append(
13522
13635
  "file",
13523
- new Blob([zipBuffer], { type: "application/zip" }),
13524
- "volume.zip"
13636
+ new Blob([tarBuffer], { type: "application/gzip" }),
13637
+ "volume.tar.gz"
13525
13638
  );
13526
13639
  const response = await apiClient.post("/api/storages", {
13527
13640
  body: formData
@@ -13555,78 +13668,8 @@ import { Command as Command5 } from "commander";
13555
13668
  import chalk7 from "chalk";
13556
13669
  import path5 from "path";
13557
13670
  import * as fs3 from "fs";
13558
- import AdmZip2 from "adm-zip";
13559
-
13560
- // src/lib/file-utils.ts
13561
- import * as fs2 from "fs";
13562
- import * as path4 from "path";
13563
- function getRemoteFilesFromZip(zipEntries) {
13564
- const remoteFiles = /* @__PURE__ */ new Set();
13565
- for (const entry of zipEntries) {
13566
- if (!entry.isDirectory) {
13567
- remoteFiles.add(entry.entryName.replace(/\\/g, "/"));
13568
- }
13569
- }
13570
- return remoteFiles;
13571
- }
13572
- async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
13573
- const files = [];
13574
- async function walkDir(currentDir, relativePath = "") {
13575
- const entries = await fs2.promises.readdir(currentDir, {
13576
- withFileTypes: true
13577
- });
13578
- for (const entry of entries) {
13579
- const entryRelativePath = relativePath ? path4.join(relativePath, entry.name) : entry.name;
13580
- if (entry.isDirectory()) {
13581
- if (!excludeDirs.includes(entry.name)) {
13582
- await walkDir(path4.join(currentDir, entry.name), entryRelativePath);
13583
- }
13584
- } else {
13585
- files.push(entryRelativePath);
13586
- }
13587
- }
13588
- }
13589
- await walkDir(dir);
13590
- return files;
13591
- }
13592
- async function removeExtraFiles(dir, remoteFiles, excludeDirs = [".vm0"]) {
13593
- const localFiles = await listLocalFiles(dir, excludeDirs);
13594
- let removedCount = 0;
13595
- for (const localFile of localFiles) {
13596
- const normalizedPath = localFile.replace(/\\/g, "/");
13597
- if (!remoteFiles.has(normalizedPath)) {
13598
- const fullPath = path4.join(dir, localFile);
13599
- await fs2.promises.unlink(fullPath);
13600
- removedCount++;
13601
- }
13602
- }
13603
- await removeEmptyDirs(dir, excludeDirs);
13604
- return removedCount;
13605
- }
13606
- async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
13607
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
13608
- let isEmpty = true;
13609
- for (const entry of entries) {
13610
- const fullPath = path4.join(dir, entry.name);
13611
- if (entry.isDirectory()) {
13612
- if (excludeDirs.includes(entry.name)) {
13613
- isEmpty = false;
13614
- } else {
13615
- const subDirEmpty = await removeEmptyDirs(fullPath, excludeDirs);
13616
- if (subDirEmpty) {
13617
- await fs2.promises.rmdir(fullPath);
13618
- } else {
13619
- isEmpty = false;
13620
- }
13621
- }
13622
- } else {
13623
- isEmpty = false;
13624
- }
13625
- }
13626
- return isEmpty;
13627
- }
13628
-
13629
- // src/commands/volume/pull.ts
13671
+ import * as os2 from "os";
13672
+ import * as tar3 from "tar";
13630
13673
  function formatBytes2(bytes) {
13631
13674
  if (bytes === 0) return "0 B";
13632
13675
  const k = 1024;
@@ -13674,31 +13717,31 @@ var pullCommand = new Command5().name("pull").description("Pull cloud files to l
13674
13717
  process.exit(1);
13675
13718
  }
13676
13719
  const arrayBuffer = await response.arrayBuffer();
13677
- const zipBuffer = Buffer.from(arrayBuffer);
13678
- console.log(chalk7.green(`\u2713 Downloaded ${formatBytes2(zipBuffer.length)}`));
13679
- console.log(chalk7.gray("Extracting files..."));
13680
- const zip = new AdmZip2(zipBuffer);
13681
- const zipEntries = zip.getEntries();
13682
- const remoteFiles = getRemoteFilesFromZip(zipEntries);
13720
+ const tarBuffer = Buffer.from(arrayBuffer);
13721
+ console.log(chalk7.green(`\u2713 Downloaded ${formatBytes2(tarBuffer.length)}`));
13722
+ const tmpDir = fs3.mkdtempSync(path5.join(os2.tmpdir(), "vm0-"));
13723
+ const tarPath = path5.join(tmpDir, "volume.tar.gz");
13724
+ await fs3.promises.writeFile(tarPath, tarBuffer);
13683
13725
  console.log(chalk7.gray("Syncing local files..."));
13684
- const removedCount = await removeExtraFiles(cwd, remoteFiles);
13726
+ const remoteFiles = await listTarFiles(tarPath);
13727
+ const remoteFilesSet = new Set(
13728
+ remoteFiles.map((f) => f.replace(/\\/g, "/"))
13729
+ );
13730
+ const removedCount = await removeExtraFiles(cwd, remoteFilesSet);
13685
13731
  if (removedCount > 0) {
13686
13732
  console.log(
13687
13733
  chalk7.green(`\u2713 Removed ${removedCount} files not in remote`)
13688
13734
  );
13689
13735
  }
13690
- let extractedCount = 0;
13691
- for (const entry of zipEntries) {
13692
- if (!entry.isDirectory) {
13693
- const targetPath = path5.join(cwd, entry.entryName);
13694
- const dir = path5.dirname(targetPath);
13695
- await fs3.promises.mkdir(dir, { recursive: true });
13696
- const data = entry.getData();
13697
- await fs3.promises.writeFile(targetPath, data);
13698
- extractedCount++;
13699
- }
13700
- }
13701
- console.log(chalk7.green(`\u2713 Extracted ${extractedCount} files`));
13736
+ console.log(chalk7.gray("Extracting files..."));
13737
+ await tar3.extract({
13738
+ file: tarPath,
13739
+ cwd,
13740
+ gzip: true
13741
+ });
13742
+ await fs3.promises.unlink(tarPath);
13743
+ await fs3.promises.rmdir(tmpDir);
13744
+ console.log(chalk7.green(`\u2713 Extracted ${remoteFiles.length} files`));
13702
13745
  } catch (error43) {
13703
13746
  console.error(chalk7.red("\u2717 Pull failed"));
13704
13747
  if (error43 instanceof Error) {
@@ -13781,7 +13824,8 @@ import { Command as Command8 } from "commander";
13781
13824
  import chalk9 from "chalk";
13782
13825
  import path7 from "path";
13783
13826
  import * as fs4 from "fs";
13784
- import AdmZip3 from "adm-zip";
13827
+ import * as os3 from "os";
13828
+ import * as tar4 from "tar";
13785
13829
  async function getAllFiles2(dirPath, baseDir = dirPath) {
13786
13830
  const files = [];
13787
13831
  const entries = await fs4.promises.readdir(dirPath, { withFileTypes: true });
@@ -13844,14 +13888,34 @@ var pushCommand2 = new Command8().name("push").description("Push local files to
13844
13888
  );
13845
13889
  }
13846
13890
  console.log(chalk9.gray("Compressing files..."));
13847
- const zip = new AdmZip3();
13848
- for (const file2 of files) {
13849
- const relativePath = path7.relative(cwd, file2);
13850
- zip.addLocalFile(file2, path7.dirname(relativePath));
13891
+ const tmpDir = fs4.mkdtempSync(path7.join(os3.tmpdir(), "vm0-"));
13892
+ const tarPath = path7.join(tmpDir, "artifact.tar.gz");
13893
+ const relativePaths = files.map((file2) => path7.relative(cwd, file2));
13894
+ if (relativePaths.length > 0) {
13895
+ await tar4.create(
13896
+ {
13897
+ gzip: true,
13898
+ file: tarPath,
13899
+ cwd
13900
+ },
13901
+ relativePaths
13902
+ );
13903
+ } else {
13904
+ await tar4.create(
13905
+ {
13906
+ gzip: true,
13907
+ file: tarPath,
13908
+ cwd,
13909
+ filter: excludeVm0Filter
13910
+ },
13911
+ ["."]
13912
+ );
13851
13913
  }
13852
- const zipBuffer = zip.toBuffer();
13914
+ const tarBuffer = await fs4.promises.readFile(tarPath);
13915
+ await fs4.promises.unlink(tarPath);
13916
+ await fs4.promises.rmdir(tmpDir);
13853
13917
  console.log(
13854
- chalk9.green(`\u2713 Compressed to ${formatBytes3(zipBuffer.length)}`)
13918
+ chalk9.green(`\u2713 Compressed to ${formatBytes3(tarBuffer.length)}`)
13855
13919
  );
13856
13920
  console.log(chalk9.gray("Uploading..."));
13857
13921
  const formData = new FormData();
@@ -13862,8 +13926,8 @@ var pushCommand2 = new Command8().name("push").description("Push local files to
13862
13926
  }
13863
13927
  formData.append(
13864
13928
  "file",
13865
- new Blob([zipBuffer], { type: "application/zip" }),
13866
- "artifact.zip"
13929
+ new Blob([tarBuffer], { type: "application/gzip" }),
13930
+ "artifact.tar.gz"
13867
13931
  );
13868
13932
  const response = await apiClient.post("/api/storages", {
13869
13933
  body: formData
@@ -13896,7 +13960,8 @@ import { Command as Command9 } from "commander";
13896
13960
  import chalk10 from "chalk";
13897
13961
  import path8 from "path";
13898
13962
  import * as fs5 from "fs";
13899
- import AdmZip4 from "adm-zip";
13963
+ import * as os4 from "os";
13964
+ import * as tar5 from "tar";
13900
13965
  function formatBytes4(bytes) {
13901
13966
  if (bytes === 0) return "0 B";
13902
13967
  const k = 1024;
@@ -13955,31 +14020,31 @@ var pullCommand2 = new Command9().name("pull").description("Pull cloud artifact
13955
14020
  process.exit(1);
13956
14021
  }
13957
14022
  const arrayBuffer = await response.arrayBuffer();
13958
- const zipBuffer = Buffer.from(arrayBuffer);
13959
- console.log(chalk10.green(`\u2713 Downloaded ${formatBytes4(zipBuffer.length)}`));
13960
- console.log(chalk10.gray("Extracting files..."));
13961
- const zip = new AdmZip4(zipBuffer);
13962
- const zipEntries = zip.getEntries();
13963
- const remoteFiles = getRemoteFilesFromZip(zipEntries);
14023
+ const tarBuffer = Buffer.from(arrayBuffer);
14024
+ console.log(chalk10.green(`\u2713 Downloaded ${formatBytes4(tarBuffer.length)}`));
14025
+ const tmpDir = fs5.mkdtempSync(path8.join(os4.tmpdir(), "vm0-"));
14026
+ const tarPath = path8.join(tmpDir, "artifact.tar.gz");
14027
+ await fs5.promises.writeFile(tarPath, tarBuffer);
13964
14028
  console.log(chalk10.gray("Syncing local files..."));
13965
- const removedCount = await removeExtraFiles(cwd, remoteFiles);
14029
+ const remoteFiles = await listTarFiles(tarPath);
14030
+ const remoteFilesSet = new Set(
14031
+ remoteFiles.map((f) => f.replace(/\\/g, "/"))
14032
+ );
14033
+ const removedCount = await removeExtraFiles(cwd, remoteFilesSet);
13966
14034
  if (removedCount > 0) {
13967
14035
  console.log(
13968
14036
  chalk10.green(`\u2713 Removed ${removedCount} files not in remote`)
13969
14037
  );
13970
14038
  }
13971
- let extractedCount = 0;
13972
- for (const entry of zipEntries) {
13973
- if (!entry.isDirectory) {
13974
- const targetPath = path8.join(cwd, entry.entryName);
13975
- const dir = path8.dirname(targetPath);
13976
- await fs5.promises.mkdir(dir, { recursive: true });
13977
- const data = entry.getData();
13978
- await fs5.promises.writeFile(targetPath, data);
13979
- extractedCount++;
13980
- }
13981
- }
13982
- console.log(chalk10.green(`\u2713 Extracted ${extractedCount} files`));
14039
+ console.log(chalk10.gray("Extracting files..."));
14040
+ await tar5.extract({
14041
+ file: tarPath,
14042
+ cwd,
14043
+ gzip: true
14044
+ });
14045
+ await fs5.promises.unlink(tarPath);
14046
+ await fs5.promises.rmdir(tmpDir);
14047
+ console.log(chalk10.green(`\u2713 Extracted ${remoteFiles.length} files`));
13983
14048
  } catch (error43) {
13984
14049
  console.error(chalk10.red("\u2717 Pull failed"));
13985
14050
  if (error43 instanceof Error) {
@@ -13994,7 +14059,7 @@ var artifactCommand = new Command10().name("artifact").description("Manage cloud
13994
14059
 
13995
14060
  // src/index.ts
13996
14061
  var program = new Command11();
13997
- program.name("vm0").description("VM0 CLI - A modern build tool").version("1.16.0");
14062
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("3.0.0");
13998
14063
  program.command("hello").description("Say hello from the App").action(() => {
13999
14064
  console.log(chalk11.blue("Welcome to the VM0 CLI!"));
14000
14065
  console.log(chalk11.green(`Core says: ${FOO}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "1.16.0",
3
+ "version": "3.0.0",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,9 +15,9 @@
15
15
  "."
16
16
  ],
17
17
  "dependencies": {
18
- "adm-zip": "^0.5.16",
19
18
  "chalk": "^5.6.0",
20
19
  "commander": "^14.0.0",
20
+ "tar": "^7.5.2",
21
21
  "yaml": "^2.3.4"
22
22
  }
23
23
  }