@vm0/cli 7.1.0 → 8.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 +1032 -1592
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command47 } from "commander";
5
- import chalk47 from "chalk";
4
+ import { Command as Command45 } from "commander";
5
+ import chalk45 from "chalk";
6
6
 
7
7
  // src/lib/api/auth.ts
8
8
  import chalk from "chalk";
@@ -79,7 +79,7 @@ async function exchangeToken(apiUrl, deviceCode) {
79
79
  return response.json();
80
80
  }
81
81
  function delay(ms) {
82
- return new Promise((resolve2) => setTimeout(resolve2, ms));
82
+ return new Promise((resolve) => setTimeout(resolve, ms));
83
83
  }
84
84
  async function authenticate(apiUrl) {
85
85
  const targetApiUrl = apiUrl ?? await getApiUrl();
@@ -3024,10 +3024,10 @@ async function getRawHeaders() {
3024
3024
  }
3025
3025
  return headers;
3026
3026
  }
3027
- async function httpGet(path16) {
3027
+ async function httpGet(path14) {
3028
3028
  const baseUrl = await getBaseUrl();
3029
3029
  const headers = await getRawHeaders();
3030
- return fetch(`${baseUrl}${path16}`, {
3030
+ return fetch(`${baseUrl}${path14}`, {
3031
3031
  method: "GET",
3032
3032
  headers
3033
3033
  });
@@ -3588,49 +3588,49 @@ var cliComposeSchema = z23.object({
3588
3588
  function formatZodError(error) {
3589
3589
  const issue = error.issues[0];
3590
3590
  if (!issue) return "Validation failed";
3591
- const path16 = issue.path.join(".");
3591
+ const path14 = issue.path.join(".");
3592
3592
  const message = issue.message;
3593
- if (!path16) return message;
3593
+ if (!path14) return message;
3594
3594
  if (issue.code === "invalid_type") {
3595
3595
  const received = issue.received;
3596
3596
  const isMissing = received === "undefined" || message.includes("received undefined") || message === "Required";
3597
- if (path16 === "version" && isMissing) {
3597
+ if (path14 === "version" && isMissing) {
3598
3598
  return "Missing config.version";
3599
3599
  }
3600
- if (path16 === "agents" && isMissing) {
3600
+ if (path14 === "agents" && isMissing) {
3601
3601
  return "Missing agents object in config";
3602
3602
  }
3603
- if (path16.startsWith("volumes.") && path16.endsWith(".name")) {
3604
- const volumeKey = path16.split(".")[1];
3603
+ if (path14.startsWith("volumes.") && path14.endsWith(".name")) {
3604
+ const volumeKey = path14.split(".")[1];
3605
3605
  return `Volume "${volumeKey}" must have a 'name' field (string)`;
3606
3606
  }
3607
- if (path16.startsWith("volumes.") && path16.endsWith(".version")) {
3608
- const volumeKey = path16.split(".")[1];
3607
+ if (path14.startsWith("volumes.") && path14.endsWith(".version")) {
3608
+ const volumeKey = path14.split(".")[1];
3609
3609
  return `Volume "${volumeKey}" must have a 'version' field (string)`;
3610
3610
  }
3611
3611
  if (issue.expected === "array") {
3612
- const fieldName = path16.replace(/^agents\.[^.]+\./, "agent.");
3612
+ const fieldName = path14.replace(/^agents\.[^.]+\./, "agent.");
3613
3613
  return `${fieldName} must be an array`;
3614
3614
  }
3615
3615
  if (issue.expected === "string" && received === "number") {
3616
- const fieldName = path16.replace(/^agents\.[^.]+\./, "agent.");
3616
+ const fieldName = path14.replace(/^agents\.[^.]+\./, "agent.");
3617
3617
  const match = fieldName.match(/^(agent\.[^.]+)\.\d+$/);
3618
3618
  if (match) {
3619
3619
  return `Each entry in ${match[1]?.replace("agent.", "")} must be a string`;
3620
3620
  }
3621
3621
  }
3622
3622
  }
3623
- if (issue.code === "invalid_key" && path16.startsWith("agents.")) {
3623
+ if (issue.code === "invalid_key" && path14.startsWith("agents.")) {
3624
3624
  return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
3625
3625
  }
3626
- if (message === "Invalid key in record" && path16.startsWith("agents.")) {
3626
+ if (message === "Invalid key in record" && path14.startsWith("agents.")) {
3627
3627
  return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
3628
3628
  }
3629
3629
  if (issue.code === "custom") {
3630
3630
  return message;
3631
3631
  }
3632
- if (path16.startsWith("agents.")) {
3633
- const cleanPath = path16.replace(/^agents\.[^.]+\./, "agent.");
3632
+ if (path14.startsWith("agents.")) {
3633
+ const cleanPath = path14.replace(/^agents\.[^.]+\./, "agent.");
3634
3634
  if (message.startsWith("Invalid input:")) {
3635
3635
  const match = message.match(/expected (\w+), received (\w+)/);
3636
3636
  if (match && match[1] === "string" && match[2] === "number") {
@@ -3642,7 +3642,7 @@ function formatZodError(error) {
3642
3642
  }
3643
3643
  return `${cleanPath}: ${message}`;
3644
3644
  }
3645
- return `${path16}: ${message}`;
3645
+ return `${path14}: ${message}`;
3646
3646
  }
3647
3647
  function validateAgentName(name) {
3648
3648
  return cliAgentNameSchema.safeParse(name).success;
@@ -3815,7 +3815,7 @@ function excludeVm0Filter(filePath) {
3815
3815
  return !shouldExclude;
3816
3816
  }
3817
3817
  function listTarFiles(tarPath) {
3818
- return new Promise((resolve2, reject) => {
3818
+ return new Promise((resolve, reject) => {
3819
3819
  const files = [];
3820
3820
  tar.list({
3821
3821
  file: tarPath,
@@ -3824,7 +3824,7 @@ function listTarFiles(tarPath) {
3824
3824
  files.push(entry.path);
3825
3825
  }
3826
3826
  }
3827
- }).then(() => resolve2(files)).catch(reject);
3827
+ }).then(() => resolve(files)).catch(reject);
3828
3828
  });
3829
3829
  }
3830
3830
  async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
@@ -3886,11 +3886,11 @@ async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
3886
3886
 
3887
3887
  // src/lib/storage/direct-upload.ts
3888
3888
  async function hashFileStream(filePath) {
3889
- return new Promise((resolve2, reject) => {
3889
+ return new Promise((resolve, reject) => {
3890
3890
  const hash = createHash("sha256");
3891
3891
  const stream = fs3.createReadStream(filePath);
3892
3892
  stream.on("data", (chunk) => hash.update(chunk));
3893
- stream.on("end", () => resolve2(hash.digest("hex")));
3893
+ stream.on("end", () => resolve(hash.digest("hex")));
3894
3894
  stream.on("error", reject);
3895
3895
  });
3896
3896
  }
@@ -3975,7 +3975,7 @@ function createManifest(files) {
3975
3975
  return Buffer.from(JSON.stringify(manifest, null, 2));
3976
3976
  }
3977
3977
  function sleep(ms) {
3978
- return new Promise((resolve2) => setTimeout(resolve2, ms));
3978
+ return new Promise((resolve) => setTimeout(resolve, ms));
3979
3979
  }
3980
3980
  async function uploadToPresignedUrl(presignedUrl, data, contentType, maxRetries = 3) {
3981
3981
  let lastError = null;
@@ -4549,7 +4549,6 @@ var EventRenderer = class {
4549
4549
  // src/commands/run/shared.ts
4550
4550
  import chalk5 from "chalk";
4551
4551
  import * as fs5 from "fs";
4552
- import * as path5 from "path";
4553
4552
  import { config as dotenvConfig } from "dotenv";
4554
4553
 
4555
4554
  // src/lib/events/claude-event-parser.ts
@@ -5783,7 +5782,7 @@ var ApiClient = class {
5783
5782
  /**
5784
5783
  * Generic GET request
5785
5784
  */
5786
- async get(path16) {
5785
+ async get(path14) {
5787
5786
  const baseUrl = await this.getBaseUrl();
5788
5787
  const token = await getToken();
5789
5788
  if (!token) {
@@ -5796,7 +5795,7 @@ var ApiClient = class {
5796
5795
  if (bypassSecret) {
5797
5796
  headers["x-vercel-protection-bypass"] = bypassSecret;
5798
5797
  }
5799
- return fetch(`${baseUrl}${path16}`, {
5798
+ return fetch(`${baseUrl}${path14}`, {
5800
5799
  method: "GET",
5801
5800
  headers
5802
5801
  });
@@ -5804,7 +5803,7 @@ var ApiClient = class {
5804
5803
  /**
5805
5804
  * Generic POST request
5806
5805
  */
5807
- async post(path16, options) {
5806
+ async post(path14, options) {
5808
5807
  const baseUrl = await this.getBaseUrl();
5809
5808
  const token = await getToken();
5810
5809
  if (!token) {
@@ -5820,7 +5819,7 @@ var ApiClient = class {
5820
5819
  if (bypassSecret) {
5821
5820
  headers["x-vercel-protection-bypass"] = bypassSecret;
5822
5821
  }
5823
- return fetch(`${baseUrl}${path16}`, {
5822
+ return fetch(`${baseUrl}${path14}`, {
5824
5823
  method: "POST",
5825
5824
  headers,
5826
5825
  body: options?.body
@@ -5829,7 +5828,7 @@ var ApiClient = class {
5829
5828
  /**
5830
5829
  * Generic DELETE request
5831
5830
  */
5832
- async delete(path16) {
5831
+ async delete(path14) {
5833
5832
  const baseUrl = await this.getBaseUrl();
5834
5833
  const token = await getToken();
5835
5834
  if (!token) {
@@ -5842,7 +5841,7 @@ var ApiClient = class {
5842
5841
  if (bypassSecret) {
5843
5842
  headers["x-vercel-protection-bypass"] = bypassSecret;
5844
5843
  }
5845
- return fetch(`${baseUrl}${path16}`, {
5844
+ return fetch(`${baseUrl}${path14}`, {
5846
5845
  method: "DELETE",
5847
5846
  headers
5848
5847
  });
@@ -5887,7 +5886,7 @@ async function streamEvents(runId, options) {
5887
5886
  const ablyClient = createRealtimeClient(async () => {
5888
5887
  return apiClient.getRealtimeToken(runId);
5889
5888
  });
5890
- return new Promise((resolve2, reject) => {
5889
+ return new Promise((resolve, reject) => {
5891
5890
  const channelName = getRunChannelName(runId);
5892
5891
  const channel = ablyClient.channels.get(channelName, {
5893
5892
  params: { rewind: "2m" }
@@ -5948,7 +5947,7 @@ async function streamEvents(runId, options) {
5948
5947
  result = { succeeded: false, runId };
5949
5948
  }
5950
5949
  cleanup();
5951
- resolve2(result);
5950
+ resolve(result);
5952
5951
  }
5953
5952
  }
5954
5953
  channel.subscribe(handleMessage).catch((err) => {
@@ -5992,30 +5991,32 @@ function extractSecretNames(composeContent) {
5992
5991
  const grouped = groupVariablesBySource(refs);
5993
5992
  return grouped.secrets.map((r) => r.name);
5994
5993
  }
5995
- function loadValues(cliValues, configNames) {
5994
+ function loadValues(cliValues, configNames, envFilePath) {
5996
5995
  const result = { ...cliValues };
5997
5996
  const missingNames = configNames.filter((name) => !(name in result));
5998
5997
  if (missingNames.length > 0) {
5999
- const envFilePath = path5.resolve(process.cwd(), ".env");
6000
- let dotenvValues = {};
6001
- if (fs5.existsSync(envFilePath)) {
5998
+ const envValues = {};
5999
+ for (const name of missingNames) {
6000
+ const envValue = process.env[name];
6001
+ if (envValue !== void 0) {
6002
+ envValues[name] = envValue;
6003
+ }
6004
+ }
6005
+ let fileValues = {};
6006
+ if (envFilePath) {
6007
+ if (!fs5.existsSync(envFilePath)) {
6008
+ throw new Error(`Environment file not found: ${envFilePath}`);
6009
+ }
6002
6010
  const dotenvResult = dotenvConfig({ path: envFilePath, quiet: true });
6003
6011
  if (dotenvResult.parsed) {
6004
- dotenvValues = Object.fromEntries(
6012
+ fileValues = Object.fromEntries(
6005
6013
  Object.entries(dotenvResult.parsed).filter(
6006
6014
  ([key]) => missingNames.includes(key)
6007
6015
  )
6008
6016
  );
6009
6017
  }
6010
6018
  }
6011
- const envValues = {};
6012
- for (const name of missingNames) {
6013
- const envValue = process.env[name];
6014
- if (envValue !== void 0) {
6015
- envValues[name] = envValue;
6016
- }
6017
- }
6018
- Object.assign(result, dotenvValues, envValues);
6019
+ Object.assign(result, envValues, fileValues);
6019
6020
  }
6020
6021
  return Object.keys(result).length > 0 ? result : void 0;
6021
6022
  }
@@ -6130,7 +6131,7 @@ async function pollEvents(runId, options) {
6130
6131
  result = { succeeded: false, runId };
6131
6132
  }
6132
6133
  if (!complete) {
6133
- await new Promise((resolve2) => setTimeout(resolve2, pollIntervalMs));
6134
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
6134
6135
  }
6135
6136
  }
6136
6137
  return result;
@@ -6173,13 +6174,16 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
6173
6174
  "<agent-name>",
6174
6175
  "Agent reference: [scope/]name[:version] (e.g., 'my-agent', 'lancy/my-agent:abc123', 'my-agent:latest')"
6175
6176
  ).argument("<prompt>", "Prompt for the agent").option(
6177
+ "--env-file <path>",
6178
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
6179
+ ).option(
6176
6180
  "--vars <KEY=value>",
6177
- "Variables for ${{ vars.xxx }} (repeatable, falls back to env vars and .env)",
6181
+ "Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
6178
6182
  collectKeyValue,
6179
6183
  {}
6180
6184
  ).option(
6181
6185
  "--secrets <KEY=value>",
6182
- "Secrets for ${{ secrets.xxx }} (repeatable, falls back to env vars and .env)",
6186
+ "Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
6183
6187
  collectKeyValue,
6184
6188
  {}
6185
6189
  ).option("--artifact-name <name>", "Artifact storage name (required for run)").option(
@@ -6256,9 +6260,13 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
6256
6260
  }
6257
6261
  }
6258
6262
  const varNames = extractVarNames(composeContent);
6259
- const vars = loadValues(options.vars, varNames);
6263
+ const vars = loadValues(options.vars, varNames, options.envFile);
6260
6264
  const secretNames = extractSecretNames(composeContent);
6261
- const secrets = loadValues(options.secrets, secretNames);
6265
+ const secrets = loadValues(
6266
+ options.secrets,
6267
+ secretNames,
6268
+ options.envFile
6269
+ );
6262
6270
  if (verbose && varNames.length > 0) {
6263
6271
  console.log(chalk6.dim(` Required vars: ${varNames.join(", ")}`));
6264
6272
  if (vars) {
@@ -6350,6 +6358,8 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
6350
6358
  console.error(
6351
6359
  chalk6.dim(" Make sure the version hash is correct.")
6352
6360
  );
6361
+ } else if (error.message.startsWith("Environment file not found:")) {
6362
+ console.error(chalk6.red(`\u2717 ${error.message}`));
6353
6363
  } else if (error.message.includes("not found")) {
6354
6364
  console.error(chalk6.red(`\u2717 Agent not found: ${identifier}`));
6355
6365
  console.error(
@@ -6373,13 +6383,16 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
6373
6383
  import { Command as Command3, Option as Option2 } from "commander";
6374
6384
  import chalk7 from "chalk";
6375
6385
  var resumeCommand = new Command3().name("resume").description("Resume an agent run from a checkpoint (uses all snapshot data)").argument("<checkpointId>", "Checkpoint ID to resume from").argument("<prompt>", "Prompt for the resumed agent").option(
6386
+ "--env-file <path>",
6387
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
6388
+ ).option(
6376
6389
  "--vars <KEY=value>",
6377
- "Variables for ${{ vars.xxx }} (repeatable, falls back to env vars and .env)",
6390
+ "Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
6378
6391
  collectKeyValue,
6379
6392
  {}
6380
6393
  ).option(
6381
6394
  "--secrets <KEY=value>",
6382
- "Secrets for ${{ secrets.xxx }} (repeatable, required for resume)",
6395
+ "Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
6383
6396
  collectKeyValue,
6384
6397
  {}
6385
6398
  ).option(
@@ -6411,7 +6424,8 @@ var resumeCommand = new Command3().name("resume").description("Resume an agent r
6411
6424
  }
6412
6425
  const checkpointInfo = await getCheckpoint(checkpointId);
6413
6426
  const requiredSecretNames = checkpointInfo.agentComposeSnapshot.secretNames || [];
6414
- const loadedSecrets = loadValues(secrets, requiredSecretNames);
6427
+ const envFile = options.envFile || allOpts.envFile;
6428
+ const loadedSecrets = loadValues(secrets, requiredSecretNames, envFile);
6415
6429
  if (verbose) {
6416
6430
  logVerbosePreFlight("Resuming agent run from checkpoint", [
6417
6431
  { label: "Checkpoint ID", value: checkpointId },
@@ -6474,6 +6488,8 @@ var resumeCommand = new Command3().name("resume").description("Resume an agent r
6474
6488
  console.error(
6475
6489
  chalk7.dim(" Try running without --experimental-realtime")
6476
6490
  );
6491
+ } else if (error.message.startsWith("Environment file not found:")) {
6492
+ console.error(chalk7.red(`\u2717 ${error.message}`));
6477
6493
  } else if (error.message.includes("not found")) {
6478
6494
  console.error(chalk7.red(`\u2717 Checkpoint not found: ${checkpointId}`));
6479
6495
  } else {
@@ -6494,13 +6510,16 @@ import chalk8 from "chalk";
6494
6510
  var continueCommand = new Command4().name("continue").description(
6495
6511
  "Continue an agent run from a session (uses latest artifact version)"
6496
6512
  ).argument("<agentSessionId>", "Agent session ID to continue from").argument("<prompt>", "Prompt for the continued agent").option(
6513
+ "--env-file <path>",
6514
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
6515
+ ).option(
6497
6516
  "--vars <KEY=value>",
6498
- "Variables for ${{ vars.xxx }} (repeatable, falls back to env vars and .env)",
6517
+ "Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
6499
6518
  collectKeyValue,
6500
6519
  {}
6501
6520
  ).option(
6502
6521
  "--secrets <KEY=value>",
6503
- "Secrets for ${{ secrets.xxx }} (repeatable, required for continue)",
6522
+ "Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
6504
6523
  collectKeyValue,
6505
6524
  {}
6506
6525
  ).option(
@@ -6532,7 +6551,8 @@ var continueCommand = new Command4().name("continue").description(
6532
6551
  }
6533
6552
  const sessionInfo = await getSession(agentSessionId);
6534
6553
  const requiredSecretNames = sessionInfo.secretNames || [];
6535
- const loadedSecrets = loadValues(secrets, requiredSecretNames);
6554
+ const envFile = options.envFile || allOpts.envFile;
6555
+ const loadedSecrets = loadValues(secrets, requiredSecretNames, envFile);
6536
6556
  if (verbose) {
6537
6557
  logVerbosePreFlight("Continuing agent run from session", [
6538
6558
  { label: "Session ID", value: agentSessionId },
@@ -6596,6 +6616,8 @@ var continueCommand = new Command4().name("continue").description(
6596
6616
  console.error(
6597
6617
  chalk8.dim(" Try running without --experimental-realtime")
6598
6618
  );
6619
+ } else if (error.message.startsWith("Environment file not found:")) {
6620
+ console.error(chalk8.red(`\u2717 ${error.message}`));
6599
6621
  } else if (error.message.includes("not found")) {
6600
6622
  console.error(
6601
6623
  chalk8.red(`\u2717 Agent session not found: ${agentSessionId}`)
@@ -6623,13 +6645,13 @@ import { Command as Command11 } from "commander";
6623
6645
  // src/commands/volume/init.ts
6624
6646
  import { Command as Command5 } from "commander";
6625
6647
  import chalk9 from "chalk";
6626
- import path7 from "path";
6648
+ import path6 from "path";
6627
6649
 
6628
6650
  // src/lib/storage/storage-utils.ts
6629
6651
  import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
6630
6652
  import { existsSync as existsSync5 } from "fs";
6631
6653
  import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
6632
- import path6 from "path";
6654
+ import path5 from "path";
6633
6655
  var CONFIG_DIR2 = ".vm0";
6634
6656
  var CONFIG_FILE2 = "storage.yaml";
6635
6657
  function isValidStorageName(name) {
@@ -6640,8 +6662,8 @@ function isValidStorageName(name) {
6640
6662
  return pattern.test(name) && !name.includes("--");
6641
6663
  }
6642
6664
  async function readStorageConfig(basePath = process.cwd()) {
6643
- const configPath = path6.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
6644
- const legacyConfigPath = path6.join(basePath, CONFIG_DIR2, "volume.yaml");
6665
+ const configPath = path5.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
6666
+ const legacyConfigPath = path5.join(basePath, CONFIG_DIR2, "volume.yaml");
6645
6667
  let actualPath = null;
6646
6668
  if (existsSync5(configPath)) {
6647
6669
  actualPath = configPath;
@@ -6659,8 +6681,8 @@ async function readStorageConfig(basePath = process.cwd()) {
6659
6681
  return config;
6660
6682
  }
6661
6683
  async function writeStorageConfig(storageName, basePath = process.cwd(), type = "volume") {
6662
- const configDir = path6.join(basePath, CONFIG_DIR2);
6663
- const configPath = path6.join(configDir, CONFIG_FILE2);
6684
+ const configDir = path5.join(basePath, CONFIG_DIR2);
6685
+ const configPath = path5.join(configDir, CONFIG_FILE2);
6664
6686
  if (!existsSync5(configDir)) {
6665
6687
  await mkdir4(configDir, { recursive: true });
6666
6688
  }
@@ -6741,14 +6763,14 @@ async function promptSelect(message, choices, initial) {
6741
6763
  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) => {
6742
6764
  try {
6743
6765
  const cwd = process.cwd();
6744
- const dirName = path7.basename(cwd);
6766
+ const dirName = path6.basename(cwd);
6745
6767
  const existingConfig = await readStorageConfig(cwd);
6746
6768
  if (existingConfig) {
6747
6769
  console.log(
6748
6770
  chalk9.yellow(`Volume already initialized: ${existingConfig.name}`)
6749
6771
  );
6750
6772
  console.log(
6751
- chalk9.dim(`Config file: ${path7.join(cwd, ".vm0", "storage.yaml")}`)
6773
+ chalk9.dim(`Config file: ${path6.join(cwd, ".vm0", "storage.yaml")}`)
6752
6774
  );
6753
6775
  return;
6754
6776
  }
@@ -6797,7 +6819,7 @@ var initCommand = new Command5().name("init").description("Initialize a volume i
6797
6819
  console.log(chalk9.green(`\u2713 Initialized volume: ${volumeName}`));
6798
6820
  console.log(
6799
6821
  chalk9.dim(
6800
- `\u2713 Config saved to ${path7.join(cwd, ".vm0", "storage.yaml")}`
6822
+ `\u2713 Config saved to ${path6.join(cwd, ".vm0", "storage.yaml")}`
6801
6823
  )
6802
6824
  );
6803
6825
  } catch (error) {
@@ -6865,7 +6887,7 @@ var pushCommand = new Command6().name("push").description("Push local files to c
6865
6887
  // src/commands/volume/pull.ts
6866
6888
  import { Command as Command7 } from "commander";
6867
6889
  import chalk12 from "chalk";
6868
- import path8 from "path";
6890
+ import path7 from "path";
6869
6891
  import * as fs6 from "fs";
6870
6892
  import * as os4 from "os";
6871
6893
  import * as tar3 from "tar";
@@ -6926,8 +6948,8 @@ var pullCommand = new Command7().name("pull").description("Pull cloud files to l
6926
6948
  const arrayBuffer = await s3Response.arrayBuffer();
6927
6949
  const tarBuffer = Buffer.from(arrayBuffer);
6928
6950
  console.log(chalk12.green(`\u2713 Downloaded ${formatBytes3(tarBuffer.length)}`));
6929
- const tmpDir = fs6.mkdtempSync(path8.join(os4.tmpdir(), "vm0-"));
6930
- const tarPath = path8.join(tmpDir, "volume.tar.gz");
6951
+ const tmpDir = fs6.mkdtempSync(path7.join(os4.tmpdir(), "vm0-"));
6952
+ const tarPath = path7.join(tmpDir, "volume.tar.gz");
6931
6953
  await fs6.promises.writeFile(tarPath, tarBuffer);
6932
6954
  console.log(chalk12.dim("Syncing local files..."));
6933
6955
  const remoteFiles = await listTarFiles(tarPath);
@@ -7080,7 +7102,7 @@ import chalk16 from "chalk";
7080
7102
 
7081
7103
  // src/lib/storage/clone-utils.ts
7082
7104
  import chalk15 from "chalk";
7083
- import path9 from "path";
7105
+ import path8 from "path";
7084
7106
  import * as fs7 from "fs";
7085
7107
  import * as os5 from "os";
7086
7108
  import * as tar4 from "tar";
@@ -7121,8 +7143,8 @@ async function cloneStorage(name, type, destination, options = {}) {
7121
7143
  const arrayBuffer = await s3Response.arrayBuffer();
7122
7144
  const tarBuffer = Buffer.from(arrayBuffer);
7123
7145
  console.log(chalk15.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
7124
- const tmpDir = fs7.mkdtempSync(path9.join(os5.tmpdir(), "vm0-clone-"));
7125
- const tarPath = path9.join(tmpDir, "archive.tar.gz");
7146
+ const tmpDir = fs7.mkdtempSync(path8.join(os5.tmpdir(), "vm0-clone-"));
7147
+ const tarPath = path8.join(tmpDir, "archive.tar.gz");
7126
7148
  await fs7.promises.writeFile(tarPath, tarBuffer);
7127
7149
  const files = await listTarFiles(tarPath);
7128
7150
  console.log(chalk15.dim("Extracting files..."));
@@ -7176,14 +7198,14 @@ import { Command as Command18 } from "commander";
7176
7198
  // src/commands/artifact/init.ts
7177
7199
  import { Command as Command12 } from "commander";
7178
7200
  import chalk17 from "chalk";
7179
- import path10 from "path";
7201
+ import path9 from "path";
7180
7202
  var initCommand2 = new Command12().name("init").description("Initialize an artifact in the current directory").option(
7181
7203
  "-n, --name <name>",
7182
7204
  "Artifact name (required in non-interactive mode)"
7183
7205
  ).action(async (options) => {
7184
7206
  try {
7185
7207
  const cwd = process.cwd();
7186
- const dirName = path10.basename(cwd);
7208
+ const dirName = path9.basename(cwd);
7187
7209
  const existingConfig = await readStorageConfig(cwd);
7188
7210
  if (existingConfig) {
7189
7211
  if (existingConfig.type === "artifact") {
@@ -7205,7 +7227,7 @@ var initCommand2 = new Command12().name("init").description("Initialize an artif
7205
7227
  );
7206
7228
  }
7207
7229
  console.log(
7208
- chalk17.dim(`Config file: ${path10.join(cwd, ".vm0", "storage.yaml")}`)
7230
+ chalk17.dim(`Config file: ${path9.join(cwd, ".vm0", "storage.yaml")}`)
7209
7231
  );
7210
7232
  return;
7211
7233
  }
@@ -7254,7 +7276,7 @@ var initCommand2 = new Command12().name("init").description("Initialize an artif
7254
7276
  console.log(chalk17.green(`\u2713 Initialized artifact: ${artifactName}`));
7255
7277
  console.log(
7256
7278
  chalk17.dim(
7257
- `\u2713 Config saved to ${path10.join(cwd, ".vm0", "storage.yaml")}`
7279
+ `\u2713 Config saved to ${path9.join(cwd, ".vm0", "storage.yaml")}`
7258
7280
  )
7259
7281
  );
7260
7282
  } catch (error) {
@@ -7327,7 +7349,7 @@ var pushCommand2 = new Command13().name("push").description("Push local files to
7327
7349
  // src/commands/artifact/pull.ts
7328
7350
  import { Command as Command14 } from "commander";
7329
7351
  import chalk19 from "chalk";
7330
- import path11 from "path";
7352
+ import path10 from "path";
7331
7353
  import * as fs8 from "fs";
7332
7354
  import * as os6 from "os";
7333
7355
  import * as tar5 from "tar";
@@ -7383,8 +7405,8 @@ var pullCommand2 = new Command14().name("pull").description("Pull cloud artifact
7383
7405
  const arrayBuffer = await s3Response.arrayBuffer();
7384
7406
  const tarBuffer = Buffer.from(arrayBuffer);
7385
7407
  console.log(chalk19.green(`\u2713 Downloaded ${formatBytes6(tarBuffer.length)}`));
7386
- const tmpDir = fs8.mkdtempSync(path11.join(os6.tmpdir(), "vm0-"));
7387
- const tarPath = path11.join(tmpDir, "artifact.tar.gz");
7408
+ const tmpDir = fs8.mkdtempSync(path10.join(os6.tmpdir(), "vm0-"));
7409
+ const tarPath = path10.join(tmpDir, "artifact.tar.gz");
7388
7410
  await fs8.promises.writeFile(tarPath, tarBuffer);
7389
7411
  console.log(chalk19.dim("Syncing local files..."));
7390
7412
  const remoteFiles = await listTarFiles(tarPath);
@@ -7556,9 +7578,9 @@ var artifactCommand = new Command18().name("artifact").description("Manage artif
7556
7578
  // src/commands/cook.ts
7557
7579
  import { Command as Command19, Option as Option4 } from "commander";
7558
7580
  import chalk24 from "chalk";
7559
- import { readFile as readFile7, mkdir as mkdir6, writeFile as writeFile6, appendFile } from "fs/promises";
7560
- import { existsSync as existsSync8, readFileSync } from "fs";
7561
- import path12 from "path";
7581
+ import { readFile as readFile7, mkdir as mkdir6 } from "fs/promises";
7582
+ import { existsSync as existsSync8 } from "fs";
7583
+ import path11 from "path";
7562
7584
  import { spawn as spawn2 } from "child_process";
7563
7585
  import { parse as parseYaml4 } from "yaml";
7564
7586
  import { config as dotenvConfig2 } from "dotenv";
@@ -7630,7 +7652,7 @@ async function getLatestVersion() {
7630
7652
  }
7631
7653
  }
7632
7654
  function performUpgrade(packageManager) {
7633
- return new Promise((resolve2) => {
7655
+ return new Promise((resolve) => {
7634
7656
  const isWindows = process.platform === "win32";
7635
7657
  const command = isWindows ? `${packageManager}.cmd` : packageManager;
7636
7658
  const args = packageManager === "pnpm" ? ["add", "-g", `${PACKAGE_NAME}@latest`] : ["install", "-g", `${PACKAGE_NAME}@latest`];
@@ -7639,10 +7661,10 @@ function performUpgrade(packageManager) {
7639
7661
  shell: isWindows
7640
7662
  });
7641
7663
  child.on("close", (code) => {
7642
- resolve2(code === 0);
7664
+ resolve(code === 0);
7643
7665
  });
7644
7666
  child.on("error", () => {
7645
- resolve2(false);
7667
+ resolve(false);
7646
7668
  });
7647
7669
  });
7648
7670
  }
@@ -7774,7 +7796,7 @@ function printCommand(cmd) {
7774
7796
  console.log(chalk24.dim(`> ${cmd}`));
7775
7797
  }
7776
7798
  function execVm0Command(args, options = {}) {
7777
- return new Promise((resolve2, reject) => {
7799
+ return new Promise((resolve, reject) => {
7778
7800
  const stdio = options.silent ? "pipe" : "inherit";
7779
7801
  const proc = spawn2("vm0", args, {
7780
7802
  cwd: options.cwd,
@@ -7793,7 +7815,7 @@ function execVm0Command(args, options = {}) {
7793
7815
  }
7794
7816
  proc.on("close", (code) => {
7795
7817
  if (code === 0) {
7796
- resolve2(stdout);
7818
+ resolve(stdout);
7797
7819
  } else {
7798
7820
  reject(new Error(stderr || `Command failed with exit code ${code}`));
7799
7821
  }
@@ -7804,7 +7826,7 @@ function execVm0Command(args, options = {}) {
7804
7826
  });
7805
7827
  }
7806
7828
  function execVm0RunWithCapture(args, options = {}) {
7807
- return new Promise((resolve2, reject) => {
7829
+ return new Promise((resolve, reject) => {
7808
7830
  const proc = spawn2("vm0", args, {
7809
7831
  cwd: options.cwd,
7810
7832
  stdio: ["inherit", "pipe", "pipe"],
@@ -7824,7 +7846,7 @@ function execVm0RunWithCapture(args, options = {}) {
7824
7846
  });
7825
7847
  proc.on("close", (code) => {
7826
7848
  if (code === 0) {
7827
- resolve2(stdout);
7849
+ resolve(stdout);
7828
7850
  } else {
7829
7851
  reject(new Error(stderr || `Command failed with exit code ${code}`));
7830
7852
  }
@@ -7871,36 +7893,26 @@ function extractRequiredVarNames(config) {
7871
7893
  return [.../* @__PURE__ */ new Set([...varNames, ...secretNames])];
7872
7894
  }
7873
7895
  function checkMissingVariables(varNames, envFilePath) {
7874
- let dotenvValues = {};
7875
- if (existsSync8(envFilePath)) {
7896
+ let fileValues = {};
7897
+ if (envFilePath) {
7898
+ if (!existsSync8(envFilePath)) {
7899
+ throw new Error(`Environment file not found: ${envFilePath}`);
7900
+ }
7876
7901
  const result = dotenvConfig2({ path: envFilePath, quiet: true });
7877
7902
  if (result.parsed) {
7878
- dotenvValues = result.parsed;
7903
+ fileValues = result.parsed;
7879
7904
  }
7880
7905
  }
7881
7906
  const missing = [];
7882
7907
  for (const name of varNames) {
7883
7908
  const inEnv = process.env[name] !== void 0;
7884
- const inDotenv = dotenvValues[name] !== void 0;
7885
- if (!inEnv && !inDotenv) {
7909
+ const inFile = fileValues[name] !== void 0;
7910
+ if (!inEnv && !inFile) {
7886
7911
  missing.push(name);
7887
7912
  }
7888
7913
  }
7889
7914
  return missing;
7890
7915
  }
7891
- async function generateEnvPlaceholders(missingVars, envFilePath) {
7892
- const placeholders = missingVars.map((name) => `${name}=`).join("\n");
7893
- if (existsSync8(envFilePath)) {
7894
- const existingContent = readFileSync(envFilePath, "utf8");
7895
- const needsNewline = existingContent.length > 0 && !existingContent.endsWith("\n");
7896
- const prefix = needsNewline ? "\n" : "";
7897
- await appendFile(envFilePath, `${prefix}${placeholders}
7898
- `);
7899
- } else {
7900
- await writeFile6(envFilePath, `${placeholders}
7901
- `);
7902
- }
7903
- }
7904
7916
  async function autoPullArtifact(runOutput, artifactDir) {
7905
7917
  const serverVersion = parseArtifactVersionFromCompletion(
7906
7918
  runOutput,
@@ -7926,11 +7938,14 @@ async function autoPullArtifact(runOutput, artifactDir) {
7926
7938
  }
7927
7939
  }
7928
7940
  var cookCmd = new Command19().name("cook").description("Quick start: prepare, compose and run agent from vm0.yaml");
7929
- cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip confirmation prompts").addOption(new Option4("--debug-no-mock-claude").hideHelp()).addOption(new Option4("--no-auto-update").hideHelp()).action(
7941
+ cookCmd.argument("[prompt]", "Prompt for the agent").option(
7942
+ "--env-file <path>",
7943
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
7944
+ ).option("-y, --yes", "Skip confirmation prompts").addOption(new Option4("--debug-no-mock-claude").hideHelp()).addOption(new Option4("--no-auto-update").hideHelp()).action(
7930
7945
  // eslint-disable-next-line complexity -- TODO: refactor complex function
7931
7946
  async (prompt, options) => {
7932
7947
  if (!options.noAutoUpdate) {
7933
- const shouldExit = await checkAndUpgrade("7.1.0", prompt);
7948
+ const shouldExit = await checkAndUpgrade("8.0.0", prompt);
7934
7949
  if (shouldExit) {
7935
7950
  process.exit(0);
7936
7951
  }
@@ -7965,21 +7980,27 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
7965
7980
  );
7966
7981
  const requiredVarNames = extractRequiredVarNames(config);
7967
7982
  if (requiredVarNames.length > 0) {
7968
- const envFilePath = path12.join(cwd, ".env");
7969
- const missingVars = checkMissingVariables(
7970
- requiredVarNames,
7971
- envFilePath
7972
- );
7973
- if (missingVars.length > 0) {
7974
- await generateEnvPlaceholders(missingVars, envFilePath);
7975
- console.log();
7976
- console.log(
7977
- chalk24.yellow(
7978
- `\u26A0 Missing environment variables. Please fill in values in .env file:`
7979
- )
7983
+ try {
7984
+ const missingVars = checkMissingVariables(
7985
+ requiredVarNames,
7986
+ options.envFile
7980
7987
  );
7981
- for (const varName of missingVars) {
7982
- console.log(chalk24.yellow(` ${varName}`));
7988
+ if (missingVars.length > 0) {
7989
+ console.log();
7990
+ console.error(chalk24.red("\u2717 Missing required variables:"));
7991
+ for (const varName of missingVars) {
7992
+ console.error(chalk24.red(` ${varName}`));
7993
+ }
7994
+ console.error(
7995
+ chalk24.dim(
7996
+ "\n Provide via --env-file, or set as environment variables"
7997
+ )
7998
+ );
7999
+ process.exit(1);
8000
+ }
8001
+ } catch (error) {
8002
+ if (error instanceof Error) {
8003
+ console.error(chalk24.red(`\u2717 ${error.message}`));
7983
8004
  }
7984
8005
  process.exit(1);
7985
8006
  }
@@ -7988,7 +8009,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
7988
8009
  console.log();
7989
8010
  console.log(chalk24.bold("Processing volumes:"));
7990
8011
  for (const volumeConfig of Object.values(config.volumes)) {
7991
- const volumeDir = path12.join(cwd, volumeConfig.name);
8012
+ const volumeDir = path11.join(cwd, volumeConfig.name);
7992
8013
  if (!existsSync8(volumeDir)) {
7993
8014
  console.error(
7994
8015
  chalk24.red(
@@ -8027,7 +8048,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
8027
8048
  }
8028
8049
  console.log();
8029
8050
  console.log(chalk24.bold("Processing artifact:"));
8030
- const artifactDir = path12.join(cwd, ARTIFACT_DIR);
8051
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
8031
8052
  try {
8032
8053
  if (!existsSync8(artifactDir)) {
8033
8054
  printCommand(`mkdir ${ARTIFACT_DIR}`);
@@ -8156,80 +8177,98 @@ cookCmd.command("logs").description("View logs from the last cook run").option("
8156
8177
  );
8157
8178
  cookCmd.command("continue").description(
8158
8179
  "Continue from the last session (latest conversation and artifact)"
8159
- ).argument("<prompt>", "Prompt for the continued agent").addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(async (prompt, options) => {
8160
- const state = await loadCookState();
8161
- if (!state.lastSessionId) {
8162
- console.error(chalk24.red("\u2717 No previous session found"));
8163
- console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
8164
- process.exit(1);
8165
- }
8166
- const cwd = process.cwd();
8167
- const artifactDir = path12.join(cwd, ARTIFACT_DIR);
8168
- printCommand(`vm0 run continue ${state.lastSessionId} "${prompt}"`);
8169
- console.log();
8170
- let runOutput;
8171
- try {
8172
- runOutput = await execVm0RunWithCapture(
8173
- [
8174
- "run",
8175
- "continue",
8176
- state.lastSessionId,
8177
- ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
8178
- prompt
8179
- ],
8180
- { cwd }
8180
+ ).argument("<prompt>", "Prompt for the continued agent").option(
8181
+ "--env-file <path>",
8182
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
8183
+ ).addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(
8184
+ async (prompt, options) => {
8185
+ const state = await loadCookState();
8186
+ if (!state.lastSessionId) {
8187
+ console.error(chalk24.red("\u2717 No previous session found"));
8188
+ console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
8189
+ process.exit(1);
8190
+ }
8191
+ const cwd = process.cwd();
8192
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
8193
+ const envFileArg = options.envFile ? ` --env-file ${options.envFile}` : "";
8194
+ printCommand(
8195
+ `vm0 run continue${envFileArg} ${state.lastSessionId} "${prompt}"`
8181
8196
  );
8182
- } catch {
8183
- process.exit(1);
8184
- }
8185
- const newIds = parseRunIdsFromOutput(runOutput);
8186
- if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
8187
- await saveCookState({
8188
- lastRunId: newIds.runId,
8189
- lastSessionId: newIds.sessionId,
8190
- lastCheckpointId: newIds.checkpointId
8191
- });
8197
+ console.log();
8198
+ let runOutput;
8199
+ try {
8200
+ runOutput = await execVm0RunWithCapture(
8201
+ [
8202
+ "run",
8203
+ "continue",
8204
+ ...options.envFile ? ["--env-file", options.envFile] : [],
8205
+ state.lastSessionId,
8206
+ ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
8207
+ prompt
8208
+ ],
8209
+ { cwd }
8210
+ );
8211
+ } catch {
8212
+ process.exit(1);
8213
+ }
8214
+ const newIds = parseRunIdsFromOutput(runOutput);
8215
+ if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
8216
+ await saveCookState({
8217
+ lastRunId: newIds.runId,
8218
+ lastSessionId: newIds.sessionId,
8219
+ lastCheckpointId: newIds.checkpointId
8220
+ });
8221
+ }
8222
+ await autoPullArtifact(runOutput, artifactDir);
8192
8223
  }
8193
- await autoPullArtifact(runOutput, artifactDir);
8194
- });
8224
+ );
8195
8225
  cookCmd.command("resume").description(
8196
8226
  "Resume from the last checkpoint (snapshotted conversation and artifact)"
8197
- ).argument("<prompt>", "Prompt for the resumed agent").addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(async (prompt, options) => {
8198
- const state = await loadCookState();
8199
- if (!state.lastCheckpointId) {
8200
- console.error(chalk24.red("\u2717 No previous checkpoint found"));
8201
- console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
8202
- process.exit(1);
8203
- }
8204
- const cwd = process.cwd();
8205
- const artifactDir = path12.join(cwd, ARTIFACT_DIR);
8206
- printCommand(`vm0 run resume ${state.lastCheckpointId} "${prompt}"`);
8207
- console.log();
8208
- let runOutput;
8209
- try {
8210
- runOutput = await execVm0RunWithCapture(
8211
- [
8212
- "run",
8213
- "resume",
8214
- state.lastCheckpointId,
8215
- ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
8216
- prompt
8217
- ],
8218
- { cwd }
8227
+ ).argument("<prompt>", "Prompt for the resumed agent").option(
8228
+ "--env-file <path>",
8229
+ "Load environment variables from file (priority: CLI flags > file > env vars)"
8230
+ ).addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(
8231
+ async (prompt, options) => {
8232
+ const state = await loadCookState();
8233
+ if (!state.lastCheckpointId) {
8234
+ console.error(chalk24.red("\u2717 No previous checkpoint found"));
8235
+ console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
8236
+ process.exit(1);
8237
+ }
8238
+ const cwd = process.cwd();
8239
+ const artifactDir = path11.join(cwd, ARTIFACT_DIR);
8240
+ const envFileArg = options.envFile ? ` --env-file ${options.envFile}` : "";
8241
+ printCommand(
8242
+ `vm0 run resume${envFileArg} ${state.lastCheckpointId} "${prompt}"`
8219
8243
  );
8220
- } catch {
8221
- process.exit(1);
8222
- }
8223
- const newIds = parseRunIdsFromOutput(runOutput);
8224
- if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
8225
- await saveCookState({
8226
- lastRunId: newIds.runId,
8227
- lastSessionId: newIds.sessionId,
8228
- lastCheckpointId: newIds.checkpointId
8229
- });
8244
+ console.log();
8245
+ let runOutput;
8246
+ try {
8247
+ runOutput = await execVm0RunWithCapture(
8248
+ [
8249
+ "run",
8250
+ "resume",
8251
+ ...options.envFile ? ["--env-file", options.envFile] : [],
8252
+ state.lastCheckpointId,
8253
+ ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
8254
+ prompt
8255
+ ],
8256
+ { cwd }
8257
+ );
8258
+ } catch {
8259
+ process.exit(1);
8260
+ }
8261
+ const newIds = parseRunIdsFromOutput(runOutput);
8262
+ if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
8263
+ await saveCookState({
8264
+ lastRunId: newIds.runId,
8265
+ lastSessionId: newIds.sessionId,
8266
+ lastCheckpointId: newIds.checkpointId
8267
+ });
8268
+ }
8269
+ await autoPullArtifact(runOutput, artifactDir);
8230
8270
  }
8231
- await autoPullArtifact(runOutput, artifactDir);
8232
- });
8271
+ );
8233
8272
  var cookCommand = cookCmd;
8234
8273
 
8235
8274
  // src/commands/logs/index.ts
@@ -8659,13 +8698,13 @@ var listCommand3 = new Command24().name("list").alias("ls").description("List al
8659
8698
  }
8660
8699
  });
8661
8700
 
8662
- // src/commands/agent/inspect.ts
8701
+ // src/commands/agent/status.ts
8663
8702
  import { Command as Command25 } from "commander";
8664
8703
  import chalk29 from "chalk";
8665
8704
 
8666
8705
  // src/lib/domain/source-derivation.ts
8667
8706
  import * as fs9 from "fs/promises";
8668
- import * as path13 from "path";
8707
+ import * as path12 from "path";
8669
8708
  import * as os7 from "os";
8670
8709
  function extractVariableReferences2(environment) {
8671
8710
  const secrets = [];
@@ -8715,7 +8754,7 @@ async function deriveAgentVariableSources(agent, options) {
8715
8754
  };
8716
8755
  }
8717
8756
  const tempDir = await fs9.mkdtemp(
8718
- path13.join(os7.tmpdir(), "vm0-source-derivation-")
8757
+ path12.join(os7.tmpdir(), "vm0-source-derivation-")
8719
8758
  );
8720
8759
  try {
8721
8760
  const skillResults = await Promise.all(
@@ -8769,67 +8808,72 @@ async function deriveComposeVariableSources(content, options) {
8769
8808
  return results;
8770
8809
  }
8771
8810
 
8772
- // src/commands/agent/inspect.ts
8811
+ // src/commands/agent/status.ts
8812
+ function formatListSection(label, items) {
8813
+ if (items.length === 0) return;
8814
+ console.log(` ${label}:`);
8815
+ for (const item of items) {
8816
+ console.log(` - ${item}`);
8817
+ }
8818
+ }
8819
+ function formatVolumes(volumes, volumeConfigs) {
8820
+ if (volumes.length === 0) return;
8821
+ console.log(` Volumes:`);
8822
+ for (const vol of volumes) {
8823
+ const volumeDef = volumeConfigs?.[vol];
8824
+ if (volumeDef) {
8825
+ console.log(` - ${vol}:${volumeDef.version.slice(0, 8)}`);
8826
+ } else {
8827
+ console.log(` - ${vol}`);
8828
+ }
8829
+ }
8830
+ }
8831
+ function formatVariableSources(sources) {
8832
+ if (sources.secrets.length > 0) {
8833
+ console.log(` Secrets:`);
8834
+ for (const secret of sources.secrets) {
8835
+ const sourceInfo = chalk29.dim(`(${secret.source})`);
8836
+ console.log(` - ${secret.name.padEnd(20)} ${sourceInfo}`);
8837
+ }
8838
+ }
8839
+ if (sources.vars.length > 0) {
8840
+ console.log(` Vars:`);
8841
+ for (const v of sources.vars) {
8842
+ const sourceInfo = chalk29.dim(`(${v.source})`);
8843
+ console.log(` - ${v.name.padEnd(20)} ${sourceInfo}`);
8844
+ }
8845
+ }
8846
+ }
8847
+ function formatAgentDetails(agentName, agent, agentSources, volumeConfigs) {
8848
+ console.log(` ${chalk29.cyan(agentName)}:`);
8849
+ console.log(` Framework: ${agent.framework}`);
8850
+ if (agent.image) {
8851
+ console.log(` Image: ${agent.image}`);
8852
+ }
8853
+ formatListSection("Apps", agent.apps ?? []);
8854
+ if (agent.working_dir) {
8855
+ console.log(` Working Dir: ${agent.working_dir}`);
8856
+ }
8857
+ formatVolumes(agent.volumes ?? [], volumeConfigs);
8858
+ formatListSection("Skills", agent.skills ?? []);
8859
+ if (agentSources) {
8860
+ formatVariableSources(agentSources);
8861
+ }
8862
+ if (agent.experimental_runner) {
8863
+ console.log(` Runner: ${agent.experimental_runner.group}`);
8864
+ }
8865
+ }
8773
8866
  function formatComposeOutput(name, versionId, content, variableSources) {
8774
8867
  console.log(chalk29.bold("Name:") + ` ${name}`);
8775
8868
  console.log(chalk29.bold("Version:") + ` ${versionId}`);
8776
8869
  console.log();
8777
8870
  console.log(chalk29.bold("Agents:"));
8778
8871
  for (const [agentName, agent] of Object.entries(content.agents)) {
8779
- console.log(` ${chalk29.cyan(agentName)}:`);
8780
- console.log(` Framework: ${agent.framework}`);
8781
- if (agent.image) {
8782
- console.log(` Image: ${agent.image}`);
8783
- }
8784
- if (agent.apps && agent.apps.length > 0) {
8785
- console.log(` Apps:`);
8786
- for (const app of agent.apps) {
8787
- console.log(` - ${app}`);
8788
- }
8789
- }
8790
- if (agent.working_dir) {
8791
- console.log(` Working Dir: ${agent.working_dir}`);
8792
- }
8793
- if (agent.volumes && agent.volumes.length > 0) {
8794
- console.log(` Volumes:`);
8795
- for (const vol of agent.volumes) {
8796
- const volumeDef = content.volumes?.[vol];
8797
- if (volumeDef) {
8798
- console.log(` - ${vol}:${volumeDef.version.slice(0, 8)}`);
8799
- } else {
8800
- console.log(` - ${vol}`);
8801
- }
8802
- }
8803
- }
8804
- if (agent.skills && agent.skills.length > 0) {
8805
- console.log(` Skills:`);
8806
- for (const skill of agent.skills) {
8807
- console.log(` - ${skill}`);
8808
- }
8809
- }
8810
8872
  const agentSources = variableSources?.get(agentName);
8811
- if (agentSources) {
8812
- if (agentSources.secrets.length > 0) {
8813
- console.log(` Secrets:`);
8814
- for (const secret of agentSources.secrets) {
8815
- const sourceInfo = chalk29.dim(`(${secret.source})`);
8816
- console.log(` - ${secret.name.padEnd(20)} ${sourceInfo}`);
8817
- }
8818
- }
8819
- if (agentSources.vars.length > 0) {
8820
- console.log(` Vars:`);
8821
- for (const v of agentSources.vars) {
8822
- const sourceInfo = chalk29.dim(`(${v.source})`);
8823
- console.log(` - ${v.name.padEnd(20)} ${sourceInfo}`);
8824
- }
8825
- }
8826
- }
8827
- if (agent.experimental_runner) {
8828
- console.log(` Runner: ${agent.experimental_runner.group}`);
8829
- }
8873
+ formatAgentDetails(agentName, agent, agentSources, content.volumes);
8830
8874
  }
8831
8875
  }
8832
- var inspectCommand = new Command25().name("inspect").description("Inspect an agent compose").argument(
8876
+ var statusCommand4 = new Command25().name("status").description("Show status of agent compose").argument(
8833
8877
  "<name[:version]>",
8834
8878
  "Agent name with optional version (e.g., my-agent:latest or my-agent:a1b2c3d4)"
8835
8879
  ).option("-s, --scope <scope>", "Scope to look up the compose from").option("--no-sources", "Skip fetching skills to determine variable sources").action(
@@ -8897,7 +8941,7 @@ var inspectCommand = new Command25().name("inspect").description("Inspect an age
8897
8941
  variableSources
8898
8942
  );
8899
8943
  } catch (error) {
8900
- console.error(chalk29.red("\u2717 Failed to inspect agent compose"));
8944
+ console.error(chalk29.red("\u2717 Failed to get agent compose status"));
8901
8945
  if (error instanceof Error) {
8902
8946
  if (error.message.includes("Not authenticated")) {
8903
8947
  console.error(chalk29.dim(" Run: vm0 auth login"));
@@ -8911,14 +8955,14 @@ var inspectCommand = new Command25().name("inspect").description("Inspect an age
8911
8955
  );
8912
8956
 
8913
8957
  // src/commands/agent/index.ts
8914
- var agentCommand = new Command26().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(inspectCommand);
8958
+ var agentCommand = new Command26().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(statusCommand4);
8915
8959
 
8916
8960
  // src/commands/init.ts
8917
8961
  import { Command as Command27 } from "commander";
8918
8962
  import chalk30 from "chalk";
8919
- import path14 from "path";
8963
+ import path13 from "path";
8920
8964
  import { existsSync as existsSync9 } from "fs";
8921
- import { writeFile as writeFile7 } from "fs/promises";
8965
+ import { writeFile as writeFile6 } from "fs/promises";
8922
8966
  var VM0_YAML_FILE = "vm0.yaml";
8923
8967
  var AGENTS_MD_FILE = "AGENTS.md";
8924
8968
  function generateVm0Yaml(agentName) {
@@ -8973,7 +9017,7 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
8973
9017
  console.error(chalk30.dim(" Usage: vm0 init --name <agent-name>"));
8974
9018
  process.exit(1);
8975
9019
  } else {
8976
- const dirName = path14.basename(process.cwd());
9020
+ const dirName = path13.basename(process.cwd());
8977
9021
  const defaultName = validateAgentName(dirName) ? dirName : void 0;
8978
9022
  const name = await promptText(
8979
9023
  "Enter agent name",
@@ -8999,10 +9043,10 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
8999
9043
  console.log(chalk30.dim(" Must start and end with letter or number"));
9000
9044
  process.exit(1);
9001
9045
  }
9002
- await writeFile7(VM0_YAML_FILE, generateVm0Yaml(agentName));
9046
+ await writeFile6(VM0_YAML_FILE, generateVm0Yaml(agentName));
9003
9047
  const vm0Status = existingFiles.includes(VM0_YAML_FILE) ? " (overwritten)" : "";
9004
9048
  console.log(chalk30.green(`\u2713 Created ${VM0_YAML_FILE}${vm0Status}`));
9005
- await writeFile7(AGENTS_MD_FILE, generateAgentsMd());
9049
+ await writeFile6(AGENTS_MD_FILE, generateAgentsMd());
9006
9050
  const agentsStatus = existingFiles.includes(AGENTS_MD_FILE) ? " (overwritten)" : "";
9007
9051
  console.log(chalk30.green(`\u2713 Created ${AGENTS_MD_FILE}${agentsStatus}`));
9008
9052
  console.log();
@@ -9018,644 +9062,100 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
9018
9062
  );
9019
9063
  });
9020
9064
 
9021
- // src/commands/setup-github.ts
9065
+ // src/commands/schedule/index.ts
9066
+ import { Command as Command34 } from "commander";
9067
+
9068
+ // src/commands/schedule/setup.ts
9022
9069
  import { Command as Command28 } from "commander";
9023
9070
  import chalk31 from "chalk";
9024
- import { existsSync as existsSync10 } from "fs";
9025
- import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
9026
- import { execSync, spawnSync } from "child_process";
9027
- import path15 from "path";
9071
+
9072
+ // src/lib/domain/schedule-utils.ts
9028
9073
  import { parse as parseYaml5 } from "yaml";
9029
- function isGhInstalled() {
9030
- try {
9031
- execSync("gh --version", { stdio: "ignore" });
9032
- return true;
9033
- } catch {
9034
- return false;
9074
+ function formatRelativeTime2(dateStr) {
9075
+ if (!dateStr) return "-";
9076
+ const date = new Date(dateStr);
9077
+ const now = /* @__PURE__ */ new Date();
9078
+ const diffMs = date.getTime() - now.getTime();
9079
+ const diffAbs = Math.abs(diffMs);
9080
+ const minutes = Math.floor(diffAbs / (1e3 * 60));
9081
+ const hours = Math.floor(diffAbs / (1e3 * 60 * 60));
9082
+ const days = Math.floor(diffAbs / (1e3 * 60 * 60 * 24));
9083
+ const isPast = diffMs < 0;
9084
+ if (days > 0) {
9085
+ return isPast ? `${days}d ago` : `in ${days}d`;
9086
+ } else if (hours > 0) {
9087
+ return isPast ? `${hours}h ago` : `in ${hours}h`;
9088
+ } else if (minutes > 0) {
9089
+ return isPast ? `${minutes}m ago` : `in ${minutes}m`;
9090
+ } else {
9091
+ return isPast ? "just now" : "soon";
9035
9092
  }
9036
9093
  }
9037
- function isGhAuthenticated() {
9038
- try {
9039
- execSync("gh auth status", { stdio: "ignore" });
9040
- return true;
9041
- } catch {
9042
- return false;
9043
- }
9094
+ function formatDateTime(dateStr) {
9095
+ if (!dateStr) return "-";
9096
+ const date = new Date(dateStr);
9097
+ const year = date.getFullYear();
9098
+ const month = String(date.getMonth() + 1).padStart(2, "0");
9099
+ const day = String(date.getDate()).padStart(2, "0");
9100
+ const hours = String(date.getHours()).padStart(2, "0");
9101
+ const minutes = String(date.getMinutes()).padStart(2, "0");
9102
+ const formatted = `${year}-${month}-${day} ${hours}:${minutes}`;
9103
+ const relative2 = formatRelativeTime2(dateStr);
9104
+ return `${formatted} (${relative2})`;
9044
9105
  }
9045
- function getGitRoot() {
9046
- try {
9047
- return execSync("git rev-parse --show-toplevel", {
9048
- encoding: "utf8"
9049
- }).trim();
9050
- } catch {
9051
- return null;
9106
+ function generateCronExpression(frequency, time, day) {
9107
+ const [hourStr, minuteStr] = time.split(":");
9108
+ const hour = parseInt(hourStr ?? "0", 10);
9109
+ const minute = parseInt(minuteStr ?? "0", 10);
9110
+ switch (frequency) {
9111
+ case "daily":
9112
+ return `${minute} ${hour} * * *`;
9113
+ case "weekly":
9114
+ return `${minute} ${hour} * * ${day ?? 1}`;
9115
+ case "monthly":
9116
+ return `${minute} ${hour} ${day ?? 1} * *`;
9052
9117
  }
9053
9118
  }
9054
- function getRelativeWorkingDir(gitRoot) {
9055
- const cwd = process.cwd();
9056
- if (cwd === gitRoot) {
9057
- return null;
9058
- }
9059
- const relativePath = path15.relative(gitRoot, cwd);
9060
- return relativePath.replace(/\\/g, "/");
9119
+ function detectTimezone() {
9120
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
9061
9121
  }
9062
- async function checkPrerequisites() {
9063
- console.log("Checking prerequisites...");
9064
- const gitRoot = getGitRoot();
9065
- if (!gitRoot) {
9066
- console.log(chalk31.red("\u2717 Not in a git repository"));
9067
- console.log();
9068
- console.log("This command must be run from within a git repository.");
9069
- console.log();
9070
- console.log("To initialize a git repository, run:");
9071
- console.log(` ${chalk31.cyan("git init")}`);
9072
- process.exit(1);
9122
+ function validateTimeFormat(time) {
9123
+ const match = time.match(/^(\d{1,2}):(\d{2})$/);
9124
+ if (!match) {
9125
+ return "Invalid format. Use HH:MM (e.g., 09:00)";
9073
9126
  }
9074
- console.log(chalk31.green("\u2713 Git repository detected"));
9075
- if (!isGhInstalled()) {
9076
- console.log(chalk31.red("\u2717 GitHub CLI (gh) is not installed"));
9077
- console.log();
9078
- console.log("GitHub CLI is required for this command.");
9079
- console.log();
9080
- console.log(` macOS: ${chalk31.cyan("brew install gh")}`);
9081
- console.log(` Other: ${chalk31.cyan("https://cli.github.com/")}`);
9082
- console.log();
9083
- console.log("After installation, run:");
9084
- console.log(` ${chalk31.cyan("gh auth login")}`);
9085
- console.log();
9086
- console.log("Then try again:");
9087
- console.log(` ${chalk31.cyan("vm0 setup-github")}`);
9088
- process.exit(1);
9127
+ const hour = parseInt(match[1], 10);
9128
+ const minute = parseInt(match[2], 10);
9129
+ if (hour < 0 || hour > 23) {
9130
+ return "Hour must be 0-23";
9089
9131
  }
9090
- console.log(chalk31.green("\u2713 GitHub CLI (gh) is installed"));
9091
- if (!isGhAuthenticated()) {
9092
- console.log(chalk31.red("\u2717 GitHub CLI is not authenticated"));
9093
- console.log();
9094
- console.log("Please authenticate GitHub CLI first:");
9095
- console.log(` ${chalk31.cyan("gh auth login")}`);
9096
- console.log();
9097
- console.log("Then try again:");
9098
- console.log(` ${chalk31.cyan("vm0 setup-github")}`);
9099
- process.exit(1);
9132
+ if (minute < 0 || minute > 59) {
9133
+ return "Minute must be 0-59";
9100
9134
  }
9101
- console.log(chalk31.green("\u2713 GitHub CLI is authenticated"));
9102
- const token = await getToken();
9103
- if (!token) {
9104
- console.log(chalk31.red("\u2717 VM0 not authenticated"));
9105
- console.log();
9106
- console.log("Please authenticate with VM0 first:");
9107
- console.log(` ${chalk31.cyan("vm0 auth login")}`);
9108
- console.log();
9109
- console.log("Then try again:");
9110
- console.log(` ${chalk31.cyan("vm0 setup-github")}`);
9111
- process.exit(1);
9135
+ return true;
9136
+ }
9137
+ function validateDateFormat(date) {
9138
+ const match = date.match(/^(\d{4})-(\d{2})-(\d{2})$/);
9139
+ if (!match) {
9140
+ return "Invalid format. Use YYYY-MM-DD (e.g., 2025-01-15)";
9112
9141
  }
9113
- console.log(chalk31.green("\u2713 VM0 authenticated"));
9114
- if (!existsSync10("vm0.yaml")) {
9115
- console.log(chalk31.red("\u2717 vm0.yaml not found"));
9116
- console.log();
9117
- console.log("This command requires a vm0.yaml configuration file.");
9118
- console.log();
9119
- console.log("To create one, run:");
9120
- console.log(` ${chalk31.cyan("vm0 init")}`);
9121
- console.log();
9122
- console.log("Then try again:");
9123
- console.log(` ${chalk31.cyan("vm0 setup-github")}`);
9124
- process.exit(1);
9142
+ const year = parseInt(match[1], 10);
9143
+ const month = parseInt(match[2], 10);
9144
+ const day = parseInt(match[3], 10);
9145
+ if (year < 2e3 || year > 2100) {
9146
+ return "Year must be between 2000 and 2100";
9125
9147
  }
9126
- console.log(chalk31.green("\u2713 vm0.yaml found"));
9127
- return { token, gitRoot };
9128
- }
9129
- function generatePublishYaml(workingDir) {
9130
- const pathPrefix = workingDir ? `${workingDir}/` : "";
9131
- const workingDirYaml = workingDir ? ` working-directory: ${workingDir}
9132
- ` : "";
9133
- return `name: Publish Agent
9134
-
9135
- on:
9136
- push:
9137
- branches: [main]
9138
- paths:
9139
- - '${pathPrefix}vm0.yaml'
9140
- - '${pathPrefix}AGENTS.md'
9141
-
9142
- jobs:
9143
- publish:
9144
- runs-on: ubuntu-latest
9145
- steps:
9146
- - uses: actions/checkout@v4
9147
-
9148
- - name: Publish Agent
9149
- uses: vm0-ai/compose-action@v1
9150
- id: compose
9151
- with:
9152
- vm0-token: \${{ secrets.VM0_TOKEN }}
9153
- ${workingDirYaml}
9154
- - name: Show Results
9155
- run: |
9156
- echo "Agent: \${{ steps.compose.outputs.name }}"
9157
- echo "Compose ID: \${{ steps.compose.outputs.compose-id }}"
9158
- echo "Version: \${{ steps.compose.outputs.version-id }}"
9159
- echo "Action: \${{ steps.compose.outputs.action }}"
9160
- `;
9161
- }
9162
- function generateRunYaml(agentName, secrets, vars) {
9163
- const otherSecrets = secrets.filter((s) => s !== "VM0_TOKEN");
9164
- const secretsLines = otherSecrets.map((s) => ` ${s}=\${{ secrets.${s} }}`).join("\n");
9165
- const varsLines = vars.map((v) => ` ${v}=\${{ vars.${v} }}`).join("\n");
9166
- let yaml = `name: Run Agent
9167
-
9168
- on:
9169
- # Uncomment to enable scheduled runs:
9170
- # schedule:
9171
- # - cron: '0 1 * * *' # Daily at 9:00 AM UTC+8
9172
- workflow_dispatch:
9173
- inputs:
9174
- prompt:
9175
- description: 'Prompt for the agent'
9176
- required: false
9177
- default: 'do the job'
9178
-
9179
- jobs:
9180
- run:
9181
- runs-on: ubuntu-latest
9182
- steps:
9183
- - name: Run Agent
9184
- uses: vm0-ai/run-action@v1
9185
- with:
9186
- agent: ${agentName}
9187
- prompt: \${{ github.event.inputs.prompt || 'do the job' }}
9188
- silent: true
9189
- vm0-token: \${{ secrets.VM0_TOKEN }}`;
9190
- if (secretsLines) {
9191
- yaml += `
9192
- secrets: |
9193
- ${secretsLines}`;
9194
- }
9195
- if (varsLines) {
9196
- yaml += `
9197
- vars: |
9198
- ${varsLines}`;
9199
- }
9200
- yaml += "\n";
9201
- return yaml;
9202
- }
9203
- function extractSecretsAndVars(config) {
9204
- const secrets = /* @__PURE__ */ new Set();
9205
- const vars = /* @__PURE__ */ new Set();
9206
- const refs = extractVariableReferences(config);
9207
- const grouped = groupVariablesBySource(refs);
9208
- for (const ref of grouped.secrets) {
9209
- secrets.add(ref.name);
9148
+ if (month < 1 || month > 12) {
9149
+ return "Month must be 1-12";
9210
9150
  }
9211
- for (const ref of grouped.vars) {
9212
- vars.add(ref.name);
9151
+ if (day < 1 || day > 31) {
9152
+ return "Day must be 1-31";
9213
9153
  }
9214
- secrets.add("VM0_TOKEN");
9215
- return {
9216
- secrets: Array.from(secrets).sort(),
9217
- vars: Array.from(vars).sort()
9218
- };
9219
- }
9220
- async function promptYesNo(question, defaultYes) {
9221
- const result = await promptConfirm(question, defaultYes);
9222
- return result ?? false;
9223
- }
9224
- function setGitHubSecret(name, value) {
9225
- const result = spawnSync("gh", ["secret", "set", name], {
9226
- input: value,
9227
- stdio: ["pipe", "pipe", "pipe"]
9228
- });
9229
- return result.status === 0;
9230
- }
9231
- function setGitHubVariable(name, value) {
9232
- const result = spawnSync("gh", ["variable", "set", name, "--body", value], {
9233
- stdio: ["pipe", "pipe", "pipe"]
9234
- });
9235
- return result.status === 0;
9236
- }
9237
- async function detectSecretValues(secrets, vars, vm0Token) {
9238
- const secretStatuses = secrets.map((name) => {
9239
- if (name === "VM0_TOKEN") {
9240
- return { name, found: true, source: "vm0 auth", value: vm0Token };
9241
- }
9242
- const envValue = process.env[name];
9243
- if (envValue) {
9244
- return { name, found: true, source: "environment", value: envValue };
9245
- }
9246
- return { name, found: false };
9247
- });
9248
- const varStatuses = vars.map((name) => {
9249
- const envValue = process.env[name];
9250
- if (envValue) {
9251
- return { name, found: true, source: "environment", value: envValue };
9252
- }
9253
- return { name, found: false };
9254
- });
9255
- return { secretStatuses, varStatuses };
9256
- }
9257
- function displaySecretsTable(secretStatuses, varStatuses) {
9258
- console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
9259
- console.log("\u2502 Detected secrets and variables: \u2502");
9260
- console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
9261
- if (secretStatuses.length > 0) {
9262
- console.log("\u2502 Secrets: \u2502");
9263
- for (const s of secretStatuses) {
9264
- const status = s.found ? chalk31.green("\u2713") : chalk31.red("\u2717");
9265
- const source = s.found ? `(from ${s.source})` : "not found";
9266
- const paddedName = (s.name + " ").padEnd(23, ".");
9267
- console.log(`\u2502 ${status} ${paddedName} ${source.padEnd(19)}\u2502`);
9268
- }
9269
- }
9270
- if (varStatuses.length > 0) {
9271
- console.log("\u2502 Variables: \u2502");
9272
- for (const v of varStatuses) {
9273
- const status = v.found ? chalk31.green("\u2713") : chalk31.red("\u2717");
9274
- const source = v.found ? `(from ${v.source})` : "not found";
9275
- const paddedName = (v.name + " ").padEnd(23, ".");
9276
- console.log(`\u2502 ${status} ${paddedName} ${source.padEnd(19)}\u2502`);
9277
- }
9278
- }
9279
- console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
9280
- }
9281
- function showManualSetupInstructions(secrets, vars) {
9282
- console.log("Skipped automatic setup. Configure secrets manually:");
9283
- console.log();
9284
- console.log(" Step 1: Get your VM0 token");
9285
- console.log(` ${chalk31.cyan("vm0 auth setup-token")}`);
9286
- console.log();
9287
- console.log(" Step 2: Set GitHub secrets");
9288
- for (const s of secrets) {
9289
- console.log(` ${chalk31.cyan(`gh secret set ${s}`)}`);
9290
- }
9291
- if (vars.length > 0) {
9292
- console.log();
9293
- console.log(" Step 3: Set GitHub variables");
9294
- for (const v of vars) {
9295
- console.log(` ${chalk31.cyan(`gh variable set ${v}`)}`);
9296
- }
9297
- }
9298
- }
9299
- function showSuccessMessage() {
9300
- console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
9301
- console.log("\u2502 \u2713 GitHub Actions setup complete! \u2502");
9302
- console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
9303
- console.log("\u2502 Workflows created: \u2502");
9304
- console.log("\u2502 \u2022 .github/workflows/publish.yml \u2502");
9305
- console.log("\u2502 \u2022 .github/workflows/run.yml \u2502");
9306
- console.log("\u2502 \u2502");
9307
- console.log("\u2502 Next steps: \u2502");
9308
- console.log("\u2502 1. Commit and push the workflow files \u2502");
9309
- console.log("\u2502 2. Push to main branch to trigger publish \u2502");
9310
- console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
9311
- }
9312
- function showPartialSuccessMessage(missingSecrets, missingVars) {
9313
- console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
9314
- console.log("\u2502 \u26A0 Setup partially complete \u2502");
9315
- console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
9316
- console.log("\u2502 Missing secrets - set them manually: \u2502");
9317
- for (const s of missingSecrets) {
9318
- console.log(`\u2502 gh secret set ${s.padEnd(40)}\u2502`);
9319
- }
9320
- for (const v of missingVars) {
9321
- console.log(`\u2502 gh variable set ${v.padEnd(38)}\u2502`);
9322
- }
9323
- console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
9324
- }
9325
- function showWorkflowsCreatedMessage() {
9326
- console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
9327
- console.log("\u2502 \u2713 Workflow files created! \u2502");
9328
- console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
9329
- console.log("\u2502 \u2022 .github/workflows/publish.yml \u2502");
9330
- console.log("\u2502 \u2022 .github/workflows/run.yml \u2502");
9331
- console.log("\u2502 \u2502");
9332
- console.log("\u2502 Next steps: \u2502");
9333
- console.log("\u2502 1. Set GitHub secrets (see commands above) \u2502");
9334
- console.log("\u2502 2. Commit and push the workflow files \u2502");
9335
- console.log("\u2502 3. Push to main branch to trigger publish \u2502");
9336
- console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
9337
- }
9338
- var setupGithubCommand = new Command28().name("setup-github").description("Initialize GitHub Actions workflows for agent deployment").option("-f, --force", "Overwrite existing workflow files").option("-y, --yes", "Auto-confirm all prompts").option("--skip-secrets", "Skip automatic secrets/variables setup").action(
9339
- // eslint-disable-next-line complexity -- TODO: refactor complex function
9340
- async (options) => {
9341
- const prereqs = await checkPrerequisites();
9342
- if (!prereqs) {
9343
- process.exit(1);
9344
- }
9345
- const { token: vm0Token, gitRoot } = prereqs;
9346
- const workingDir = getRelativeWorkingDir(gitRoot);
9347
- console.log();
9348
- console.log("Analyzing vm0.yaml...");
9349
- const content = await readFile8("vm0.yaml", "utf8");
9350
- const config = parseYaml5(content);
9351
- const agents = config.agents;
9352
- const agentName = Object.keys(agents)[0];
9353
- console.log(chalk31.green(`\u2713 Agent: ${agentName}`));
9354
- const { secrets, vars } = extractSecretsAndVars(config);
9355
- console.log(
9356
- chalk31.green(
9357
- `\u2713 Found ${secrets.length} secrets, ${vars.length} variables`
9358
- )
9359
- );
9360
- console.log();
9361
- const publishPath = path15.join(gitRoot, ".github/workflows/publish.yml");
9362
- const runPath = path15.join(gitRoot, ".github/workflows/run.yml");
9363
- const displayPublishPath = ".github/workflows/publish.yml";
9364
- const displayRunPath = ".github/workflows/run.yml";
9365
- const existingFiles = [];
9366
- if (existsSync10(publishPath)) existingFiles.push(displayPublishPath);
9367
- if (existsSync10(runPath)) existingFiles.push(displayRunPath);
9368
- if (existingFiles.length > 0 && !options.force) {
9369
- console.log(chalk31.yellow("\u26A0 Existing workflow files detected:"));
9370
- for (const file of existingFiles) {
9371
- console.log(` \u2022 ${file}`);
9372
- }
9373
- console.log();
9374
- if (!options.yes) {
9375
- const overwrite = await promptYesNo(
9376
- "Overwrite existing files?",
9377
- false
9378
- );
9379
- if (!overwrite) {
9380
- console.log();
9381
- console.log("Aborted. To force overwrite, run:");
9382
- console.log(` ${chalk31.cyan("vm0 setup-github --force")}`);
9383
- process.exit(0);
9384
- }
9385
- }
9386
- console.log();
9387
- }
9388
- console.log("Creating workflow files...");
9389
- await mkdir7(path15.join(gitRoot, ".github/workflows"), { recursive: true });
9390
- await writeFile8(publishPath, generatePublishYaml(workingDir));
9391
- const publishStatus = existingFiles.includes(displayPublishPath) ? "Overwrote" : "Created";
9392
- console.log(chalk31.green(`\u2713 ${publishStatus} ${displayPublishPath}`));
9393
- await writeFile8(runPath, generateRunYaml(agentName, secrets, vars));
9394
- const runStatus = existingFiles.includes(displayRunPath) ? "Overwrote" : "Created";
9395
- console.log(chalk31.green(`\u2713 ${runStatus} ${displayRunPath}`));
9396
- console.log();
9397
- if (options.skipSecrets) {
9398
- console.log(chalk31.green("\u2713 Done (secrets setup skipped)"));
9399
- return;
9400
- }
9401
- const { secretStatuses, varStatuses } = await detectSecretValues(
9402
- secrets,
9403
- vars,
9404
- vm0Token
9405
- );
9406
- displaySecretsTable(secretStatuses, varStatuses);
9407
- console.log();
9408
- const hasFoundValues = secretStatuses.some((s) => s.found) || varStatuses.some((v) => v.found);
9409
- if (!hasFoundValues) {
9410
- console.log("No secret/variable values found in environment.");
9411
- console.log();
9412
- showManualSetupInstructions(secrets, vars);
9413
- console.log();
9414
- showWorkflowsCreatedMessage();
9415
- return;
9416
- }
9417
- let shouldSetup = options.yes;
9418
- if (!shouldSetup) {
9419
- shouldSetup = await promptYesNo(
9420
- "Set up GitHub secrets/variables automatically?",
9421
- true
9422
- );
9423
- }
9424
- if (!shouldSetup) {
9425
- console.log();
9426
- showManualSetupInstructions(secrets, vars);
9427
- console.log();
9428
- showWorkflowsCreatedMessage();
9429
- return;
9430
- }
9431
- console.log();
9432
- console.log("Setting secrets...");
9433
- const failedSecrets = [];
9434
- for (const s of secretStatuses) {
9435
- if (s.found && s.value) {
9436
- const success = setGitHubSecret(s.name, s.value);
9437
- if (success) {
9438
- console.log(` ${chalk31.green("\u2713")} ${s.name}`);
9439
- } else {
9440
- console.log(` ${chalk31.red("\u2717")} ${s.name} (failed)`);
9441
- failedSecrets.push(s.name);
9442
- }
9443
- } else {
9444
- console.log(
9445
- ` ${chalk31.yellow("\u26A0")} ${s.name} (skipped - not found)`
9446
- );
9447
- }
9448
- }
9449
- const failedVars = [];
9450
- if (varStatuses.length > 0) {
9451
- console.log();
9452
- console.log("Setting variables...");
9453
- for (const v of varStatuses) {
9454
- if (v.found && v.value) {
9455
- const success = setGitHubVariable(v.name, v.value);
9456
- if (success) {
9457
- console.log(` ${chalk31.green("\u2713")} ${v.name}`);
9458
- } else {
9459
- console.log(` ${chalk31.red("\u2717")} ${v.name} (failed)`);
9460
- failedVars.push(v.name);
9461
- }
9462
- } else {
9463
- console.log(
9464
- ` ${chalk31.yellow("\u26A0")} ${v.name} (skipped - not found)`
9465
- );
9466
- }
9467
- }
9468
- }
9469
- console.log();
9470
- const missingSecrets = [
9471
- ...secretStatuses.filter((s) => !s.found).map((s) => s.name),
9472
- ...failedSecrets
9473
- ];
9474
- const missingVars = [
9475
- ...varStatuses.filter((v) => !v.found).map((v) => v.name),
9476
- ...failedVars
9477
- ];
9478
- if (missingSecrets.length === 0 && missingVars.length === 0) {
9479
- showSuccessMessage();
9480
- } else {
9481
- showPartialSuccessMessage(missingSecrets, missingVars);
9482
- }
9483
- }
9484
- );
9485
-
9486
- // src/commands/schedule/index.ts
9487
- import { Command as Command36 } from "commander";
9488
-
9489
- // src/commands/schedule/init.ts
9490
- import { Command as Command29 } from "commander";
9491
- import chalk32 from "chalk";
9492
- import { existsSync as existsSync12, writeFileSync } from "fs";
9493
- import { stringify as stringifyYaml2 } from "yaml";
9494
-
9495
- // src/lib/domain/schedule-utils.ts
9496
- import { existsSync as existsSync11, readFileSync as readFileSync2 } from "fs";
9497
- import { parse as parseYaml6 } from "yaml";
9498
- var CONFIG_FILE4 = "vm0.yaml";
9499
- var SCHEDULE_FILE = "schedule.yaml";
9500
- function loadAgentName() {
9501
- if (!existsSync11(CONFIG_FILE4)) {
9502
- return { agentName: null };
9503
- }
9504
- try {
9505
- const content = readFileSync2(CONFIG_FILE4, "utf8");
9506
- const config = parseYaml6(content);
9507
- const agentNames = Object.keys(config.agents || {});
9508
- return { agentName: agentNames[0] || null };
9509
- } catch (err) {
9510
- return {
9511
- agentName: null,
9512
- error: err instanceof Error ? err.message : "Failed to parse vm0.yaml"
9513
- };
9514
- }
9515
- }
9516
- function loadScheduleName() {
9517
- if (!existsSync11(SCHEDULE_FILE)) {
9518
- return { scheduleName: null };
9519
- }
9520
- try {
9521
- const content = readFileSync2(SCHEDULE_FILE, "utf8");
9522
- const parsed = parseYaml6(content);
9523
- if (!parsed?.schedules) {
9524
- return {
9525
- scheduleName: null,
9526
- error: "No schedules defined in schedule.yaml"
9527
- };
9528
- }
9529
- const scheduleNames = Object.keys(parsed.schedules);
9530
- return { scheduleName: scheduleNames[0] || null };
9531
- } catch (err) {
9532
- return {
9533
- scheduleName: null,
9534
- error: err instanceof Error ? err.message : "Failed to parse schedule.yaml"
9535
- };
9536
- }
9537
- }
9538
- function formatRelativeTime2(dateStr) {
9539
- if (!dateStr) return "-";
9540
- const date = new Date(dateStr);
9541
- const now = /* @__PURE__ */ new Date();
9542
- const diffMs = date.getTime() - now.getTime();
9543
- const diffAbs = Math.abs(diffMs);
9544
- const minutes = Math.floor(diffAbs / (1e3 * 60));
9545
- const hours = Math.floor(diffAbs / (1e3 * 60 * 60));
9546
- const days = Math.floor(diffAbs / (1e3 * 60 * 60 * 24));
9547
- const isPast = diffMs < 0;
9548
- if (days > 0) {
9549
- return isPast ? `${days}d ago` : `in ${days}d`;
9550
- } else if (hours > 0) {
9551
- return isPast ? `${hours}h ago` : `in ${hours}h`;
9552
- } else if (minutes > 0) {
9553
- return isPast ? `${minutes}m ago` : `in ${minutes}m`;
9554
- } else {
9555
- return isPast ? "just now" : "soon";
9556
- }
9557
- }
9558
- function formatDateTime(dateStr) {
9559
- if (!dateStr) return "-";
9560
- const date = new Date(dateStr);
9561
- const year = date.getFullYear();
9562
- const month = String(date.getMonth() + 1).padStart(2, "0");
9563
- const day = String(date.getDate()).padStart(2, "0");
9564
- const hours = String(date.getHours()).padStart(2, "0");
9565
- const minutes = String(date.getMinutes()).padStart(2, "0");
9566
- const formatted = `${year}-${month}-${day} ${hours}:${minutes}`;
9567
- const relative2 = formatRelativeTime2(dateStr);
9568
- return `${formatted} (${relative2})`;
9569
- }
9570
- function generateCronExpression(frequency, time, day) {
9571
- const [hourStr, minuteStr] = time.split(":");
9572
- const hour = parseInt(hourStr ?? "0", 10);
9573
- const minute = parseInt(minuteStr ?? "0", 10);
9574
- switch (frequency) {
9575
- case "daily":
9576
- return `${minute} ${hour} * * *`;
9577
- case "weekly":
9578
- return `${minute} ${hour} * * ${day ?? 1}`;
9579
- case "monthly":
9580
- return `${minute} ${hour} ${day ?? 1} * *`;
9581
- }
9582
- }
9583
- function detectTimezone() {
9584
- return Intl.DateTimeFormat().resolvedOptions().timeZone;
9585
- }
9586
- function extractVarsAndSecrets() {
9587
- const result = { vars: [], secrets: [] };
9588
- if (!existsSync11(CONFIG_FILE4)) {
9589
- return result;
9590
- }
9591
- try {
9592
- const content = readFileSync2(CONFIG_FILE4, "utf8");
9593
- const config = parseYaml6(content);
9594
- const agents = Object.values(config.agents || {});
9595
- const agent = agents[0];
9596
- if (!agent) {
9597
- return result;
9598
- }
9599
- if (agent.environment) {
9600
- for (const value of Object.values(agent.environment)) {
9601
- const varsMatches = value.matchAll(/\$\{\{\s*vars\.(\w+)\s*\}\}/g);
9602
- for (const match of varsMatches) {
9603
- if (match[1] && !result.vars.includes(match[1])) {
9604
- result.vars.push(match[1]);
9605
- }
9606
- }
9607
- const secretsMatches = value.matchAll(
9608
- /\$\{\{\s*secrets\.(\w+)\s*\}\}/g
9609
- );
9610
- for (const match of secretsMatches) {
9611
- if (match[1] && !result.secrets.includes(match[1])) {
9612
- result.secrets.push(match[1]);
9613
- }
9614
- }
9615
- }
9616
- }
9617
- return result;
9618
- } catch {
9619
- return result;
9620
- }
9621
- }
9622
- function validateTimeFormat(time) {
9623
- const match = time.match(/^(\d{1,2}):(\d{2})$/);
9624
- if (!match) {
9625
- return "Invalid format. Use HH:MM (e.g., 09:00)";
9626
- }
9627
- const hour = parseInt(match[1], 10);
9628
- const minute = parseInt(match[2], 10);
9629
- if (hour < 0 || hour > 23) {
9630
- return "Hour must be 0-23";
9631
- }
9632
- if (minute < 0 || minute > 59) {
9633
- return "Minute must be 0-59";
9634
- }
9635
- return true;
9636
- }
9637
- function validateDateFormat(date) {
9638
- const match = date.match(/^(\d{4})-(\d{2})-(\d{2})$/);
9639
- if (!match) {
9640
- return "Invalid format. Use YYYY-MM-DD (e.g., 2025-01-15)";
9641
- }
9642
- const year = parseInt(match[1], 10);
9643
- const month = parseInt(match[2], 10);
9644
- const day = parseInt(match[3], 10);
9645
- if (year < 2e3 || year > 2100) {
9646
- return "Year must be between 2000 and 2100";
9647
- }
9648
- if (month < 1 || month > 12) {
9649
- return "Month must be 1-12";
9650
- }
9651
- if (day < 1 || day > 31) {
9652
- return "Day must be 1-31";
9653
- }
9654
- const testDate = new Date(year, month - 1, day);
9655
- if (testDate.getFullYear() !== year || testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
9656
- return "Invalid date";
9657
- }
9658
- return true;
9154
+ const testDate = new Date(year, month - 1, day);
9155
+ if (testDate.getFullYear() !== year || testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
9156
+ return "Invalid date";
9157
+ }
9158
+ return true;
9659
9159
  }
9660
9160
  function getTomorrowDateLocal() {
9661
9161
  const tomorrow = /* @__PURE__ */ new Date();
@@ -9679,11 +9179,11 @@ function toISODateTime(dateTimeStr) {
9679
9179
  const date = new Date(isoStr);
9680
9180
  return date.toISOString();
9681
9181
  }
9682
- async function resolveScheduleByName(name) {
9182
+ async function resolveScheduleByAgent(agentName) {
9683
9183
  const { schedules } = await listSchedules();
9684
- const schedule = schedules.find((s) => s.name === name);
9184
+ const schedule = schedules.find((s) => s.composeName === agentName);
9685
9185
  if (!schedule) {
9686
- throw new Error(`Schedule "${name}" not found`);
9186
+ throw new Error(`No schedule found for agent "${agentName}"`);
9687
9187
  }
9688
9188
  return {
9689
9189
  name: schedule.name,
@@ -9692,8 +9192,7 @@ async function resolveScheduleByName(name) {
9692
9192
  };
9693
9193
  }
9694
9194
 
9695
- // src/commands/schedule/init.ts
9696
- var SCHEDULE_FILE2 = "schedule.yaml";
9195
+ // src/commands/schedule/setup.ts
9697
9196
  var FREQUENCY_CHOICES = [
9698
9197
  { title: "Daily", value: "daily", description: "Run every day" },
9699
9198
  {
@@ -9741,281 +9240,12 @@ function parseDayOption(day, frequency) {
9741
9240
  }
9742
9241
  return void 0;
9743
9242
  }
9744
- var initCommand4 = new Command29().name("init").description("Create a schedule.yaml interactively").option("-n, --name <name>", "Schedule name").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("--no-vars", "Don't include vars from vm0.yaml").option("--force", "Overwrite existing schedule.yaml").action(
9745
- // eslint-disable-next-line complexity -- TODO: refactor complex function
9746
- async (options) => {
9747
- try {
9748
- const { agentName, error } = loadAgentName();
9749
- if (error) {
9750
- console.error(chalk32.red(`\u2717 Invalid vm0.yaml: ${error}`));
9751
- process.exit(1);
9752
- }
9753
- if (!agentName) {
9754
- console.error(chalk32.red("\u2717 No vm0.yaml found"));
9755
- console.error(
9756
- chalk32.dim(" Run this command from an agent directory")
9757
- );
9758
- process.exit(1);
9759
- }
9760
- if (existsSync12(SCHEDULE_FILE2) && !options.force) {
9761
- if (!isInteractive()) {
9762
- console.error(chalk32.red("\u2717 schedule.yaml already exists"));
9763
- console.error(chalk32.dim(" Use --force to overwrite"));
9764
- process.exit(1);
9765
- }
9766
- const overwrite = await promptConfirm(
9767
- "schedule.yaml exists. Overwrite?",
9768
- false
9769
- );
9770
- if (!overwrite) {
9771
- console.log(chalk32.dim("Cancelled"));
9772
- return;
9773
- }
9774
- }
9775
- let scheduleName = options.name;
9776
- if (!scheduleName) {
9777
- if (!isInteractive()) {
9778
- console.error(
9779
- chalk32.red("\u2717 --name is required in non-interactive mode")
9780
- );
9781
- process.exit(1);
9782
- }
9783
- scheduleName = await promptText(
9784
- "Schedule name",
9785
- `${agentName}-schedule`
9786
- );
9787
- if (!scheduleName) {
9788
- console.log(chalk32.dim("Cancelled"));
9789
- return;
9790
- }
9791
- }
9792
- let frequency = options.frequency;
9793
- if (!frequency || !["daily", "weekly", "monthly", "once"].includes(frequency)) {
9794
- if (!isInteractive()) {
9795
- console.error(
9796
- chalk32.red(
9797
- "\u2717 --frequency is required (daily|weekly|monthly|once)"
9798
- )
9799
- );
9800
- process.exit(1);
9801
- }
9802
- frequency = await promptSelect(
9803
- "Schedule frequency",
9804
- FREQUENCY_CHOICES,
9805
- 0
9806
- );
9807
- if (!frequency) {
9808
- console.log(chalk32.dim("Cancelled"));
9809
- return;
9810
- }
9811
- }
9812
- let day;
9813
- if (frequency === "weekly" || frequency === "monthly") {
9814
- if (options.day) {
9815
- day = parseDayOption(options.day, frequency);
9816
- if (day === void 0) {
9817
- console.error(
9818
- chalk32.red(
9819
- `\u2717 Invalid day: ${options.day}. Use mon-sun for weekly or 1-31 for monthly.`
9820
- )
9821
- );
9822
- process.exit(1);
9823
- }
9824
- } else if (isInteractive()) {
9825
- if (frequency === "weekly") {
9826
- day = await promptSelect("Day of week", DAY_OF_WEEK_CHOICES, 0);
9827
- if (day === void 0) {
9828
- console.log(chalk32.dim("Cancelled"));
9829
- return;
9830
- }
9831
- } else {
9832
- const dayStr = await promptText("Day of month (1-31)", "1");
9833
- if (!dayStr) {
9834
- console.log(chalk32.dim("Cancelled"));
9835
- return;
9836
- }
9837
- day = parseInt(dayStr, 10);
9838
- if (isNaN(day) || day < 1 || day > 31) {
9839
- console.error(chalk32.red("\u2717 Day must be between 1 and 31"));
9840
- process.exit(1);
9841
- }
9842
- }
9843
- } else {
9844
- console.error(chalk32.red("\u2717 --day is required for weekly/monthly"));
9845
- process.exit(1);
9846
- }
9847
- }
9848
- let time = options.time;
9849
- let atTime;
9850
- if (frequency === "once") {
9851
- if (!isInteractive()) {
9852
- console.error(
9853
- chalk32.red("\u2717 One-time schedules require interactive mode")
9854
- );
9855
- console.error(
9856
- chalk32.dim(" Use cron frequency for non-interactive mode")
9857
- );
9858
- process.exit(1);
9859
- }
9860
- const tomorrowDate = getTomorrowDateLocal();
9861
- const date = await promptText(
9862
- "Date (YYYY-MM-DD, default tomorrow)",
9863
- tomorrowDate,
9864
- validateDateFormat
9865
- );
9866
- if (!date) {
9867
- console.log(chalk32.dim("Cancelled"));
9868
- return;
9869
- }
9870
- const currentTime = getCurrentTimeLocal();
9871
- time = await promptText(
9872
- "Time (HH:MM)",
9873
- currentTime,
9874
- validateTimeFormat
9875
- );
9876
- if (!time) {
9877
- console.log(chalk32.dim("Cancelled"));
9878
- return;
9879
- }
9880
- const dateStr = `${date} ${time}`;
9881
- atTime = dateStr;
9882
- } else {
9883
- if (!time) {
9884
- if (!isInteractive()) {
9885
- console.error(chalk32.red("\u2717 --time is required (HH:MM format)"));
9886
- process.exit(1);
9887
- }
9888
- time = await promptText(
9889
- "Time (HH:MM)",
9890
- "09:00",
9891
- validateTimeFormat
9892
- );
9893
- if (!time) {
9894
- console.log(chalk32.dim("Cancelled"));
9895
- return;
9896
- }
9897
- } else {
9898
- const validation = validateTimeFormat(time);
9899
- if (validation !== true) {
9900
- console.error(chalk32.red(`\u2717 Invalid time: ${validation}`));
9901
- process.exit(1);
9902
- }
9903
- }
9904
- }
9905
- const detectedTimezone = detectTimezone();
9906
- let timezone = options.timezone;
9907
- if (!timezone) {
9908
- if (isInteractive()) {
9909
- timezone = await promptText("Timezone", detectedTimezone);
9910
- if (!timezone) {
9911
- console.log(chalk32.dim("Cancelled"));
9912
- return;
9913
- }
9914
- } else {
9915
- timezone = detectedTimezone;
9916
- }
9917
- }
9918
- let promptText_ = options.prompt;
9919
- if (!promptText_) {
9920
- if (!isInteractive()) {
9921
- console.error(chalk32.red("\u2717 --prompt is required"));
9922
- process.exit(1);
9923
- }
9924
- promptText_ = await promptText(
9925
- "Prompt to run",
9926
- "let's start working."
9927
- );
9928
- if (!promptText_) {
9929
- console.log(chalk32.dim("Cancelled"));
9930
- return;
9931
- }
9932
- }
9933
- let vars;
9934
- let secrets;
9935
- if (options.vars) {
9936
- const extracted = extractVarsAndSecrets();
9937
- if (extracted.vars.length > 0 || extracted.secrets.length > 0) {
9938
- let includeVars = true;
9939
- if (isInteractive()) {
9940
- const varCount = extracted.vars.length;
9941
- const secretCount = extracted.secrets.length;
9942
- const parts = [];
9943
- if (varCount > 0) parts.push(`${varCount} variable(s)`);
9944
- if (secretCount > 0) parts.push(`${secretCount} secret(s)`);
9945
- const itemList = [
9946
- ...extracted.vars.map((v) => `vars.${v}`),
9947
- ...extracted.secrets.map((s) => `secrets.${s}`)
9948
- ];
9949
- includeVars = await promptConfirm(
9950
- `Include ${parts.join(" and ")} from vm0.yaml? (${itemList.join(", ")})`,
9951
- true
9952
- ) ?? true;
9953
- }
9954
- if (includeVars) {
9955
- if (extracted.vars.length > 0) {
9956
- vars = {};
9957
- for (const v of extracted.vars) {
9958
- vars[v] = `\${${v}}`;
9959
- }
9960
- }
9961
- if (extracted.secrets.length > 0) {
9962
- secrets = {};
9963
- for (const s of extracted.secrets) {
9964
- secrets[s] = `\${${s}}`;
9965
- }
9966
- }
9967
- }
9968
- }
9969
- }
9970
- const scheduleYaml = {
9971
- version: "1.0",
9972
- schedules: {
9973
- [scheduleName]: {
9974
- on: {
9975
- timezone
9976
- },
9977
- run: {
9978
- agent: agentName,
9979
- prompt: promptText_
9980
- }
9981
- }
9982
- }
9983
- };
9984
- if (atTime) {
9985
- scheduleYaml.schedules[scheduleName].on.at = atTime;
9986
- } else if (time && frequency !== "once") {
9987
- scheduleYaml.schedules[scheduleName].on.cron = generateCronExpression(frequency, time, day);
9988
- }
9989
- if (vars && Object.keys(vars).length > 0) {
9990
- scheduleYaml.schedules[scheduleName].run.vars = vars;
9991
- }
9992
- if (secrets && Object.keys(secrets).length > 0) {
9993
- scheduleYaml.schedules[scheduleName].run.secrets = secrets;
9994
- }
9995
- writeFileSync(SCHEDULE_FILE2, stringifyYaml2(scheduleYaml));
9996
- console.log(chalk32.green(`\u2713 Created ${SCHEDULE_FILE2}`));
9997
- console.log(chalk32.dim(" Deploy with: vm0 schedule deploy"));
9998
- } catch (error) {
9999
- console.error(chalk32.red("\u2717 Failed to create schedule.yaml"));
10000
- if (error instanceof Error) {
10001
- console.error(chalk32.dim(` ${error.message}`));
10002
- }
10003
- process.exit(1);
10004
- }
10005
- }
10006
- );
10007
-
10008
- // src/commands/schedule/deploy.ts
10009
- import { Command as Command30 } from "commander";
10010
- import chalk33 from "chalk";
10011
- import { existsSync as existsSync13, readFileSync as readFileSync3 } from "fs";
10012
- import { parse as parseYaml7 } from "yaml";
10013
9243
  function expandEnvVars(value) {
10014
9244
  return value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
10015
9245
  const envValue = process.env[varName];
10016
9246
  if (envValue === void 0) {
10017
9247
  console.warn(
10018
- chalk33.yellow(` Warning: Environment variable ${varName} not set`)
9248
+ chalk31.yellow(` Warning: Environment variable ${varName} not set`)
10019
9249
  );
10020
9250
  return match;
10021
9251
  }
@@ -10044,134 +9274,407 @@ function formatInTimezone(isoDate, timezone) {
10044
9274
  const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
10045
9275
  return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}`;
10046
9276
  }
10047
- var deployCommand = new Command30().name("deploy").description("Deploy a schedule from schedule.yaml (create or update)").argument("[file]", "Path to schedule.yaml", "schedule.yaml").action(async (file) => {
9277
+ function parseFrequencyFromCron(cron) {
9278
+ const parts = cron.split(" ");
9279
+ if (parts.length !== 5) return null;
9280
+ const [minute, hour, dayOfMonth, , dayOfWeek] = parts;
9281
+ const time = `${hour.padStart(2, "0")}:${minute.padStart(2, "0")}`;
9282
+ if (dayOfMonth === "*" && dayOfWeek === "*") {
9283
+ return { frequency: "daily", time };
9284
+ } else if (dayOfMonth === "*" && dayOfWeek !== "*") {
9285
+ return { frequency: "weekly", day: parseInt(dayOfWeek, 10), time };
9286
+ } else if (dayOfMonth !== "*" && dayOfWeek === "*") {
9287
+ return { frequency: "monthly", day: parseInt(dayOfMonth, 10), time };
9288
+ }
9289
+ return null;
9290
+ }
9291
+ function collect(value, previous) {
9292
+ return previous.concat([value]);
9293
+ }
9294
+ function parseKeyValuePairs(pairs) {
9295
+ const result = {};
9296
+ for (const pair of pairs) {
9297
+ const eqIndex = pair.indexOf("=");
9298
+ if (eqIndex > 0) {
9299
+ const key = pair.slice(0, eqIndex);
9300
+ const value = pair.slice(eqIndex + 1);
9301
+ result[key] = value;
9302
+ }
9303
+ }
9304
+ return result;
9305
+ }
9306
+ function getExistingDefaults(existingSchedule) {
9307
+ const defaults = {};
9308
+ if (existingSchedule?.cronExpression) {
9309
+ const parsed = parseFrequencyFromCron(existingSchedule.cronExpression);
9310
+ if (parsed) {
9311
+ defaults.frequency = parsed.frequency;
9312
+ defaults.day = parsed.day;
9313
+ defaults.time = parsed.time;
9314
+ }
9315
+ } else if (existingSchedule?.atTime) {
9316
+ defaults.frequency = "once";
9317
+ }
9318
+ return defaults;
9319
+ }
9320
+ async function gatherFrequency(optionFrequency, existingFrequency) {
9321
+ let frequency = optionFrequency;
9322
+ if (frequency && ["daily", "weekly", "monthly", "once"].includes(frequency)) {
9323
+ return frequency;
9324
+ }
9325
+ if (!isInteractive()) {
9326
+ console.error(
9327
+ chalk31.red("\u2717 --frequency is required (daily|weekly|monthly|once)")
9328
+ );
9329
+ process.exit(1);
9330
+ }
9331
+ const defaultIndex = existingFrequency ? FREQUENCY_CHOICES.findIndex((c19) => c19.value === existingFrequency) : 0;
9332
+ frequency = await promptSelect(
9333
+ "Schedule frequency",
9334
+ FREQUENCY_CHOICES,
9335
+ defaultIndex >= 0 ? defaultIndex : 0
9336
+ );
9337
+ return frequency || null;
9338
+ }
9339
+ async function gatherDay(frequency, optionDay, existingDay) {
9340
+ if (frequency !== "weekly" && frequency !== "monthly") {
9341
+ return null;
9342
+ }
9343
+ if (optionDay) {
9344
+ const day2 = parseDayOption(optionDay, frequency);
9345
+ if (day2 === void 0) {
9346
+ console.error(
9347
+ chalk31.red(
9348
+ `\u2717 Invalid day: ${optionDay}. Use mon-sun for weekly or 1-31 for monthly.`
9349
+ )
9350
+ );
9351
+ process.exit(1);
9352
+ }
9353
+ return day2;
9354
+ }
9355
+ if (!isInteractive()) {
9356
+ console.error(chalk31.red("\u2717 --day is required for weekly/monthly"));
9357
+ process.exit(1);
9358
+ }
9359
+ if (frequency === "weekly") {
9360
+ const defaultDayIndex = existingDay !== void 0 ? DAY_OF_WEEK_CHOICES.findIndex((c19) => c19.value === existingDay) : 0;
9361
+ const day2 = await promptSelect(
9362
+ "Day of week",
9363
+ DAY_OF_WEEK_CHOICES,
9364
+ defaultDayIndex >= 0 ? defaultDayIndex : 0
9365
+ );
9366
+ return day2 ?? null;
9367
+ }
9368
+ const dayStr = await promptText(
9369
+ "Day of month (1-31)",
9370
+ existingDay?.toString() || "1"
9371
+ );
9372
+ if (!dayStr) return null;
9373
+ const day = parseInt(dayStr, 10);
9374
+ if (isNaN(day) || day < 1 || day > 31) {
9375
+ console.error(chalk31.red("\u2717 Day must be between 1 and 31"));
9376
+ process.exit(1);
9377
+ }
9378
+ return day;
9379
+ }
9380
+ async function gatherRecurringTime(optionTime, existingTime) {
9381
+ if (optionTime) {
9382
+ const validation = validateTimeFormat(optionTime);
9383
+ if (validation !== true) {
9384
+ console.error(chalk31.red(`\u2717 Invalid time: ${validation}`));
9385
+ process.exit(1);
9386
+ }
9387
+ return optionTime;
9388
+ }
9389
+ if (!isInteractive()) {
9390
+ console.error(chalk31.red("\u2717 --time is required (HH:MM format)"));
9391
+ process.exit(1);
9392
+ }
9393
+ return await promptText(
9394
+ "Time (HH:MM)",
9395
+ existingTime || "09:00",
9396
+ validateTimeFormat
9397
+ );
9398
+ }
9399
+ async function gatherOneTimeSchedule(optionDay, optionTime, existingTime) {
9400
+ if (optionDay && optionTime) {
9401
+ if (!validateDateFormat(optionDay)) {
9402
+ console.error(
9403
+ chalk31.red(
9404
+ `\u2717 Invalid date format: ${optionDay}. Use YYYY-MM-DD format.`
9405
+ )
9406
+ );
9407
+ process.exit(1);
9408
+ }
9409
+ if (!validateTimeFormat(optionTime)) {
9410
+ console.error(
9411
+ chalk31.red(`\u2717 Invalid time format: ${optionTime}. Use HH:MM format.`)
9412
+ );
9413
+ process.exit(1);
9414
+ }
9415
+ return `${optionDay} ${optionTime}`;
9416
+ }
9417
+ if (!isInteractive()) {
9418
+ console.error(chalk31.red("\u2717 One-time schedules require interactive mode"));
9419
+ console.error(
9420
+ chalk31.dim(" Or provide --day (YYYY-MM-DD) and --time (HH:MM) flags")
9421
+ );
9422
+ process.exit(1);
9423
+ }
9424
+ const tomorrowDate = getTomorrowDateLocal();
9425
+ const date = await promptText(
9426
+ "Date (YYYY-MM-DD, default tomorrow)",
9427
+ tomorrowDate,
9428
+ validateDateFormat
9429
+ );
9430
+ if (!date) return null;
9431
+ const currentTime = getCurrentTimeLocal();
9432
+ const time = await promptText(
9433
+ "Time (HH:MM)",
9434
+ existingTime || currentTime,
9435
+ validateTimeFormat
9436
+ );
9437
+ if (!time) return null;
9438
+ return `${date} ${time}`;
9439
+ }
9440
+ async function gatherTimezone(optionTimezone, existingTimezone) {
9441
+ if (optionTimezone) return optionTimezone;
9442
+ const detectedTimezone = detectTimezone();
9443
+ if (!isInteractive()) {
9444
+ return detectedTimezone;
9445
+ }
9446
+ return await promptText("Timezone", existingTimezone || detectedTimezone);
9447
+ }
9448
+ async function gatherPromptText(optionPrompt, existingPrompt) {
9449
+ if (optionPrompt) return optionPrompt;
9450
+ if (!isInteractive()) {
9451
+ console.error(chalk31.red("\u2717 --prompt is required"));
9452
+ process.exit(1);
9453
+ }
9454
+ return await promptText(
9455
+ "Prompt to run",
9456
+ existingPrompt || "let's start working."
9457
+ );
9458
+ }
9459
+ async function gatherVars(optionVars, existingVars) {
9460
+ if (optionVars.length > 0) {
9461
+ return parseKeyValuePairs(optionVars);
9462
+ }
9463
+ if (isInteractive() && existingVars) {
9464
+ const keepVars = await promptConfirm(
9465
+ `Keep existing variables? (${Object.keys(existingVars).join(", ")})`,
9466
+ true
9467
+ );
9468
+ if (keepVars) {
9469
+ return existingVars;
9470
+ }
9471
+ }
9472
+ return void 0;
9473
+ }
9474
+ async function gatherSecrets(optionSecrets, existingSecretNames) {
9475
+ if (optionSecrets.length > 0) {
9476
+ return parseKeyValuePairs(optionSecrets);
9477
+ }
9478
+ if (isInteractive() && existingSecretNames && existingSecretNames.length > 0) {
9479
+ const keepSecrets = await promptConfirm(
9480
+ `Keep existing secrets? (${existingSecretNames.join(", ")})`,
9481
+ true
9482
+ );
9483
+ if (!keepSecrets) {
9484
+ console.log(
9485
+ chalk31.dim(" Note: You'll need to provide new secret values")
9486
+ );
9487
+ }
9488
+ }
9489
+ return void 0;
9490
+ }
9491
+ async function resolveAgent(agentName) {
9492
+ const compose = await getComposeByName(agentName);
9493
+ if (!compose) {
9494
+ console.error(chalk31.red(`\u2717 Agent not found: ${agentName}`));
9495
+ console.error(chalk31.dim(" Make sure the agent is composed first"));
9496
+ process.exit(1);
9497
+ }
9498
+ return {
9499
+ composeId: compose.id,
9500
+ scheduleName: `${agentName}-schedule`
9501
+ };
9502
+ }
9503
+ async function findExistingSchedule(agentName) {
9504
+ const { schedules } = await listSchedules();
9505
+ return schedules.find((s) => s.composeName === agentName);
9506
+ }
9507
+ async function buildAndDeploy(params) {
9508
+ let cronExpression;
9509
+ let atTimeISO;
9510
+ if (params.atTime) {
9511
+ atTimeISO = toISODateTime(params.atTime);
9512
+ } else if (params.time && params.frequency !== "once") {
9513
+ cronExpression = generateCronExpression(
9514
+ params.frequency,
9515
+ params.time,
9516
+ params.day
9517
+ );
9518
+ }
9519
+ const expandedVars = expandEnvVarsInObject(params.vars);
9520
+ const expandedSecrets = expandEnvVarsInObject(params.secrets);
9521
+ console.log(
9522
+ `
9523
+ Deploying schedule for agent ${chalk31.cyan(params.agentName)}...`
9524
+ );
9525
+ const deployResult = await deploySchedule({
9526
+ name: params.scheduleName,
9527
+ composeId: params.composeId,
9528
+ cronExpression,
9529
+ atTime: atTimeISO,
9530
+ timezone: params.timezone,
9531
+ prompt: params.prompt,
9532
+ vars: expandedVars,
9533
+ secrets: expandedSecrets,
9534
+ artifactName: params.artifactName
9535
+ });
9536
+ displayDeployResult(params.agentName, deployResult);
9537
+ }
9538
+ function handleSetupError(error) {
9539
+ console.error(chalk31.red("\u2717 Failed to setup schedule"));
9540
+ if (error instanceof Error) {
9541
+ if (error.message.includes("Not authenticated")) {
9542
+ console.error(chalk31.dim(" Run: vm0 auth login"));
9543
+ } else {
9544
+ console.error(chalk31.dim(` ${error.message}`));
9545
+ }
9546
+ }
9547
+ process.exit(1);
9548
+ }
9549
+ function displayDeployResult(agentName, deployResult) {
9550
+ if (deployResult.created) {
9551
+ console.log(
9552
+ chalk31.green(`\u2713 Created schedule for agent ${chalk31.cyan(agentName)}`)
9553
+ );
9554
+ } else {
9555
+ console.log(
9556
+ chalk31.green(`\u2713 Updated schedule for agent ${chalk31.cyan(agentName)}`)
9557
+ );
9558
+ }
9559
+ console.log(chalk31.dim(` Timezone: ${deployResult.schedule.timezone}`));
9560
+ if (deployResult.schedule.cronExpression) {
9561
+ console.log(chalk31.dim(` Cron: ${deployResult.schedule.cronExpression}`));
9562
+ if (deployResult.schedule.nextRunAt) {
9563
+ const nextRun = formatInTimezone(
9564
+ deployResult.schedule.nextRunAt,
9565
+ deployResult.schedule.timezone
9566
+ );
9567
+ console.log(chalk31.dim(` Next run: ${nextRun}`));
9568
+ }
9569
+ } else if (deployResult.schedule.atTime) {
9570
+ const atTimeFormatted = formatInTimezone(
9571
+ deployResult.schedule.atTime,
9572
+ deployResult.schedule.timezone
9573
+ );
9574
+ console.log(chalk31.dim(` At: ${atTimeFormatted}`));
9575
+ }
9576
+ }
9577
+ 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) => {
10048
9578
  try {
10049
- if (!existsSync13(file)) {
10050
- console.error(chalk33.red(`\u2717 File not found: ${file}`));
10051
- console.error(chalk33.dim(" Create a schedule.yaml file first"));
10052
- process.exit(1);
9579
+ const { composeId, scheduleName } = await resolveAgent(agentName);
9580
+ const existingSchedule = await findExistingSchedule(agentName);
9581
+ console.log(
9582
+ chalk31.dim(
9583
+ existingSchedule ? `Editing existing schedule for agent ${agentName}` : `Creating new schedule for agent ${agentName}`
9584
+ )
9585
+ );
9586
+ const defaults = getExistingDefaults(existingSchedule);
9587
+ const frequency = await gatherFrequency(
9588
+ options.frequency,
9589
+ defaults.frequency
9590
+ );
9591
+ if (!frequency) {
9592
+ console.log(chalk31.dim("Cancelled"));
9593
+ return;
10053
9594
  }
10054
- const content = readFileSync3(file, "utf-8");
10055
- let parsed;
10056
- try {
10057
- parsed = parseYaml7(content);
10058
- } catch (err) {
10059
- console.error(chalk33.red("\u2717 Invalid YAML syntax"));
10060
- if (err instanceof Error) {
10061
- console.error(chalk33.dim(` ${err.message}`));
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;
10062
9607
  }
10063
- process.exit(1);
10064
- }
10065
- const result = scheduleYamlSchema.safeParse(parsed);
10066
- if (!result.success) {
10067
- console.error(chalk33.red("\u2717 Invalid schedule.yaml format"));
10068
- for (const issue of result.error.issues) {
10069
- console.error(
10070
- chalk33.dim(` ${issue.path.join(".")}: ${issue.message}`)
10071
- );
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;
10072
9614
  }
10073
- process.exit(1);
10074
- }
10075
- const scheduleYaml = result.data;
10076
- const scheduleEntries = Object.entries(scheduleYaml.schedules);
10077
- if (scheduleEntries.length === 0) {
10078
- console.error(chalk33.red("\u2717 No schedules defined in file"));
10079
- process.exit(1);
10080
- }
10081
- if (scheduleEntries.length > 1) {
10082
- console.error(
10083
- chalk33.red("\u2717 Multiple schedules per file not supported yet")
9615
+ const timeResult = await gatherRecurringTime(
9616
+ options.time,
9617
+ defaults.time
10084
9618
  );
10085
- console.error(chalk33.dim(" Please use one schedule per file"));
10086
- process.exit(1);
9619
+ if (!timeResult) {
9620
+ console.log(chalk31.dim("Cancelled"));
9621
+ return;
9622
+ }
9623
+ time = timeResult;
10087
9624
  }
10088
- const [scheduleName, schedule] = scheduleEntries[0];
10089
- console.log(`Deploying schedule ${chalk33.cyan(scheduleName)}...`);
10090
- const agentRef = schedule.run.agent;
10091
- const namePart = agentRef.includes("/") ? agentRef.split("/").pop() : agentRef;
10092
- const agentName = namePart.includes(":") ? namePart.split(":")[0] : namePart;
10093
- const compose = await getComposeByName(agentName);
10094
- if (!compose) {
10095
- console.error(chalk33.red(`\u2717 Agent not found: ${agentRef}`));
10096
- console.error(chalk33.dim(" Make sure the agent is pushed first"));
10097
- process.exit(1);
9625
+ const timezone = await gatherTimezone(
9626
+ options.timezone,
9627
+ existingSchedule?.timezone
9628
+ );
9629
+ if (!timezone) {
9630
+ console.log(chalk31.dim("Cancelled"));
9631
+ return;
9632
+ }
9633
+ const promptText_ = await gatherPromptText(
9634
+ options.prompt,
9635
+ existingSchedule?.prompt
9636
+ );
9637
+ if (!promptText_) {
9638
+ console.log(chalk31.dim("Cancelled"));
9639
+ return;
10098
9640
  }
10099
- const composeId = compose.id;
10100
- const expandedVars = expandEnvVarsInObject(schedule.run.vars);
10101
- const expandedSecrets = expandEnvVarsInObject(schedule.run.secrets);
10102
- const atTime = schedule.on.at ? toISODateTime(schedule.on.at) : void 0;
10103
- const body = {
10104
- name: scheduleName,
9641
+ const vars = await gatherVars(options.var || [], existingSchedule?.vars);
9642
+ const secrets = await gatherSecrets(
9643
+ options.secret || [],
9644
+ existingSchedule?.secretNames
9645
+ );
9646
+ await buildAndDeploy({
9647
+ scheduleName,
10105
9648
  composeId,
10106
- cronExpression: schedule.on.cron,
9649
+ agentName,
9650
+ frequency,
9651
+ time,
9652
+ day,
10107
9653
  atTime,
10108
- timezone: schedule.on.timezone || "UTC",
10109
- prompt: schedule.run.prompt,
10110
- vars: expandedVars,
10111
- secrets: expandedSecrets,
10112
- artifactName: schedule.run.artifactName,
10113
- artifactVersion: schedule.run.artifactVersion,
10114
- volumeVersions: schedule.run.volumeVersions
10115
- };
10116
- const deployResult = await deploySchedule(body);
10117
- if (deployResult.created) {
10118
- console.log(
10119
- chalk33.green(`\u2713 Created schedule ${chalk33.cyan(scheduleName)}`)
10120
- );
10121
- } else {
10122
- console.log(
10123
- chalk33.green(`\u2713 Updated schedule ${chalk33.cyan(scheduleName)}`)
10124
- );
10125
- }
10126
- console.log(chalk33.dim(` Timezone: ${deployResult.schedule.timezone}`));
10127
- if (deployResult.schedule.cronExpression) {
10128
- console.log(
10129
- chalk33.dim(` Cron: ${deployResult.schedule.cronExpression}`)
10130
- );
10131
- if (deployResult.schedule.nextRunAt) {
10132
- const nextRun = formatInTimezone(
10133
- deployResult.schedule.nextRunAt,
10134
- deployResult.schedule.timezone
10135
- );
10136
- console.log(chalk33.dim(` Next run: ${nextRun}`));
10137
- }
10138
- } else if (deployResult.schedule.atTime) {
10139
- const atTime2 = formatInTimezone(
10140
- deployResult.schedule.atTime,
10141
- deployResult.schedule.timezone
10142
- );
10143
- console.log(chalk33.dim(` At: ${atTime2}`));
10144
- }
9654
+ timezone,
9655
+ prompt: promptText_,
9656
+ vars,
9657
+ secrets,
9658
+ artifactName: options.artifactName
9659
+ });
10145
9660
  } catch (error) {
10146
- console.error(chalk33.red("\u2717 Failed to deploy schedule"));
10147
- if (error instanceof Error) {
10148
- if (error.message.includes("Not authenticated")) {
10149
- console.error(chalk33.dim(" Run: vm0 auth login"));
10150
- } else {
10151
- console.error(chalk33.dim(` ${error.message}`));
10152
- }
10153
- }
10154
- process.exit(1);
9661
+ handleSetupError(error);
10155
9662
  }
10156
9663
  });
10157
9664
 
10158
9665
  // src/commands/schedule/list.ts
10159
- import { Command as Command31 } from "commander";
10160
- import chalk34 from "chalk";
10161
- var listCommand4 = new Command31().name("list").alias("ls").description("List all schedules").action(async () => {
9666
+ import { Command as Command29 } from "commander";
9667
+ import chalk32 from "chalk";
9668
+ var listCommand4 = new Command29().name("list").alias("ls").description("List all schedules").action(async () => {
10162
9669
  try {
10163
9670
  const result = await listSchedules();
10164
9671
  if (result.schedules.length === 0) {
10165
- console.log(chalk34.dim("No schedules found"));
9672
+ console.log(chalk32.dim("No schedules found"));
10166
9673
  console.log(
10167
- chalk34.dim(" Create one with: vm0 schedule deploy schedule.yaml")
9674
+ chalk32.dim(" Create one with: vm0 schedule setup <agent-name>")
10168
9675
  );
10169
9676
  return;
10170
9677
  }
10171
- const nameWidth = Math.max(
10172
- 4,
10173
- ...result.schedules.map((s) => s.name.length)
10174
- );
10175
9678
  const agentWidth = Math.max(
10176
9679
  5,
10177
9680
  ...result.schedules.map((s) => s.composeName.length)
@@ -10183,19 +9686,17 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
10183
9686
  )
10184
9687
  );
10185
9688
  const header = [
10186
- "NAME".padEnd(nameWidth),
10187
9689
  "AGENT".padEnd(agentWidth),
10188
9690
  "TRIGGER".padEnd(triggerWidth),
10189
9691
  "STATUS".padEnd(8),
10190
9692
  "NEXT RUN"
10191
9693
  ].join(" ");
10192
- console.log(chalk34.dim(header));
9694
+ console.log(chalk32.dim(header));
10193
9695
  for (const schedule of result.schedules) {
10194
9696
  const trigger = schedule.cronExpression ? `${schedule.cronExpression} (${schedule.timezone})` : schedule.atTime || "-";
10195
- const status = schedule.enabled ? chalk34.green("enabled") : chalk34.yellow("disabled");
9697
+ const status = schedule.enabled ? chalk32.green("enabled") : chalk32.yellow("disabled");
10196
9698
  const nextRun = schedule.enabled ? formatRelativeTime2(schedule.nextRunAt) : "-";
10197
9699
  const row = [
10198
- schedule.name.padEnd(nameWidth),
10199
9700
  schedule.composeName.padEnd(agentWidth),
10200
9701
  trigger.padEnd(triggerWidth),
10201
9702
  status.padEnd(8 + (schedule.enabled ? 0 : 2)),
@@ -10205,12 +9706,12 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
10205
9706
  console.log(row);
10206
9707
  }
10207
9708
  } catch (error) {
10208
- console.error(chalk34.red("\u2717 Failed to list schedules"));
9709
+ console.error(chalk32.red("\u2717 Failed to list schedules"));
10209
9710
  if (error instanceof Error) {
10210
9711
  if (error.message.includes("Not authenticated")) {
10211
- console.error(chalk34.dim(" Run: vm0 auth login"));
9712
+ console.error(chalk32.dim(" Run: vm0 auth login"));
10212
9713
  } else {
10213
- console.error(chalk34.dim(` ${error.message}`));
9714
+ console.error(chalk32.dim(` ${error.message}`));
10214
9715
  }
10215
9716
  }
10216
9717
  process.exit(1);
@@ -10218,213 +9719,181 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
10218
9719
  });
10219
9720
 
10220
9721
  // src/commands/schedule/status.ts
10221
- import { Command as Command32 } from "commander";
10222
- import chalk35 from "chalk";
9722
+ import { Command as Command30 } from "commander";
9723
+ import chalk33 from "chalk";
10223
9724
  function formatDateTimeStyled(dateStr) {
10224
- if (!dateStr) return chalk35.dim("-");
9725
+ if (!dateStr) return chalk33.dim("-");
10225
9726
  const formatted = formatDateTime(dateStr);
10226
- return formatted.replace(/\(([^)]+)\)$/, chalk35.dim("($1)"));
9727
+ return formatted.replace(/\(([^)]+)\)$/, chalk33.dim("($1)"));
10227
9728
  }
10228
9729
  function formatTrigger(schedule) {
10229
9730
  if (schedule.cronExpression) {
10230
9731
  return schedule.cronExpression;
10231
9732
  }
10232
9733
  if (schedule.atTime) {
10233
- return `${schedule.atTime} ${chalk35.dim("(one-time)")}`;
9734
+ return `${schedule.atTime} ${chalk33.dim("(one-time)")}`;
10234
9735
  }
10235
- return chalk35.dim("-");
9736
+ return chalk33.dim("-");
10236
9737
  }
10237
9738
  function formatRunStatus(status) {
10238
9739
  switch (status) {
10239
9740
  case "completed":
10240
- return chalk35.green(status);
9741
+ return chalk33.green(status);
10241
9742
  case "failed":
10242
9743
  case "timeout":
10243
- return chalk35.red(status);
9744
+ return chalk33.red(status);
10244
9745
  case "running":
10245
- return chalk35.blue(status);
9746
+ return chalk33.blue(status);
10246
9747
  case "pending":
10247
- return chalk35.yellow(status);
9748
+ return chalk33.yellow(status);
10248
9749
  default:
10249
9750
  return status;
10250
9751
  }
10251
9752
  }
10252
- var statusCommand4 = new Command32().name("status").description("Show detailed status of a schedule").argument(
10253
- "[name]",
10254
- "Schedule name (auto-detected from schedule.yaml if omitted)"
10255
- ).option(
10256
- "-l, --limit <number>",
10257
- "Number of recent runs to show (0 to hide)",
10258
- "5"
10259
- ).action(async (nameArg, options) => {
10260
- try {
10261
- let name = nameArg;
10262
- if (!name) {
10263
- const scheduleResult = loadScheduleName();
10264
- if (scheduleResult.error) {
10265
- console.error(chalk35.red(`\u2717 ${scheduleResult.error}`));
10266
- process.exit(1);
10267
- }
10268
- if (!scheduleResult.scheduleName) {
10269
- console.error(chalk35.red("\u2717 Schedule name required"));
10270
- console.error(
10271
- chalk35.dim(
10272
- " Provide name or run from directory with schedule.yaml"
10273
- )
10274
- );
10275
- process.exit(1);
10276
- }
10277
- name = scheduleResult.scheduleName;
10278
- }
10279
- const resolved = await resolveScheduleByName(name);
10280
- const composeId = resolved.composeId;
10281
- const schedule = await getScheduleByName({ name, composeId });
10282
- console.log();
10283
- console.log(`Schedule: ${chalk35.cyan(schedule.name)}`);
10284
- console.log(chalk35.dim("\u2501".repeat(50)));
10285
- const statusText = schedule.enabled ? chalk35.green("enabled") : chalk35.yellow("disabled");
10286
- console.log(`${"Status:".padEnd(16)}${statusText}`);
9753
+ function printRunConfiguration(schedule) {
9754
+ const statusText = schedule.enabled ? chalk33.green("enabled") : chalk33.yellow("disabled");
9755
+ console.log(`${"Status:".padEnd(16)}${statusText}`);
9756
+ console.log(
9757
+ `${"Agent:".padEnd(16)}${schedule.composeName} ${chalk33.dim(`(${schedule.scopeSlug})`)}`
9758
+ );
9759
+ const promptPreview = schedule.prompt.length > 60 ? schedule.prompt.slice(0, 57) + "..." : schedule.prompt;
9760
+ console.log(`${"Prompt:".padEnd(16)}${chalk33.dim(promptPreview)}`);
9761
+ if (schedule.vars && Object.keys(schedule.vars).length > 0) {
10287
9762
  console.log(
10288
- `${"Agent:".padEnd(16)}${schedule.composeName} ${chalk35.dim(`(${schedule.scopeSlug})`)}`
9763
+ `${"Variables:".padEnd(16)}${Object.keys(schedule.vars).join(", ")}`
10289
9764
  );
10290
- const promptPreview = schedule.prompt.length > 60 ? schedule.prompt.slice(0, 57) + "..." : schedule.prompt;
10291
- console.log(`${"Prompt:".padEnd(16)}${chalk35.dim(promptPreview)}`);
10292
- if (schedule.vars && Object.keys(schedule.vars).length > 0) {
10293
- console.log(
10294
- `${"Variables:".padEnd(16)}${Object.keys(schedule.vars).join(", ")}`
10295
- );
10296
- }
10297
- if (schedule.secretNames && schedule.secretNames.length > 0) {
10298
- console.log(
10299
- `${"Secrets:".padEnd(16)}${schedule.secretNames.join(", ")}`
10300
- );
10301
- }
10302
- if (schedule.artifactName) {
10303
- const artifactInfo = schedule.artifactVersion ? `${schedule.artifactName}:${schedule.artifactVersion}` : schedule.artifactName;
10304
- console.log(`${"Artifact:".padEnd(16)}${artifactInfo}`);
10305
- }
10306
- if (schedule.volumeVersions && Object.keys(schedule.volumeVersions).length > 0) {
9765
+ }
9766
+ if (schedule.secretNames && schedule.secretNames.length > 0) {
9767
+ console.log(`${"Secrets:".padEnd(16)}${schedule.secretNames.join(", ")}`);
9768
+ }
9769
+ if (schedule.artifactName) {
9770
+ const artifactInfo = schedule.artifactVersion ? `${schedule.artifactName}:${schedule.artifactVersion}` : schedule.artifactName;
9771
+ console.log(`${"Artifact:".padEnd(16)}${artifactInfo}`);
9772
+ }
9773
+ if (schedule.volumeVersions && Object.keys(schedule.volumeVersions).length > 0) {
9774
+ console.log(
9775
+ `${"Volumes:".padEnd(16)}${Object.keys(schedule.volumeVersions).join(", ")}`
9776
+ );
9777
+ }
9778
+ }
9779
+ function printTimeSchedule(schedule) {
9780
+ console.log();
9781
+ console.log(`${"Trigger:".padEnd(16)}${formatTrigger(schedule)}`);
9782
+ console.log(`${"Timezone:".padEnd(16)}${detectTimezone()}`);
9783
+ if (schedule.enabled) {
9784
+ console.log(
9785
+ `${"Next Run:".padEnd(16)}${formatDateTimeStyled(schedule.nextRunAt)}`
9786
+ );
9787
+ }
9788
+ }
9789
+ async function printRecentRuns(name, composeId, limit) {
9790
+ if (limit <= 0) return;
9791
+ try {
9792
+ const { runs } = await listScheduleRuns({ name, composeId, limit });
9793
+ if (runs.length > 0) {
9794
+ console.log();
9795
+ console.log("Recent Runs:");
10307
9796
  console.log(
10308
- `${"Volumes:".padEnd(16)}${Object.keys(schedule.volumeVersions).join(", ")}`
9797
+ chalk33.dim("RUN ID STATUS CREATED")
10309
9798
  );
9799
+ for (const run of runs) {
9800
+ const id = run.id;
9801
+ const status = formatRunStatus(run.status).padEnd(10);
9802
+ const created = formatDateTimeStyled(run.createdAt);
9803
+ console.log(`${id} ${status} ${created}`);
9804
+ }
10310
9805
  }
9806
+ } catch {
10311
9807
  console.log();
10312
- console.log(`${"Trigger:".padEnd(16)}${formatTrigger(schedule)}`);
10313
- console.log(`${"Timezone:".padEnd(16)}${detectTimezone()}`);
10314
- if (schedule.enabled) {
10315
- console.log(
10316
- `${"Next Run:".padEnd(16)}${formatDateTimeStyled(schedule.nextRunAt)}`
10317
- );
9808
+ console.log(chalk33.dim("Recent Runs: (unable to fetch)"));
9809
+ }
9810
+ }
9811
+ function handleStatusError(error, agentName) {
9812
+ console.error(chalk33.red("\u2717 Failed to get schedule status"));
9813
+ if (error instanceof Error) {
9814
+ if (error.message.includes("Not authenticated")) {
9815
+ console.error(chalk33.dim(" Run: vm0 auth login"));
9816
+ } else if (error.message.includes("not found") || error.message.includes("Not found") || error.message.includes("No schedule found")) {
9817
+ console.error(chalk33.dim(` No schedule found for agent "${agentName}"`));
9818
+ console.error(chalk33.dim(" Run: vm0 schedule list"));
9819
+ } else {
9820
+ console.error(chalk33.dim(` ${error.message}`));
10318
9821
  }
9822
+ }
9823
+ process.exit(1);
9824
+ }
9825
+ var statusCommand5 = new Command30().name("status").description("Show detailed status of a schedule").argument("<agent-name>", "Agent name").option(
9826
+ "-l, --limit <number>",
9827
+ "Number of recent runs to show (0 to hide)",
9828
+ "5"
9829
+ ).action(async (agentName, options) => {
9830
+ try {
9831
+ const resolved = await resolveScheduleByAgent(agentName);
9832
+ const { name, composeId } = resolved;
9833
+ const schedule = await getScheduleByName({ name, composeId });
9834
+ console.log();
9835
+ console.log(`Schedule for agent: ${chalk33.cyan(agentName)}`);
9836
+ console.log(chalk33.dim("\u2501".repeat(50)));
9837
+ printRunConfiguration(schedule);
9838
+ printTimeSchedule(schedule);
10319
9839
  const limit = Math.min(
10320
9840
  Math.max(0, parseInt(options.limit, 10) || 5),
10321
9841
  100
10322
9842
  );
10323
- if (limit > 0) {
10324
- try {
10325
- const { runs } = await listScheduleRuns({
10326
- name,
10327
- composeId,
10328
- limit
10329
- });
10330
- if (runs.length > 0) {
10331
- console.log();
10332
- console.log("Recent Runs:");
10333
- console.log(
10334
- chalk35.dim(
10335
- "RUN ID STATUS CREATED"
10336
- )
10337
- );
10338
- for (const run of runs) {
10339
- const id = run.id;
10340
- const status = formatRunStatus(run.status).padEnd(10);
10341
- const created = formatDateTimeStyled(run.createdAt);
10342
- console.log(`${id} ${status} ${created}`);
10343
- }
10344
- }
10345
- } catch {
10346
- console.log();
10347
- console.log(chalk35.dim("Recent Runs: (unable to fetch)"));
10348
- }
10349
- }
9843
+ await printRecentRuns(name, composeId, limit);
10350
9844
  console.log();
10351
9845
  } catch (error) {
10352
- console.error(chalk35.red("\u2717 Failed to get schedule status"));
10353
- if (error instanceof Error) {
10354
- if (error.message.includes("Not authenticated")) {
10355
- console.error(chalk35.dim(" Run: vm0 auth login"));
10356
- } else if (error.message.includes("not found") || error.message.includes("Not found")) {
10357
- console.error(
10358
- chalk35.dim(` Schedule "${nameArg ?? "unknown"}" not found`)
10359
- );
10360
- console.error(chalk35.dim(" Run: vm0 schedule list"));
10361
- } else {
10362
- console.error(chalk35.dim(` ${error.message}`));
10363
- }
10364
- }
10365
- process.exit(1);
9846
+ handleStatusError(error, agentName);
10366
9847
  }
10367
9848
  });
10368
9849
 
10369
9850
  // src/commands/schedule/delete.ts
10370
- import { Command as Command33 } from "commander";
10371
- import chalk36 from "chalk";
9851
+ import { Command as Command31 } from "commander";
9852
+ import chalk34 from "chalk";
10372
9853
  import * as readline from "readline";
10373
9854
  async function confirm(message) {
10374
9855
  const rl = readline.createInterface({
10375
9856
  input: process.stdin,
10376
9857
  output: process.stdout
10377
9858
  });
10378
- return new Promise((resolve2) => {
9859
+ return new Promise((resolve) => {
10379
9860
  rl.question(`${message} (y/N) `, (answer) => {
10380
9861
  rl.close();
10381
- resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
9862
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
10382
9863
  });
10383
9864
  });
10384
9865
  }
10385
- var deleteCommand = new Command33().name("delete").alias("rm").description("Delete a schedule").argument(
10386
- "[name]",
10387
- "Schedule name (auto-detected from schedule.yaml if omitted)"
10388
- ).option("-f, --force", "Skip confirmation prompt").action(async (nameArg, options) => {
10389
- let name = nameArg;
9866
+ var deleteCommand = new Command31().name("delete").alias("rm").description("Delete a schedule").argument("<agent-name>", "Agent name").option("-f, --force", "Skip confirmation prompt").action(async (agentName, options) => {
10390
9867
  try {
10391
- if (!name) {
10392
- const scheduleResult = loadScheduleName();
10393
- if (scheduleResult.error) {
10394
- console.error(chalk36.red(`\u2717 ${scheduleResult.error}`));
10395
- process.exit(1);
10396
- }
10397
- if (!scheduleResult.scheduleName) {
10398
- console.error(chalk36.red("\u2717 Schedule name required"));
10399
- console.error(
10400
- chalk36.dim(
10401
- " Provide name or run from directory with schedule.yaml"
10402
- )
10403
- );
10404
- process.exit(1);
10405
- }
10406
- name = scheduleResult.scheduleName;
10407
- }
10408
- const resolved = await resolveScheduleByName(name);
9868
+ const resolved = await resolveScheduleByAgent(agentName);
10409
9869
  if (!options.force) {
10410
- const confirmed = await confirm(`Delete schedule ${chalk36.cyan(name)}?`);
9870
+ const confirmed = await confirm(
9871
+ `Delete schedule for agent ${chalk34.cyan(agentName)}?`
9872
+ );
10411
9873
  if (!confirmed) {
10412
- console.log(chalk36.dim("Cancelled"));
9874
+ console.log(chalk34.dim("Cancelled"));
10413
9875
  return;
10414
9876
  }
10415
9877
  }
10416
- await deleteSchedule({ name, composeId: resolved.composeId });
10417
- console.log(chalk36.green(`\u2713 Deleted schedule ${chalk36.cyan(name)}`));
9878
+ await deleteSchedule({
9879
+ name: resolved.name,
9880
+ composeId: resolved.composeId
9881
+ });
9882
+ console.log(
9883
+ chalk34.green(`\u2713 Deleted schedule for agent ${chalk34.cyan(agentName)}`)
9884
+ );
10418
9885
  } catch (error) {
10419
- console.error(chalk36.red("\u2717 Failed to delete schedule"));
9886
+ console.error(chalk34.red("\u2717 Failed to delete schedule"));
10420
9887
  if (error instanceof Error) {
10421
9888
  if (error.message.includes("Not authenticated")) {
10422
- console.error(chalk36.dim(" Run: vm0 auth login"));
10423
- } else if (error.message.toLowerCase().includes("not found")) {
10424
- console.error(chalk36.dim(` Schedule "${name}" not found`));
10425
- console.error(chalk36.dim(" Run: vm0 schedule list"));
9889
+ console.error(chalk34.dim(" Run: vm0 auth login"));
9890
+ } else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
9891
+ console.error(
9892
+ chalk34.dim(` No schedule found for agent "${agentName}"`)
9893
+ );
9894
+ console.error(chalk34.dim(" Run: vm0 schedule list"));
10426
9895
  } else {
10427
- console.error(chalk36.dim(` ${error.message}`));
9896
+ console.error(chalk34.dim(` ${error.message}`));
10428
9897
  }
10429
9898
  }
10430
9899
  process.exit(1);
@@ -10432,44 +9901,30 @@ var deleteCommand = new Command33().name("delete").alias("rm").description("Dele
10432
9901
  });
10433
9902
 
10434
9903
  // src/commands/schedule/enable.ts
10435
- import { Command as Command34 } from "commander";
10436
- import chalk37 from "chalk";
10437
- var enableCommand = new Command34().name("enable").description("Enable a schedule").argument(
10438
- "[name]",
10439
- "Schedule name (auto-detected from schedule.yaml if omitted)"
10440
- ).action(async (nameArg) => {
9904
+ import { Command as Command32 } from "commander";
9905
+ import chalk35 from "chalk";
9906
+ var enableCommand = new Command32().name("enable").description("Enable a schedule").argument("<agent-name>", "Agent name").action(async (agentName) => {
10441
9907
  try {
10442
- let name = nameArg;
10443
- if (!name) {
10444
- const scheduleResult = loadScheduleName();
10445
- if (scheduleResult.error) {
10446
- console.error(chalk37.red(`\u2717 ${scheduleResult.error}`));
10447
- process.exit(1);
10448
- }
10449
- if (!scheduleResult.scheduleName) {
10450
- console.error(chalk37.red("\u2717 Schedule name required"));
10451
- console.error(
10452
- chalk37.dim(
10453
- " Provide name or run from directory with schedule.yaml"
10454
- )
10455
- );
10456
- process.exit(1);
10457
- }
10458
- name = scheduleResult.scheduleName;
10459
- }
10460
- const resolved = await resolveScheduleByName(name);
10461
- await enableSchedule({ name, composeId: resolved.composeId });
10462
- console.log(chalk37.green(`\u2713 Enabled schedule ${chalk37.cyan(name)}`));
9908
+ const resolved = await resolveScheduleByAgent(agentName);
9909
+ await enableSchedule({
9910
+ name: resolved.name,
9911
+ composeId: resolved.composeId
9912
+ });
9913
+ console.log(
9914
+ chalk35.green(`\u2713 Enabled schedule for agent ${chalk35.cyan(agentName)}`)
9915
+ );
10463
9916
  } catch (error) {
10464
- console.error(chalk37.red("\u2717 Failed to enable schedule"));
9917
+ console.error(chalk35.red("\u2717 Failed to enable schedule"));
10465
9918
  if (error instanceof Error) {
10466
9919
  if (error.message.includes("Not authenticated")) {
10467
- console.error(chalk37.dim(" Run: vm0 auth login"));
10468
- } else if (error.message.toLowerCase().includes("not found")) {
10469
- console.error(chalk37.dim(` Schedule "${nameArg}" not found`));
10470
- console.error(chalk37.dim(" Run: vm0 schedule list"));
9920
+ console.error(chalk35.dim(" Run: vm0 auth login"));
9921
+ } else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
9922
+ console.error(
9923
+ chalk35.dim(` No schedule found for agent "${agentName}"`)
9924
+ );
9925
+ console.error(chalk35.dim(" Run: vm0 schedule list"));
10471
9926
  } else {
10472
- console.error(chalk37.dim(` ${error.message}`));
9927
+ console.error(chalk35.dim(` ${error.message}`));
10473
9928
  }
10474
9929
  }
10475
9930
  process.exit(1);
@@ -10477,44 +9932,30 @@ var enableCommand = new Command34().name("enable").description("Enable a schedul
10477
9932
  });
10478
9933
 
10479
9934
  // src/commands/schedule/disable.ts
10480
- import { Command as Command35 } from "commander";
10481
- import chalk38 from "chalk";
10482
- var disableCommand = new Command35().name("disable").description("Disable a schedule").argument(
10483
- "[name]",
10484
- "Schedule name (auto-detected from schedule.yaml if omitted)"
10485
- ).action(async (nameArg) => {
9935
+ import { Command as Command33 } from "commander";
9936
+ import chalk36 from "chalk";
9937
+ var disableCommand = new Command33().name("disable").description("Disable a schedule").argument("<agent-name>", "Agent name").action(async (agentName) => {
10486
9938
  try {
10487
- let name = nameArg;
10488
- if (!name) {
10489
- const scheduleResult = loadScheduleName();
10490
- if (scheduleResult.error) {
10491
- console.error(chalk38.red(`\u2717 ${scheduleResult.error}`));
10492
- process.exit(1);
10493
- }
10494
- if (!scheduleResult.scheduleName) {
10495
- console.error(chalk38.red("\u2717 Schedule name required"));
10496
- console.error(
10497
- chalk38.dim(
10498
- " Provide name or run from directory with schedule.yaml"
10499
- )
10500
- );
10501
- process.exit(1);
10502
- }
10503
- name = scheduleResult.scheduleName;
10504
- }
10505
- const resolved = await resolveScheduleByName(name);
10506
- await disableSchedule({ name, composeId: resolved.composeId });
10507
- console.log(chalk38.green(`\u2713 Disabled schedule ${chalk38.cyan(name)}`));
9939
+ const resolved = await resolveScheduleByAgent(agentName);
9940
+ await disableSchedule({
9941
+ name: resolved.name,
9942
+ composeId: resolved.composeId
9943
+ });
9944
+ console.log(
9945
+ chalk36.green(`\u2713 Disabled schedule for agent ${chalk36.cyan(agentName)}`)
9946
+ );
10508
9947
  } catch (error) {
10509
- console.error(chalk38.red("\u2717 Failed to disable schedule"));
9948
+ console.error(chalk36.red("\u2717 Failed to disable schedule"));
10510
9949
  if (error instanceof Error) {
10511
9950
  if (error.message.includes("Not authenticated")) {
10512
- console.error(chalk38.dim(" Run: vm0 auth login"));
10513
- } else if (error.message.toLowerCase().includes("not found")) {
10514
- console.error(chalk38.dim(` Schedule "${nameArg}" not found`));
10515
- console.error(chalk38.dim(" Run: vm0 schedule list"));
9951
+ console.error(chalk36.dim(" Run: vm0 auth login"));
9952
+ } else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
9953
+ console.error(
9954
+ chalk36.dim(` No schedule found for agent "${agentName}"`)
9955
+ );
9956
+ console.error(chalk36.dim(" Run: vm0 schedule list"));
10516
9957
  } else {
10517
- console.error(chalk38.dim(` ${error.message}`));
9958
+ console.error(chalk36.dim(` ${error.message}`));
10518
9959
  }
10519
9960
  }
10520
9961
  process.exit(1);
@@ -10522,11 +9963,11 @@ var disableCommand = new Command35().name("disable").description("Disable a sche
10522
9963
  });
10523
9964
 
10524
9965
  // src/commands/schedule/index.ts
10525
- var scheduleCommand = new Command36().name("schedule").description("Manage agent schedules").addCommand(initCommand4).addCommand(deployCommand).addCommand(listCommand4).addCommand(statusCommand4).addCommand(deleteCommand).addCommand(enableCommand).addCommand(disableCommand);
9966
+ var scheduleCommand = new Command34().name("schedule").description("Manage agent schedules").addCommand(setupCommand).addCommand(listCommand4).addCommand(statusCommand5).addCommand(deleteCommand).addCommand(enableCommand).addCommand(disableCommand);
10526
9967
 
10527
9968
  // src/commands/usage.ts
10528
- import { Command as Command37 } from "commander";
10529
- import chalk39 from "chalk";
9969
+ import { Command as Command35 } from "commander";
9970
+ import chalk37 from "chalk";
10530
9971
 
10531
9972
  // src/lib/utils/duration-formatter.ts
10532
9973
  function formatDuration(ms) {
@@ -10599,7 +10040,7 @@ function fillMissingDates(daily, startDate, endDate) {
10599
10040
  result.sort((a, b) => b.date.localeCompare(a.date));
10600
10041
  return result;
10601
10042
  }
10602
- var usageCommand = new Command37().name("usage").description("View usage statistics").option("--since <date>", "Start date (ISO format or relative: 7d, 30d)").option(
10043
+ var usageCommand = new Command35().name("usage").description("View usage statistics").option("--since <date>", "Start date (ISO format or relative: 7d, 30d)").option(
10603
10044
  "--until <date>",
10604
10045
  "End date (ISO format or relative, defaults to now)"
10605
10046
  ).action(async (options) => {
@@ -10613,7 +10054,7 @@ var usageCommand = new Command37().name("usage").description("View usage statist
10613
10054
  endDate = new Date(untilMs);
10614
10055
  } catch {
10615
10056
  console.error(
10616
- chalk39.red(
10057
+ chalk37.red(
10617
10058
  "Error: Invalid --until format. Use ISO (2026-01-01) or relative (7d, 30d)"
10618
10059
  )
10619
10060
  );
@@ -10628,7 +10069,7 @@ var usageCommand = new Command37().name("usage").description("View usage statist
10628
10069
  startDate = new Date(sinceMs);
10629
10070
  } catch {
10630
10071
  console.error(
10631
- chalk39.red(
10072
+ chalk37.red(
10632
10073
  "Error: Invalid --since format. Use ISO (2026-01-01) or relative (7d, 30d)"
10633
10074
  )
10634
10075
  );
@@ -10638,13 +10079,13 @@ var usageCommand = new Command37().name("usage").description("View usage statist
10638
10079
  startDate = new Date(endDate.getTime() - DEFAULT_RANGE_MS);
10639
10080
  }
10640
10081
  if (startDate >= endDate) {
10641
- console.error(chalk39.red("Error: --since must be before --until"));
10082
+ console.error(chalk37.red("Error: --since must be before --until"));
10642
10083
  process.exit(1);
10643
10084
  }
10644
10085
  const rangeMs = endDate.getTime() - startDate.getTime();
10645
10086
  if (rangeMs > MAX_RANGE_MS) {
10646
10087
  console.error(
10647
- chalk39.red(
10088
+ chalk37.red(
10648
10089
  "Error: Time range exceeds maximum of 30 days. Use --until to specify an end date."
10649
10090
  )
10650
10091
  );
@@ -10661,19 +10102,19 @@ var usageCommand = new Command37().name("usage").description("View usage statist
10661
10102
  );
10662
10103
  console.log();
10663
10104
  console.log(
10664
- chalk39.bold(
10105
+ chalk37.bold(
10665
10106
  `Usage Summary (${formatDateRange(usage.period.start, usage.period.end)})`
10666
10107
  )
10667
10108
  );
10668
10109
  console.log();
10669
- console.log(chalk39.dim("DATE RUNS RUN TIME"));
10110
+ console.log(chalk37.dim("DATE RUNS RUN TIME"));
10670
10111
  for (const day of filledDaily) {
10671
10112
  const dateDisplay = formatDateDisplay(day.date).padEnd(10);
10672
10113
  const runsDisplay = String(day.run_count).padStart(6);
10673
10114
  const timeDisplay = formatDuration(day.run_time_ms);
10674
10115
  console.log(`${dateDisplay}${runsDisplay} ${timeDisplay}`);
10675
10116
  }
10676
- console.log(chalk39.dim("\u2500".repeat(29)));
10117
+ console.log(chalk37.dim("\u2500".repeat(29)));
10677
10118
  const totalRunsDisplay = String(usage.summary.total_runs).padStart(6);
10678
10119
  const totalTimeDisplay = formatDuration(usage.summary.total_run_time_ms);
10679
10120
  console.log(
@@ -10684,68 +10125,68 @@ var usageCommand = new Command37().name("usage").description("View usage statist
10684
10125
  if (error instanceof Error) {
10685
10126
  if (error.message.includes("Not authenticated")) {
10686
10127
  console.error(
10687
- chalk39.red("Error: Not authenticated. Run: vm0 auth login")
10128
+ chalk37.red("Error: Not authenticated. Run: vm0 auth login")
10688
10129
  );
10689
10130
  } else {
10690
- console.error(chalk39.red(`Error: ${error.message}`));
10131
+ console.error(chalk37.red(`Error: ${error.message}`));
10691
10132
  }
10692
10133
  } else {
10693
- console.error(chalk39.red("Error: An unexpected error occurred"));
10134
+ console.error(chalk37.red("Error: An unexpected error occurred"));
10694
10135
  }
10695
10136
  process.exit(1);
10696
10137
  }
10697
10138
  });
10698
10139
 
10699
10140
  // src/commands/credential/index.ts
10700
- import { Command as Command41 } from "commander";
10141
+ import { Command as Command39 } from "commander";
10701
10142
 
10702
10143
  // src/commands/credential/list.ts
10703
- import { Command as Command38 } from "commander";
10704
- import chalk40 from "chalk";
10705
- var listCommand5 = new Command38().name("list").alias("ls").description("List all credentials").action(async () => {
10144
+ import { Command as Command36 } from "commander";
10145
+ import chalk38 from "chalk";
10146
+ var listCommand5 = new Command36().name("list").alias("ls").description("List all credentials").action(async () => {
10706
10147
  try {
10707
10148
  const result = await listCredentials();
10708
10149
  if (result.credentials.length === 0) {
10709
- console.log(chalk40.dim("No credentials found."));
10150
+ console.log(chalk38.dim("No credentials found."));
10710
10151
  console.log();
10711
10152
  console.log("To add a credential:");
10712
- console.log(chalk40.cyan(" vm0 credential set MY_API_KEY <value>"));
10153
+ console.log(chalk38.cyan(" vm0 credential set MY_API_KEY <value>"));
10713
10154
  return;
10714
10155
  }
10715
- console.log(chalk40.bold("Credentials:"));
10156
+ console.log(chalk38.bold("Credentials:"));
10716
10157
  console.log();
10717
10158
  for (const credential of result.credentials) {
10718
- const typeIndicator = credential.type === "model-provider" ? chalk40.dim(" [model-provider]") : "";
10719
- console.log(` ${chalk40.cyan(credential.name)}${typeIndicator}`);
10159
+ const typeIndicator = credential.type === "model-provider" ? chalk38.dim(" [model-provider]") : "";
10160
+ console.log(` ${chalk38.cyan(credential.name)}${typeIndicator}`);
10720
10161
  if (credential.description) {
10721
- console.log(` ${chalk40.dim(credential.description)}`);
10162
+ console.log(` ${chalk38.dim(credential.description)}`);
10722
10163
  }
10723
10164
  console.log(
10724
- ` ${chalk40.dim(`Updated: ${new Date(credential.updatedAt).toLocaleString()}`)}`
10165
+ ` ${chalk38.dim(`Updated: ${new Date(credential.updatedAt).toLocaleString()}`)}`
10725
10166
  );
10726
10167
  console.log();
10727
10168
  }
10728
10169
  console.log(
10729
- chalk40.dim(`Total: ${result.credentials.length} credential(s)`)
10170
+ chalk38.dim(`Total: ${result.credentials.length} credential(s)`)
10730
10171
  );
10731
10172
  } catch (error) {
10732
10173
  if (error instanceof Error) {
10733
10174
  if (error.message.includes("Not authenticated")) {
10734
- console.error(chalk40.red("\u2717 Not authenticated. Run: vm0 auth login"));
10175
+ console.error(chalk38.red("\u2717 Not authenticated. Run: vm0 auth login"));
10735
10176
  } else {
10736
- console.error(chalk40.red(`\u2717 ${error.message}`));
10177
+ console.error(chalk38.red(`\u2717 ${error.message}`));
10737
10178
  }
10738
10179
  } else {
10739
- console.error(chalk40.red("\u2717 An unexpected error occurred"));
10180
+ console.error(chalk38.red("\u2717 An unexpected error occurred"));
10740
10181
  }
10741
10182
  process.exit(1);
10742
10183
  }
10743
10184
  });
10744
10185
 
10745
10186
  // src/commands/credential/set.ts
10746
- import { Command as Command39 } from "commander";
10747
- import chalk41 from "chalk";
10748
- var setCommand2 = new Command39().name("set").description("Create or update a credential").argument("<name>", "Credential name (uppercase, e.g., MY_API_KEY)").argument("<value>", "Credential value").option("-d, --description <description>", "Optional description").action(
10187
+ import { Command as Command37 } from "commander";
10188
+ import chalk39 from "chalk";
10189
+ var setCommand2 = new Command37().name("set").description("Create or update a credential").argument("<name>", "Credential name (uppercase, e.g., MY_API_KEY)").argument("<value>", "Credential value").option("-d, --description <description>", "Optional description").action(
10749
10190
  async (name, value, options) => {
10750
10191
  try {
10751
10192
  const credential = await setCredential({
@@ -10753,29 +10194,29 @@ var setCommand2 = new Command39().name("set").description("Create or update a cr
10753
10194
  value,
10754
10195
  description: options.description
10755
10196
  });
10756
- console.log(chalk41.green(`\u2713 Credential "${credential.name}" saved`));
10197
+ console.log(chalk39.green(`\u2713 Credential "${credential.name}" saved`));
10757
10198
  console.log();
10758
10199
  console.log("Use in vm0.yaml:");
10759
- console.log(chalk41.cyan(` environment:`));
10760
- console.log(chalk41.cyan(` ${name}: \${{ credentials.${name} }}`));
10200
+ console.log(chalk39.cyan(` environment:`));
10201
+ console.log(chalk39.cyan(` ${name}: \${{ credentials.${name} }}`));
10761
10202
  } catch (error) {
10762
10203
  if (error instanceof Error) {
10763
10204
  if (error.message.includes("Not authenticated")) {
10764
10205
  console.error(
10765
- chalk41.red("\u2717 Not authenticated. Run: vm0 auth login")
10206
+ chalk39.red("\u2717 Not authenticated. Run: vm0 auth login")
10766
10207
  );
10767
10208
  } else if (error.message.includes("must contain only uppercase")) {
10768
- console.error(chalk41.red(`\u2717 ${error.message}`));
10209
+ console.error(chalk39.red(`\u2717 ${error.message}`));
10769
10210
  console.log();
10770
10211
  console.log("Examples of valid credential names:");
10771
- console.log(chalk41.dim(" MY_API_KEY"));
10772
- console.log(chalk41.dim(" GITHUB_TOKEN"));
10773
- console.log(chalk41.dim(" AWS_ACCESS_KEY_ID"));
10212
+ console.log(chalk39.dim(" MY_API_KEY"));
10213
+ console.log(chalk39.dim(" GITHUB_TOKEN"));
10214
+ console.log(chalk39.dim(" AWS_ACCESS_KEY_ID"));
10774
10215
  } else {
10775
- console.error(chalk41.red(`\u2717 ${error.message}`));
10216
+ console.error(chalk39.red(`\u2717 ${error.message}`));
10776
10217
  }
10777
10218
  } else {
10778
- console.error(chalk41.red("\u2717 An unexpected error occurred"));
10219
+ console.error(chalk39.red("\u2717 An unexpected error occurred"));
10779
10220
  }
10780
10221
  process.exit(1);
10781
10222
  }
@@ -10783,14 +10224,14 @@ var setCommand2 = new Command39().name("set").description("Create or update a cr
10783
10224
  );
10784
10225
 
10785
10226
  // src/commands/credential/delete.ts
10786
- import { Command as Command40 } from "commander";
10787
- import chalk42 from "chalk";
10788
- var deleteCommand2 = new Command40().name("delete").description("Delete a credential").argument("<name>", "Credential name to delete").option("-y, --yes", "Skip confirmation prompt").action(async (name, options) => {
10227
+ import { Command as Command38 } from "commander";
10228
+ import chalk40 from "chalk";
10229
+ var deleteCommand2 = new Command38().name("delete").description("Delete a credential").argument("<name>", "Credential name to delete").option("-y, --yes", "Skip confirmation prompt").action(async (name, options) => {
10789
10230
  try {
10790
10231
  try {
10791
10232
  await getCredential(name);
10792
10233
  } catch {
10793
- console.error(chalk42.red(`\u2717 Credential "${name}" not found`));
10234
+ console.error(chalk40.red(`\u2717 Credential "${name}" not found`));
10794
10235
  process.exit(1);
10795
10236
  }
10796
10237
  if (!options.yes) {
@@ -10799,55 +10240,55 @@ var deleteCommand2 = new Command40().name("delete").description("Delete a creden
10799
10240
  input: process.stdin,
10800
10241
  output: process.stdout
10801
10242
  });
10802
- const confirmed = await new Promise((resolve2) => {
10243
+ const confirmed = await new Promise((resolve) => {
10803
10244
  rl.question(
10804
- chalk42.yellow(
10245
+ chalk40.yellow(
10805
10246
  `Are you sure you want to delete credential "${name}"? (y/N) `
10806
10247
  ),
10807
10248
  (answer) => {
10808
10249
  rl.close();
10809
- resolve2(answer.toLowerCase() === "y");
10250
+ resolve(answer.toLowerCase() === "y");
10810
10251
  }
10811
10252
  );
10812
10253
  });
10813
10254
  if (!confirmed) {
10814
- console.log(chalk42.dim("Cancelled."));
10255
+ console.log(chalk40.dim("Cancelled."));
10815
10256
  return;
10816
10257
  }
10817
10258
  }
10818
10259
  await deleteCredential(name);
10819
- console.log(chalk42.green(`\u2713 Credential "${name}" deleted`));
10260
+ console.log(chalk40.green(`\u2713 Credential "${name}" deleted`));
10820
10261
  } catch (error) {
10821
10262
  if (error instanceof Error) {
10822
10263
  if (error.message.includes("Not authenticated")) {
10823
- console.error(chalk42.red("\u2717 Not authenticated. Run: vm0 auth login"));
10264
+ console.error(chalk40.red("\u2717 Not authenticated. Run: vm0 auth login"));
10824
10265
  } else {
10825
- console.error(chalk42.red(`\u2717 ${error.message}`));
10266
+ console.error(chalk40.red(`\u2717 ${error.message}`));
10826
10267
  }
10827
10268
  } else {
10828
- console.error(chalk42.red("\u2717 An unexpected error occurred"));
10269
+ console.error(chalk40.red("\u2717 An unexpected error occurred"));
10829
10270
  }
10830
10271
  process.exit(1);
10831
10272
  }
10832
10273
  });
10833
10274
 
10834
10275
  // src/commands/credential/index.ts
10835
- var credentialCommand = new Command41().name("credential").description("Manage stored credentials for agent runs").addCommand(listCommand5).addCommand(setCommand2).addCommand(deleteCommand2);
10276
+ var credentialCommand = new Command39().name("credential").description("Manage stored credentials for agent runs").addCommand(listCommand5).addCommand(setCommand2).addCommand(deleteCommand2);
10836
10277
 
10837
10278
  // src/commands/model-provider/index.ts
10838
- import { Command as Command46 } from "commander";
10279
+ import { Command as Command44 } from "commander";
10839
10280
 
10840
10281
  // src/commands/model-provider/list.ts
10841
- import { Command as Command42 } from "commander";
10842
- import chalk43 from "chalk";
10843
- var listCommand6 = new Command42().name("list").alias("ls").description("List all model providers").action(async () => {
10282
+ import { Command as Command40 } from "commander";
10283
+ import chalk41 from "chalk";
10284
+ var listCommand6 = new Command40().name("list").alias("ls").description("List all model providers").action(async () => {
10844
10285
  try {
10845
10286
  const result = await listModelProviders();
10846
10287
  if (result.modelProviders.length === 0) {
10847
- console.log(chalk43.dim("No model providers configured."));
10288
+ console.log(chalk41.dim("No model providers configured."));
10848
10289
  console.log();
10849
10290
  console.log("To add a model provider:");
10850
- console.log(chalk43.cyan(" vm0 model-provider setup"));
10291
+ console.log(chalk41.cyan(" vm0 model-provider setup"));
10851
10292
  return;
10852
10293
  }
10853
10294
  const byFramework = result.modelProviders.reduce(
@@ -10861,15 +10302,15 @@ var listCommand6 = new Command42().name("list").alias("ls").description("List al
10861
10302
  },
10862
10303
  {}
10863
10304
  );
10864
- console.log(chalk43.bold("Model Providers:"));
10305
+ console.log(chalk41.bold("Model Providers:"));
10865
10306
  console.log();
10866
10307
  for (const [framework, providers] of Object.entries(byFramework)) {
10867
- console.log(` ${chalk43.cyan(framework)}:`);
10308
+ console.log(` ${chalk41.cyan(framework)}:`);
10868
10309
  for (const provider of providers) {
10869
- const defaultTag = provider.isDefault ? chalk43.green(" (default)") : "";
10310
+ const defaultTag = provider.isDefault ? chalk41.green(" (default)") : "";
10870
10311
  console.log(` ${provider.type}${defaultTag}`);
10871
10312
  console.log(
10872
- chalk43.dim(
10313
+ chalk41.dim(
10873
10314
  ` Updated: ${new Date(provider.updatedAt).toLocaleString()}`
10874
10315
  )
10875
10316
  );
@@ -10877,25 +10318,25 @@ var listCommand6 = new Command42().name("list").alias("ls").description("List al
10877
10318
  console.log();
10878
10319
  }
10879
10320
  console.log(
10880
- chalk43.dim(`Total: ${result.modelProviders.length} provider(s)`)
10321
+ chalk41.dim(`Total: ${result.modelProviders.length} provider(s)`)
10881
10322
  );
10882
10323
  } catch (error) {
10883
10324
  if (error instanceof Error) {
10884
10325
  if (error.message.includes("Not authenticated")) {
10885
- console.error(chalk43.red("x Not authenticated. Run: vm0 auth login"));
10326
+ console.error(chalk41.red("x Not authenticated. Run: vm0 auth login"));
10886
10327
  } else {
10887
- console.error(chalk43.red(`x ${error.message}`));
10328
+ console.error(chalk41.red(`x ${error.message}`));
10888
10329
  }
10889
10330
  } else {
10890
- console.error(chalk43.red("x An unexpected error occurred"));
10331
+ console.error(chalk41.red("x An unexpected error occurred"));
10891
10332
  }
10892
10333
  process.exit(1);
10893
10334
  }
10894
10335
  });
10895
10336
 
10896
10337
  // src/commands/model-provider/setup.ts
10897
- import { Command as Command43 } from "commander";
10898
- import chalk44 from "chalk";
10338
+ import { Command as Command41 } from "commander";
10339
+ import chalk42 from "chalk";
10899
10340
  import prompts3 from "prompts";
10900
10341
  var providerChoices = Object.entries(MODEL_PROVIDER_TYPES).map(
10901
10342
  ([type, config]) => ({
@@ -10903,7 +10344,7 @@ var providerChoices = Object.entries(MODEL_PROVIDER_TYPES).map(
10903
10344
  value: type
10904
10345
  })
10905
10346
  );
10906
- var setupCommand = new Command43().name("setup").description("Configure a model provider").option("-t, --type <type>", "Provider type (for non-interactive mode)").option(
10347
+ var setupCommand2 = new Command41().name("setup").description("Configure a model provider").option("-t, --type <type>", "Provider type (for non-interactive mode)").option(
10907
10348
  "-c, --credential <credential>",
10908
10349
  "Credential value (for non-interactive mode)"
10909
10350
  ).option("--convert", "Convert existing user credential to model provider").action(
@@ -10914,11 +10355,11 @@ var setupCommand = new Command43().name("setup").description("Configure a model
10914
10355
  const shouldConvert = options.convert ?? false;
10915
10356
  if (options.type && options.credential) {
10916
10357
  if (!Object.keys(MODEL_PROVIDER_TYPES).includes(options.type)) {
10917
- console.error(chalk44.red(`x Invalid type "${options.type}"`));
10358
+ console.error(chalk42.red(`x Invalid type "${options.type}"`));
10918
10359
  console.log();
10919
10360
  console.log("Valid types:");
10920
10361
  for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
10921
- console.log(` ${chalk44.cyan(t)} - ${config.label}`);
10362
+ console.log(` ${chalk42.cyan(t)} - ${config.label}`);
10922
10363
  }
10923
10364
  process.exit(1);
10924
10365
  }
@@ -10926,16 +10367,16 @@ var setupCommand = new Command43().name("setup").description("Configure a model
10926
10367
  credential = options.credential;
10927
10368
  } else if (options.type || options.credential) {
10928
10369
  console.error(
10929
- chalk44.red("x Both --type and --credential are required")
10370
+ chalk42.red("x Both --type and --credential are required")
10930
10371
  );
10931
10372
  process.exit(1);
10932
10373
  } else {
10933
10374
  if (!isInteractive()) {
10934
- console.error(chalk44.red("x Interactive mode requires a TTY"));
10375
+ console.error(chalk42.red("x Interactive mode requires a TTY"));
10935
10376
  console.log();
10936
10377
  console.log("Use non-interactive mode:");
10937
10378
  console.log(
10938
- chalk44.cyan(
10379
+ chalk42.cyan(
10939
10380
  ' vm0 model-provider setup --type <type> --credential "<value>"'
10940
10381
  )
10941
10382
  );
@@ -10966,19 +10407,19 @@ var setupCommand = new Command43().name("setup").description("Configure a model
10966
10407
  const provider2 = await convertModelProviderCredential(type);
10967
10408
  const defaultNote2 = provider2.isDefault ? ` (default for ${provider2.framework})` : "";
10968
10409
  console.log(
10969
- chalk44.green(
10410
+ chalk42.green(
10970
10411
  `Done Converted "${checkResult.credentialName}" to model provider${defaultNote2}`
10971
10412
  )
10972
10413
  );
10973
10414
  return;
10974
10415
  } else {
10975
- console.log(chalk44.dim("Aborted."));
10416
+ console.log(chalk42.dim("Aborted."));
10976
10417
  process.exit(0);
10977
10418
  }
10978
10419
  }
10979
10420
  const config = MODEL_PROVIDER_TYPES[type];
10980
10421
  console.log();
10981
- console.log(chalk44.dim(config.helpText));
10422
+ console.log(chalk42.dim(config.helpText));
10982
10423
  console.log();
10983
10424
  const credentialResponse = await prompts3(
10984
10425
  {
@@ -10999,24 +10440,24 @@ var setupCommand = new Command43().name("setup").description("Configure a model
10999
10440
  const action = created ? "created" : "updated";
11000
10441
  const defaultNote = provider.isDefault ? ` (default for ${provider.framework})` : "";
11001
10442
  console.log(
11002
- chalk44.green(`Done Model provider "${type}" ${action}${defaultNote}`)
10443
+ chalk42.green(`Done Model provider "${type}" ${action}${defaultNote}`)
11003
10444
  );
11004
10445
  } catch (error) {
11005
10446
  if (error instanceof Error) {
11006
10447
  if (error.message.includes("already exists")) {
11007
- console.error(chalk44.red(`x ${error.message}`));
10448
+ console.error(chalk42.red(`x ${error.message}`));
11008
10449
  console.log();
11009
10450
  console.log("To convert the existing credential, run:");
11010
- console.log(chalk44.cyan(" vm0 model-provider setup --convert"));
10451
+ console.log(chalk42.cyan(" vm0 model-provider setup --convert"));
11011
10452
  } else if (error.message.includes("Not authenticated")) {
11012
10453
  console.error(
11013
- chalk44.red("x Not authenticated. Run: vm0 auth login")
10454
+ chalk42.red("x Not authenticated. Run: vm0 auth login")
11014
10455
  );
11015
10456
  } else {
11016
- console.error(chalk44.red(`x ${error.message}`));
10457
+ console.error(chalk42.red(`x ${error.message}`));
11017
10458
  }
11018
10459
  } else {
11019
- console.error(chalk44.red("x An unexpected error occurred"));
10460
+ console.error(chalk42.red("x An unexpected error occurred"));
11020
10461
  }
11021
10462
  process.exit(1);
11022
10463
  }
@@ -11024,81 +10465,81 @@ var setupCommand = new Command43().name("setup").description("Configure a model
11024
10465
  );
11025
10466
 
11026
10467
  // src/commands/model-provider/delete.ts
11027
- import { Command as Command44 } from "commander";
11028
- import chalk45 from "chalk";
11029
- var deleteCommand3 = new Command44().name("delete").description("Delete a model provider").argument("<type>", "Model provider type to delete").action(async (type) => {
10468
+ import { Command as Command42 } from "commander";
10469
+ import chalk43 from "chalk";
10470
+ var deleteCommand3 = new Command42().name("delete").description("Delete a model provider").argument("<type>", "Model provider type to delete").action(async (type) => {
11030
10471
  try {
11031
10472
  if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
11032
- console.error(chalk45.red(`x Invalid type "${type}"`));
10473
+ console.error(chalk43.red(`x Invalid type "${type}"`));
11033
10474
  console.log();
11034
10475
  console.log("Valid types:");
11035
10476
  for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
11036
- console.log(` ${chalk45.cyan(t)} - ${config.label}`);
10477
+ console.log(` ${chalk43.cyan(t)} - ${config.label}`);
11037
10478
  }
11038
10479
  process.exit(1);
11039
10480
  }
11040
10481
  await deleteModelProvider(type);
11041
- console.log(chalk45.green(`Done Model provider "${type}" deleted`));
10482
+ console.log(chalk43.green(`Done Model provider "${type}" deleted`));
11042
10483
  } catch (error) {
11043
10484
  if (error instanceof Error) {
11044
10485
  if (error.message.includes("not found")) {
11045
- console.error(chalk45.red(`x Model provider "${type}" not found`));
10486
+ console.error(chalk43.red(`x Model provider "${type}" not found`));
11046
10487
  } else if (error.message.includes("Not authenticated")) {
11047
- console.error(chalk45.red("x Not authenticated. Run: vm0 auth login"));
10488
+ console.error(chalk43.red("x Not authenticated. Run: vm0 auth login"));
11048
10489
  } else {
11049
- console.error(chalk45.red(`x ${error.message}`));
10490
+ console.error(chalk43.red(`x ${error.message}`));
11050
10491
  }
11051
10492
  } else {
11052
- console.error(chalk45.red("x An unexpected error occurred"));
10493
+ console.error(chalk43.red("x An unexpected error occurred"));
11053
10494
  }
11054
10495
  process.exit(1);
11055
10496
  }
11056
10497
  });
11057
10498
 
11058
10499
  // src/commands/model-provider/set-default.ts
11059
- import { Command as Command45 } from "commander";
11060
- import chalk46 from "chalk";
11061
- var setDefaultCommand = new Command45().name("set-default").description("Set a model provider as default for its framework").argument("<type>", "Model provider type to set as default").action(async (type) => {
10500
+ import { Command as Command43 } from "commander";
10501
+ import chalk44 from "chalk";
10502
+ var setDefaultCommand = new Command43().name("set-default").description("Set a model provider as default for its framework").argument("<type>", "Model provider type to set as default").action(async (type) => {
11062
10503
  try {
11063
10504
  if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
11064
- console.error(chalk46.red(`x Invalid type "${type}"`));
10505
+ console.error(chalk44.red(`x Invalid type "${type}"`));
11065
10506
  console.log();
11066
10507
  console.log("Valid types:");
11067
10508
  for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
11068
- console.log(` ${chalk46.cyan(t)} - ${config.label}`);
10509
+ console.log(` ${chalk44.cyan(t)} - ${config.label}`);
11069
10510
  }
11070
10511
  process.exit(1);
11071
10512
  }
11072
10513
  const provider = await setModelProviderDefault(type);
11073
10514
  console.log(
11074
- chalk46.green(
10515
+ chalk44.green(
11075
10516
  `Done Default for ${provider.framework} set to "${provider.type}"`
11076
10517
  )
11077
10518
  );
11078
10519
  } catch (error) {
11079
10520
  if (error instanceof Error) {
11080
10521
  if (error.message.includes("not found")) {
11081
- console.error(chalk46.red(`x Model provider "${type}" not found`));
10522
+ console.error(chalk44.red(`x Model provider "${type}" not found`));
11082
10523
  } else if (error.message.includes("Not authenticated")) {
11083
- console.error(chalk46.red("x Not authenticated. Run: vm0 auth login"));
10524
+ console.error(chalk44.red("x Not authenticated. Run: vm0 auth login"));
11084
10525
  } else {
11085
- console.error(chalk46.red(`x ${error.message}`));
10526
+ console.error(chalk44.red(`x ${error.message}`));
11086
10527
  }
11087
10528
  } else {
11088
- console.error(chalk46.red("x An unexpected error occurred"));
10529
+ console.error(chalk44.red("x An unexpected error occurred"));
11089
10530
  }
11090
10531
  process.exit(1);
11091
10532
  }
11092
10533
  });
11093
10534
 
11094
10535
  // src/commands/model-provider/index.ts
11095
- var modelProviderCommand = new Command46().name("model-provider").description("Manage model providers for agent runs").addCommand(listCommand6).addCommand(setupCommand).addCommand(deleteCommand3).addCommand(setDefaultCommand);
10536
+ var modelProviderCommand = new Command44().name("model-provider").description("Manage model providers for agent runs").addCommand(listCommand6).addCommand(setupCommand2).addCommand(deleteCommand3).addCommand(setDefaultCommand);
11096
10537
 
11097
10538
  // src/index.ts
11098
- var program = new Command47();
11099
- program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("7.1.0");
10539
+ var program = new Command45();
10540
+ program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("8.0.0");
11100
10541
  program.command("info").description("Display environment information").action(async () => {
11101
- console.log(chalk47.bold("System Information:"));
10542
+ console.log(chalk45.bold("System Information:"));
11102
10543
  console.log(`Node Version: ${process.version}`);
11103
10544
  console.log(`Platform: ${process.platform}`);
11104
10545
  console.log(`Architecture: ${process.arch}`);
@@ -11127,7 +10568,6 @@ program.addCommand(logsCommand);
11127
10568
  program.addCommand(scopeCommand);
11128
10569
  program.addCommand(agentCommand);
11129
10570
  program.addCommand(initCommand3);
11130
- program.addCommand(setupGithubCommand);
11131
10571
  program.addCommand(scheduleCommand);
11132
10572
  program.addCommand(usageCommand);
11133
10573
  program.addCommand(credentialCommand);