@vm0/cli 8.0.0 → 8.0.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 +226 -142
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -2950,10 +2950,6 @@ var FEATURE_SWITCHES = {
2950
2950
  maintainer: "ethan@vm0.ai",
2951
2951
  enabled: true
2952
2952
  },
2953
- ["platformOnboarding" /* PlatformOnboarding */]: {
2954
- maintainer: "ethan@vm0.ai",
2955
- enabled: false
2956
- },
2957
2953
  ["platformAgents" /* PlatformAgents */]: {
2958
2954
  maintainer: "ethan@vm0.ai",
2959
2955
  enabled: false
@@ -6758,6 +6754,24 @@ async function promptSelect(message, choices, initial) {
6758
6754
  );
6759
6755
  return response.value;
6760
6756
  }
6757
+ async function promptPassword(message) {
6758
+ if (!isInteractive()) {
6759
+ return void 0;
6760
+ }
6761
+ const response = await prompts2(
6762
+ {
6763
+ type: "password",
6764
+ name: "value",
6765
+ message
6766
+ },
6767
+ {
6768
+ onCancel: () => {
6769
+ return false;
6770
+ }
6771
+ }
6772
+ );
6773
+ return response.value;
6774
+ }
6761
6775
 
6762
6776
  // src/commands/volume/init.ts
6763
6777
  var initCommand = new Command5().name("init").description("Initialize a volume in the current directory").option("-n, --name <name>", "Volume name (required in non-interactive mode)").action(async (options) => {
@@ -7945,7 +7959,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option(
7945
7959
  // eslint-disable-next-line complexity -- TODO: refactor complex function
7946
7960
  async (prompt, options) => {
7947
7961
  if (!options.noAutoUpdate) {
7948
- const shouldExit = await checkAndUpgrade("8.0.0", prompt);
7962
+ const shouldExit = await checkAndUpgrade("8.0.1", prompt);
7949
7963
  if (shouldExit) {
7950
7964
  process.exit(0);
7951
7965
  }
@@ -8655,10 +8669,9 @@ import { Command as Command26 } from "commander";
8655
8669
  // src/commands/agent/list.ts
8656
8670
  import { Command as Command24 } from "commander";
8657
8671
  import chalk28 from "chalk";
8658
- var listCommand3 = new Command24().name("list").alias("ls").description("List all agent composes").option("-s, --scope <scope>", "Scope to list composes from").action(async (options) => {
8672
+ var listCommand3 = new Command24().name("list").alias("ls").description("List all agent composes").action(async () => {
8659
8673
  try {
8660
- const url = options.scope ? `/api/agent/composes/list?scope=${encodeURIComponent(options.scope)}` : "/api/agent/composes/list";
8661
- const response = await httpGet(url);
8674
+ const response = await httpGet("/api/agent/composes/list");
8662
8675
  if (!response.ok) {
8663
8676
  const error = await response.json();
8664
8677
  throw new Error(error.error?.message || "List failed");
@@ -8706,27 +8719,6 @@ import chalk29 from "chalk";
8706
8719
  import * as fs9 from "fs/promises";
8707
8720
  import * as path12 from "path";
8708
8721
  import * as os7 from "os";
8709
- function extractVariableReferences2(environment) {
8710
- const secrets = [];
8711
- const vars = [];
8712
- for (const value of Object.values(environment)) {
8713
- const matches = value.matchAll(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g);
8714
- for (const match of matches) {
8715
- const varName = match[1];
8716
- if (!varName) continue;
8717
- if (varName.endsWith("_KEY") || varName.endsWith("_SECRET") || varName.endsWith("_TOKEN") || varName.endsWith("_PASSWORD") || varName.includes("API_KEY") || varName.includes("SECRET")) {
8718
- if (!secrets.includes(varName)) {
8719
- secrets.push(varName);
8720
- }
8721
- } else {
8722
- if (!vars.includes(varName)) {
8723
- vars.push(varName);
8724
- }
8725
- }
8726
- }
8727
- }
8728
- return { secrets: secrets.sort(), vars: vars.sort() };
8729
- }
8730
8722
  async function fetchSkillFrontmatter(skillUrl, tempDir) {
8731
8723
  try {
8732
8724
  const parsed = parseGitHubTreeUrl2(skillUrl);
@@ -8738,19 +8730,31 @@ async function fetchSkillFrontmatter(skillUrl, tempDir) {
8738
8730
  }
8739
8731
  }
8740
8732
  async function deriveAgentVariableSources(agent, options) {
8741
- const { secrets: secretNames, vars: varNames } = agent.environment ? extractVariableReferences2(agent.environment) : { secrets: [], vars: [] };
8733
+ const refs = agent.environment ? extractVariableReferences(agent.environment) : [];
8734
+ const grouped = groupVariablesBySource(refs);
8742
8735
  const secretSources = /* @__PURE__ */ new Map();
8743
8736
  const varSources = /* @__PURE__ */ new Map();
8744
- for (const name of secretNames) {
8745
- secretSources.set(name, { name, source: "agent environment" });
8737
+ const credentialSources = /* @__PURE__ */ new Map();
8738
+ for (const ref of grouped.secrets) {
8739
+ secretSources.set(ref.name, {
8740
+ name: ref.name,
8741
+ source: "agent environment"
8742
+ });
8746
8743
  }
8747
- for (const name of varNames) {
8748
- varSources.set(name, { name, source: "agent environment" });
8744
+ for (const ref of grouped.vars) {
8745
+ varSources.set(ref.name, { name: ref.name, source: "agent environment" });
8746
+ }
8747
+ for (const ref of grouped.credentials) {
8748
+ credentialSources.set(ref.name, {
8749
+ name: ref.name,
8750
+ source: "agent environment"
8751
+ });
8749
8752
  }
8750
8753
  if (options?.skipNetwork || !agent.skills || agent.skills.length === 0) {
8751
8754
  return {
8752
8755
  secrets: Array.from(secretSources.values()),
8753
- vars: Array.from(varSources.values())
8756
+ vars: Array.from(varSources.values()),
8757
+ credentials: Array.from(credentialSources.values())
8754
8758
  };
8755
8759
  }
8756
8760
  const tempDir = await fs9.mkdtemp(
@@ -8791,7 +8795,8 @@ async function deriveAgentVariableSources(agent, options) {
8791
8795
  }
8792
8796
  return {
8793
8797
  secrets: Array.from(secretSources.values()),
8794
- vars: Array.from(varSources.values())
8798
+ vars: Array.from(varSources.values()),
8799
+ credentials: Array.from(credentialSources.values())
8795
8800
  };
8796
8801
  }
8797
8802
  async function deriveComposeVariableSources(content, options) {
@@ -8843,6 +8848,13 @@ function formatVariableSources(sources) {
8843
8848
  console.log(` - ${v.name.padEnd(20)} ${sourceInfo}`);
8844
8849
  }
8845
8850
  }
8851
+ if (sources.credentials.length > 0) {
8852
+ console.log(` Credentials:`);
8853
+ for (const cred of sources.credentials) {
8854
+ const sourceInfo = chalk29.dim(`(${cred.source})`);
8855
+ console.log(` - ${cred.name.padEnd(20)} ${sourceInfo}`);
8856
+ }
8857
+ }
8846
8858
  }
8847
8859
  function formatAgentDetails(agentName, agent, agentSources, volumeConfigs) {
8848
8860
  console.log(` ${chalk29.cyan(agentName)}:`);
@@ -8876,83 +8888,81 @@ function formatComposeOutput(name, versionId, content, variableSources) {
8876
8888
  var statusCommand4 = new Command25().name("status").description("Show status of agent compose").argument(
8877
8889
  "<name[:version]>",
8878
8890
  "Agent name with optional version (e.g., my-agent:latest or my-agent:a1b2c3d4)"
8879
- ).option("-s, --scope <scope>", "Scope to look up the compose from").option("--no-sources", "Skip fetching skills to determine variable sources").action(
8880
- async (argument, options) => {
8881
- try {
8882
- const colonIndex = argument.lastIndexOf(":");
8883
- let name;
8884
- let version;
8885
- if (colonIndex === -1) {
8886
- name = argument;
8887
- version = "latest";
8888
- } else {
8889
- name = argument.slice(0, colonIndex);
8890
- version = argument.slice(colonIndex + 1) || "latest";
8891
- }
8892
- const compose = await getComposeByName(name, options.scope);
8893
- if (!compose) {
8894
- console.error(chalk29.red(`\u2717 Agent compose not found: ${name}`));
8895
- console.error(chalk29.dim(" Run: vm0 agent list"));
8896
- process.exit(1);
8897
- }
8898
- let resolvedVersionId = compose.headVersionId;
8899
- if (version !== "latest" && compose.headVersionId) {
8900
- if (version.length < 64) {
8901
- try {
8902
- const versionInfo = await getComposeVersion(compose.id, version);
8903
- resolvedVersionId = versionInfo.versionId;
8904
- } catch (error) {
8905
- if (error instanceof Error && error.message.includes("not found")) {
8906
- console.error(chalk29.red(`\u2717 Version not found: ${version}`));
8907
- console.error(
8908
- chalk29.dim(
8909
- ` HEAD version: ${compose.headVersionId?.slice(0, 8)}`
8910
- )
8911
- );
8912
- process.exit(1);
8913
- }
8914
- throw error;
8915
- }
8916
- } else {
8917
- resolvedVersionId = version;
8918
- }
8919
- }
8920
- if (!resolvedVersionId || !compose.content) {
8921
- console.error(chalk29.red(`\u2717 No version found for: ${name}`));
8922
- process.exit(1);
8923
- }
8924
- const content = compose.content;
8925
- let variableSources;
8926
- if (options.sources !== false) {
8891
+ ).option("--no-sources", "Skip fetching skills to determine variable sources").action(async (argument, options) => {
8892
+ try {
8893
+ const colonIndex = argument.lastIndexOf(":");
8894
+ let name;
8895
+ let version;
8896
+ if (colonIndex === -1) {
8897
+ name = argument;
8898
+ version = "latest";
8899
+ } else {
8900
+ name = argument.slice(0, colonIndex);
8901
+ version = argument.slice(colonIndex + 1) || "latest";
8902
+ }
8903
+ const compose = await getComposeByName(name);
8904
+ if (!compose) {
8905
+ console.error(chalk29.red(`\u2717 Agent compose not found: ${name}`));
8906
+ console.error(chalk29.dim(" Run: vm0 agent list"));
8907
+ process.exit(1);
8908
+ }
8909
+ let resolvedVersionId = compose.headVersionId;
8910
+ if (version !== "latest" && compose.headVersionId) {
8911
+ if (version.length < 64) {
8927
8912
  try {
8928
- variableSources = await deriveComposeVariableSources(content);
8929
- } catch {
8930
- console.error(
8931
- chalk29.yellow(
8932
- "\u26A0 Warning: Failed to fetch skill sources, showing basic info"
8933
- )
8934
- );
8913
+ const versionInfo = await getComposeVersion(compose.id, version);
8914
+ resolvedVersionId = versionInfo.versionId;
8915
+ } catch (error) {
8916
+ if (error instanceof Error && error.message.includes("not found")) {
8917
+ console.error(chalk29.red(`\u2717 Version not found: ${version}`));
8918
+ console.error(
8919
+ chalk29.dim(
8920
+ ` HEAD version: ${compose.headVersionId?.slice(0, 8)}`
8921
+ )
8922
+ );
8923
+ process.exit(1);
8924
+ }
8925
+ throw error;
8935
8926
  }
8927
+ } else {
8928
+ resolvedVersionId = version;
8936
8929
  }
8937
- formatComposeOutput(
8938
- compose.name,
8939
- resolvedVersionId,
8940
- content,
8941
- variableSources
8930
+ }
8931
+ if (!resolvedVersionId || !compose.content) {
8932
+ console.error(chalk29.red(`\u2717 No version found for: ${name}`));
8933
+ process.exit(1);
8934
+ }
8935
+ const content = compose.content;
8936
+ let variableSources;
8937
+ try {
8938
+ variableSources = await deriveComposeVariableSources(content, {
8939
+ skipNetwork: options.sources === false
8940
+ });
8941
+ } catch {
8942
+ console.error(
8943
+ chalk29.yellow(
8944
+ "\u26A0 Warning: Failed to fetch skill sources, showing basic info"
8945
+ )
8942
8946
  );
8943
- } catch (error) {
8944
- console.error(chalk29.red("\u2717 Failed to get agent compose status"));
8945
- if (error instanceof Error) {
8946
- if (error.message.includes("Not authenticated")) {
8947
- console.error(chalk29.dim(" Run: vm0 auth login"));
8948
- } else {
8949
- console.error(chalk29.dim(` ${error.message}`));
8950
- }
8947
+ }
8948
+ formatComposeOutput(
8949
+ compose.name,
8950
+ resolvedVersionId,
8951
+ content,
8952
+ variableSources
8953
+ );
8954
+ } catch (error) {
8955
+ console.error(chalk29.red("\u2717 Failed to get agent compose status"));
8956
+ if (error instanceof Error) {
8957
+ if (error.message.includes("Not authenticated")) {
8958
+ console.error(chalk29.dim(" Run: vm0 auth login"));
8959
+ } else {
8960
+ console.error(chalk29.dim(` ${error.message}`));
8951
8961
  }
8952
- process.exit(1);
8953
8962
  }
8963
+ process.exit(1);
8954
8964
  }
8955
- );
8965
+ });
8956
8966
 
8957
8967
  // src/commands/agent/index.ts
8958
8968
  var agentCommand = new Command26().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(statusCommand4);
@@ -9179,6 +9189,22 @@ function toISODateTime(dateTimeStr) {
9179
9189
  const date = new Date(isoStr);
9180
9190
  return date.toISOString();
9181
9191
  }
9192
+ function extractRequiredConfiguration(composeContent) {
9193
+ const result = {
9194
+ secrets: [],
9195
+ vars: [],
9196
+ credentials: []
9197
+ };
9198
+ if (!composeContent) {
9199
+ return result;
9200
+ }
9201
+ const refs = extractVariableReferences(composeContent);
9202
+ const grouped = groupVariablesBySource(refs);
9203
+ result.secrets = grouped.secrets.map((r) => r.name);
9204
+ result.vars = grouped.vars.map((r) => r.name);
9205
+ result.credentials = grouped.credentials.map((r) => r.name);
9206
+ return result;
9207
+ }
9182
9208
  async function resolveScheduleByAgent(agentName) {
9183
9209
  const { schedules } = await listSchedules();
9184
9210
  const schedule = schedules.find((s) => s.composeName === agentName);
@@ -9488,6 +9514,59 @@ async function gatherSecrets(optionSecrets, existingSecretNames) {
9488
9514
  }
9489
9515
  return void 0;
9490
9516
  }
9517
+ async function gatherMissingConfiguration(required, providedSecrets, providedVars, existingSecretNames) {
9518
+ const secrets = { ...providedSecrets };
9519
+ const vars = { ...providedVars };
9520
+ const providedSecretNames = Object.keys(providedSecrets);
9521
+ const existingNames = existingSecretNames ?? [];
9522
+ const missingSecrets = required.secrets.filter(
9523
+ (name) => !providedSecretNames.includes(name) && !existingNames.includes(name)
9524
+ );
9525
+ const providedVarNames = Object.keys(providedVars);
9526
+ const missingVars = required.vars.filter(
9527
+ (name) => !providedVarNames.includes(name)
9528
+ );
9529
+ if (missingSecrets.length === 0 && missingVars.length === 0) {
9530
+ return { secrets, vars };
9531
+ }
9532
+ if (!isInteractive()) {
9533
+ return { secrets, vars };
9534
+ }
9535
+ if (missingSecrets.length > 0 || missingVars.length > 0) {
9536
+ console.log(chalk31.yellow("\nAgent requires the following configuration:"));
9537
+ if (missingSecrets.length > 0) {
9538
+ console.log(chalk31.dim(" Secrets:"));
9539
+ for (const name of missingSecrets) {
9540
+ console.log(chalk31.dim(` ${name}`));
9541
+ }
9542
+ }
9543
+ if (missingVars.length > 0) {
9544
+ console.log(chalk31.dim(" Vars:"));
9545
+ for (const name of missingVars) {
9546
+ console.log(chalk31.dim(` ${name}`));
9547
+ }
9548
+ }
9549
+ console.log("");
9550
+ }
9551
+ for (const name of missingSecrets) {
9552
+ const value = await promptPassword(
9553
+ `Enter value for secret ${chalk31.cyan(name)}`
9554
+ );
9555
+ if (value) {
9556
+ secrets[name] = value;
9557
+ }
9558
+ }
9559
+ for (const name of missingVars) {
9560
+ const value = await promptText(
9561
+ `Enter value for var ${chalk31.cyan(name)}`,
9562
+ ""
9563
+ );
9564
+ if (value) {
9565
+ vars[name] = value;
9566
+ }
9567
+ }
9568
+ return { secrets, vars };
9569
+ }
9491
9570
  async function resolveAgent(agentName) {
9492
9571
  const compose = await getComposeByName(agentName);
9493
9572
  if (!compose) {
@@ -9497,9 +9576,28 @@ async function resolveAgent(agentName) {
9497
9576
  }
9498
9577
  return {
9499
9578
  composeId: compose.id,
9500
- scheduleName: `${agentName}-schedule`
9579
+ scheduleName: `${agentName}-schedule`,
9580
+ composeContent: compose.content
9501
9581
  };
9502
9582
  }
9583
+ async function gatherTiming(frequency, options, defaults) {
9584
+ if (frequency === "once") {
9585
+ const result = await gatherOneTimeSchedule(
9586
+ options.day,
9587
+ options.time,
9588
+ defaults.time
9589
+ );
9590
+ if (!result) return null;
9591
+ return { day: void 0, time: void 0, atTime: result };
9592
+ }
9593
+ const day = await gatherDay(frequency, options.day, defaults.day) ?? void 0;
9594
+ if (day === null && (frequency === "weekly" || frequency === "monthly")) {
9595
+ return null;
9596
+ }
9597
+ const time = await gatherRecurringTime(options.time, defaults.time);
9598
+ if (!time) return null;
9599
+ return { day, time, atTime: void 0 };
9600
+ }
9503
9601
  async function findExistingSchedule(agentName) {
9504
9602
  const { schedules } = await listSchedules();
9505
9603
  return schedules.find((s) => s.composeName === agentName);
@@ -9576,7 +9674,8 @@ function displayDeployResult(agentName, deployResult) {
9576
9674
  }
9577
9675
  var setupCommand = new Command28().name("setup").description("Create or edit a schedule for an agent").argument("<agent-name>", "Agent name to configure schedule for").option("-f, --frequency <type>", "Frequency: daily|weekly|monthly|once").option("-t, --time <HH:MM>", "Time to run (24-hour format)").option("-d, --day <day>", "Day of week (mon-sun) or day of month (1-31)").option("-z, --timezone <tz>", "IANA timezone").option("-p, --prompt <text>", "Prompt to run").option("--var <name=value>", "Variable (can be repeated)", collect, []).option("--secret <name=value>", "Secret (can be repeated)", collect, []).option("--artifact-name <name>", "Artifact name", "artifact").action(async (agentName, options) => {
9578
9676
  try {
9579
- const { composeId, scheduleName } = await resolveAgent(agentName);
9677
+ const { composeId, scheduleName, composeContent } = await resolveAgent(agentName);
9678
+ const requiredConfig = extractRequiredConfiguration(composeContent);
9580
9679
  const existingSchedule = await findExistingSchedule(agentName);
9581
9680
  console.log(
9582
9681
  chalk31.dim(
@@ -9592,36 +9691,12 @@ var setupCommand = new Command28().name("setup").description("Create or edit a s
9592
9691
  console.log(chalk31.dim("Cancelled"));
9593
9692
  return;
9594
9693
  }
9595
- let day;
9596
- let time;
9597
- let atTime;
9598
- if (frequency === "once") {
9599
- const result = await gatherOneTimeSchedule(
9600
- options.day,
9601
- options.time,
9602
- defaults.time
9603
- );
9604
- if (!result) {
9605
- console.log(chalk31.dim("Cancelled"));
9606
- return;
9607
- }
9608
- atTime = result;
9609
- } else {
9610
- day = await gatherDay(frequency, options.day, defaults.day) ?? void 0;
9611
- if (day === null && (frequency === "weekly" || frequency === "monthly")) {
9612
- console.log(chalk31.dim("Cancelled"));
9613
- return;
9614
- }
9615
- const timeResult = await gatherRecurringTime(
9616
- options.time,
9617
- defaults.time
9618
- );
9619
- if (!timeResult) {
9620
- console.log(chalk31.dim("Cancelled"));
9621
- return;
9622
- }
9623
- time = timeResult;
9694
+ const timing = await gatherTiming(frequency, options, defaults);
9695
+ if (!timing) {
9696
+ console.log(chalk31.dim("Cancelled"));
9697
+ return;
9624
9698
  }
9699
+ const { day, time, atTime } = timing;
9625
9700
  const timezone = await gatherTimezone(
9626
9701
  options.timezone,
9627
9702
  existingSchedule?.timezone
@@ -9638,11 +9713,20 @@ var setupCommand = new Command28().name("setup").description("Create or edit a s
9638
9713
  console.log(chalk31.dim("Cancelled"));
9639
9714
  return;
9640
9715
  }
9641
- const vars = await gatherVars(options.var || [], existingSchedule?.vars);
9642
- const secrets = await gatherSecrets(
9716
+ const initialVars = await gatherVars(
9717
+ options.var || [],
9718
+ existingSchedule?.vars
9719
+ );
9720
+ const initialSecrets = await gatherSecrets(
9643
9721
  options.secret || [],
9644
9722
  existingSchedule?.secretNames
9645
9723
  );
9724
+ const { secrets, vars } = await gatherMissingConfiguration(
9725
+ requiredConfig,
9726
+ initialSecrets ?? {},
9727
+ initialVars ?? {},
9728
+ existingSchedule?.secretNames
9729
+ );
9646
9730
  await buildAndDeploy({
9647
9731
  scheduleName,
9648
9732
  composeId,
@@ -9653,8 +9737,8 @@ var setupCommand = new Command28().name("setup").description("Create or edit a s
9653
9737
  atTime,
9654
9738
  timezone,
9655
9739
  prompt: promptText_,
9656
- vars,
9657
- secrets,
9740
+ vars: Object.keys(vars).length > 0 ? vars : void 0,
9741
+ secrets: Object.keys(secrets).length > 0 ? secrets : void 0,
9658
9742
  artifactName: options.artifactName
9659
9743
  });
9660
9744
  } catch (error) {
@@ -10537,7 +10621,7 @@ var modelProviderCommand = new Command44().name("model-provider").description("M
10537
10621
 
10538
10622
  // src/index.ts
10539
10623
  var program = new Command45();
10540
- program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("8.0.0");
10624
+ program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("8.0.1");
10541
10625
  program.command("info").description("Display environment information").action(async () => {
10542
10626
  console.log(chalk45.bold("System Information:"));
10543
10627
  console.log(`Node Version: ${process.version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "8.0.0",
3
+ "version": "8.0.1",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",