@vm0/cli 4.22.0 → 4.24.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 +197 -62
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -15407,17 +15407,18 @@ ${action}...`));
15407
15407
  function showNextSteps(result) {
15408
15408
  const { runId, sessionId, checkpointId } = result;
15409
15409
  console.log();
15410
- console.log("Next steps:");
15411
- console.log(" View telemetry logs:");
15410
+ console.log(" View agent logs:");
15412
15411
  console.log(chalk5.cyan(` vm0 logs ${runId}`));
15413
15412
  if (sessionId) {
15414
- console.log(" Continue with session (latest state):");
15413
+ console.log(" Continue with session (latest conversation and artifact):");
15415
15414
  console.log(
15416
15415
  chalk5.cyan(` vm0 run continue ${sessionId} "your next prompt"`)
15417
15416
  );
15418
15417
  }
15419
15418
  if (checkpointId) {
15420
- console.log(" Resume from checkpoint (exact snapshot state):");
15419
+ console.log(
15420
+ " Resume from checkpoint (snapshotted conversation and artifact):"
15421
+ );
15421
15422
  console.log(
15422
15423
  chalk5.cyan(` vm0 run resume ${checkpointId} "your next prompt"`)
15423
15424
  );
@@ -16397,8 +16398,8 @@ var artifactCommand = new Command12().name("artifact").description("Manage cloud
16397
16398
  // src/commands/cook.ts
16398
16399
  import { Command as Command13 } from "commander";
16399
16400
  import chalk16 from "chalk";
16400
- import { readFile as readFile5, mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
16401
- import { existsSync as existsSync6, readFileSync } from "fs";
16401
+ import { readFile as readFile6, mkdir as mkdir6, writeFile as writeFile6, appendFile } from "fs/promises";
16402
+ import { existsSync as existsSync7, readFileSync } from "fs";
16402
16403
  import path11 from "path";
16403
16404
  import { spawn as spawn2 } from "child_process";
16404
16405
  import { parse as parseYaml3 } from "yaml";
@@ -16512,9 +16513,37 @@ async function checkAndUpgrade(currentVersion, prompt) {
16512
16513
  return true;
16513
16514
  }
16514
16515
 
16516
+ // src/lib/cook-state.ts
16517
+ import { homedir as homedir2 } from "os";
16518
+ import { join as join6 } from "path";
16519
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
16520
+ import { existsSync as existsSync6 } from "fs";
16521
+ var CONFIG_DIR3 = join6(homedir2(), ".vm0");
16522
+ var COOK_STATE_FILE = join6(CONFIG_DIR3, "cook.json");
16523
+ async function loadCookState() {
16524
+ if (!existsSync6(COOK_STATE_FILE)) {
16525
+ return {};
16526
+ }
16527
+ try {
16528
+ const content = await readFile5(COOK_STATE_FILE, "utf8");
16529
+ return JSON.parse(content);
16530
+ } catch {
16531
+ return {};
16532
+ }
16533
+ }
16534
+ async function saveCookState(state) {
16535
+ await mkdir5(CONFIG_DIR3, { recursive: true });
16536
+ const existing = await loadCookState();
16537
+ const merged = { ...existing, ...state };
16538
+ await writeFile5(COOK_STATE_FILE, JSON.stringify(merged, null, 2), "utf8");
16539
+ }
16540
+
16515
16541
  // src/commands/cook.ts
16516
16542
  var CONFIG_FILE3 = "vm0.yaml";
16517
16543
  var ARTIFACT_DIR = "artifact";
16544
+ function printCommand(cmd) {
16545
+ console.log(chalk16.dim(`> ${cmd}`));
16546
+ }
16518
16547
  function execVm0Command(args, options = {}) {
16519
16548
  return new Promise((resolve2, reject) => {
16520
16549
  const proc = spawn2("vm0", args, {
@@ -16590,6 +16619,20 @@ function parseArtifactVersionFromCompletion(output, artifactName) {
16590
16619
  function escapeRegExp(str) {
16591
16620
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16592
16621
  }
16622
+ function parseRunIdsFromOutput(output) {
16623
+ const completionMarker = "Run completed successfully";
16624
+ const completionIndex = output.indexOf(completionMarker);
16625
+ if (completionIndex === -1) return {};
16626
+ const section = output.slice(completionIndex);
16627
+ const ESC = String.fromCharCode(27);
16628
+ const ansiPattern = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
16629
+ const stripped = section.replace(ansiPattern, "");
16630
+ return {
16631
+ runId: stripped.match(/vm0 logs ([0-9a-f-]{36})/)?.[1],
16632
+ sessionId: stripped.match(/vm0 run continue ([0-9a-f-]{36})/)?.[1],
16633
+ checkpointId: stripped.match(/vm0 run resume ([0-9a-f-]{36})/)?.[1]
16634
+ };
16635
+ }
16593
16636
  function extractRequiredVarNames(config2) {
16594
16637
  const refs = extractVariableReferences(config2);
16595
16638
  const grouped = groupVariablesBySource(refs);
@@ -16599,7 +16642,7 @@ function extractRequiredVarNames(config2) {
16599
16642
  }
16600
16643
  function checkMissingVariables(varNames, envFilePath) {
16601
16644
  let dotenvValues = {};
16602
- if (existsSync6(envFilePath)) {
16645
+ if (existsSync7(envFilePath)) {
16603
16646
  const result = dotenvConfig2({ path: envFilePath, quiet: true });
16604
16647
  if (result.parsed) {
16605
16648
  dotenvValues = result.parsed;
@@ -16617,31 +16660,56 @@ function checkMissingVariables(varNames, envFilePath) {
16617
16660
  }
16618
16661
  async function generateEnvPlaceholders(missingVars, envFilePath) {
16619
16662
  const placeholders = missingVars.map((name) => `${name}=`).join("\n");
16620
- if (existsSync6(envFilePath)) {
16663
+ if (existsSync7(envFilePath)) {
16621
16664
  const existingContent = readFileSync(envFilePath, "utf8");
16622
16665
  const needsNewline = existingContent.length > 0 && !existingContent.endsWith("\n");
16623
16666
  const prefix = needsNewline ? "\n" : "";
16624
16667
  await appendFile(envFilePath, `${prefix}${placeholders}
16625
16668
  `);
16626
16669
  } else {
16627
- await writeFile5(envFilePath, `${placeholders}
16670
+ await writeFile6(envFilePath, `${placeholders}
16628
16671
  `);
16629
16672
  }
16630
16673
  }
16631
- var cookCommand = new Command13().name("cook").description("One-click agent preparation and execution from vm0.yaml").argument("[prompt]", "Prompt for the agent").action(async (prompt) => {
16632
- const shouldExit = await checkAndUpgrade("4.22.0", prompt);
16674
+ async function autoPullArtifact(runOutput, artifactDir) {
16675
+ const serverVersion = parseArtifactVersionFromCompletion(
16676
+ runOutput,
16677
+ ARTIFACT_DIR
16678
+ );
16679
+ if (serverVersion && existsSync7(artifactDir)) {
16680
+ console.log();
16681
+ console.log(chalk16.blue("Pulling updated artifact:"));
16682
+ printCommand(`cd ${ARTIFACT_DIR}`);
16683
+ printCommand(`vm0 artifact pull ${serverVersion}`);
16684
+ try {
16685
+ await execVm0Command(["artifact", "pull", serverVersion], {
16686
+ cwd: artifactDir,
16687
+ silent: true
16688
+ });
16689
+ printCommand("cd ..");
16690
+ } catch (error43) {
16691
+ console.error(chalk16.red(`\u2717 Artifact pull failed`));
16692
+ if (error43 instanceof Error) {
16693
+ console.error(chalk16.gray(` ${error43.message}`));
16694
+ }
16695
+ }
16696
+ }
16697
+ }
16698
+ var cookCmd = new Command13().name("cook").description("One-click agent preparation and execution from vm0.yaml");
16699
+ cookCmd.argument("[prompt]", "Prompt for the agent").action(async (prompt) => {
16700
+ const shouldExit = await checkAndUpgrade("4.24.0", prompt);
16633
16701
  if (shouldExit) {
16634
16702
  process.exit(0);
16635
16703
  }
16636
16704
  const cwd = process.cwd();
16637
16705
  console.log(chalk16.blue(`Reading config: ${CONFIG_FILE3}`));
16638
- if (!existsSync6(CONFIG_FILE3)) {
16706
+ if (!existsSync7(CONFIG_FILE3)) {
16639
16707
  console.error(chalk16.red(`\u2717 Config file not found: ${CONFIG_FILE3}`));
16640
16708
  process.exit(1);
16641
16709
  }
16642
16710
  let config2;
16643
16711
  try {
16644
- const content = await readFile5(CONFIG_FILE3, "utf8");
16712
+ const content = await readFile6(CONFIG_FILE3, "utf8");
16645
16713
  config2 = parseYaml3(content);
16646
16714
  } catch (error43) {
16647
16715
  console.error(chalk16.red("\u2717 Invalid YAML format"));
@@ -16681,78 +16749,80 @@ var cookCommand = new Command13().name("cook").description("One-click agent prep
16681
16749
  }
16682
16750
  if (config2.volumes && Object.keys(config2.volumes).length > 0) {
16683
16751
  console.log();
16684
- console.log(chalk16.blue("Processing volumes..."));
16752
+ console.log(chalk16.blue("Processing volumes:"));
16685
16753
  for (const volumeConfig of Object.values(config2.volumes)) {
16686
16754
  const volumeDir = path11.join(cwd, volumeConfig.name);
16687
- console.log(chalk16.gray(` ${volumeConfig.name}/`));
16688
- if (!existsSync6(volumeDir)) {
16755
+ if (!existsSync7(volumeDir)) {
16689
16756
  console.error(
16690
16757
  chalk16.red(
16691
- ` \u2717 Directory not found. Create the directory and add files first.`
16758
+ `\u2717 Directory not found: ${volumeConfig.name}. Create the directory and add files first.`
16692
16759
  )
16693
16760
  );
16694
16761
  process.exit(1);
16695
16762
  }
16696
16763
  try {
16764
+ printCommand(`cd ${volumeConfig.name}`);
16697
16765
  const existingConfig = await readStorageConfig(volumeDir);
16698
16766
  if (!existingConfig) {
16767
+ printCommand("vm0 volume init");
16699
16768
  await execVm0Command(["volume", "init"], {
16700
16769
  cwd: volumeDir,
16701
16770
  silent: true
16702
16771
  });
16703
- console.log(chalk16.green(` \u2713 Initialized`));
16704
16772
  }
16773
+ printCommand("vm0 volume push");
16705
16774
  await execVm0Command(["volume", "push"], {
16706
16775
  cwd: volumeDir,
16707
16776
  silent: true
16708
16777
  });
16709
- console.log(chalk16.green(` \u2713 Pushed`));
16778
+ printCommand("cd ..");
16710
16779
  } catch (error43) {
16711
- console.error(chalk16.red(` \u2717 Failed`));
16780
+ console.error(chalk16.red(`\u2717 Failed`));
16712
16781
  if (error43 instanceof Error) {
16713
- console.error(chalk16.gray(` ${error43.message}`));
16782
+ console.error(chalk16.gray(` ${error43.message}`));
16714
16783
  }
16715
16784
  process.exit(1);
16716
16785
  }
16717
16786
  }
16718
16787
  }
16719
16788
  console.log();
16720
- console.log(chalk16.blue("Processing artifact..."));
16789
+ console.log(chalk16.blue("Processing artifact:"));
16721
16790
  const artifactDir = path11.join(cwd, ARTIFACT_DIR);
16722
- console.log(chalk16.gray(` ${ARTIFACT_DIR}/`));
16723
16791
  try {
16724
- if (!existsSync6(artifactDir)) {
16725
- await mkdir5(artifactDir, { recursive: true });
16726
- console.log(chalk16.green(` \u2713 Created directory`));
16792
+ if (!existsSync7(artifactDir)) {
16793
+ printCommand(`mkdir ${ARTIFACT_DIR}`);
16794
+ await mkdir6(artifactDir, { recursive: true });
16727
16795
  }
16796
+ printCommand(`cd ${ARTIFACT_DIR}`);
16728
16797
  const existingConfig = await readStorageConfig(artifactDir);
16729
16798
  if (!existingConfig) {
16799
+ printCommand("vm0 artifact init");
16730
16800
  await execVm0Command(["artifact", "init"], {
16731
16801
  cwd: artifactDir,
16732
16802
  silent: true
16733
16803
  });
16734
- console.log(chalk16.green(` \u2713 Initialized`));
16735
16804
  }
16805
+ printCommand("vm0 artifact push");
16736
16806
  await execVm0Command(["artifact", "push"], {
16737
16807
  cwd: artifactDir,
16738
16808
  silent: true
16739
16809
  });
16740
- console.log(chalk16.green(` \u2713 Pushed`));
16810
+ printCommand("cd ..");
16741
16811
  } catch (error43) {
16742
- console.error(chalk16.red(` \u2717 Failed`));
16812
+ console.error(chalk16.red(`\u2717 Failed`));
16743
16813
  if (error43 instanceof Error) {
16744
- console.error(chalk16.gray(` ${error43.message}`));
16814
+ console.error(chalk16.gray(` ${error43.message}`));
16745
16815
  }
16746
16816
  process.exit(1);
16747
16817
  }
16748
16818
  console.log();
16749
- console.log(chalk16.blue("Uploading compose..."));
16819
+ console.log(chalk16.blue("Composing agent:"));
16820
+ printCommand(`vm0 compose ${CONFIG_FILE3}`);
16750
16821
  try {
16751
16822
  await execVm0Command(["compose", CONFIG_FILE3], {
16752
16823
  cwd,
16753
16824
  silent: true
16754
16825
  });
16755
- console.log(chalk16.green(`\u2713 Compose uploaded: ${agentName}`));
16756
16826
  } catch (error43) {
16757
16827
  console.error(chalk16.red(`\u2717 Compose failed`));
16758
16828
  if (error43 instanceof Error) {
@@ -16762,7 +16832,10 @@ var cookCommand = new Command13().name("cook").description("One-click agent prep
16762
16832
  }
16763
16833
  if (prompt) {
16764
16834
  console.log();
16765
- console.log(chalk16.blue(`Running agent: ${agentName}`));
16835
+ console.log(chalk16.blue("Running agent:"));
16836
+ printCommand(
16837
+ `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "${prompt}"`
16838
+ );
16766
16839
  console.log();
16767
16840
  let runOutput;
16768
16841
  try {
@@ -16777,36 +16850,98 @@ var cookCommand = new Command13().name("cook").description("One-click agent prep
16777
16850
  } catch {
16778
16851
  process.exit(1);
16779
16852
  }
16780
- const serverVersion = parseArtifactVersionFromCompletion(
16781
- runOutput,
16782
- ARTIFACT_DIR
16783
- );
16784
- if (serverVersion) {
16785
- console.log();
16786
- console.log(chalk16.blue("Pulling updated artifact..."));
16787
- try {
16788
- await execVm0Command(["artifact", "pull", serverVersion], {
16789
- cwd: artifactDir,
16790
- silent: true
16791
- });
16792
- console.log(chalk16.green(`\u2713 Artifact pulled (${serverVersion})`));
16793
- } catch (error43) {
16794
- console.error(chalk16.red(`\u2717 Artifact pull failed`));
16795
- if (error43 instanceof Error) {
16796
- console.error(chalk16.gray(` ${error43.message}`));
16797
- }
16798
- }
16853
+ const runIds = parseRunIdsFromOutput(runOutput);
16854
+ if (runIds.runId || runIds.sessionId || runIds.checkpointId) {
16855
+ await saveCookState({
16856
+ lastRunId: runIds.runId,
16857
+ lastSessionId: runIds.sessionId,
16858
+ lastCheckpointId: runIds.checkpointId
16859
+ });
16799
16860
  }
16861
+ await autoPullArtifact(runOutput, artifactDir);
16800
16862
  } else {
16801
16863
  console.log();
16802
- console.log(" Run your agent:");
16803
- console.log(
16804
- chalk16.cyan(
16805
- ` vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "your prompt"`
16806
- )
16864
+ console.log("To run your agent:");
16865
+ printCommand(
16866
+ `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "your prompt"`
16867
+ );
16868
+ }
16869
+ });
16870
+ cookCmd.command("logs").description("View logs from the last cook run").action(async () => {
16871
+ const state = await loadCookState();
16872
+ if (!state.lastRunId) {
16873
+ console.error(chalk16.red("\u2717 No previous run found"));
16874
+ console.error(chalk16.gray(" Run 'vm0 cook <prompt>' first"));
16875
+ process.exit(1);
16876
+ }
16877
+ printCommand(`vm0 logs ${state.lastRunId}`);
16878
+ await execVm0Command(["logs", state.lastRunId]);
16879
+ });
16880
+ cookCmd.command("continue").description(
16881
+ "Continue from the last session (latest conversation and artifact)"
16882
+ ).argument("<prompt>", "Prompt for the continued agent").action(async (prompt) => {
16883
+ const state = await loadCookState();
16884
+ if (!state.lastSessionId) {
16885
+ console.error(chalk16.red("\u2717 No previous session found"));
16886
+ console.error(chalk16.gray(" Run 'vm0 cook <prompt>' first"));
16887
+ process.exit(1);
16888
+ }
16889
+ const cwd = process.cwd();
16890
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
16891
+ printCommand(`vm0 run continue ${state.lastSessionId} "${prompt}"`);
16892
+ console.log();
16893
+ let runOutput;
16894
+ try {
16895
+ runOutput = await execVm0RunWithCapture(
16896
+ ["run", "continue", state.lastSessionId, prompt],
16897
+ { cwd }
16807
16898
  );
16899
+ } catch {
16900
+ process.exit(1);
16901
+ }
16902
+ const newIds = parseRunIdsFromOutput(runOutput);
16903
+ if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
16904
+ await saveCookState({
16905
+ lastRunId: newIds.runId,
16906
+ lastSessionId: newIds.sessionId,
16907
+ lastCheckpointId: newIds.checkpointId
16908
+ });
16909
+ }
16910
+ await autoPullArtifact(runOutput, artifactDir);
16911
+ });
16912
+ cookCmd.command("resume").description(
16913
+ "Resume from the last checkpoint (snapshotted conversation and artifact)"
16914
+ ).argument("<prompt>", "Prompt for the resumed agent").action(async (prompt) => {
16915
+ const state = await loadCookState();
16916
+ if (!state.lastCheckpointId) {
16917
+ console.error(chalk16.red("\u2717 No previous checkpoint found"));
16918
+ console.error(chalk16.gray(" Run 'vm0 cook <prompt>' first"));
16919
+ process.exit(1);
16920
+ }
16921
+ const cwd = process.cwd();
16922
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
16923
+ printCommand(`vm0 run resume ${state.lastCheckpointId} "${prompt}"`);
16924
+ console.log();
16925
+ let runOutput;
16926
+ try {
16927
+ runOutput = await execVm0RunWithCapture(
16928
+ ["run", "resume", state.lastCheckpointId, prompt],
16929
+ { cwd }
16930
+ );
16931
+ } catch {
16932
+ process.exit(1);
16933
+ }
16934
+ const newIds = parseRunIdsFromOutput(runOutput);
16935
+ if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
16936
+ await saveCookState({
16937
+ lastRunId: newIds.runId,
16938
+ lastSessionId: newIds.sessionId,
16939
+ lastCheckpointId: newIds.checkpointId
16940
+ });
16808
16941
  }
16942
+ await autoPullArtifact(runOutput, artifactDir);
16809
16943
  });
16944
+ var cookCommand = cookCmd;
16810
16945
 
16811
16946
  // src/commands/image/index.ts
16812
16947
  import { Command as Command18 } from "commander";
@@ -16814,8 +16949,8 @@ import { Command as Command18 } from "commander";
16814
16949
  // src/commands/image/build.ts
16815
16950
  import { Command as Command14 } from "commander";
16816
16951
  import chalk17 from "chalk";
16817
- import { readFile as readFile6 } from "fs/promises";
16818
- import { existsSync as existsSync7 } from "fs";
16952
+ import { readFile as readFile7 } from "fs/promises";
16953
+ import { existsSync as existsSync8 } from "fs";
16819
16954
 
16820
16955
  // src/lib/dockerfile-validator.ts
16821
16956
  var ALLOWED_INSTRUCTIONS = /* @__PURE__ */ new Set(["FROM", "RUN"]);
@@ -16852,7 +16987,7 @@ var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
16852
16987
  var buildCommand = new Command14().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(
16853
16988
  async (options) => {
16854
16989
  const { file: file2, name, deleteExisting } = options;
16855
- if (!existsSync7(file2)) {
16990
+ if (!existsSync8(file2)) {
16856
16991
  console.error(chalk17.red(`\u2717 Dockerfile not found: ${file2}`));
16857
16992
  process.exit(1);
16858
16993
  }
@@ -16875,7 +17010,7 @@ var buildCommand = new Command14().name("build").description("Build a custom ima
16875
17010
  }
16876
17011
  try {
16877
17012
  const scope = await apiClient.getScope();
16878
- const dockerfile = await readFile6(file2, "utf8");
17013
+ const dockerfile = await readFile7(file2, "utf8");
16879
17014
  const validation = validateDockerfile(dockerfile);
16880
17015
  if (!validation.valid) {
16881
17016
  console.error(chalk17.red("\u2717 Dockerfile validation failed\n"));
@@ -17567,7 +17702,7 @@ var scopeCommand = new Command22().name("scope").description("Manage your scope
17567
17702
 
17568
17703
  // src/index.ts
17569
17704
  var program = new Command23();
17570
- program.name("vm0").description("VM0 CLI - A modern build tool").version("4.22.0");
17705
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("4.24.0");
17571
17706
  program.command("info").description("Display environment information").action(async () => {
17572
17707
  console.log(chalk24.cyan("System Information:"));
17573
17708
  console.log(`Node Version: ${process.version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "4.22.0",
3
+ "version": "4.24.0",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",